Skip to content

Commit

Permalink
Quest system moved to Lua (#4388)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Zbizu <Zbizu@users.noreply.github.com>
  • Loading branch information
MillhioreBT and Zbizu committed Apr 19, 2023
1 parent 74c9f8f commit d50730a
Show file tree
Hide file tree
Showing 26 changed files with 485 additions and 258 deletions.
14 changes: 0 additions & 14 deletions data/XML/quests.xml

This file was deleted.

1 change: 1 addition & 0 deletions data/events/events.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<event class="Player" method="onWrapItem" enabled="1" />
<event class="Player" method="onInventoryUpdate" enabled="1" />
<event class="Player" method="onNetworkMessage" enabled="1" />
<event class="Player" method="onUpdateStorage" enabled="1" />

<!-- Monster methods -->
<event class="Monster" method="onDropLoot" enabled="1" />
Expand Down
7 changes: 7 additions & 0 deletions data/events/scripts/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,10 @@ function Player:onNetworkMessage(recvByte, msg)

handler(self, msg)
end

function Player:onUpdateStorage(key, value, oldValue, isLogin)
local onUpdateStorage = EventCallback.onUpdateStorage
if onUpdateStorage then
onUpdateStorage(self, key, value, oldValue, isLogin)
end
end
1 change: 1 addition & 0 deletions data/lib/core/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ dofile('data/lib/core/position.lua')
dofile('data/lib/core/teleport.lua')
dofile('data/lib/core/tile.lua')
dofile('data/lib/core/vocation.lua')
dofile('data/lib/core/quests.lua')
66 changes: 66 additions & 0 deletions data/lib/core/game.lua
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,69 @@ end
function Game.setStorageValue(key, value)
globalStorageTable[key] = value
end

do
local quests = {}
local missions = {}
local trackedQuests = {}

function Game.getQuests() return quests end
function Game.getMissions() return missions end
function Game.getTrackedQuests() return trackedQuests end

function Game.getQuestById(id) return quests[id] end
function Game.getMissionById(id) return missions[id] end

function Game.clearQuests()
quests = {}
missions = {}
for playerId, _ in pairs(trackedQuests) do
local player = Player(playerId)
if player then
player:sendQuestTracker({})
end
end
trackedQuests = {}
return true
end

function Game.createQuest(name, quest)
if not isScriptsInterface() then
return
end

if type(quest) == "table" then
setmetatable(quest, Quest)
quest.id = -1
quest.name = name
if type(quest.missions) ~= "table" then
quest.missions = {}
end

return quest
end

quest = setmetatable({}, Quest)
quest.id = -1
quest.name = name
quest.storageId = 0
quest.storageValue = 0
quest.missions = {}
return quest
end

function Game.isQuestStorage(key, value, oldValue)
for _, quest in pairs(quests) do
if quest.storageId == key and quest.storageValue == value then
return true
end
end

for _, mission in pairs(missions) do
if mission.storageId == key and value >= mission.startValue and value <= mission.endValue then
return not mission.description or oldValue < mission.startValue or oldValue > mission.endValue
end
end
return false
end
end
105 changes: 105 additions & 0 deletions data/lib/core/player.lua
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,111 @@ function Player.isPromoted(self)
return vocation:getId() ~= fromVocId
end

function Player.getMaxTrackedQuests(self)
return configManager.getNumber(self:isPremium() and configKeys.QUEST_TRACKER_PREMIUM_LIMIT or configKeys.QUEST_TRACKER_FREE_LIMIT)
end

function Player.getQuests(self)
local quests = {}
for _, quest in pairs(Game.getQuests()) do
if quest:isStarted(self) then
quests[#quests + 1] = quest
end
end
return quests
end

function Player.sendQuestLog(self)
local msg = NetworkMessage()
msg:addByte(0xF0)
local quests = self:getQuests()
msg:addU16(#quests)

for _, quest in pairs(quests) do
msg:addU16(quest.id)
msg:addString(quest.name)
msg:addByte(quest:isCompleted(self))
end

msg:sendToPlayer(self)
msg:delete()
return true
end

function Player.sendQuestLine(self, quest)
local msg = NetworkMessage()
msg:addByte(0xF1)
msg:addU16(quest.id)
local missions = quest:getMissions(self)
msg:addByte(#missions)

for _, mission in pairs(missions) do
msg:addU16(mission.id)
msg:addString(mission:getName(self))
msg:addString(mission:getDescription(self))
end

msg:sendToPlayer(self)
msg:delete()
return true
end

function Player.getTrackedQuests(self, missionsId)
local playerId = self:getId()
local maxTrackedQuests = self:getMaxTrackedQuests()
local trackedQuests = {}
Game.getTrackedQuests()[playerId] = trackedQuests
local quests = Game.getQuests()
local missions = Game.getMissions()
local trackeds = 0
for _, missionId in pairs(missionsId) do
local mission = missions[missionId]
if mission and mission:isStarted(self) then
trackedQuests[mission] = quests[mission.questId]
trackeds = trackeds + 1
if trackeds >= maxTrackedQuests then
break
end
end
end
return trackedQuests, trackeds
end

function Player.sendQuestTracker(self, missionsId)
local msg = NetworkMessage()
msg:addByte(0xD0)
msg:addByte(1)

local trackedQuests, trackeds = self:getTrackedQuests(missionsId)
msg:addByte(self:getMaxTrackedQuests() - trackeds)
msg:addByte(trackeds)

for mission, quest in pairs(trackedQuests or {}) do
msg:addU16(mission.id)
msg:addString(quest.name)
msg:addString(mission:getName(self))
msg:addString(mission:getDescription(self))
end

msg:sendToPlayer(self)
msg:delete()
return true
end

function Player.sendUpdateQuestTracker(self, mission)
local msg = NetworkMessage()
msg:addByte(0xD0)
msg:addByte(0)

msg:addU16(mission.id)
msg:addString(mission:getName(self))
msg:addString(mission:getDescription(self))

msg:sendToPlayer(self)
msg:delete()
return true
end

function Player.getBestiaryKills(self, raceId)
return math.max(0, self:getStorageValue(PlayerStorageKeys.bestiaryKillsBase + raceId))
end
Expand Down
116 changes: 116 additions & 0 deletions data/lib/core/quests.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
Quest = {}
Quest.__index = Quest

function Quest:register()
local quests = Game.getQuests()
local missions = Game.getMissions()

self.id = #quests + 1

for _, mission in pairs(self.missions) do
mission.id = #missions + 1
mission.questId = self.id
missions[mission.id] = setmetatable(mission, Mission)
end

quests[self.id] = self
return true
end

function Quest:isStarted(player)
return player:getStorageValue(self.storageId) >= self.storageValue
end

function Quest:isCompleted(player)
for _, mission in pairs(self.missions) do
if not mission:isCompleted(player) then
return false
end
end
return true
end

function Quest:getMissions(player)
local missions = {}
for _, mission in pairs(self.missions) do
if mission:isStarted(player) then
missions[#missions + 1] = mission
end
end
return missions
end

function Quest:isTracking(key, value)
if self.storageId == key and value == self.storageValue then
return true
end

for _, mission in pairs(self.missions) do
if mission.storageId == key and value >= mission.startValue and value <= mission.endValue then
return true
end
end
return false
end

Mission = {}
Mission.__index = Mission

function Mission:isStarted(player)
local value = player:getStorageValue(self.storageId)
if value < self.startValue then
return false
end

if not self.ignoreEndValue and value > self.endValue then
return false
end

return true
end

function Mission:isCompleted(player)
if self.ignoreEndValue then
return player:getStorageValue(self.storageId) >= self.endValue
end
return player:getStorageValue(self.storageId) == self.endValue
end

function Mission:getName(player)
if self:isCompleted(player) then
return string.format("%s (Completed)", self.name)
end
return self.name
end

function Mission:getDescription(player)
local descriptionType = type(self.description)
if descriptionType == "function" then
return self.description(player)
end

local value = player:getStorageValue(self.storageId)
if descriptionType == "string" then
local description = self.description:gsub("|STATE|", value)
description = self.description:gsub("\\n", "\n")
return description
end

if descriptionType == "table" then
if self.ignoreEndValue then
for current = self.endValue, self.startValue, -1 do
if value >= current then
return self.description[current]
end
end
else
for current = self.endValue, self.startValue, -1 do
if value == current then
return self.description[current]
end
end
end
end

return "An error has occurred, please contact a gamemaster."
end
8 changes: 8 additions & 0 deletions data/scripts/creaturescripts/clean_tracked_quests.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local cleanTrackedQuests = CreatureEvent("cleanTrackedQuests")

function cleanTrackedQuests.onLogout(player)
Game.getTrackedQuests()[player:getId()] = nil
return true
end

cleanTrackedQuests:register()
29 changes: 29 additions & 0 deletions data/scripts/eventcallbacks/player/default_onUpdateStorage.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
local lastQuestUpdate = {}

local ec = EventCallback

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

local playerId = player:getId()
local now = os.mtime()
if not lastQuestUpdate[playerId] then
lastQuestUpdate[playerId] = now
end

if lastQuestUpdate[playerId] - now <= 0 and Game.isQuestStorage(key, value, oldValue) then
lastQuestUpdate[playerId] = os.mtime() + 100
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your questlog has been updated.")
end

local trackedQuests = Game.getTrackedQuests()[playerId] or {}
for mission, quest in pairs(trackedQuests) do
if quest:isTracking(key, value) then
player:sendUpdateQuestTracker(mission)
end
end
end

ec:register()
1 change: 1 addition & 0 deletions data/scripts/lib/event_callbacks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ ec.onLoseExperience = {[2] = 1}
ec.onGainSkillTries = {[3] = 1}
ec.onWrapItem = {}
ec.onInventoryUpdate = {}
ec.onUpdateStorage = {}
-- Monster
ec.onDropLoot = {}
ec.onSpawn = {}
Expand Down
Loading

0 comments on commit d50730a

Please sign in to comment.