landsandboat/scripts/globals/porter_moogle.lua
2023-08-24 18:10:39 -04:00

317 lines
9.7 KiB
Lua

-----------------------------------
-- Porter Moogle Utilities
-- desc: Common functionality for Porter Moogles.
-----------------------------------
local slipItems = require('scripts/globals/porter_slip_items')
-----------------------------------
xi = xi or {}
xi.porter_moogle = xi.porter_moogle or {}
-- Item IDs for all of the slips.
local slipIds =
{
xi.item.MOOGLE_STORAGE_SLIP_01,
xi.item.MOOGLE_STORAGE_SLIP_02,
xi.item.MOOGLE_STORAGE_SLIP_03,
xi.item.MOOGLE_STORAGE_SLIP_04,
xi.item.MOOGLE_STORAGE_SLIP_05,
xi.item.MOOGLE_STORAGE_SLIP_06,
xi.item.MOOGLE_STORAGE_SLIP_07,
xi.item.MOOGLE_STORAGE_SLIP_08,
xi.item.MOOGLE_STORAGE_SLIP_09,
xi.item.MOOGLE_STORAGE_SLIP_10,
xi.item.MOOGLE_STORAGE_SLIP_11,
xi.item.MOOGLE_STORAGE_SLIP_12,
xi.item.MOOGLE_STORAGE_SLIP_13,
xi.item.MOOGLE_STORAGE_SLIP_14,
xi.item.MOOGLE_STORAGE_SLIP_15,
xi.item.MOOGLE_STORAGE_SLIP_16,
xi.item.MOOGLE_STORAGE_SLIP_17,
xi.item.MOOGLE_STORAGE_SLIP_18,
xi.item.MOOGLE_STORAGE_SLIP_19,
xi.item.MOOGLE_STORAGE_SLIP_20,
xi.item.MOOGLE_STORAGE_SLIP_21,
xi.item.MOOGLE_STORAGE_SLIP_22,
xi.item.MOOGLE_STORAGE_SLIP_23,
xi.item.MOOGLE_STORAGE_SLIP_24,
xi.item.MOOGLE_STORAGE_SLIP_25,
xi.item.MOOGLE_STORAGE_SLIP_26,
xi.item.MOOGLE_STORAGE_SLIP_27,
xi.item.MOOGLE_STORAGE_SLIP_28,
}
-----------------------------------
-- desc : Checks if the supplied item is a Moogle Storage Slip.
-----------------------------------
local function isSlip(itemId)
return slipItems[itemId] ~= nil
end
-----------------------------------
-- desc : Checks if the supplied slip can store the supplied item.
-----------------------------------
local function isStorableOn(slipId, itemId)
for _, id in ipairs(slipItems[slipId]) do
if id == itemId then
return true
end
end
return false
end
-----------------------------------
-- desc : Gets IDs of retrievable items from the extra data on a slip.
-----------------------------------
local function getItemsOnSlip(extra, slipId)
local slip = slipItems[slipId]
local itemsOnSlip = {}
local x = 1
for k, v in ipairs(slip) do
local byte = extra[math.floor((k - 1) / 8) + 1]
if byte < 0 then
byte = byte + 256
end
if bit.band(bit.rshift(byte, (k - 1) % 8), 1) ~= 0 then
itemsOnSlip[x] = v
x = x + 1
end
end
return itemsOnSlip
end
-----------------------------------
-- desc : Finds the key in table t where the value equals i.
-----------------------------------
local function find(t, i)
for k, v in ipairs(t) do
if v == i then
return k
end
end
return nil
end
-----------------------------------
-- desc : Converts the 8 bit extra data into 32 bit params for events.
-----------------------------------
local function int8ToInt32(extra)
local params = {}
local int32 = ''
for k, v in ipairs(extra) do
int32 = string.format('%02x%s', v, int32)
if k % 4 == 0 then
params[#params + 1] = tonumber(int32, 16)
int32 = ''
end
end
if int32 ~= '' then
params[#params + 1] = tonumber(int32, 16)
end
return params
end
-----------------------------------
-- desc : Gets Storage Slip ID from the trade window (does nothing
-- if there are two or more Storage Slips in the trade and no
-- storable items.
-----------------------------------
local function getSlipId(trade)
local slipId = 0
local slips = 0
for _, itemId in ipairs(slipIds) do
if trade:hasItemQty(itemId, 1) then
slips = slips + 1
if slipId == 0 then
slipId = itemId
end
end
end
if slips == trade:getItemCount() and slips > 1 then
slipId = 0
end
return slipId, slips
end
-----------------------------------
-- desc : Gets all items in the trade window that are storable on the
-- slip in the trade window.
-----------------------------------
local function getStorableItems(player, trade, slipId)
local storableItemIds = { }
for i = 0, 7 do
local slotItemId = trade:getItemId(i)
if
slotItemId ~= 0 and
not isSlip(slotItemId) and
player:hasItem(slotItemId)
then
if isStorableOn(slipId, slotItemId) then
storableItemIds[#storableItemIds + 1] = slotItemId
end
end
end
return storableItemIds
end
-----------------------------------
-- desc : Stores the items on the Storage Slip extra data and starts
-- the event indicating that the storage was successful.
-----------------------------------
local function storeItems(player, storableItemIds, slipId, eventTable)
if #storableItemIds > 0 then
local param0 = #storableItemIds
local param1 = 1
if #storableItemIds == 1 then
param0 = storableItemIds[1]
param1 = 0
end
local extra = { }
for i = 0, 23 do
extra[i] = 0
end
for k, v in ipairs(slipItems[slipId]) do
if find(storableItemIds, v) ~= nil then
local bitmask = extra[math.floor((k - 1) / 8)]
if bitmask < 0 then
bitmask = bitmask + 256
end
extra[math.floor((k - 1) / 8)] = bit.bor(bitmask, bit.lshift(1, (k - 1) % 8))
end
end
local result = player:storeWithPorterMoogle(slipId, extra, storableItemIds)
if result == 0 then
player:startEvent(eventTable.STORE_EVENT_ID, param0, param1)
elseif result == 1 then
player:startEvent(eventTable.ALREADY_STORED_ID)
elseif result == 2 then
player:startEvent(eventTable.MAGIAN_TRIAL_ID)
end
end
end
-----------------------------------
-- desc : Returns a zero-based identifier for the slip (Storage Slip 1
-- is index 0, Storage Slip 2 is index 1, etc).
-----------------------------------
local function getSlipIndex(slipId)
return find(slipIds, slipId) - 1
end
-----------------------------------
-- desc : Gets the extra data from the traded slip and starts the
-- retrieval event.
-----------------------------------
local function startRetrieveProcess(player, eventId, slipId)
local extra = player:getRetrievableItemsForSlip(slipId)
local params = int8ToInt32(extra)
local slipIndex = getSlipIndex(slipId)
player:setLocalVar('slipId', slipId)
player:startEvent(eventId, params[1], params[2], params[3], params[4], params[5], params[6], nil, slipIndex)
end
-----------------------------------
-- desc : Begins the storage or retrieval process based on the items
-- supplied in the trade.
-----------------------------------
xi.porter_moogle.onTrade = function(player, trade, eventTable)
local slipId, slipCount = getSlipId(trade)
if slipId == 0 or slipCount > 1 then
return
end
local storableItemIds = getStorableItems(player, trade, slipId)
if #storableItemIds > 0 then
storeItems(player, storableItemIds, slipId, eventTable)
else
startRetrieveProcess(player, eventTable.RETRIEVE_EVENT_ID, slipId)
end
end
-----------------------------------
-- desc : Retrieves the selected item from storage, removes it from
-- the slip's extra data, displays a message to the user, and
-- updates the user's event data.
-----------------------------------
xi.porter_moogle.onEventUpdate = function(player, csid, option, retrieveEventId)
local slipId = player:getLocalVar('slipId')
if csid == retrieveEventId and slipId ~= 0 and slipId ~= nil then
local extra = player:getRetrievableItemsForSlip(slipId)
local itemsOnSlip = getItemsOnSlip(extra, slipId)
local retrievedItemId = itemsOnSlip[option + 1]
if player:hasItem(retrievedItemId) or player:getFreeSlotsCount() == 0 then
player:messageSpecial(zones[player:getZoneID()].text.ITEM_CANNOT_BE_OBTAINED, retrievedItemId)
else
local k = find(slipItems[slipId], retrievedItemId)
local extraId = math.floor((k - 1) / 8)
local bitmask = extra[extraId + 1]
if bitmask < 0 then
bitmask = bitmask + 256
end
bitmask = bit.band(bitmask, bit.bnot(bit.lshift(1, (k - 1) % 8)))
extra[extraId + 1] = bitmask
player:retrieveItemFromSlip(slipId, retrievedItemId, extraId, bitmask)
player:messageSpecial(zones[player:getZoneID()].text.RETRIEVE_DIALOG_ID, retrievedItemId, nil, nil, retrievedItemId, false)
end
local params = int8ToInt32(extra)
player:updateEvent(params[1], params[2], params[3], params[4], params[5], params[6], slipId)
end
end
-----------------------------------
-- desc : Completes the event.
-----------------------------------
xi.porter_moogle.onEventFinish = function(player, csid, option, talkEventId)
if csid == talkEventId and option < 1000 then
option = math.floor(option / 16) + (option % 16)
local hasItem = player:hasItem(slipIds[option])
if hasItem or player:getFreeSlotsCount() == 0 then
player:messageSpecial(zones[player:getZoneID()].text.ITEM_CANNOT_BE_OBTAINED, slipIds[option])
return
end
if player:delGil(1000) then
player:addItem(slipIds[option])
player:messageSpecial(zones[player:getZoneID()].text.ITEM_OBTAINED, slipIds[option])
else
player:messageSpecial(zones[player:getZoneID()].text.NOT_HAVE_ENOUGH_GIL, slipIds[option])
return
end
else
player:setLocalVar('slipId', 0)
end
end