480 lines
20 KiB
Lua
480 lines
20 KiB
Lua
-----------------------------------
|
|
-- Waypoint Teleporters
|
|
-- https://www.bg-wiki.com/ffxi/Waypoint
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
xi.waypoint = xi.waypoint or {}
|
|
|
|
local waypointStartIndex =
|
|
{
|
|
[xi.zone.WESTERN_ADOULIN ] = 0,
|
|
[xi.zone.EASTERN_ADOULIN ] = 20,
|
|
[xi.zone.YAHSE_HUNTING_GROUNDS] = 30,
|
|
[xi.zone.CEIZAK_BATTLEGROUNDS ] = 40,
|
|
[xi.zone.FORET_DE_HENNETIEL ] = 50,
|
|
[xi.zone.MORIMAR_BASALT_FIELDS] = 60,
|
|
[xi.zone.YORCIA_WEALD ] = 70,
|
|
[xi.zone.MARJAMI_RAVINE ] = 80,
|
|
[xi.zone.KAMIHR_DRIFTS ] = 90,
|
|
[xi.zone.LOWER_JEUNO ] = 100,
|
|
}
|
|
|
|
-- Table Format: Index = { Offset, GroupID, EventID, { Teleport Position }, unlockTablePos }
|
|
-- Note: Group ID is unused at this time, but can be observed in the eventUpdate option. This remains
|
|
-- in case it is needed for future implementations. unlockTablePos is the location stored in char_unlocks
|
|
-- which is utilized for displaying the appropriate map marker.
|
|
local waypointInfo =
|
|
{
|
|
-- Western Adoulin
|
|
[1] = { 0, 1, 5000, { 4.896, 0, -4.789, 33, xi.zone.WESTERN_ADOULIN }, 0 }, -- Platea Triumphus
|
|
[2] = { 1, 1, 5001, { -110.5, 3.85, -13.482, 191, xi.zone.WESTERN_ADOULIN }, 1 }, -- Pioneer's Coalition
|
|
[3] = { 2, 1, 5002, { -20.982, -0.15, -79.891, 127, xi.zone.WESTERN_ADOULIN }, 2 }, -- Mummer's Coalition
|
|
[4] = { 3, 1, 5003, { 91.451, -0.15, -49.013, 0, xi.zone.WESTERN_ADOULIN }, 3 }, -- Inventor's Coalition
|
|
[5] = { 4, 1, 5004, { -68.099, 4, -73.672, 28, xi.zone.WESTERN_ADOULIN }, 4 }, -- Auction House
|
|
[6] = { 5, 1, 5005, { 5.731, 0, -123.043, 127, xi.zone.WESTERN_ADOULIN }, 5 }, -- Rent-a-Room
|
|
[7] = { 6, 1, 5006, { 174.783, 3.85, -35.788, 63, xi.zone.WESTERN_ADOULIN }, 6 }, -- Big Bridge
|
|
[8] = { 7, 1, 5007, { 14.586, 0, 162.608, 191, xi.zone.WESTERN_ADOULIN }, 7 }, -- Airship Docks
|
|
[9] = { 8, 1, 5008, { 51.094, 32, 126.299, 191, xi.zone.WESTERN_ADOULIN }, 8 }, -- Adoulin Waterfront
|
|
|
|
-- Eastern Adoulin
|
|
[21] = { 15, 2, 5000, { -101.274, -0.15, -10.726, 191, xi.zone.EASTERN_ADOULIN }, 9 }, -- Peacekeeper's Coalition
|
|
[22] = { 16, 2, 5001, { -77.944, -0.15, -63.926, 0, xi.zone.EASTERN_ADOULIN }, 10 }, -- Scout's Coalition
|
|
[23] = { 17, 2, 5002, { -46.838, -0.075, -12.767, 63, xi.zone.EASTERN_ADOULIN }, 11 }, -- Statue of the Goddess
|
|
[24] = { 18, 2, 5003, { -57.773, -0.15, 85.237, 127, xi.zone.EASTERN_ADOULIN }, 12 }, -- Yahse Wharf
|
|
[25] = { 19, 2, 5004, { -61.865, -0.15, -120.81, 127, xi.zone.EASTERN_ADOULIN }, 13 }, -- Rent-a-Room
|
|
[26] = { 20, 2, 5005, { -42.065, -0.15, -89.979, 191, xi.zone.EASTERN_ADOULIN }, 14 }, -- Auction House
|
|
[27] = { 21, 2, 5006, { 11.681, -22.15, 29.976, 127, xi.zone.EASTERN_ADOULIN }, 15 }, -- Sverdhried Hillock
|
|
[28] = { 22, 2, 5007, { 27.124, -40.15, -60.844, 127, xi.zone.EASTERN_ADOULIN }, 16 }, -- Coronal Esplanade
|
|
[29] = { 23, 2, 5008, { 95.994, -40.15, -74.541, 0, xi.zone.EASTERN_ADOULIN }, 17 }, -- Castle Gates
|
|
|
|
-- Yahse Hunting Grounds
|
|
[31] = { 38, 4, 5000, { 321, 0, -199.8, 127, xi.zone.YAHSE_HUNTING_GROUNDS }, 22 }, -- Frontier Station
|
|
[32] = { 39, 4, 5001, { 86.5, 0, 1.5, 0, xi.zone.YAHSE_HUNTING_GROUNDS }, 23 }, -- Bivouac #1
|
|
[33] = { 40, 4, 5002, { -286.5, 0, 43.5, 127, xi.zone.YAHSE_HUNTING_GROUNDS }, 24 }, -- Bivouac #2
|
|
[34] = { 41, 4, 5003, { -162.4, 0, -272.8, 191, xi.zone.YAHSE_HUNTING_GROUNDS }, 25 }, -- Bivouac #3
|
|
|
|
-- Ceizak Battlegrounds
|
|
[41] = { 32, 3, 5000, { 365, 0.448, 190, 128, xi.zone.CEIZAK_BATTLEGROUNDS }, 18 }, -- Frontier Station
|
|
[42] = { 33, 3, 5001, { -6.879, 0, -117.511, 63, xi.zone.CEIZAK_BATTLEGROUNDS }, 19 }, -- Bivouac #1
|
|
[43] = { 34, 3, 5002, { -42, 0, 155, 191, xi.zone.CEIZAK_BATTLEGROUNDS }, 20 }, -- Bivouac #2
|
|
[44] = { 35, 3, 5003, { -442, 0, -247, 191, xi.zone.CEIZAK_BATTLEGROUNDS }, 21 }, -- Bivouac #3
|
|
|
|
-- Foret de Hennetiel
|
|
[51] = { 64, 5, 5000, { 398.11, -2, 279.11, 0, xi.zone.FORET_DE_HENNETIEL }, 26 }, -- Frontier Station
|
|
[52] = { 65, 5, 5001, { 12.6, -2.4, 342, 0, xi.zone.FORET_DE_HENNETIEL }, 27 }, -- Bivouac #1
|
|
[53] = { 66, 5, 5002, { 505, -2.25, -303.5, 127, xi.zone.FORET_DE_HENNETIEL }, 28 }, -- Bivouac #2
|
|
[54] = { 67, 5, 5003, { 103, -2.2, -92.3, 63, xi.zone.FORET_DE_HENNETIEL }, 29 }, -- Bivouac #3
|
|
[55] = { 68, 5, 5004, { -251.8, -2.37, -39.25, 63, xi.zone.FORET_DE_HENNETIEL }, 30 }, -- Bivouac #4
|
|
|
|
-- Morimar Basalt Fields
|
|
[61] = { 70, 6, 5000, { 443.728, -16, -325.428, 191, xi.zone.MORIMAR_BASALT_FIELDS }, 31 }, -- Frontier Station
|
|
[62] = { 71, 6, 5001, { 368, -16, 37.5, 127, xi.zone.MORIMAR_BASALT_FIELDS }, 32 }, -- Bivouac #1
|
|
[63] = { 72, 6, 5002, { 112.8, -0.483, 324.4, 63, xi.zone.MORIMAR_BASALT_FIELDS }, 33 }, -- Bivouac #2
|
|
[64] = { 73, 6, 5003, { 175.5, -15.581, -318.2, 127, xi.zone.MORIMAR_BASALT_FIELDS }, 34 }, -- Bivouac #3
|
|
[65] = { 74, 6, 5004, { -323, -32, 2, 63, xi.zone.MORIMAR_BASALT_FIELDS }, 35 }, -- Bivouac #4
|
|
[66] = { 75, 6, 5005, { -78.2, -47.284, 303, 191, xi.zone.MORIMAR_BASALT_FIELDS }, 36 }, -- Bivouac #5
|
|
|
|
-- Yorcia Weald
|
|
[71] = { 96, 7, 5000, { 353.3, 0.2, 153.3, 223, xi.zone.YORCIA_WEALD }, 37 }, -- Frontier Station
|
|
[72] = { 97, 7, 5001, { -40.5, 0.367, 296.367, 0, xi.zone.YORCIA_WEALD }, 38 }, -- Bivouac #1
|
|
[73] = { 98, 7, 5002, { 122.132, 0.146, -287.731, 127, xi.zone.YORCIA_WEALD }, 39 }, -- Bivouac #2
|
|
[74] = { 99, 7, 5003, { -274.776, 0.357, 85.376, 127, xi.zone.YORCIA_WEALD }, 40 }, -- Bivouac #3
|
|
|
|
-- Marjami Ravine
|
|
[81] = { 102, 8, 5000, { 358, -60, 165, 63, xi.zone.MARJAMI_RAVINE }, 41 }, -- Frontier Station
|
|
[82] = { 103, 8, 5001, { 323, -20, -79, 0, xi.zone.MARJAMI_RAVINE }, 42 }, -- Bivouac #1
|
|
[83] = { 104, 8, 5002, { 6.808, 0, 78.437, 191, xi.zone.MARJAMI_RAVINE }, 43 }, -- Bivouac #2
|
|
[84] = { 105, 8, 5003, { -318.708, -20, -127.275, 63, xi.zone.MARJAMI_RAVINE }, 44 }, -- Bivouac #3
|
|
[85] = { 106, 8, 5004, { -326.022, -40.023, 201.096, 191, xi.zone.MARJAMI_RAVINE }, 45 }, -- Bivouac #4
|
|
|
|
-- Kamihr Drifts
|
|
[91] = { 134, 9, 5000, { 439.403, 63, -272.554, 63, xi.zone.KAMIHR_DRIFTS }, 46 }, -- Frontier Station
|
|
[92] = { 135, 9, 5001, { -42.574, 43, -71.319, 0, xi.zone.KAMIHR_DRIFTS }, 47 }, -- Bivouac #1
|
|
[93] = { 136, 9, 5002, { 8.24, 43, -283.017, 191, xi.zone.KAMIHR_DRIFTS }, 48 }, -- Bivouac #2
|
|
[94] = { 137, 9, 5003, { 9.24, 23, 162.803, 63, xi.zone.KAMIHR_DRIFTS }, 49 }, -- Bivouac #3
|
|
[95] = { 138, 9, 5004, { -228.942, 3.567, 364.512, 127, xi.zone.KAMIHR_DRIFTS }, 50 }, -- Bivouac #4
|
|
|
|
-- Jeuno (Special case, Default Active, uses multiple options)
|
|
[100] = { nil, 10, 10121, { -33.550, 0, -31.840, 150, xi.zone.LOWER_JEUNO } },
|
|
[101] = { nil, 10, 10121, { -33.550, 0, -31.840, 150, xi.zone.LOWER_JEUNO } },
|
|
|
|
-- Runes (One-way, bitmask determined by key items)
|
|
[200] = { nil, nil, nil, { 96.642, -0.199, -4.8, 160, xi.zone.NORTHERN_SAN_DORIA } }, -- Northern San d'Oria
|
|
[201] = { nil, nil, nil, { -176.619, -8, -30.091, 128, xi.zone.BASTOK_MARKETS } }, -- Bastok Markets
|
|
[202] = { nil, nil, nil, { -85.592, -5, 37.239, 0, xi.zone.WINDURST_WOODS } }, -- Windurst Woods
|
|
[203] = { nil, nil, nil, { 18.360, -14.559, 74.017, 64, xi.zone.SELBINA } }, -- Selbina
|
|
[204] = { nil, nil, nil, { -0.201, -4, 109.852, 64, xi.zone.MHAURA } }, -- Mhaura
|
|
[205] = { nil, nil, nil, { -51.565, -10, -104.851, 192, xi.zone.KAZHAM } }, -- Kazham
|
|
[206] = { nil, nil, nil, { -7.348, 0, -82.643, 192, xi.zone.RABAO } }, -- Rabao
|
|
[207] = { nil, nil, nil, { -19.104, 0.220, -47.464, 192, xi.zone.NORG } }, -- Norg
|
|
[208] = { nil, nil, nil, { 0, -23.5, 35.466, 64, xi.zone.TAVNAZIAN_SAFEHOLD } }, -- Tavnazian Safehold
|
|
[209] = { nil, nil, nil, { 676.982, -15.5, 220, 0, xi.zone.WAJAOM_WOODLANDS } }, -- Aht Urhgan Whitegate (Wajaom Woodlands)
|
|
[210] = { nil, nil, nil, { 13.202, -3.675, -453.439, 128, xi.zone.CAEDARVA_MIRE } }, -- Nashmau (Caedarva Mire)
|
|
|
|
-- Enigmatic Devices (One-way, so event should never be called)
|
|
-- NOTE: These are stored at the end of the teleport table internally, and are not displayed as map markers
|
|
[300] = { 128, 11, nil, { -588, -7.5, 19, 192, xi.zone.RALA_WATERWAYS }, 51 }, -- Rala Waterways
|
|
[301] = { 129, 11, nil, { -108.288, 4, -12.370, 151, xi.zone.CIRDAS_CAVERNS }, 52 }, -- Cirdas Caverns
|
|
[302] = { 130, 11, nil, { 171, 4.47, -259, 128, xi.zone.YORCIA_WEALD }, 53 }, -- Yorcia Weald
|
|
[303] = { 131, 11, nil, { -148, -170, 27, 192, xi.zone.OUTER_RAKAZNAR }, 54 }, -- Outer Ra'Kaznar
|
|
}
|
|
|
|
local function buildTeleportLookup()
|
|
local tableLookup = {}
|
|
|
|
for waypointIndex, waypointData in pairs(waypointInfo) do
|
|
if waypointData[5] then
|
|
tableLookup[waypointData[5]] = waypointIndex
|
|
end
|
|
end
|
|
|
|
return tableLookup
|
|
end
|
|
|
|
local tableIndexToWaypoint = buildTeleportLookup()
|
|
|
|
local groupStartIndex =
|
|
{
|
|
[1] = 0, -- Western Adoulin
|
|
[2] = 20, -- Eastern Adoulin
|
|
[3] = 40, -- Ceizak Battlegrounds
|
|
[4] = 30, -- Yahse Hunting Grounds
|
|
[5] = 50, -- Foret de Hennetiel
|
|
[6] = 60, -- Morimar Basalt Fields
|
|
[7] = 70, -- Yorcia Weald
|
|
[8] = 80, -- Marjami Ravine
|
|
[9] = 90, -- Kamihr Drifts
|
|
[10] = 100, -- Lower Jeuno
|
|
[11] = 300, -- Enigmatic Devices
|
|
}
|
|
|
|
local runeKeyItems =
|
|
{
|
|
xi.ki.SAN_DORIA_WARP_RUNE,
|
|
xi.ki.BASTOK_WARP_RUNE,
|
|
xi.ki.WINDURST_WARP_RUNE,
|
|
xi.ki.SELBINA_WARP_RUNE,
|
|
xi.ki.MHAURA_WARP_RUNE,
|
|
xi.ki.KAZHAM_WARP_RUNE,
|
|
xi.ki.RABAO_WARP_RUNE,
|
|
xi.ki.NORG_WARP_RUNE,
|
|
xi.ki.TAVNAZIA_WARP_RUNE,
|
|
xi.ki.WHITEGATE_WARP_RUNE,
|
|
xi.ki.NASHMAU_WARP_RUNE,
|
|
}
|
|
|
|
-- Number of Kinetic Units granted for each item trade.
|
|
local crystalTradeValues =
|
|
{
|
|
[xi.item.EARTH_CRYSTAL ] = 15,
|
|
[xi.item.FIRE_CRYSTAL ] = 15,
|
|
[xi.item.WATER_CRYSTAL ] = 15,
|
|
[xi.item.WIND_CRYSTAL ] = 15,
|
|
[xi.item.ICE_CRYSTAL ] = 30,
|
|
[xi.item.LIGHTNING_CRYSTAL] = 30,
|
|
[xi.item.DARK_CRYSTAL ] = 80,
|
|
[xi.item.LIGHT_CRYSTAL ] = 80,
|
|
[xi.item.EARTH_CLUSTER ] = 200,
|
|
[xi.item.FIRE_CLUSTER ] = 200,
|
|
[xi.item.WATER_CLUSTER ] = 200,
|
|
[xi.item.WIND_CLUSTER ] = 200,
|
|
[xi.item.ICE_CLUSTER ] = 400,
|
|
[xi.item.LIGHTNING_CLUSTER] = 400,
|
|
[xi.item.INFERNO_CRYSTAL ] = 500,
|
|
[xi.item.GLACIER_CRYSTAL ] = 500,
|
|
[xi.item.CYCLONE_CRYSTAL ] = 500,
|
|
[xi.item.TERRA_CRYSTAL ] = 500,
|
|
[xi.item.PLASMA_CRYSTAL ] = 500,
|
|
[xi.item.TORRENT_CRYSTAL ] = 500,
|
|
[xi.item.AURORA_CRYSTAL ] = 500,
|
|
[xi.item.TWILIGHT_CRYSTAL ] = 500,
|
|
[xi.item.DARK_CLUSTER ] = 1000,
|
|
[xi.item.LIGHT_CLUSTER ] = 1000,
|
|
}
|
|
|
|
local function getWaypointIndex(npcObj)
|
|
local waypointNpcId = npcObj:getID()
|
|
local zoneObject = npcObj:getZone()
|
|
local waypointList = zoneObject:queryEntitiesByName('Waypoint')
|
|
|
|
for indexVal, npcData in ipairs(waypointList) do
|
|
if npcData:getID() == waypointNpcId then
|
|
return waypointStartIndex[zoneObject:getID()] + indexVal
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
-- Teleports are stored in an array of uint32, but referenced in-game using multiple
|
|
-- methods. This will return a table of 5 parameters corresponding to what the event
|
|
-- onTrigger uses.
|
|
local function getUnlockedWaypointParameters(player)
|
|
local unlockedWaypoints = player:getTeleportTable(xi.teleport.type.WAYPOINT)
|
|
local parameterTable = { 0, 0, 0, 0, 0 }
|
|
|
|
for indexVal, waypointField in ipairs(unlockedWaypoints) do
|
|
for bitPos = 0, 31 do
|
|
if bit.band(bit.rshift(waypointField, bitPos), 1) == 1 then
|
|
local waypointIndex = tableIndexToWaypoint[32 * (indexVal - 1) + bitPos]
|
|
local waypointOffset = waypointInfo[waypointIndex][1]
|
|
local parameterIndex = math.floor(waypointOffset / 32) + 1
|
|
local parameterBitPos = waypointOffset % 32
|
|
|
|
parameterTable[parameterIndex] = utils.mask.setBit(parameterTable[parameterIndex], parameterBitPos, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
return parameterTable
|
|
end
|
|
|
|
local function getRuneMask(player)
|
|
local resultMask = utils.MAX_UINT32 - 1
|
|
|
|
for bitIndex, keyItem in ipairs(runeKeyItems) do
|
|
if player:hasKeyItem(keyItem) then
|
|
resultMask = utils.mask.setBit(resultMask, bitIndex, false)
|
|
end
|
|
end
|
|
|
|
return resultMask
|
|
end
|
|
|
|
-- The below functions are used by all Waypoint NPCs
|
|
xi.waypoint.onTrade = function(player, npc, trade)
|
|
local ID = zones[player:getZoneID()]
|
|
local currentUnits = player:getCurrency('kinetic_unit')
|
|
local kineticValue = 0
|
|
|
|
if currentUnits == 50000 then
|
|
player:messageSpecial(ID.text.REACHED_KINETIC_UNIT_LIMIT)
|
|
return
|
|
end
|
|
|
|
for crystalId, crystalValue in pairs(crystalTradeValues) do
|
|
local numCrystals = trade:getItemQty(crystalId)
|
|
|
|
if numCrystals > 0 then
|
|
kineticValue = kineticValue + numCrystals * crystalValue
|
|
trade:confirmItem(crystalId, numCrystals)
|
|
end
|
|
end
|
|
|
|
if kineticValue > 0 then
|
|
player:confirmTrade()
|
|
|
|
if currentUnits + kineticValue > 50000 then
|
|
local lostUnits = currentUnits + kineticValue - 50000
|
|
|
|
player:messageSpecial(ID.text.ARTIFACT_TERMINAL_VOLUME)
|
|
player:messageSpecial(ID.text.SURPLUS_LOST_TO_AETHER, lostUnits)
|
|
|
|
player:setCurrency('kinetic_unit', 50000)
|
|
else
|
|
player:messageName(ID.text.ARTIFACT_HAS_BEEN_CHARGED, nil, kineticValue, kineticValue + currentUnits)
|
|
player:addCurrency('kinetic_unit', kineticValue)
|
|
end
|
|
else
|
|
player:messageName(ID.text.CANNOT_RECEIVE_KINETIC, nil)
|
|
end
|
|
end
|
|
|
|
xi.waypoint.onTrigger = function(player, npc)
|
|
local waypointIndex = getWaypointIndex(npc)
|
|
local zoneId = player:getZoneID()
|
|
|
|
if
|
|
zoneId == xi.zone.LOWER_JEUNO or
|
|
player:hasTeleport(xi.teleport.type.WAYPOINT, waypointInfo[waypointIndex][5])
|
|
then
|
|
local unlockedWaypoints = getUnlockedWaypointParameters(player)
|
|
local destConfirmation = player:getTeleportMenu(xi.teleport.type.WAYPOINT)[1] and 1 or 0
|
|
|
|
-- Note: The '4' Value is setting the discount value for waypoints, which appears to always
|
|
-- be the case on retail now.
|
|
local menuParams = bit.bor(4, bit.lshift(destConfirmation, 3))
|
|
|
|
-- Waypoint Event ID
|
|
local eventId = waypointInfo[waypointIndex][3]
|
|
|
|
-- First event parameters packs the player's kinetic units, two bits that determine teleportation cost,
|
|
-- and the Index value of the waypoint (See: waypointInfo table)
|
|
local p0 = bit.lshift(player:getCurrency('kinetic_unit'), 16) + bit.lshift(menuParams, 12) + waypointIndex
|
|
|
|
-- Eastern and Western Adoulin Waypoints
|
|
local p1 = unlockedWaypoints[1]
|
|
|
|
-- Third event parameter is an inverted bitfield for all but bit 0, the remainder of the field is set
|
|
-- by the key items for warp runes.
|
|
local p2 = getRuneMask(player)
|
|
|
|
-- Unlock all non-city Waypoints
|
|
local p3 = unlockedWaypoints[2]
|
|
local p4 = unlockedWaypoints[3]
|
|
local p5 = unlockedWaypoints[4]
|
|
local p6 = unlockedWaypoints[5]
|
|
|
|
player:startEvent(eventId, p0, p1, p2, p3, p4, p5, p6)
|
|
elseif zoneId ~= xi.zone.LOWER_JEUNO then
|
|
local ID = zones[zoneId]
|
|
|
|
player:addTeleport(xi.teleport.type.WAYPOINT, waypointInfo[waypointIndex][5])
|
|
player:messageSpecial(ID.text.WAYPOINT_ATTUNED, waypointIndex - waypointStartIndex[zoneId], xi.ki.GEOMAGNETRON)
|
|
end
|
|
end
|
|
|
|
-- Note: There is additional data packed into the event update option that is unused at this time (below):
|
|
-- currentWaypointIndex = bit.band(option, 0x7F)
|
|
-- destinationGroup = bit.band(bit.rshift(option, 7), 0xF)
|
|
-- destinationOffset = bit.band(bit.rshift(option, 11), 0xF)
|
|
|
|
local function isFieldWaypoint(groupId)
|
|
return groupId ~= nil and groupId >= 3 and groupId <= 9
|
|
end
|
|
|
|
local function isAdoulinCity(groupId)
|
|
return groupId == 1 or groupId == 2
|
|
end
|
|
|
|
local function getWaypointTravelCost(sourceIndex, destIndex)
|
|
local dest = waypointInfo[destIndex]
|
|
if dest == nil then
|
|
return 0
|
|
end
|
|
|
|
if destIndex >= 300 then
|
|
return 150 -- Enigmatic Devices
|
|
end
|
|
|
|
if destIndex >= 200 then
|
|
return 100 -- Warp Runes
|
|
end
|
|
|
|
local source = waypointInfo[sourceIndex]
|
|
if source == nil then
|
|
return 0
|
|
end
|
|
|
|
local sourceZone = source[4][5]
|
|
local destZone = dest[4][5]
|
|
local sameZone = sourceZone == destZone or (isAdoulinCity(source[2]) and isAdoulinCity(dest[2]))
|
|
|
|
if isFieldWaypoint(dest[2]) then
|
|
return sameZone and 2 or 50 -- Field: 2 intra-zone, 50 cross-zone
|
|
else
|
|
return sameZone and 1 or 15 -- City: 1 intra-zone, 15 cross-zone
|
|
end
|
|
end
|
|
|
|
xi.waypoint.onEventUpdate = function(player, csid, option, npc)
|
|
local ID = zones[player:getZoneID()]
|
|
|
|
local destGroup = bit.band(bit.rshift(option, 7), 0xF)
|
|
local destOffset = bit.band(bit.rshift(option, 11), 0xF)
|
|
local start = groupStartIndex[destGroup]
|
|
local destIndex = start and start + destOffset or nil
|
|
local waypoint = destIndex and waypointInfo[destIndex]
|
|
|
|
if waypoint == nil then
|
|
return
|
|
end
|
|
|
|
-- Validate destination is unlocked
|
|
if waypoint[5] ~= nil then
|
|
if not player:hasTeleport(xi.teleport.type.WAYPOINT, waypoint[5]) then
|
|
return
|
|
end
|
|
elseif destIndex >= 200 and destIndex <= 210 then
|
|
local runeIndex = destIndex - 199
|
|
if
|
|
runeKeyItems[runeIndex] and
|
|
not player:hasKeyItem(runeKeyItems[runeIndex])
|
|
then
|
|
return
|
|
end
|
|
end
|
|
|
|
local sourceIndex = getWaypointIndex(npc)
|
|
local source = waypointInfo[sourceIndex]
|
|
local sourceGroup = source and source[2]
|
|
local destWaypointGrp = waypoint[2]
|
|
|
|
-- Field destinations only reachable from Adoulin city waypoints
|
|
if isFieldWaypoint(destWaypointGrp) and not isAdoulinCity(sourceGroup) then
|
|
return
|
|
end
|
|
|
|
-- Lower Jeuno can only reach Adoulin cities
|
|
if sourceGroup == 10 and not isAdoulinCity(destWaypointGrp) then
|
|
return
|
|
end
|
|
|
|
local travelCost = getWaypointTravelCost(sourceIndex, destIndex)
|
|
|
|
if player:getCurrency('kinetic_unit') < travelCost then
|
|
player:messageSpecial(ID.text.INSUFFICIENT_UNITS)
|
|
return
|
|
end
|
|
|
|
player:delCurrency('kinetic_unit', travelCost)
|
|
player:setLocalVar('waypointPaid', 1)
|
|
player:messageSpecial(ID.text.EXPENDED_KINETIC_UNITS, travelCost)
|
|
player:updateEvent(0, 0, 0, 0, 0, 0, 0, 1)
|
|
end
|
|
|
|
xi.waypoint.onEventFinish = function(player, csid, option, npc)
|
|
if option > 0 and option <= 303 then
|
|
local waypoint = waypointInfo[option]
|
|
if waypoint == nil then
|
|
return
|
|
end
|
|
|
|
-- Validate destination is unlocked
|
|
if waypoint[5] ~= nil then
|
|
if not player:hasTeleport(xi.teleport.type.WAYPOINT, waypoint[5]) then
|
|
return
|
|
end
|
|
elseif option >= 200 and option <= 210 then
|
|
local runeIndex = option - 199
|
|
if
|
|
runeKeyItems[runeIndex] and
|
|
not player:hasKeyItem(runeKeyItems[runeIndex])
|
|
then
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Validate route
|
|
local sourceIndex = getWaypointIndex(npc)
|
|
local source = waypointInfo[sourceIndex]
|
|
local sourceGroup = source and source[2]
|
|
local destGroup = waypoint[2]
|
|
|
|
if isFieldWaypoint(destGroup) and not isAdoulinCity(sourceGroup) then
|
|
return
|
|
end
|
|
|
|
if sourceGroup == 10 and not isAdoulinCity(destGroup) then
|
|
return
|
|
end
|
|
|
|
if player:getLocalVar('waypointPaid') == 0 then
|
|
local travelCost = getWaypointTravelCost(sourceIndex, option)
|
|
if player:getCurrency('kinetic_unit') < travelCost then
|
|
return
|
|
end
|
|
|
|
player:delCurrency('kinetic_unit', travelCost)
|
|
end
|
|
|
|
player:setLocalVar('waypointPaid', 0)
|
|
|
|
player:setPos(unpack(waypoint[4]))
|
|
elseif option == 1000 then
|
|
-- Decline Confirmation (Default Off)
|
|
player:setTeleportMenu(xi.teleport.type.WAYPOINT, true)
|
|
elseif option == 1001 then
|
|
player:setTeleportMenu(xi.teleport.type.WAYPOINT, false)
|
|
end
|
|
end
|