From 85fe7e31baf857e61c8ae123eef6a81240d6e5b2 Mon Sep 17 00:00:00 2001 From: Kipling Date: Wed, 10 Jun 2026 00:24:15 -0700 Subject: [PATCH] Pirates Spawning Behavior Co-authored-by: Xaver-DaRed --- scripts/globals/pirates.lua | 165 ++++++++++++------ .../Ship_bound_for_Mhaura_Pirates/IDs.lua | 3 +- .../mobs/Crossbones.lua | 4 + .../mobs/Ship_Wight.lua | 29 +++ .../mobs/Silverhook.lua | 4 +- .../Ship_bound_for_Selbina_Pirates/IDs.lua | 1 + .../mobs/Blackbeard.lua | 4 +- .../mobs/Crossbones.lua | 6 +- .../mobs/Ship_Wight.lua | 31 +++- sql/mob_spawn_points.sql | 4 +- 10 files changed, 187 insertions(+), 64 deletions(-) diff --git a/scripts/globals/pirates.lua b/scripts/globals/pirates.lua index 139bac5c9eb..a226aee530f 100644 --- a/scripts/globals/pirates.lua +++ b/scripts/globals/pirates.lua @@ -2,12 +2,9 @@ -- Ship bound for [Mhaura/Selbina] Pirates helpers -- NOTE: Careful with queues as they don't resolve until a zone wakes from sleep, potentially having mismatched timing. Timers are fine. ----------------------------------- - xi = xi or {} xi.pirates = xi.pirates or {} - --- chance for encounter to have a special middle NPC, which indicates a chance for NM to spawn -local vermCloakPirateChance = 10 +----------------------------------- local actions = { @@ -20,92 +17,113 @@ local actions = DEPARTING = 6, } --- times are minutes after midnight for first cycle, cycle is 480 minutes +-- Times are minutes after midnight for first cycle. Cycle is 480 minutes. local piratesSchedule = { - { endTime = utils.timeStringToMinutes('01:10'), action = actions.ARRIVING }, - { endTime = utils.timeStringToMinutes('01:30'), action = actions.ARRIVE }, - { endTime = utils.timeStringToMinutes('01:32'), action = actions.PIRATES_ARRIVE }, - { endTime = utils.timeStringToMinutes('01:35'), action = actions.MOBS_SPAWN }, + { endTime = utils.timeStringToMinutes('01:10'), action = actions.ARRIVING }, + { endTime = utils.timeStringToMinutes('01:30'), action = actions.ARRIVE }, + { endTime = utils.timeStringToMinutes('01:32'), action = actions.PIRATES_ARRIVE }, + { endTime = utils.timeStringToMinutes('01:34'), action = actions.MOBS_SPAWN }, { endTime = utils.timeStringToMinutes('04:20'), action = actions.PIRATES_RETREAT }, - { endTime = utils.timeStringToMinutes('04:27'), action = actions.DEPART }, - { endTime = utils.timeStringToMinutes('04:48'), action = actions.DEPARTING }, + { endTime = utils.timeStringToMinutes('04:27'), action = actions.DEPART }, + { endTime = utils.timeStringToMinutes('04:48'), action = actions.DEPARTING }, } local piratesData = { - -- Pirate ship is on left side of boat + -- Pirate ship is on left side of boat. [xi.zone.SHIP_BOUND_FOR_SELBINA_PIRATES] = { { startPos = { x = -33.601, y = -7.16, z = 13.37, rotation = 0 }, - standingPos = { x = -21.90, y = -7.16, z = 10.46, rotation = 0 }, + standingPos = { x = -21.900, y = -7.16, z = 10.46, rotation = 0 }, }, { - startPos = { x = -29.728, y = -7.16, z = 1.30, rotation = 0 }, - standingPos = { x = -21.90, y = -7.16, z = 6.59, rotation = 0 }, + startPos = { x = -29.728, y = -7.16, z = 1.30, rotation = 0 }, + standingPos = { x = -21.900, y = -7.16, z = 6.59, rotation = 0 }, }, { startPos = { x = -29.602, y = -7.16, z = -2.47, rotation = 0 }, - standingPos = { x = -21.90, y = -7.16, z = 2.10, rotation = 0 }, + standingPos = { x = -21.900, y = -7.16, z = 2.10, rotation = 0 }, }, }, - -- Pirate ship is on right side of boat + -- Pirate ship is on right side of boat. [xi.zone.SHIP_BOUND_FOR_MHAURA_PIRATES] = { { startPos = { x = 33.601, y = -7.16, z = 13.37, rotation = 128 }, - standingPos = { x = 21.90, y = -7.16, z = 10.46, rotation = 128 }, + standingPos = { x = 21.900, y = -7.16, z = 10.46, rotation = 128 }, }, { - startPos = { x = 29.728, y = -7.16, z = 1.30, rotation = 128 }, - standingPos = { x = 21.90, y = -7.16, z = 6.59, rotation = 128 }, + startPos = { x = 29.728, y = -7.16, z = 1.30, rotation = 128 }, + standingPos = { x = 21.900, y = -7.16, z = 6.59, rotation = 128 }, }, { startPos = { x = 29.602, y = -7.16, z = -2.47, rotation = 128 }, - standingPos = { x = 21.90, y = -7.16, z = 2.10, rotation = 128 }, + standingPos = { x = 21.900, y = -7.16, z = 2.10, rotation = 128 }, }, }, } -xi.pirates.setupPirateNPCSchedule = function(npc) - npc:initNpcAi() +-- This ride's NM: Blackbeard sails the Selbina route, Silverhook the Mhaura route. +local function getNMId(zoneId) + if zoneId == xi.zone.SHIP_BOUND_FOR_SELBINA_PIRATES then + return zones[zoneId].mob.BLACKBEARD + end - -- create triggers for every stage of the encounter on each Pirate NPC - for _, eventData in ipairs(piratesSchedule) do - npc:addPeriodicTrigger(eventData.action, 480, eventData.endTime) + return zones[zoneId].mob.SILVERHOOK +end + +-- Clear the deck of pirate mobs: disable respawns, despawn idle ones, and leave any still in combat to be finished off (they won't respawn). +-- Used at retreat and before a fresh ride spawns +local function clearPirates(zoneId) + local ID = zones[zoneId] + local mobIdTable = { ID.mob.SHIP_WIGHT, getNMId(zoneId) } + for _, mobId in ipairs(ID.mob.CROSSBONES) do + table.insert(mobIdTable, mobId) + end + + for _, mobId in ipairs(mobIdTable) do + local mob = GetMobByID(mobId) + if mob then + mob:setRespawnTime(0) -- Stop the waves / Cancel any pending respawn. + if mob:isSpawned() and not mob:isEngaged() then + DespawnMob(mobId) -- Engaged mobs stay until killed, then won't return. + end + end end end --- calls itself via timer until the npc is hidden +-- Calls itself via timer until the npc is hidden. local function summonAnimations(npc, rotation, offset) if npc:getStatus() == xi.status.DISAPPEAR then return end if not npc:isFollowingPath() then - local pos = npc:getPos() + local pos = npc:getPos() + local currentTime = GetSystemTime() + if npc:getLocalVar('initialNpcState') == 1 then npc:setLocalVar('initialNpcState', 0) -- rotate to face the player boat npc:setPos(pos.x, pos.y, pos.z, rotation) -- first summoning rotation happens in order of NPC ID - npc:setLocalVar('summonStartTime', GetSystemTime() + (offset - 1) * 2) + npc:setLocalVar('summonStartTime', currentTime + (offset - 1) * 2) end local summonStartTime = npc:getLocalVar('summonStartTime') - if summonStartTime ~= 0 and summonStartTime <= GetSystemTime() then + if summonStartTime ~= 0 and summonStartTime <= currentTime then npc:setLocalVar('summonStartTime', 0) - npc:setLocalVar('summonEndTime', GetSystemTime() + math.random(1, 2)) + npc:setLocalVar('summonEndTime', currentTime + math.random(1, 2)) npc:entityAnimationPacket(xi.animationString.CAST_SUMMONER_START) end local summonEndTime = npc:getLocalVar('summonEndTime') - if summonEndTime ~= 0 and summonEndTime <= GetSystemTime() then + if summonEndTime ~= 0 and summonEndTime <= currentTime then + npc:setLocalVar('summonStartTime', currentTime + math.random(4 + offset, 10)) npc:setLocalVar('summonEndTime', 0) - -- npcs seem to wait anywhere from 5 to 10s to do another summoning animation - npc:setLocalVar('summonStartTime', GetSystemTime() + math.random(4 + offset, 10)) npc:entityAnimationPacket(xi.animationString.CAST_SUMMONER_STOP) end @@ -126,7 +144,16 @@ local function summonAnimations(npc, rotation, offset) end) end --- called on every NPC periodic trigger, which is mapped 1-1 to the schedule table, with triggerId == action +xi.pirates.setupPirateNPCSchedule = function(npc) + npc:initNpcAi() + + -- Create triggers for every stage of the encounter on each Pirate NPC. + for _, eventData in ipairs(piratesSchedule) do + npc:addPeriodicTrigger(eventData.action, 480, eventData.endTime) + end +end + +-- Called on every NPC periodic trigger, which is mapped 1-1 to the schedule table, with triggerId == action xi.pirates.pirateNPCTimeTrigger = function(npc, triggerId, zoneKey) local pirateZone = npc:getZone() if not pirateZone then @@ -134,7 +161,8 @@ xi.pirates.pirateNPCTimeTrigger = function(npc, triggerId, zoneKey) end local pirateNPCs = zones[pirateZone:getID()].npc.PIRATES - local pirateIdx = 0 + local pirateIdx = 0 + for i, npcId in ipairs(pirateNPCs) do if npcId == npc:getID() then pirateIdx = i @@ -147,19 +175,13 @@ xi.pirates.pirateNPCTimeTrigger = function(npc, triggerId, zoneKey) return end + -- Pirates appear and run to position if triggerId == actions.PIRATES_ARRIVE then - -- Pirates appear and run to position if pirateIdx == 2 then -- middle pirate has chance to wear a verm cloak, which then means the pirate encounter _might_ have the NM spawn - local bodyModel = 8195 - local nmCanSpawn = 0 - if math.random(1, 100) <= vermCloakPirateChance then - bodyModel = 47 - nmCanSpawn = 1 - end - - npc:setModelId(bodyModel, xi.slot.BODY) - pirateZone:setLocalVar('nmCanSpawn', nmCanSpawn) + local hasVermCloak = math.random(1, 100) <= 10 + npc:setModelId(hasVermCloak and 47 or 8195, xi.slot.BODY) -- 47 = verm cloak body, 8195 = default body + pirateZone:setLocalVar('nmCanSpawn', hasVermCloak and 1 or 0) -- 1 = NM still eligible; cleared to 0 once it spawns end npc:setPos(pirateData.startPos) @@ -170,8 +192,9 @@ xi.pirates.pirateNPCTimeTrigger = function(npc, triggerId, zoneKey) -- Indicates we need to rotate NPC after pathing completes npc:setLocalVar('initialNpcState', 1) summonAnimations(npc, pirateData.standingPos.rotation, pirateIdx) + + -- Retreat. elseif triggerId == actions.PIRATES_RETREAT then - -- retreat local summonEndTime = npc:getLocalVar('summonEndTime') -- No more animations will happen and recursive function self destructs npc:setLocalVar('summonStartTime', 0) @@ -181,8 +204,9 @@ xi.pirates.pirateNPCTimeTrigger = function(npc, triggerId, zoneKey) end npc:pathTo(pirateData.startPos.x, pirateData.startPos.y, pirateData.startPos.z, xi.path.flag.RUN + xi.path.flag.WALLHACK) + + -- Just in case summonAnimations didn't set status elseif triggerId == actions.DEPART then - -- Just in case summonAnimations didn't set status npc:clearPath() npc:setStatus(xi.status.DISAPPEAR) end @@ -192,15 +216,42 @@ end xi.pirates.zoneStateChange = function(zone, action) -- change the zone's state once per action cycle (this function is called by each NPC) - if zone:getLocalVar('currPiratesAction') ~= action then - zone:setLocalVar('currPiratesAction', action) - - if action == actions.MOBS_SPAWN then - -- TODO enable mob spawns (and NM spawns if nmCanSpawn is set to 1) - -- set them to setRespawn(1s), then set normal respawnTime in onMobSpawn - elseif action == actions.PIRATES_RETREAT then - -- TODO disable all spawns and despawn any not in combat - -- mobs in combat do not despawn when the ship leaves + if zone:getLocalVar('currPiratesAction') == action then + return + end + + zone:setLocalVar('currPiratesAction', action) + + local zoneId = zone:getID() + local ID = zones[zoneId] + + if action == actions.MOBS_SPAWN then + -- clear any mobs lingering from a previous ride before summoning fresh ones + clearPirates(zoneId) + + -- the skeletons the pirate NPCs are "summoning" onto the deck + for _, mobId in ipairs(ID.mob.CROSSBONES) do + local crossbones = GetMobByID(mobId) + if crossbones then + crossbones:setRespawnTime(1) + end + end + + if zone:getLocalVar('nmCanSpawn') == 1 and math.random(1, 100) <= 75 then + -- HQ ride, 75%: NM appears from the start + local nm = GetMobByID(getNMId(zoneId)) + if nm then + nm:setRespawnTime(1) + zone:setLocalVar('nmCanSpawn', 0) -- NM is up; no longer eligible to spawn + end + else + -- normal ride, or the 25% placeholder Wight on an HQ ride + local wight = GetMobByID(ID.mob.SHIP_WIGHT) + if wight then + wight:setRespawnTime(1) + end end + elseif action == actions.PIRATES_RETREAT then + clearPirates(zoneId) end end diff --git a/scripts/zones/Ship_bound_for_Mhaura_Pirates/IDs.lua b/scripts/zones/Ship_bound_for_Mhaura_Pirates/IDs.lua index 8d4d5899e6d..58d45f04748 100644 --- a/scripts/zones/Ship_bound_for_Mhaura_Pirates/IDs.lua +++ b/scripts/zones/Ship_bound_for_Mhaura_Pirates/IDs.lua @@ -23,7 +23,8 @@ zones[xi.zone.SHIP_BOUND_FOR_MHAURA_PIRATES] = }, mob = { - WIGHT = GetFirstID('Ship_Wight'), + CROSSBONES = GetTableOfIDs('Crossbones'), + SHIP_WIGHT = GetFirstID('Ship_Wight'), SILVERHOOK = GetFirstID('Silverhook'), }, npc = diff --git a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Crossbones.lua b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Crossbones.lua index c0f6eb8abeb..0644d15f6e1 100644 --- a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Crossbones.lua +++ b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Crossbones.lua @@ -9,4 +9,8 @@ entity.onMobInitialize = function(mob) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) end +entity.onMobSpawn = function(mob) + mob:setRespawnTime(60) -- respawns every 60s while the pirate ship is alongside +end + return entity diff --git a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Ship_Wight.lua b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Ship_Wight.lua index 0aa84cbc16a..8b5c38610d7 100644 --- a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Ship_Wight.lua +++ b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Ship_Wight.lua @@ -9,4 +9,33 @@ entity.onMobInitialize = function(mob) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) end +entity.onMobSpawn = function(mob) + mob:setRespawnTime(60) -- Respawns every 60s while the pirate ship is alongside. +end + +entity.onMobDespawn = function(mob) + local zone = mob:getZone() + if not zone then + return + end + + -- On an HQ ride, the Ship Wight is Silverhook's placeholder (90%) until he appears. + if zone:getLocalVar('nmCanSpawn') == 0 then + return + end + + if math.random(1, 100) > 90 then + return + end + + local silverhook = GetMobByID(zones[xi.zone.SHIP_BOUND_FOR_MHAURA_PIRATES].mob.SILVERHOOK) + if not silverhook then + return + end + + mob:setRespawnTime(0) -- This Wight stays down; Silverhook takes its slot. + silverhook:setRespawnTime(1) + zone:setLocalVar('nmCanSpawn', 0) +end + return entity diff --git a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Silverhook.lua b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Silverhook.lua index 877ed544530..6172151a613 100644 --- a/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Silverhook.lua +++ b/scripts/zones/Ship_bound_for_Mhaura_Pirates/mobs/Silverhook.lua @@ -1,7 +1,8 @@ ----------------------------------- -- Area: Ship Bound for Mhaura (Pirates) -- NM: Silverhook --- To do: This NM does not use an SP ability like JP wiki claims but it does have a dust cloud animation that triggers as soon as it is pushed below 50% HP. It is unclear what this does, if anything, after multiple retail captures. +-- To do: This NM does not use an SP ability like JP wiki claims but it does have a dust cloud animation that triggers as soon as it is pushed below 50% HP. +-- It is unclear what this does, if anything, after multiple retail captures. ----------------------------------- ---@type TMobEntity local entity = {} @@ -19,6 +20,7 @@ entity.onMobSpawn = function(mob) mob:setMod(xi.mod.ICE_RES_RANK, 10) mob:setMod(xi.mod.POWER_MULTIPLIER_SPELL, 55) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) + mob:setRespawnTime(0) -- one-and-done per ride; the 1s spawn timer must not respawn him end entity.onMobSpellChoose = function(mob, target, spellId) diff --git a/scripts/zones/Ship_bound_for_Selbina_Pirates/IDs.lua b/scripts/zones/Ship_bound_for_Selbina_Pirates/IDs.lua index e56833cd947..ca59c2b1a71 100644 --- a/scripts/zones/Ship_bound_for_Selbina_Pirates/IDs.lua +++ b/scripts/zones/Ship_bound_for_Selbina_Pirates/IDs.lua @@ -24,6 +24,7 @@ zones[xi.zone.SHIP_BOUND_FOR_SELBINA_PIRATES] = mob = { BLACKBEARD = GetFirstID('Blackbeard'), + CROSSBONES = GetTableOfIDs('Crossbones'), ENAGAKURE = GetFirstID('Enagakure'), SHIP_WIGHT = GetFirstID('Ship_Wight'), }, diff --git a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Blackbeard.lua b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Blackbeard.lua index a59f8414d5d..31995ea3ec9 100644 --- a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Blackbeard.lua +++ b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Blackbeard.lua @@ -1,7 +1,8 @@ ----------------------------------- -- Area: Ship Bound for Selbina (Pirates) -- NM: Blackbeard --- To do: This NM does not use an SP ability like JP wiki claims but it does have a dust cloud animation that triggers as soon as it is pushed below 50% HP. It is unclear what this does, if anything, after multiple retail captures. +-- To do: This NM does not use an SP ability like JP wiki claims but it does have a dust cloud animation that triggers as soon as it is pushed below 50% HP. +-- It is unclear what this does, if anything, after multiple retail captures. ----------------------------------- ---@type TMobEntity local entity = {} @@ -19,6 +20,7 @@ entity.onMobSpawn = function(mob) mob:setMod(xi.mod.ICE_RES_RANK, 10) mob:setMod(xi.mod.POWER_MULTIPLIER_SPELL, 55) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) + mob:setRespawnTime(0) -- one-and-done per ride; the 1s spawn timer must not respawn him end entity.onMobSpellChoose = function(mob, target, spellId) diff --git a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Crossbones.lua b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Crossbones.lua index fc6a03c5870..56e1f84bda3 100644 --- a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Crossbones.lua +++ b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Crossbones.lua @@ -1,6 +1,6 @@ ----------------------------------- -- Area: Ship bound for Selbina Pirates --- Mob: Ship Wight +-- Mob: Crossbones ----------------------------------- ---@type TMobEntity local entity = {} @@ -9,4 +9,8 @@ entity.onMobInitialize = function(mob) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) end +entity.onMobSpawn = function(mob) + mob:setRespawnTime(60) -- respawns every 60s while the pirate ship is alongside +end + return entity diff --git a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Ship_Wight.lua b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Ship_Wight.lua index 7007bfb4639..a1a0af53209 100644 --- a/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Ship_Wight.lua +++ b/scripts/zones/Ship_bound_for_Selbina_Pirates/mobs/Ship_Wight.lua @@ -1,6 +1,6 @@ ----------------------------------- -- Area: Ship bound for Selbina Pirates --- Mob: Crossbones +-- Mob: Ship Wight ----------------------------------- ---@type TMobEntity local entity = {} @@ -9,4 +9,33 @@ entity.onMobInitialize = function(mob) mob:setMobMod(xi.mobMod.NO_STANDBACK, 1) end +entity.onMobSpawn = function(mob) + mob:setRespawnTime(60) -- Respawns every 60s while the pirate ship is alongside. +end + +entity.onMobDespawn = function(mob) + local zone = mob:getZone() + if not zone then + return + end + + -- On an HQ ride, the Ship Wight is Blackbeard's placeholder (90%) until he appears. + if zone:getLocalVar('nmCanSpawn') == 0 then + return + end + + if math.random(1, 100) > 90 then + return + end + + local blackbeard = GetMobByID(zones[xi.zone.SHIP_BOUND_FOR_SELBINA_PIRATES].mob.BLACKBEARD) + if not blackbeard then + return + end + + mob:setRespawnTime(0) -- This Wight stays down; Blackbeard takes its slot. + blackbeard:setRespawnTime(1) + zone:setLocalVar('nmCanSpawn', 0) +end + return entity diff --git a/sql/mob_spawn_points.sql b/sql/mob_spawn_points.sql index 71dbac0a6b4..d25d204034c 100644 --- a/sql/mob_spawn_points.sql +++ b/sql/mob_spawn_points.sql @@ -79269,7 +79269,7 @@ INSERT INTO `mob_spawn_points` VALUES (17707021,0,'Crossbones','Crossbones',10,2 INSERT INTO `mob_spawn_points` VALUES (17707022,0,'Crossbones','Crossbones',11,28,31,-7.737,-7.279,11.354,155); INSERT INTO `mob_spawn_points` VALUES (17707023,0,'Crossbones','Crossbones',11,28,31,7.177,-7.309,17.648,54); INSERT INTO `mob_spawn_points` VALUES (17707024,0,'Ship_Wight','Ship Wight',12,36,39,-0.725,-7.312,23.111,60); -INSERT INTO `mob_spawn_points` VALUES (17707025,0,'Blackbeard','Blackbeard',13,67,68,-6.387,-11.662,-13.986,56); +INSERT INTO `mob_spawn_points` VALUES (17707025,0,'Blackbeard','Blackbeard',13,67,68,-0.725,-7.312,23.111,60); INSERT INTO `mob_spawn_points` VALUES (17707026,0,'Enagakure','Enagakure',14,55,55,1.000,-7.000,13.000,60); -- ------------------------------------------------------------ @@ -79294,7 +79294,7 @@ INSERT INTO `mob_spawn_points` VALUES (17711117,0,'Crossbones','Crossbones',10,2 INSERT INTO `mob_spawn_points` VALUES (17711118,0,'Crossbones','Crossbones',11,28,31,8.710,-7.560,13.220,176); INSERT INTO `mob_spawn_points` VALUES (17711119,0,'Crossbones','Crossbones',11,28,31,7.040,-7.510,11.060,107); INSERT INTO `mob_spawn_points` VALUES (17711120,0,'Ship_Wight','Ship Wight',12,36,39,7.770,-7.450,17.190,188); -INSERT INTO `mob_spawn_points` VALUES (17711121,0,'Silverhook','Silverhook',13,68,70,-3.560,-7.290,2.430,13); +INSERT INTO `mob_spawn_points` VALUES (17711121,0,'Silverhook','Silverhook',13,68,70,7.770,-7.450,17.190,188); -- ------------------------------------------------------------ -- Throne Room [V] (Zone 229)