diff --git a/data-canary/monster/bosses/apocalypse.lua b/data-canary/monster/bosses/apocalypse.lua index fba0ab1613a..a49629a479d 100644 --- a/data-canary/monster/bosses/apocalypse.lua +++ b/data-canary/monster/bosses/apocalypse.lua @@ -86,9 +86,9 @@ monster.loot = { { name = "crystal coin", chance = 36600, maxCount = 15 }, { name = "gold ring", chance = 28000 }, { name = "golden legs", chance = 15000 }, - { name = "giant ruby", chance = 31500 }, - { name = "giant sapphire", chance = 31500 }, - { name = "giant emerald", chance = 31500 }, + { name = "giant ruby", chance = 31500, version = 1100 }, + { name = "giant sapphire", chance = 31500, version = 1100 }, + { name = "giant emerald", chance = 31500, version = 1100 }, { name = "ice rapier", chance = 27500 }, { name = "magic plate armor", chance = 13000 }, { name = "mastermind shield", chance = 17500 }, diff --git a/data-canary/monster/demons/fury.lua b/data-canary/monster/demons/fury.lua index f15ce87af92..943d2124c7a 100644 --- a/data-canary/monster/demons/fury.lua +++ b/data-canary/monster/demons/fury.lua @@ -107,8 +107,8 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -510 }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_FIREDAMAGE, minDamage = -200, maxDamage = -300, length = 8, spread = 3, effect = CONST_ME_EXPLOSIONAREA, target = false }, - { name = "combat", interval = 2000, chance = 5, type = COMBAT_DEATHDAMAGE, minDamage = -120, maxDamage = -700, length = 8, spread = 3, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -120, maxDamage = -300, radius = 4, target = false }, + { name = "combat", interval = 2000, chance = 5, effect = CONST_ME_DRAWBLOOD, type = COMBAT_DEATHDAMAGE, minDamage = -120, maxDamage = -700, length = 8, spread = 3, target = false }, + { name = "combat", interval = 2000, chance = 10, effect = CONST_ME_DRAWBLOOD, type = COMBAT_DEATHDAMAGE, minDamage = -120, maxDamage = -300, radius = 4, target = false }, -- {name ="fury skill reducer", interval = 2000, chance = 5, target = false}, { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = -120, maxDamage = -300, radius = 3, effect = CONST_ME_HITAREA, target = false }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_DEATHDAMAGE, minDamage = -125, maxDamage = -250, range = 7, shootEffect = CONST_ANI_SUDDENDEATH, effect = CONST_ME_SMALLCLOUDS, target = false }, diff --git a/data-canary/monster/familiars/sorcerer_familiar.lua b/data-canary/monster/familiars/sorcerer_familiar.lua index 3628a8b8a1e..a8ca6cd8b2d 100644 --- a/data-canary/monster/familiars/sorcerer_familiar.lua +++ b/data-canary/monster/familiars/sorcerer_familiar.lua @@ -70,8 +70,8 @@ monster.voices = { monster.loot = {} monster.attacks = { - { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -200 }, - { name = "combat", interval = 2000, chance = 25, type = COMBAT_LIFEDRAIN, minDamage = -90, maxDamage = -150, length = 2, spread = 0, target = false }, + { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -280 }, + { name = "combat", interval = 2000, chance = 25, type = COMBAT_LIFEDRAIN, minDamage = -90, maxDamage = -150, length = 2, spread = 0, effect = CONST_ME_MAGIC_RED, target = false }, { name = "combat", interval = 2000, chance = 25, type = COMBAT_ENERGYDAMAGE, minDamage = -190, maxDamage = -210, length = 2, spread = 0, effect = CONST_ME_ENERGYHIT, target = false }, { name = "summon challenge", interval = 2000, chance = 40, target = false }, } diff --git a/data-canary/monster/magicals/frazzlemaw.lua b/data-canary/monster/magicals/frazzlemaw.lua index a8ffdd9e6dd..97eea8a9bf7 100644 --- a/data-canary/monster/magicals/frazzlemaw.lua +++ b/data-canary/monster/magicals/frazzlemaw.lua @@ -119,8 +119,8 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -400 }, -- bleed - { name = "condition", type = CONDITION_BLEEDING, interval = 2000, chance = 10, minDamage = -300, maxDamage = -400, radius = 3, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -700, length = 5, spread = 3, effect = CONST_ME_EXPLOSIONAREA, target = false }, + { name = "condition", type = CONDITION_BLEEDING, interval = 2000, chance = 10, minDamage = -300, maxDamage = -400, radius = 3, effect = CONST_ME_DRAWBLOOD, target = false }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = 0, maxDamage = -700, length = 5, spread = 0, effect = CONST_ME_EXPLOSIONAREA, target = false }, { name = "combat", interval = 2000, chance = 15, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -400, radius = 2, shootEffect = CONST_ANI_LARGEROCK, effect = CONST_ME_STONES, target = true }, { name = "speed", interval = 2000, chance = 15, speedChange = -600, radius = 5, effect = CONST_ME_MAGIC_RED, target = false, duration = 15000 }, { name = "combat", interval = 2000, chance = 10, type = COMBAT_MANADRAIN, minDamage = -80, maxDamage = -150, radius = 4, effect = CONST_ME_MAGIC_RED, target = false }, diff --git a/data-canary/monster/magicals/guzzlemaw.lua b/data-canary/monster/magicals/guzzlemaw.lua index 92413db132a..6a3f2904f23 100644 --- a/data-canary/monster/magicals/guzzlemaw.lua +++ b/data-canary/monster/magicals/guzzlemaw.lua @@ -115,11 +115,11 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -499 }, -- bleed - { name = "condition", type = CONDITION_BLEEDING, interval = 2000, chance = 10, minDamage = -500, maxDamage = -1000, radius = 3, target = false }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -900, length = 8, spread = 3, effect = CONST_ME_EXPLOSIONAREA, target = false }, + { name = "condition", type = CONDITION_BLEEDING, interval = 2000, chance = 10, minDamage = -500, maxDamage = -1000, radius = 3, effect = CONST_ME_DRAWBLOOD, target = false }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = 0, maxDamage = -900, length = 8, spread = 0, effect = CONST_ME_EXPLOSIONAREA, target = false }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -500, radius = 2, shootEffect = CONST_ANI_LARGEROCK, effect = CONST_ME_STONES, target = true }, - { name = "speed", interval = 2000, chance = 15, speedChange = -100, radius = 6, effect = CONST_ME_MAGIC_RED, target = false, duration = 15000 }, - { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = 0, maxDamage = -800, length = 8, spread = 3, effect = CONST_ME_MAGIC_RED, target = false }, + { name = "speed", interval = 2000, chance = 15, speedChange = -800, radius = 6, effect = CONST_ME_MAGIC_RED, target = false, duration = 15000 }, + { name = "combat", interval = 2000, chance = 10, type = COMBAT_LIFEDRAIN, minDamage = 0, maxDamage = -800, length = 8, spread = 0, effect = CONST_ME_MAGIC_RED, target = false }, } monster.defenses = { diff --git a/data-canary/monster/vermins/spider.lua b/data-canary/monster/vermins/spider.lua index 3932f902488..a93e7c93f77 100644 --- a/data-canary/monster/vermins/spider.lua +++ b/data-canary/monster/vermins/spider.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Spider") local monster = {} diff --git a/data-canary/scripts/actions/worldboard.lua b/data-canary/scripts/actions/worldboard.lua index 80fe5e737f3..dcabdad8d5c 100644 --- a/data-canary/scripts/actions/worldboard.lua +++ b/data-canary/scripts/actions/worldboard.lua @@ -5,7 +5,6 @@ local communicates = { } local scriptConfig = { - itemId = 19236, registerPositions = { { x = 4998, y = 5000, z = 7 }, { x = 4971, y = 5300, z = 5 }, @@ -21,7 +20,7 @@ local worldBoard = Action() function worldBoard.onUse(player, item, fromPosition, target, toPosition, isHotkey) -- If the item id is not the one on the worldboard, it will return here - if item:getId() ~= scriptConfig.itemId then + if item:getId() ~= ITEM_WORLD_BOARD then return false end @@ -34,7 +33,7 @@ end -- Usage: action:position(position, itemId) -- Explanation: The variable "item id" is optional, the id or the name of the item can be added, the item will be created in the map if it does not exist. If it already exists on the map, it will send a warning informing (in the distro) so the id must be removed so that the warning disappears keeping only the position) for index, value in pairs(scriptConfig.registerPositions) do - worldBoard:position(value, scriptConfig.itemId) + worldBoard:position(value, ITEM_WORLD_BOARD) end worldBoard:register() diff --git a/data-canary/scripts/lib/register_item_tier.lua b/data-canary/scripts/lib/register_item_tier.lua index 51ad62ccc6e..b869dd229a1 100644 --- a/data-canary/scripts/lib/register_item_tier.lua +++ b/data-canary/scripts/lib/register_item_tier.lua @@ -14,8 +14,9 @@ end registerItemClassification.Upgrades = function(itemClassification, mask) if mask.Upgrades then for _, value in ipairs(mask.Upgrades) do - if value.TierId and value.Price then - itemClassification:addTier(value.TierId, value.Price, value.Core) + if value.TierId then + logger.debug("Registering tier {}, core {}, regular price {}, fusion price {}, transfer price {}", value.TierId, value.Core, value.RegularPrice, value.ConvergenceFustionPrice, value.ConvergenceTransferPrice) + itemClassification:addTier(value.TierId, value.Core, value.RegularPrice, value.ConvergenceFustionPrice, value.ConvergenceTransferPrice) else logger.warn("[registerItemClassification.Upgrades] - Item classification failed on adquire TierID or Price attribute.") end diff --git a/data-canary/scripts/lib/register_monster_type.lua b/data-canary/scripts/lib/register_monster_type.lua index 9e1f3848453..ae22d66eda4 100644 --- a/data-canary/scripts/lib/register_monster_type.lua +++ b/data-canary/scripts/lib/register_monster_type.lua @@ -24,6 +24,11 @@ registerMonsterType.description = function(mtype, mask) mtype:nameDescription(mask.description) end end +registerMonsterType.variant = function(mtype, mask) + if mask.variant then + mtype:variant(mask.variant) + end +end registerMonsterType.experience = function(mtype, mask) if mask.experience then mtype:experience(mask.experience) @@ -68,9 +73,6 @@ end registerMonsterType.bosstiary = function(mtype, mask) local bossClass = nil if mask.bosstiary then - if mask.bosstiary.bossRaceId then - mtype:bossRaceId(mask.bosstiary.bossRaceId) - end if mask.bosstiary.bossRace then if mask.bosstiary.bossRace == RARITY_BANE then bossClass = "Bane" @@ -79,14 +81,17 @@ registerMonsterType.bosstiary = function(mtype, mask) elseif mask.bosstiary.bossRace == RARITY_NEMESIS then bossClass = "Nemesis" end - if bossClass ~= nil then - mtype:bossRace(mask.bosstiary.bossRace, bossClass) - end - local storage = mask.bosstiary.storageCooldown - if storage ~= nil then - mtype:bossStorageCooldown(storage) - end end + if bossClass == nil then + Spdlog.error(string.format("Attempting to register a bosstiary boss without a race. Boss name: %s", mtype:name())) + return + end + if mask.bosstiary.bossRaceId then + mtype:bossRaceId(mask.bosstiary.bossRaceId) + else + Spdlog.error(string.format("Attempting to register a bosstiary boss without a raceId. Boss name: %s", mtype:name())) + end + mtype:bossRace(mask.bosstiary.bossRace, bossClass) end end registerMonsterType.skull = function(mtype, mask) @@ -193,6 +198,9 @@ registerMonsterType.flags = function(mtype, mask) if mask.flags.canPushCreatures ~= nil then mtype:canPushCreatures(mask.flags.canPushCreatures) end + if mask.flags.critChance ~= nil then + mtype:critChance(mask.flags.critChance) + end if mask.flags.targetDistance then mtype:targetDistance(math.max(1, mask.flags.targetDistance)) end @@ -335,6 +343,10 @@ registerMonsterType.loot = function(mtype, mask) SortLootByChance(mask.loot) local lootError = false for _, loot in pairs(mask.loot) do + if loot.version and CLIENT_VERSION < loot.version then + goto continue + end + local parent = Loot() if loot.name then if not parent:setIdFromName(loot.name) then @@ -458,26 +470,49 @@ registerMonsterType.loot = function(mtype, mask) end end mtype:addLoot(parent) + + ::continue:: end if lootError then logger.warn("[registerMonsterType.loot] - Monster: {} loot could not correctly be load", mtype:name()) end end end + +local playerElements = { COMBAT_PHYSICALDAMAGE, COMBAT_ENERGYDAMAGE, COMBAT_EARTHDAMAGE, COMBAT_FIREDAMAGE, COMBAT_ICEDAMAGE, COMBAT_HOLYDAMAGE, COMBAT_DEATHDAMAGE } registerMonsterType.elements = function(mtype, mask) + local min = configManager.getNumber(configKeys.MIN_ELEMENTAL_RESISTANCE) + local max = configManager.getNumber(configKeys.MAX_ELEMENTAL_RESISTANCE) + local canClip = false if type(mask.elements) == "table" then + for _, playerElement in pairs(playerElements) do + local found = false + for _, element in pairs(mask.elements) do + if element.type == playerElement then + found = true + canClip = canClip or element.percent ~= 100 + break + end + end + canClip = canClip or not found + end for _, element in pairs(mask.elements) do if element.type and element.percent then - mtype:addElement(element.type, element.percent) + local value = element.percent + if canClip then + value = math.min(math.max(element.percent, min), max) + end + mtype:addElement(element.type, value) end end end end registerMonsterType.reflects = function(mtype, mask) + local max = configManager.getNumber(configKeys.MAX_DAMAGE_REFLECTION) if type(mask.reflects) == "table" then for _, reflect in pairs(mask.reflects) do if reflect.type and reflect.percent then - mtype:addReflect(reflect.type, reflect.percent) + mtype:addReflect(reflect.type, math.min(reflect.percent, max)) end end end @@ -923,6 +958,11 @@ function readSpell(incomingLua, mtype) spell:setConditionDamage(incomingLua.condition.totalDamage, incomingLua.condition.totalDamage, 0) end + local isArea = (incomingLua.radius and incomingLua.radius > 1) or incomingLua.length or incomingLua.spread + if isArea and incomingLua.effect == nil and not string.find(incomingLua.name, "field") then + logger.warn("[readSpell] - Monster {}: Spell {} is area but has no effect. Set to `false` explicitly to supress this alert and hide the effect", mtype:name(), incomingLua.name) + spell:setCombatEffect(CONST_ME_POFF) + end elseif incomingLua.script then spell:setScriptName("monster/" .. incomingLua.script .. ".lua") if incomingLua.interval then diff --git a/data-canary/scripts/lib/register_spells.lua b/data-canary/scripts/lib/register_spells.lua index 6871ecc5651..49a5d7aec2f 100644 --- a/data-canary/scripts/lib/register_spells.lua +++ b/data-canary/scripts/lib/register_spells.lua @@ -229,6 +229,14 @@ AREADIAGONAL_BEAM7 = { { 0, 0, 0, 0, 0, 0, 3 }, } +AREADIAGONAL_SQUAREWAVE5_NAGA = { + { 0, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 0 }, + { 0, 0, 0, 0, 3 }, +} + --Circles AREA_CIRCLE2X2 = { { 0, 1, 1, 1, 0 }, @@ -275,6 +283,18 @@ AREA_CIRCLE1X1 = { { 0, 1, 0 }, } +AREA_CIRCLE4X4 = { + { 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 0, 0 }, + { 0, 1, 1, 1, 1, 1, 1, 1, 0 }, + { 1, 1, 1, 1, 3, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 1, 1, 1, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0 }, +} + AREA_CIRCLE5X5 = { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0 }, @@ -326,6 +346,13 @@ AREA_SQUARE1X1 = { { 1, 1, 1 }, } +AREA_SQUAREWAVE5_NAGA = { + { 1, 1, 1 }, + { 1, 1, 1 }, + { 0, 1, 0 }, + { 0, 3, 0 }, +} + -- Walls AREA_WALLFIELD = { { 1, 1, 3, 1, 1 }, diff --git a/data-canary/scripts/reward_chest/reward_chest.lua b/data-canary/scripts/reward_chest/reward_chest.lua index d7724ca0b32..20436162565 100644 --- a/data-canary/scripts/reward_chest/reward_chest.lua +++ b/data-canary/scripts/reward_chest/reward_chest.lua @@ -3,6 +3,6 @@ local chest = Action() function chest.onUse(player, item, fromPosition, target, toPosition, isHotkey) end -- Create reward chest in the Montag temple -chest:position({ x = 5003, y = 4996, z = 7 }, 19250) +chest:position({ x = 5003, y = 4996, z = 7 }, ITEM_REWARD_CHEST) chest:register() diff --git a/data-canary/world/canary-house.xml b/data-canary/world/canary-house.xml index adaa1a1c8f2..43d8e80d11e 100644 --- a/data-canary/world/canary-house.xml +++ b/data-canary/world/canary-house.xml @@ -1,4 +1,4 @@ - + diff --git a/data-canary/world/canary-zones.xml b/data-canary/world/canary-zones.xml new file mode 100644 index 00000000000..a9224bd3c2d --- /dev/null +++ b/data-canary/world/canary-zones.xml @@ -0,0 +1,2 @@ + + diff --git a/data-canary/world/canary.otbm b/data-canary/world/canary.otbm index 280d57a9194..5d254e6c021 100644 Binary files a/data-canary/world/canary.otbm and b/data-canary/world/canary.otbm differ diff --git a/data-otservbr-global/lib/compat/compat.lua b/data-otservbr-global/lib/compat/compat.lua index cd9b8146763..d6ad0452fdf 100644 --- a/data-otservbr-global/lib/compat/compat.lua +++ b/data-otservbr-global/lib/compat/compat.lua @@ -4,7 +4,7 @@ FALSE = false MESSAGE_STATUS_CONSOLE_RED = MESSAGE_GAMEMASTER_CONSOLE MESSAGE_STATUS_DEFAULT = MESSAGE_LOGIN -MESSAGE_STATUS_WARNING = MESSAGE_ADMINISTRADOR +MESSAGE_STATUS_WARNING = MESSAGE_ADMINISTRATOR MESSAGE_EVENT_ADVANCE = MESSAGE_EVENT_ADVANCE MESSAGE_STATUS_SMALL = MESSAGE_FAILURE diff --git a/data-otservbr-global/lib/quests/grimvale.lua b/data-otservbr-global/lib/quests/grimvale.lua index ba2f5842feb..2e7e7d80653 100644 --- a/data-otservbr-global/lib/quests/grimvale.lua +++ b/data-otservbr-global/lib/quests/grimvale.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local oldpos = {} local config = { diff --git a/data-otservbr-global/monster/aquatics/quara_constrictor_scout.lua b/data-otservbr-global/monster/aquatics/quara_constrictor_scout.lua index b47c315119f..52df8aaebea 100644 --- a/data-otservbr-global/monster/aquatics/quara_constrictor_scout.lua +++ b/data-otservbr-global/monster/aquatics/quara_constrictor_scout.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 780 then - return -end - local mType = Game.createMonsterType("Quara Constrictor Scout") local monster = {} diff --git a/data-otservbr-global/monster/aquatics/quara_pincher.lua b/data-otservbr-global/monster/aquatics/quara_pincher.lua index 47c13e25d1a..a212ad81d5d 100644 --- a/data-otservbr-global/monster/aquatics/quara_pincher.lua +++ b/data-otservbr-global/monster/aquatics/quara_pincher.lua @@ -92,7 +92,7 @@ monster.loot = { { name = "warrior helmet", chance = 1460 }, { name = "crown armor", chance = 280 }, { name = "glacier robe", chance = 120 }, - { name = "giant shrimp", chance = 40 }, + { name = "giant shrimp", chance = 40, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/aquatics/quara_predator.lua b/data-otservbr-global/monster/aquatics/quara_predator.lua index ea28c4a43da..774e69a369d 100644 --- a/data-otservbr-global/monster/aquatics/quara_predator.lua +++ b/data-otservbr-global/monster/aquatics/quara_predator.lua @@ -95,7 +95,7 @@ monster.loot = { { name = "assassin star", chance = 530 }, { name = "glacier robe", chance = 440 }, { name = "skull helmet", chance = 390 }, - { name = "giant shrimp", chance = 10 }, + { name = "giant shrimp", chance = 10, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/aquatics/renegade_quara_pincher.lua b/data-otservbr-global/monster/aquatics/renegade_quara_pincher.lua index adf311b45d1..60519b3f06d 100644 --- a/data-otservbr-global/monster/aquatics/renegade_quara_pincher.lua +++ b/data-otservbr-global/monster/aquatics/renegade_quara_pincher.lua @@ -97,7 +97,7 @@ monster.loot = { { id = 3053, chance = 410 }, -- time ring { name = "talon", chance = 310 }, { name = "glacier robe", chance = 200 }, - { name = "giant shrimp", chance = 100 }, + { name = "giant shrimp", chance = 100, version = 1100 }, { name = "twiceslicer", chance = 100 }, } diff --git a/data-otservbr-global/monster/aquatics/renegade_quara_predator.lua b/data-otservbr-global/monster/aquatics/renegade_quara_predator.lua index 52e1a87eb92..47eadd206f3 100644 --- a/data-otservbr-global/monster/aquatics/renegade_quara_predator.lua +++ b/data-otservbr-global/monster/aquatics/renegade_quara_predator.lua @@ -95,7 +95,7 @@ monster.loot = { { name = "glacier robe", chance = 400 }, { name = "abyss hammer", chance = 320 }, { name = "frozen plate", chance = 160 }, - { name = "giant shrimp", chance = 80 }, + { name = "giant shrimp", chance = 80, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/bosses/apocalypse.lua b/data-otservbr-global/monster/bosses/apocalypse.lua index 3e60fe75e34..b3926abb903 100644 --- a/data-otservbr-global/monster/bosses/apocalypse.lua +++ b/data-otservbr-global/monster/bosses/apocalypse.lua @@ -86,9 +86,9 @@ monster.loot = { { name = "crystal coin", chance = 36600, maxCount = 15 }, { name = "gold ring", chance = 28000 }, { name = "golden legs", chance = 15000 }, - { name = "giant ruby", chance = 31500 }, - { name = "giant sapphire", chance = 31500 }, - { name = "giant emerald", chance = 31500 }, + { name = "giant ruby", chance = 31500, version = 1100 }, + { name = "giant sapphire", chance = 31500, version = 1100 }, + { name = "giant emerald", chance = 31500, version = 1100 }, { name = "ice rapier", chance = 27500 }, { name = "magic plate armor", chance = 13000 }, { name = "mastermind shield", chance = 17500 }, diff --git a/data-otservbr-global/monster/demons/infernal_demon.lua b/data-otservbr-global/monster/demons/infernal_demon.lua index 7c4a1d80302..b37294c6a48 100644 --- a/data-otservbr-global/monster/demons/infernal_demon.lua +++ b/data-otservbr-global/monster/demons/infernal_demon.lua @@ -81,15 +81,15 @@ monster.loot = { { name = "crystal coin", chance = 64000 }, { name = "ultimate health potion", chance = 22860, maxCount = 5 }, { name = "gold ingot", chance = 18860 }, - { name = "cyan crystal fragment", chance = 7430 }, - { name = "red crystal fragment", chance = 7430 }, - { name = "blue crystal shard", chance = 5710 }, + { name = "cyan crystal fragment", chance = 7430, version = 1100 }, + { name = "red crystal fragment", chance = 7430, version = 1100 }, + { name = "blue crystal shard", chance = 5710, version = 1100 }, { name = "small diamond", chance = 4570 }, { name = "blue gem", chance = 4570 }, - { name = "green crystal fragment", chance = 3430 }, + { name = "green crystal fragment", chance = 3430, version = 1100 }, { name = "magma amulet", chance = 3430 }, { name = "mercenary sword", chance = 2860 }, - { name = "onyx chip", chance = 2860 }, + { name = "onyx chip", chance = 2860, version = 1100 }, { name = "war axe", chance = 2860 }, { name = "giant sword", chance = 2860 }, { name = "magma boots", chance = 2290 }, diff --git a/data-otservbr-global/monster/demons/many_faces.lua b/data-otservbr-global/monster/demons/many_faces.lua index 8b9724a6252..2e38053e315 100644 --- a/data-otservbr-global/monster/demons/many_faces.lua +++ b/data-otservbr-global/monster/demons/many_faces.lua @@ -81,7 +81,7 @@ monster.voices = { monster.loot = { { name = "crystal coin", chance = 76710 }, { name = "ultimate health potion", chance = 14920, maxCount = 7 }, - { name = "apron", chance = 7990 }, + { name = "apron", chance = 7990, version = 1100 }, { name = "hailstorm rod", chance = 7610 }, { name = "stone skin amulet", chance = 5780 }, { name = "green gem", chance = 5710 }, @@ -93,8 +93,8 @@ monster.loot = { { id = 33932, chance = 3500 }, -- head many faces { name = "glacier shoes", chance = 2510 }, { name = "glacier robe", chance = 2130 }, - { name = "gruesome fan", chance = 610 }, - { name = "glacial rod", chance = 610 }, + { name = "gruesome fan", chance = 610, version = 1100 }, + { name = "glacial rod", chance = 610, version = 1100 }, { id = 34109, chance = 20 }, -- bag you desire } diff --git a/data-otservbr-global/monster/demons/nightfiend.lua b/data-otservbr-global/monster/demons/nightfiend.lua index 2f1b22034ac..8db3ea75931 100644 --- a/data-otservbr-global/monster/demons/nightfiend.lua +++ b/data-otservbr-global/monster/demons/nightfiend.lua @@ -78,7 +78,7 @@ monster.loot = { { name = "platinum coin", chance = 50000, maxCount = 1 }, { name = "gold coin", chance = 50000, maxCount = 148 }, { id = 3030, chance = 1052, maxCount = 3 }, - { name = "tooth file", chance = 564 }, + { name = "tooth file", chance = 564, version = 1100 }, { name = "blood preservation", chance = 1000 }, { name = "emerald bangle", chance = 120 }, { name = "vampire teeth", chance = 10000 }, diff --git a/data-otservbr-global/monster/demons/shaburak_prince.lua b/data-otservbr-global/monster/demons/shaburak_prince.lua index 7885b5b086f..8c13791270a 100644 --- a/data-otservbr-global/monster/demons/shaburak_prince.lua +++ b/data-otservbr-global/monster/demons/shaburak_prince.lua @@ -95,7 +95,7 @@ monster.loot = { { name = "strong health potion", chance = 14285 }, { name = "strong mana potion", chance = 14285 }, { name = "magma coat", chance = 714 }, - { name = "demonic finger", chance = 178 }, + { name = "demonic finger", chance = 178, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/demons/true_dawnfire_asura.lua b/data-otservbr-global/monster/demons/true_dawnfire_asura.lua index b434eb8be72..fca9b0a8fa3 100644 --- a/data-otservbr-global/monster/demons/true_dawnfire_asura.lua +++ b/data-otservbr-global/monster/demons/true_dawnfire_asura.lua @@ -83,7 +83,7 @@ monster.loot = { { name = "small enchanted ruby", chance = 9440, maxCount = 3 }, { id = 3030, chance = 11890, maxCount = 2 }, -- small ruby { id = 9057, chance = 8560, maxCount = 2 }, -- small topaz - { name = "royal star", chance = 4050, maxCount = 3 }, + { name = "royal star", chance = 4050, maxCount = 3, version = 1100 }, { id = 3041, chance = 1300 }, -- blue gem { id = 3039, chance = 3800 }, -- red gem { id = 6299, chance = 1100 }, -- death ring diff --git a/data-otservbr-global/monster/demons/true_frost_flower_asura.lua b/data-otservbr-global/monster/demons/true_frost_flower_asura.lua index cd1b3bd8dda..f2d9a7946de 100644 --- a/data-otservbr-global/monster/demons/true_frost_flower_asura.lua +++ b/data-otservbr-global/monster/demons/true_frost_flower_asura.lua @@ -92,7 +92,7 @@ monster.loot = { { id = 8074, chance = 940 }, -- spellbook of mind control { name = "small enchanted sapphire", chance = 9640, maxCount = 3 }, { id = 3567, chance = 1540 }, -- blue robe - { name = "royal star", chance = 4000, maxCount = 3 }, + { name = "royal star", chance = 4000, maxCount = 3, version = 1100 }, { id = 7368, chance = 9640, maxCount = 5 }, -- assassin star { id = 3403, chance = 3590 }, -- tribal mask { id = 9058, chance = 2380 }, -- gold ingot diff --git a/data-otservbr-global/monster/demons/true_midnight_asura.lua b/data-otservbr-global/monster/demons/true_midnight_asura.lua index ebdac293ea6..1aa8035882e 100644 --- a/data-otservbr-global/monster/demons/true_midnight_asura.lua +++ b/data-otservbr-global/monster/demons/true_midnight_asura.lua @@ -75,7 +75,7 @@ monster.voices = { monster.loot = { { name = "violet gem", chance = 1390 }, { name = "crystal coin", chance = 5760, maxCount = 1 }, - { name = "royal star", chance = 4090, maxCount = 3 }, + { name = "royal star", chance = 4090, maxCount = 3, version = 1100 }, { id = 3035, chance = 100000, maxCount = 8 }, -- platinum coin { id = 7368, chance = 9210, maxCount = 5 }, -- assassin star { id = 3027, chance = 9870, maxCount = 2 }, -- black pearl diff --git a/data-otservbr-global/monster/demons/vexclaw.lua b/data-otservbr-global/monster/demons/vexclaw.lua index d31045eacc8..231dcada510 100644 --- a/data-otservbr-global/monster/demons/vexclaw.lua +++ b/data-otservbr-global/monster/demons/vexclaw.lua @@ -82,7 +82,7 @@ monster.loot = { { name = "platinum coin", chance = 100000, maxCount = 6 }, { name = "great spirit potion", chance = 26010, maxCount = 5 }, { name = "great mana potion", chance = 25210, maxCount = 5 }, - { name = "vexclaw talon", chance = 21500 }, + { name = "vexclaw talon", chance = 21500, version = 1100 }, { name = "demonic essence", chance = 20730 }, { name = "ultimate health potion", chance = 19960, maxCount = 5 }, { name = "fire mushroom", chance = 19940, maxCount = 6 }, @@ -102,13 +102,13 @@ monster.loot = { { name = "giant sword", chance = 1880 }, { id = 3049, chance = 1790 }, -- stealth ring { id = 3051, chance = 1790 }, -- energy ring - { name = "rift lance", chance = 1360 }, + { name = "rift lance", chance = 1360, version = 1100 }, { id = 3098, chance = 1320 }, -- ring of healing { name = "platinum amulet", chance = 940 }, { name = "devil helmet", chance = 520 }, - { name = "rift crossbow", chance = 370 }, - { name = "rift bow", chance = 370 }, - { name = "rift shield", chance = 370 }, + { name = "rift crossbow", chance = 370, version = 1100 }, + { name = "rift bow", chance = 370, version = 1100 }, + { name = "rift shield", chance = 370, version = 1100 }, { name = "demon shield", chance = 370 }, { name = "magic plate armor", chance = 70 }, { name = "golden legs", chance = 50 }, diff --git a/data-otservbr-global/monster/dragons/draken_abomination.lua b/data-otservbr-global/monster/dragons/draken_abomination.lua index de692231264..cb9e378c269 100644 --- a/data-otservbr-global/monster/dragons/draken_abomination.lua +++ b/data-otservbr-global/monster/dragons/draken_abomination.lua @@ -107,7 +107,7 @@ monster.loot = { { name = "shield of corruption", chance = 10 }, { name = "draken boots", chance = 540 }, { name = "snake god's wristguard", chance = 10 }, - { name = "bamboo leaves", chance = 360 }, + { name = "bamboo leaves", chance = 360, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/dragons/draken_spellweaver.lua b/data-otservbr-global/monster/dragons/draken_spellweaver.lua index b1a75c29b80..2da7f2daf14 100644 --- a/data-otservbr-global/monster/dragons/draken_spellweaver.lua +++ b/data-otservbr-global/monster/dragons/draken_spellweaver.lua @@ -96,8 +96,8 @@ monster.loot = { { name = "zaoan legs", chance = 550 }, { name = "zaoan robe", chance = 370 }, { name = "ring of the sky", chance = 180 }, - { name = "bamboo leaves", chance = 180 }, - { name = "harness", chance = 30 }, + { name = "bamboo leaves", chance = 180, version = 1100 }, + { name = "harness", chance = 30, version = 1100 }, { name = "draken trophy", chance = 10 }, } diff --git a/data-otservbr-global/monster/humanoids/chakoya_toolshaper.lua b/data-otservbr-global/monster/humanoids/chakoya_toolshaper.lua index 010c4cf0603..f2f1e27b984 100644 --- a/data-otservbr-global/monster/humanoids/chakoya_toolshaper.lua +++ b/data-otservbr-global/monster/humanoids/chakoya_toolshaper.lua @@ -87,7 +87,7 @@ monster.loot = { { name = "green perch", chance = 70 }, { name = "mammoth whopper", chance = 160 }, { id = 7441, chance = 450 }, -- ice cube - { name = "fireproof horn", chance = 390 }, + { name = "fireproof horn", chance = 390, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humanoids/chakoya_tribewarden.lua b/data-otservbr-global/monster/humanoids/chakoya_tribewarden.lua index 76ef89e5fbf..b0b0b3e4653 100644 --- a/data-otservbr-global/monster/humanoids/chakoya_tribewarden.lua +++ b/data-otservbr-global/monster/humanoids/chakoya_tribewarden.lua @@ -85,7 +85,7 @@ monster.loot = { { name = "rainbow trout", chance = 50 }, { name = "green perch", chance = 100 }, { name = "mammoth whopper", chance = 130 }, - { name = "fireproof horn", chance = 350 }, + { name = "fireproof horn", chance = 350, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humanoids/chakoya_windcaller.lua b/data-otservbr-global/monster/humanoids/chakoya_windcaller.lua index e99f6ddb408..ecd09ac6ef2 100644 --- a/data-otservbr-global/monster/humanoids/chakoya_windcaller.lua +++ b/data-otservbr-global/monster/humanoids/chakoya_windcaller.lua @@ -84,7 +84,7 @@ monster.loot = { { name = "northern pike", chance = 40 }, { name = "rainbow trout", chance = 40 }, { name = "green perch", chance = 110 }, - { name = "fireproof horn", chance = 410 }, + { name = "fireproof horn", chance = 410, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humanoids/corym_charlatan.lua b/data-otservbr-global/monster/humanoids/corym_charlatan.lua index e9dce8b0055..65f415ba1d8 100644 --- a/data-otservbr-global/monster/humanoids/corym_charlatan.lua +++ b/data-otservbr-global/monster/humanoids/corym_charlatan.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Corym Charlatan") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/corym_skirmisher.lua b/data-otservbr-global/monster/humanoids/corym_skirmisher.lua index 59980509138..68635a5e229 100644 --- a/data-otservbr-global/monster/humanoids/corym_skirmisher.lua +++ b/data-otservbr-global/monster/humanoids/corym_skirmisher.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Corym Skirmisher") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/corym_vanguard.lua b/data-otservbr-global/monster/humanoids/corym_vanguard.lua index 3326d290d21..179a9f5f68f 100644 --- a/data-otservbr-global/monster/humanoids/corym_vanguard.lua +++ b/data-otservbr-global/monster/humanoids/corym_vanguard.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Corym Vanguard") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/crazed_summer_rearguard.lua b/data-otservbr-global/monster/humanoids/crazed_summer_rearguard.lua index 8e269d760c4..6984ac04fd4 100644 --- a/data-otservbr-global/monster/humanoids/crazed_summer_rearguard.lua +++ b/data-otservbr-global/monster/humanoids/crazed_summer_rearguard.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1200 then + return +end + local mType = Game.createMonsterType("Crazed Summer Rearguard") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/crazed_summer_vanguard.lua b/data-otservbr-global/monster/humanoids/crazed_summer_vanguard.lua index 38fe860c679..5a3768fa679 100644 --- a/data-otservbr-global/monster/humanoids/crazed_summer_vanguard.lua +++ b/data-otservbr-global/monster/humanoids/crazed_summer_vanguard.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1200 then + return +end + local mType = Game.createMonsterType("Crazed Summer Vanguard") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/crazed_winter_rearguard.lua b/data-otservbr-global/monster/humanoids/crazed_winter_rearguard.lua index d7d54c49c01..f3c872d837a 100644 --- a/data-otservbr-global/monster/humanoids/crazed_winter_rearguard.lua +++ b/data-otservbr-global/monster/humanoids/crazed_winter_rearguard.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1200 then + return +end + local mType = Game.createMonsterType("Crazed Winter Rearguard") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/crazed_winter_vanguard.lua b/data-otservbr-global/monster/humanoids/crazed_winter_vanguard.lua index 22cac33bccb..9b80c9622eb 100644 --- a/data-otservbr-global/monster/humanoids/crazed_winter_vanguard.lua +++ b/data-otservbr-global/monster/humanoids/crazed_winter_vanguard.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1200 then + return +end + local mType = Game.createMonsterType("Crazed Winter Vanguard") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/insane_siren.lua b/data-otservbr-global/monster/humanoids/insane_siren.lua index b5a6d3acddd..e5fc1226fcc 100644 --- a/data-otservbr-global/monster/humanoids/insane_siren.lua +++ b/data-otservbr-global/monster/humanoids/insane_siren.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1200 then + return +end + local mType = Game.createMonsterType("Insane Siren") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/lost_basher.lua b/data-otservbr-global/monster/humanoids/lost_basher.lua index e19aef97f78..48a56f1a0cf 100644 --- a/data-otservbr-global/monster/humanoids/lost_basher.lua +++ b/data-otservbr-global/monster/humanoids/lost_basher.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Lost Basher") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/lost_berserker.lua b/data-otservbr-global/monster/humanoids/lost_berserker.lua index cd48787df95..a43cde9acf0 100644 --- a/data-otservbr-global/monster/humanoids/lost_berserker.lua +++ b/data-otservbr-global/monster/humanoids/lost_berserker.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Lost Berserker") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/lost_exile.lua b/data-otservbr-global/monster/humanoids/lost_exile.lua index 0acd587804b..4d16a17518f 100644 --- a/data-otservbr-global/monster/humanoids/lost_exile.lua +++ b/data-otservbr-global/monster/humanoids/lost_exile.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Lost Exile") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/lost_husher.lua b/data-otservbr-global/monster/humanoids/lost_husher.lua index d8167f9710d..5a61c6d7ecf 100644 --- a/data-otservbr-global/monster/humanoids/lost_husher.lua +++ b/data-otservbr-global/monster/humanoids/lost_husher.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Lost Husher") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/lost_thrower.lua b/data-otservbr-global/monster/humanoids/lost_thrower.lua index c2a87f0d551..42fa0a278a4 100644 --- a/data-otservbr-global/monster/humanoids/lost_thrower.lua +++ b/data-otservbr-global/monster/humanoids/lost_thrower.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 980 then + return +end + local mType = Game.createMonsterType("Lost Thrower") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/minotaur_cult_follower.lua b/data-otservbr-global/monster/humanoids/minotaur_cult_follower.lua index 18883c46827..097c0e666d1 100644 --- a/data-otservbr-global/monster/humanoids/minotaur_cult_follower.lua +++ b/data-otservbr-global/monster/humanoids/minotaur_cult_follower.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1140 then + return +end + local mType = Game.createMonsterType("Minotaur Cult Follower") local monster = {} diff --git a/data-otservbr-global/monster/humanoids/swamp_troll.lua b/data-otservbr-global/monster/humanoids/swamp_troll.lua index c95481f5718..e9061a908f7 100644 --- a/data-otservbr-global/monster/humanoids/swamp_troll.lua +++ b/data-otservbr-global/monster/humanoids/swamp_troll.lua @@ -91,7 +91,7 @@ monster.loot = { { name = "troll green", chance = 1200 }, { id = 5901, chance = 2140 }, -- wood { name = "swamp grass", chance = 3100 }, - { name = "medicine pouch", chance = 2160 }, + { name = "medicine pouch", chance = 2160, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humans/dark_apprentice.lua b/data-otservbr-global/monster/humans/dark_apprentice.lua index a80e9fbb00b..bba29df5138 100644 --- a/data-otservbr-global/monster/humans/dark_apprentice.lua +++ b/data-otservbr-global/monster/humans/dark_apprentice.lua @@ -87,7 +87,7 @@ monster.loot = { { name = "blank rune", chance = 8125, maxCount = 3 }, { name = "health potion", chance = 2900 }, { name = "mana potion", chance = 2980 }, - { name = "reins", chance = 10 }, + { name = "reins", chance = 10, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humans/dark_magician.lua b/data-otservbr-global/monster/humans/dark_magician.lua index 4b2475a1060..0da26087499 100644 --- a/data-otservbr-global/monster/humans/dark_magician.lua +++ b/data-otservbr-global/monster/humans/dark_magician.lua @@ -88,7 +88,7 @@ monster.loot = { { name = "strong mana potion", chance = 2860 }, { name = "health potion", chance = 12000 }, { name = "mana potion", chance = 11900 }, - { name = "reins", chance = 20 }, + { name = "reins", chance = 20, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/humans/nomad.lua b/data-otservbr-global/monster/humans/nomad.lua index eca95f7b581..9c34282bf80 100644 --- a/data-otservbr-global/monster/humans/nomad.lua +++ b/data-otservbr-global/monster/humans/nomad.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 940 then - return -end - local mType = Game.createMonsterType("Nomad") local monster = {} diff --git a/data-otservbr-global/monster/humans/witch.lua b/data-otservbr-global/monster/humans/witch.lua index d51520b8687..a50e60cff46 100644 --- a/data-otservbr-global/monster/humans/witch.lua +++ b/data-otservbr-global/monster/humans/witch.lua @@ -97,7 +97,7 @@ monster.loot = { { name = "witch broom", chance = 10000 }, { name = "witch hat", chance = 80 }, { name = "stuffed toad", chance = 10 }, - { name = "bag of apple slices", chance = 920 }, + { name = "bag of apple slices", chance = 920, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/magicals/bonelord.lua b/data-otservbr-global/monster/magicals/bonelord.lua index 77753b196a1..d39fc0e9f5b 100644 --- a/data-otservbr-global/monster/magicals/bonelord.lua +++ b/data-otservbr-global/monster/magicals/bonelord.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Bonelord") local monster = {} diff --git a/data-otservbr-global/monster/mammals/horse.lua b/data-otservbr-global/monster/mammals/horse.lua index f106494dc50..e15dc79cd21 100644 --- a/data-otservbr-global/monster/mammals/horse.lua +++ b/data-otservbr-global/monster/mammals/horse.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 910 then - return -end - local mType = Game.createMonsterType("Horse") local monster = {} diff --git a/data-otservbr-global/monster/mammals/wolf.lua b/data-otservbr-global/monster/mammals/wolf.lua index f81f6244569..2b1a13734e9 100644 --- a/data-otservbr-global/monster/mammals/wolf.lua +++ b/data-otservbr-global/monster/mammals/wolf.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Wolf") local monster = {} diff --git a/data-otservbr-global/monster/plants/carniphila.lua b/data-otservbr-global/monster/plants/carniphila.lua index ac60ee69042..335007a9fe2 100644 --- a/data-otservbr-global/monster/plants/carniphila.lua +++ b/data-otservbr-global/monster/plants/carniphila.lua @@ -86,7 +86,7 @@ monster.loot = { { name = "shadow herb", chance = 880 }, { name = "seeds", chance = 490 }, { name = "carniphila seeds", chance = 4166 }, - { name = "carrot on a stick", chance = 170 }, + { name = "carrot on a stick", chance = 170, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/banshee.lua b/data-otservbr-global/monster/undeads/banshee.lua index 3859f54b565..ab81eed4468 100644 --- a/data-otservbr-global/monster/undeads/banshee.lua +++ b/data-otservbr-global/monster/undeads/banshee.lua @@ -110,7 +110,7 @@ monster.loot = { { name = "terra mantle", chance = 340 }, { name = "petrified scream", chance = 4150 }, { name = "hair of a banshee", chance = 4810 }, - { name = "sweet smelling bait", chance = 40 }, + { name = "sweet smelling bait", chance = 40, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/lich.lua b/data-otservbr-global/monster/undeads/lich.lua index 30cc8463ddc..84295ff2587 100644 --- a/data-otservbr-global/monster/undeads/lich.lua +++ b/data-otservbr-global/monster/undeads/lich.lua @@ -113,7 +113,7 @@ monster.loot = { { name = "strong mana potion", chance = 7500 }, { name = "lightning boots", chance = 200 }, { name = "small topaz", chance = 2430, maxCount = 3 }, - { name = "maxilla maximus", chance = 100 }, + { name = "maxilla maximus", chance = 100, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/putrid_mummy.lua b/data-otservbr-global/monster/undeads/putrid_mummy.lua index dd29eb90880..fa231991d7a 100644 --- a/data-otservbr-global/monster/undeads/putrid_mummy.lua +++ b/data-otservbr-global/monster/undeads/putrid_mummy.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1140 then + return +end + local mType = Game.createMonsterType("Putrid Mummy") local monster = {} diff --git a/data-otservbr-global/monster/undeads/ripper_spectre.lua b/data-otservbr-global/monster/undeads/ripper_spectre.lua index e8a2175a1f2..bac5df19394 100644 --- a/data-otservbr-global/monster/undeads/ripper_spectre.lua +++ b/data-otservbr-global/monster/undeads/ripper_spectre.lua @@ -85,9 +85,9 @@ monster.loot = { { name = "twin hooks", chance = 1170 }, { name = "springsprout rod", chance = 1210 }, { id = 30083, chance = 1890 }, -- green ectoplasm - { name = "coral brooch", chance = 1330 }, + { name = "coral brooch", chance = 1330, version = 1100 }, { name = "serpent sword", chance = 1950 }, - { name = "hexagonal ruby", chance = 800 }, + { name = "hexagonal ruby", chance = 800, version = 1100 }, { name = "assassin dagger", chance = 970 }, { name = "spike sword", chance = 530 }, { name = "wyvern fang", chance = 120 }, diff --git a/data-otservbr-global/monster/undeads/sorcerer's_apparition.lua b/data-otservbr-global/monster/undeads/sorcerer's_apparition.lua index 8cf059be62c..38803f4d849 100644 --- a/data-otservbr-global/monster/undeads/sorcerer's_apparition.lua +++ b/data-otservbr-global/monster/undeads/sorcerer's_apparition.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1240 then + return +end + local mType = Game.createMonsterType("Sorcerer's Apparition") local monster = {} diff --git a/data-otservbr-global/monster/undeads/tomb_servant.lua b/data-otservbr-global/monster/undeads/tomb_servant.lua index 3cca930ed30..7d7d64ffe3d 100644 --- a/data-otservbr-global/monster/undeads/tomb_servant.lua +++ b/data-otservbr-global/monster/undeads/tomb_servant.lua @@ -85,7 +85,7 @@ monster.loot = { { name = "bone shield", chance = 5300 }, { name = "worm", chance = 25325, maxCount = 12 }, { name = "half-digested piece of meat", chance = 1000 }, - { name = "fist on a stick", chance = 230 }, + { name = "fist on a stick", chance = 230, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/undead_cavebear.lua b/data-otservbr-global/monster/undeads/undead_cavebear.lua index 92370c87ca5..41b44d85dbf 100644 --- a/data-otservbr-global/monster/undeads/undead_cavebear.lua +++ b/data-otservbr-global/monster/undeads/undead_cavebear.lua @@ -76,8 +76,8 @@ monster.voices = { monster.loot = { { name = "gold coin", chance = 31820, maxCount = 80 }, - { name = "maxilla", chance = 13640 }, - { name = "cavebear skull", chance = 2600 }, + { name = "maxilla", chance = 13640, version = 1100 }, + { name = "cavebear skull", chance = 2600, version = 1100 }, { name = "health potion", chance = 1300 }, } diff --git a/data-otservbr-global/monster/undeads/vampire_bride.lua b/data-otservbr-global/monster/undeads/vampire_bride.lua index 191501f78b6..299cf5b8d76 100644 --- a/data-otservbr-global/monster/undeads/vampire_bride.lua +++ b/data-otservbr-global/monster/undeads/vampire_bride.lua @@ -96,7 +96,7 @@ monster.loot = { { id = 8923, chance = 970 }, -- velvet tapestry { name = "vampire teeth", chance = 10000 }, { name = "blood preservation", chance = 4950 }, - { name = "leather whip", chance = 20 }, + { name = "leather whip", chance = 20, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/vampire_viscount.lua b/data-otservbr-global/monster/undeads/vampire_viscount.lua index eb84adcb65e..54d901c31fe 100644 --- a/data-otservbr-global/monster/undeads/vampire_viscount.lua +++ b/data-otservbr-global/monster/undeads/vampire_viscount.lua @@ -90,8 +90,8 @@ monster.loot = { { name = "strong mana potion", chance = 8180 }, { name = "vampire teeth", chance = 7200 }, { name = "blood preservation", chance = 2910 }, - { name = "tooth file", chance = 6560 }, - { name = "vampire's cape chain", chance = 4460 }, + { name = "tooth file", chance = 6560, version = 1100 }, + { name = "vampire's cape chain", chance = 4460, version = 1100 }, } monster.attacks = { diff --git a/data-otservbr-global/monster/undeads/vibrant_phantom.lua b/data-otservbr-global/monster/undeads/vibrant_phantom.lua index 9f17bee76ee..1a0550056fe 100644 --- a/data-otservbr-global/monster/undeads/vibrant_phantom.lua +++ b/data-otservbr-global/monster/undeads/vibrant_phantom.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1240 then + return +end + local mType = Game.createMonsterType("Vibrant Phantom") local monster = {} diff --git a/data-otservbr-global/monster/vermins/bug.lua b/data-otservbr-global/monster/vermins/bug.lua index cc0ae2a137b..d6e9cdfc86e 100644 --- a/data-otservbr-global/monster/vermins/bug.lua +++ b/data-otservbr-global/monster/vermins/bug.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Bug") local monster = {} diff --git a/data-otservbr-global/monster/vermins/giant_spider.lua b/data-otservbr-global/monster/vermins/giant_spider.lua index a169896dcce..215f5884405 100644 --- a/data-otservbr-global/monster/vermins/giant_spider.lua +++ b/data-otservbr-global/monster/vermins/giant_spider.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Giant Spider") local monster = {} diff --git a/data-otservbr-global/monster/vermins/spider.lua b/data-otservbr-global/monster/vermins/spider.lua index 632ef61746d..bc27cdd73b9 100644 --- a/data-otservbr-global/monster/vermins/spider.lua +++ b/data-otservbr-global/monster/vermins/spider.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Spider") local monster = {} diff --git a/data-otservbr-global/monster/vermins/wasp.lua b/data-otservbr-global/monster/vermins/wasp.lua index 90db54ff2de..d89b7522eb8 100644 --- a/data-otservbr-global/monster/vermins/wasp.lua +++ b/data-otservbr-global/monster/vermins/wasp.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 944 then - return -end - local mType = Game.createMonsterType("Wasp") local monster = {} diff --git a/data-otservbr-global/npc/nomad.lua b/data-otservbr-global/npc/nomad.lua index d89a9674f8b..79dee9cc229 100644 --- a/data-otservbr-global/npc/nomad.lua +++ b/data-otservbr-global/npc/nomad.lua @@ -1,7 +1,3 @@ -if CLIENT_VERSION < 940 then - return -end - local internalNpcName = "Nomad" local npcType = Game.createNpcType(internalNpcName) local npcConfig = {} diff --git a/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua b/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua index ad737b276d3..dbc7c1d0dbf 100644 --- a/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua +++ b/data-otservbr-global/scripts/actions/adventurers_guild/adventurers_stone.lua @@ -99,5 +99,5 @@ function adventurersStone.onUse(player, item, fromPosition, target, toPosition, return true end -adventurersStone:id(16277) +adventurersStone:id(ITEM_ADVENTURERS_STONE) adventurersStone:register() diff --git a/data-otservbr-global/scripts/actions/quests/cults_of_tibia/misguided.lua b/data-otservbr-global/scripts/actions/quests/cults_of_tibia/misguided.lua index 3c555338d21..60cff50099d 100644 --- a/data-otservbr-global/scripts/actions/quests/cults_of_tibia/misguided.lua +++ b/data-otservbr-global/scripts/actions/quests/cults_of_tibia/misguided.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local function cleanMMap(frompos, topos) for _x = frompos.x, topos.x, 1 do for _y = frompos.y, topos.y, 1 do diff --git a/data-otservbr-global/scripts/creaturescripts/players/namelock.lua b/data-otservbr-global/scripts/creaturescripts/players/namelock.lua index c34eab6f181..4fce91d52d9 100644 --- a/data-otservbr-global/scripts/creaturescripts/players/namelock.lua +++ b/data-otservbr-global/scripts/creaturescripts/players/namelock.lua @@ -5,7 +5,7 @@ function CheckNamelock(player) end player:setMoveLocked(true) player:teleportTo(player:getTown():getTemplePosition()) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Your name has been locked for the following reason: " .. namelockReason .. ".") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Your name has been locked for the following reason: " .. namelockReason .. ".") player:openStore("extras") addPlayerEvent(sendRequestPurchaseData, 50, player, 65002, GameStore.ClientOfferTypes.CLIENT_STORE_OFFER_NAMECHANGE) addPlayerEvent(CheckNamelock, 30000, player) diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/boss_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/boss_kill.lua index 9fa65883199..9e0b7da812b 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/boss_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/boss_kill.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local taskBoss = { [0] = "the snapper", [1] = "hide", diff --git a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua index b2ba681d251..e1986d2513a 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/killing_in_the_name_of/monster_kill.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local function killCheck(player, targetName, taskName, taskStage, taskInfo, taskAltKillCount, taskkillCount) if player:getStorageValue(taskName) == taskStage then if table.contains(taskInfo, targetName) then diff --git a/data-otservbr-global/scripts/creaturescripts/quests/svargrond_arena/arena_kill.lua b/data-otservbr-global/scripts/creaturescripts/quests/svargrond_arena/arena_kill.lua index 674dc5d27d5..bb27dd4476f 100644 --- a/data-otservbr-global/scripts/creaturescripts/quests/svargrond_arena/arena_kill.lua +++ b/data-otservbr-global/scripts/creaturescripts/quests/svargrond_arena/arena_kill.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local deathEvent = CreatureEvent("SvargrondArenaBossDeath") function deathEvent.onDeath(creature, _corpse, _lastHitKiller, mostDamageKiller) local player = Player(mostDamageKiller) diff --git a/data-otservbr-global/scripts/globalevents/others/startup.lua b/data-otservbr-global/scripts/globalevents/others/startup.lua index 603e50a3ba1..4536e4c8004 100644 --- a/data-otservbr-global/scripts/globalevents/others/startup.lua +++ b/data-otservbr-global/scripts/globalevents/others/startup.lua @@ -3,54 +3,57 @@ function serverstartup.onStartup() logger.debug("Loading map attributes") logger.debug("Loaded {} npcs and spawned {} monsters", Game.getNpcCount(), Game.getMonsterCount()) logger.debug("Loaded {} towns with {} houses in total", #Game.getTowns(), #Game.getHouses()) - -- Sign table - loadLuaMapSign(SignTable) - logger.debug("Loaded {} signs in the map", #SignTable) - -- Book/Document table - loadLuaMapBookDocument(BookDocumentTable) - - -- Action and unique tables - -- Chest table - loadLuaMapAction(ChestAction) - loadLuaMapUnique(ChestUnique) - -- Corpse table - loadLuaMapAction(CorpseAction) - loadLuaMapUnique(CorpseUnique) - -- Doors key table - loadLuaMapAction(KeyDoorAction) - -- Doors level table - loadLuaMapAction(LevelDoorAction) - -- Doors quest table - loadLuaMapAction(QuestDoorAction) - loadLuaMapUnique(QuestDoorUnique) - -- Item table - loadLuaMapAction(ItemAction) - loadLuaMapUnique(ItemUnique) - -- Item daily reward table - -- This is temporary disabled > loadLuaMapAction(DailyRewardAction) - -- Item unmovable table - loadLuaMapAction(ItemUnmovableAction) - -- Lever table - loadLuaMapAction(LeverAction) - loadLuaMapUnique(LeverUnique) - -- Teleport (magic forcefields) table - loadLuaMapAction(TeleportAction) - loadLuaMapUnique(TeleportUnique) - -- Teleport item table - loadLuaMapAction(TeleportItemAction) - loadLuaMapUnique(TeleportItemUnique) - -- Tile table - loadLuaMapAction(TileAction) - loadLuaMapUnique(TileUnique) - -- Tile pick table - loadLuaMapAction(TilePickAction) - -- Create new item on map - CreateMapItem(CreateItemOnMap) - -- Update old quest storage keys - updateKeysStorage(QuestKeysUpdate) - - logger.debug("Loaded all actions in the map") - logger.debug("Loaded all uniques in the map") + if CLIENT_VERSION >= 1100 then + -- Sign table + loadLuaMapSign(SignTable) + + logger.debug("Loaded {} signs in the map", #SignTable) + -- Book/Document table + loadLuaMapBookDocument(BookDocumentTable) + + -- Action and unique tables + -- Chest table + loadLuaMapAction(ChestAction) + loadLuaMapUnique(ChestUnique) + -- Corpse table + loadLuaMapAction(CorpseAction) + loadLuaMapUnique(CorpseUnique) + -- Doors key table + loadLuaMapAction(KeyDoorAction) + -- Doors level table + loadLuaMapAction(LevelDoorAction) + -- Doors quest table + loadLuaMapAction(QuestDoorAction) + loadLuaMapUnique(QuestDoorUnique) + -- Item table + loadLuaMapAction(ItemAction) + loadLuaMapUnique(ItemUnique) + -- Item daily reward table + -- This is temporary disabled > loadLuaMapAction(DailyRewardAction) + -- Item unmovable table + loadLuaMapAction(ItemUnmovableAction) + -- Lever table + loadLuaMapAction(LeverAction) + loadLuaMapUnique(LeverUnique) + -- Teleport (magic forcefields) table + loadLuaMapAction(TeleportAction) + loadLuaMapUnique(TeleportUnique) + -- Teleport item table + loadLuaMapAction(TeleportItemAction) + loadLuaMapUnique(TeleportItemUnique) + -- Tile table + loadLuaMapAction(TileAction) + loadLuaMapUnique(TileUnique) + -- Tile pick table + loadLuaMapAction(TilePickAction) + -- Create new item on map + CreateMapItem(CreateItemOnMap) + -- Update old quest storage keys + updateKeysStorage(QuestKeysUpdate) + + logger.debug("Loaded all actions in the map") + logger.debug("Loaded all uniques in the map") + end for i = 1, #startupGlobalStorages do Game.setStorageValue(startupGlobalStorages[i], 0) diff --git a/data-otservbr-global/scripts/globalevents/quests/grimvale_feroxa.lua b/data-otservbr-global/scripts/globalevents/quests/grimvale_feroxa.lua index d2f97956d6c..1f47e43b060 100644 --- a/data-otservbr-global/scripts/globalevents/quests/grimvale_feroxa.lua +++ b/data-otservbr-global/scripts/globalevents/quests/grimvale_feroxa.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local spawnByDay = true local spawnDay = 13 local currentDay = os.date("%d") diff --git a/data-otservbr-global/scripts/globalevents/worldchanges/dream_courts_worldchange.lua b/data-otservbr-global/scripts/globalevents/worldchanges/dream_courts_worldchange.lua index 090887b9540..cf7cf77f5c0 100644 --- a/data-otservbr-global/scripts/globalevents/worldchanges/dream_courts_worldchange.lua +++ b/data-otservbr-global/scripts/globalevents/worldchanges/dream_courts_worldchange.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local config = { ["Monday"] = "Plagueroot", ["Tuesday"] = "Malofur_Mangrinder", diff --git a/data-otservbr-global/scripts/globalevents/worldchanges/fury_gates.lua b/data-otservbr-global/scripts/globalevents/worldchanges/fury_gates.lua index a50cc253016..e1c3c56ac55 100644 --- a/data-otservbr-global/scripts/globalevents/worldchanges/fury_gates.lua +++ b/data-otservbr-global/scripts/globalevents/worldchanges/fury_gates.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local gates = { -- Ab'dendriel [1] = { diff --git a/data-otservbr-global/scripts/globalevents/worldchanges/nightmare_isle.lua b/data-otservbr-global/scripts/globalevents/worldchanges/nightmare_isle.lua index fb0d3b6f672..86820c98fa4 100644 --- a/data-otservbr-global/scripts/globalevents/worldchanges/nightmare_isle.lua +++ b/data-otservbr-global/scripts/globalevents/worldchanges/nightmare_isle.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local config = { -- ankrahmun - north [1] = { diff --git a/data-otservbr-global/scripts/globalevents/worldchanges/yasir.lua b/data-otservbr-global/scripts/globalevents/worldchanges/yasir.lua index 5807c44387b..7a6b6b7bcef 100644 --- a/data-otservbr-global/scripts/globalevents/worldchanges/yasir.lua +++ b/data-otservbr-global/scripts/globalevents/worldchanges/yasir.lua @@ -1,3 +1,7 @@ +if CLIENT_VERSION < 1100 then + return +end + local config = { -- Ankrahmun [1] = { diff --git a/data-otservbr-global/scripts/lib/register_monster_type.lua b/data-otservbr-global/scripts/lib/register_monster_type.lua index c62ca0281d7..ae22d66eda4 100644 --- a/data-otservbr-global/scripts/lib/register_monster_type.lua +++ b/data-otservbr-global/scripts/lib/register_monster_type.lua @@ -343,6 +343,10 @@ registerMonsterType.loot = function(mtype, mask) SortLootByChance(mask.loot) local lootError = false for _, loot in pairs(mask.loot) do + if loot.version and CLIENT_VERSION < loot.version then + goto continue + end + local parent = Loot() if loot.name then if not parent:setIdFromName(loot.name) then @@ -466,12 +470,15 @@ registerMonsterType.loot = function(mtype, mask) end end mtype:addLoot(parent) + + ::continue:: end if lootError then logger.warn("[registerMonsterType.loot] - Monster: {} loot could not correctly be load", mtype:name()) end end end + local playerElements = { COMBAT_PHYSICALDAMAGE, COMBAT_ENERGYDAMAGE, COMBAT_EARTHDAMAGE, COMBAT_FIREDAMAGE, COMBAT_ICEDAMAGE, COMBAT_HOLYDAMAGE, COMBAT_DEATHDAMAGE } registerMonsterType.elements = function(mtype, mask) local min = configManager.getNumber(configKeys.MIN_ELEMENTAL_RESISTANCE) diff --git a/data-otservbr-global/scripts/movements/others/tiles.lua b/data-otservbr-global/scripts/movements/others/tiles.lua index b797f0380a3..f20f4f48de0 100644 --- a/data-otservbr-global/scripts/movements/others/tiles.lua +++ b/data-otservbr-global/scripts/movements/others/tiles.lua @@ -33,13 +33,18 @@ function tiles.onStepIn(creature, item, position, fromPosition) if depotItem ~= nil then local depotItems = 0 + local message = "" for id = 1, configManager.getNumber(configKeys.DEPOT_BOXES) do depotItems = depotItems + player:getDepotChest(id, true):getItemHoldingCount() end - - player:sendTextMessage(MESSAGE_FAILURE, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") .. "\ + if CLIENT_VERSION >= 1100 then + player:sendTextMessage(MESSAGE_STATUS, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") .. "\ Your supply stash contains " .. player:getStashCount() .. " item" .. (player:getStashCount() > 1 and "s." or ".")) - player:setSpecialContainersAvailable(true, true, true) + player:setSpecialContainersAvailable(true, true, true) + else + message = "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") + player:sendTextMessage(MESSAGE_STATUS, message) + end return true end end diff --git a/data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua b/data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua index 7f35b07170e..5c8b3fc0f94 100644 --- a/data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua +++ b/data-otservbr-global/scripts/quests/threatened_dreams/event-raven_herb_bush.lua @@ -28,9 +28,9 @@ function createRavenHerb.onPeriodChange(period, light) end for index, item in pairs(config) do if item.createItem == period then -- Adding - local createItem = Game.createItem(item.bushId, 1, item.pos) - createItem:setActionId(item.storage) - if createItem then + local newItem = Game.createItem(item.bushId, 1, item.pos) + if newItem then + newItem:setActionId(item.storage) item.pos:sendMagicEffect(CONST_ME_BIGPLANTS) end elseif item.removeItem == period then -- Removing diff --git a/data-otservbr-global/scripts/spells/monster/exploding_cask.lua b/data-otservbr-global/scripts/spells/monster/exploding_cask.lua index 5448d0733ff..5ec45591fa6 100644 --- a/data-otservbr-global/scripts/spells/monster/exploding_cask.lua +++ b/data-otservbr-global/scripts/spells/monster/exploding_cask.lua @@ -29,15 +29,13 @@ combat:setCallback(CALLBACK_PARAM_TARGETCREATURE, "onTargetCreature") local function createBarrel() local template = Game.createItem(barrelId, 1) - - template:setDuration(3) - template:stopDecay() - + if template then + template:setDuration(3) + template:stopDecay() + end return template end -createBarrel() - local function explodeBomb(position, creatureId) local var = {} @@ -66,6 +64,9 @@ function onTargetCreature(creature, target) local position = target:getPosition() local template = createBarrel() + if not template then + return false + end template:setOwner(creature:getId()) local tile = Tile(position) diff --git a/data/global.lua b/data/global.lua index 2d5ffc31cf0..f58c574dc39 100644 --- a/data/global.lua +++ b/data/global.lua @@ -1,10 +1,12 @@ math.randomseed(os.time()) dofile(DATA_DIRECTORY .. "/lib/lib.lua") -local startupFile = io.open(DATA_DIRECTORY .. "/startup/startup.lua", "r") -if startupFile ~= nil then - io.close(startupFile) - dofile(DATA_DIRECTORY .. "/startup/startup.lua") +if CLIENT_VERSION >= 1100 then + local startupFile = io.open(DATA_DIRECTORY .. "/startup/startup.lua", "r") + if startupFile ~= nil then + io.close(startupFile) + dofile(DATA_DIRECTORY .. "/startup/startup.lua") + end end function IsRunningGlobalDatapack() diff --git a/data/items/860/items.otb b/data/items/860/items.otb new file mode 100644 index 00000000000..b5f75556ac8 Binary files /dev/null and b/data/items/860/items.otb differ diff --git a/data/items/860/items.xml b/data/items/860/items.xml new file mode 100644 index 00000000000..a306a0b28bf --- /dev/null +++ b/data/items/860/items.xml @@ -0,0 +1,26074 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/modules/scripts/blessings/blessings.lua b/data/modules/scripts/blessings/blessings.lua index adfa364e7e1..c4b7f29ddbd 100644 --- a/data/modules/scripts/blessings/blessings.lua +++ b/data/modules/scripts/blessings/blessings.lua @@ -77,6 +77,9 @@ Blessings.S_Packet = { } function onRecvbyte(player, msg, byte) + if CLIENT_VERSION < 1100 then + return + end if byte == Blessings.C_Packet.OpenWindow then Blessings.sendBlessDialog(player) end diff --git a/data/scripts/reward_chest/boss_death.lua b/data/scripts/reward_chest/boss_death.lua index 04bb12b84bc..98acb6bdff7 100644 --- a/data/scripts/reward_chest/boss_death.lua +++ b/data/scripts/reward_chest/boss_death.lua @@ -111,7 +111,8 @@ function bossDeath.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUn reward:addRewardBossItems(playerLoot) if con.player then - local lootMessage = ("The following items dropped by %s are available in your reward chest: %s"):format(creature:getName(), reward:getContentDescription()) + local oldProtocol = player:getClient().version < 1200 + local lootMessage = ("The following items dropped by %s are available in your reward chest: %s"):format(creature:getName(), reward:getContentDescription(oldProtocol)) if rolls > 1 then lootMessage = lootMessage .. " (boss bonus)" end diff --git a/data/scripts/talkactions/gm/ban.lua b/data/scripts/talkactions/gm/ban.lua index 8b46394a3e2..0ab07040333 100644 --- a/data/scripts/talkactions/gm/ban.lua +++ b/data/scripts/talkactions/gm/ban.lua @@ -37,11 +37,11 @@ function ban.onSay(player, words, param) local target = Player(name) if target then local text = target:getName() .. " has been banned" - player:sendTextMessage(MESSAGE_ADMINISTRADOR, text) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, text) Webhook.sendMessage("Player Banned", text .. " reason: " .. reason .. ". (by: " .. player:getName() .. ")", WEBHOOK_COLOR_YELLOW, announcementChannels["serverAnnouncements"]) target:remove() else - player:sendTextMessage(MESSAGE_ADMINISTRADOR, name .. " has been banned.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, name .. " has been banned.") end end diff --git a/data/scripts/talkactions/gm/broadcast.lua b/data/scripts/talkactions/gm/broadcast.lua index 021f218ebe3..dffbe1bb0e9 100644 --- a/data/scripts/talkactions/gm/broadcast.lua +++ b/data/scripts/talkactions/gm/broadcast.lua @@ -5,7 +5,7 @@ function Broadcast(text, filter) if filter and not filter(targetPlayer) then goto continue end - targetPlayer:sendTextMessage(MESSAGE_ADMINISTRADOR, text) + targetPlayer:sendTextMessage(MESSAGE_ADMINISTRATOR, text) ::continue:: end end diff --git a/data/scripts/talkactions/gm/clean.lua b/data/scripts/talkactions/gm/clean.lua index 6fe5f3cddfa..18ab149e216 100644 --- a/data/scripts/talkactions/gm/clean.lua +++ b/data/scripts/talkactions/gm/clean.lua @@ -6,7 +6,7 @@ function clean.onSay(player, words, param) local itemCount = cleanMap() if itemCount ~= 0 then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Cleaned " .. itemCount .. " item" .. (itemCount > 1 and "s" or "") .. " from the map.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Cleaned " .. itemCount .. " item" .. (itemCount > 1 and "s" or "") .. " from the map.") end return true end diff --git a/data/scripts/talkactions/gm/mc_check.lua b/data/scripts/talkactions/gm/mc_check.lua index c93661053e4..1815b7fdc9d 100644 --- a/data/scripts/talkactions/gm/mc_check.lua +++ b/data/scripts/talkactions/gm/mc_check.lua @@ -4,7 +4,7 @@ function mcCheck.onSay(player, words, param) -- create log logCommand(player, words, param) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Multiclient Check List:") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Multiclient Check List:") local ipList = {} local players = Game.getPlayers() for i = 1, #players do @@ -29,7 +29,7 @@ function mcCheck.onSay(player, words, param) tmpPlayer = list[i] message = ("%s, %s [%d]"):format(message, tmpPlayer:getName(), tmpPlayer:getLevel()) end - player:sendTextMessage(MESSAGE_ADMINISTRADOR, message .. ".") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, message .. ".") end end return true diff --git a/data/scripts/talkactions/gm/namelock.lua b/data/scripts/talkactions/gm/namelock.lua index 1b204759e02..27e41710096 100644 --- a/data/scripts/talkactions/gm/namelock.lua +++ b/data/scripts/talkactions/gm/namelock.lua @@ -33,7 +33,7 @@ function namelock.onSay(player, words, param) target:kv():set("namelock", reason) local text = target:getName() .. " has been namelocked" logger.info(text .. ", reason: " .. reason) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, text) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, text) Webhook.sendMessage("Player Namelocked", text .. " reason: " .. reason .. ".", WEBHOOK_COLOR_YELLOW, announcementChannels["serverAnnouncements"]) if online then CheckNamelock(target) diff --git a/data/scripts/talkactions/gm/unban.lua b/data/scripts/talkactions/gm/unban.lua index 264335eeec7..3d1caba8b77 100644 --- a/data/scripts/talkactions/gm/unban.lua +++ b/data/scripts/talkactions/gm/unban.lua @@ -18,7 +18,7 @@ function unban.onSay(player, words, param) db.asyncQuery("DELETE FROM `ip_bans` WHERE `ip` = " .. Result.getNumber(resultId, "lastip")) Result.free(resultId) local text = param .. " has been unbanned." - player:sendTextMessage(MESSAGE_ADMINISTRADOR, text) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, text) Webhook.sendMessage("Player Unbanned", text .. " (by: " .. player:getName() .. ")", WEBHOOK_COLOR_YELLOW, announcementChannels["serverAnnouncements"]) return true end diff --git a/data/scripts/talkactions/god/add_bosstiary_kills.lua b/data/scripts/talkactions/god/add_bosstiary_kills.lua index 4e5aecdfbd4..a6463c26162 100644 --- a/data/scripts/talkactions/god/add_bosstiary_kills.lua +++ b/data/scripts/talkactions/god/add_bosstiary_kills.lua @@ -24,9 +24,9 @@ function talkaction.onSay(player, words, param) local message = "Added received kills: " .. kills .. ", for boss: " .. monsterName if target == player then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, message .. " to yourself.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, message .. " to yourself.") else - player:sendTextMessage(MESSAGE_ADMINISTRADOR, message .. " to player: " .. targetName) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, message .. " to player: " .. targetName) target:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You received kills: " .. kills .. ", to boss: " .. monsterName) end target:addBosstiaryKill(monsterName, kills) diff --git a/data/scripts/talkactions/god/add_mount.lua b/data/scripts/talkactions/god/add_mount.lua index 813f46f38a9..b8b2f00dda1 100644 --- a/data/scripts/talkactions/god/add_mount.lua +++ b/data/scripts/talkactions/god/add_mount.lua @@ -22,8 +22,8 @@ function addOutfit.onSay(player, words, param) if target then local mount = tonumber(split[2]) target:addMount(mount) - target:sendTextMessage(MESSAGE_ADMINISTRADOR, "" .. player:getName() .. " has been added a new mount for you.") - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "You have sucessfull added mount " .. mount .. " to the player " .. target:getName() .. ".") + target:sendTextMessage(MESSAGE_ADMINISTRATOR, "" .. player:getName() .. " has been added a new mount for you.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "You have sucessfull added mount " .. mount .. " to the player " .. target:getName() .. ".") if printConsole then logger.info("[addOutfit.onSay] - Player: {} has been added mount: {} to the player: {}", player:getName(), lookType, target:getName()) end diff --git a/data/scripts/talkactions/god/add_outfit.lua b/data/scripts/talkactions/god/add_outfit.lua index 825aaf84c7e..a27fb4f8a18 100644 --- a/data/scripts/talkactions/god/add_outfit.lua +++ b/data/scripts/talkactions/god/add_outfit.lua @@ -24,8 +24,8 @@ function addOutfit.onSay(player, words, param) if target then local lookType = tonumber(split[2]) target:addOutfit(lookType) - target:sendTextMessage(MESSAGE_ADMINISTRADOR, "" .. player:getName() .. " has been added a new outfit for you.") - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "You have sucessfull added looktype " .. lookType .. " to the player " .. target:getName() .. ".") + target:sendTextMessage(MESSAGE_ADMINISTRATOR, "" .. player:getName() .. " has been added a new outfit for you.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "You have sucessfull added looktype " .. lookType .. " to the player " .. target:getName() .. ".") if printConsole then logger.info("[addOutfit.onSay] - Player: {} has been added looktype: {} to the player: {}", player:getName(), lookType, target:getName()) end diff --git a/data/scripts/talkactions/god/close_server.lua b/data/scripts/talkactions/god/close_server.lua index 930cde5be79..ad8cfadef2c 100644 --- a/data/scripts/talkactions/god/close_server.lua +++ b/data/scripts/talkactions/god/close_server.lua @@ -19,10 +19,10 @@ function closeServer.onSay(player, words, param) end elseif param == "maintainance" then Game.setGameState(GAME_STATE_MAINTAIN) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is set to maintenance mode.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Server is set to maintenance mode.") else Game.setGameState(GAME_STATE_CLOSED) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is now closed.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Server is now closed.") Webhook.sendMessage(":yellow_square: Server was closed by: **" .. player:getName() .. "**", announcementChannels["serverAnnouncements"]) end return true diff --git a/data/scripts/talkactions/god/ip_ban.lua b/data/scripts/talkactions/god/ip_ban.lua index 9826d726c87..09269de446f 100644 --- a/data/scripts/talkactions/god/ip_ban.lua +++ b/data/scripts/talkactions/god/ip_ban.lua @@ -32,14 +32,14 @@ function ipBan.onSay(player, words, param) resultId = db.storeQuery("SELECT 1 FROM `ip_bans` WHERE `ip` = " .. targetIp) if resultId ~= false then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, targetName .. " is already IP banned.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, targetName .. " is already IP banned.") Result.free(resultId) return true end local timeNow = os.time() db.query("INSERT INTO `ip_bans` (`ip`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES (" .. targetIp .. ", '', " .. timeNow .. ", " .. timeNow + (ipBanDays * 86400) .. ", " .. player:getGuid() .. ")") - player:sendTextMessage(MESSAGE_ADMINISTRADOR, targetName .. " has been IP banned.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, targetName .. " has been IP banned.") return true end diff --git a/data/scripts/talkactions/god/open_server.lua b/data/scripts/talkactions/god/open_server.lua index 84f0b4afe49..83ac47878a1 100644 --- a/data/scripts/talkactions/god/open_server.lua +++ b/data/scripts/talkactions/god/open_server.lua @@ -5,7 +5,7 @@ function openServer.onSay(player, words, param) logCommand(player, words, param) Game.setGameState(GAME_STATE_NORMAL) - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is now open.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Server is now open.") Webhook.sendMessage(":green_circle: Server was opened by: **" .. player:getName() .. "**", announcementChannels["serverAnnouncements"]) return true end diff --git a/data/scripts/talkactions/god/raids.lua b/data/scripts/talkactions/god/raids.lua index 15f21801293..b9b4b909668 100644 --- a/data/scripts/talkactions/god/raids.lua +++ b/data/scripts/talkactions/god/raids.lua @@ -12,18 +12,18 @@ function startRaid.onSay(player, words, param) if Raid.registry[param] then local raid = Raid.registry[param] if raid:tryStart(true) then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Raid " .. param .. " started.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Raid " .. param .. " started.") else - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Raid " .. param .. " could not be started.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Raid " .. param .. " could not be started.") end return true end local returnValue = Game.startRaid(param) if returnValue ~= RETURNVALUE_NOERROR then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, Game.getReturnMessage(returnValue)) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, Game.getReturnMessage(returnValue)) else - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Raid started.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Raid started.") end return true end diff --git a/data/scripts/talkactions/god/reload.lua b/data/scripts/talkactions/god/reload.lua index 290288a19df..6c1cffcaa1c 100644 --- a/data/scripts/talkactions/god/reload.lua +++ b/data/scripts/talkactions/god/reload.lua @@ -72,7 +72,7 @@ function Player.reloadTalkaction(self, words, param) saveServer() SaveHirelings() logger.info("Saved Hirelings") - self:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is saved.. Now will reload configs!") + self:sendTextMessage(MESSAGE_ADMINISTRATOR, "Server is saved.. Now will reload configs!") Game.reload(reloadType) self:sendTextMessage(MESSAGE_LOOK, string.format("Reloaded %s.", param:lower())) diff --git a/data/scripts/talkactions/god/save.lua b/data/scripts/talkactions/god/save.lua index ac2ad633d32..56a75359f49 100644 --- a/data/scripts/talkactions/god/save.lua +++ b/data/scripts/talkactions/god/save.lua @@ -21,7 +21,7 @@ function save.onSay(player, words, param) saveServer() SaveHirelings() logger.info("Saved Hirelings") - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Server is saved ...") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Server is saved ...") end end diff --git a/data/scripts/talkactions/god/start_raid.lua b/data/scripts/talkactions/god/start_raid.lua index b43e77c061a..cbd3fe24a79 100644 --- a/data/scripts/talkactions/god/start_raid.lua +++ b/data/scripts/talkactions/god/start_raid.lua @@ -11,9 +11,9 @@ function startRaid.onSay(player, words, param) local returnValue = Game.startRaid(param) if returnValue ~= RETURNVALUE_NOERROR then - player:sendTextMessage(MESSAGE_ADMINISTRADOR, Game.getReturnMessage(returnValue)) + player:sendTextMessage(MESSAGE_ADMINISTRATOR, Game.getReturnMessage(returnValue)) else - player:sendTextMessage(MESSAGE_ADMINISTRADOR, "Raid started.") + player:sendTextMessage(MESSAGE_ADMINISTRATOR, "Raid started.") end return true end diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 9e13e8b05fd..ba629b5fa51 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -59,8 +59,8 @@ int CanaryServer::run() { [this] { try { loadConfigLua(); - - logger.info("Server protocol: {}.{}{}", CLIENT_VERSION_UPPER, CLIENT_VERSION_LOWER, g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__) ? " and 10x allowed!" : ""); + auto allowedOutdated1100 = g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__) && CLIENT_VERSION >= 1100; + logger.info("Server protocol: {}.{}{}", CLIENT_VERSION_UPPER, CLIENT_VERSION_LOWER, allowedOutdated1100 ? " and 10x allowed!" : ""); metrics::Options metricsOptions; metricsOptions.enablePrometheusExporter = g_configManager().getBoolean(METRICS_ENABLE_PROMETHEUS, __FUNCTION__); if (metricsOptions.enablePrometheusExporter) { @@ -165,10 +165,12 @@ void CanaryServer::loadMaps() const { try { g_game().loadMainMap(g_configManager().getString(MAP_NAME, __FUNCTION__)); +#if CLIENT_VERSION >= 1100 // If "mapCustomEnabled" is true on config.lua, then load the custom map if (g_configManager().getBoolean(TOGGLE_MAP_CUSTOM, __FUNCTION__)) { g_game().loadCustomMaps(g_configManager().getString(DATA_DIRECTORY, __FUNCTION__) + "/world/custom/"); } +#endif Zone::refreshAll(); } catch (const std::exception &err) { throw FailedToInitializeCanary(err.what()); @@ -341,8 +343,12 @@ void CanaryServer::loadModules() { } auto coreFolder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__); - // Load items dependencies +// Load items dependencies +#if CLIENT_VERSION < 1100 + modulesLoadHelper(Item::items.loadFromOtb("data/items/" + std::to_string(CLIENT_VERSION) + "/items.otb"), "items.otb"); +#else modulesLoadHelper((g_game().loadAppearanceProtobuf(coreFolder + "/items/appearances.dat") == ERROR_NONE), "appearances.dat"); +#endif modulesLoadHelper(Item::items.loadFromXml(), "items.xml"); const auto datapackFolder = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); diff --git a/src/config/config_definitions.hpp b/src/config/config_definitions.hpp index b3f1e51a739..39a2a0a8f4e 100644 --- a/src/config/config_definitions.hpp +++ b/src/config/config_definitions.hpp @@ -301,6 +301,7 @@ enum ConfigKey_t : uint16_t { VIP_STAY_ONLINE, VIP_SYSTEM_ENABLED, WARN_UNSAFE_SCRIPTS, + WARN_UNREGISTERED_DAT_INFO, WEATHER_RAIN, WEATHER_THUNDER, WEEK_KILLS_TO_RED, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 2287a3ed0a4..15ade76c2a7 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -95,6 +95,7 @@ bool ConfigManager::load() { loadBoolConfig(L, EMOTE_SPELLS, "emoteSpells", false); loadBoolConfig(L, STAMINA_SYSTEM, "staminaSystem", true); loadBoolConfig(L, WARN_UNSAFE_SCRIPTS, "warnUnsafeScripts", true); + loadBoolConfig(L, WARN_UNREGISTERED_DAT_INFO, "warnUnregisteredDatInfo", false); loadBoolConfig(L, CONVERT_UNSAFE_SCRIPTS, "convertUnsafeScripts", true); loadBoolConfig(L, CLASSIC_ATTACK_SPEED, "classicAttackSpeed", false); loadBoolConfig(L, TOGGLE_ATTACK_SPEED_ONFIST, "toggleAttackSpeedOnFist", false); diff --git a/src/core.hpp b/src/core.hpp index 8ca52d107da..4b8c69eb761 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -15,7 +15,7 @@ static constexpr auto AUTHENTICATOR_PERIOD = 30U; // SERVER_MAJOR_VERSION is the actual full version of the server, including minor and patch numbers. // This is intended for internal use to identify the exact state of the server (release) software. static constexpr auto SERVER_RELEASE_VERSION = "3.1.2"; -static constexpr auto CLIENT_VERSION = 1332; - +// Supported versions: 860 and 11.00/13.32 +#define CLIENT_VERSION 860 #define CLIENT_VERSION_UPPER (CLIENT_VERSION / 100) #define CLIENT_VERSION_LOWER (CLIENT_VERSION % 100) diff --git a/src/creatures/appearance/mounts/mounts.cpp b/src/creatures/appearance/mounts/mounts.cpp index e28b34ea7d4..a21e11f5cf4 100644 --- a/src/creatures/appearance/mounts/mounts.cpp +++ b/src/creatures/appearance/mounts/mounts.cpp @@ -9,10 +9,12 @@ #include "pch.hpp" -#include "creatures/appearance/mounts/mounts.hpp" -#include "game/game.hpp" -#include "utils/pugicast.hpp" -#include "utils/tools.hpp" +#if CLIENT_VERSION >= 870 + + #include "creatures/appearance/mounts/mounts.hpp" + #include "game/game.hpp" + #include "utils/pugicast.hpp" + #include "utils/tools.hpp" bool Mounts::reload() { mounts.clear(); @@ -30,7 +32,7 @@ bool Mounts::loadFromXml() { for (auto mountNode : doc.child("mounts").children()) { uint16_t lookType = pugi::cast(mountNode.attribute("clientid").value()); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("{} - An unregistered creature mount with id '{}' was blocked to prevent client crash.", __FUNCTION__, lookType); continue; } @@ -72,3 +74,5 @@ std::shared_ptr Mounts::getMountByClientID(uint16_t clientId) { return it != mounts.end() ? *it : nullptr; // Returning the shared_ptr to the Mount object } + +#endif diff --git a/src/creatures/appearance/mounts/mounts.hpp b/src/creatures/appearance/mounts/mounts.hpp index fea303203b6..a471309f1e3 100644 --- a/src/creatures/appearance/mounts/mounts.hpp +++ b/src/creatures/appearance/mounts/mounts.hpp @@ -9,6 +9,8 @@ #pragma once +#if CLIENT_VERSION >= 870 + struct Mount { Mount(uint8_t initId, uint16_t initClientId, std::string initName, int32_t initSpeed, bool initPremium, std::string initType) : name(initName), speed(initSpeed), clientId(initClientId), id(initId), premium(initPremium), @@ -37,3 +39,5 @@ class Mounts { private: std::vector> mounts; }; + +#endif diff --git a/src/creatures/appearance/outfit/outfit.cpp b/src/creatures/appearance/outfit/outfit.cpp index 5425f7fed31..67cd726b0ca 100644 --- a/src/creatures/appearance/outfit/outfit.cpp +++ b/src/creatures/appearance/outfit/outfit.cpp @@ -46,12 +46,13 @@ bool Outfits::loadFromXml() { continue; } - if (uint16_t lookType = pugi::cast(lookTypeAttribute.value()); - g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 - && !g_game().isLookTypeRegistered(lookType)) { + uint16_t lookType = pugi::cast(lookTypeAttribute.value()); +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && lookType && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("[Outfits::loadFromXml] An unregistered creature looktype type with id '{}' was ignored to prevent client crash.", lookType); continue; } +#endif outfits[type].emplace_back(std::make_shared( outfitNode.attribute("name").as_string(), diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index f1e2ec6e6d1..8629b84bc26 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -2268,10 +2268,13 @@ void ConditionOutfit::serialize(PropWriteStream &propWriteStream) { } bool ConditionOutfit::startCondition(std::shared_ptr creature) { - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - g_logger().warn("[ConditionOutfit::startCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); + const auto lookType = outfit.lookType; +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { + g_logger().warn("[ConditionOutfit::startCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); return false; } +#endif if ((outfit.lookType == 0 && outfit.lookTypeEx == 0) && !monsterName.empty()) { const auto monsterType = g_monsters().getMonsterType(monsterName); @@ -2300,10 +2303,13 @@ void ConditionOutfit::endCondition(std::shared_ptr creature) { } void ConditionOutfit::addCondition(std::shared_ptr creature, const std::shared_ptr addCondition) { - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { + const auto lookType = outfit.lookType; +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { g_logger().warn("[ConditionOutfit::addCondition] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); return; } +#endif if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); @@ -2449,12 +2455,14 @@ void ConditionSpellCooldown::addCondition(std::shared_ptr creature, co if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); +#if CLIENT_VERSION >= 870 if (subId != 0 && ticks > 0) { std::shared_ptr player = creature->getPlayer(); if (player) { player->sendSpellCooldown(subId, ticks); } } +#endif } } @@ -2462,13 +2470,14 @@ bool ConditionSpellCooldown::startCondition(std::shared_ptr creature) if (!Condition::startCondition(creature)) { return false; } - +#if CLIENT_VERSION >= 870 if (subId != 0 && ticks > 0) { std::shared_ptr player = creature->getPlayer(); if (player) { player->sendSpellCooldown(subId, ticks); } } +#endif return true; } @@ -2480,12 +2489,14 @@ void ConditionSpellGroupCooldown::addCondition(std::shared_ptr creatur if (updateCondition(addCondition)) { setTicks(addCondition->getTicks()); +#if CLIENT_VERSION >= 870 if (subId != 0 && ticks > 0) { std::shared_ptr player = creature->getPlayer(); if (player) { player->sendSpellGroupCooldown(static_cast(subId), ticks); } } +#endif } } @@ -2494,11 +2505,13 @@ bool ConditionSpellGroupCooldown::startCondition(std::shared_ptr creat return false; } +#if CLIENT_VERSION >= 870 if (subId != 0 && ticks > 0) { std::shared_ptr player = creature->getPlayer(); if (player) { player->sendSpellGroupCooldown(static_cast(subId), ticks); } } +#endif return true; } diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index ee1f390b9a2..3ce02a98015 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -379,10 +379,18 @@ enum Slots_t : uint8_t { CONST_SLOT_FEET = 8, CONST_SLOT_RING = 9, CONST_SLOT_AMMO = 10, - CONST_SLOT_STORE_INBOX = 11, CONST_SLOT_FIRST = CONST_SLOT_HEAD, +#if CLIENT_VERSION >= 1092 + CONST_SLOT_STORE_INBOX = 11, CONST_SLOT_LAST = CONST_SLOT_STORE_INBOX, +#elif CLIENT_VERSION >= 953 && CLIENT_VERSION < 1080 + CONST_SLOT_PURSE = 11, + CONST_SLOT_LAST = CONST_SLOT_PURSE, +#else + CONST_SLOT_STORE_INBOX = CONST_SLOT_AMMO, + CONST_SLOT_LAST = CONST_SLOT_AMMO, +#endif }; enum charmRune_t : int8_t { @@ -463,7 +471,11 @@ enum skills_t : int8_t { SKILL_LEVEL = 14, SKILL_FIRST = SKILL_FIST, - SKILL_LAST = SKILL_MANA_LEECH_AMOUNT +#if CLIENT_VERSION >= 1100 + SKILL_LAST = SKILL_MANA_LEECH_AMOUNT, +#else + SKILL_LAST = SKILL_FISHING, +#endif }; enum CreatureType_t : uint8_t { diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index c0f2fb382e8..fa20d9c8ce4 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -299,9 +299,9 @@ bool SpawnMonster::addMonster(const std::string &name, const Position &pos, Dire break; } } - const auto monsterType = g_monsters().getMonsterType(variant + name); + const auto monsterType = g_monsters().getMonsterType(variant + name, true); if (!monsterType) { - g_logger().error("Can not find {}", name); + g_logger().error("Cannot find monster '{}'. Position: {}", name, pos.toString()); return false; } diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index 68fa0c2407b..75acf3e2393 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -247,7 +247,7 @@ void SpawnNpc::cleanup() { bool SpawnNpc::addNpc(const std::string &name, const Position &pos, Direction dir, uint32_t scheduleInterval) { const auto &npcType = g_npcs().getNpcType(name); if (!npcType) { - g_logger().error("Can not find {}", name); + g_logger().error("Cannot find npc '{}'. Position: {}", name, pos.toString()); return false; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 46528c79241..76501498d0e 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -33,6 +33,10 @@ #include "map/spectators.hpp" #include "lib/metrics/metrics.hpp" +#if CLIENT_VERSION >= 870 + #include "creatures/appearance/mounts/mounts.hpp" +#endif + MuteCountMap Player::muteCountMap; Player::Player(ProtocolGame_ptr p) : @@ -1218,6 +1222,7 @@ std::shared_ptr Player::getDepotChest(uint32_t depotId, bool autoCre } std::shared_ptr depotChest; +#if CLIENT_VERSION >= 1100 if (depotId > 0 && depotId < 18) { depotChest = std::make_shared(ITEM_DEPOT_NULL + depotId); } else if (depotId == 18) { @@ -1227,7 +1232,11 @@ std::shared_ptr Player::getDepotChest(uint32_t depotId, bool autoCre } else { depotChest = std::make_shared(ITEM_DEPOT_XX); } - +#else + if (depotId > 0 && depotId <= 20) { + depotChest = std::make_shared(ITEM_DEPOT_NULL + depotId); + } +#endif depotChests[depotId] = depotChest; return depotChest; } @@ -1235,7 +1244,9 @@ std::shared_ptr Player::getDepotChest(uint32_t depotId, bool autoCre std::shared_ptr Player::getDepotLocker(uint32_t depotId) { auto it = depotLockerMap.find(depotId); if (it != depotLockerMap.end()) { +#if CLIENT_VERSION >= 940 inbox->setParent(it->second); +#endif for (uint32_t i = g_configManager().getNumber(DEPOT_BOXES, __FUNCTION__); i > 0; i--) { if (std::shared_ptr depotBox = getDepotChest(i, false)) { depotBox->setParent(it->second->getItemByIndex(0)->getContainer()); @@ -1244,25 +1255,28 @@ std::shared_ptr Player::getDepotLocker(uint32_t depotId) { return it->second; } - // We need to make room for supply stash on 12+ protocol versions and remove it for 10x. - bool createSupplyStash = !client->oldProtocol; - - std::shared_ptr depotLocker = std::make_shared(ITEM_LOCKER, createSupplyStash ? 4 : 3); - depotLocker->setDepotId(depotId); - depotLocker->internalAddThing(Item::CreateItem(ITEM_MARKET)); - depotLocker->internalAddThing(inbox); - if (createSupplyStash) { - depotLocker->internalAddThing(Item::CreateItem(ITEM_SUPPLY_STASH)); - } + uint8_t boxCount = 1; +#if CLIENT_VERSION >= 1180 + boxCount = client->oldProtocol ? boxCount : 4; +#endif + std::shared_ptr playerDepotLocker = std::make_shared(ITEM_LOCKER, boxCount); + playerDepotLocker->setDepotId(depotId); +#if CLIENT_VERSION >= 940 + playerDepotLocker->internalAddThing(Item::CreateItem(ITEM_MARKET)); + playerDepotLocker->internalAddThing(inbox); +#endif +#if CLIENT_VERSION >= 1180 + playerDepotLocker->internalAddThing(Item::CreateItem(ITEM_SUPPLY_STASH)); +#endif std::shared_ptr depotChest = Item::CreateItemAsContainer(ITEM_DEPOT, static_cast(g_configManager().getNumber(DEPOT_BOXES, __FUNCTION__))); for (uint32_t i = g_configManager().getNumber(DEPOT_BOXES, __FUNCTION__); i > 0; i--) { std::shared_ptr depotBox = getDepotChest(i, true); depotChest->internalAddThing(depotBox); depotBox->setParent(depotChest); } - depotLocker->internalAddThing(depotChest); - depotLockerMap[depotId] = depotLocker; - return depotLocker; + playerDepotLocker->internalAddThing(depotChest); + depotLockerMap[depotId] = playerDepotLocker; + return playerDepotLocker; } std::shared_ptr Player::getRewardChest() { @@ -1741,10 +1755,11 @@ void Player::onCreatureAppear(std::shared_ptr creature, bool isLogin) } sendBlessStatus(); } - +#if CLIENT_VERSION >= 870 if (getCurrentMount() != 0) { toggleMount(true); } +#endif g_game().changePlayerSpeed(static_self_cast(), 0); } @@ -1773,11 +1788,13 @@ void Player::onChangeZone(ZoneType_t zone) { onAttackedCreatureDisappear(false); } +#if CLIENT_VERSION >= 870 if (!g_configManager().getBoolean(TOGGLE_MOUNT_IN_PZ, __FUNCTION__) && !group->access && isMounted()) { dismount(); g_game().internalCreatureChangeOutfit(getPlayer(), defaultOutfit); wasMounted = true; } +#endif } else { int32_t ticks = g_configManager().getNumber(STAIRHOP_DELAY, __FUNCTION__); if (ticks > 0) { @@ -1785,10 +1802,12 @@ void Player::onChangeZone(ZoneType_t zone) { addCondition(condition); } } +#if CLIENT_VERSION >= 870 if (wasMounted) { toggleMount(true); wasMounted = false; } +#endif } updateImbuementTrackerStats(); @@ -2178,7 +2197,7 @@ void Player::onThink(uint32_t interval) { } else if (client && idleTime == 60000 * kickAfterMinutes) { std::ostringstream ss; ss << "There was no variation in your behaviour for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if there is no change in your actions until then."; - client->sendTextMessage(TextMessage(MESSAGE_ADMINISTRADOR, ss.str())); + client->sendTextMessage(TextMessage(MESSAGE_STATUS_WARNING, ss.str())); } } @@ -4472,10 +4491,12 @@ void Player::updateItemsLight(bool internal /*=false*/) { void Player::onAddCondition(ConditionType_t type) { Creature::onAddCondition(type); +#if CLIENT_VERSION >= 870 if (type == CONDITION_OUTFIT && isMounted()) { dismount(); wasMounted = true; } +#endif sendIcons(); } @@ -4548,9 +4569,11 @@ void Player::onEndCondition(ConditionType_t type) { } } +#if CLIENT_VERSION >= 870 if (type == CONDITION_OUTFIT && wasMounted) { toggleMount(true); } +#endif sendIcons(); } @@ -4853,10 +4876,12 @@ void Player::changeSoul(int32_t soulChange) { } bool Player::canWear(uint16_t lookType, uint8_t addons) const { - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { g_logger().warn("[Player::canWear] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); return false; } +#endif if (group->access) { return true; @@ -5716,8 +5741,9 @@ void Player::setCurrentMount(uint8_t mount) { addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mount); } +#if CLIENT_VERSION >= 870 bool Player::hasAnyMount() const { - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().m_mountsPtr->getMounts(); for (const auto mount : mounts) { if (hasMount(mount)) { return true; @@ -5728,7 +5754,7 @@ bool Player::hasAnyMount() const { uint8_t Player::getRandomMountId() const { std::vector playerMounts; - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().m_mountsPtr->getMounts(); for (const auto mount : mounts) { if (hasMount(mount)) { playerMounts.push_back(mount->id); @@ -5772,7 +5798,7 @@ bool Player::toggleMount(bool mount) { currentMountId = getRandomMountId(); } - const auto currentMount = g_game().mounts.getMountByID(currentMountId); + const auto currentMount = g_game().m_mountsPtr->getMountByID(currentMountId); if (!currentMount) { return false; } @@ -5815,7 +5841,7 @@ bool Player::toggleMount(bool mount) { } bool Player::tameMount(uint8_t mountId) { - if (!g_game().mounts.getMountByID(mountId)) { + if (!g_game().m_mountsPtr->getMountByID(mountId)) { return false; } @@ -5834,7 +5860,7 @@ bool Player::tameMount(uint8_t mountId) { } bool Player::untameMount(uint8_t mountId) { - if (!g_game().mounts.getMountByID(mountId)) { + if (!g_game().m_mountsPtr->getMountByID(mountId)) { return false; } @@ -5882,7 +5908,7 @@ bool Player::hasMount(const std::shared_ptr mount) const { } void Player::dismount() { - const auto mount = g_game().mounts.getMountByID(getCurrentMount()); + const auto mount = g_game().m_mountsPtr->getMountByID(getCurrentMount()); if (mount && mount->speed > 0) { g_game().changeSpeed(static_self_cast(), -mount->speed); } @@ -5890,6 +5916,7 @@ void Player::dismount() { defaultOutfit.lookMount = 0; setCurrentMount(0); } +#endif bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries) { if (tries == 0 || skill == SKILL_LEVEL) { @@ -6572,6 +6599,7 @@ bool Player::isCreatureUnlockedOnTaskHunting(const std::shared_ptr } void Player::triggerMomentum() { +#if CLIENT_VERSION >= 1281 auto item = getInventoryItem(CONST_SLOT_HEAD); if (item == nullptr) { return; @@ -6602,9 +6630,11 @@ void Player::triggerMomentum() { sendTextMessage(MESSAGE_ATTENTION, "Momentum was triggered."); } } +#endif } void Player::clearCooldowns() { +#if CLIENT_VERSION >= 870 auto it = conditions.begin(); while (it != conditions.end()) { auto condItem = *it; @@ -6618,6 +6648,7 @@ void Player::clearCooldowns() { } ++it; } +#endif } void Player::triggerTranscendance() { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index ecf489b89ec..a3afc36b0f1 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -24,7 +24,6 @@ #include "items/containers/inbox/inbox.hpp" #include "io/ioguild.hpp" #include "io/ioprey.hpp" -#include "creatures/appearance/mounts/mounts.hpp" #include "creatures/appearance/outfit/outfit.hpp" #include "grouping/party.hpp" #include "server/network/protocol/protocolgame.hpp" @@ -49,6 +48,9 @@ class TaskHuntingSlot; class Spell; class PlayerWheel; class Spectators; +#if CLIENT_VERSION >= 870 +struct Mount; +#endif struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -159,6 +161,8 @@ class Player final : public Creature, public Cylinder, public Bankable { bool isMounted() const { return defaultOutfit.lookMount != 0; } + +#if CLIENT_VERSION >= 870 bool toggleMount(bool mount); bool tameMount(uint8_t mountId); bool untameMount(uint8_t mountId); @@ -166,6 +170,8 @@ class Player final : public Creature, public Cylinder, public Bankable { bool hasAnyMount() const; uint8_t getRandomMountId() const; void dismount(); +#endif + uint16_t getDodgeChance() const; uint8_t isRandomMounted() const { @@ -1186,6 +1192,7 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendCreatureType(creature, creatureType); } } +#if CLIENT_VERSION >= 870 void sendSpellCooldown(uint16_t spellId, uint32_t time) { if (client) { client->sendSpellCooldown(spellId, time); @@ -1196,6 +1203,7 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendSpellGroupCooldown(groupId, time); } } +#endif void sendUseItemCooldown(uint32_t time) const { if (client) { client->sendUseItemCooldown(time); @@ -1582,6 +1590,7 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendItemInspection(itemId, itemCount, item, cyclopedia); } } +#if CLIENT_VERSION > 1100 void sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode) { if (client) { client->sendCyclopediaCharacterNoData(characterInfoType, errorCode); @@ -1651,6 +1660,8 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendCyclopediaCharacterTitles(); } } +#endif + void sendHighscoresNoData() { if (client) { client->sendHighscoresNoData(); diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index dfd685bd83f..13e17b5e36b 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -2443,6 +2443,7 @@ void PlayerWheel::onThink(bool force /* = false*/) { } void PlayerWheel::reduceAllSpellsCooldownTimer(int32_t value) { +#if CLIENT_VERSION >= 870 for (const auto &condition : m_player.getConditionsByType(CONDITION_SPELLCOOLDOWN)) { if (condition->getTicks() <= value) { m_player.sendSpellCooldown(condition->getSubId(), 0); @@ -2452,6 +2453,7 @@ void PlayerWheel::reduceAllSpellsCooldownTimer(int32_t value) { m_player.sendSpellCooldown(condition->getSubId(), condition->getTicks()); } } +#endif } void PlayerWheel::resetUpgradedSpells() { diff --git a/src/game/functions/game_reload.cpp b/src/game/functions/game_reload.cpp index f40e9cf9047..4aad4bc20b3 100644 --- a/src/game/functions/game_reload.cpp +++ b/src/game/functions/game_reload.cpp @@ -42,8 +42,10 @@ bool GameReload::init(Reload_t reloadTypes) const { return reloadModules(); case Reload_t::RELOAD_TYPE_MONSTERS: return reloadMonsters(); +#if CLIENT_VERSION >= 870 case Reload_t::RELOAD_TYPE_MOUNTS: return reloadMounts(); +#endif case Reload_t::RELOAD_TYPE_NPCS: return reloadNpcs(); case Reload_t::RELOAD_TYPE_RAIDS: @@ -136,9 +138,11 @@ bool GameReload::reloadMonsters() const { return false; } +#if CLIENT_VERSION >= 870 bool GameReload::reloadMounts() const { - return g_game().mounts.reload(); + return g_game().m_mountsPtr->reload(); } +#endif bool GameReload::reloadNpcs() const { return g_npcs().reload(); diff --git a/src/game/game.cpp b/src/game/game.cpp index 732b589e488..2e4ad6d6190 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -43,6 +43,10 @@ #include "kv/kv.hpp" +#if CLIENT_VERSION >= 870 + #include "creatures/appearance/mounts/mounts.hpp" +#endif + namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, std::shared_ptr source) { if (blockType == BLOCK_DEFENSE) { @@ -179,6 +183,9 @@ Game::Game() { // Create instance of IOWheel to Game class m_IOWheel = std::make_unique(); +#if CLIENT_VERSION >= 870 + m_mountsPtr = std::make_unique(); +#endif } Game::~Game() = default; @@ -337,7 +344,9 @@ void Game::setGameState(GameState_t newState) { raids.loadFromXml(); raids.startup(); - mounts.loadFromXml(); +#if CLIENT_VERSION >= 870 + m_mountsPtr->loadFromXml(); +#endif loadMotdNum(); loadPlayersRecord(); @@ -419,10 +428,24 @@ bool Game::loadItemsPrice() { void Game::loadMainMap(const std::string &filename) { Monster::despawnRange = g_configManager().getNumber(DEFAULT_DESPAWNRANGE, __FUNCTION__); Monster::despawnRadius = g_configManager().getNumber(DEFAULT_DESPAWNRADIUS, __FUNCTION__); +#if CLIENT_VERSION < 1100 + const auto useAnyDatapack = g_configManager().getBoolean(USE_ANY_DATAPACK_FOLDER, __FUNCTION__); + auto datapackName = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__); + std::string mapName; + if (useAnyDatapack || (datapackName == "data-canary")) { + mapName = fmt::format("/world/{}.otbm", filename); + } else { + mapName = fmt::format("/world/{}/{}.otbm", CLIENT_VERSION, filename); + } + auto string = g_configManager().getString(DATA_DIRECTORY, __FUNCTION__) + mapName; + map.loadMap(string, true, true, true, true, true); +#else map.loadMap(g_configManager().getString(DATA_DIRECTORY, __FUNCTION__) + "/world/" + filename + ".otbm", true, true, true, true, true); +#endif } void Game::loadCustomMaps(const std::filesystem::path &customMapPath) { +#if CLIENT_VERSION > 1100 Monster::despawnRange = g_configManager().getNumber(DEFAULT_DESPAWNRANGE, __FUNCTION__); Monster::despawnRadius = g_configManager().getNumber(DEFAULT_DESPAWNRADIUS, __FUNCTION__); @@ -467,6 +490,7 @@ void Game::loadCustomMaps(const std::filesystem::path &customMapPath) { // Must be done after all maps have been loaded map.loadHouseInfo(); +#endif } void Game::loadMap(const std::string &path, const Position &pos) { @@ -1040,22 +1064,19 @@ FILELOADER_ERRORS Game::loadAppearanceProtobuf(const std::string &file) { // Parsing all items into ItemType Item::items.loadFromProtobuf(); - // Only iterate other objects if necessary - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__)) { - // Registering distance effects - for (uint32_t it = 0; it < appearances.effect_size(); it++) { - registeredMagicEffects.push_back(static_cast(appearances.effect(it).id())); - } + // Registering distance effects + for (uint32_t it = 0; it < appearances.effect_size(); it++) { + registeredMagicEffects.push_back(static_cast(appearances.effect(it).id())); + } - // Registering missile effects - for (uint32_t it = 0; it < appearances.missile_size(); it++) { - registeredDistanceEffects.push_back(static_cast(appearances.missile(it).id())); - } + // Registering missile effects + for (uint32_t it = 0; it < appearances.missile_size(); it++) { + registeredDistanceEffects.push_back(static_cast(appearances.missile(it).id())); + } - // Registering outfits - for (uint32_t it = 0; it < appearances.outfit_size(); it++) { - registeredLookTypes.push_back(static_cast(appearances.outfit(it).id())); - } + // Registering outfits + for (uint32_t it = 0; it < appearances.outfit_size(); it++) { + registeredLookTypes.push_back(static_cast(appearances.outfit(it).id())); } fileStream.close(); @@ -1474,8 +1495,15 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo } std::shared_ptr toCylinderTile = toCylinder->getTile(); - const Position &mapToPos = toCylinderTile->getPosition(); + if (!toCylinderTile) { + toCylinderTile = toCylinder->getRealParent() ? toCylinder->getRealParent()->getTile() : nullptr; + if (!toCylinderTile) { + g_logger().error("[{}] player {} can't move item {}, to position {}, to move item tile is nullptr", __FUNCTION__, player->getName(), Item::items[itemId].name, toPos); + return; + } + } + const Position &mapToPos = toCylinderTile->getPosition(); // hangable item specific code if (item->isHangable() && toCylinderTile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) { // destination supports hangable objects so need to move there first @@ -3470,6 +3498,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo return; } +#if CLIENT_VERSION >= 1100 bool canUseHouseItem = !g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS, __FUNCTION__) || InternalGame::playerCanUseItemOnHouseTile(player, item); if (!canUseHouseItem && item->hasOwner() && !item->isOwner(player)) { player->sendCancelMessage(RETURNVALUE_ITEMISNOTYOURS); @@ -3478,6 +3507,7 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); return; } +#endif const ItemType &it = Item::items[item->getID()]; if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3806,6 +3836,7 @@ void Game::playerRotateItem(uint32_t playerId, const Position &pos, uint8_t stac } } +#if CLIENT_VERSION >= 1100 void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, uint8_t stackPos, const uint16_t itemId) { std::shared_ptr player = getPlayerByID(playerId); if (!player || pos.x == 0xFFFF) { @@ -3921,11 +3952,23 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos outfit.lookAddons = 0; } - const auto mount = mounts.getMountByClientID(outfit.lookMount); + const auto &mount = m_mountsPtr->getMountByClientID(outfit.lookMount); if (!mount || !player->hasMount(mount)) { outfit.lookMount = 0; } + if (outfit.lookMount != 0) { + item->setCustomAttribute("LookMount", static_cast(outfit.lookMount)); + item->setCustomAttribute("LookMountHead", static_cast(outfit.lookMountHead)); + item->setCustomAttribute("LookMountBody", static_cast(outfit.lookMountBody)); + item->setCustomAttribute("LookMountLegs", static_cast(outfit.lookMountLegs)); + item->setCustomAttribute("LookMountFeet", static_cast(outfit.lookMountFeet)); + } else if (auto pastLookMount = item->getCustomAttribute("PastLookMount"); + pastLookMount && pastLookMount->getInteger() > 0) { + item->removeCustomAttribute("LookMount"); + item->removeCustomAttribute("PastLookMount"); + } + if (outfit.lookType != 0) { item->setCustomAttribute("LookType", static_cast(outfit.lookType)); item->setCustomAttribute("LookHead", static_cast(outfit.lookHead)); @@ -3939,22 +3982,11 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos item->removeCustomAttribute("PastLookType"); } - if (outfit.lookMount != 0) { - item->setCustomAttribute("LookMount", static_cast(outfit.lookMount)); - item->setCustomAttribute("LookMountHead", static_cast(outfit.lookMountHead)); - item->setCustomAttribute("LookMountBody", static_cast(outfit.lookMountBody)); - item->setCustomAttribute("LookMountLegs", static_cast(outfit.lookMountLegs)); - item->setCustomAttribute("LookMountFeet", static_cast(outfit.lookMountFeet)); - } else if (auto pastLookMount = item->getCustomAttribute("PastLookMount"); - pastLookMount && pastLookMount->getInteger() > 0) { - item->removeCustomAttribute("LookMount"); - item->removeCustomAttribute("PastLookMount"); - } - item->setCustomAttribute("PodiumVisible", static_cast(podiumVisible)); item->setCustomAttribute("LookDirection", static_cast(direction)); // Change Podium name + if (outfit.lookType != 0 || outfit.lookMount != 0) { std::ostringstream name; name << item->getName() << " displaying the "; @@ -3985,6 +4017,7 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos spectator->getPlayer()->sendUpdateTileItem(tile, pos, item); } } +#endif void Game::playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t stackPos, const uint16_t itemId) { std::shared_ptr player = getPlayerByID(playerId); @@ -5563,12 +5596,14 @@ void Game::playerRequestOutfit(uint32_t playerId) { } void Game::playerToggleMount(uint32_t playerId, bool mount) { +#if CLIENT_VERSION >= 870 std::shared_ptr player = getPlayerByID(playerId); if (!player) { return; } player->toggleMount(mount); +#endif } void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMountRandomized /* = 0*/) { @@ -5581,10 +5616,11 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun return; } +#if CLIENT_VERSION > 870 player->setRandomMount(isMountRandomized); if (isMountRandomized && outfit.lookMount != 0 && player->hasAnyMount()) { - auto randomMount = mounts.getMountByID(player->getRandomMountId()); + auto randomMount = m_mountsPtr->getMountByID(player->getRandomMountId()); outfit.lookMount = randomMount->clientId; } @@ -5594,7 +5630,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun } if (outfit.lookMount != 0) { - const auto mount = mounts.getMountByClientID(outfit.lookMount); + const auto mount = m_mountsPtr->getMountByClientID(outfit.lookMount); if (!mount) { return; } @@ -5614,7 +5650,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun auto deltaSpeedChange = mount->speed; if (player->isMounted()) { - const auto prevMount = mounts.getMountByID(player->getLastMount()); + const auto prevMount = m_mountsPtr->getMountByID(player->getLastMount()); if (prevMount) { deltaSpeedChange -= prevMount->speed; } @@ -5625,6 +5661,7 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, uint8_t isMoun } else if (player->isMounted()) { player->dismount(); } +#endif if (player->canWear(outfit.lookType, outfit.lookAddons)) { player->defaultOutfit = outfit; @@ -5700,6 +5737,9 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, c case TALKTYPE_PRIVATE_TO: case TALKTYPE_PRIVATE_RED_TO: +#if CLIENT_VERSION <= 860 + case TALKTYPE_RVR_ANSWER: +#endif playerSpeakTo(player, type, receiver, text); break; @@ -5710,6 +5750,9 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, c break; case TALKTYPE_PRIVATE_PN: +#if CLIENT_VERSION <= 860 + case TALKTYPE_PRIVATE_FROM: +#endif playerSpeakToNpc(player, text); break; @@ -5796,7 +5839,13 @@ bool Game::playerSpeakTo(std::shared_ptr player, SpeakClasses type, cons if (type == TALKTYPE_PRIVATE_RED_TO && (player->hasFlag(PlayerFlags_t::CanTalkRedPrivate) || player->getAccountType() >= account::AccountType::ACCOUNT_TYPE_GAMEMASTER)) { type = TALKTYPE_PRIVATE_RED_FROM; } else { +#if CLIENT_VERSION <= 860 + if (type != TALKTYPE_RVR_ANSWER) { + type = TALKTYPE_PRIVATE_FROM; + } +#else type = TALKTYPE_PRIVATE_FROM; +#endif } toPlayer->sendPrivateMessage(player, type, text); @@ -7942,6 +7991,7 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) { player->removePlayer(displayEffect); } +#if CLIENT_VERSION > 1100 void Game::playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_t characterID, CyclopediaCharacterInfoType_t characterInfoType, uint16_t entriesPerPage, uint16_t page) { uint32_t playerGUID = player->getGUID(); if (characterID != playerGUID) { @@ -8101,6 +8151,7 @@ void Game::playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_ break; } } +#endif std::string Game::generateHighscoreQueryForEntries(const std::string &categoryName, uint32_t page, uint8_t entriesPerPage, uint32_t vocation) { std::ostringstream query; @@ -9286,6 +9337,7 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con player->updateUIExhausted(); } +#if CLIENT_VERSION >= 1100 void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t stackPos, const uint16_t itemId) { std::shared_ptr player = getPlayerByID(playerId); if (!player) { @@ -9370,6 +9422,7 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st playerSetShowOffSocket(player->getID(), newOutfit, pos, stackPos, itemId, isPodiumVisible, directionValue); } +#endif void Game::playerRequestInventoryImbuements(uint32_t playerId, bool isTrackerOpen) { std::shared_ptr player = getPlayerByID(playerId); diff --git a/src/game/game.hpp b/src/game/game.hpp index aff723fd780..59c805874d7 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -35,8 +35,10 @@ class IOPrey; class IOWheel; class ItemClassification; class Guild; -class Mounts; class Spectators; +#if CLIENT_VERSION >= 870 +class Mounts; +#endif static constexpr uint16_t SERVER_BEAT = 0x32; static constexpr int32_t EVENT_MS = 10000; @@ -565,7 +567,9 @@ class Game { Groups groups; Map map; - Mounts mounts; +#if CLIENT_VERSION >= 870 + std::unique_ptr m_mountsPtr; +#endif Raids raids; Canary::protobuf::appearances::Appearances appearances; diff --git a/src/game/movement/position.hpp b/src/game/movement/position.hpp index 2f79453eb39..2e03583339c 100644 --- a/src/game/movement/position.hpp +++ b/src/game/movement/position.hpp @@ -99,14 +99,7 @@ struct Position { } std::string toString() const { - std::string str; - return str.append("( ") - .append(std::to_string(getX())) - .append(", ") - .append(std::to_string(getY())) - .append(", ") - .append(std::to_string(getZ())) - .append(" )"); + return fmt::format("( {}, {}, {} )", x, y, z); } int_fast32_t getX() const { @@ -129,5 +122,17 @@ namespace std { }; } +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context &ctx) { + return ctx.begin(); + } + + template + auto format(const Position &pos, FormatContext &ctx) { + return fmt::format_to(ctx.out(), "{}", pos.toString()); + } +}; + std::ostream &operator<<(std::ostream &, const Position &); std::ostream &operator<<(std::ostream &, const Direction &); diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 2c50938a37f..207dc36e86a 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -243,10 +243,13 @@ void IOLoginDataLoad::loadPlayerDefaultOutfit(std::shared_ptr player, DB } player->defaultOutfit.lookType = result->getNumber("looktype"); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && player->defaultOutfit.lookType != 0 && !g_game().isLookTypeRegistered(player->defaultOutfit.lookType)) { - g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookType); +#if CLIENT_VERSION > 1100 + const auto &lookType = player->defaultOutfit.lookType; + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && lookType != 0 && !g_game().isLookTypeRegistered(lookType)) { + g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", lookType); return; } +#endif player->defaultOutfit.lookHead = static_cast(result->getNumber("lookhead")); player->defaultOutfit.lookBody = static_cast(result->getNumber("lookbody")); @@ -258,11 +261,13 @@ void IOLoginDataLoad::loadPlayerDefaultOutfit(std::shared_ptr player, DB player->defaultOutfit.lookMountLegs = static_cast(result->getNumber("lookmountlegs")); player->defaultOutfit.lookMountFeet = static_cast(result->getNumber("lookmountfeet")); player->defaultOutfit.lookFamiliarsType = result->getNumber("lookfamiliarstype"); - - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && player->defaultOutfit.lookFamiliarsType != 0 && !g_game().isLookTypeRegistered(player->defaultOutfit.lookFamiliarsType)) { - g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", player->defaultOutfit.lookFamiliarsType); + const auto &familiarType = player->defaultOutfit.lookFamiliarsType; +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && familiarType != 0 && !g_game().isLookTypeRegistered(familiarType)) { + g_logger().warn("[IOLoginData::loadPlayer] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", familiarType); return; } +#endif player->currentOutfit = player->defaultOutfit; } @@ -297,7 +302,8 @@ void IOLoginDataLoad::loadPlayerSkill(std::shared_ptr player, DBResult_p static const std::array skillNames = { "skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing", "skill_critical_hit_chance", "skill_critical_hit_damage", "skill_life_leech_chance", "skill_life_leech_amount", "skill_mana_leech_chance", "skill_mana_leech_amount" }; static const std::array skillNameTries = { "skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing_tries", "skill_critical_hit_chance_tries", "skill_critical_hit_damage_tries", "skill_life_leech_chance_tries", "skill_life_leech_amount_tries", "skill_mana_leech_chance_tries", "skill_mana_leech_amount_tries" }; - for (size_t i = 0; i < skillNames.size(); ++i) { + + for (size_t i = 0; i <= SKILL_LAST; ++i) { uint16_t skillLevel = result->getNumber(skillNames[i]); uint64_t skillTries = result->getNumber(skillNameTries[i]); uint64_t nextSkillTries = player->vocation->getReqSkillTries(static_cast(i), skillLevel + 1); @@ -380,6 +386,7 @@ void IOLoginDataLoad::loadPlayerGuild(std::shared_ptr player, DBResult_p } } +#if CLIENT_VERSION > 1100 void IOLoginDataLoad::loadPlayerStashItems(std::shared_ptr player, DBResult_ptr result) { if (!result || !player) { g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); @@ -448,7 +455,7 @@ void IOLoginDataLoad::loadPlayerBestiaryCharms(std::shared_ptr player, D Database::getInstance().executeQuery(query.str()); } } - +#endif void IOLoginDataLoad::loadPlayerInstantSpellList(std::shared_ptr player, DBResult_ptr result) { if (!player) { g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); @@ -547,14 +554,18 @@ void IOLoginDataLoad::loadPlayerInventoryItems(std::shared_ptr player, D } } +#if CLIENT_VERSION > 1100 void IOLoginDataLoad::loadPlayerStoreInbox(std::shared_ptr player) { if (!player) { g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); return; } - if (!player->inventory[CONST_SLOT_STORE_INBOX]) { - player->internalAddThing(CONST_SLOT_STORE_INBOX, Item::CreateItem(ITEM_STORE_INBOX)); + Slots_t slot = CONST_SLOT_STORE_INBOX; + if (!player->inventory[slot]) { + auto item = Item::CreateItem(ITEM_STORE_INBOX); + item->setAttribute(ItemAttribute_t::NAME, "your store inbox"); + player->internalAddThing(slot, item); } } @@ -575,6 +586,7 @@ void IOLoginDataLoad::loadRewardItems(std::shared_ptr player) { insertItemsIntoRewardBag(rewardItems); } } +#endif void IOLoginDataLoad::loadPlayerDepotItems(std::shared_ptr player, DBResult_ptr result) { if (!result || !player) { @@ -682,7 +694,7 @@ void IOLoginDataLoad::loadPlayerVip(std::shared_ptr player, DBResult_ptr } while (result->next()); } } - +#if CLIENT_VERSION >= 1100 void IOLoginDataLoad::loadPlayerPreyClass(std::shared_ptr player, DBResult_ptr result) { if (!result || !player) { g_logger().warn("[IOLoginData::loadPlayer] - Player or Result nullptr: {}", __FUNCTION__); @@ -886,7 +898,7 @@ void IOLoginDataLoad::loadPlayerInitializeSystem(std::shared_ptr player) player->initializePrey(); player->initializeTaskHunting(); } - +#endif void IOLoginDataLoad::loadPlayerUpdateSystem(std::shared_ptr player) { if (!player) { g_logger().warn("[IOLoginData::loadPlayer] - Player nullptr: {}", __FUNCTION__); diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 3ca34026db2..0008cddc835 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -91,6 +91,7 @@ bool IOLoginData::loadPlayerById(std::shared_ptr player, uint32_t id, bo Database &db = Database::getInstance(); std::ostringstream query; query << "SELECT * FROM `players` WHERE `id` = " << id; + return loadPlayer(player, db.storeQuery(query.str()), disableIrrelevantInfo); } @@ -136,24 +137,12 @@ bool IOLoginData::loadPlayer(std::shared_ptr player, DBResult_ptr result // guild load IOLoginDataLoad::loadPlayerGuild(player, result); - // stash load items - IOLoginDataLoad::loadPlayerStashItems(player, result); - - // bestiary charms - IOLoginDataLoad::loadPlayerBestiaryCharms(player, result); - // load inventory items IOLoginDataLoad::loadPlayerInventoryItems(player, result); - // store Inbox - IOLoginDataLoad::loadPlayerStoreInbox(player); - // load depot items IOLoginDataLoad::loadPlayerDepotItems(player, result); - // load reward items - IOLoginDataLoad::loadRewardItems(player); - // load inbox items IOLoginDataLoad::loadPlayerInboxItems(player, result); @@ -163,6 +152,19 @@ bool IOLoginData::loadPlayer(std::shared_ptr player, DBResult_ptr result // load vip IOLoginDataLoad::loadPlayerVip(player, result); +#if CLIENT_VERSION >= 1100 + // load reward items + IOLoginDataLoad::loadRewardItems(player); + + // stash load items + IOLoginDataLoad::loadPlayerStashItems(player, result); + + // bestiary charms + IOLoginDataLoad::loadPlayerBestiaryCharms(player, result); + + // store Inbox + IOLoginDataLoad::loadPlayerStoreInbox(player); + // load prey class IOLoginDataLoad::loadPlayerPreyClass(player, result); @@ -180,8 +182,8 @@ bool IOLoginData::loadPlayer(std::shared_ptr player, DBResult_ptr result IOLoginDataLoad::loadPlayerBosstiary(player, result); IOLoginDataLoad::loadPlayerInitializeSystem(player); +#endif IOLoginDataLoad::loadPlayerUpdateSystem(player); - return true; } catch (const std::system_error &error) { g_logger().warn("[{}] Error while load player: {}", __FUNCTION__, error.what()); @@ -190,6 +192,7 @@ bool IOLoginData::loadPlayer(std::shared_ptr player, DBResult_ptr result g_logger().warn("[{}] Error while load player: {}", __FUNCTION__, e.what()); return false; } + return false; } bool IOLoginData::savePlayer(std::shared_ptr player) { diff --git a/src/io/iomap.hpp b/src/io/iomap.hpp index e74f1ce3bfa..28e1f668dfb 100644 --- a/src/io/iomap.hpp +++ b/src/io/iomap.hpp @@ -32,7 +32,7 @@ class IOMap { if (map->monsterfile.empty()) { // OTBM file doesn't tell us about the monsterfile, // Lets guess it is mapname-monster.xml. - map->monsterfile = g_configManager().getString(MAP_NAME, __FUNCTION__); + map->monsterfile = getMapFilePath("monster", __FUNCTION__); map->monsterfile += "-monster.xml"; } @@ -48,7 +48,7 @@ class IOMap { if (map->zonesfile.empty()) { // OTBM file doesn't tell us about the zonesfile, // Lets guess it is mapname-zone.xml. - map->zonesfile = g_configManager().getString(MAP_NAME, __FUNCTION__); + map->zonesfile = getMapFilePath("zones", __FUNCTION__); map->zonesfile += "-zones.xml"; } @@ -64,7 +64,7 @@ class IOMap { if (map->npcfile.empty()) { // OTBM file doesn't tell us about the npcfile, // Lets guess it is mapname-npc.xml. - map->npcfile = g_configManager().getString(MAP_NAME, __FUNCTION__); + map->npcfile = getMapFilePath("npc", __FUNCTION__); map->npcfile += "-npc.xml"; } @@ -80,7 +80,7 @@ class IOMap { if (map->housefile.empty()) { // OTBM file doesn't tell us about the housefile, // Lets guess it is mapname-house.xml. - map->housefile = g_configManager().getString(MAP_NAME, __FUNCTION__); + map->housefile = getMapFilePath("house", __FUNCTION__); map->housefile += "-house.xml"; } @@ -93,6 +93,7 @@ class IOMap { * \returns true if the monsters spawn map custom was loaded successfully */ static bool loadMonstersCustom(Map* map, const std::string &mapName, int customMapIndex) { +#if CLIENT_VERSION >= 1100 if (map->monsterfile.empty()) { // OTBM file doesn't tell us about the monsterfile, // Lets guess it is mapname-monster.xml. @@ -100,6 +101,9 @@ class IOMap { map->monsterfile += "-monster.xml"; } return map->spawnsMonsterCustomMaps[customMapIndex].loadFromXML(map->monsterfile); +#else + return true; +#endif } /** @@ -108,6 +112,7 @@ class IOMap { * \returns true if the zones spawn map custom was loaded successfully */ static bool loadZonesCustom(Map* map, const std::string &mapName, int customMapIndex) { +#if CLIENT_VERSION >= 1100 if (map->zonesfile.empty()) { // OTBM file doesn't tell us about the zonesfile, // Lets guess it is mapname-zones.xml. @@ -115,6 +120,9 @@ class IOMap { map->zonesfile += "-zones.xml"; } return Zone::loadFromXML(map->zonesfile, customMapIndex); +#else + return true; +#endif } /** @@ -123,6 +131,7 @@ class IOMap { * \returns true if the npcs spawn map custom was loaded successfully */ static bool loadNpcsCustom(Map* map, const std::string &mapName, int customMapIndex) { +#if CLIENT_VERSION >= 1100 if (map->npcfile.empty()) { // OTBM file doesn't tell us about the npcfile, // Lets guess it is mapname-npc.xml. @@ -131,6 +140,9 @@ class IOMap { } return map->spawnsNpcCustomMaps[customMapIndex].loadFromXml(map->npcfile); +#else + return true; +#endif } /** @@ -139,6 +151,7 @@ class IOMap { * \returns true if the map custom houses was loaded successfully */ static bool loadHousesCustom(Map* map, const std::string &mapName, int customMapIndex) { +#if CLIENT_VERSION >= 1100 if (map->housefile.empty()) { // OTBM file doesn't tell us about the housefile, // Lets guess it is mapname-house.xml. @@ -146,9 +159,19 @@ class IOMap { map->housefile += "-house.xml"; } return map->housesCustomMaps[customMapIndex].loadHousesXML(map->housefile); +#else + return true; +#endif } private: + static const std::string &getMapFilePath(const std::string &fileName, const std::string &functionCallName) { + static std::string mapNameFilePack; + static std::string prefix = CLIENT_VERSION < 1100 ? fmt::format("{}/", CLIENT_VERSION) : ""; + mapNameFilePack = fmt::format("{}{}-{}.xml", prefix, g_configManager().getString(MAP_NAME, functionCallName), fileName); + return mapNameFilePack; + } + static void parseMapDataAttributes(FileStream &stream, Map* map); static void parseWaypoints(FileStream &stream, Map &map); static void parseTowns(FileStream &stream, Map &map); diff --git a/src/items/item.cpp b/src/items/item.cpp index a7ff8ec957c..769a3362f70 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -64,9 +64,9 @@ std::shared_ptr Item::CreateItem(const uint16_t type, uint16_t count /*= 0 } } else if (type > 0 && itemPosition) { auto position = *itemPosition; - g_logger().warn("[Item::CreateItem] Item with id '{}', in position '{}' not exists in the appearances.dat and cannot be created.", type, position.toString()); + g_logger().debug("[Item::CreateItem] Item with id '{}', in position '{}' not exists and cannot be created.", type, position.toString()); } else { - g_logger().warn("[Item::CreateItem] Item with id '{}' is not registered and cannot be created.", type); + g_logger().debug("[Item::CreateItem] Item with id '{}' is not registered and cannot be created.", type); } return newItem; diff --git a/src/items/items.cpp b/src/items/items.cpp index 6c517c8a1ed..caaecc5c516 100644 --- a/src/items/items.cpp +++ b/src/items/items.cpp @@ -13,6 +13,7 @@ #include "items/items.hpp" #include "game/game.hpp" #include "utils/pugicast.hpp" +#include "core.hpp" Items::Items() = default; @@ -63,7 +64,11 @@ ItemTypes_t Items::getLootType(const std::string &strValue) { bool Items::reload() { clear(); +#if CLIENT_VERSION < 1100 + loadFromOtb("data/items/" + std::to_string(CLIENT_VERSION) + "/items.otb"); +#else loadFromProtobuf(); +#endif if (!loadFromXml()) { return false; @@ -190,9 +195,390 @@ void Items::loadFromProtobuf() { items.shrink_to_fit(); } +#if CLIENT_VERSION < 1100 +constexpr auto OTBI = OTB::Identifier { { 'O', 'T', 'B', 'I' } }; + +enum rootattrib_ { + ROOT_ATTR_VERSION = 0x01, +}; + +struct VERSIONINFO { + uint32_t dwMajorVersion; + uint32_t dwMinorVersion; + uint32_t dwBuildNumber; + uint8_t CSDVersion[128]; +}; + +enum OtbItemAttribute_t { + ITEM_ATTR_FIRST = 0x10, + ITEM_ATTR_SERVERID = ITEM_ATTR_FIRST, + ITEM_ATTR_CLIENTID, + ITEM_ATTR_NAME, + ITEM_ATTR_DESCR, + ITEM_ATTR_SPEED, + ITEM_ATTR_SLOT, + ITEM_ATTR_MAXITEMS, + ITEM_ATTR_WEIGHT, + ITEM_ATTR_WEAPON, + ITEM_ATTR_AMMO, + ITEM_ATTR_ARMOR, + ITEM_ATTR_MAGLEVEL, + ITEM_ATTR_MAGFIELDTYPE, + ITEM_ATTR_WRITEABLE, + ITEM_ATTR_ROTATETO, + ITEM_ATTR_DECAY, + ITEM_ATTR_SPRITEHASH, + ITEM_ATTR_MINIMAPCOLOR, + ITEM_ATTR_07, + ITEM_ATTR_08, + ITEM_ATTR_LIGHT, + + // 1-byte aligned + ITEM_ATTR_DECAY2, + ITEM_ATTR_WEAPON2, + ITEM_ATTR_AMMO2, + ITEM_ATTR_ARMOR2, + ITEM_ATTR_WRITEABLE2, + ITEM_ATTR_LIGHT2, + ITEM_ATTR_TOPORDER, + ITEM_ATTR_WRITEABLE3, + + ITEM_ATTR_WAREID, + + ITEM_ATTR_LAST +}; + +enum OtbItemFlags_t { + FLAG_BLOCK_SOLID = 1 << 0, + FLAG_BLOCK_PROJECTILE = 1 << 1, + FLAG_BLOCK_PATHFIND = 1 << 2, + FLAG_HAS_HEIGHT = 1 << 3, + FLAG_USEABLE = 1 << 4, + FLAG_PICKUPABLE = 1 << 5, + FLAG_MOVEABLE = 1 << 6, + FLAG_STACKABLE = 1 << 7, + FLAG_FLOORCHANGEDOWN = 1 << 8, + FLAG_FLOORCHANGENORTH = 1 << 9, + FLAG_FLOORCHANGEEAST = 1 << 10, + FLAG_FLOORCHANGESOUTH = 1 << 11, + FLAG_FLOORCHANGEWEST = 1 << 12, + FLAG_ALWAYSONTOP = 1 << 13, + FLAG_READABLE = 1 << 14, + FLAG_ROTATABLE = 1 << 15, + FLAG_HANGABLE = 1 << 16, + FLAG_VERTICAL = 1 << 17, + FLAG_HORIZONTAL = 1 << 18, + FLAG_CANNOTDECAY = 1 << 19, + FLAG_ALLOWDISTREAD = 1 << 20, + FLAG_UNUSED = 1 << 21, // unused + FLAG_CLIENTCHARGES = 1 << 22, /* deprecated */ + FLAG_LOOKTHROUGH = 1 << 23, + FLAG_ANIMATION = 1 << 24, + FLAG_FULLTILE = 1 << 25, // unused + FLAG_FORCEUSE = 1 << 26, +}; + +struct OtbLightBlock { + uint16_t lightLevel; + uint16_t lightColor; +}; + +enum slotsOTB_t { + OTB_SLOT_DEFAULT, + OTB_SLOT_HEAD, + OTB_SLOT_BODY, + OTB_SLOT_LEGS, + OTB_SLOT_BACKPACK, + OTB_SLOT_WEAPON, + OTB_SLOT_2HAND, + OTB_SLOT_FEET, + OTB_SLOT_AMULET, + OTB_SLOT_RING, + OTB_SLOT_HAND, +}; + +enum subfightOTB_t { + OTB_DIST_NONE, + OTB_DIST_BOLT, + OTB_DIST_ARROW, + OTB_DIST_FIRE, + OTB_DIST_ENERGY, + OTB_DIST_POISONARROW, + OTB_DIST_BURSTARROW, + OTB_DIST_THROWINGSTAR, + OTB_DIST_THROWINGKNIFE, + OTB_DIST_SMALLSTONE, + OTB_DIST_SUDDENDEATH, + OTB_DIST_LARGEROCK, + OTB_DIST_SNOWBALL, + OTB_DIST_POWERBOLT, + OTB_DIST_SPEAR, + OTB_DIST_POISONFIELD +}; + +struct weaponBlock { + uint8_t weaponType; + uint8_t ammoType; + uint8_t shootType; + uint8_t attack; + uint8_t defense; +}; + +struct ammoBlock { + uint8_t ammoType; + uint8_t shootType; + uint8_t attack; +}; + +struct armorBlock { + uint16_t armor; + double weight; + uint16_t slotPosition; +}; + +struct decayBlock { + uint16_t decayTo; + uint16_t decayTime; +}; + +bool Items::loadFromOtb(const std::string &file) { + if (!std::filesystem::exists(file)) { + std::cout << "[Fatal Error - Items::loadFromOtb] Failed to load " << file << ": File doesn't exist." << std::endl; + return false; + } + + OTB::Loader loader { file, OTBI }; + auto &root = loader.parseTree(); + + PropStream props; + if (loader.getProps(root, props)) { + // 4 byte flags + // attributes + // 0x01 = version data + uint32_t flags; + if (!props.read(flags)) { + return false; + } + + uint8_t attr; + if (!props.read(attr)) { + return false; + } + + if (attr == ROOT_ATTR_VERSION) { + uint16_t datalen; + if (!props.read(datalen)) { + return false; + } + + if (datalen != sizeof(VERSIONINFO)) { + return false; + } + + VERSIONINFO vi; + if (!props.read(vi)) { + return false; + } + + majorVersion = vi.dwMajorVersion; // items otb format file version + minorVersion = vi.dwMinorVersion; // client version + buildNumber = vi.dwBuildNumber; // revision + } + } + + if (majorVersion == 0xFFFFFFFF) { + g_logger().warn("Items::loadFromOtb] items.otb using generic client version."); + } + + for (auto &itemNode : root.children) { + PropStream stream; + if (!loader.getProps(itemNode, stream)) { + return false; + } + + uint32_t flags; + if (!stream.read(flags)) { + return false; + } + + uint16_t serverId = 0; + uint16_t clientId = 0; + uint16_t speed = 0; + uint16_t wareId = 0; + uint8_t lightLevel = 0; + uint8_t lightColor = 0; + uint8_t alwaysOnTopOrder = 0; + + uint8_t attrib; + while (stream.read(attrib)) { + uint16_t datalen; + if (!stream.read(datalen)) { + return false; + } + + switch (attrib) { + case ITEM_ATTR_SERVERID: { + if (datalen != sizeof(uint16_t)) { + return false; + } + + if (!stream.read(serverId)) { + return false; + } + break; + } + + case ITEM_ATTR_CLIENTID: { + if (datalen != sizeof(uint16_t)) { + return false; + } + + if (!stream.read(clientId)) { + return false; + } + break; + } + + case ITEM_ATTR_SPEED: { + if (datalen != sizeof(uint16_t)) { + return false; + } + + if (!stream.read(speed)) { + return false; + } + break; + } + + case ITEM_ATTR_LIGHT2: { + if (datalen != sizeof(OtbLightBlock)) { + return false; + } + + OtbLightBlock light; + if (!stream.read(light)) { + return false; + } + + lightLevel = static_cast(light.lightLevel); + lightColor = static_cast(light.lightColor); + break; + } + + case ITEM_ATTR_TOPORDER: { + if (datalen != sizeof(uint8_t)) { + return false; + } + + if (!stream.read(alwaysOnTopOrder)) { + return false; + } + break; + } + + case ITEM_ATTR_WAREID: { + if (datalen != sizeof(uint16_t)) { + return false; + } + + if (!stream.read(wareId)) { + return false; + } + break; + } + + default: { + // skip unknown attributes + if (!stream.skip(datalen)) { + return false; + } + break; + } + } + } + + // store the found item + if (clientId >= items.size()) { + items.resize(clientId + 1); + } + ItemType &iType = items[clientId]; + + iType.group = static_cast(itemNode.type); + switch (itemNode.type) { + case ITEM_GROUP_CONTAINER: + iType.type = ITEM_TYPE_CONTAINER; + break; + case ITEM_GROUP_DOOR: + // not used + iType.type = ITEM_TYPE_DOOR; + break; + case ITEM_GROUP_MAGICFIELD: + // not used + iType.type = ITEM_TYPE_MAGICFIELD; + break; + case ITEM_GROUP_TELEPORT: + // not used + iType.type = ITEM_TYPE_TELEPORT; + break; + case ITEM_GROUP_NONE: + case ITEM_GROUP_GROUND: + case ITEM_GROUP_SPLASH: + case ITEM_GROUP_FLUID: + case ITEM_GROUP_CHARGES: + case ITEM_GROUP_DEPRECATED: + break; + default: + return false; + } + + iType.blockSolid = hasBitSet(FLAG_BLOCK_SOLID, flags); + iType.blockProjectile = hasBitSet(FLAG_BLOCK_PROJECTILE, flags); + iType.blockPathFind = hasBitSet(FLAG_BLOCK_PATHFIND, flags); + iType.hasHeight = hasBitSet(FLAG_HAS_HEIGHT, flags); + iType.multiUse = hasBitSet(FLAG_USEABLE, flags); + iType.pickupable = hasBitSet(FLAG_PICKUPABLE, flags); + iType.movable = hasBitSet(FLAG_MOVEABLE, flags); + iType.stackable = hasBitSet(FLAG_STACKABLE, flags); + + iType.alwaysOnTopOrder = hasBitSet(FLAG_ALWAYSONTOP, flags); + iType.isVertical = hasBitSet(FLAG_VERTICAL, flags); + iType.isHorizontal = hasBitSet(FLAG_HORIZONTAL, flags); + iType.isHangable = hasBitSet(FLAG_HANGABLE, flags); + iType.allowDistRead = hasBitSet(FLAG_ALLOWDISTREAD, flags); + iType.rotatable = hasBitSet(FLAG_ROTATABLE, flags); + iType.canReadText = hasBitSet(FLAG_READABLE, flags); + iType.lookThrough = hasBitSet(FLAG_LOOKTHROUGH, flags); + iType.m_isAnimation = hasBitSet(FLAG_ANIMATION, flags); + // iType.walkStack = !hasBitSet(FLAG_FULLTILE, flags); + iType.forceUse = hasBitSet(FLAG_FORCEUSE, flags); + + iType.id = clientId; + iType.speed = speed; + iType.lightLevel = lightLevel; + iType.lightColor = lightColor; + iType.wareId = wareId; + iType.alwaysOnTopOrder = alwaysOnTopOrder; + + // TODO: make this as actual .otb flag + if (clientId == 35973 || clientId == 35974) { + iType.isPodium = true; + } + } + + items.shrink_to_fit(); + return true; +} +#endif + bool Items::loadFromXml() { pugi::xml_document doc; - auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + "/items/items.xml"; + std::string xmlLocation; +#if CLIENT_VERSION > 1100 + xmlLocation = "/items/items.xml"; +#else + xmlLocation = "/items/" + std::to_string(CLIENT_VERSION) + "/items.xml"; +#endif + auto folder = g_configManager().getString(CORE_DIRECTORY, __FUNCTION__) + xmlLocation; pugi::xml_parse_result result = doc.load_file(folder.c_str()); if (!result) { printXMLError(__FUNCTION__, folder, result); diff --git a/src/items/items.hpp b/src/items/items.hpp index 3201219d47e..aa01f6c3e68 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -12,7 +12,6 @@ #include "config/configmanager.hpp" #include "utils/utils_definitions.hpp" #include "declarations.hpp" -#include "game/movement/position.hpp" struct Abilities { public: @@ -358,6 +357,7 @@ class ItemType { bool spellbook = false; bool isWrapKit = false; bool m_canBeUsedByGuests = false; + bool m_isAnimation = false; }; class Items { @@ -395,6 +395,7 @@ class Items { ItemTypes_t getLootType(const std::string &strValue); + bool loadFromOtb(const std::string &file); bool loadFromXml(); void parseItemNode(const pugi::xml_node &itemNode, uint16_t id); @@ -428,4 +429,8 @@ class Items { std::vector ladders; std::unordered_map dummys; InventoryVector inventory; + + uint32_t majorVersion = 0; + uint32_t minorVersion = 0; + uint32_t buildNumber = 0; }; diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 3c200daa3e9..8679c874332 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -128,8 +128,18 @@ enum ItemGroup_t { ITEM_GROUP_GROUND, ITEM_GROUP_CONTAINER, + ITEM_GROUP_WEAPON, + ITEM_GROUP_AMMUNITION, + ITEM_GROUP_ARMOR, + ITEM_GROUP_CHARGES, + ITEM_GROUP_TELEPORT, + ITEM_GROUP_MAGICFIELD, + ITEM_GROUP_WRITEABLE, + ITEM_GROUP_KEY, ITEM_GROUP_SPLASH, ITEM_GROUP_FLUID, + ITEM_GROUP_DOOR, + ITEM_GROUP_DEPRECATED, ITEM_GROUP_LAST }; diff --git a/src/lua/creature/actions.cpp b/src/lua/creature/actions.cpp index 4ce69aed8d2..af6a1e4ebf2 100644 --- a/src/lua/creature/actions.cpp +++ b/src/lua/creature/actions.cpp @@ -170,6 +170,7 @@ ReturnValue Actions::canUse(std::shared_ptr player, const Position &pos) if (pos.x != 0xFFFF) { const Position &playerPos = player->getPosition(); if (playerPos.z != pos.z) { + g_logger().info("Return pos different"); return playerPos.z > pos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS; } @@ -299,11 +300,11 @@ ReturnValue Actions::internalUseItem(std::shared_ptr player, const Posit std::shared_ptr openContainer; // depot container - if (std::shared_ptr depot = container->getDepotLocker()) { - std::shared_ptr myDepotLocker = player->getDepotLocker(depot->getDepotId()); - myDepotLocker->setParent(depot->getParent()->getTile()); - openContainer = myDepotLocker; - player->setLastDepotId(depot->getDepotId()); + if (std::shared_ptr mapDepotLocker = container->getDepotLocker()) { + auto playerDepotLocker = player->getDepotLocker(mapDepotLocker->getDepotId()); + playerDepotLocker->setParent(mapDepotLocker->getParent()->getTile()); + openContainer = playerDepotLocker; + player->setLastDepotId(mapDepotLocker->getDepotId()); } else { openContainer = container; } diff --git a/src/lua/creature/raids.cpp b/src/lua/creature/raids.cpp index f30155578b3..a390108d9af 100644 --- a/src/lua/creature/raids.cpp +++ b/src/lua/creature/raids.cpp @@ -300,7 +300,7 @@ bool AnnounceEvent::configureRaidEvent(const pugi::xml_node &eventNode) { } else if (tmpStrValue == "smallstatus") { messageType = MESSAGE_FAILURE; } else if (tmpStrValue == "redconsole") { - messageType = MESSAGE_GAMEMASTER_CONSOLE; + messageType = MESSAGE_STATUS_CONSOLE_RED; } else { g_logger().warn("{} - " "Unknown type tag missing for announce event, " diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index 0ee12e65000..74818dcdf65 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -212,6 +212,7 @@ void LuaEnums::initOthersEnums(lua_State* L) { registerEnum(L, WEAPON_WAND); registerEnum(L, WEAPON_AMMO); registerEnum(L, WEAPON_MISSILE); + registerEnum(L, CLIENT_VERSION); } void LuaEnums::initAccountEnums(lua_State* L) { @@ -707,7 +708,9 @@ void LuaEnums::initConstSlotEnums(lua_State* L) { registerEnum(L, CONST_SLOT_FEET); registerEnum(L, CONST_SLOT_RING); registerEnum(L, CONST_SLOT_AMMO); +#if CLIENT_VERSION >= 1092 registerEnum(L, CONST_SLOT_STORE_INBOX); +#endif registerEnum(L, CONST_SLOT_LAST); } @@ -738,9 +741,9 @@ void LuaEnums::initGameStateEnums(lua_State* L) { } void LuaEnums::initMessageEnums(lua_State* L) { - registerEnum(L, MESSAGE_GAMEMASTER_CONSOLE); + registerEnum(L, MESSAGE_STATUS_CONSOLE_ORANGE); registerEnum(L, MESSAGE_LOGIN); - registerEnum(L, MESSAGE_ADMINISTRADOR); + registerEnum(L, MESSAGE_ADMINISTRATOR); registerEnum(L, MESSAGE_EVENT_ADVANCE); registerEnum(L, MESSAGE_GAME_HIGHLIGHT); registerEnum(L, MESSAGE_FAILURE); @@ -761,7 +764,7 @@ void LuaEnums::initMessageEnums(lua_State* L) { registerEnum(L, MESSAGE_REPORT); registerEnum(L, MESSAGE_HOTKEY_PRESSED); registerEnum(L, MESSAGE_TUTORIAL_HINT); - registerEnum(L, MESSAGE_THANK_YOU); + registerEnum(L, MESSAGE_THANKYOU); registerEnum(L, MESSAGE_MARKET); registerEnum(L, MESSAGE_MANA); registerEnum(L, MESSAGE_BEYOND_LAST); @@ -860,6 +863,8 @@ void LuaEnums::initItemIdEnums(lua_State* L) { registerEnum(L, ITEM_PLATINUM_COIN); registerEnum(L, ITEM_CRYSTAL_COIN); registerEnum(L, ITEM_STORE_COIN); + registerEnum(L, ITEM_ADVENTURERS_STONE); + registerEnum(L, ITEM_WORLD_BOARD); registerEnum(L, ITEM_REWARD_CHEST); registerEnum(L, ITEM_REWARD_CONTAINER); registerEnum(L, ITEM_AMULETOFLOSS); @@ -996,8 +1001,8 @@ void LuaEnums::initTalkTypeEnums(lua_State* L) { registerEnum(L, TALKTYPE_SAY); registerEnum(L, TALKTYPE_WHISPER); registerEnum(L, TALKTYPE_YELL); - registerEnum(L, TALKTYPE_PRIVATE_FROM); - registerEnum(L, TALKTYPE_PRIVATE_TO); + registerEnum(L, TALKTYPE_PRIVATE_PN); + registerEnum(L, TALKTYPE_PRIVATE_NP); registerEnum(L, TALKTYPE_CHANNEL_Y); registerEnum(L, TALKTYPE_CHANNEL_O); registerEnum(L, TALKTYPE_PRIVATE_NP); diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 8315a690f36..7af872cf125 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -643,10 +643,12 @@ int CreatureFunctions::luaCreatureSetOutfit(lua_State* L) { std::shared_ptr creature = getUserdataShared(L, 1); if (creature) { Outfit_t outfit = getOutfit(L, 2); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { g_logger().warn("[CreatureFunctions::luaCreatureSetOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); return 1; } +#endif creature->defaultOutfit = outfit; g_game().internalCreatureChangeOutfit(creature, creature->defaultOutfit); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 269ff6aa3c3..4434b6e9414 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1159,13 +1159,14 @@ int MonsterTypeFunctions::luaMonsterTypeOutfit(lua_State* L) { pushOutfit(L, monsterType->info.outfit); } else { Outfit_t outfit = getOutfit(L, 2); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - g_logger().warn("[MonsterTypeFunctions::luaMonsterTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); - lua_pushnil(L); - } else { - monsterType->info.outfit = outfit; - pushBoolean(L, true); +#if CLIENT_VERSION > 1100 + if (outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { + reportErrorFunc(fmt::format("An unregistered monster looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType)); + return 1; } +#endif + monsterType->info.outfit = outfit; + pushBoolean(L, true); } } else { lua_pushnil(L); diff --git a/src/lua/functions/creatures/npc/npc_type_functions.cpp b/src/lua/functions/creatures/npc/npc_type_functions.cpp index 35bbebbe848..65c3800faac 100644 --- a/src/lua/functions/creatures/npc/npc_type_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_type_functions.cpp @@ -308,13 +308,14 @@ int NpcTypeFunctions::luaNpcTypeOutfit(lua_State* L) { pushOutfit(L, npcType->info.outfit); } else { Outfit_t outfit = getOutfit(L, 2); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { - g_logger().warn("[NpcTypeFunctions::luaNpcTypeOutfit] An unregistered creature looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType); - lua_pushnil(L); - } else { - npcType->info.outfit = getOutfit(L, 2); - pushBoolean(L, true); +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && outfit.lookType != 0 && !g_game().isLookTypeRegistered(outfit.lookType)) { + reportErrorFunc(fmt::format("An unregistered npc looktype type with id '{}' was blocked to prevent client crash.", outfit.lookType)); + return 1; } +#endif + npcType->info.outfit = getOutfit(L, 2); + pushBoolean(L, true); } } else { lua_pushnil(L); diff --git a/src/lua/functions/creatures/player/mount_functions.cpp b/src/lua/functions/creatures/player/mount_functions.cpp index 719c75ad7c2..ad825be046a 100644 --- a/src/lua/functions/creatures/player/mount_functions.cpp +++ b/src/lua/functions/creatures/player/mount_functions.cpp @@ -9,18 +9,20 @@ #include "pch.hpp" -#include "creatures/appearance/mounts/mounts.hpp" -#include "game/game.hpp" -#include "lua/functions/creatures/player/mount_functions.hpp" +#if CLIENT_VERSION >= 870 + + #include "creatures/appearance/mounts/mounts.hpp" + #include "game/game.hpp" + #include "lua/functions/creatures/player/mount_functions.hpp" int MountFunctions::luaCreateMount(lua_State* L) { // Mount(id or name) std::shared_ptr mount; if (isNumber(L, 2)) { - mount = g_game().mounts.getMountByID(getNumber(L, 2)); + mount = g_game().m_mountsPtr->getMountByID(getNumber(L, 2)); } else if (isString(L, 2)) { std::string mountName = getString(L, 2); - mount = g_game().mounts.getMountByName(mountName); + mount = g_game().m_mountsPtr->getMountByName(mountName); } else { mount = nullptr; } @@ -82,3 +84,5 @@ int MountFunctions::luaMountGetSpeed(lua_State* L) { return 1; } + +#endif // CLIENT_VERSION >= 870 diff --git a/src/lua/functions/creatures/player/mount_functions.hpp b/src/lua/functions/creatures/player/mount_functions.hpp index 61222e912b4..16ccf21b011 100644 --- a/src/lua/functions/creatures/player/mount_functions.hpp +++ b/src/lua/functions/creatures/player/mount_functions.hpp @@ -9,7 +9,9 @@ #pragma once -#include "lua/scripts/luascript.hpp" +#if CLIENT_VERSION >= 870 + + #include "lua/scripts/luascript.hpp" class MountFunctions final : LuaScriptInterface { public: @@ -30,3 +32,5 @@ class MountFunctions final : LuaScriptInterface { static int luaMountGetClientId(lua_State* L); static int luaMountGetSpeed(lua_State* L); }; + +#endif diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 1c90bb5b4da..c2241344c5b 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -23,6 +23,10 @@ #include "game/scheduling/dispatcher.hpp" #include "map/spectators.hpp" +#if CLIENT_VERSION >= 870 + #include "creatures/appearance/mounts/mounts.hpp" +#endif + int PlayerFunctions::luaPlayerSendInventory(lua_State* L) { // player:sendInventory() std::shared_ptr player = getUserdataShared(L, 1); @@ -793,6 +797,26 @@ int PlayerFunctions::luaPlayerGetInbox(lua_State* L) { return 1; } +int PlayerFunctions::luaPlayerStoreGetInbox(lua_State* L) { + // player:getStoreInbox() + // In 8.6, is created item in arrow slot + std::shared_ptr player = getUserdataShared(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + std::shared_ptr thing = player->getThing(CONST_SLOT_STORE_INBOX); + auto item = thing->getItem(); + if (item) { + pushUserdata(L, item); + setItemMetatable(L, -1, item); + } else { + pushBoolean(L, false); + } + return 1; +} + int PlayerFunctions::luaPlayerGetSkullTime(lua_State* L) { // player:getSkullTime() std::shared_ptr player = getUserdataShared(L, 1); @@ -899,6 +923,7 @@ int PlayerFunctions::luaPlayerGetMagicShieldCapacityPercent(lua_State* L) { return 1; } +#if CLIENT_VERSION >= 870 int PlayerFunctions::luaPlayerSendSpellCooldown(lua_State* L) { // player:sendSpellCooldown(spellId, time) std::shared_ptr player = getUserdataShared(L, 1); @@ -930,6 +955,7 @@ int PlayerFunctions::luaPlayerSendSpellGroupCooldown(lua_State* L) { return 1; } +#endif int PlayerFunctions::luaPlayerGetMagicLevel(lua_State* L) { // player:getMagicLevel() @@ -2332,6 +2358,7 @@ int PlayerFunctions::luaPlayerSendOutfitWindow(lua_State* L) { } int PlayerFunctions::luaPlayerAddMount(lua_State* L) { +#if CLIENT_VERSION >= 870 // player:addMount(mountId or mountName) std::shared_ptr player = getUserdataShared(L, 1); if (!player) { @@ -2343,7 +2370,7 @@ int PlayerFunctions::luaPlayerAddMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - const std::shared_ptr mount = g_game().mounts.getMountByName(getString(L, 2)); + const std::shared_ptr mount = g_game().m_mountsPtr->getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2351,10 +2378,12 @@ int PlayerFunctions::luaPlayerAddMount(lua_State* L) { mountId = mount->id; } pushBoolean(L, player->tameMount(mountId)); +#endif return 1; } int PlayerFunctions::luaPlayerRemoveMount(lua_State* L) { +#if CLIENT_VERSION >= 870 // player:removeMount(mountId or mountName) std::shared_ptr player = getUserdataShared(L, 1); if (!player) { @@ -2366,7 +2395,7 @@ int PlayerFunctions::luaPlayerRemoveMount(lua_State* L) { if (isNumber(L, 2)) { mountId = getNumber(L, 2); } else { - const std::shared_ptr mount = g_game().mounts.getMountByName(getString(L, 2)); + const std::shared_ptr mount = g_game().m_mountsPtr->getMountByName(getString(L, 2)); if (!mount) { lua_pushnil(L); return 1; @@ -2374,10 +2403,12 @@ int PlayerFunctions::luaPlayerRemoveMount(lua_State* L) { mountId = mount->id; } pushBoolean(L, player->untameMount(mountId)); +#endif return 1; } int PlayerFunctions::luaPlayerHasMount(lua_State* L) { +#if CLIENT_VERSION >= 870 // player:hasMount(mountId or mountName) std::shared_ptr player = getUserdataShared(L, 1); if (!player) { @@ -2387,9 +2418,9 @@ int PlayerFunctions::luaPlayerHasMount(lua_State* L) { std::shared_ptr mount = nullptr; if (isNumber(L, 2)) { - mount = g_game().mounts.getMountByID(getNumber(L, 2)); + mount = g_game().m_mountsPtr->getMountByID(getNumber(L, 2)); } else { - mount = g_game().mounts.getMountByName(getString(L, 2)); + mount = g_game().m_mountsPtr->getMountByName(getString(L, 2)); } if (mount) { @@ -2397,6 +2428,7 @@ int PlayerFunctions::luaPlayerHasMount(lua_State* L) { } else { lua_pushnil(L); } +#endif return 1; } diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 47b9f4b3a34..8cbb764feba 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -78,6 +78,7 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "getDepotLocker", PlayerFunctions::luaPlayerGetDepotLocker); registerMethod(L, "Player", "getDepotChest", PlayerFunctions::luaPlayerGetDepotChest); registerMethod(L, "Player", "getInbox", PlayerFunctions::luaPlayerGetInbox); + registerMethod(L, "Player", "getStoreInbox", PlayerFunctions::luaPlayerStoreGetInbox); registerMethod(L, "Player", "getSkullTime", PlayerFunctions::luaPlayerGetSkullTime); registerMethod(L, "Player", "setSkullTime", PlayerFunctions::luaPlayerSetSkullTime); @@ -91,8 +92,10 @@ class PlayerFunctions final : LuaScriptInterface { registerMethod(L, "Player", "getMagicShieldCapacityFlat", PlayerFunctions::luaPlayerGetMagicShieldCapacityFlat); registerMethod(L, "Player", "getMagicShieldCapacityPercent", PlayerFunctions::luaPlayerGetMagicShieldCapacityPercent); +#if CLIENT_VERSION >= 870 registerMethod(L, "Player", "sendSpellCooldown", PlayerFunctions::luaPlayerSendSpellCooldown); registerMethod(L, "Player", "sendSpellGroupCooldown", PlayerFunctions::luaPlayerSendSpellGroupCooldown); +#endif registerMethod(L, "Player", "getMagicLevel", PlayerFunctions::luaPlayerGetMagicLevel); registerMethod(L, "Player", "getBaseMagicLevel", PlayerFunctions::luaPlayerGetBaseMagicLevel); @@ -359,7 +362,9 @@ class PlayerFunctions final : LuaScriptInterface { GroupFunctions::init(L); GuildFunctions::init(L); +#if CLIENT_VERSION >= 870 MountFunctions::init(L); +#endif PartyFunctions::init(L); VocationFunctions::init(L); } @@ -422,6 +427,7 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerGetDepotLocker(lua_State* L); static int luaPlayerGetDepotChest(lua_State* L); static int luaPlayerGetInbox(lua_State* L); + static int luaPlayerStoreGetInbox(lua_State* L); static int luaPlayerGetSkullTime(lua_State* L); static int luaPlayerSetSkullTime(lua_State* L); diff --git a/src/lua/functions/map/position_functions.cpp b/src/lua/functions/map/position_functions.cpp index b68d8501588..245ebed4ab3 100644 --- a/src/lua/functions/map/position_functions.cpp +++ b/src/lua/functions/map/position_functions.cpp @@ -160,11 +160,12 @@ int PositionFunctions::luaPositionSendMagicEffect(lua_State* L) { } MagicEffectClasses magicEffect = getNumber(L, 2); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && !g_game().isMagicEffectRegistered(magicEffect)) { - g_logger().warn("[PositionFunctions::luaPositionSendMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); - pushBoolean(L, false); +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && !g_game().isMagicEffectRegistered(magicEffect)) { + reportErrorFunc(fmt::format("An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect))); return 1; } +#endif const Position &position = getPosition(L, 1); if (!spectators.empty()) { @@ -191,11 +192,12 @@ int PositionFunctions::luaPositionRemoveMagicEffect(lua_State* L) { } MagicEffectClasses magicEffect = getNumber(L, 2); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && !g_game().isMagicEffectRegistered(magicEffect)) { - g_logger().warn("[PositionFunctions::luaPositionRemoveMagicEffect] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect)); - pushBoolean(L, false); +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && !g_game().isMagicEffectRegistered(magicEffect)) { + reportErrorFunc(fmt::format("An unregistered magic effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(magicEffect))); return 1; } +#endif const Position &position = getPosition(L, 1); if (!spectators.empty()) { @@ -224,10 +226,12 @@ int PositionFunctions::luaPositionSendDistanceEffect(lua_State* L) { ShootType_t distanceEffect = getNumber(L, 3); const Position &positionEx = getPosition(L, 2); const Position &position = getPosition(L, 1); - if (g_configManager().getBoolean(WARN_UNSAFE_SCRIPTS, __FUNCTION__) && !g_game().isDistanceEffectRegistered(distanceEffect)) { - g_logger().warn("[PositionFunctions::luaPositionSendDistanceEffect] An unregistered distance effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(distanceEffect)); +#if CLIENT_VERSION > 1100 + if (g_configManager().getBoolean(WARN_UNREGISTERED_DAT_INFO, __FUNCTION__) && !g_game().isDistanceEffectRegistered(distanceEffect)) { + reportErrorFunc(fmt::format("An unregistered distance effect type with id '{}' was blocked to prevent client crash.", fmt::underlying(distanceEffect))); return 1; } +#endif if (!spectators.empty()) { Game::addDistanceEffect(spectators, position, positionEx, distanceEffect); diff --git a/src/map/map.cpp b/src/map/map.cpp index 662e85f18d7..6e423dcf17d 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -34,7 +34,8 @@ void Map::load(const std::string &identifier, const Position &pos) { } void Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool loadHouses /*= false*/, bool loadMonsters /*= false*/, bool loadNpcs /*= false*/, bool loadZones /*= false*/, const Position &pos /*= Position()*/) { - // Only download map if is loading the main map and it is not already downloaded +// Only download map if is loading the main map and it is not already downloaded +#if CLIENT_VERSION > 1100 if (mainMap && g_configManager().getBoolean(TOGGLE_DOWNLOAD_MAP, __FUNCTION__) && !std::filesystem::exists(identifier)) { const auto mapDownloadUrl = g_configManager().getString(MAP_DOWNLOAD_URL, __FUNCTION__); if (mapDownloadUrl.empty()) { @@ -53,6 +54,7 @@ void Map::loadMap(const std::string &identifier, bool mainMap /*= false*/, bool fclose(otbm); } } +#endif // Load the map load(identifier, pos); diff --git a/src/pch.hpp b/src/pch.hpp index ea0233ee1a8..5e574f33632 100644 --- a/src/pch.hpp +++ b/src/pch.hpp @@ -13,6 +13,9 @@ // Internal Includes // -------------------- +// Common macros +#include "core.hpp" + // Utils #include "utils/benchmark.hpp" #include "utils/definitions.hpp" diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index fd8413faa13..596838dda97 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -32,6 +32,10 @@ #include "creatures/players/management/waitlist.hpp" #include "items/weapons/weapons.hpp" +#if CLIENT_VERSION >= 870 + #include "creatures/appearance/mounts/mounts.hpp" +#endif + /* * NOTE: This namespace is used so that we can add functions without having to declare them in the ".hpp/.hpp" file * Do not use functions only in the .cpp scope without having a namespace, it may conflict with functions in other files of the same name @@ -120,7 +124,7 @@ namespace { if (imbuementInfo.duration > 0) { auto imbuement = *imbuementInfo.imbuement; if (imbuement.combatType != COMBAT_NONE) { - msg.addByte(static_cast(imbuement.elementDamage)); + msg.addByte(imbuement.elementDamage); msg.addByte(getCipbiaElement(imbuement.combatType)); imbueDmg = true; break; @@ -253,10 +257,11 @@ void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint const ItemType &it = Item::items[id]; msg.add(it.id); - +#if CLIENT_VERSION >= 1000 if (oldProtocol) { - msg.addByte(0xFF); + msg.addByte(0xFF); // // Mark unmarked } +#endif if (it.stackable) { msg.addByte(count); @@ -266,6 +271,7 @@ void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint msg.addByte(count); } +#if CLIENT_VERSION >= 910 if (oldProtocol) { if (it.animationType == ANIMATION_RANDOM) { msg.addByte(0xFE); @@ -306,6 +312,7 @@ void ProtocolGame::AddItem(NetworkMessage &msg, uint16_t id, uint8_t count, uint if (it.isWrapKit && !oldProtocol) { msg.add(0x00); } +#endif } void ProtocolGame::AddItem(NetworkMessage &msg, std::shared_ptr item) { @@ -317,9 +324,11 @@ void ProtocolGame::AddItem(NetworkMessage &msg, std::shared_ptr item) { msg.add(it.id); +#if CLIENT_VERSION >= 1000 if (oldProtocol) { - msg.addByte(0xFF); + msg.addByte(0xFF); // // Mark unmarked } +#endif if (it.stackable) { msg.addByte(static_cast(std::min(std::numeric_limits::max(), item->getItemCount()))); @@ -329,6 +338,7 @@ void ProtocolGame::AddItem(NetworkMessage &msg, std::shared_ptr item) { msg.addByte(item->getAttribute(ItemAttribute_t::FLUIDTYPE)); } +#if CLIENT_VERSION >= 910 if (oldProtocol) { if (it.animationType == ANIMATION_RANDOM) { msg.addByte(0xFE); @@ -450,6 +460,7 @@ void ProtocolGame::AddItem(NetworkMessage &msg, std::shared_ptr item) { msg.add(0x00); } } +#endif } void ProtocolGame::release() { @@ -689,7 +700,11 @@ void ProtocolGame::logout(bool displayEffect, bool forced) { g_game().addMagicEffect(player->getPosition(), CONST_ME_POFF); } +#if CLIENT_VERSION >= 1262 sendSessionEndInformation(forced ? SESSION_END_FORCECLOSE : SESSION_END_LOGOUT); +#else + disconnect(); +#endif g_game().removeCreature(player, true); } @@ -705,20 +720,23 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { // Old protocol support oldProtocol = g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__) && version <= 1100; - if (oldProtocol) { setChecksumMethod(CHECKSUM_METHOD_ADLER32); } else if (operatingSystem <= CLIENTOS_OTCLIENT_MAC) { setChecksumMethod(CHECKSUM_METHOD_SEQUENCE); } +#if CLIENT_VERSION >= 971 clientVersion = static_cast(msg.get()); +#endif if (!oldProtocol) { msg.getString(); // Client version (String) } +#if CLIENT_VERSION >= 1100 msg.skipBytes(3); // U16 dat revision, U8 game preview state +#endif if (!Protocol::RSA_decrypt(msg)) { g_logger().warn("[ProtocolGame::onRecvFirstMessage] - RSA Decrypt Failed"); @@ -734,6 +752,7 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { std::string authType = g_configManager().getString(AUTH_TYPE, __FUNCTION__); std::ostringstream ss; +#if CLIENT_VERSION >= 1074 std::string sessionKey = msg.getString(); std::string accountDescriptor = sessionKey; std::string password = ""; @@ -760,9 +779,26 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { msg.getString(); msg.getString(); } +#endif + +#if CLIENT_VERSION >= 830 && CLIENT_VERSION < 1220 + std::string accountDescriptor = msg.getString(); + if (accountDescriptor.empty()) { + disconnectClient("You must enter your account name."); + return; + } +#endif std::string characterName = msg.getString(); +#if CLIENT_VERSION >= 830 && CLIENT_VERSION < 1220 + std::string password = msg.getString(); + if (password.empty()) { + disconnectClient("Invalid password."); + return; + } +#endif + std::shared_ptr foundPlayer = g_game().getPlayerUniqueLogin(characterName); if (foundPlayer && foundPlayer->client) { if (foundPlayer->getProtocolVersion() != getVersion() && foundPlayer->isOldProtocol() != oldProtocol) { @@ -786,17 +822,6 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) { otclientV8 = msg.get(); // 253, 260, 261, ... } - if (!oldProtocol && clientVersion != CLIENT_VERSION) { - ss.str(std::string()); - ss << "Only clients with protocol " << CLIENT_VERSION_UPPER << "." << CLIENT_VERSION_LOWER; - if (g_configManager().getBoolean(OLD_PROTOCOL, __FUNCTION__)) { - ss << " or 11.00"; - } - ss << " allowed!"; - disconnectClient(ss.str()); - return; - } - if (g_game().getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait."); return; @@ -1355,11 +1380,13 @@ void ProtocolGame::parseHotkeyEquip(NetworkMessage &msg) { } void ProtocolGame::GetTileDescription(std::shared_ptr tile, NetworkMessage &msg) { +#if CLIENT_VERSION >= 910 if (oldProtocol) { msg.add(0x00); // Env effects } +#endif - int32_t count; + int32_t count = 0; std::shared_ptr ground = tile->getGround(); if (ground) { AddItem(msg, ground); @@ -1368,16 +1395,13 @@ void ProtocolGame::GetTileDescription(std::shared_ptr tile, NetworkMessage count = 0; } + static const uint8_t maxStackCount = 10; const TileItemVector* items = tile->getItemList(); if (items) { for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) { AddItem(msg, *it); - - count++; - if (count == 9 && tile->getPosition() == player->getPosition()) { + if (++count == maxStackCount) { break; - } else if (count == 10) { - return; } } } @@ -1385,36 +1409,40 @@ void ProtocolGame::GetTileDescription(std::shared_ptr tile, NetworkMessage const CreatureVector* creatures = tile->getCreatures(); if (creatures) { bool playerAdded = false; - for (auto it = creatures->rbegin(); it != creatures->rend(); ++it) { - std::shared_ptr creature = *it; - if (!player->canSeeCreature(creature)) { - continue; - } + if (count < maxStackCount) { + for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) { + auto creature = (*it); + if (!player->canSeeCreature(creature)) { + continue; + } - if (tile->getPosition() == player->getPosition() && count == 9 && !playerAdded) { - creature = player; - } + if (creature->getID() == player->getID()) { + playerAdded = true; + } - if (creature->getID() == player->getID()) { - playerAdded = true; + bool known; + uint32_t removedKnown; + checkCreatureAsKnown(creature->getID(), known, removedKnown); + AddCreature(msg, creature, known, removedKnown); + if (++count == maxStackCount) { + break; + } } + } + if (!playerAdded && tile->getPosition() == player->getPosition()) { + const auto creature = player; bool known; uint32_t removedKnown; checkCreatureAsKnown(creature->getID(), known, removedKnown); AddCreature(msg, creature, known, removedKnown); - - if (++count == 10) { - return; - } } } - if (items) { - for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) { + if (items && count < maxStackCount) { + for (auto it = ItemVector::const_reverse_iterator(items->getEndDownItem()), end = ItemVector::const_reverse_iterator(items->getBeginDownItem()); it != end; ++it) { AddItem(msg, *it); - - if (++count == 10) { + if (++count == maxStackCount) { return; } } @@ -1475,16 +1503,27 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool &known, uint32_t &remo return; } known = false; - if (knownCreatureSet.size() > 1300) { + size_t compareCreaturesSize; +#if CLIENT_VERSION >= 870 + compareCreaturesSize = 1300; +#elif CLIENT_VERSION >= 840 + compareCreaturesSize = 250; +#elif CLIENT_VERSION >= 713 + compareCreaturesSize = 150; +#else + compareCreaturesSize = 100; +#endif + if (knownCreatureSet.size() > compareCreaturesSize) { // Look for a creature to remove for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) { if (*it == id) { continue; } + // We need to protect party players from removing std::shared_ptr creature = g_game().getCreatureByID(*it); if (std::shared_ptr checkPlayer; - creature && (checkPlayer = creature->getPlayer()) != nullptr) { + !oldProtocol && creature && (checkPlayer = creature->getPlayer()) != nullptr) { if (player->getParty() != checkPlayer->getParty() && !canSee(creature)) { removedKnown = *it; knownCreatureSet.erase(it); @@ -1646,6 +1685,7 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { newOutfit.lookFeet = std::min(132, msg.getByte()); newOutfit.lookAddons = msg.getByte(); if (outfitType == 0) { +#if CLIENT_VERSION >= 870 newOutfit.lookMount = msg.get(); if (!oldProtocol) { newOutfit.lookMountHead = std::min(132, msg.getByte()); @@ -1656,7 +1696,12 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { } uint8_t isMountRandomized = msg.getByte(); g_game().playerChangeOutfit(player->getID(), newOutfit, isMountRandomized); - } else if (outfitType == 1) { +#else + g_game().playerChangeOutfit(player->getID(), newOutfit); +#endif + } +#if CLIENT_VERSION >= 870 + else if (outfitType == 1) { // This value probably has something to do with try outfit variable inside outfit window dialog // if try outfit is set to 2 it expects uint32_t value after mounted and disable mounts from outfit window dialog newOutfit.lookMount = 0; @@ -1674,6 +1719,7 @@ void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { uint8_t podiumVisible = msg.getByte(); g_game().playerSetShowOffSocket(player->getID(), newOutfit, pos, stackpos, itemId, podiumVisible, direction); } +#endif } } @@ -1840,14 +1886,16 @@ void ProtocolGame::parseQuickLootBlackWhitelist(NetworkMessage &msg) { void ProtocolGame::parseSay(NetworkMessage &msg) { std::string receiver; - uint16_t channelId; + uint16_t channelId = 0; SpeakClasses type = static_cast(msg.getByte()); switch (type) { case TALKTYPE_PRIVATE_TO: case TALKTYPE_PRIVATE_RED_TO: +#if CLIENT_VERSION <= 860 + case TALKTYPE_RVR_ANSWER: +#endif receiver = msg.getString(); - channelId = 0; break; case TALKTYPE_CHANNEL_Y: @@ -1856,7 +1904,6 @@ void ProtocolGame::parseSay(NetworkMessage &msg) { break; default: - channelId = 0; break; } @@ -1972,11 +2019,13 @@ void ProtocolGame::parseRotateItem(NetworkMessage &msg) { uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); const auto &itemType = Item::items[itemId]; +#if CLIENT_VERSION >= 1100 if (itemType.isPodium) { addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerRotatePodium", &Game::playerRotatePodium, player->getID(), pos, stackpos, itemId); - } else { - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerRotateItem", &Game::playerRotateItem, player->getID(), pos, stackpos, itemId); + return; } +#endif + addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, "Game::playerRotateItem", &Game::playerRotateItem, player->getID(), pos, stackpos, itemId); } void ProtocolGame::parseWrapableItem(NetworkMessage &msg) { @@ -2045,10 +2094,7 @@ void ProtocolGame::sendItemInspection(uint16_t itemId, uint8_t itemCount, std::s } void ProtocolGame::parseCyclopediaCharacterInfo(NetworkMessage &msg) { - if (oldProtocol) { - return; - } - +#if CLIENT_VERSION > 1100 uint32_t characterID; CyclopediaCharacterInfoType_t characterInfoType; characterID = msg.get(); @@ -2062,6 +2108,7 @@ void ProtocolGame::parseCyclopediaCharacterInfo(NetworkMessage &msg) { characterID = player->getGUID(); } g_game().playerCyclopediaCharacterInfo(player, characterID, characterInfoType, entriesPerPage, page); +#endif } void ProtocolGame::parseHighscores(NetworkMessage &msg) { @@ -2197,14 +2244,13 @@ void ProtocolGame::sendHighscores(const std::vector &charact } void ProtocolGame::parseConfigureShowOffSocket(NetworkMessage &msg) { - if (oldProtocol) { - return; - } +#if CLIENT_VERSION > 1100 Position pos = msg.getPosition(); uint16_t itemId = msg.get(); uint8_t stackpos = msg.getByte(); g_game().playerConfigureShowOffSocket(player->getID(), pos, stackpos, itemId); +#endif } void ProtocolGame::parseRuleViolationReport(NetworkMessage &msg) { @@ -3159,12 +3205,14 @@ void ProtocolGame::sendCreatureOutfit(std::shared_ptr creature, const msg.addByte(0x8E); msg.add(creature->getID()); AddOutfit(msg, outfit); - if (!oldProtocol && outfit.lookMount != 0) { +#if CLIENT_VERSION >= 1260 + if (outfit.lookMount != 0) { msg.addByte(outfit.lookMountHead); msg.addByte(outfit.lookMountBody); msg.addByte(outfit.lookMountLegs); msg.addByte(outfit.lookMountFeet); } +#endif writeToOutputBuffer(msg); } @@ -3315,10 +3363,19 @@ void ProtocolGame::sendCreatureSquare(std::shared_ptr creature, Square } NetworkMessage msg; +#if CLIENT_VERSION >= 1000 msg.addByte(0x93); + #if CLIENT_VERSION < 1035 + msg.addByte(0x01); + #endif msg.add(creature->getID()); msg.addByte(0x01); msg.addByte(color); +#else + msg.addByte(0x86); + msg.add(creature->getID()); + msg.addByte(color); +#endif writeToOutputBuffer(msg); } @@ -3343,6 +3400,7 @@ void ProtocolGame::sendAddMarker(const Position &pos, uint8_t markType, const st writeToOutputBuffer(msg); } +#if CLIENT_VERSION > 1100 void ProtocolGame::sendCyclopediaCharacterNoData(CyclopediaCharacterInfoType_t characterInfoType, uint8_t errorCode) { if (!player || oldProtocol) { return; @@ -3719,7 +3777,7 @@ void ProtocolGame::sendCyclopediaCharacterOutfitsMounts() { uint16_t mountSize = 0; auto startMounts = msg.getBufferPosition(); msg.skipBytes(2); - for (const auto mount : g_game().mounts.getMounts()) { + for (const auto &mount : g_game().m_mountsPtr->getMounts()) { const std::string type = mount->type; if (player->hasMount(mount)) { ++mountSize; @@ -3908,15 +3966,20 @@ void ProtocolGame::sendCyclopediaCharacterTitles() { msg.addByte(0x00); writeToOutputBuffer(msg); } +#endif void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction) { NetworkMessage msg; msg.addByte(0x28); +#if CLIENT_VERSION >= 1055 msg.addByte(0x00); +#endif +#if CLIENT_VERSION >= 862 msg.addByte(unfairFightReduction); - if (!oldProtocol) { - msg.addByte(0x00); // use death redemption (boolean) - } +#endif +#if CLIENT_VERSION >= 1121 + msg.addByte(0x01); // use death redemption (boolean) +#endif writeToOutputBuffer(msg); } @@ -3927,6 +3990,7 @@ void ProtocolGame::sendStats() { } void ProtocolGame::sendBasicData() { +#if CLIENT_VERSION >= 950 NetworkMessage msg; msg.addByte(0x9F); if (player->isPremium() || player->isVip()) { @@ -3989,6 +4053,7 @@ void ProtocolGame::sendBasicData() { } writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendBlessStatus() { @@ -3996,6 +4061,7 @@ void ProtocolGame::sendBlessStatus() { return; } +#if CLIENT_VERSION >= 1100 NetworkMessage msg; // uint8_t maxClientBlessings = (player->operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6; (compartability for the client 10) // Ignore ToF (bless 1) @@ -4022,9 +4088,11 @@ void ProtocolGame::sendBlessStatus() { } writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendPremiumTrigger() { +#if CLIENT_VERSION >= 1100 if (!g_configManager().getBoolean(FREE_PREMIUM, __FUNCTION__) && !g_configManager().getBoolean(VIP_SYSTEM_ENABLED, __FUNCTION__)) { NetworkMessage msg; msg.addByte(0x9E); @@ -4035,78 +4103,913 @@ void ProtocolGame::sendPremiumTrigger() { } writeToOutputBuffer(msg); } +#endif +} + +uint8_t translateSpeakClassToClient(SpeakClasses talkType) { +#if CLIENT_VERSION >= 1055 + #if CLIENT_VERSION >= 1200 + if (talkType == TALKTYPE_BOOSTED_CREATURE) { + return 0x31; + } + #endif + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x04; + case TALKTYPE_PRIVATE_TO: + return 0x05; + case TALKTYPE_CHANNEL_M: + return 0x06; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x08; + case TALKTYPE_SPELL_USE: + return 0x09; + case TALKTYPE_PRIVATE_NP: + return 0x0A; + case TALKTYPE_PRIVATE_PN: + return 0x0C; + case TALKTYPE_BROADCAST: + return 0x0D; + case TALKTYPE_CHANNEL_R1: + return 0x0E; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0F; + case TALKTYPE_PRIVATE_RED_TO: + return 0x10; + case TALKTYPE_MONSTER_SAY: + return 0x24; + case TALKTYPE_MONSTER_YELL: + return 0x25; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 1036 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x04; + case TALKTYPE_PRIVATE_TO: + return 0x05; + case TALKTYPE_CHANNEL_M: + return 0x06; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x08; + case TALKTYPE_SPELL_USE: + return 0x09; + case TALKTYPE_PRIVATE_NP: + return 0x0A; + case TALKTYPE_PRIVATE_PN: + return 0x0C; + case TALKTYPE_BROADCAST: + return 0x0D; + case TALKTYPE_CHANNEL_R1: + return 0x0E; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0F; + case TALKTYPE_PRIVATE_RED_TO: + return 0x10; + case TALKTYPE_MONSTER_SAY: + return 0x23; + case TALKTYPE_MONSTER_YELL: + return 0x24; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 900 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x04; + case TALKTYPE_PRIVATE_TO: + return 0x05; + case TALKTYPE_CHANNEL_M: + return 0x06; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x08; + case TALKTYPE_SPELL_USE: + return 0x09; + case TALKTYPE_PRIVATE_NP: + return 0x0A; + case TALKTYPE_PRIVATE_PN: + return 0x0B; + case TALKTYPE_BROADCAST: + return 0x0C; + case TALKTYPE_CHANNEL_R1: + return 0x0D; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0E; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0F; + case TALKTYPE_MONSTER_SAY: + return 0x22; + case TALKTYPE_MONSTER_YELL: + return 0x23; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 861 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x06; + case TALKTYPE_PRIVATE_TO: + return 0x06; + case TALKTYPE_CHANNEL_M: + return 0x08; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x0C; + case TALKTYPE_SPELL_USE: + return 0x01; + case TALKTYPE_PRIVATE_NP: + return 0x05; + case TALKTYPE_PRIVATE_PN: + return 0x04; + case TALKTYPE_BROADCAST: + return 0x09; + case TALKTYPE_CHANNEL_R1: + return 0x0A; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0B; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0B; + case TALKTYPE_MONSTER_SAY: + return 0x0D; + case TALKTYPE_MONSTER_YELL: + return 0x0E; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 840 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x06; + case TALKTYPE_PRIVATE_TO: + return 0x06; + case TALKTYPE_CHANNEL_M: + return 0x08; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x0F; + case TALKTYPE_RVR_CHANNEL: + return 0x09; + case TALKTYPE_RVR_ANSWER: + return 0x0A; + case TALKTYPE_RVR_CONTINUE: + return 0x0B; + case TALKTYPE_SPELL_USE: + return 0x01; + case TALKTYPE_PRIVATE_NP: + return 0x05; + case TALKTYPE_PRIVATE_PN: + return 0x04; + case TALKTYPE_BROADCAST: + return 0x0C; + case TALKTYPE_CHANNEL_R1: + return 0x0D; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0E; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0E; + case TALKTYPE_MONSTER_SAY: + return 0x13; + case TALKTYPE_MONSTER_YELL: + return 0x14; + case TALKTYPE_CHANNEL_R2: + return TALKTYPE_NONE; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 820 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x06; + case TALKTYPE_PRIVATE_TO: + return 0x06; + case TALKTYPE_CHANNEL_M: + return 0x07; + case TALKTYPE_CHANNEL_Y: + return 0x07; + case TALKTYPE_CHANNEL_O: + return 0x0E; + case TALKTYPE_RVR_CHANNEL: + return 0x08; + case TALKTYPE_RVR_ANSWER: + return 0x09; + case TALKTYPE_RVR_CONTINUE: + return 0x0A; + case TALKTYPE_SPELL_USE: + return 0x01; + case TALKTYPE_PRIVATE_NP: + return 0x05; + case TALKTYPE_PRIVATE_PN: + return 0x04; + case TALKTYPE_BROADCAST: + return 0x0B; + case TALKTYPE_CHANNEL_R1: + return 0x0C; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0D; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0D; + case TALKTYPE_MONSTER_SAY: + return 0x12; + case TALKTYPE_MONSTER_YELL: + return 0x13; + case TALKTYPE_CHANNEL_R2: + return TALKTYPE_NONE; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 723 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x04; + case TALKTYPE_PRIVATE_TO: + return 0x04; + case TALKTYPE_CHANNEL_M: + return 0x05; + case TALKTYPE_CHANNEL_Y: + return 0x05; + case TALKTYPE_CHANNEL_O: + return 0x0C; + case TALKTYPE_RVR_CHANNEL: + return 0x06; + case TALKTYPE_RVR_ANSWER: + return 0x07; + case TALKTYPE_RVR_CONTINUE: + return 0x08; + case TALKTYPE_SPELL_USE: + return 0x01; + case TALKTYPE_PRIVATE_NP: + return 0x01; + case TALKTYPE_PRIVATE_PN: + return 0x01; + case TALKTYPE_BROADCAST: + return 0x09; + case TALKTYPE_CHANNEL_R1: + return 0x0A; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0B; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0B; + case TALKTYPE_MONSTER_SAY: + return 0x10; + case TALKTYPE_MONSTER_YELL: + return 0x11; + case TALKTYPE_CHANNEL_R2: + return TALKTYPE_NONE; + default: + return TALKTYPE_NONE; + } +#elif CLIENT_VERSION >= 710 + switch (talkType) { + case TALKTYPE_SAY: + return 0x01; + case TALKTYPE_WHISPER: + return 0x02; + case TALKTYPE_YELL: + return 0x03; + case TALKTYPE_PRIVATE_FROM: + return 0x04; + case TALKTYPE_PRIVATE_TO: + return 0x04; + case TALKTYPE_CHANNEL_M: + return 0x05; + case TALKTYPE_CHANNEL_Y: + return 0x05; + case TALKTYPE_CHANNEL_O: + return 0x05; + case TALKTYPE_RVR_CHANNEL: + return 0x06; + case TALKTYPE_RVR_ANSWER: + return 0x07; + case TALKTYPE_RVR_CONTINUE: + return 0x08; + case TALKTYPE_SPELL_USE: + return 0x01; + case TALKTYPE_PRIVATE_NP: + return 0x01; + case TALKTYPE_PRIVATE_PN: + return 0x01; + case TALKTYPE_BROADCAST: + return 0x09; + case TALKTYPE_CHANNEL_R1: + return 0x05; + case TALKTYPE_PRIVATE_RED_FROM: + return 0x0A; + case TALKTYPE_PRIVATE_RED_TO: + return 0x0A; + case TALKTYPE_MONSTER_SAY: + return 0x0D; + case TALKTYPE_MONSTER_YELL: + return 0x0E; + case TALKTYPE_CHANNEL_R2: + return TALKTYPE_NONE; + default: + return TALKTYPE_NONE; + } +#endif +} + +uint8_t translateMessageClassToClient(MessageClasses messageType) { +#if CLIENT_VERSION >= 1055 + #if CLIENT_VERSION >= 1094 + if (messageType == MESSAGE_MANA) { + return 0x2B; + } + #endif + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x04; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x0D; + case MESSAGE_STATUS_DEFAULT: + return 0x11; + case MESSAGE_STATUS_WARNING: + return 0x12; + case MESSAGE_EVENT_ADVANCE: + return 0x13; + case MESSAGE_STATUS_SMALL: + return 0x15; + case MESSAGE_INFO_DESCR: + return 0x16; + case MESSAGE_EVENT_DEFAULT: + return 0x1E; + case MESSAGE_GUILD: + return 0x21; + case MESSAGE_PARTY_MANAGEMENT: + return 0x22; + case MESSAGE_PARTY: + return 0x23; + case MESSAGE_EVENT_ORANGE: + return 0x24; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x25; + case MESSAGE_DAMAGE_DEALT: + return 0x17; + case MESSAGE_DAMAGE_RECEIVED: + return 0x18; + case MESSAGE_MANA: + return 0x19; + case MESSAGE_HEALED: + return 0x19; + case MESSAGE_EXPERIENCE: + return 0x1A; + case MESSAGE_DAMAGE_OTHERS: + return 0x1B; + case MESSAGE_HEALED_OTHERS: + return 0x1C; + case MESSAGE_EXPERIENCE_OTHERS: + return 0x1D; + case MESSAGE_LOOT: + return 0x1F; + case MESSAGE_LOGIN: + return 0x11; + case MESSAGE_ADMINISTRATOR: + return 0x12; + case MESSAGE_GAME: + return 0x13; + case MESSAGE_GAME_HIGHLIGHT: + return 0x14; + case MESSAGE_FAILURE: + return 0x15; + case MESSAGE_LOOK: + return 0x16; + case MESSAGE_STATUS: + return 0x1E; + case MESSAGE_TRADE: + return 0x20; + case MESSAGE_REPORT: + return 0x26; + case MESSAGE_HOTKEY: + return 0x27; + case MESSAGE_TUTORIAL: + return 0x28; + case MESSAGE_THANKYOU: + return 0x29; + case MESSAGE_MARKET: + return 0x2A; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 1036 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x04; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x0D; + case MESSAGE_STATUS_DEFAULT: + return 0x11; + case MESSAGE_STATUS_WARNING: + return 0x12; + case MESSAGE_EVENT_ADVANCE: + return 0x13; + case MESSAGE_STATUS_SMALL: + return 0x14; + case MESSAGE_INFO_DESCR: + return 0x15; + case MESSAGE_EVENT_DEFAULT: + return 0x1D; + case MESSAGE_GUILD: + return 0x20; + case MESSAGE_PARTY_MANAGEMENT: + return 0x21; + case MESSAGE_PARTY: + return 0x22; + case MESSAGE_EVENT_ORANGE: + return 0x23; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x24; + case MESSAGE_DAMAGE_DEALT: + return 0x16; + case MESSAGE_DAMAGE_RECEIVED: + return 0x17; + case MESSAGE_MANA: + return 0x18; + case MESSAGE_HEALED: + return 0x18; + case MESSAGE_EXPERIENCE: + return 0x19; + case MESSAGE_DAMAGE_OTHERS: + return 0x1A; + case MESSAGE_HEALED_OTHERS: + return 0x1B; + case MESSAGE_EXPERIENCE_OTHERS: + return 0x1C; + case MESSAGE_LOOT: + return 0x1E; + case MESSAGE_LOGIN: + return 0x11; + case MESSAGE_ADMINISTRATOR: + return 0x12; + case MESSAGE_GAME: + return 0x13; + case MESSAGE_GAME_HIGHLIGHT: + return 0x12; + case MESSAGE_FAILURE: + return 0x14; + case MESSAGE_LOOK: + return 0x15; + case MESSAGE_STATUS: + return 0x1D; + case MESSAGE_TRADE: + return 0x1F; + case MESSAGE_REPORT: + return 0x25; + case MESSAGE_HOTKEY: + return 0x26; + case MESSAGE_TUTORIAL: + return 0x27; + case MESSAGE_THANKYOU: + return 0x28; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 900 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x04; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x0C; + case MESSAGE_STATUS_DEFAULT: + return 0x10; + case MESSAGE_STATUS_WARNING: + return 0x11; + case MESSAGE_EVENT_ADVANCE: + return 0x12; + case MESSAGE_STATUS_SMALL: + return 0x13; + case MESSAGE_INFO_DESCR: + return 0x14; + case MESSAGE_EVENT_DEFAULT: + return 0x1C; + case MESSAGE_GUILD: + return 0x1F; + case MESSAGE_PARTY_MANAGEMENT: + return 0x20; + case MESSAGE_PARTY: + return 0x21; + case MESSAGE_EVENT_ORANGE: + return 0x22; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x23; + case MESSAGE_DAMAGE_DEALT: + return 0x15; + case MESSAGE_DAMAGE_RECEIVED: + return 0x16; + case MESSAGE_MANA: + return 0x17; + case MESSAGE_HEALED: + return 0x17; + case MESSAGE_EXPERIENCE: + return 0x18; + case MESSAGE_DAMAGE_OTHERS: + return 0x19; + case MESSAGE_HEALED_OTHERS: + return 0x1A; + case MESSAGE_EXPERIENCE_OTHERS: + return 0x1B; + case MESSAGE_LOOT: + return 0x1D; + case MESSAGE_LOGIN: + return 0x10; + case MESSAGE_ADMINISTRATOR: + return 0x11; + case MESSAGE_GAME: + return 0x12; + case MESSAGE_GAME_HIGHLIGHT: + return 0x11; + case MESSAGE_FAILURE: + return 0x13; + case MESSAGE_LOOK: + return 0x14; + case MESSAGE_STATUS: + return 0x1C; + case MESSAGE_TRADE: + return 0x1E; + case MESSAGE_REPORT: + return 0x24; + case MESSAGE_HOTKEY: + return 0x25; + case MESSAGE_TUTORIAL: + return 0x26; + case MESSAGE_THANKYOU: + return 0x27; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 861 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x15; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x16; + case MESSAGE_STATUS_DEFAULT: + return 0x11; + case MESSAGE_STATUS_WARNING: + return 0x0F; + case MESSAGE_EVENT_ADVANCE: + return 0x10; + case MESSAGE_STATUS_SMALL: + return 0x14; + case MESSAGE_INFO_DESCR: + return 0x13; + case MESSAGE_EVENT_DEFAULT: + return 0x12; + case MESSAGE_EVENT_ORANGE: + return 0x0D; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x0E; + case MESSAGE_LOOT: + return 0x13; + case MESSAGE_LOGIN: + return 0x11; + case MESSAGE_ADMINISTRATOR: + return 0x0F; + case MESSAGE_GAME: + return 0x10; + case MESSAGE_GAME_HIGHLIGHT: + return 0x0F; + case MESSAGE_FAILURE: + return 0x14; + case MESSAGE_LOOK: + return 0x13; + case MESSAGE_STATUS: + return 0x12; + case MESSAGE_TRADE: + return 0x13; + case MESSAGE_REPORT: + return 0x13; + case MESSAGE_HOTKEY: + return 0x13; + case MESSAGE_TUTORIAL: + return 0x13; + case MESSAGE_THANKYOU: + return 0x13; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 840 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x1B; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x12; + case MESSAGE_STATUS_DEFAULT: + return 0x17; + case MESSAGE_STATUS_WARNING: + return 0x15; + case MESSAGE_EVENT_ADVANCE: + return 0x16; + case MESSAGE_STATUS_SMALL: + return 0x1A; + case MESSAGE_INFO_DESCR: + return 0x19; + case MESSAGE_EVENT_DEFAULT: + return 0x18; + case MESSAGE_EVENT_ORANGE: + return 0x13; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x14; + case MESSAGE_LOOT: + return 0x19; + case MESSAGE_LOGIN: + return 0x17; + case MESSAGE_ADMINISTRATOR: + return 0x15; + case MESSAGE_GAME: + return 0x16; + case MESSAGE_GAME_HIGHLIGHT: + return 0x15; + case MESSAGE_FAILURE: + return 0x1A; + case MESSAGE_LOOK: + return 0x19; + case MESSAGE_STATUS: + return 0x18; + case MESSAGE_TRADE: + return 0x19; + case MESSAGE_REPORT: + return 0x19; + case MESSAGE_HOTKEY: + return 0x19; + case MESSAGE_TUTORIAL: + return 0x19; + case MESSAGE_THANKYOU: + return 0x19; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 820 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x1A; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x11; + case MESSAGE_STATUS_DEFAULT: + return 0x16; + case MESSAGE_STATUS_WARNING: + return 0x14; + case MESSAGE_EVENT_ADVANCE: + return 0x15; + case MESSAGE_STATUS_SMALL: + return 0x19; + case MESSAGE_INFO_DESCR: + return 0x18; + case MESSAGE_EVENT_DEFAULT: + return 0x17; + case MESSAGE_EVENT_ORANGE: + return 0x12; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x13; + case MESSAGE_LOOT: + return 0x18; + case MESSAGE_LOGIN: + return 0x16; + case MESSAGE_ADMINISTRATOR: + return 0x14; + case MESSAGE_GAME: + return 0x15; + case MESSAGE_GAME_HIGHLIGHT: + return 0x14; + case MESSAGE_FAILURE: + return 0x19; + case MESSAGE_LOOK: + return 0x18; + case MESSAGE_STATUS: + return 0x17; + case MESSAGE_TRADE: + return 0x18; + case MESSAGE_REPORT: + return 0x18; + case MESSAGE_HOTKEY: + return 0x18; + case MESSAGE_TUTORIAL: + return 0x18; + case MESSAGE_THANKYOU: + return 0x18; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 723 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x18; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x19; + case MESSAGE_STATUS_DEFAULT: + return 0x14; + case MESSAGE_STATUS_WARNING: + return 0x12; + case MESSAGE_EVENT_ADVANCE: + return 0x13; + case MESSAGE_STATUS_SMALL: + return 0x17; + case MESSAGE_INFO_DESCR: + return 0x16; + case MESSAGE_EVENT_DEFAULT: + return 0x15; + case MESSAGE_EVENT_ORANGE: + return 0x10; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x11; + case MESSAGE_LOOT: + return 0x16; + case MESSAGE_LOGIN: + return 0x14; + case MESSAGE_ADMINISTRATOR: + return 0x12; + case MESSAGE_GAME: + return 0x13; + case MESSAGE_GAME_HIGHLIGHT: + return 0x12; + case MESSAGE_FAILURE: + return 0x17; + case MESSAGE_LOOK: + return 0x16; + case MESSAGE_STATUS: + return 0x15; + case MESSAGE_TRADE: + return 0x16; + case MESSAGE_REPORT: + return 0x16; + case MESSAGE_HOTKEY: + return 0x16; + case MESSAGE_TUTORIAL: + return 0x16; + case MESSAGE_THANKYOU: + return 0x16; + default: + return MESSAGE_NONE; + } +#elif CLIENT_VERSION >= 710 + switch (messageType) { + case MESSAGE_STATUS_CONSOLE_BLUE: + return 0x04; + case MESSAGE_STATUS_CONSOLE_RED: + return 0x09; + case MESSAGE_STATUS_DEFAULT: + return 0x11; + case MESSAGE_STATUS_WARNING: + return 0x0F; + case MESSAGE_EVENT_ADVANCE: + return 0x10; + case MESSAGE_STATUS_SMALL: + return 0x14; + case MESSAGE_INFO_DESCR: + return 0x13; + case MESSAGE_EVENT_DEFAULT: + return 0x12; + case MESSAGE_EVENT_ORANGE: + return 0x0D; + case MESSAGE_STATUS_CONSOLE_ORANGE: + return 0x0E; + case MESSAGE_LOOT: + return 0x13; + case MESSAGE_LOGIN: + return 0x11; + case MESSAGE_ADMINISTRATOR: + return 0x0F; + case MESSAGE_GAME: + return 0x10; + case MESSAGE_GAME_HIGHLIGHT: + return 0x0F; + case MESSAGE_FAILURE: + return 0x14; + case MESSAGE_LOOK: + return 0x13; + case MESSAGE_STATUS: + return 0x12; + case MESSAGE_TRADE: + return 0x13; + case MESSAGE_REPORT: + return 0x13; + case MESSAGE_HOTKEY: + return 0x13; + case MESSAGE_TUTORIAL: + return 0x13; + case MESSAGE_THANKYOU: + return 0x13; + default: + return MESSAGE_NONE; + } +#endif } void ProtocolGame::sendTextMessage(const TextMessage &message) { + uint8_t messageType = translateMessageClassToClient(message.type); if (message.type == MESSAGE_NONE) { g_logger().error("[ProtocolGame::sendTextMessage] - Message type is wrong, missing or invalid for player with name {}, on position {}", player->getName(), player->getPosition().toString()); - player->sendTextMessage(MESSAGE_ADMINISTRADOR, "There was a problem requesting your message, please contact the administrator"); - return; - } - - MessageClasses internalType = message.type; - if (oldProtocol && message.type > MESSAGE_LAST_OLDPROTOCOL) { - switch (internalType) { - case MESSAGE_REPORT: { - internalType = MESSAGE_LOOT; - break; - } - case MESSAGE_HOTKEY_PRESSED: { - internalType = MESSAGE_LOOK; - break; - } - case MESSAGE_TUTORIAL_HINT: { - internalType = MESSAGE_LOGIN; - break; - } - case MESSAGE_THANK_YOU: { - internalType = MESSAGE_LOGIN; - break; - } - case MESSAGE_MARKET: { - internalType = MESSAGE_GAME_HIGHLIGHT; - break; - } - case MESSAGE_MANA: { - internalType = MESSAGE_THANK_YOU; - break; - } - case MESSAGE_BEYOND_LAST: { - internalType = MESSAGE_LOOT; - break; - } - case MESSAGE_ATTENTION: { - internalType = MESSAGE_DAMAGE_DEALT; - break; - } - case MESSAGE_BOOSTED_CREATURE: { - internalType = MESSAGE_LOOT; - break; - } - case MESSAGE_OFFLINE_TRAINING: { - internalType = MESSAGE_LOOT; - break; - } - case MESSAGE_TRANSACTION: { - internalType = MESSAGE_LOOT; + player->sendTextMessage(MESSAGE_STATUS_WARNING, "There was a problem requesting your message, please contact the administrator"); + return; + } + + if (messageType == MESSAGE_NONE) { + // Backward compatibility + switch (message.type) { +#if CLIENT_VERSION < 900 + case MESSAGE_DAMAGE_DEALT: + case MESSAGE_DAMAGE_RECEIVED: + case MESSAGE_DAMAGE_OTHERS: { + NetworkMessage msg; + if (message.primary.value != 0) { + msg.addByte(0x84); + msg.addPosition(message.position); + msg.addByte(message.primary.color); + msg.addString(std::to_string(message.primary.value), fmt::format("{} - {}", __FUNCTION__, "PrimaryValue")); + } + if (message.secondary.value != 0) { + msg.addByte(0x84); + msg.addPosition(message.position); + msg.addByte(message.secondary.color); + msg.addString(std::to_string(message.secondary.value), fmt::format("{} - {}", __FUNCTION__, "SecondaryValue")); + } + if (!message.text.empty()) { + msg.addByte(0xB4); + msg.addByte(translateMessageClassToClient(MESSAGE_EVENT_DEFAULT)); + msg.addString(message.text, fmt::format("{} - {}", __FUNCTION__, "Text")); + } + writeToOutputBuffer(msg); break; } - case MESSAGE_POTION: { - internalType = MESSAGE_FAILURE; + case MESSAGE_MANA: + case MESSAGE_HEALED: + case MESSAGE_HEALED_OTHERS: + case MESSAGE_EXPERIENCE: + case MESSAGE_EXPERIENCE_OTHERS: { + NetworkMessage msg; + msg.addByte(0x84); + msg.addPosition(message.position); + msg.addByte(message.primary.color); + msg.addString(std::to_string(message.primary.value), fmt::format("{} - {}", __FUNCTION__, "Primary Experience")); + if (!message.text.empty()) { + msg.addByte(0xB4); + msg.addByte(translateMessageClassToClient(MESSAGE_EVENT_DEFAULT)); + msg.addString(message.text, fmt::format("{} - {}", __FUNCTION__, "Text Experience")); + } + writeToOutputBuffer(msg); break; } - +#endif default: { - internalType = MESSAGE_EVENT_ADVANCE; break; } } + return; } NetworkMessage msg; msg.addByte(0xB4); - msg.addByte(internalType); - switch (internalType) { + msg.addByte(messageType); + switch (message.type) { case MESSAGE_DAMAGE_DEALT: case MESSAGE_DAMAGE_RECEIVED: case MESSAGE_DAMAGE_OTHERS: { @@ -4117,6 +5020,7 @@ void ProtocolGame::sendTextMessage(const TextMessage &message) { msg.addByte(message.secondary.color); break; } + case MESSAGE_MANA: case MESSAGE_HEALED: case MESSAGE_HEALED_OTHERS: { msg.addPosition(message.position); @@ -4178,10 +5082,9 @@ void ProtocolGame::sendChannelsDialog() { void ProtocolGame::sendChannel(uint16_t channelId, const std::string &channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) { NetworkMessage msg; msg.addByte(0xAC); - msg.add(channelId); msg.addString(channelName, "ProtocolGame::sendChannel - channelName"); - +#if CLIENT_VERSION >= 910 if (channelUsers) { msg.add(channelUsers->size()); for (const auto &it : *channelUsers) { @@ -4199,10 +5102,16 @@ void ProtocolGame::sendChannel(uint16_t channelId, const std::string &channelNam } else { msg.add(0x00); } +#endif writeToOutputBuffer(msg); } void ProtocolGame::sendChannelMessage(const std::string &author, const std::string &text, SpeakClasses type, uint16_t channel) { + uint8_t talkType = translateSpeakClassToClient(type); + if (talkType == TALKTYPE_NONE) { + return; + } + NetworkMessage msg; msg.addByte(0xAA); msg.add(0x00); @@ -4215,18 +5124,29 @@ void ProtocolGame::sendChannelMessage(const std::string &author, const std::stri } void ProtocolGame::sendIcons(uint32_t icons) { + if (icons < 0) { + return; + } + NetworkMessage msg; msg.addByte(0xA2); +#if CLIENT_VERSION < 780 + msg.addByte(static_cast(icons)); +#else if (oldProtocol) { + // CLIENT_VERSION 860/1100 msg.add(static_cast(icons)); } else { + // CLIENT_VERSION 1140 >= msg.add(icons); msg.addByte(0x00); // 13.20 icon counter } +#endif writeToOutputBuffer(msg); } void ProtocolGame::sendUnjustifiedPoints(const uint8_t &dayProgress, const uint8_t &dayLeft, const uint8_t &weekProgress, const uint8_t &weekLeft, const uint8_t &monthProgress, const uint8_t &monthLeft, const uint8_t &skullDuration) { +#if CLIENT_VERSION >= 1100 NetworkMessage msg; msg.addByte(0xB7); msg.addByte(dayProgress); @@ -4237,6 +5157,7 @@ void ProtocolGame::sendUnjustifiedPoints(const uint8_t &dayProgress, const uint8 msg.addByte(monthLeft); msg.addByte(skullDuration); writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr container, bool hasParent, uint16_t firstIndex) { @@ -4248,7 +5169,7 @@ void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr contain msg.addByte(0x6E); msg.addByte(cid); - +#if CLIENT_VERSION >= 984 if (container->getID() == ITEM_BROWSEFIELD) { AddItem(msg, ITEM_BAG, 1, container->getTier()); msg.addString("Browse Field", "ProtocolGame::sendContainer - Browse Field"); @@ -4256,18 +5177,21 @@ void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr contain AddItem(msg, container); msg.addString(container->getName(), "ProtocolGame::sendContainer - container->getName()"); } - - const auto itemsStoreInboxToSend = container->getStoreInboxFilteredItems(); +#else + AddItem(msg, container); + msg.addString(container->getName(), "ProtocolGame::sendContainer - container->getName()"); +#endif msg.addByte(container->capacity()); msg.addByte(hasParent ? 0x01 : 0x00); +#if CLIENT_VERSION >= 1220 + // Can use depot search + msg.addByte((player->isDepotSearchAvailable() && container->isInsideDepot(true)) ? 0x01 : 0x00); +#endif - // Depot search - if (!oldProtocol) { - msg.addByte((player->isDepotSearchAvailable() && container->isInsideDepot(true)) ? 0x01 : 0x00); - } - +#if CLIENT_VERSION >= 984 + const auto itemsStoreInboxToSend = container->getStoreInboxFilteredItems(); msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination @@ -4302,13 +5226,17 @@ void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr contain AddItem(msg, *it); } } +#else + uint8_t itemsToSend = std::min(container->size(), std::numeric_limits::max()); - // From here on down is for version 13.21+ - if (oldProtocol) { - writeToOutputBuffer(msg); - return; + msg.addByte(itemsToSend); + for (auto it = container->getItemList().begin(), end = it + itemsToSend; it != end; ++it) { + AddItem(msg, *it); } +#endif +// 13.21 version +#if CLIENT_VERSION >= 1321 if (container->isStoreInbox()) { const auto &categories = container->getStoreInboxValidCategories(); const auto enumName = container->getAttribute(ItemAttribute_t::STORE_INBOX_CATEGORY); @@ -4337,7 +5265,7 @@ void ProtocolGame::sendContainer(uint8_t cid, std::shared_ptr contain msg.addByte(0x00); msg.addByte(0x00); } - +#endif writeToOutputBuffer(msg); } @@ -4407,16 +5335,25 @@ void ProtocolGame::sendLootStats(std::shared_ptr item, uint8_t count) { void ProtocolGame::sendShop(std::shared_ptr npc) { NetworkMessage msg; msg.addByte(0x7A); +#if CLIENT_VERSION >= 910 msg.addString(npc->getName(), "ProtocolGame::sendShop - npc->getName()"); +#endif - if (!oldProtocol) { - msg.add(npc->getCurrency()); - msg.addString(std::string(), "ProtocolGame::sendShop - std::string()"); // Currency name - } +#if CLIENT_VERSION >= 1203 + msg.add(npc->getCurrency()); +#endif +#if CLIENT_VERSION >= 1240 + msg.addString(std::string(), "ProtocolGame::sendShop - std::string()"); // Currency name +#endif std::vector shoplist = npc->getShopItemVector(player->getGUID()); - uint16_t itemsToSend = std::min(shoplist.size(), std::numeric_limits::max()); +#if CLIENT_VERSION >= 980 + auto itemsToSend = std::min(shoplist.size(), std::numeric_limits::max()); msg.add(itemsToSend); +#else + auto itemsToSend = std::min(shoplist.size(), std::numeric_limits::max()); + msg.addByte(itemsToSend); +#endif uint16_t i = 0; for (ShopBlock shopBlock : shoplist) { @@ -4471,6 +5408,7 @@ void ProtocolGame::sendResourcesBalance(uint64_t money /*= 0*/, uint64_t bank /* } void ProtocolGame::sendResourceBalance(Resource_t resourceType, uint64_t value) { +#if CLIENT_VERSION >= 1100 if (oldProtocol && resourceType > RESOURCE_PREY_CARDS) { return; } @@ -4480,6 +5418,7 @@ void ProtocolGame::sendResourceBalance(Resource_t resourceType, uint64_t value) msg.addByte(resourceType); msg.add(value); writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendSaleItemList(const std::vector &shopVector, const std::map &inventoryMap) { @@ -4499,6 +5438,7 @@ void ProtocolGame::sendSaleItemList(const std::vector &shopVector, co } NetworkMessage msg; +#if CLIENT_VERSION >= 1100 msg.addByte(0xEE); msg.addByte(0x00); msg.add(player->getBankBalance()); @@ -4522,6 +5462,14 @@ void ProtocolGame::sendSaleItemList(const std::vector &shopVector, co if (oldProtocol) { msg.add(player->getMoney() + player->getBankBalance()); } +#elif CLIENT_VERSION >= 973 +#elif CLIENT_VERSION >= 973 + msg.addByte(0x7B); + msg.add(playerMoney); +#else + msg.addByte(0x7B); + msg.add(std::min(playerMoney, std::numeric_limits::max())); +#endif uint8_t itemsToSend = 0; auto msgPosition = msg.getBufferPosition(); @@ -4596,6 +5544,7 @@ void ProtocolGame::sendMarketEnter(uint32_t depotId) { } void ProtocolGame::sendCoinBalance() { +#if CLIENT_VERSION >= 1100 if (!player) { return; } @@ -4621,6 +5570,7 @@ void ProtocolGame::sendCoinBalance() { } writeToOutputBuffer(msg); +#endif } void ProtocolGame::updateCoinBalance() { @@ -4876,6 +5826,7 @@ void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList &buyO } void ProtocolGame::sendForgingData() { +#if CLIENT_VERSION >= 1280 if (!player || oldProtocol) { return; } @@ -4956,6 +5907,7 @@ void ProtocolGame::sendForgingData() { parseSendResourceBalance(); writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendOpenForge() { @@ -5710,36 +6662,42 @@ void ProtocolGame::sendCreatureTurn(std::shared_ptr creature, uint32_t msg.add(0x63); msg.add(creature->getID()); msg.addByte(creature->getDirection()); +#if CLIENT_VERSION >= 953 msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); +#endif writeToOutputBuffer(msg); } void ProtocolGame::sendCreatureSay(std::shared_ptr creature, SpeakClasses type, const std::string &text, const Position* pos /* = nullptr*/) { + uint8_t talkType = translateSpeakClassToClient(type); + if (talkType == TALKTYPE_NONE) { + return; + } + NetworkMessage msg; msg.addByte(0xAA); +#if CLIENT_VERSION >= 770 static uint32_t statementId = 0; msg.add(++statementId); - +#endif msg.addString(creature->getName(), "ProtocolGame::sendCreatureSay - creature->getName()"); - - if (!oldProtocol) { - msg.addByte(0x00); // Show (Traded) +#if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } +#endif +#if CLIENT_VERSION >= 780 // Add level only for players if (std::shared_ptr speaker = creature->getPlayer()) { msg.add(speaker->getLevel()); } else { msg.add(0x00); } +#endif - if (oldProtocol && type >= TALKTYPE_MONSTER_LAST_OLDPROTOCOL && type != TALKTYPE_CHANNEL_R2) { - msg.addByte(TALKTYPE_MONSTER_SAY); - } else { - msg.addByte(type); - } - + msg.addByte(talkType); if (pos) { msg.addPosition(*pos); } else { @@ -5751,42 +6709,58 @@ void ProtocolGame::sendCreatureSay(std::shared_ptr creature, SpeakClas } void ProtocolGame::sendToChannel(std::shared_ptr creature, SpeakClasses type, const std::string &text, uint16_t channelId) { + uint8_t talkType = translateSpeakClassToClient(type); + if (talkType == TALKTYPE_NONE) { + return; + } + NetworkMessage msg; msg.addByte(0xAA); +#if CLIENT_VERSION >= 770 static uint32_t statementId = 0; msg.add(++statementId); +#endif if (!creature) { msg.add(0x00); - if (!oldProtocol && statementId != 0) { - msg.addByte(0x00); // Show (Traded) +#if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } +#endif +#if CLIENT_VERSION >= 780 + msg.add(0x00); +#endif } else if (type == TALKTYPE_CHANNEL_R2) { msg.add(0x00); - if (!oldProtocol && statementId != 0) { - msg.addByte(0x00); // Show (Traded) +#if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } +#endif +#if CLIENT_VERSION >= 780 + msg.add(0x00); +#endif type = TALKTYPE_CHANNEL_R1; } else { msg.addString(creature->getName(), "ProtocolGame::sendToChannel - creature->getName()"); - if (!oldProtocol && statementId != 0) { - msg.addByte(0x00); // Show (Traded) +#if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } +#endif +#if CLIENT_VERSION >= 780 // Add level only for players if (std::shared_ptr speaker = creature->getPlayer()) { msg.add(speaker->getLevel()); } else { msg.add(0x00); } +#endif } - if (oldProtocol && type >= TALKTYPE_MONSTER_LAST_OLDPROTOCOL && type != TALKTYPE_CHANNEL_R2) { - msg.addByte(TALKTYPE_CHANNEL_O); - } else { - msg.addByte(type); - } - + msg.addByte(talkType); msg.add(channelId); msg.addString(text, "ProtocolGame::sendToChannel - text"); writeToOutputBuffer(msg); @@ -5794,28 +6768,50 @@ void ProtocolGame::sendToChannel(std::shared_ptr creature, SpeakClasse void ProtocolGame::sendPrivateMessage(std::shared_ptr speaker, SpeakClasses type, const std::string &text) { NetworkMessage msg; + uint8_t talkType = translateSpeakClassToClient(type); + if (talkType == TALKTYPE_NONE) { + return; + } + msg.addByte(0xAA); +#if CLIENT_VERSION >= 770 static uint32_t statementId = 0; msg.add(++statementId); +#endif if (speaker) { +#if CLIENT_VERSION <= 860 + if (type == TALKTYPE_RVR_ANSWER) { + msg.addString("Gamemaster", "ProtocolGame::sendPrivateMessage - Gamemaster"); + #if CLIENT_VERSION >= 780 + msg.add(0); + #endif + } else { + msg.addString(speaker->getName(), "ProtocolGame::sendPrivateMessage - speaker->getName"); + #if CLIENT_VERSION >= 780 + msg.add(speaker->getLevel()); + #endif + } +#else msg.addString(speaker->getName(), "ProtocolGame::sendPrivateMessage - speaker->getName()"); - if (!oldProtocol && statementId != 0) { - msg.addByte(0x00); // Show (Traded) + #if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } + #endif + #if CLIENT_VERSION >= 780 msg.add(speaker->getLevel()); + #endif +#endif } else { msg.add(0x00); - if (!oldProtocol && statementId != 0) { - msg.addByte(0x00); // Show (Traded) +#if CLIENT_VERSION >= 1250 + if (statementId != 0) { + msg.addByte(0x00); // Show traded } +#endif } - if (oldProtocol && type >= TALKTYPE_MONSTER_LAST_OLDPROTOCOL && type != TALKTYPE_CHANNEL_R2) { - msg.addByte(TALKTYPE_PRIVATE_TO); - } else { - msg.addByte(type); - } - + msg.addByte(talkType); msg.addString(text, "ProtocolGame::sendPrivateMessage - text"); writeToOutputBuffer(msg); } @@ -5831,8 +6827,10 @@ void ProtocolGame::sendChangeSpeed(std::shared_ptr creature, uint16_t NetworkMessage msg; msg.addByte(0x8F); msg.add(creature->getID()); +#if CLIENT_VERSION >= 1059 msg.add(creature->getBaseSpeed()); - msg.add(speed); +#endif + msg.add(speed * 2); writeToOutputBuffer(msg); } @@ -5852,11 +6850,13 @@ void ProtocolGame::sendSkills() { } void ProtocolGame::sendPing() { - if (player) { - NetworkMessage msg; - msg.addByte(0x1D); - writeToOutputBuffer(msg); - } + NetworkMessage msg; +#if CLIENT_VERSION >= 953 + msg.addByte(0x1D); +#else + msg.addByte(0x1E); +#endif + writeToOutputBuffer(msg); } void ProtocolGame::sendPingBack() { @@ -5866,16 +6866,22 @@ void ProtocolGame::sendPingBack() { } void ProtocolGame::sendDistanceShoot(const Position &from, const Position &to, uint16_t type) { - if (oldProtocol && type > 0xFF) { - return; - } NetworkMessage msg; +#if CLIENT_VERSION >= 1100 if (oldProtocol) { + if (type > std::numeric_limits::max()) { + return; + } + msg.addByte(0x85); msg.addPosition(from); msg.addPosition(to); - msg.addByte(static_cast(type)); + msg.addByte(type); } else { + if (type > std::numeric_limits::max()) { + return; + } + msg.addByte(0x83); msg.addPosition(from); msg.addByte(MAGIC_EFFECTS_CREATE_DISTANCEEFFECT); @@ -5884,6 +6890,16 @@ void ProtocolGame::sendDistanceShoot(const Position &from, const Position &to, u msg.addByte(static_cast(static_cast(static_cast(to.y) - static_cast(from.y)))); msg.addByte(MAGIC_EFFECTS_END_LOOP); } +#elif CLIENT_VERSION == 860 + if (type > std::numeric_limits::max()) { + return; + } + + msg.addByte(0x85); + msg.addPosition(from); + msg.addPosition(to); + msg.add(type); +#endif writeToOutputBuffer(msg); } @@ -5928,22 +6944,44 @@ void ProtocolGame::sendRestingStatus(uint8_t protection) { } void ProtocolGame::sendMagicEffect(const Position &pos, uint16_t type) { - if (!canSee(pos) || (oldProtocol && type > 0xFF)) { + if (!canSee(pos)) { return; } +#if CLIENT_VERSION > 1100 + if (!g_game().isMagicEffectRegistered(static_cast(type))) { + g_logger().error("[{}] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", __FUNCTION__, type); + return; + } +#endif + NetworkMessage msg; + msg.addByte(0x83); + msg.addPosition(pos); +// Client 860 has extended effects +#if CLIENT_VERSION >= 1100 if (oldProtocol) { - msg.addByte(0x83); - msg.addPosition(pos); + if (type > std::numeric_limits::max()) { + return; + } + msg.addByte(static_cast(type)); } else { - msg.addByte(0x83); - msg.addPosition(pos); + if (type > std::numeric_limits::max()) { + return; + } + msg.addByte(MAGIC_EFFECTS_CREATE_EFFECT); msg.add(type); msg.addByte(MAGIC_EFFECTS_END_LOOP); } +#elif CLIENT_VERSION == 860 + if (type > std::numeric_limits::max()) { + return; + } + + msg.add(type); +#endif writeToOutputBuffer(msg); } @@ -5951,12 +6989,28 @@ void ProtocolGame::removeMagicEffect(const Position &pos, uint16_t type) { if (oldProtocol && type > 0xFF) { return; } + +#if CLIENT_VERSION > 1100 + if (!g_game().isMagicEffectRegistered(static_cast(type))) { + g_logger().debug("[{}] An unregistered magic effect type with id '{}' was blocked to prevent client crash.", __FUNCTION__, type); + return; + } +#endif + NetworkMessage msg; msg.addByte(0x84); msg.addPosition(pos); if (oldProtocol) { - msg.addByte(static_cast(type)); + if (type > std::numeric_limits::max()) { + return; + } + + msg.addByte(type); } else { + if (type > std::numeric_limits::max()) { + return; + } + msg.add(type); } writeToOutputBuffer(msg); @@ -6256,42 +7310,57 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos } NetworkMessage msg; +#if CLIENT_VERSION >= 980 msg.addByte(0x17); +#else + msg.addByte(0x0A); +#endif msg.add(player->getID()); - msg.add(SERVER_BEAT); // beat duration (50) + msg.add(0x32); // beat duration (50) + +#if CLIENT_VERSION < 1310 + if (player->getAccountType() >= account::ACCOUNT_TYPE_NORMAL) { + msg.addByte(0x01); + } else { + msg.addByte(0x00); + } +#endif +#if CLIENT_VERSION >= 980 msg.addDouble(Creature::speedA, 3); msg.addDouble(Creature::speedB, 3); msg.addDouble(Creature::speedC, 3); +#endif - // Allow bug report (Ctrl + Z) - if (oldProtocol) { - if (player->getAccountType() >= account::ACCOUNT_TYPE_NORMAL) { - msg.addByte(0x01); - } else { - msg.addByte(0x00); - } - } - +#if CLIENT_VERSION >= 1054 msg.addByte(0x00); // can change pvp framing option +#endif +#if CLIENT_VERSION >= 1058 msg.addByte(0x00); // expert mode button enabled +#endif +#if CLIENT_VERSION >= 1080 msg.addString(g_configManager().getString(STORE_IMAGES_URL, __FUNCTION__), "ProtocolGame::sendAddCreature - g_configManager().getString(STORE_IMAGES_URL)"); msg.add(static_cast(g_configManager().getNumber(STORE_COIN_PACKET, __FUNCTION__))); +#endif - if (!oldProtocol) { - msg.addByte(shouldAddExivaRestrictions ? 0x01 : 0x00); // exiva button enabled - } +#if CLIENT_VERSION >= 1150 + msg.addByte(shouldAddExivaRestrictions ? 0x01 : 0x00); // exiva button enabled +#endif writeToOutputBuffer(msg); - // Allow bug report (Ctrl + Z) +// Allow bug report (Ctrl + Z) +#if CLIENT_VERSION >= 1321 sendAllowBugReport(); +#endif sendTibiaTime(g_game().getLightHour()); +#if CLIENT_VERSION >= 980 sendPendingStateEntered(); sendEnterWorld(); +#endif sendMapDescription(pos); loggedIn = true; @@ -6305,6 +7374,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos sendStats(); sendSkills(); +#if CLIENT_VERSION >= 1100 sendBlessStatus(); sendPremiumTrigger(); sendItemsPrice(); @@ -6312,6 +7382,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr creature, const Pos player->sendPreyData(); player->sendTaskHuntingData(); sendForgingData(); +#endif // gameworld light-settings sendWorldLight(g_game().getWorldLightInfo()); @@ -6448,6 +7519,7 @@ void ProtocolGame::sendInventoryItem(Slots_t slot, std::shared_ptr item) { } void ProtocolGame::sendInventoryIds() { +#if CLIENT_VERSION >= 1100 ItemsTierCountList items = player->getInventoryItemsId(); NetworkMessage msg; @@ -6474,13 +7546,16 @@ void ProtocolGame::sendInventoryIds() { msg.setBufferPosition(countPosition); msg.add(totalItemsCount + 11); writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, std::shared_ptr item) { NetworkMessage msg; msg.addByte(0x70); msg.addByte(cid); +#if CLIENT_VERSION >= 984 msg.add(slot); +#endif AddItem(msg, item); writeToOutputBuffer(msg); } @@ -6489,7 +7564,11 @@ void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, std::shar NetworkMessage msg; msg.addByte(0x71); msg.addByte(cid); +#if CLIENT_VERSION >= 984 msg.add(slot); +#else + msg.addByte(slot); +#endif AddItem(msg, item); writeToOutputBuffer(msg); } @@ -6498,12 +7577,16 @@ void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, std::shar NetworkMessage msg; msg.addByte(0x72); msg.addByte(cid); +#if CLIENT_VERSION >= 984 msg.add(slot); if (lastItem) { AddItem(msg, lastItem); } else { msg.add(0x00); } +#else + msg.addByte(slot); +#endif writeToOutputBuffer(msg); } @@ -6572,70 +7655,77 @@ void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string &tex void ProtocolGame::sendOutfitWindow() { NetworkMessage msg; msg.addByte(0xC8); +#if CLIENT_VERSION <= 1100 + Outfit_t currentOutfit = player->getDefaultOutfit(); + #if CLIENT_VERSION >= 870 + bool mounted = false; + const auto ¤tMount = g_game().m_mountsPtr->getMountByID(player->getCurrentMount()); + if (currentMount) { + mounted = (currentOutfit.lookMount == currentMount->clientId); + currentOutfit.lookMount = currentMount->clientId; + } + #endif - if (oldProtocol) { - Outfit_t currentOutfit = player->getDefaultOutfit(); - const auto currentMount = g_game().mounts.getMountByID(player->getLastMount()); - if (currentMount) { - currentOutfit.lookMount = currentMount->clientId; - } - - AddOutfit(msg, currentOutfit); - - std::vector protocolOutfits; - if (player->isAccessPlayer()) { - static const std::string gamemasterOutfitName = "Game Master"; - protocolOutfits.emplace_back(gamemasterOutfitName, 75, 0); - - static const std::string gmCustomerSupport = "Customer Support"; - protocolOutfits.emplace_back(gmCustomerSupport, 266, 0); - - static const std::string communityManager = "Community Manager"; - protocolOutfits.emplace_back(communityManager, 302, 0); - } + AddOutfit(msg, currentOutfit); + #if CLIENT_VERSION >= 780 && CLIENT_VERSION <= 860 + auto startOutfits = msg.getBufferPosition(); + #if CLIENT_VERSION < 800 + uint8_t limitOutfits = 15; + #elif CLIENT_VERSION < 870 + uint8_t limitOutfits = 25; + #elif CLIENT_VERSION < 1062 + uint8_t limitOutfits = 50; + #else + uint8_t limitOutfits = std::numeric_limits::max(); + #endif + uint8_t outfitSize = 0; + msg.skipBytes(1); + #endif - const auto outfits = Outfits::getInstance().getOutfits(player->getSex()); - protocolOutfits.reserve(outfits.size()); - for (const auto &outfit : outfits) { - uint8_t addons; - if (!player->getOutfitAddons(outfit, addons)) { - continue; - } + if (player->isAccessPlayer()) { + msg.add(75); + msg.addString("Gamemaster", "ProtocolGame::sendOutfitWindow - Gamemaster"); + msg.addByte(0); + ++outfitSize; - protocolOutfits.emplace_back(outfit->name, outfit->lookType, addons); - // Game client doesn't allow more than 100 outfits - if (protocolOutfits.size() == 150) { - break; - } - } + #if CLIENT_VERSION >= 800 + msg.add(266); + msg.addString("Customer Support", "ProtocolGame::sendOutfitWindow - Costumer"); + msg.addByte(0); + ++outfitSize; + #endif - msg.addByte(protocolOutfits.size()); - for (const ProtocolOutfit &outfit : protocolOutfits) { - msg.add(outfit.lookType); - msg.addString(outfit.name, "ProtocolGame::sendOutfitWindow - outfit.name"); - msg.addByte(outfit.addons); - } + #if CLIENT_VERSION >= 830 + msg.add(302); + msg.addString("Community Manager", "ProtocolGame::sendOutfitWindow - Community"); + msg.addByte(0); + ++outfitSize; + #endif + } - std::vector> mounts; - for (const auto mount : g_game().mounts.getMounts()) { - if (player->hasMount(mount)) { - mounts.push_back(mount); - } + const auto &outfits = Outfits::getInstance().getOutfits(player->getSex()); + for (auto &outfit : outfits) { + uint8_t addons; + if (!player->getOutfitAddons(outfit, addons)) { + continue; } - msg.addByte(mounts.size()); - for (const auto mount : mounts) { - msg.add(mount->clientId); - msg.addString(mount->name, "ProtocolGame::sendOutfitWindow - mount->name"); + g_logger().debug("Sending outfit {} to the client", outfit->name); + msg.add(outfit->lookType); + msg.addString(outfit->name, "ProtocolGame::sendOutfitWindow - outfit->name"); + msg.addByte(addons); + if (++outfitSize == limitOutfits) { + break; } - - writeToOutputBuffer(msg); - return; } - + auto endOutfits = msg.getBufferPosition(); + msg.setBufferPosition(startOutfits); + msg.addByte(outfitSize); + msg.setBufferPosition(endOutfits); +#else bool mounted = false; Outfit_t currentOutfit = player->getDefaultOutfit(); - const auto currentMount = g_game().mounts.getMountByID(player->getLastMount()); + const auto currentMount = g_game().m_mountsPtr->getMountByID(player->getLastMount()); if (currentMount) { mounted = (currentOutfit.lookMount == currentMount->clientId); currentOutfit.lookMount = currentMount->clientId; @@ -6721,7 +7811,7 @@ void ProtocolGame::sendOutfitWindow() { uint16_t mountSize = 0; msg.skipBytes(2); - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().m_mountsPtr->getMounts(); for (const auto mount : mounts) { if (player->hasMount(mount)) { msg.add(mount->clientId); @@ -6776,10 +7866,11 @@ void ProtocolGame::sendOutfitWindow() { // Version 12.81 - Random mount 'bool' msg.addByte(player->isRandomMounted() ? 0x01 : 0x00); - +#endif // CLIENT_VERSION <= 1100 writeToOutputBuffer(msg); } +#if CLIENT_VERSION > 1100 void ProtocolGame::sendPodiumWindow(std::shared_ptr podium, const Position &position, uint16_t itemId, uint8_t stackpos) { if (!podium || oldProtocol) { g_logger().error("[{}] item is nullptr", __FUNCTION__); @@ -6841,7 +7932,7 @@ void ProtocolGame::sendPodiumWindow(std::shared_ptr podium, const Position uint16_t mountSize = 0; msg.skipBytes(2); - const auto mounts = g_game().mounts.getMounts(); + const auto mounts = g_game().m_mountsPtr->getMounts(); for (const auto mount : mounts) { if (player->hasMount(mount)) { msg.add(mount->clientId); @@ -6874,6 +7965,7 @@ void ProtocolGame::sendPodiumWindow(std::shared_ptr podium, const Position msg.addByte(lookDirection ? lookDirection->getAttribute() : 2); writeToOutputBuffer(msg); } +#endif void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { if (oldProtocol && newStatus == VIPSTATUS_TRAINING) { @@ -6881,9 +7973,14 @@ void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { } NetworkMessage msg; +#if CLIENT_VERSION >= 980 msg.addByte(0xD3); msg.add(guid); msg.addByte(newStatus); +#else + msg.addByte(newStatus == VIPSTATUS_OFFLINE ? 0xD4 : 0xD3); + msg.add(guid); +#endif writeToOutputBuffer(msg); } @@ -6896,16 +7993,22 @@ void ProtocolGame::sendVIP(uint32_t guid, const std::string &name, const std::st msg.addByte(0xD2); msg.add(guid); msg.addString(name, "ProtocolGame::sendVIP - name"); +#if CLIENT_VERSION <= 860 + msg.addByte(notify ? 0x01 : 0x00); +#endif +#if CLIENT_VERSION >= 963 msg.addString(description, "ProtocolGame::sendVIP - description"); msg.add(std::min(10, icon)); msg.addByte(notify ? 0x01 : 0x00); msg.addByte(status); - if (!oldProtocol) { - msg.addByte(0x00); // vipGroups - } +#endif +#if CLIENT_VERSION >= 1110 + msg.addByte(0x00); // Vip groups +#endif writeToOutputBuffer(msg); } +#if CLIENT_VERSION >= 870 void ProtocolGame::sendSpellCooldown(uint16_t spellId, uint32_t time) { NetworkMessage msg; msg.addByte(0xA4); @@ -6933,6 +8036,7 @@ void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) { msg.add(time); writeToOutputBuffer(msg); } +#endif void ProtocolGame::sendUseItemCooldown(uint32_t time) { if (!player || oldProtocol) { @@ -6960,6 +8064,7 @@ void ProtocolGame::sendPreyTimeLeft(const std::unique_ptr &slot) { } void ProtocolGame::sendPreyData(const std::unique_ptr &slot) { +#if CLIENT_VERSION >= 1100 if (!player) { return; } @@ -7080,6 +8185,7 @@ void ProtocolGame::sendPreyData(const std::unique_ptr &slot) { } writeToOutputBuffer(msg); +#endif } void ProtocolGame::sendPreyPrices() { @@ -7146,12 +8252,18 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr cr msg.add(0x61); msg.add(remove); msg.add(creature->getID()); +#if CLIENT_VERSION <= 860 + msg.addString(creature->isHealthHidden() ? "" : creature->getName(), "ProtocolGame::AddCreature - creature->getName()"); +#endif +#if CLIENT_VERSION >= 910 if (!oldProtocol && creature->isHealthHidden()) { msg.addByte(CREATURETYPE_HIDDEN); } else { msg.addByte(creatureType); } +#endif +#if CLIENT_VERSION >= 1121 if (!oldProtocol && creatureType == CREATURETYPE_SUMMON_PLAYER) { if (std::shared_ptr master = creature->getMaster()) { msg.add(master->getID()); @@ -7165,25 +8277,30 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr cr } else { msg.addString(creature->getName(), "ProtocolGame::AddCreature - creature->getName()"); } +#endif } +#if CLIENT_VERSION >= 860 if (creature->isHealthHidden()) { msg.addByte(0x00); } else { msg.addByte(std::ceil((static_cast(creature->getHealth()) / std::max(creature->getMaxHealth(), 1)) * 100)); } +#endif msg.addByte(creature->getDirection()); if (!creature->isInGhostMode() && !creature->isInvisible()) { const Outfit_t &outfit = creature->getCurrentOutfit(); AddOutfit(msg, outfit); +#if CLIENT_VERSION >= 870 if (!oldProtocol && outfit.lookMount != 0) { msg.addByte(outfit.lookMountHead); msg.addByte(outfit.lookMountBody); msg.addByte(outfit.lookMountLegs); msg.addByte(outfit.lookMountFeet); } +#endif } else { static Outfit_t outfit; AddOutfit(msg, outfit); @@ -7195,15 +8312,20 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr cr msg.add(creature->getStepSpeed()); +#if CLIENT_VERSION >= 1240 addCreatureIcon(msg, creature); +#endif msg.addByte(player->getSkullClient(creature)); msg.addByte(player->getPartyShield(otherPlayer)); +#if CLIENT_VERSION >= 854 if (!known) { msg.addByte(player->getGuildEmblem(otherPlayer)); } +#endif +#if CLIENT_VERSION >= 1000 if (!oldProtocol && creatureType == CREATURETYPE_MONSTER) { if (std::shared_ptr master = creature->getMaster()) { if (std::shared_ptr masterPlayer = master->getPlayer()) { @@ -7246,98 +8368,122 @@ void ProtocolGame::AddCreature(NetworkMessage &msg, std::shared_ptr cr msg.add(0x00); } } +#endif +#if CLIENT_VERSION >= 854 msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); +#endif } void ProtocolGame::AddPlayerStats(NetworkMessage &msg) { msg.addByte(0xA0); - - if (oldProtocol) { - msg.add(std::min(player->getHealth(), std::numeric_limits::max())); - msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); - } else { - msg.add(std::min(player->getHealth(), std::numeric_limits::max())); - msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); - } - +#if CLIENT_VERSION < 1310 + msg.add(std::min(player->getHealth(), std::numeric_limits::max())); + msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); +#else + msg.add(std::min(player->getHealth(), std::numeric_limits::max())); + msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); +#endif + +#if CLIENT_VERSION >= 840 msg.add(player->hasFlag(PlayerFlags_t::HasInfiniteCapacity) ? 1000000 : player->getFreeCapacity()); - if (oldProtocol) { - msg.add(player->getFreeCapacity()); - } - + #if CLIENT_VERSION == 1100 + msg.add(player->getFreeCapacity()); + #endif // CLIENT_VERSION == 1100 +#else + msg.add(player->getFreeCapacity()); +#endif // CLIENT_VERSION >= 840 + +#if CLIENT_VERSION >= 870 msg.add(player->getExperience()); +#else + msg.add(std::min(player->getExperience(), std::numeric_limits::max())); +#endif msg.add(player->getLevel()); msg.addByte(std::min(player->getLevelPercent(), 100)); +#if CLIENT_VERSION >= 1054 + #if CLIENT_VERSION >= 1097 msg.add(player->getBaseXpGain()); // base xp gain rate - - if (oldProtocol) { - msg.add(player->getVoucherXpBoost()); // xp voucher - } - + #if CLIENT_VERSION < 1150 + msg.add(player->getVoucherXpBoost()); // xp voucher + #endif msg.add(player->getGrindingXpBoost()); // low level bonus msg.add(player->getStoreXpBoost()); // xp boost - msg.add(player->getStaminaXpBoost()); // stamina multiplier (100 = 1.0x) - - if (!oldProtocol) { - msg.add(std::min(player->getMana(), std::numeric_limits::max())); - msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); - } else { - msg.add(std::min(player->getMana(), std::numeric_limits::max())); - msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); - - msg.addByte(static_cast(std::min(player->getMagicLevel(), std::numeric_limits::max()))); - msg.addByte(static_cast(std::min(player->getBaseMagicLevel(), std::numeric_limits::max()))); - msg.addByte(std::min(static_cast(player->getMagicLevelPercent()), 100)); - } + msg.add(player->getStaminaXpBoost()); // stamina multiplier (100 = x1.0) + #else + msg.addDouble<2>(0.0); + #endif +#endif + +#if CLIENT_VERSION >= 1310 + msg.add(std::min(player->getMana(), std::numeric_limits::max())); + msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); +#else + msg.add(std::min(player->getMana(), std::numeric_limits::max())); + msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); + + msg.addByte(static_cast(std::min(player->getMagicLevel(), std::numeric_limits::max()))); + #if CLIENT_VERSION >= 910 + msg.addByte(static_cast(std::min(player->getBaseMagicLevel(), std::numeric_limits::max()))); + #endif // >= 910 + msg.addByte(std::min(static_cast(player->getMagicLevelPercent()), 100)); +#endif // >= 1310 msg.addByte(player->getSoul()); +#if CLIENT_VERSION >= 780 msg.add(player->getStaminaMinutes()); +#endif +#if CLIENT_VERSION >= 910 msg.add(player->getBaseSpeed()); +#endif +#if CLIENT_VERSION >= 910 std::shared_ptr condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT); msg.add(condition ? condition->getTicks() / 1000 : 0x00); +#endif +#if CLIENT_VERSION >= 960 msg.add(player->getOfflineTrainingTime() / 60 / 1000); +#endif +#if CLIENT_VERSION >= 1097 msg.add(player->getExpBoostStamina()); // xp boost time (seconds) msg.addByte(1); // enables exp boost in the store +#endif - if (!oldProtocol) { - msg.add(player->getManaShield()); // remaining mana shield - msg.add(player->getMaxManaShield()); // total mana shield - } +#if CLIENT_VERSION >= 1255 + msg.add(player->getManaShield()); // remaining mana shield + msg.add(player->getMaxManaShield()); // total mana shield +#endif } void ProtocolGame::AddPlayerSkills(NetworkMessage &msg) { msg.addByte(0xA1); +#if CLIENT_VERSION < 1035 + for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; ++i) { + skills_t skill = static_cast(i); + msg.addByte(std::min(player->getSkillLevel(skill), std::numeric_limits::max())); + msg.addByte(std::min(player->getBaseSkill(skill), std::numeric_limits::max())); + } +#else + msg.add(player->getMagicLevel()); + msg.add(player->getBaseMagicLevel()); + msg.add(player->getLoyaltyMagicLevel()); + msg.add(player->getMagicLevelPercent() * 100); - if (oldProtocol) { - for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; ++i) { - skills_t skill = static_cast(i); - msg.add(std::min(player->getSkillLevel(skill), std::numeric_limits::max())); - msg.add(player->getBaseSkill(skill)); - msg.addByte(std::min(100, static_cast(player->getSkillPercent(skill)))); - } - } else { - msg.add(player->getMagicLevel()); - msg.add(player->getBaseMagicLevel()); - msg.add(player->getLoyaltyMagicLevel()); - msg.add(player->getMagicLevelPercent() * 100); - - for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; ++i) { - skills_t skill = static_cast(i); - msg.add(std::min(player->getSkillLevel(skill), std::numeric_limits::max())); - msg.add(player->getBaseSkill(skill)); - msg.add(player->getLoyaltySkill(skill)); - msg.add(player->getSkillPercent(skill) * 100); - } + for (uint8_t i = SKILL_FIRST; i <= SKILL_FISHING; ++i) { + skills_t skill = static_cast(i); + msg.add(player->getSkillLevel(skill)); + msg.add(player->getBaseSkill(skill)); + msg.add(player->getLoyaltySkill(skill)); + msg.add(player->getSkillPercent(skill) * 100); } + #if CLIENT_VERSION >= 1094 for (uint8_t i = SKILL_CRITICAL_HIT_CHANCE; i <= SKILL_LAST; ++i) { if (!oldProtocol && (i == SKILL_LIFE_LEECH_CHANCE || i == SKILL_MANA_LEECH_CHANCE)) { continue; @@ -7346,34 +8492,46 @@ void ProtocolGame::AddPlayerSkills(NetworkMessage &msg) { msg.add(std::min(player->getSkillLevel(skill), std::numeric_limits::max())); msg.add(player->getBaseSkill(skill)); } + #endif // CLIENT_VERSION >= 1094 +#endif // CLIENT_VERSION < 1035 - if (!oldProtocol) { - // 13.10 list (U8 + U16) - msg.addByte(0); - // Version 12.81 new skill (Fatal, Dodge and Momentum) - sendForgeSkillStats(msg); +#if CLIENT_VERSION >= 1310 + // 13.10 list (U8 + U16) + msg.addByte(0); + // Version 12.81 new skill (Fatal, Dodge and Momentum) + sendForgeSkillStats(msg); +#endif // CLIENT_VERSION >= 1310 - // used for imbuement (Feather) - msg.add(player->getCapacity()); // total capacity - msg.add(player->getBaseCapacity()); // base total capacity - } +#if CLIENT_VERSION >= 1150 + // used for imbuement (Feather) + msg.add(player->getCapacity()); // total capacity + msg.add(player->getBaseCapacity()); // base total capacity +#endif // CLIENT_VERSION >= 1150 } void ProtocolGame::AddOutfit(NetworkMessage &msg, const Outfit_t &outfit, bool addMount /* = true*/) { +#if CLIENT_VERSION >= 770 msg.add(outfit.lookType); +#else + msg.addByte(outfit.lookType); +#endif if (outfit.lookType != 0) { msg.addByte(outfit.lookHead); msg.addByte(outfit.lookBody); msg.addByte(outfit.lookLegs); msg.addByte(outfit.lookFeet); +#if CLIENT_VERSION >= 780 msg.addByte(outfit.lookAddons); +#endif } else { msg.add(outfit.lookTypeEx); } +#if CLIENT_VERSION >= 870 if (addMount) { msg.add(outfit.lookMount); } +#endif } void ProtocolGame::addImbuementInfo(NetworkMessage &msg, uint16_t imbuementId) const { @@ -7648,6 +8806,7 @@ void ProtocolGame::sendUpdateInputAnalyzer(CombatType_t type, int32_t amount, st } void ProtocolGame::sendTaskHuntingData(const std::unique_ptr &slot) { +#if CLIENT_VERSION >= 1100 if (!player || oldProtocol) { return; } @@ -7715,6 +8874,7 @@ void ProtocolGame::sendTaskHuntingData(const std::unique_ptr &s msg.add(std::max(static_cast(((slot->freeRerollTimeStamp - OTSYS_TIME()) / 1000)), 0)); writeToOutputBuffer(msg); +#endif } void ProtocolGame::MoveUpCreature(NetworkMessage &msg, std::shared_ptr creature, const Position &newPos, const Position &oldPos) { diff --git a/src/server/network/protocol/protocollogin.cpp b/src/server/network/protocol/protocollogin.cpp index 53dc9f05126..441f52f2f87 100644 --- a/src/server/network/protocol/protocollogin.cpp +++ b/src/server/network/protocol/protocollogin.cpp @@ -29,6 +29,22 @@ void ProtocolLogin::disconnectClient(const std::string &message) { } void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const std::string &password) { +#if CLIENT_VERSION < 1012 + static uint32_t serverIp = INADDR_NONE; + if (serverIp == INADDR_NONE) { + std::string cfgIp = g_configManager().getString(IP, "ProtocolLogin::getCharacterList - getIP"); + serverIp = inet_addr(cfgIp.c_str()); + if (serverIp == INADDR_NONE) { + struct hostent* he = gethostbyname(cfgIp.c_str()); + if (!he || he->h_addrtype != AF_INET) { // Only ipv4 + disconnectClient("ERROR: Cannot resolve hostname."); + return; + } + memcpy(&serverIp, he->h_addr, sizeof(serverIp)); + } + } +#endif + account::Account account(accountDescriptor); account.setProtocolCompat(oldProtocol); @@ -59,9 +75,11 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const output->addString(ss.str(), "ProtocolLogin::getCharacterList - ss.str()"); } +#if CLIENT_VERSION >= 1074 // Add session key output->addByte(0x28); output->addString(accountDescriptor + "\n" + password, "ProtocolLogin::getCharacterList - accountDescriptor + password"); +#endif // Add char list auto [players, result] = account.getAccountPlayers(); @@ -71,6 +89,7 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const output->addByte(0x64); +#if CLIENT_VERSION >= 1012 output->addByte(1); // number of worlds output->addByte(0); // world id @@ -80,19 +99,42 @@ void ProtocolLogin::getCharacterList(const std::string &accountDescriptor, const output->add(g_configManager().getNumber(GAME_PORT, __FUNCTION__)); output->addByte(0); - +#else uint8_t size = std::min(std::numeric_limits::max(), players.size()); output->addByte(size); for (const auto &[name, deletion] : players) { + output->addString(name, "ProtocolLogin::getCharacterList - name"); + auto stringName = "ProtocolLogin::getCharacterList - ServerName"; + output->addString(g_configManager().getString(SERVER_NAME, stringName), stringName); + output->add(serverIp); + output->add(g_configManager().getNumber(GAME_PORT, "ProtocolLogin::getCharacterList - GamePort")); + #if CLIENT_VERSION >= 971 output->addByte(0); output->addString(name, "ProtocolLogin::getCharacterList - name"); + #endif } +#endif + auto freePremiumEnabled = g_configManager().getBoolean(FREE_PREMIUM, "ProtocolLogin::getCharacterList - FreePremium"); +#if CLIENT_VERSION >= 1080 + #if CLIENT_VERSION >= 1082 // Add premium days output->addByte(0); - - output->addByte(account.getPremiumRemainingDays() > 0); - output->add(account.getPremiumLastDay()); + #endif + if (freePremiumEnabled) { + output->addByte(1); + output->add(0); + } else { + output->addByte(account.getPremiumRemainingDays() > 0); + output->add(account.getPremiumLastDay()); + } +#else + if (freePremiumEnabled) { + output->add(0xFFFF); // client displays free premium + } else { + output->add(account.getPremiumRemainingDays()); + } +#endif send(output); @@ -108,11 +150,19 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { msg.skipBytes(2); // client OS uint16_t version = msg.get(); - - // Old protocol support - oldProtocol = version == 1100; - +#if CLIENT_VERSION >= 971 + version = msg.get(); + msg.skipBytes(17); + /* + - Skipped bytes: + - 4 bytes: client version (971+) + - 12 bytes: dat, spr, pic signatures (4 bytes each) + - 1 byte: preview world(971+) + */ msg.skipBytes(17); +#else + msg.skipBytes(12); +#endif /* - Skipped bytes: - 4 bytes: client version (971+) @@ -120,9 +170,14 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { - 1 byte: preview world(971+) */ + // Old protocol support + oldProtocol = version <= 1100; + +#if CLIENT_VERSION >= 770 if (!Protocol::RSA_decrypt(msg)) { - g_logger().warn("[ProtocolLogin::onRecvFirstMessage] - RSA Decrypt Failed"); - disconnect(); + auto message = "[ProtocolLogin::onRecvFirstMessage] - RSA Decrypt Failed"; + g_logger().warn(message); + disconnectClient(message); return; } @@ -130,7 +185,10 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { enableXTEAEncryption(); setXTEAKey(key.data()); + #if CLIENT_VERSION >= 830 setChecksumMethod(CHECKSUM_METHOD_ADLER32); + #endif +#endif if (g_game().getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait."); @@ -160,7 +218,11 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage &msg) { return; } +#if CLIENT_VERSION >= 830 std::string accountDescriptor = msg.getString(); +#else + std::string accountDescriptor = std::to_string(msg.get()); +#endif if (accountDescriptor.empty()) { std::ostringstream ss; ss << "Invalid " << (oldProtocol ? "username" : "email") << "."; diff --git a/src/server/signals.cpp b/src/server/signals.cpp index 149554d3b4f..f20e01d5494 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -109,8 +109,10 @@ void Signals::sighupHandler() { Item::items.reload(); g_logger().info("Reloaded items"); - g_game().mounts.reload(); +#if CLIENT_VERSION >= 870 + g_game().m_mountsPtr->reload(); g_logger().info("Reloaded mounts"); +#endif g_events().loadFromXml(); g_logger().info("Reloaded events"); diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index 712f3f5f587..55f46359465 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -310,63 +310,71 @@ enum ShootType_t : uint8_t { }; enum SpeakClasses : uint8_t { + TALKTYPE_NONE = 0, TALKTYPE_SAY = 1, TALKTYPE_WHISPER = 2, TALKTYPE_YELL = 3, TALKTYPE_PRIVATE_FROM = 4, TALKTYPE_PRIVATE_TO = 5, - TALKTYPE_CHANNEL_MANAGER = 6, + TALKTYPE_CHANNEL_M = 6, TALKTYPE_CHANNEL_Y = 7, TALKTYPE_CHANNEL_O = 8, - TALKTYPE_SPELL_USE = 9, - TALKTYPE_PRIVATE_NP = 10, - TALKTYPE_NPC_UNKOWN = 11, /* no effect (?)*/ - TALKTYPE_PRIVATE_PN = 12, - TALKTYPE_BROADCAST = 13, - TALKTYPE_CHANNEL_R1 = 14, // red - #c text - TALKTYPE_PRIVATE_RED_FROM = 15, //@name@text - TALKTYPE_PRIVATE_RED_TO = 16, //@name@text - TALKTYPE_MONSTER_SAY = 36, - TALKTYPE_MONSTER_YELL = 37, - - TALKTYPE_MONSTER_LAST_OLDPROTOCOL = 38, /* Dont forget about the CHANNEL_R2*/ - TALKTYPE_CHANNEL_R2 = 0xFF // #d + TALKTYPE_RVR_CHANNEL = 9, + TALKTYPE_RVR_ANSWER = 10, + TALKTYPE_RVR_CONTINUE = 11, + TALKTYPE_SPELL_USE = 12, + TALKTYPE_PRIVATE_NP = 13, + TALKTYPE_PRIVATE_PN = 14, + TALKTYPE_BROADCAST = 15, + TALKTYPE_CHANNEL_R1 = 16, // red - #c text + TALKTYPE_PRIVATE_RED_FROM = 17, //@name@text + TALKTYPE_PRIVATE_RED_TO = 18, //@name@text + TALKTYPE_MONSTER_SAY = 19, + TALKTYPE_MONSTER_YELL = 20, + TALKTYPE_CHANNEL_R2 = 21, // #d + TALKTYPE_BOOSTED_CREATURE = 22, }; enum MessageClasses : uint8_t { - MESSAGE_NONE = 0, /* None */ - - MESSAGE_GAMEMASTER_CONSOLE = 13, - /* Red message in the console*/ /* TALKTYPE_BROADCAST */ - - MESSAGE_LOGIN = 17, /* White message at the bottom of the game window and in the console*/ - MESSAGE_ADMINISTRADOR = 18, /* Red message in game window and in the console*/ - MESSAGE_EVENT_ADVANCE = 19, /* White message in game window and in the console*/ - MESSAGE_GAME_HIGHLIGHT = 20, /* Red message in game window and in the console*/ - MESSAGE_FAILURE = 21, /* White message at the bottom of the game window"*/ - MESSAGE_LOOK = 22, /* Green message in game window and in the console*/ - MESSAGE_DAMAGE_DEALT = 23, /* White message on the console*/ - MESSAGE_DAMAGE_RECEIVED = 24, /* White message on the console*/ - MESSAGE_HEALED = 25, /* White message on the console*/ - MESSAGE_EXPERIENCE = 26, /* White message on the console*/ - MESSAGE_DAMAGE_OTHERS = 27, /* White message on the console*/ - MESSAGE_HEALED_OTHERS = 28, /* White message on the console*/ - MESSAGE_EXPERIENCE_OTHERS = 29, /* White message on the console*/ - MESSAGE_STATUS = 30, /* White message at the bottom of the game window and in the console*/ - MESSAGE_LOOT = 31, /* White message on the game window and in the console*/ - MESSAGE_TRADE = 32, /* Green message in game window and in the console*/ - MESSAGE_GUILD = 33, /* White message in channel (+ channelId)*/ - MESSAGE_PARTY_MANAGEMENT = 34, /* Green message in game window and in the console*/ - MESSAGE_PARTY = 35, /* White message on the console*/ - - MESSAGE_LAST_OLDPROTOCOL = 37, /* Last Message on old protocol*/ - - MESSAGE_REPORT = 38, /* White message on the game window and in the console*/ + MESSAGE_NONE = 0, + MESSAGE_STATUS_CONSOLE_BLUE = 1, // Blue message in the console + MESSAGE_STATUS_CONSOLE_RED = 2, // Red message in the console + MESSAGE_STATUS_DEFAULT = 3, // White message at the bottom of the game window and in the console + MESSAGE_STATUS_WARNING = 4, // Red message in game window and in the console + MESSAGE_EVENT_ADVANCE = 5, // White message in game window and in the console + MESSAGE_STATUS_SMALL = 6, // White message at the bottom of the game window + MESSAGE_INFO_DESCR = 7, // Green message in game window and in the console + MESSAGE_EVENT_DEFAULT = 8, // White message at the bottom of the game window and in the console + MESSAGE_GUILD = 9, // White message in channel (+ channelId) + MESSAGE_PARTY_MANAGEMENT = 10, // White message in channel (+ channelId) + MESSAGE_PARTY = 11, // White message in channel (+ channelId) + MESSAGE_EVENT_ORANGE = 12, // Orange message in the console + MESSAGE_STATUS_CONSOLE_ORANGE = 13, // Orange message in the console + MESSAGE_DAMAGE_DEALT = 14, + MESSAGE_DAMAGE_RECEIVED = 15, + MESSAGE_MANA = 16, + MESSAGE_HEALED = 17, + MESSAGE_EXPERIENCE = 18, + MESSAGE_DAMAGE_OTHERS = 19, + MESSAGE_HEALED_OTHERS = 20, + MESSAGE_EXPERIENCE_OTHERS = 21, + MESSAGE_LOOT = 22, + MESSAGE_LOGIN = 23, + MESSAGE_ADMINISTRATOR = 24, + MESSAGE_GAME = 25, + MESSAGE_GAME_HIGHLIGHT = 26, + MESSAGE_FAILURE = 27, + MESSAGE_LOOK = 28, + MESSAGE_STATUS = 29, + MESSAGE_TRADE = 30, + MESSAGE_REPORT = 31, + MESSAGE_HOTKEY = 32, + MESSAGE_TUTORIAL = 33, + MESSAGE_THANKYOU = 34, + MESSAGE_MARKET = 35, + MESSAGE_HOTKEY_PRESSED = 39, /* Green message in game window and in the console*/ MESSAGE_TUTORIAL_HINT = 40, /* no effect (?)*/ - MESSAGE_THANK_YOU = 41, /* no effect (?)*/ - MESSAGE_MARKET = 42, /* Popout a modal window with the message and a 'ok' button*/ - MESSAGE_MANA = 43, /* no effect (?)*/ MESSAGE_BEYOND_LAST = 44, /* White message on the game window and in the console*/ MESSAGE_ATTENTION = 48, /* White message on the console*/ MESSAGE_BOOSTED_CREATURE = 49, /* White message on the game window and in the console*/ @@ -506,7 +514,6 @@ enum NameEval_t : uint8_t { enum ItemID_t : uint16_t { ITEM_BROWSEFIELD = 470, // for internal use ITEM_SUPPLY_STASH_INDEX = 1, // for internal use - ITEM_DEPOT_NULL = 22796, // for internal use - Actual Item ID: 168 ITEM_DECORATION_KIT = 23398, // For internal use (wrap item) ITEM_DOCUMENT_RO = 2834, // Read-only @@ -568,8 +575,19 @@ enum ItemID_t : uint16_t { ITEM_MINOR_CRYSTALLINE_TOKENS = 16128, ITEM_MAJOR_CRYSTALLINE_TOKENS = 16129, + #if CLIENT_VERSION >= 1100 + ITEM_DEPOT_NULL = 22796, // for internal use only ITEM_REWARD_CONTAINER = 19202, ITEM_REWARD_CHEST = 19250, + ITEM_ADVENTURERS_STONE = 16277, + ITEM_WORLD_BOARD = 19236, + #else + ITEM_DEPOT_NULL = 11703, // for internal use only + ITEM_REWARD_CHEST = 11724, + ITEM_REWARD_CONTAINER = 11725, + ITEM_ADVENTURERS_STONE = 11726, + ITEM_WORLD_BOARD = 11727, + #endif ITEM_DEPOT = 3502, ITEM_LOCKER = 3497,