Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions data/creaturescripts/scripts/login.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ function onLogin(player)
-- Update client exp display
player:updateClientExpDisplay()

player:sendBlessings()

-- achievements points for highscores
if player:getStorageValue(PlayerStorageKeys.achievementsTotal) == -1 then
player:setStorageValue(PlayerStorageKeys.achievementsTotal, player:getAchievementPoints())
Expand Down
82 changes: 72 additions & 10 deletions data/lib/core/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -630,16 +630,6 @@ function Player.takeScreenshot(self, screenshotType, ignoreConfig)
return true
end

function Player.getBlessings(self)
local blessings = 0
for i = 1, 6 do
if self:hasBlessing(i) then
blessings = blessings + 1
end
end
return blessings
end

local slots = {
CONST_SLOT_RIGHT,
CONST_SLOT_LEFT,
Expand Down Expand Up @@ -674,3 +664,75 @@ function Player.getTotalDefense(self)
end
return total
end

function Player.isPromoted(self)
return not self:getVocation():getPromotion() and true or false
end

function Player.hasBlessing(self, blessingId)
return self:getBlessing(blessingId) > 0
end

function Player.getBlessings(self, excludeSpecial)
local first = BLESSING_FIRST
if excludeSpecial then
first = BLESSING_WISDOM_OF_SOLITUDE -- skip first two blessings
end

local blessings = {}
for blessingId = first, BLESSING_LAST, 1 do
if self:hasBlessing(blessingId) then
blessings[#blessings + 1] = blessingId
end
end
return blessings
end

function Player.addBlessingsHistory(self, event, type)
return db.query("INSERT INTO `blessings_history` (`player_id`, `type`, `event`, `created_at`) VALUES (" .. self:getGuid() .. ", " .. type .. ", " .. db.escapeString(event) .. ", " .. os.time() .. ")")
end

function Player.getBlessingsHistory(self)
local resultId = db.storeQuery("SELECT `type`, `event`, `created_at` FROM `blessings_history` WHERE `player_id` = " .. self:getGuid() .. " ORDER BY `created_at` DESC")
if not resultId then
return {}
end

local history = {}
repeat
history[#history + 1] = {
type = result.getNumber(resultId, "type"),
event = result.getString(resultId, "event"),
ts = result.getString(resultId, "created_at")
}
until not result.next(resultId)
result.free(resultId)

return history
end

function Player.sendBlessings(self)
local msg = NetworkMessage()
msg:addByte(0x9C)

local bits = 0

local blessings = self:getBlessings(false)
for _, blessing in ipairs(blessings) do
bits = bit.bor(bits, 2 ^ blessing)
end

msg:addU16(bits)
if #blessings >= 8 then
msg:addByte(0x03)
elseif #blessings > 0 then
msg:addByte(0x02)
else
msg:addByte(0x01)
end

msg:sendToPlayer(self)
msg:delete()

return true
end
40 changes: 39 additions & 1 deletion data/migrations/35.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,41 @@
function onUpdateDatabase()
return false
print("> Updating database to version 36 (blessings update)")

-- skip BLESSING_ADVENTURERS_BLESSING & BLESSING_TWIST_OF_FATE
-- start at 10003001 + BLESSING_WISDOM_OF_SOLITUDE (2)
local storageRange = 10003003

local resultId = db.storeQuery("SELECT `id`, `blessings` FROM `players`")
if resultId then
local rows = {}
repeat
local blessings = result.getNumber(resultId, "blessings")
local playerId = result.getNumber(resultId, "id")
for id = 0, 4 do
if bit.band(blessings, 2 ^ id) ~= 0 then
rows[#rows + 1] = "(" .. playerId .. ", " .. (storageRange + id) .. ", 1)"
end
end
until not result.next(resultId)
result.free(resultId)

local stmt = "INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "
if #rows > 0 then
db.query(stmt .. table.concat(rows, ","))
end
end

db.query([[
CREATE TABLE IF NOT EXISTS `blessings_history` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`player_id` int NOT NULL,
`type` tinyint NOT NULL DEFAULT '0',
`event` varchar(255) NOT NULL,
`created_at` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
]])
db.query("ALTER TABLE `players` DROP COLUMN `blessings`")
return true
end
3 changes: 3 additions & 0 deletions data/migrations/36.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function onUpdateDatabase()
return false
end
15 changes: 15 additions & 0 deletions data/npc/lib/npcsystem/modules.lua
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ if not Modules then
return true
end

local blessings = {
[BLESSING_TWIST_OF_FATE] = {name = "Twist of Fate"},
[BLESSING_WISDOM_OF_SOLITUDE] = {name = "Wisdom of Solitude"},
[BLESSING_SPARK_OF_THE_PHOENIX] = {name = "Spark of the Phoenix"},
[BLESSING_FIRE_OF_THE_SUNS] = {name = "Fire of the Suns"},
[BLESSING_SPIRITUAL_SHIELDING] = {name = "Spiritual Shielding"},
[BLESSING_EMBRACE_OF_THE_WORLD] = {name = "Embrace of the World"},
[BLESSING_HEART_OF_THE_MOUNTAIN] = {name = "Heart of the Mountain"},
[BLESSING_BLOOD_OF_THE_MOUNTAIN] = {name = "Blood of the Mountain"}
}

function StdModule.bless(cid, message, keywords, parameters, node)
local npcHandler = parameters.npcHandler
if not npcHandler then
Expand All @@ -140,6 +151,10 @@ if not Modules then
elseif not player:removeTotalMoney(parameters.cost) then
npcHandler:say("You don't have enough money for blessing.", cid)
else
local blessing = blessings[parameters.bless]
if blessing then
player:addBlessingsHistory("\"" .. blessing.name .. "\" purchased from " .. Npc():getName() .. ".", 1)
end
player:addBlessing(parameters.bless)
npcHandler:say("You have been blessed by one of the five gods!", cid)
end
Expand Down
22 changes: 17 additions & 5 deletions data/npc/scripts/bless.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,36 @@ function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end
function onThink() npcHandler:onThink() end

local node0 = keywordHandler:addKeyword({'twist of fate'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the Twist of Fate blessing for 0 gold?'})
node0:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 1, premium = false, cost = 0})
node0:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node1 = keywordHandler:addKeyword({'first bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the first blessing for 10000 gold?'})
node1:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 1, premium = true, cost = 10000})
node1:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 2, premium = true, cost = 10000})
node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node2 = keywordHandler:addKeyword({'second bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the second blessing for 10000 gold?'})
node2:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 2, premium = true, cost = 10000})
node2:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 3, premium = true, cost = 10000})
node2:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node3 = keywordHandler:addKeyword({'third bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the third blessing for 10000 gold?'})
node3:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 3, premium = true, cost = 10000})
node3:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 4, premium = true, cost = 10000})
node3:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node4 = keywordHandler:addKeyword({'fourth bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the fourth blessing for 10000 gold?'})
node4:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 4, premium = true, cost = 10000})
node4:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 5, premium = true, cost = 10000})
node4:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node5 = keywordHandler:addKeyword({'fifth bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the fifth blessing for 10000 gold?'})
node5:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 5, premium = true, cost = 10000})
node5:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 6, premium = true, cost = 10000})
node5:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node6 = keywordHandler:addKeyword({'sixth bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the sixth blessing for 10000 gold?'})
node6:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 7, premium = true, cost = 10000})
node6:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

local node7 = keywordHandler:addKeyword({'seventh bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the seventh blessing for 10000 gold?'})
node7:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, bless = 8, premium = true, cost = 10000})
node7:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'})

npcHandler:addModule(FocusModule:new())
13 changes: 8 additions & 5 deletions data/scripts/actions/others/blessing_charms.lua
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
local items = {
[11260] = {text = "The Spiritual Shielding protects you.", id = 1, effect = CONST_ME_LOSEENERGY},
[11259] = {text = "The Embrace of the World surrounds you.", id = 2, effect = CONST_ME_MAGIC_BLUE},
[11261] = {text = "The Fire of the Suns engulfs you.", id = 3, effect = CONST_ME_MAGIC_RED},
[11262] = {text = "The Wisdom of Solitude inspires you.", id = 4, effect = CONST_ME_MAGIC_GREEN},
[11258] = {text = "The Spark of the Phoenix emblazes you.", id = 5, effect = CONST_ME_FIREATTACK}
[11260] = {name = "Spiritual Shielding", text = "The Spiritual Shielding protects you.", id = BLESSING_SPIRITUAL_SHIELDING, effect = CONST_ME_LOSEENERGY},
[11259] = {name = "Embrace of the World", text = "The Embrace of the World surrounds you.", id = BLESSING_EMBRACE_OF_THE_WORLD, effect = CONST_ME_MAGIC_BLUE},
[11261] = {name = "Fire of the Suns", text = "The Fire of the Suns engulfs you.", id = BLESSING_FIRE_OF_THE_SUNS, effect = CONST_ME_MAGIC_RED},
[11262] = {name = "Wisdom of Solitude", text = "The Wisdom of Solitude inspires you.", id = BLESSING_WISDOM_OF_SOLITUDE, effect = CONST_ME_MAGIC_GREEN},
[11258] = {name = "Spark of the Phoenix", text = "The Spark of the Phoenix emblazes you.", id = BLESSING_SPARK_OF_THE_PHOENIX, effect = CONST_ME_FIREATTACK},
[28016] = {name = "Heart of the Mountain", text = "The Heart of the Mountain encourages you.", id = BLESSING_HEART_OF_THE_MOUNTAIN, effect = CONST_ME_STONESSINGLESPACE},
[28017] = {name = "Blood of the Mountain", text = "The Blood of the Mountain strenghtens you.", id = BLESSING_BLOOD_OF_THE_MOUNTAIN, effect = CONST_ME_DRAWBLOOD}
}

local blessingCharms = Action()
Expand All @@ -15,6 +17,7 @@ function blessingCharms.onUse(player, item, fromPosition, target, toPosition, is
player:say("You already possess this blessing.", TALKTYPE_MONSTER_SAY)
return true
end
player:addBlessingsHistory("\"" .. blessItem.name .. "\" gained from using a blessing charm.", 1)
player:addBlessing(blessItem.id)
player:say(blessItem.text, TALKTYPE_MONSTER_SAY)
player:getPosition():sendMagicEffect(blessItem.effect)
Expand Down
14 changes: 8 additions & 6 deletions data/scripts/actions/tools/check_bless.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
local blessings = {
"Spiritual Shielding",
"Embrace of the World",
"Fire of the Suns",
"Spark of the Phoenix",
"Wisdom of Solitude",
"Twist of Fate"
[BLESSING_EMBRACE_OF_THE_WORLD] = "Embrace of the World",
[BLESSING_FIRE_OF_THE_SUNS] = "Fire of the Suns",
[BLESSING_SPARK_OF_THE_PHOENIX] = "Spark of the Phoenix",
[BLESSING_WISDOM_OF_SOLITUDE] = "Wisdom of Solitude",
[BLESSING_SPIRITUAL_SHIELDING] = "Spiritual Shielding",
[BLESSING_HEART_OF_THE_MOUNTAIN] = "Heart of the Mountain",
[BLESSING_BLOOD_OF_THE_MOUNTAIN] = "Blood of the Mountain",
[BLESSING_TWIST_OF_FATE] = "Twist of Fate"
}

local checkBless = Action()
Expand Down
85 changes: 85 additions & 0 deletions data/scripts/network/blessings_window.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
local ec = EventCallback

ec.onUpdateStorage = function(player, key, value, oldValue, isLogin)
if isLogin then
return
end

local storageBlessing = 10003001
if key >= (storageBlessing + BLESSING_FIRST) and key <= (storageBlessing + BLESSING_LAST) then
player:sendBlessings()
end
end

ec:register()


local function sendBlessingsWindow(player)
local msg = NetworkMessage()
msg:addByte(0x9B)

msg:addByte(BLESSING_LAST + 1)
for blessingId = BLESSING_FIRST, BLESSING_LAST, 1 do
msg:addU16(2 ^ blessingId)
msg:addByte(player:getBlessing(blessingId))
msg:addByte(0) -- charges bought from store
end

local isPromoted = player:isPromoted()
msg:addByte(isPromoted and 0x01 or 0x00)
msg:addByte(30) -- fixed value

local percentReduction = 0
if isPromoted then
percentReduction = percentReduction + 30
end

local blessings = player:getBlessings(true)
percentReduction = percentReduction + (#blessings * 8)

msg:addByte(percentReduction) -- exp/skill min loss pvp death
msg:addByte(percentReduction + 9) -- exp/skill max loss pvp death
msg:addByte(percentReduction) -- exp/skill loss pve death

local equipmentLoss = {100, 70, 45, 25, 10, 0, 0, 0}
local equipmentPercent = equipmentLoss[#blessings + 1]

local isBlackOrRedSkull = (player:getSkull() == SKULL_RED or player:getSkull() == SKULL_BLACK)

if isBlackOrRedSkull then
equipmentPercent = 100
end

msg:addByte(equipmentPercent) -- loss container death pvp & 10% for pve (100pvp => 10pve)
msg:addByte(equipmentPercent) -- loss equipment death pvp & 10% for pve (100pvp => 10pve)

msg:addByte(isBlackOrRedSkull and 0x01 or 0x00)

local amulet = player:getSlotItem(CONST_SLOT_NECKLACE)
if amulet and amulet:getId() == ITEM_AMULETOFLOSS then
msg:addByte(0x01)
else
msg:addByte(0x00)
end

local history = player:getBlessingsHistory()
msg:addByte(#history)
for _, entry in ipairs(history) do
msg:addU32(entry.ts)
msg:addByte(entry.type)
msg:addString(entry.event)
end

msg:sendToPlayer(player)
msg:delete()

return true
end

local handler = PacketHandler(0xCF)

function handler.onReceive(player, msg)
sendBlessingsWindow(player)
end

handler:register()
4 changes: 3 additions & 1 deletion data/scripts/network/cyclopedia_character.lua
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ local function sendCombatStats(self, msg)
end

msg:addU16(0) -- damage reflection (flat, one value for all combat types)
msg:addByte(self:getBlessings())

local blessings = self:getBlessings(false)
msg:addByte(#blessings)
msg:addByte(8) -- blessings count

-- weapon
Expand Down
10 changes: 10 additions & 0 deletions schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ CREATE TABLE IF NOT EXISTS `account_viplist` (
FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

CREATE TABLE IF NOT EXISTS `blessings_history` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`player_id` int NOT NULL,
`type` tinyint NOT NULL DEFAULT '0',
`event` varchar(255) NOT NULL,
`inserted` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;

CREATE TABLE IF NOT EXISTS `guilds` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
Expand Down
3 changes: 3 additions & 0 deletions src/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@ static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500;
static constexpr int32_t PSTRG_MOUNTS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2001);
static constexpr int32_t PSTRG_MOUNTS_RANGE_SIZE = 10;
static constexpr int32_t PSTRG_MOUNTS_CURRENTMOUNT = (PSTRG_MOUNTS_RANGE_START + 10);
//[3001 - 3011];
static constexpr int32_t PSTRG_BLESSINGS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 3001);
static constexpr int32_t PSTRG_BLESSINGS_RANGE_SIZE = 10;

#define IS_IN_KEYRANGE(key, range) \
(key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE))
Expand Down
Loading