landsandboat/scripts/globals/roe.lua
2026-05-17 20:58:21 -04:00

322 lines
13 KiB
Lua

-----------------------------------
-- Records of Eminence
-----------------------------------
require('scripts/globals/npc_util')
require('scripts/globals/quests')
require('scripts/globals/roe_records')
-----------------------------------
xi = xi or {}
xi.roe = xi.roe or {}
-----------------------------------
-- Checks
-----------------------------------
local checks =
{
mobID = function(self, player, params) -- Mob ID check
return (params.mob and self.reqs.mobID[params.mob:getID()]) and true or false
end,
mobName = function(self, player, params)
return (params.mob and self.reqs.mobName[params.mob:getName()]) and true or false
end,
mobXP = function(self, player, params) -- Mob yields xp
return (params.mob and player:checkKillCredit(params.mob)) and true or false
end,
mobSpecies = function(self, player, params) -- Mob species in set
return (params.mob and self.reqs.mobSpecies[params.mob:getSpecies()]) and true or false
end,
mobFamily = function(self, player, params) -- Mob super family in set
return (params.mob and self.reqs.mobFamily[params.mob:getFamily()]) and true or false
end,
mobSystem = function(self, player, params) -- Mob system in set
return (params.mob and self.reqs.mobSystem[params.mob:getEcosystem()]) and true or false
end,
dmgMin = function(self, player, params) -- Minimum Dmg Dealt/Taken
return (params.dmg and params.dmg >= self.reqs.dmgMin) and true or false
end,
dmgMax = function(self, player, params) -- Maximum Dmg Dealt/Taken
return (params.dmg and params.dmg <= self.reqs.dmgMax) and true or false
end,
atkType = function(self, player, params) -- Dmg Type is
return (params.atkType == self.reqs.atkType) and true or false
end,
healMin = function(self, player, params) -- Minimum Amount healed
return (params.heal and params.heal >= self.reqs.healMin) and true or false
end,
zone = function(self, player, params) -- Player in Zone
return (self.reqs.zone[player:getZoneID()]) and true or false
end,
zoneNot = function(self, player, params) -- Player not in Zone
return (not self.reqs.zoneNot[player:getZoneID()]) and true or false
end,
itemID = function(self, player, params) -- itemid in set
return (params.itemid and self.reqs.itemID[params.itemid]) and true or false
end,
levelSync = function(self, player, params) -- Player is Level Sync'd
return self.reqs.levelSync and player:hasStatusEffect(xi.effect.LEVEL_SYNC) and true or false
end,
jobLvl = function(self, player, params) -- Player has job at minimum level X
return player:getJobLevel(self.reqs.jobLvl[1]) >= self.reqs.jobLvl[2] and true or false
end,
questComplete = function(self, player, params) -- Player has { KINGDOM, QUEST } marked complete
return player:getQuestStatus(self.reqs.questComplete[1], self.reqs.questComplete[2]) == xi.questStatus.QUEST_COMPLETED
end,
missionComplete = function(self, player, params) -- Player has { NATION, MISSION } marked complete
return player:hasCompletedMission(self.reqs.missionComplete[1], self.reqs.missionComplete[2])
end,
unityLeader = function(self, player, params) -- Player is a member of the specified Unity (1..11)
return player:getUnityLeader() == self.reqs.unityLeader
end,
skillType = function(self, player, params) -- Generic numeric check, used for synthSuccess and helmSuccess
return params.skillType == self.reqs.skillType and true or false
end,
}
-- Main general check function for all-purpose use.
-- Some functions may specify custom handlers (ie. gain exp or deal dmg.)
local masterCheck = function(self, player, params)
for func in pairs(self.reqs) do
if not checks[func] or not checks[func](self, player, params) then
return false
end
end
return true
end
-----------------------------------
-- records
-----------------------------------
-- Schedule for Timed Records.
local timedSchedule =
{
-- 4-hour timeslots (6 per day) all days/times are in JST
-- 00-04 04-08 08-12 12-16 16-20 20-00
{ 4021, 4010, 4016, 4012, 4018, 4013 }, -- Sunday
{ 4015, 4011, 4017, 4014, 4019, 4008 }, -- Monday
{ 4016, 4012, 4018, 4013, 4020, 4009 }, -- Tuesday
{ 4017, 4014, 4019, 4008, 4021, 4010 }, -- Wednesday
{ 4018, 4013, 4020, 4009, 4015, 4011 }, -- Thursdsay
{ 4019, 4008, 4021, 4010, 4016, 4012 }, -- Friday
{ 4020, 4009, 4015, 4011, 4017, 4014 }, -- Saturday
}
local defaults =
{
check = masterCheck, -- Check function should return true/false
increment = 1, -- Amount to increment per successful check
notify = 1, -- Progress notifications shown every X increases
goal = 1, -- Progress goal
flags = {}, -- Special flags. This should be a set. Possible values:
-- 'timed' - 4-hour record.
-- 'repeat' - Repeatable record.
-- 'daily' - Daily record.
-- 'weekly' - Weekly record.
-- 'unity' - Weekly reset, but doesn't reset progress, only completion.
-- 'retro' - Can be claimed retroactively. Calls check on taking record.
-- 'hidden' - Special internal-use record used only as a client-flag to unlock others.
-- Does not count towards completed record count.
reqs = {}, -- Other requirements. List of function names from above, with required values.
reward = {}, -- Reward parameters give on completion. (See completeRecord directly below.)
}
xi.roe.initialize = function()
-- Apply defaults for records. Since this table may already exist in the global state,
-- check for missing entries first.
for recordId, _ in pairs(xi.roe.records) do
for defaultKey, defaultValue in pairs(defaults) do
if not xi.roe.records[recordId][defaultKey] then
xi.roe.records[recordId][defaultKey] = defaultValue
end
end
end
-- Build global map of implemented records.
-- This is used to deny taking records which aren't implemented in the above table.
RoeParseRecords(xi.roe.records)
-- Load timetable for timed records
if xi.settings.main.ENABLE_ROE and xi.settings.main.ENABLE_ROE_TIMED > 0 then
RoeParseTimed(timedSchedule)
end
end
xi.roe.initialize()
local function isRepeatItemRewardException(items)
local itemExceptionsMap =
{
[xi.item.SILT_POUCH] = true, -- Escha Bead/Silt rewards are always rewarded
[xi.item.BEAD_POUCH] = true, -- Escha Bead/Silt rewards are always rewarded
}
-- clone of npcUtil.giveItem logic
if type(items) == 'table' then
for _, v in pairs(items) do
if type(v) == 'number' then
if itemExceptionsMap[v] == nil then
return false -- if any item in the rewards list doesn't match, bail out
end
end
end
elseif type(items) == 'number' then
return itemExceptionsMap[items] ~= nil -- If the input is only an integer, then just check the map
end
return true
end
--[[ --------------------------------------------------------------------------
Complete a record of eminence. This is for internal roe use only.
For external calls use onRecordTrigger below. (see healing.lua for example)
If record rewards items, and the player cannot carry them, return false.
Otherwise, return true.
Example of usage + reward table (all params are optional):
completeRecord(player, record#)
reward =
{
item = { { 640, 2 }, 641 }, -- see npcUtil.giveItem for formats (Only given on first completion)
keyItem = xi.ki.ZERUHN_REPORT, -- see npcUtil.giveKeyItem for formats
sparks = 500,
xp = 1000,
accolades = 300,
capacity = 400,
})
--------------------------------------------------------------------------- --]]
local function completeRecord(player, record)
local recordEntry = xi.roe.records[record]
local recordFlags = recordEntry.flags
local rewards = recordEntry.reward
local canRewardItem = rewards['item'] and (not player:getEminenceCompleted(record) or isRepeatItemRewardException(rewards['item']))
if canRewardItem then
if not npcUtil.giveItem(player, rewards['item'], { silent = true }) then
player:messageBasic(xi.msg.basic.ROE_UNABLE_BONUS_ITEM)
return false
end
end
player:messageBasic(xi.msg.basic.ROE_COMPLETE, record)
if rewards['sparks'] ~= nil and type(rewards['sparks']) == 'number' then
local bonus = 1
if player:getEminenceCompleted(record) then
player:addCurrency('spark_of_eminence', rewards['sparks'] * bonus * xi.settings.main.SPARKS_RATE, xi.settings.main.CAP_CURRENCY_SPARKS)
player:messageBasic(xi.msg.basic.ROE_RECEIVE_SPARKS, rewards['sparks'] * xi.settings.main.SPARKS_RATE, player:getCurrency('spark_of_eminence'))
else
bonus = 3
player:addCurrency('spark_of_eminence', rewards['sparks'] * bonus * xi.settings.main.SPARKS_RATE, xi.settings.main.CAP_CURRENCY_SPARKS)
player:messageBasic(xi.msg.basic.ROE_FIRST_TIME_SPARKS, rewards['sparks'] * bonus * xi.settings.main.SPARKS_RATE, player:getCurrency('spark_of_eminence'))
end
end
if rewards['exp'] ~= nil and type(rewards['exp']) == 'number' then
player:addExp(rewards['exp'] * xi.settings.main.ROE_EXP_RATE)
end
if rewards['capacity'] ~= nil and type(rewards['capacity']) == 'number' then
player:addCapacityPoints(rewards['capacity'])
end
-- NOTE: To preserve retail order, messaging is here, but item is given if able at the beginning of this
-- function, since if it fails, it will need to bail out.
if canRewardItem then
local itemQty = type(rewards['item'][1]) == 'table' and rewards['item'][1][2] or 1
local itemId = type(rewards['item'][1]) == 'table' and rewards['item'][1][1] or rewards['item'][1]
local messageId = itemQty > 1 and xi.msg.basic.ROE_BONUS_ITEM_PLURAL or xi.msg.basic.ROE_BONUS_ITEM
player:messageBasic(messageId, itemId, itemQty)
end
if recordFlags['repeat'] then
if recordFlags['timed'] then
player:messageBasic(xi.msg.basic.ROE_TIMED_CLEAR)
else
player:messageBasic(xi.msg.basic.ROE_REPEAT_OR_CANCEL)
end
player:setEminenceCompleted(record, true)
else
player:setEminenceCompleted(record)
end
if
player:getUnityLeader() > 0 and
rewards['accolades'] ~= nil and
type(rewards['accolades']) == 'number'
then
local bonusAccoladeRate = 1.0
if record ~= 5 then -- Do not grant a bonus for All for One
bonusAccoladeRate = bonusAccoladeRate + ((player:getUnityRank() - 1) * 0.05)
end
local accoladePayout = math.floor(rewards['accolades'] * bonusAccoladeRate)
player:addCurrency('unity_accolades', accoladePayout, xi.settings.main.CAP_CURRENCY_ACCOLADES)
player:messageBasic(xi.msg.basic.ROE_RECEIVED_ACCOLADES, accoladePayout, player:getCurrency('unity_accolades'))
end
if rewards['keyItem'] ~= nil then
npcUtil.giveKeyItem(player, rewards['keyItem'])
end
-- Workaround for Hidden Record #4085 (10 RoE Objectives Completed)
if
not player:getEminenceCompleted(4085) and
player:getNumEminenceCompleted() >= 10
then
player:setEminenceCompleted(4085)
end
return true
end
-- *** onRecordTrigger is the primary entry point for all record calls. ***
-- Even records which are completed through Lua scripts should point here and
-- have record information entered in records. This keeps everything neat.
function xi.roe.onRecordTrigger(player, recordID, params)
params = params or {}
params.progress = params.progress or player:getEminenceProgress(recordID)
local entry = xi.roe.records[recordID]
local isClaiming = params.claim
if entry and params.progress then
local awaitingClaim = params.progress >= entry.goal
if awaitingClaim and not isClaiming then
player:messageBasic(xi.msg.basic.ROE_YET_TO_RECEIVE)
return
elseif isClaiming or entry:check(player, params) then
params.progress = params.progress + (isClaiming and 0 or entry.increment)
if params.progress >= entry.goal then
if not completeRecord(player, recordID) and not awaitingClaim then
player:setEminenceProgress(recordID, entry.goal)
end
else
player:setEminenceProgress(recordID, params.progress, entry.goal)
end
end
end
end