diff --git a/data-otservbr-global/npc/kais.lua b/data-otservbr-global/npc/kais.lua
index d7fe97f0e75..5da84b1d9cf 100644
--- a/data-otservbr-global/npc/kais.lua
+++ b/data-otservbr-global/npc/kais.lua
@@ -46,8 +46,8 @@ npcType.onCloseChannel = function(npc, creature)
end
-- Blood of the Mountain
-local blessKeyword = keywordHandler:addKeyword({ "blood" }, StdModule.say, { npcHandler = npcHandler, text = "Would you like to receive that protection for a sacrifice of |BLESSCOST| gold, child?" })
-blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the Blood of the Mountain, pilgrim.", cost = "|BLESSCOST|", bless = 7 })
+local blessKeyword = keywordHandler:addKeyword({ "enhanced" }, StdModule.say, { npcHandler = npcHandler, text = "I have the power to grant you the blood of the mountain's blessing. But I must ask of you to sacrifice |BLESSCOST| gold. Are you prepared for that?" })
+blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the blood of the mountain, master.", cost = "|BLESSCOST|", bless = 8 })
blessKeyword:addChildKeyword({ "" }, StdModule.say, { npcHandler = npcHandler, text = "Fine. You are free to decline my offer.", reset = true })
-- Healing
@@ -58,6 +58,7 @@ local function addHealKeyword(text, condition, effect)
player:removeCondition(condition)
player:getPosition():sendMagicEffect(effect)
end)
+ keywordHandler:addAliasKeyword({ "help" })
end
addHealKeyword("You are burning. Let me quench those flames.", CONDITION_FIRE, CONST_ME_MAGIC_GREEN)
@@ -74,26 +75,19 @@ end, function(player)
player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)
end)
keywordHandler:addKeyword({ "heal" }, StdModule.say, { npcHandler = npcHandler, text = "You aren't looking that bad. Sorry, I can't help you. But if you are looking for additional protection you should go on the {pilgrimage} of ashes or get the protection of the {twist of fate} here." })
+keywordHandler:addAliasKeyword({ "help" })
-- Basic
-keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Whenever you receive a lethal wound, your vital force is damaged and there is a chance that you lose some of your equipment. With every single of the five {blessings} you have, this damage and chance of loss will be reduced." })
-keywordHandler:addKeyword({ "blessings" }, StdModule.say, { npcHandler = npcHandler, text = "There are five blessings available in five sacred places: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}. Additionally, you can receive the {twist of fate} here." })
-keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "I see you received the spiritual shielding in the whiteflower temple south of Thais." }, function(player)
- return player:hasBlessing(1)
-end)
-keywordHandler:addAliasKeyword({ "shield" })
-keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "I can see you received the blessing of the two suns in the suntower near Ab'Dendriel." }, function(player)
- return player:hasBlessing(3)
-end)
-keywordHandler:addAliasKeyword({ "fire" })
-keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense that the spark of the phoenix already was given to you by the dwarven priests of earth and fire in Kazordoon." }, function(player)
- return player:hasBlessing(4)
-end)
-keywordHandler:addAliasKeyword({ "spark" })
-keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense you already talked to the hermit Eremo on the isle of Cormaya and received this blessing." }, function(player)
- return player:hasBlessing(5)
-end)
-keywordHandler:addAliasKeyword({ "wisdom" })
+keywordHandler:addKeyword({ "Kais" }, StdModule.say, { npcHandler = npcHandler, text = "I am Kais, Kais the Bound. Eternally {fixed} to the wretched place, unless... unless I prove my worth in aiding all those who seek my {help}." })
+
+keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Well, as I am quite in a fix currently, my only hope to escape this situation may be to grant you {healing} or an {enhanced} blessing if you wish. You must desire SOMETHING, right? Sure you do." })
+keywordHandler:addAliasKeyword({ "job" })
+
+keywordHandler:addKeyword({ "blessing" }, StdModule.say, {
+ npcHandler = npcHandler,
+ text = "Besides the {enhanced} blessing available from me, I know of one other, granted by a solitary {nomad} far west of Svargrond. There are also five different other blessings available, each in a sacred place. ...\nThese blessings are: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}.",
+})
+
keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of spiritual shielding in the whiteflower temple south of Thais." })
keywordHandler:addAliasKeyword({ "shield" })
keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of the two suns in the suntower near Ab'Dendriel." })
@@ -102,10 +96,14 @@ keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandle
keywordHandler:addAliasKeyword({ "spark" })
keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "Talk to the hermit Eremo on the isle of Cormaya about this blessing." })
keywordHandler:addAliasKeyword({ "wisdom" })
+keywordHandler:addKeyword({ "embrace" }, StdModule.say, { npcHandler = npcHandler, text = "The druids north of Carlin will provide you with the embrace of Tibia." })
+keywordHandler:addAliasKeyword({ "tibia" })
+
+keywordHandler:addKeyword({ "nomad" }, StdModule.say, { npcHandler = npcHandler, text = "I know everyone and everything. It is certain that there is another enhanced blessing, the 'heart of the mountain'. Talk to a nomad far to the west of Svargrond, hiding slightly above ground." })
-npcHandler:setMessage(MESSAGE_GREET, "Welcome, noble |PLAYERNAME|")
-npcHandler:setMessage(MESSAGE_WALKAWAY, "Good Bye, noble |PLAYERNAME|")
-npcHandler:setMessage(MESSAGE_FAREWELL, "Good Bye, noble |PLAYERNAME|")
+npcHandler:setMessage(MESSAGE_GREET, "Hmm... surely you are in need of {help} - will you let me? I am {Kais} the Bound and I can lend you a hand in {healing} your body and soul or even grant an {enhanced} blessing!")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Fare you well... |PLAYERNAME|")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Fare you well... |PLAYERNAME|")
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
diff --git a/data-otservbr-global/npc/nomad.lua b/data-otservbr-global/npc/nomad.lua
index 79dee9cc229..2e33e91a872 100644
--- a/data-otservbr-global/npc/nomad.lua
+++ b/data-otservbr-global/npc/nomad.lua
@@ -50,8 +50,8 @@ npcType.onCloseChannel = function(npc, creature)
end
-- Heart of the Mountain
-local blessKeyword = keywordHandler:addKeyword({ "heart" }, StdModule.say, { npcHandler = npcHandler, text = "Would you like to receive that protection for a sacrifice of |BLESSCOST| gold, child?" })
-blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the Heart of the Mountain, pilgrim.", cost = "|BLESSCOST|", bless = 8 })
+local blessKeyword = keywordHandler:addKeyword({ "enhanced" }, StdModule.say, { npcHandler = npcHandler, text = "I am able to grant you the heart of the mountain's blessing. But I must ask of you to sacrifice |BLESSCOST| gold. Are you willing to part with that amount of wealth?" })
+blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "Receive the heart of the mountain then.", cost = "|BLESSCOST|", bless = 7 })
blessKeyword:addChildKeyword({ "" }, StdModule.say, { npcHandler = npcHandler, text = "Fine. You are free to decline my offer.", reset = true })
-- Healing
@@ -62,6 +62,7 @@ local function addHealKeyword(text, condition, effect)
player:removeCondition(condition)
player:getPosition():sendMagicEffect(effect)
end)
+ keywordHandler:addAliasKeyword({ "help" })
end
addHealKeyword("You are burning. Let me quench those flames.", CONDITION_FIRE, CONST_ME_MAGIC_GREEN)
@@ -78,26 +79,17 @@ end, function(player)
player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)
end)
keywordHandler:addKeyword({ "heal" }, StdModule.say, { npcHandler = npcHandler, text = "You aren't looking that bad. Sorry, I can't help you. But if you are looking for additional protection you should go on the {pilgrimage} of ashes or get the protection of the {twist of fate} here." })
+keywordHandler:addAliasKeyword({ "help" })
-- Basic
-keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Whenever you receive a lethal wound, your vital force is damaged and there is a chance that you lose some of your equipment. With every single of the five {blessings} you have, this damage and chance of loss will be reduced." })
-keywordHandler:addKeyword({ "blessings" }, StdModule.say, { npcHandler = npcHandler, text = "There are five blessings available in five sacred places: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}. Additionally, you can receive the {twist of fate} here." })
-keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "I see you received the spiritual shielding in the whiteflower temple south of Thais." }, function(player)
- return player:hasBlessing(1)
-end)
-keywordHandler:addAliasKeyword({ "shield" })
-keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "I can see you received the blessing of the two suns in the suntower near Ab'Dendriel." }, function(player)
- return player:hasBlessing(3)
-end)
-keywordHandler:addAliasKeyword({ "fire" })
-keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense that the spark of the phoenix already was given to you by the dwarven priests of earth and fire in Kazordoon." }, function(player)
- return player:hasBlessing(4)
-end)
-keywordHandler:addAliasKeyword({ "spark" })
-keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense you already talked to the hermit Eremo on the isle of Cormaya and received this blessing." }, function(player)
- return player:hasBlessing(5)
-end)
-keywordHandler:addAliasKeyword({ "wisdom" })
+keywordHandler:addKeyword({ "blessing" }, StdModule.say, {
+ npcHandler = npcHandler,
+ text = "Besides the {enhanced} blessing available from me, I know of one other, granted by a solitary {nomad} far west of Svargrond. There are also five different other blessings available, each in a sacred place. ...\nThese blessings are: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}.",
+})
+
+keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Well, as I am quite in a {fix} currently, my only hope to escape this situation may be to grant you {healing} or an {enhanced} blessing if you wish. You must desire SOMETHING, right? Sure you do." })
+keywordHandler:addAliasKeyword({ "job" })
+
keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of spiritual shielding in the whiteflower temple south of Thais." })
keywordHandler:addAliasKeyword({ "shield" })
keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of the two suns in the suntower near Ab'Dendriel." })
@@ -106,10 +98,16 @@ keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandle
keywordHandler:addAliasKeyword({ "spark" })
keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "Talk to the hermit Eremo on the isle of Cormaya about this blessing." })
keywordHandler:addAliasKeyword({ "wisdom" })
+keywordHandler:addKeyword({ "embrace" }, StdModule.say, { npcHandler = npcHandler, text = "The druids north of Carlin will provide you with the embrace of Tibia." })
+keywordHandler:addAliasKeyword({ "tibia" })
+
+keywordHandler:addKeyword({ "djinn" }, StdModule.say, { npcHandler = npcHandler, text = "I know of a mysterious djinn, bound to an existence of slavery far to the north of Tiquanda's jungles. He is bound to tell you his secrets and offer you the 'blood of the mountain'. ...\nI do not know where exactly but I guess you must first dig that one out to actually talk to him." })
+keywordHandler:addKeyword({ "nomad" }, StdModule.say, { npcHandler = npcHandler, text = "I am one with nature, one with the ice and the cold. Names are of no worth out here. Out here, you face isolation and loneliness. Only your strength, willpower and {training} can keep you alive." })
+keywordHandler:addKeyword({ "training" }, StdModule.say, { npcHandler = npcHandler, text = "My life is one of hardship, hardship I chose freely. Endurance, strength and the power of the will are my only companions in this frigid wilderness. My strength comes from disciplined training and knowledge of the outdoors." })
-npcHandler:setMessage(MESSAGE_GREET, "Welcome, noble |PLAYERNAME|")
-npcHandler:setMessage(MESSAGE_WALKAWAY, "Good Bye, noble |PLAYERNAME|")
-npcHandler:setMessage(MESSAGE_FAREWELL, "Good Bye, noble |PLAYERNAME|")
+npcHandler:setMessage(MESSAGE_GREET, "Ah, another diciple of the extreme... surviving the icy outdoors? Let me {help}! If you need some first aid out here, I can provide {healing} or grant you an {enhanced} blessing!")
+npcHandler:setMessage(MESSAGE_WALKAWAY, "Keep a stiff upper lip!")
+npcHandler:setMessage(MESSAGE_FAREWELL, "Keep a stiff upper lip!")
npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true)
diff --git a/data-otservbr-global/scripts/creaturescripts/others/droploot.lua b/data-otservbr-global/scripts/creaturescripts/others/droploot.lua
index 7be3564048d..9bf77afa71b 100644
--- a/data-otservbr-global/scripts/creaturescripts/others/droploot.lua
+++ b/data-otservbr-global/scripts/creaturescripts/others/droploot.lua
@@ -1,5 +1,7 @@
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
+
local dropLoot = CreatureEvent("DropLoot")
+
function dropLoot.onDeath(player, corpse, killer, mostDamage, unjustified, mostDamage_unjustified)
local town = player:getTown()
if town and town:getId() == TOWNS_LIST.DAWNPORT and player:getLevel() < 8 then
@@ -9,6 +11,7 @@ function dropLoot.onDeath(player, corpse, killer, mostDamage, unjustified, mostD
if player:hasFlag(PlayerFlag_NotGenerateLoot) then
return true
end
+
Blessings.DebugPrint("onDeath DROPLOOT EVENT DropLoot")
return Blessings.PlayerDeath(player, corpse, killer)
end
diff --git a/data/libs/compat/compat.lua b/data/libs/compat/compat.lua
index 061d1dbe47e..33aa0290841 100644
--- a/data/libs/compat/compat.lua
+++ b/data/libs/compat/compat.lua
@@ -572,8 +572,8 @@ function isPremium(cid)
return p ~= nil and p:isPremium() or false
end
-function getBlessingsCost(level, byCommand)
- return Blessings.getBlessingsCost(level, byCommand)
+function getBlessingCost(level, byCommand, blessId)
+ return Blessings.getBlessingCost(level, byCommand, blessId)
end
function getPvpBlessingCost(level, byCommand)
diff --git a/data/modules/scripts/blessings/blessings.lua b/data/libs/systems/blessing.lua
similarity index 52%
rename from data/modules/scripts/blessings/blessings.lua
rename to data/libs/systems/blessing.lua
index e061501a330..fee0e6fe8e8 100644
--- a/data/modules/scripts/blessings/blessings.lua
+++ b/data/libs/systems/blessing.lua
@@ -6,29 +6,44 @@ Blessings.Credits = {
lastUpdate = "08/04/2020",
todo = {
"Insert & Select query in blessings_history",
- "Add unfair fight reductio (convert the get killer is pvp fight with getDamageMap of dead player)",
- "Gamestore buy blessing",
- "Test ank print text",
- "Test all functions",
- "Test henricus prices/blessings",
- "Add data \\movements\\scripts\\quests\\cults of tibia\\icedeath.lua blessing information",
- "WotE data\\movements\\scripts\\quests\\wrath of the emperor\\realmTeleport.lua has line checking if player has bless 1??? wtf",
- "add blessings module support npc\\lib\\npcsystem\\modules.lua",
- "Fix store buying bless",
- "Check if store is inside lua or source...",
},
}
Blessings.Config = {
AdventurerBlessingLevel = configManager.getNumber(configKeys.ADVENTURERSBLESSING_LEVEL), -- Free full bless until level
HasToF = not configManager.getBoolean(configKeys.TOGGLE_SERVER_IS_RETRO), -- Enables/disables twist of fate
- InquisitonBlessPriceMultiplier = 1.1, -- Bless price multiplied by henricus
+ InquisitonBlessPriceMultiplier = 1.1, -- Bless price multiplier by henricus
SkulledDeathLoseStoreItem = configManager.getBoolean(configKeys.SKULLED_DEATH_LOSE_STORE_ITEM), -- Destroy all items on store when dying with red/blackskull
- InventoryGlowOnFiveBless = configManager.getBoolean(configKeys.INVENTORY_GLOW), -- Glow in yellow inventory items when the player has 5 or more bless,
- Debug = false, -- Prin debug messages in console if enabled
}
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/assets.lua")
+Blessings.Types = {
+ REGULAR = 1,
+ ENHANCED = 2,
+ PvP = 3,
+}
+
+Blessings.All = {
+ [1] = { id = 1, name = "Twist of Fate", type = Blessings.Types.PvP },
+ [2] = { id = 2, name = "The Wisdom of Solitude", charm = 10345, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
+ [3] = { id = 3, name = "The Spark of the Phoenix", charm = 10341, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
+ [4] = { id = 4, name = "The Fire of the Suns", charm = 10344, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
+ [5] = { id = 5, name = "The Spiritual Shielding", charm = 10343, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
+ [6] = { id = 6, name = "The Embrace of Tibia", charm = 10342, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
+ [7] = { id = 7, name = "Heart of the Mountain", charm = 25360, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false },
+ [8] = { id = 8, name = "Blood of the Mountain", charm = 25361, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false },
+}
+
+Blessings.LossPercent = {
+ [0] = { item = 100, skill = 0 },
+ [1] = { item = 70, skill = 8 },
+ [2] = { item = 45, skill = 16 },
+ [3] = { item = 25, skill = 24 },
+ [4] = { item = 10, skill = 32 },
+ [5] = { item = 0, skill = 40 },
+ [6] = { item = 0, skill = 48 },
+ [7] = { item = 0, skill = 56 },
+ [8] = { item = 0, skill = 56 },
+}
--[=====[
--
@@ -45,9 +60,6 @@ CREATE TABLE IF NOT EXISTS `blessings_history` (
--]=====]
Blessings.DebugPrint = function(content, pre, pos)
- if not Blessings.Config.Debug then
- return
- end
if pre == nil then
pre = ""
else
@@ -67,145 +79,93 @@ Blessings.DebugPrint = function(content, pre, pos)
end
end
-Blessings.C_Packet = {
- OpenWindow = 0xCF,
-}
-
-Blessings.S_Packet = {
- BlessDialog = 0x9B,
- BlessStatus = 0x9C,
-}
-
-function onRecvbyte(player, msg, byte)
- if byte == Blessings.C_Packet.OpenWindow then
- Blessings.sendBlessDialog(player)
- end
-end
-
-Blessings.sendBlessStatus = function(player, curBless)
- if player:getClient().version < 1200 then
- return true
- end
-
- -- why not using ProtocolGame::sendBlessStatus ?
- local msg = NetworkMessage()
- msg:addByte(Blessings.S_Packet.BlessStatus)
- callback = function(k)
- return true
- end
- if curBless == nil then
- curBless = player:getBlessings(callback) -- ex: {1, 2, 5, 7}
- end
- Blessings.DebugPrint(#curBless, "sendBlessStatus curBless")
- local bitWiseCurrentBless = 0
- local blessCount = 0
+Blessings.PlayerDeath = function(player, corpse, killer)
+ local hasAol = (player:getSlotItem(CONST_SLOT_NECKLACE) and player:getSlotItem(CONST_SLOT_NECKLACE):getId() == ITEM_AMULETOFLOSS)
+ local hasSkull = table.contains({ SKULL_RED, SKULL_BLACK }, player:getSkull())
+ local currBlessCount = player:getBlessings()
- for i = 1, #curBless do
- if curBless[i].losscount then
- blessCount = blessCount + 1
- end
- if (not curBless[i].losscount and Blessings.Config.HasToF) or curBless[i].losscount then
- bitWiseCurrentBless = bit.bor(bitWiseCurrentBless, Blessings.BitWiseTable[curBless[i].id])
- end
+ if hasSkull then
+ Blessings.DropLoot(player, corpse, 100, true)
+ elseif #currBlessCount < 5 and not hasAol then
+ local equipLossChance = Blessings.LossPercent[#currBlessCount].item
+ Blessings.DropLoot(player, corpse, equipLossChance)
end
- if blessCount > 5 and Blessings.Config.InventoryGlowOnFiveBless then
- bitWiseCurrentBless = bit.bor(bitWiseCurrentBless, 1)
+ if not player:getSlotItem(CONST_SLOT_BACKPACK) then
+ player:addItem(ITEM_BAG, 1, false, CONST_SLOT_BACKPACK)
end
- msg:addU16(bitWiseCurrentBless)
- msg:addByte(blessCount >= 7 and 3 or (blessCount > 0 and 2 or 1)) -- Bless dialog button colour 1 = Disabled | 2 = normal | 3 = green
-
- -- if #curBless >= 5 then
- -- msg:addU16(1) -- TODO ?
- -- else
- -- msg:addU16(0)
- -- end
-
- msg:sendToPlayer(player)
+ return true
end
-Blessings.sendBlessDialog = function(player)
- -- TODO: Migrate to protocolgame.cpp
- local msg = NetworkMessage()
- msg:addByte(Blessings.S_Packet.BlessDialog)
+Blessings.DropLoot = function(player, corpse, chance, skulled)
+ local multiplier = 100
+ math.randomseed(os.time())
+ chance = chance * multiplier
+ Blessings.DebugPrint("DropLoot chance " .. chance)
+ for i = CONST_SLOT_HEAD, CONST_SLOT_AMMO do
+ local item = player:getSlotItem(i)
+ if item then
+ local thisChance = item:isContainer() and chance or (chance / 10)
+ local thisRandom = math.random(100 * multiplier)
- callback = function(k)
- return true
- end
- local curBless = player:getBlessings()
-
- msg:addByte(Blessings.Config.HasToF and #Blessings.All or (#Blessings.All - 1)) -- total blessings
- for k = 1, #Blessings.All do
- v = Blessings.All[k]
- if v.type ~= Blessings.Types.PvP or Blessings.Config.HasToF then
- msg:addU16(Blessings.BitWiseTable[v.id])
- msg:addByte(player:getBlessingCount(v.id))
- if player:getClient().version > 1200 then
- msg:addByte(player:getBlessingCount(v.id, true)) -- Store Blessings Count
+ Blessings.DebugPrint(thisChance / multiplier .. "%" .. " | thisRandom " .. thisRandom / multiplier .. "%", "DropLoot item " .. item:getName() .. " |")
+ if skulled or thisRandom <= thisChance then
+ Blessings.DebugPrint("Dropped " .. item:getName())
+ item:moveTo(corpse)
end
end
end
- local promotion = (player:isPremium() and player:isPromoted()) and 30 or 0
- local PvPminXPLoss = Blessings.LossPercent[#curBless].skill + promotion
- local PvPmaxXPLoss = PvPminXPLoss
- if Blessings.Config.HasToF then
- PvPmaxXPLoss = math.floor(PvPminXPLoss * 1.15)
- end
- local PvEXPLoss = PvPminXPLoss
-
- local playerAmulet = player:getSlotItem(CONST_SLOT_NECKLACE)
- local haveSkull = player:getSkull() >= 4
- hasAol = (playerAmulet and playerAmulet:getId() == ITEM_AMULETOFLOSS)
-
- equipLoss = Blessings.LossPercent[#curBless].item
- if haveSkull then
- equipLoss = 100
- elseif hasAol then
- equipLoss = 0
- end
-
- msg:addByte(2) -- BYTE PREMIUM (only work with premium days)
- msg:addByte(promotion) -- XP Loss Lower POR SER PREMIUM
- msg:addByte(PvPminXPLoss) -- XP/Skill loss min pvp death
- msg:addByte(PvPmaxXPLoss) -- XP/Skill loss max pvp death
- msg:addByte(PvEXPLoss) -- XP/Skill pve death
- msg:addByte(equipLoss) -- Equip container lose pvp death
- msg:addByte(equipLoss) -- Equip container pve death
-
- msg:addByte(haveSkull and 1 or 0) -- is red/black skull
- msg:addByte(hasAol and 1 or 0)
-
- -- History
- local historyAmount = 1
- msg:addByte(historyAmount) -- History log count
- for i = 1, historyAmount do
- msg:addU32(os.time()) -- timestamp
- msg:addByte(0) -- Color message (1 - Red | 0 = White loss)
- msg:addString("Blessing Purchased", "Blessings.sendBlessDialog - Blessing Purchased") -- History message
+ if skulled and Blessings.Config.SkulledDeathLoseStoreItem then
+ local inbox = player:getStoreInbox()
+ local toBeDeleted = {}
+ if inbox and inbox:getSize() > 0 then
+ for i = 0, inbox:getSize() do
+ local item = inbox:getItem(i)
+ if item then
+ toBeDeleted[#toBeDeleted + 1] = item.uid
+ end
+ end
+ if #toBeDeleted > 0 then
+ for i, v in pairs(toBeDeleted) do
+ local item = Item(v)
+ if item then
+ item:remove()
+ end
+ end
+ end
+ end
end
-
- msg:sendToPlayer(player)
end
+-- Blessing Helpers --
Blessings.getCommandFee = function(cost)
local fee = configManager.getNumber(configKeys.BUY_BLESS_COMMAND_FEE) or 0
return cost + cost * (((fee > 100 and 100) or fee) / 100)
end
-Blessings.getBlessingsCost = function(level, byCommand)
+Blessings.getBlessingCost = function(level, byCommand, enhanced)
if byCommand == nil then
byCommand = false
end
+
+ if enhanced == nil then
+ enhanced = false
+ end
+
local cost
if level <= 30 then
cost = 2000
elseif level >= 120 then
- cost = 20000
+ local base_cost = enhanced and 26000 or 20000
+ local multiplier = enhanced and 100 or 75
+ cost = base_cost + multiplier * (level - 120)
else
- cost = (level - 20) * 200
+ local multiplier = enhanced and 260 or 200
+ cost = multiplier * (level - 20)
end
+
return byCommand and Blessings.getCommandFee(cost) or cost
end
@@ -213,6 +173,7 @@ Blessings.getPvpBlessingCost = function(level, byCommand)
if byCommand == nil then
byCommand = false
end
+
local cost
if level <= 30 then
cost = 2000
@@ -221,6 +182,7 @@ Blessings.getPvpBlessingCost = function(level, byCommand)
else
cost = (level - 20) * 200
end
+
return byCommand and Blessings.getCommandFee(cost) or cost
end
@@ -250,6 +212,7 @@ Blessings.checkBless = function(player)
for k, v in pairs(Blessings.All) do
result = player:hasBlessing(k) and result .. "\n" .. v.name or result
end
+
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 20 > result:len() and "No blessings received." or result)
return true
end
@@ -258,6 +221,7 @@ Blessings.doAdventurerBlessing = function(player)
if player:getLevel() > Blessings.Config.AdventurerBlessingLevel then
return true
end
+
player:addMissingBless(true, true)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have adventurer's blessings for being level lower than " .. Blessings.Config.AdventurerBlessingLevel .. "!")
@@ -270,70 +234,16 @@ Blessings.getInquisitionPrice = function(player)
inquifilter = function(b)
return b.inquisition
end
+
donthavefilter = function(p, b)
return not p:hasBlessing(b)
end
+
local missing = #player:getBlessings(inquifilter, donthavefilter)
- local totalBlessPrice = Blessings.getBlessingsCost(player:getLevel(), false) * missing * Blessings.Config.InquisitonBlessPriceMultiplier
+ local totalBlessPrice = Blessings.getBlessingCost(player:getLevel(), false) * missing * Blessings.Config.InquisitonBlessPriceMultiplier
return missing, totalBlessPrice
end
-Blessings.DropLoot = function(player, corpse, chance, skulled)
- local multiplier = 100 -- Improve the loot randomness spectrum
- math.randomseed(os.time()) -- Improve the loot randomness spectrum
- chance = chance * multiplier
- Blessings.DebugPrint(chance, "DropLoot chance")
- for i = CONST_SLOT_HEAD, CONST_SLOT_AMMO do
- local item = player:getSlotItem(i)
- if item then
- local thisChance = chance
- local thisRandom = math.random(100 * multiplier)
- if not item:isContainer() then
- thisChance = chance / 10
- end
- Blessings.DebugPrint(thisChance / multiplier .. "%" .. " | thisRandom " .. thisRandom / multiplier .. "%", "DropLoot item " .. item:getName() .. " |")
- if skulled or thisRandom <= thisChance then
- Blessings.DebugPrint("Dropped " .. item:getName())
- item:moveTo(corpse)
- end
- end
- end
- if skulled and Blessings.Config.SkulledDeathLoseStoreItem then
- local inbox = player:getStoreInbox()
- local toBeDeleted = {}
- if inbox and inbox:getSize() > 0 then
- for i = 0, inbox:getSize() do
- local item = inbox:getItem(i)
- if item then
- toBeDeleted[#toBeDeleted + 1] = item.uid
- end
- end
- if #toBeDeleted > 0 then
- for i, v in pairs(toBeDeleted) do
- local item = Item(v)
- if item then
- item:remove()
- end
- end
- end
- end
- end
-end
-
-Blessings.ClearBless = function(player, killer, currentBless)
- Blessings.DebugPrint(#currentBless, "ClearBless #currentBless")
- local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or false
- if hasToF and killer(killer:isPlayer() or (killer:getMaster() and killer:getMaster():isPlayer())) then -- TODO add better check if its pvp or pve
- player:removeBlessing(1)
- return
- end
- for i = 1, #currentBless do
- Blessings.DebugPrint(i, "ClearBless curBless i", " | " .. currentBless[i].name)
- player:removeBlessing(currentBless[i].id, 1)
- end
-end
-
--- bought by command
Blessings.BuyAllBlesses = function(player)
if not Tile(player:getPosition()):hasFlag(TILESTATE_PROTECTIONZONE) and (player:isPzLocked() or player:getCondition(CONDITION_INFIGHT, CONDITIONID_DEFAULT)) then
player:sendCancelMessage("You can't buy bless while in battle.")
@@ -341,23 +251,26 @@ Blessings.BuyAllBlesses = function(player)
return true
end
- local blessCost = Blessings.getBlessingsCost(player:getLevel(), true)
- local PvPBlessCost = Blessings.getPvpBlessingCost(player:getLevel(), true)
- local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or true
donthavefilter = function(p, b)
return not p:hasBlessing(b)
end
+
+ local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or true
local missingBless = player:getBlessings(nil, donthavefilter)
local missingBlessAmt = #missingBless + (hasToF and 0 or 1)
- local totalCost = blessCost * #missingBless
+ local totalCost
+ for i, bless in ipairs(missingBless) do
+ totalCost = totalCost + Blessings.getBlessingCost(player:getLevel(), true, bless.id >= 7)
+ end
if missingBlessAmt == 0 then
player:sendCancelMessage("You are already blessed.")
player:getPosition():sendMagicEffect(CONST_ME_POFF)
return true
end
+
if not hasToF then
- totalCost = totalCost + PvPBlessCost
+ totalCost = totalCost + Blessings.getPvpBlessingCost(player:getLevel(), true)
end
if player:removeMoneyBank(totalCost) then
@@ -365,9 +278,11 @@ Blessings.BuyAllBlesses = function(player)
player = player:getName(),
context = "blessings",
})
+
for i, v in ipairs(missingBless) do
player:addBlessing(v.id, 1)
end
+
player:sendCancelMessage("You received the remaining " .. missingBlessAmt .. " blesses for a total of " .. totalCost .. " gold.")
player:getPosition():sendMagicEffect(CONST_ME_HOLYAREA)
else
@@ -378,29 +293,6 @@ Blessings.BuyAllBlesses = function(player)
return true
end
-Blessings.PlayerDeath = function(player, corpse, killer)
- local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or false
- local hasAol = (player:getSlotItem(CONST_SLOT_NECKLACE) and player:getSlotItem(CONST_SLOT_NECKLACE):getId() == ITEM_AMULETOFLOSS)
- local haveSkull = table.contains({ SKULL_RED, SKULL_BLACK }, player:getSkull())
- local curBless = player:getBlessings()
-
- if haveSkull then -- lose all bless + drop all items
- Blessings.DropLoot(player, corpse, 100, true)
- elseif #curBless < 5 and not hasAol then -- lose all items
- local equipLoss = Blessings.LossPercent[#curBless].item
- Blessings.DropLoot(player, corpse, equipLoss)
- elseif #curBless < 5 and hasAol and not hasToF then
- player:removeItem(ITEM_AMULETOFLOSS, 1, -1, false)
- end
- --Blessings.ClearBless(player, killer, curBless) IMPLEMENTED IN SOURCE BECAUSE THIS WAS HAPPENING BEFORE SKILL/EXP CALCULATIONS
-
- if not player:getSlotItem(CONST_SLOT_BACKPACK) then
- player:addItem(ITEM_BAG, 1, false, CONST_SLOT_BACKPACK)
- end
-
- return true
-end
-
function Player.getBlessings(self, filter, hasblessingFilter)
local blessings = {}
if filter == nil then
@@ -414,11 +306,13 @@ function Player.getBlessings(self, filter, hasblessingFilter)
return p:hasBlessing(b)
end
end
+
for k, v in pairs(Blessings.All) do
if filter(v) and hasblessingFilter(self, k) then
table.insert(blessings, v)
end
end
+
return blessings
end
@@ -426,11 +320,13 @@ function Player.addMissingBless(self, all, tof)
if all == nil then
all = true
end
+
if tof == nil then
tof = false
elseif tof then
tof = tof and Blessings.Config.HasToF
end
+
for k, v in pairs(Blessings.All) do
if all or (v.type == Blessings.Types.REGULAR) or (tof and v.type == Blessings.Types.PvP) then
if not self:hasBlessing(k) then
@@ -438,5 +334,5 @@ function Player.addMissingBless(self, all, tof)
end
end
end
- Blessings.sendBlessStatus(self)
+ self:sendBlessStatus()
end
diff --git a/data/libs/systems/load.lua b/data/libs/systems/load.lua
index 281b8b1d466..ffe02320b96 100644
--- a/data/libs/systems/load.lua
+++ b/data/libs/systems/load.lua
@@ -1,4 +1,5 @@
-- Load systems functions
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/concoctions.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/daily_reward.lua")
dofile(CORE_DIRECTORY .. "/libs/systems/encounters.lua")
diff --git a/data/modules/modules.xml b/data/modules/modules.xml
index c45646183ac..e51bf055f2a 100644
--- a/data/modules/modules.xml
+++ b/data/modules/modules.xml
@@ -10,9 +10,6 @@
-
-
-
@@ -26,5 +23,4 @@
-
diff --git a/data/modules/scripts/blessings/assets.lua b/data/modules/scripts/blessings/assets.lua
deleted file mode 100644
index 73d02a272f3..00000000000
--- a/data/modules/scripts/blessings/assets.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-Blessings.Types = {
- REGULAR = 1,
- ENHANCED = 2,
- PvP = 3,
-}
-
-Blessings.All = {
- [1] = { id = 1, name = "Twist of Fate", type = Blessings.Types.PvP },
- [2] = { id = 2, name = "The Wisdom of Solitude", charm = 10345, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
- [3] = { id = 3, name = "The Spark of the Phoenix", charm = 10341, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
- [4] = { id = 4, name = "The Fire of the Suns", charm = 10344, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
- [5] = { id = 5, name = "The Spiritual Shielding", charm = 10343, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
- [6] = { id = 6, name = "The Embrace of Tibia", charm = 10342, type = Blessings.Types.REGULAR, losscount = true, inquisition = true },
- [7] = { id = 7, name = "Blood of the Mountain", charm = 25360, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false },
- [8] = { id = 8, name = "Heart of the Mountain", charm = 25361, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false },
-}
-
-Blessings.LossPercent = {
- [0] = { item = 100, skill = 0 },
- [1] = { item = 70, skill = 8 },
- [2] = { item = 45, skill = 16 },
- [3] = { item = 25, skill = 24 },
- [4] = { item = 10, skill = 32 },
- [5] = { item = 0, skill = 40 },
- [6] = { item = 0, skill = 48 },
- [7] = { item = 0, skill = 56 },
- [8] = { item = 0, skill = 56 },
-}
-
-Blessings.BitWiseTable = {
- [0] = 1,
- [1] = 2,
- [2] = 4,
- [3] = 8,
- [4] = 16,
- [5] = 32,
- [6] = 64,
- [7] = 128,
- [8] = 256,
- [9] = 512,
- [10] = 1024,
- [11] = 2048,
- [12] = 4096,
- [13] = 8192,
- [14] = 16384,
- [15] = 32768,
-}
diff --git a/data/npclib/npc.lua b/data/npclib/npc.lua
index 4de60c45c49..4a8097ff27f 100644
--- a/data/npclib/npc.lua
+++ b/data/npclib/npc.lua
@@ -89,7 +89,7 @@ function SayEvent(npcId, playerId, messageDelayed, npcHandler, textType)
local parseInfo = {
[TAG_PLAYERNAME] = player:getName(),
[TAG_TIME] = getFormattedWorldTime(),
- [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false),
+ [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true),
[TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false),
}
npc:say(npcHandler:parseMessage(messageDelayed, parseInfo), textType or TALKTYPE_PRIVATE_NP, false, player, npc:getPosition())
diff --git a/data/npclib/npc_system/modules.lua b/data/npclib/npc_system/modules.lua
index aca92804e80..08027a67d2b 100644
--- a/data/npclib/npc_system/modules.lua
+++ b/data/npclib/npc_system/modules.lua
@@ -60,7 +60,7 @@ if Modules == nil then
local parseInfo = {
[TAG_PLAYERNAME] = player:getName(),
[TAG_TIME] = getFormattedWorldTime(),
- [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false),
+ [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true),
[TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false),
[TAG_TRAVELCOST] = costMessage,
}
@@ -160,7 +160,7 @@ if Modules == nil then
end
local parseInfo = {
- [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false),
+ [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true),
[TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false),
}
if player:hasBlessing(parameters.bless) then
@@ -175,7 +175,7 @@ if Modules == nil then
npc,
player
)
- elseif not player:removeMoneyBank(type(parameters.cost) == "string" and npcHandler:parseMessage(parameters.cost, parseInfo) or parameters.cost) then
+ elseif not player:removeMoneyBank(type(parameters.cost) == "string" and tonumber(npcHandler:parseMessage(parameters.cost, parseInfo)) or parameters.cost) then
npcHandler:say("Oh. You do not have enough money.", npc, player)
else
npcHandler:say(parameters.text or "You have been blessed by one of the seven gods!", npc, player)
diff --git a/data/scripts/actions/items/blessing_charms.lua b/data/scripts/actions/items/blessing_charms.lua
index 7e70c5a6719..fdca561c3a2 100644
--- a/data/scripts/actions/items/blessing_charms.lua
+++ b/data/scripts/actions/items/blessing_charms.lua
@@ -1,4 +1,4 @@
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
local blessingCharms = Action()
diff --git a/data/scripts/actions/items/check_bless.lua b/data/scripts/actions/items/check_bless.lua
index 0d2478f5f11..09d07fffd85 100644
--- a/data/scripts/actions/items/check_bless.lua
+++ b/data/scripts/actions/items/check_bless.lua
@@ -1,4 +1,4 @@
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
local checkBless = Action()
diff --git a/data/scripts/creaturescripts/player/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
index 8ebf88a72e9..79c694d73bc 100644
--- a/data/scripts/creaturescripts/player/adventure_blessing_login.lua
+++ b/data/scripts/creaturescripts/player/adventure_blessing_login.lua
@@ -1,4 +1,4 @@
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
local adventurerBlessingLogin = CreatureEvent("AdventurerBlessingLogin")
diff --git a/data/scripts/systems/item_tiers.lua b/data/scripts/systems/item_tiers.lua
index 06a8321a034..cc7a2a2697d 100644
--- a/data/scripts/systems/item_tiers.lua
+++ b/data/scripts/systems/item_tiers.lua
@@ -90,7 +90,7 @@ local itemTierClassifications = {
},
}
--- Item tier with gold price for uprading it
+-- Item tier with gold price for upgrading it
for classificationId, classificationTable in ipairs(itemTierClassifications) do
local itemClassification = Game.createItemClassification(classificationId)
local classification = {}
diff --git a/data/scripts/talkactions/gm/bless_status.lua b/data/scripts/talkactions/gm/bless_status.lua
index 7cb861947dd..a316330db31 100644
--- a/data/scripts/talkactions/gm/bless_status.lua
+++ b/data/scripts/talkactions/gm/bless_status.lua
@@ -1,4 +1,4 @@
-dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua")
+dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua")
local blessStatus = TalkAction("/bless")
@@ -6,7 +6,7 @@ function blessStatus.onSay(player, words, param)
-- create log
logCommand(player, words, param)
- Blessings.sendBlessStatus(player)
+ player:sendBlessStatus()
return true
end
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index a8ef732b900..d0f1cfae288 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -3450,7 +3450,6 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
g_game().sendSingleSoundEffect(static_self_cast()->getPosition(), sex == PLAYERSEX_FEMALE ? SoundEffect_t::HUMAN_FEMALE_DEATH : SoundEffect_t::HUMAN_MALE_DEATH, getPlayer());
if (skillLoss) {
- uint8_t unfairFightReduction = 100;
int playerDmg = 0;
int othersDmg = 0;
uint32_t sumLevels = 0;
@@ -3467,10 +3466,13 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
}
}
}
+
bool pvpDeath = false;
if (playerDmg > 0 || othersDmg > 0) {
pvpDeath = (Player::lastHitIsPlayer(lastHitCreature) || playerDmg / (playerDmg + static_cast(othersDmg)) >= 0.05);
}
+
+ uint8_t unfairFightReduction = 100;
if (pvpDeath && sumLevels > level) {
double reduce = level / static_cast(sumLevels);
unfairFightReduction = std::max(20, std::floor((reduce * 100) + 0.5));
@@ -3480,7 +3482,7 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
uint64_t sumMana = 0;
uint64_t lostMana = 0;
- // sum up all the mana
+ // Sum up all the mana
for (uint32_t i = 1; i <= magLevel; ++i) {
sumMana += vocation->getReqMana(i);
}
@@ -3490,12 +3492,10 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
double deathLossPercent = getLostPercent() * (unfairFightReduction / 100.);
// Charm bless bestiary
- if (lastHitCreature && lastHitCreature->getMonster()) {
- if (charmRuneBless != 0) {
- const auto mType = g_monsters().getMonsterType(lastHitCreature->getName());
- if (mType && mType->info.raceid == charmRuneBless) {
- deathLossPercent = (deathLossPercent * 90) / 100;
- }
+ if (lastHitCreature && lastHitCreature->getMonster() && charmRuneBless != 0) {
+ const auto &mType = g_monsters().getMonsterType(lastHitCreature->getName());
+ if (mType && mType->info.raceid == charmRuneBless) {
+ deathLossPercent = (deathLossPercent * 90) / 100;
}
}
@@ -3517,7 +3517,9 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
}
// Level loss
- auto expLoss = static_cast(experience * deathLossPercent);
+ auto expLoss = static_cast(std::ceil((experience * deathLossPercent) / 100.));
+ g_logger().debug("[{}] - experience lost {}", __FUNCTION__, expLoss);
+
g_events().eventPlayerOnLoseExperience(static_self_cast(), expLoss);
g_callbacks().executeCallback(EventCallback_t::playerOnLoseExperience, &EventCallback::playerOnLoseExperience, getPlayer(), expLoss);
@@ -3526,9 +3528,9 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
lostExp << "You lost " << expLoss << " experience.";
// Skill loss
- for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // for each skill
+ for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // For each skill
uint64_t sumSkillTries = 0;
- for (uint16_t c = 11; c <= skills[i].level; ++c) { // sum up all required tries for all skill levels
+ for (uint16_t c = 11; c <= skills[i].level; ++c) { // Sum up all required tries for all skill levels
sumSkillTries += vocation->getReqSkillTries(i, c);
}
@@ -3604,14 +3606,21 @@ void Player::death(const std::shared_ptr &lastHitCreature) {
bless.empty() ? blessOutput << "You weren't protected with any blessings."
: blessOutput << "You were blessed with " << bless;
- // Make player lose bless
+ const auto playerSkull = getSkull();
+ bool hasSkull = (playerSkull == Skulls_t::SKULL_RED || playerSkull == Skulls_t::SKULL_BLACK);
uint8_t maxBlessing = 8;
- if (pvpDeath && hasBlessing(1)) {
+ if (!hasSkull && pvpDeath && hasBlessing(1)) {
removeBlessing(1, 1); // Remove TOF only
} else {
for (int i = 2; i <= maxBlessing; i++) {
removeBlessing(i, 1);
}
+
+ const auto &playerAmulet = getThing(CONST_SLOT_NECKLACE);
+ bool usingAol = (playerAmulet && playerAmulet->getItem()->getID() == ITEM_AMULETOFLOSS);
+ if (usingAol) {
+ removeItemOfType(ITEM_AMULETOFLOSS, 1, -1);
+ }
}
}
sendTextMessage(MESSAGE_EVENT_ADVANCE, blessOutput.str());
@@ -6293,21 +6302,30 @@ double Player::getLostPercent() const {
return std::max(0, deathLosePercent) / 100.;
}
+ bool isRetro = g_configManager().getBoolean(TOGGLE_SERVER_IS_RETRO);
+ const auto factor = (isRetro ? 6.31 : 8);
+ double percentReduction = (blessingCount * factor) / 100.;
+
double lossPercent;
if (level >= 24) {
const double tmpLevel = level + (levelPercent / 100.);
lossPercent = ((tmpLevel + 50) * 50 * ((tmpLevel * tmpLevel) - (5 * tmpLevel) + 8)) / experience;
} else {
- lossPercent = 5;
+ percentReduction = (percentReduction >= 0.40 ? 0.50 : percentReduction);
+ lossPercent = 10;
}
- double percentReduction = 0;
+ g_logger().debug("[{}] - lossPercent {}", __FUNCTION__, lossPercent);
+ g_logger().debug("[{}] - before promotion {}", __FUNCTION__, percentReduction);
+
if (isPromoted()) {
- percentReduction += 30;
+ percentReduction += 30 / 100.;
+ g_logger().debug("[{}] - after promotion {}", __FUNCTION__, percentReduction);
}
- percentReduction += blessingCount * 8;
- return lossPercent * (1 - (percentReduction / 100.)) / 100.;
+ g_logger().debug("[{}] - total lost percent {}", __FUNCTION__, lossPercent - (lossPercent * percentReduction));
+
+ return lossPercent - (lossPercent * percentReduction);
}
[[nodiscard]] const std::string &Player::getGuildNick() const {
diff --git a/src/enums/player_blessings.hpp b/src/enums/player_blessings.hpp
index 086f04a6442..f71396f71b3 100644
--- a/src/enums/player_blessings.hpp
+++ b/src/enums/player_blessings.hpp
@@ -20,6 +20,6 @@ enum class Blessings : uint8_t {
TheFireOfTheSuns = 4,
TheSpiritualShielding = 5,
TheEmbraceOfTibia = 6,
- BloodOfTheMountain = 7,
- HearthOfTheMountain = 8
+ HearthOfTheMountain = 7,
+ BloodOfTheMountain = 8
};
diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp
index 34c665f2fe3..08045f1de00 100644
--- a/src/lua/functions/creatures/player/player_functions.cpp
+++ b/src/lua/functions/creatures/player/player_functions.cpp
@@ -249,6 +249,7 @@ void PlayerFunctions::init(lua_State* L) {
Lua::registerMethod(L, "Player", "addTransferableCoins", PlayerFunctions::luaPlayerAddTransferableCoins);
Lua::registerMethod(L, "Player", "removeTransferableCoins", PlayerFunctions::luaPlayerRemoveTransferableCoins);
+ Lua::registerMethod(L, "Player", "sendBlessStatus", PlayerFunctions::luaPlayerSendBlessStatus);
Lua::registerMethod(L, "Player", "hasBlessing", PlayerFunctions::luaPlayerHasBlessing);
Lua::registerMethod(L, "Player", "addBlessing", PlayerFunctions::luaPlayerAddBlessing);
Lua::registerMethod(L, "Player", "removeBlessing", PlayerFunctions::luaPlayerRemoveBlessing);
@@ -3075,6 +3076,20 @@ int PlayerFunctions::luaPlayerRemoveTransferableCoins(lua_State* L) {
return 1;
}
+int PlayerFunctions::luaPlayerSendBlessStatus(lua_State* L) {
+ // player:sendBlessStatus()
+ const auto &player = Lua::getUserdataShared(L, 1);
+ if (!player) {
+ Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND));
+ Lua::pushBoolean(L, false);
+ return 0;
+ }
+
+ player->sendBlessStatus();
+ Lua::pushBoolean(L, true);
+ return 1;
+}
+
int PlayerFunctions::luaPlayerHasBlessing(lua_State* L) {
// player:hasBlessing(blessing)
const uint8_t blessing = Lua::getNumber(L, 2);
diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp
index 8ecb42b83aa..ab617c35132 100644
--- a/src/lua/functions/creatures/player/player_functions.hpp
+++ b/src/lua/functions/creatures/player/player_functions.hpp
@@ -229,6 +229,7 @@ class PlayerFunctions {
static int luaPlayerAddTransferableCoins(lua_State* L);
static int luaPlayerRemoveTransferableCoins(lua_State* L);
+ static int luaPlayerSendBlessStatus(lua_State* L);
static int luaPlayerHasBlessing(lua_State* L);
static int luaPlayerAddBlessing(lua_State* L);
static int luaPlayerRemoveBlessing(lua_State* L);
diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp
index 466a2413353..fead3665db3 100644
--- a/src/server/network/protocol/protocolgame.cpp
+++ b/src/server/network/protocol/protocolgame.cpp
@@ -64,16 +64,6 @@
// This "getIteration" function will allow us to get the total number of iterations that run within a specific map
// Very useful to send the total amount in certain bytes in the ProtocolGame class
namespace {
- template
- uint16_t getIterationIncreaseCount(T &map) {
- uint16_t totalIterationCount = 0;
- for ([[maybe_unused]] const auto &[first, second] : map) {
- totalIterationCount++;
- }
-
- return totalIterationCount;
- }
-
template
uint16_t getVectorIterationIncreaseCount(T &vector) {
uint16_t totalIterationCount = 0;
@@ -1013,24 +1003,24 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage &msg, uint8_t recvby
case 0x1E:
g_game().playerReceivePing(player->getID());
break;
- case 0x2a:
+ case 0x28:
+ parseStashWithdraw(msg);
+ break;
+ case 0x29:
+ parseRetrieveDepotSearch(msg);
+ break;
+ case 0x2A:
parseCyclopediaMonsterTracker(msg);
break;
case 0x2B:
parsePartyAnalyzerAction(msg);
break;
- case 0x2c:
+ case 0x2C:
parseLeaderFinderWindow(msg);
break;
- case 0x2d:
+ case 0x2D:
parseMemberFinderWindow(msg);
break;
- case 0x28:
- parseStashWithdraw(msg);
- break;
- case 0x29:
- parseRetrieveDepotSearch(msg);
- break;
case 0x32:
parseExtendedOpcode(msg);
break; // otclient extended opcode
@@ -1275,6 +1265,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage &msg, uint8_t recvby
case 0xCD:
parseInspectionObject(msg);
break;
+ case 0xCF:
+ sendBlessingWindow();
+ break;
case 0xD2:
g_game().playerRequestOutfit(player->getID());
break;
@@ -2314,7 +2307,7 @@ void ProtocolGame::parseBestiarysendRaces() {
}
NetworkMessage msg;
- msg.addByte(0xd5);
+ msg.addByte(0xD5);
msg.add(BESTY_RACE_LAST);
std::map mtype_list = g_game().getBestiaryList();
for (uint8_t i = BESTY_RACE_FIRST; i <= BESTY_RACE_LAST; i++) {
@@ -2346,7 +2339,7 @@ void ProtocolGame::sendBestiaryEntryChanged(uint16_t raceid) {
}
NetworkMessage msg;
- msg.addByte(0xd9);
+ msg.addByte(0xD9);
msg.add(raceid);
writeToOutputBuffer(msg);
}
@@ -2380,7 +2373,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) {
uint8_t currentLevel = g_iobestiary().getKillStatus(mtype, killCounter);
NetworkMessage newmsg;
- newmsg.addByte(0xd7);
+ newmsg.addByte(0xD7);
newmsg.add(raceId);
newmsg.addString(Class);
@@ -2442,7 +2435,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) {
}
newmsg.addByte(attackmode);
- newmsg.addByte(0x2);
+ newmsg.addByte(0x02);
newmsg.add(mtype->info.healthMax);
newmsg.add(mtype->info.experience);
newmsg.add(mtype->getBaseSpeed());
@@ -2914,7 +2907,7 @@ void ProtocolGame::BestiarysendCharms() {
removeRuneCost = (removeRuneCost * 75) / 100;
}
NetworkMessage msg;
- msg.addByte(0xd8);
+ msg.addByte(0xD8);
msg.add(player->getCharmPoints());
const auto charmList = g_game().getCharmList();
@@ -2993,7 +2986,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) {
text = raceName;
}
NetworkMessage newmsg;
- newmsg.addByte(0xd6);
+ newmsg.addByte(0xD6);
newmsg.addString(text);
newmsg.add(race.size());
std::map creaturesKilled = g_iobestiary().getBestiaryKillCountByMonsterIDs(player, race);
@@ -4345,28 +4338,96 @@ void ProtocolGame::sendBasicData() {
writeToOutputBuffer(msg);
}
-void ProtocolGame::sendBlessStatus() {
+void ProtocolGame::sendBlessingWindow() {
if (!player) {
return;
}
NetworkMessage msg;
- // uint8_t maxClientBlessings = (player->operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6; (compartability for the client 10)
- // Ignore ToF (bless 1)
+ msg.addByte(0x9B);
+
+ bool isRetro = g_configManager().getBoolean(TOGGLE_SERVER_IS_RETRO);
+
+ msg.addByte(isRetro ? 0x07 : 0x08);
+ for (auto blessing : magic_enum::enum_values()) {
+ if (isRetro && blessing == Blessings::TwistOfFate) {
+ continue;
+ }
+
+ const auto blessingValue = enumToValue(blessing);
+ const auto blessingId = 1 << blessingValue;
+ msg.add(blessingId);
+ msg.addByte(player->getBlessingCount(blessingValue));
+ msg.addByte(player->getBlessingCount(blessingValue, true));
+ }
+
+ // Start at "The Wisdom Of Solitude"
uint8_t blessCount = 0;
- uint16_t flag = 0;
- uint16_t pow2 = 2;
- for (int i = 1; i <= 8; i++) {
- if (player->hasBlessing(i)) {
- if (i > 1) {
- blessCount++;
- }
+ for (auto bless : magic_enum::enum_values()) {
+ if (bless == Blessings::TwistOfFate) {
+ continue;
+ }
- flag |= pow2;
+ if (player->hasBlessing(enumToValue(bless))) {
+ blessCount++;
+ }
+ }
+
+ const auto isPromoted = player->isPromoted();
+ const auto factor = (isRetro ? 6.31 : 8);
+ const auto skillReduction = factor * blessCount;
+ const auto promotionReduction = (isPromoted ? 30 : 0);
+ const auto minReduction = skillReduction + promotionReduction;
+ const auto maxLossPvpDeath = calculateMaxPvpReduction(blessCount, isPromoted);
+
+ msg.addByte(isPromoted);
+ msg.addByte(30); // Reduction bonus with promotion
+ msg.addByte(minReduction);
+ msg.addByte(isRetro ? minReduction : maxLossPvpDeath);
+ msg.addByte(minReduction);
+
+ const auto playerSkull = player->getSkull();
+ const auto &playerAmulet = player->getThing(CONST_SLOT_NECKLACE);
+ bool hasSkull = (playerSkull == Skulls_t::SKULL_RED || playerSkull == Skulls_t::SKULL_BLACK);
+ bool usingAol = (playerAmulet && playerAmulet->getItem()->getID() == ITEM_AMULETOFLOSS);
+ if (hasSkull) {
+ msg.addByte(100);
+ msg.addByte(100);
+ } else if (usingAol) {
+ msg.addByte(0);
+ msg.addByte(0);
+ } else {
+ msg.addByte(calculateEquipmentLoss(blessCount, true));
+ msg.addByte(calculateEquipmentLoss(blessCount, true));
+ }
+
+ msg.addByte(hasSkull);
+ msg.addByte(usingAol);
+
+ msg.addByte(0x00);
+
+ writeToOutputBuffer(msg);
+}
+
+void ProtocolGame::sendBlessStatus() {
+ if (!player) {
+ return;
+ }
+
+ // Ignore Twist of Fate (Id 1)
+ uint8_t blessCount = 0;
+ for (auto bless : magic_enum::enum_values()) {
+ if (bless == Blessings::TwistOfFate) {
+ continue;
+ }
+ if (player->hasBlessing(enumToValue(bless))) {
+ blessCount++;
}
}
+ NetworkMessage msg;
msg.addByte(0x9C);
+
if (oldProtocol) {
msg.add(blessCount >= 5 ? 0x01 : 0x00);
} else {
@@ -4379,16 +4440,20 @@ void ProtocolGame::sendBlessStatus() {
}
void ProtocolGame::sendPremiumTrigger() {
- if (!g_configManager().getBoolean(FREE_PREMIUM) && !g_configManager().getBoolean(VIP_SYSTEM_ENABLED)) {
- NetworkMessage msg;
- msg.addByte(0x9E);
- msg.addByte(16);
- for (uint16_t i = 0; i <= 15; i++) {
- // PREMIUM_TRIGGER_TRAIN_OFFLINE = false, PREMIUM_TRIGGER_XP_BOOST = false, PREMIUM_TRIGGER_MARKET = false, PREMIUM_TRIGGER_VIP_LIST = false, PREMIUM_TRIGGER_DEPOT_SPACE = false, PREMIUM_TRIGGER_INVITE_PRIVCHAT = false
- msg.addByte(0x01);
- }
- writeToOutputBuffer(msg);
+ if (g_configManager().getBoolean(FREE_PREMIUM) || g_configManager().getBoolean(VIP_SYSTEM_ENABLED)) {
+ return;
}
+
+ NetworkMessage msg;
+ msg.addByte(0x9E);
+
+ msg.addByte(16);
+ for (uint16_t i = 0; i <= 15; i++) {
+ // PREMIUM_TRIGGER_TRAIN_OFFLINE = false, PREMIUM_TRIGGER_XP_BOOST = false, PREMIUM_TRIGGER_MARKET = false, PREMIUM_TRIGGER_VIP_LIST = false, PREMIUM_TRIGGER_DEPOT_SPACE = false, PREMIUM_TRIGGER_INVITE_PRIVCHAT = false
+ msg.addByte(0x01);
+ }
+
+ writeToOutputBuffer(msg);
}
void ProtocolGame::sendTextMessage(const TextMessage &message) {
@@ -5481,8 +5546,8 @@ void ProtocolGame::sendOpenForge() {
msg.add(convergenceFusionCount);
msg.setBufferPosition(transferTotalCountPosition);
- auto transferTotalCount = getIterationIncreaseCount(donorTierItemMap);
- msg.addByte(static_cast(transferTotalCount));
+ auto transferTotalCount = donorTierItemMap.size();
+ msg.addByte(transferTotalCount);
if (transferTotalCount > 0) {
for (const auto &[itemId, tierAndCountMap] : donorTierItemMap) {
// Let's access the itemType to check the item's (donator of tier) classification level
@@ -5494,7 +5559,7 @@ void ProtocolGame::sendOpenForge() {
}
// Total count of item (donator of tier)
- auto donorTierTotalItemsCount = getIterationIncreaseCount(tierAndCountMap);
+ auto donorTierTotalItemsCount = tierAndCountMap.size();
msg.add(donorTierTotalItemsCount);
for (const auto &[donorItemTier, donorItemCount] : tierAndCountMap) {
msg.add(itemId);
@@ -5679,8 +5744,7 @@ void ProtocolGame::sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId
void ProtocolGame::sendForgeHistory(uint8_t page) {
page = page + 1;
auto historyVector = player->getForgeHistory();
- auto historyVectorLen = getVectorIterationIncreaseCount(historyVector);
-
+ auto historyVectorLen = historyVector.size();
uint16_t lastPage = (1 < std::floor((historyVectorLen - 1) / 9) + 1) ? static_cast(std::floor((historyVectorLen - 1) / 9) + 1) : 1;
uint16_t currentPage = (lastPage < page) ? lastPage : page;
@@ -5691,8 +5755,7 @@ void ProtocolGame::sendForgeHistory(uint8_t page) {
historyPerPage.emplace_back(historyVector[entry - 1]);
}
- auto historyPageToSend = getVectorIterationIncreaseCount(historyPerPage);
-
+ auto historyPageToSend = historyPerPage.size();
NetworkMessage msg;
msg.addByte(0x88);
msg.add(currentPage - 1); // Current page
diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp
index 27fa6c8716c..2048bea7c16 100644
--- a/src/server/network/protocol/protocolgame.hpp
+++ b/src/server/network/protocol/protocolgame.hpp
@@ -466,7 +466,10 @@ class ProtocolGame final : public Protocol {
void AddPlayerStats(NetworkMessage &msg);
void AddOutfit(NetworkMessage &msg, const Outfit_t &outfit, bool addMount = true);
void AddPlayerSkills(NetworkMessage &msg);
+ // Blessing
+ void sendBlessingWindow();
void sendBlessStatus();
+ // End Blessing
void sendPremiumTrigger();
void sendMessageDialog(const std::string &message);
void AddWorldLight(NetworkMessage &msg, LightInfo lightInfo);
diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp
index f393f5332ae..03c24054023 100644
--- a/src/utils/tools.cpp
+++ b/src/utils/tools.cpp
@@ -2079,3 +2079,44 @@ const std::map &getMaxValuePerSkill() {
return maxValuePerSkill;
}
+
+float calculateEquipmentLoss(uint8_t blessingAmount, bool isContainer /* = false*/) {
+ float lossPercent = 0;
+ switch (blessingAmount) {
+ case 0:
+ lossPercent = 10;
+ break;
+ case 1:
+ lossPercent = 7;
+ break;
+ case 2:
+ lossPercent = 4.5;
+ break;
+ case 3:
+ lossPercent = 2.5;
+ break;
+ case 4:
+ lossPercent = 1;
+ break;
+ default:
+ // Blessing Amount >= 5
+ lossPercent = 0;
+ break;
+ }
+
+ return isContainer ? lossPercent * 10 : lossPercent;
+}
+
+uint8_t calculateMaxPvpReduction(uint8_t blessCount, bool isPromoted /* = false*/) {
+ uint8_t result = 80 + (2 * blessCount) - (blessCount / 3);
+
+ if (blessCount == 5) {
+ result -= 1;
+ }
+
+ if (isPromoted) {
+ result += 6;
+ }
+
+ return result;
+}
diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp
index 7a7d4a22bdd..e1477ece7ad 100644
--- a/src/utils/tools.hpp
+++ b/src/utils/tools.hpp
@@ -212,3 +212,6 @@ bool caseInsensitiveCompare(std::string_view str1, std::string_view str2, size_t
void printStackTrace();
const std::map &getMaxValuePerSkill();
+
+float calculateEquipmentLoss(uint8_t blessingAmount, bool isContainer = false);
+uint8_t calculateMaxPvpReduction(uint8_t blessCount, bool isPromoted = false);