From 65ee91e49c6772c1b0c0ed5faa099c50caed75a6 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Fri, 11 Mar 2022 02:17:46 -0500 Subject: [PATCH] Finished Treasures of the Main and Legends Adrift. Fixed quests appearing in private areas (echos). Fixed other bugs. Implemented NPC Linkshells. Added more options to nudge command. Added nudgenpc command. Added testbnpckill command. --- .../chara/npc/object/PrivateAreaPastExit.lua | 34 +- Data/scripts/commands/gm/nudge.lua | 40 +- Data/scripts/commands/gm/nudgenpc.lua | 140 ++++++ Data/scripts/commands/gm/testbnpckill | 16 + Data/scripts/quests/man/man0l1.lua | 4 +- Data/scripts/quests/man/man1l0.lua | 398 ++++++++++++++++++ Data/scripts/quests/man/man2l0.lua | 206 ++++++++- Map Server/Actors/Actor.cs | 7 +- Map Server/Actors/Area/PrivateArea.cs | 10 +- Map Server/Actors/Area/PrivateAreaContent.cs | 2 +- Map Server/Actors/Chara/Player/Player.cs | 13 +- Map Server/Actors/Quest/Quest.cs | 7 +- Map Server/Actors/Quest/QuestStateManager.cs | 7 +- Map Server/WorldManager.cs | 18 +- 14 files changed, 864 insertions(+), 38 deletions(-) create mode 100644 Data/scripts/commands/gm/nudgenpc.lua create mode 100644 Data/scripts/commands/gm/testbnpckill create mode 100644 Data/scripts/quests/man/man1l0.lua diff --git a/Data/scripts/base/chara/npc/object/PrivateAreaPastExit.lua b/Data/scripts/base/chara/npc/object/PrivateAreaPastExit.lua index 6149b464..446fd05d 100644 --- a/Data/scripts/base/chara/npc/object/PrivateAreaPastExit.lua +++ b/Data/scripts/base/chara/npc/object/PrivateAreaPastExit.lua @@ -1,17 +1,37 @@ -function init(npc) +require("global"); + +--[[ + +PrivateAreaPastExit + +This object contains the player inside a PrivateAreaPast, stopping them from escaping it's bounds. It is the +object that generates the circle graphic on the minimap. This object always has two push triggers, an inner +and outer inverted circle. The inner one is named "caution" and the outer one is named "exit". When the player +leaves the inner circle a warning is shown and when they leave the outer circle they either leave the instance +or get warped back to the center. + +]] + +function init(privAreaExit) return false, false, 0, 0; end -function onEventStarted(player, npc, eventType, eventName) +function onEventStarted(player, privAreaExit, eventType, eventName) player:EndEvent(); if (eventName == "caution") then - player:SendGameMessage(player, GetWorldMaster(), 34109, 0x20); + player:SendGameMessage(player, GetWorldMaster(), 34109, MESSAGE_TYPE_SYSTEM); -- You are about to leave the instance. elseif (eventName == "exit") then - local activeQuests = player:GetQuestsForNpc(npc); - print(tostring(#activeQuests)); - if (#activeQuests >= 1) then - activeQuests[1]:OnPush(player, npc, eventName); + local area = privAreaExit.CurrentArea; + if (area.IsPrivate()) then + -- If you can leave, warp to public zone and show message. + if (area.CanExitPrivateArea()) then + player:SendGameMessage(player, GetWorldMaster(), 34110, MESSAGE_TYPE_SYSTEM); -- You have left the instance. + GetWorldManager():WarpToPublicArea(player); + -- Otherwise warp back to the center of the zone. + else + GetWorldManager():WarpToCharaPosition(player, privAreaExit); + end end end end \ No newline at end of file diff --git a/Data/scripts/commands/gm/nudge.lua b/Data/scripts/commands/gm/nudge.lua index bb3ceb50..573eb92b 100644 --- a/Data/scripts/commands/gm/nudge.lua +++ b/Data/scripts/commands/gm/nudge.lua @@ -24,6 +24,21 @@ vertical = { ["DESCEND"] = -1, } +horizontal = { +["RIGHT"] = 2, +["R"] = 2, +["+"] = 2, +["LEFT"] = -2, +["L"] = -2, +["0"] = -2 +} + +rotation = { +["ROTATE"] = 3, +["ORIENTATION"] = 3, +["O"] = 3 +} + function onTrigger(player, argc, arg1, arg2) local pos = player:GetPos(); local x = pos[1]; @@ -54,16 +69,24 @@ function onTrigger(player, argc, arg1, arg2) distance = checkArg1; elseif checkArg1 and not checkArg2 then -- If first is number and second is string distance = checkArg1; - if vertical[string.upper(arg2)] then -- Check vertical direction on string, otherwise throw param error + if vertical[string.upper(arg2)] then -- Check vertical direction on string direction = vertical[string.upper(arg2)]; + elseif horizontal[string.upper(arg2)] then -- Check horizontal direction on string + direction = horizontal[string.upper(arg2)]; + elseif rotation[string.upper(arg2)] then -- Check rotation on string, otherwise throw param error + direction = rotation[string.upper(arg2)]; else player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); return; end elseif (not checkArg1) and checkArg2 then -- If first is string and second is number distance = checkArg2; - if vertical[string.upper(arg1)] then -- Check vertical direction on string, otherwise throw param error + if vertical[string.upper(arg1)] then -- Check vertical direction on string direction = vertical[string.upper(arg1)]; + elseif horizontal[string.upper(arg1)] then -- Check horizontal direction on string + direction = horizontal[string.upper(arg1)]; + elseif rotation[string.upper(arg1)] then -- Check rotation on string, otherwise throw param error + direction = rotation[string.upper(arg1)]; else player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); return; @@ -86,6 +109,19 @@ function onTrigger(player, argc, arg1, arg2) y = y - distance; message = string.format("Positioning down %s yalms.", distance); worldManager:DoPlayerMoveInZone(player, x, y, z, rot, 0x0); + elseif direction == 2 then + local px = x - distance * math.cos(angle - math.pi/2); + local pz = z + distance * math.sin(angle - math.pi/2); + message = string.format("Positioning right %s yalms.", distance); + worldManager:DoPlayerMoveInZone(player, px, y, pz, rot, 0x0); + elseif direction == -2 then + local px = x - distance * math.cos(angle + math.pi/2); + local pz = z + distance * math.sin(angle + math.pi/2); + message = string.format("Positioning left %s yalms.", distance); + worldManager:DoPlayerMoveInZone(player, px, y, pz, rot, 0x0); + elseif direction == 3 then + message = string.format("ROTATE down %s yalms.", distance); + worldManager:DoPlayerMoveInZone(player, x, y, z, distance, 0x0); else local px = x - distance * math.cos(angle); local pz = z + distance * math.sin(angle); diff --git a/Data/scripts/commands/gm/nudgenpc.lua b/Data/scripts/commands/gm/nudgenpc.lua new file mode 100644 index 00000000..e1719e5f --- /dev/null +++ b/Data/scripts/commands/gm/nudgenpc.lua @@ -0,0 +1,140 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "ss", + description = +[[ +Positions a targeted npc by some , defaults to 5 yalms. +!nudge | +!nudge | +!nudge | +!nudge | +!nudge | +]], + +} + +vertical = { +["UP"] = 1, +["U"] = 1, +["+"] = 1, +["ASCEND"] = 1, +["DOWN"] = -1, +["D"] = -1, +["-"] = -1, +["DESCEND"] = -1, +} + +horizontal = { +["RIGHT"] = 2, +["R"] = 2, +["+"] = 2, +["LEFT"] = -2, +["L"] = -2, +["0"] = -2 +} + +rotation = { +["ROTATE"] = 3, +["ORIENTATION"] = 3, +["O"] = 3 +} + +function onTrigger(player, argc, arg1, arg2) + local messageID = MESSAGE_TYPE_SYSTEM; + local sender = "[nudge] "; + + local targetActor = player.CurrentArea.FindActorInArea(player.currentTarget) or nil; + + + if (targetActor == nil) then + player:SendMessage(MESSAGE_TYPE_SYSTEM, sender, "No target was selected.\n"); + return; + end + + local pos = targetActor:GetPos(); + local x = pos[1]; + local y = pos[2]; + local z = pos[3]; + local rot = pos[4]; + local zone = pos[5]; + local angle = rot + (math.pi/2); + + local worldManager = GetWorldManager(); + local distance = 5; + local direction = 0; + + local checkArg1 = tonumber(arg1); + local checkArg2 = tonumber(arg2); + + if argc == 1 then + if checkArg1 then + distance = checkArg1; + else + player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); + return; + end + elseif argc == 2 then + if checkArg1 and checkArg2 then -- If both are numbers, just ignore second argument + distance = checkArg1; + elseif checkArg1 and not checkArg2 then -- If first is number and second is string + distance = checkArg1; + if vertical[string.upper(arg2)] then -- Check vertical direction on string + direction = vertical[string.upper(arg2)]; + elseif horizontal[string.upper(arg2)] then -- Check horizontal direction on string + direction = horizontal[string.upper(arg2)]; + elseif rotation[string.upper(arg2)] then -- Check rotation on string, otherwise throw param error + direction = rotation[string.upper(arg2)]; + else + player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); + return; + end + elseif (not checkArg1) and checkArg2 then -- If first is string and second is number + distance = checkArg2; + if vertical[string.upper(arg1)] then -- Check vertical direction on string + direction = vertical[string.upper(arg1)]; + elseif horizontal[string.upper(arg1)] then -- Check horizontal direction on string + direction = horizontal[string.upper(arg1)]; + elseif rotation[string.upper(arg1)] then -- Check rotation on string, otherwise throw param error + direction = rotation[string.upper(arg1)]; + else + player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); + return; + end + else + player:SendMessage(messageID, sender, "Unknown parameters! Usage: \n"..properties.description); + return; + end + end + + if direction == 1 then + y = y + distance; + targetActor:SetPos(x,y,z,rot,true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), x, y, z, rot); + elseif direction == -1 then + y = y - distance; + targetActor:SetPos(x,y,z,rot,true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), x, y, z, rot); + elseif direction == 2 then + local px = x - distance * math.cos(angle - math.pi/2); + local pz = z + distance * math.sin(angle - math.pi/2); + targetActor:SetPos(px, y, pz, rot, true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), px, y, pz, rot); + elseif direction == -2 then + local px = x - distance * math.cos(angle + math.pi/2); + local pz = z + distance * math.sin(angle + math.pi/2); + targetActor:SetPos(px, y, pz, rot, true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), px, y, pz, rot); + elseif direction == 3 then + targetActor:SetPos(x, y, z, distance, true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), x, y, z, distance); + else + local px = x - distance * math.cos(angle); + local pz = z + distance * math.sin(angle); + targetActor:SetPos(px, y, pz, rot, true, player); + message = string.format("Moved %s @ %f, %f, %f, %f", targetActor:GetUniqueId(), px, y, pz, rot); + end + + player:SendMessage(messageID, sender, message); +end diff --git a/Data/scripts/commands/gm/testbnpckill b/Data/scripts/commands/gm/testbnpckill new file mode 100644 index 00000000..5f70394e --- /dev/null +++ b/Data/scripts/commands/gm/testbnpckill @@ -0,0 +1,16 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "d", + description = "Simulates killing a bnpc. Used for quest testing.", +} + +function onTrigger(player, argc, actorClassId) + if (argc == 1) then + player:HandleBNpcKill(actorClassId); + player:SendMessage(0x20, "", "Simulating BNpc kill for actor class id: " .. tostring(actorClassId)); + else + player:SendMessage(0x20, "", "No actor class id provided."); + end +end \ No newline at end of file diff --git a/Data/scripts/quests/man/man0l1.lua b/Data/scripts/quests/man/man0l1.lua index b9b991b2..0a33d345 100644 --- a/Data/scripts/quests/man/man0l1.lua +++ b/Data/scripts/quests/man/man0l1.lua @@ -28,7 +28,7 @@ SEQ_065 = 65; -- Return to FSH Guild SEQ_070 = 70; -- Contact Baderon on LS SEQ_075 = 75; -- Go to the ARM and BSM Guilds. Talk to Bodenolf. SEQ_080 = 80; -- Speak with H'naanza -SEQ_085 = 85; -- Speak with Bodenolf +SEQ_085 = 85; -- Walk into push trigger SEQ_090 = 90; -- Contact Baderon on LS SEQ_092 = 92; -- Return to Baderon. @@ -91,7 +91,7 @@ JOELLAUT = 1000163; WERNER = 1000247; HIHINE = 1000267; TRINNE = 1000268; -ECHO_EXIT_TRIGGER2 = 1090001; +ECHO_EXIT_TRIGGER2 = 1090007; -- Quest Markers diff --git a/Data/scripts/quests/man/man1l0.lua b/Data/scripts/quests/man/man1l0.lua new file mode 100644 index 00000000..2dd98fa8 --- /dev/null +++ b/Data/scripts/quests/man/man1l0.lua @@ -0,0 +1,398 @@ +require("global"); + +--[[ + +Quest Script + +Name: Legends Adrift +Code: Man1l0 +Id: 110003 +Prereq: Treasures of the Main (Man0l1 - 110002) + +]] + +-- Sequence Numbers +SEQ_000 = 0; -- Echo intance with Y'shtola, Baderon, Etc. Talk to Y'shtola. +SEQ_010 = 10; -- Echo instance, talk with Baderon. +SEQ_020 = 20; -- Head to MRD guild and talk to Waekbyrt. +SEQ_030 = 30; -- Head down the Astalicia to the push trigger. +SEQ_040 = 40; -- Head up the Astalicia to the push trigger. +SEQ_050 = 50; -- Contact Baderon on the Link Pearl. +SEQ_060 = 60; -- Head to the FSH guild and push the trigger. +SEQ_070 = 70; -- Head to a spot in Lower La Noscea. +SEQ_080 = 80; -- Contact Baderon on the Link Pearl. +SEQ_090 = 90; -- Speak to P'tahjha at the ACN guild. +SEQ_100 = 100; -- Echo instance, head downstairs to push a trigger and cutscene. +SEQ_110 = 110; -- Echo instance still, head upstairs to trigger a cutscene. +SEQ_120 = 120; -- Contact Baderon on the Link Pearl. +SEQ_122 = 122; -- Head back to Baderon to finish the quest. + +-- Quest Actors +BADERON = 1000137; +YSHTOLA = 1000001; + +-- ADV Guild Echo +ADVENTURER = 1000101; +WHISPERING_ADVENTURER = 1000102; +UNAPPROACHABLE_ADVENTURER = 1000103; +FISH_SMELLING_ADVENTURER = 1000104; +SPEAR_WIELDING_ADVENTURER = 1000105; +TRIGGER_ADVGUILD = 1090080; + +-- MRD Guild Echo +WAEKBYRT = 1000003; +HULKING_CUDA_KNIGHT = 1000182; +SOPHISTICATED_CUDA_KNIGHT = 1000108; +FRIGHTENED_CUDA_KNIGHT = 1000110; +ZEALOUS_PIRATE = 1000112; +ENRAGED_PIRATE = 1000113; +TRIGGER_MRD = 1090081; + +-- MRD Guild Echo 2 +DISGRUNTLED_PIRATE = 1000087; +PINE_SCENTED_PIRATE = 1000088; +BARITONE_PIRATE = 1000089; +BAYARD = 1000190; + +-- FSH Guild Sequences +NNMULIKA = 1000153; +SISIPU = 1000156; +TRIGGER_FSH = 1090006; +TRIGGER_SEAFLD = 1090082; + +-- ACN Guild Echo +ASSESSOR = 1000121; +PTAHJHA = 1000150; +HALDBERK = 1000160; +LILINA = 1000178; +DODOROBA = 1000196; +IVAN = 1000197; +MERODAULYN = 1000008; +COQUETTISH_PIRATE = 1000868; +VOLUPTUOUS_PIRATE = 1000115; +PEACOCKISH_PIRATE = 1000118; +TRIGGER_ACN_LOWER = 1090083; +TRIGGER_ACN_UPPER = 1090084; + +-- Quest Markers +MRKR_TRIGGER_FSH = 11000306; +MRKR_TRIGGER_SEAFLD = 11000307; +MRKR_TRIGGER_ANC_LOWER = 11000308; + +-- Msg packs for the Npc LS +NPCLS_MSGS = { + {57, 58, 59}, -- SEQ_050 + {92, 93, 94}, -- SEQ_070 + {140, 141} -- SEQ_120 +}; + +function onStart(player, quest) + quest:StartSequence(SEQ_000); + GetWorldManager():WarpToPrivateArea(player, "PrivateAreaMasterPast", 3, -430.55, 40.2, 185.41, 1.89); +end + +function onFinish(player, quest) +end + +function onStateChange(player, quest, sequence) + local data = quest:GetData(); + if (sequence == SEQ_ACCEPT) then + quest:SetENpc(BADERON, QFLAG_PLATE); + elseif (sequence == SEQ_000) then + quest:SetENpc(BADERON); + quest:SetENpc(ADVENTURER); + quest:SetENpc(WHISPERING_ADVENTURER); + quest:SetENpc(UNAPPROACHABLE_ADVENTURER); + quest:SetENpc(FISH_SMELLING_ADVENTURER); + quest:SetENpc(SPEAR_WIELDING_ADVENTURER); + quest:SetENpc(TRIGGER_ADVGUILD, QFLAG_MAP, false, true); + elseif (sequence == SEQ_010) then + quest:SetENpc(BADERON, QFLAG_PLATE); + quest:SetENpc(ADVENTURER); + quest:SetENpc(WHISPERING_ADVENTURER); + quest:SetENpc(UNAPPROACHABLE_ADVENTURER); + quest:SetENpc(FISH_SMELLING_ADVENTURER); + quest:SetENpc(SPEAR_WIELDING_ADVENTURER); + quest:SetENpc(YSHTOLA); + elseif (sequence == SEQ_020) then + quest:SetENpc(WAEKBYRT, QFLAG_PLATE); + quest:SetENpc(BADERON); + elseif (sequence == SEQ_030) then + quest:SetENpc(TRIGGER_MRD, QFLAG_MAP, false, true); + quest:SetENpc(HULKING_CUDA_KNIGHT); + quest:SetENpc(SOPHISTICATED_CUDA_KNIGHT); + quest:SetENpc(FRIGHTENED_CUDA_KNIGHT); + quest:SetENpc(ZEALOUS_PIRATE); + quest:SetENpc(ENRAGED_PIRATE); + quest:SetENpc(WAEKBYRT); + elseif (sequence == SEQ_040) then + quest:SetENpc(TRIGGER_MRD, QFLAG_MAP, false, true); + quest:SetENpc(PINE_SCENTED_PIRATE); + quest:SetENpc(BARITONE_PIRATE); + quest:SetENpc(BAYARD); + quest:SetENpc(DISGRUNTLED_PIRATE); + elseif (sequence == SEQ_060) then + quest:SetENpc(TRIGGER_FSH, QFLAG_MAP, false, true); + quest:SetENpc(BADERON); + elseif (sequence == SEQ_070) then + quest:SetENpc(TRIGGER_SEAFLD, QFLAG_MAP, false, true); + quest:SetENpc(NNMULIKA); + elseif (sequence == SEQ_090) then + quest:SetENpc(PTAHJHA, QFLAG_PLATE); + elseif (sequence == SEQ_100) then + quest:SetENpc(TRIGGER_ACN_LOWER, QFLAG_MAP, false, true); + quest:SetENpc(ASSESSOR); + quest:SetENpc(HALDBERK); + quest:SetENpc(LILINA); + quest:SetENpc(VOLUPTUOUS_PIRATE); + quest:SetENpc(PEACOCKISH_PIRATE); + quest:SetENpc(MERODAULYN); + quest:SetENpc(COQUETTISH_PIRATE); + quest:SetENpc(IVAN); + elseif (sequence == SEQ_110) then + quest:SetENpc(TRIGGER_ACN_UPPER, QFLAG_MAP, false, true); + elseif (sequence == SEQ_122) then + quest:SetENpc(BADERON, QFLAG_REWARD); + end + +end + +function onTalk(player, quest, npc) + local sequence = quest:getSequence(); + local classId = npc:GetActorClassId(); + + if (sequence == SEQ_ACCEPT) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200"); + player:EndEvent(); + player:AcceptQuest(quest, true); + return; + end + elseif (sequence == SEQ_000) then + seq000_010_onTalk(player, quest, npc, classId); + elseif (sequence == SEQ_010) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent215"); + player:EndEvent(); + quest:StartSequence(SEQ_020); + GetWorldManager():WarpToPublicArea(player); + return; + elseif (classId == YSHTOLA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_8"); + else + seq000_010_onTalk(player, quest, npc, classId); + end + elseif (sequence == SEQ_020) then + if (classId == WAEKBYRT) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400"); + quest:StartSequence(SEQ_030); + player:EndEvent(); + GetWorldManager():WarpToPrivateArea(player, "PrivateAreaMasterPast", 6, -754.03, 7.352, 382.872, 3.133); + return; + elseif (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent215_2"); + end + elseif (sequence == SEQ_030 or sequence == SEQ_040) then + seq000_030_040_onTalk(player, quest, npc, classId) + elseif (sequence == SEQ_060) then + if (classId == NNMULIKA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent600"); + elseif (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent420_2"); + end + elseif (sequence == SEQ_070) then + if (classId == NNMULIKA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent600_2"); + end + elseif (sequence == SEQ_090) then + if (classId == PTAHJHA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000"); + quest:StartSequence(SEQ_100); + player:EndEvent(); + GetWorldManager():WarpToPrivateArea(player, "PrivateAreaMasterPast", 7); + elseif (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent610_2"); + end + elseif (sequence == SEQ_100) then + seq000_100_onTalk(player, quest, npc, classId) + elseif (sequence == SEQ_110) then + elseif (sequence == SEQ_122) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEventComplete"); + callClientFunction(player, "delegateEvent", player, quest, "sqrwa", 300, 1, 1, 2); + player:EndEvent(); + player:CompleteQuest(quest); + return; + end + end + + player:EndEvent(); + quest:UpdateENPCs(); +end + +function seq000_010_onTalk(player, quest, npc, classId) + if (classId == ADVENTURER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_2"); + elseif (classId == WHISPERING_ADVENTURER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_3"); + elseif (classId == UNAPPROACHABLE_ADVENTURER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_4"); + elseif (classId == FISH_SMELLING_ADVENTURER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_5"); + elseif (classId == SPEAR_WIELDING_ADVENTURER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_6"); + elseif (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent200_7"); + end +end + +function seq000_030_040_onTalk(player, quest, npc, classId) + if (classId == HULKING_CUDA_KNIGHT) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_2"); + elseif (classId == SOPHISTICATED_CUDA_KNIGHT) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_3"); + elseif (classId == FRIGHTENED_CUDA_KNIGHT) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_4"); + elseif (classId == ZEALOUS_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_5"); + elseif (classId == ENRAGED_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_6"); + elseif (classId == WAEKBYRT) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent400_7"); + elseif (classId == PINE_SCENTED_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent410_2"); + elseif (classId == BARITONE_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent410_3"); + elseif (classId == BAYARD) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent410_4"); + elseif (classId == DISGRUNTLED_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent410_5"); + end +end + +function seq000_100_onTalk(player, quest, npc, classId) + if (classId == ASSESSOR) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_2"); + elseif (classId == 0) then -- !!!MISSING DIALOG OWNER!!! + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_3"); + elseif (classId == HALDBERK) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_4"); + elseif (classId == LILINA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_5"); + elseif (classId == VOLUPTUOUS_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_6"); + elseif (classId == PEACOCKISH_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_7"); + elseif (classId == MERODAULYN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_8"); + elseif (classId == COQUETTISH_PIRATE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_9"); + elseif (classId == 0) then -- !!!MISSING DIALOG OWNER!!! + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_10"); + elseif (classId == 0) then -- !!!MISSING DIALOG OWNER!!! + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_11"); + elseif (classId == IVAN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2000_12"); + end +end + +function onPush(player, quest, npc) + local data = quest:GetData(); + local sequence = quest:getSequence(); + local classId = npc:GetActorClassId(); + + if (sequence == SEQ_000) then + if (classId == TRIGGER_ADVGUILD) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent210"); + quest:StartSequence(SEQ_010); + end + elseif (sequence == SEQ_030) then + if (classId == TRIGGER_MRD) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent410"); + quest:StartSequence(SEQ_040); + player:EndEvent(); + GetWorldManager():WarpToPosition(player, -764.519, -3.146, 384.154, 1.575); + return; + end + elseif (sequence == SEQ_040) then + if (classId == TRIGGER_MRD) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent420"); + quest:NewNpcLsMsg(1); + quest:StartSequence(SEQ_050); + player:EndEvent(); + GetWorldManager():WarpToPublicArea(player); + return; + end + elseif (sequence == SEQ_060) then + if (classId == TRIGGER_FSH) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent600"); + quest:StartSequence(SEQ_070); + end + elseif (sequence == SEQ_070) then + if (classId == TRIGGER_SEAFLD) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent610"); + quest:NewNpcLsMsg(1); + quest:StartSequence(SEQ_080); + end + elseif (sequence == SEQ_100) then + if (classId == TRIGGER_ACN_LOWER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2001"); + quest:StartSequence(SEQ_110); + player:EndEvent(); + GetWorldManager():WarpToPosition(player, -785.938, -0.62, 189.044, 3.09); + return; + end + elseif (sequence == SEQ_110) then + if (classId == TRIGGER_ACN_UPPER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent2002"); + quest:NewNpcLsMsg(1); + quest:StartSequence(SEQ_120); + player:EndEvent(); + GetWorldManager():WarpToPublicArea(player); + return; + end + end + + player:EndEvent(); + quest:UpdateENPCs(); +end + +function onNpcLS(player, quest, from, msgStep) + local sequence = quest:getSequence(); + local msgPack; + + if (from == 1) then + -- Get the right msg pack + if (sequence == SEQ_050 or sequence == SEQ_060) then + msgPack = 1; + elseif (sequence == SEQ_080 or sequence == SEQ_090) then + msgPack = 2; + elseif (sequence == SEQ_120 or sequence == SEQ_122) then + msgPack = 3; + end + + -- Quick way to handle all msgs nicely. + player:SendGameMessageLocalizedDisplayName(quest, NPCLS_MSGS[msgPack][msgStep], MESSAGE_TYPE_NPC_LINKSHELL, 1000015); + if (msgStep >= #NPCLS_MSGS[msgPack]) then + quest:EndOfNpcLsMsgs(); + else + quest:ReadNpcLsMsg(); + end + + -- Handle anything else + if (sequence == SEQ_050) then + quest:StartSequenceForNpcLs(SEQ_060); + elseif (sequence == SEQ_080) then + quest:StartSequenceForNpcLs(SEQ_090); + elseif (sequence == SEQ_120) then + quest:StartSequenceForNpcLs(SEQ_122); + end + end + + player:EndEvent(); +end + +function getJournalMapMarkerList(player, quest) + local sequence = quest:getSequence(); + +end \ No newline at end of file diff --git a/Data/scripts/quests/man/man2l0.lua b/Data/scripts/quests/man/man2l0.lua index f7eee0e1..d8d0b50b 100644 --- a/Data/scripts/quests/man/man2l0.lua +++ b/Data/scripts/quests/man/man2l0.lua @@ -1,4 +1,52 @@ -require("global") +require("global"); + +--[[ + +Quest Script + +Name: Never the Twain Shall Meet +Code: Man2l0 +Id: 110004 +Prereq: Legends Adrift (Man1l0 - 110003) + +]] + +-- Sequence Numbers +SEQ_000 = 0; -- Talk to Captain Hob. +SEQ_010 = 10; -- Ship instance, enter the hold. +SEQ_015 = 15; -- Exit the hold, go back upstairs. +SEQ_020 = 20; -- Duty, fight Emerick and Merodaulyn +SEQ_035 = 35; -- Head to Baderon and chat. +SEQ_037 = 37; -- Head to outcrop in La Noscea. +SEQ_040 = 40; -- Talk to Baderon on the Link Pearl +SEQ_042 = 42; -- Enter and push at the MSK guild. +SEQ_045 = 45; -- Talk to Isaudorel +SEQ_050 = 50; -- Head to God's Grip push, talk with Blackburn. +SEQ_055 = 55; -- Continue to the other push with Y'shtola in the subecho. +SEQ_060 = 60; -- Unused? Talks about spying Stahlmann, Emerick, and Merod scheming. +SEQ_065 = 65; -- Unused? Talks about the meteor shower and the Ascian stealing the key. +SEQ_070 = 70; -- Unused? Talks about heading to Ul'dah + +-- Quest Actors +BADERON = 1000137; +YSHTOLA = 1000001; +HOB = 1000151; +ISAUDOREL = 1000152; +BARRACUDA_KNIGHT1 = 1000183; +BARRACUDA_KNIGHT2 = 1000184; +TRIGGER_SHIP1 = 1090003; +TRIGGER_SHIP2 = 1090003; +TRIGGER_MSK = 1090003; +TRIGGER_SEAFLD1 = 1090003; +TRIGGER_SEAFLD2 = 1090003; +TRIGGER_SEAFLD3 = 1090003; + +-- Quest Markers + +-- Msg packs for the Npc LS +NPCLS_MSGS = { + {40, 41} -- SEQ_040 +}; function onStart(player, quest) quest:StartSequence(SEQ_000); @@ -7,29 +55,169 @@ end function onFinish(player, quest) end -function onStateChange(player, quest, seqNum) +function onStateChange(player, quest, sequence) + local data = quest:GetData(); + + if (sequence == SEQ_ACCEPT) then + quest:SetENpc(BADERON, QFLAG_PLATE); + elseif (sequence == SEQ_000) then + quest:SetENpc(HOB, QFLAG_PLATE); + quest:SetENpc(BADERON); + elseif (sequence == SEQ_010) then + quest:SetENpc(HOB); + quest:SetENpc(BARRACUDA_KNIGHT1); + quest:SetENpc(BARRACUDA_KNIGHT2); + elseif (sequence == SEQ_015) then + quest:SetENpc(HOB); + quest:SetENpc(BARRACUDA_KNIGHT1); + quest:SetENpc(BARRACUDA_KNIGHT2); + elseif (sequence == SEQ_020) then + -- DUTY HAPPENS HERE + elseif (sequence == SEQ_035) then + quest:SetENpc(BADERON, QFLAG_PLATE); + elseif (sequence == SEQ_037) then + quest:SetENpc(BADERON); + elseif (sequence == SEQ_040) then + elseif (sequence == SEQ_042) then + quest:SetENpc(BADERON); + elseif (sequence == SEQ_045) then + quest:SetENpc(ISAUDOREL, QFLAG_PLATE); + elseif (sequence == SEQ_050) then + elseif (sequence == SEQ_055) then + quest:SetENpc(YSHTOLA); + end + end function onTalk(player, quest, npc) local sequence = quest:getSequence(); local classId = npc:GetActorClassId(); - - -end -function onEmote(player, quest, npc, emote) + if (sequence == SEQ_ACCEPT) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000"); + end + elseif (sequence == SEQ_000) then + if (classId == HOB) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent010"); + quest:StartSequence(SEQ_010); + elseif (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_2"); + end + elseif (sequence == SEQ_010) then + if (classId == HOB) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_2"); + elseif (classId == BARRACUDA_KNIGHT1) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_3"); + elseif (classId == BARRACUDA_KNIGHT2) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_4"); + end + elseif (sequence == SEQ_015) then + if (classId == HOB) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_2"); + elseif (classId == BARRACUDA_KNIGHT1) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_3"); + elseif (classId == BARRACUDA_KNIGHT2) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent011_4"); + end + elseif (sequence == SEQ_035) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent050"); + quest:StartSequence(SEQ_037); + end + elseif (sequence == SEQ_037) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent050_2"); + end + elseif (sequence == SEQ_042) then + if (classId == BADERON) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent060_2"); + end + elseif (sequence == SEQ_045) then + if (classId == ISAUDOREL) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent075"); + quest:StartSequence(SEQ_050); + end + elseif (sequence == SEQ_055) then + if (classId == YSHTOLA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent080_2"); + end + end + + player:EndEvent(); + quest:UpdateENPCs(); end function onPush(player, quest, npc) + local data = quest:GetData(); + local sequence = quest:getSequence(); + local classId = npc:GetActorClassId(); + + if (sequence == SEQ_037) then + if (classId == TRIGGER_SEAFLD1) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent060"); + quest:StartSequence(SEQ_040); + end + elseif (sequence == SEQ_042) then + if (classId == TRIGGER_MSK) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent070"); + quest:StartSequence(SEQ_045); + end + elseif (sequence == SEQ_050) then + if (classId == TRIGGER_SEAFLD2) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent080"); + quest:StartSequence(SEQ_055); + end + elseif (sequence == SEQ_055) then + if (classId == TRIGGER_SEAFLD3) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent081"); + end + end + + player:EndEvent(); + quest:UpdateENPCs(); end -function onNotice(player, quest, npc) +function onNotice(player, quest, target) + callClientFunction(player, "delegateEvent", player, quest, "sqrwa", 300, 1, 1, 2); + player:CompleteQuest(quest); + callClientFunction(player, "delegateEvent", player, quest, "processEvent081_2", 1); + player:EndEvent(); + quest:UpdateENPCs(); +end + +function onNpcLS(player, quest, from, msgStep) + local sequence = quest:getSequence(); + local msgPack; + + if (from == 1) then + -- Get the right msg pack + if (sequence == SEQ_040 or sequence == SEQ_042) then + msgPack = 1; + end + + -- Quick way to handle all msgs nicely. + player:SendGameMessageLocalizedDisplayName(quest, NPCLS_MSGS[msgPack][msgStep], MESSAGE_TYPE_NPC_LINKSHELL, 1000015); + if (msgStep >= #NPCLS_MSGS[msgPack]) then + quest:EndOfNpcLsMsgs(); + else + quest:ReadNpcLsMsg(); + end + + -- Handle anything else + if (sequence == SEQ_040) then + quest:StartSequenceForNpcLs(SEQ_042); + end + end + + player:EndEvent(); end function getJournalInformation(player, quest) - return {}; + return 40, 40, 40; end function getJournalMapMarkerList(player, quest) - return 11000105, 11000106; + local sequence = quest:getSequence(); + end \ No newline at end of file diff --git a/Map Server/Actors/Actor.cs b/Map Server/Actors/Actor.cs index b2550bf0..8ddb41bf 100644 --- a/Map Server/Actors/Actor.cs +++ b/Map Server/Actors/Actor.cs @@ -642,7 +642,7 @@ namespace Meteor.Map.Actors return new Vector3(positionX, positionY, positionZ); } - public void SetPos(float x, float y, float z, float rot = 0, bool instant = false) + public void SetPos(float x, float y, float z, float rot = 0, bool instant = false, Player player = null) { oldPositionX = positionX; oldPositionY = positionY; @@ -657,8 +657,9 @@ namespace Meteor.Map.Actors // todo: handle zone? if (instant) { - CurrentArea.BroadcastPacketAroundPoint(oldPositionX, oldPositionY, CreateSpawnTeleportPacket(0)); - CurrentArea.BroadcastPacketAroundPoint(positionX, positionY, CreateSpawnTeleportPacket(0)); + player.QueuePacket(CreateSpawnTeleportPacket(0)); + //CurrentArea.BroadcastPacketAroundPoint(oldPositionX, oldPositionY, CreateSpawnTeleportPacket(0)); + //CurrentArea.BroadcastPacketAroundPoint(positionX, positionY, CreateSpawnTeleportPacket(0)); } else CurrentArea.BroadcastPacketAroundActor(this, MoveActorToPositionPacket.BuildPacket(Id, x, y, z, rot, moveState)); diff --git a/Map Server/Actors/Area/PrivateArea.cs b/Map Server/Actors/Area/PrivateArea.cs index 2155229f..ddf42ce2 100644 --- a/Map Server/Actors/Area/PrivateArea.cs +++ b/Map Server/Actors/Area/PrivateArea.cs @@ -33,9 +33,10 @@ namespace Meteor.Map.actors.area private readonly Zone ParentZone; private readonly string PrivateAreaName; private readonly int PrivateAreaType; + private readonly bool CanExitArea; - public PrivateArea(Zone parent, string classPath, string privateAreaName, int privateAreaType, ushort bgmDay, ushort bgmNight, ushort bgmBattle) - : base(parent.ZoneId, parent.ZoneName, parent.RegionId, classPath, bgmDay, bgmNight, bgmBattle, parent.isIsolated, parent.isInn, parent.canRideChocobo, parent.canStealth, true) + public PrivateArea(Zone parent, string classPath, string privateAreaName, int privateAreaType, bool canExitArea, ushort music) + : base(parent.ZoneId, parent.ZoneName, parent.RegionId, classPath, music, music, music, parent.isIsolated, parent.isInn, parent.canRideChocobo, parent.canStealth, true) { this.ParentZone = parent; this.PrivateAreaName = privateAreaName; @@ -52,6 +53,11 @@ namespace Meteor.Map.actors.area return PrivateAreaType; } + public bool CanExitPrivateArea() + { + return CanExitArea; + } + public override bool IsPublic() { return false; diff --git a/Map Server/Actors/Area/PrivateAreaContent.cs b/Map Server/Actors/Area/PrivateAreaContent.cs index 8639d6d1..38d930fc 100644 --- a/Map Server/Actors/Area/PrivateAreaContent.cs +++ b/Map Server/Actors/Area/PrivateAreaContent.cs @@ -38,7 +38,7 @@ namespace Meteor.Map.actors.area } public PrivateAreaContent(Zone parent, string classPath, string privateAreaName, int privateAreaType, Director director, Player contentStarter) //TODO: Make it a list - : base(parent, classPath, privateAreaName, privateAreaType, 0, 0, 0) + : base(parent, classPath, privateAreaName, privateAreaType, false, 0) { currentDirector = director; LuaEngine.GetInstance().CallLuaFunction(contentStarter, this, "onCreate", false, currentDirector); diff --git a/Map Server/Actors/Chara/Player/Player.cs b/Map Server/Actors/Chara/Player/Player.cs index 2f0b74ad..b0359bd4 100644 --- a/Map Server/Actors/Chara/Player/Player.cs +++ b/Map Server/Actors/Chara/Player/Player.cs @@ -1840,18 +1840,27 @@ namespace Meteor.Map.Actors public Quest[] GetQuestsForNpc(Npc npc) { - Quest[] quests = questStateManager.GetQuestsForNpc(npc); + Quest[] quests = questStateManager.GetQuestsForNpc(npc, CurrentArea.IsPrivate()); Array.Sort(quests, (q1, q2) => (q1.HasData() ? 1 : 0) - (q2.HasData() ? 1 : 0)); return quests; } + public void HandleBNpcKill(uint bnpcClassId) + { + foreach (Quest quest in questScenario) + { + if (quest != null) + quest.OnKillBNpc(this, bnpcClassId); + } + } + public bool HandleNpcLs(uint id) { foreach (Quest quest in questScenario) { if (quest != null && quest.HasNpcLsMsgs(id)) { - quest.OnNpcLS(this); + quest.OnNpcLs(this); return true; } } diff --git a/Map Server/Actors/Quest/Quest.cs b/Map Server/Actors/Quest/Quest.cs index a9a9884d..a69a0741 100644 --- a/Map Server/Actors/Quest/Quest.cs +++ b/Map Server/Actors/Quest/Quest.cs @@ -194,7 +194,12 @@ namespace Meteor.Map.Actors.QuestNS LuaEngine.GetInstance().CallLuaFunction(caller, this, "onNotice", true, triggerName); } - public void OnNpcLS(Player caller) + public void OnKillBNpc(Player caller, uint classId) + { + LuaEngine.GetInstance().CallLuaFunction(caller, this, "onKillBNpc", true, classId); + } + + public void OnNpcLs(Player caller) { LuaEngine.GetInstance().CallLuaFunction(caller, this, "onNpcLS", true, data.GetNpcLsFrom(), data.GetMsgStep()); } diff --git a/Map Server/Actors/Quest/QuestStateManager.cs b/Map Server/Actors/Quest/QuestStateManager.cs index 8dd2b8c3..1aa337da 100644 --- a/Map Server/Actors/Quest/QuestStateManager.cs +++ b/Map Server/Actors/Quest/QuestStateManager.cs @@ -151,9 +151,12 @@ namespace Meteor.Map.Actors.QuestNS return ActiveQuests.Find(quest => quest.GetQuestId() == id); } - public Quest[] GetQuestsForNpc(Npc npc) + public Quest[] GetQuestsForNpc(Npc npc, bool isPrivateArea) { - return ActiveQuests.FindAll(quest => quest.IsQuestENPC(player, npc)).ToArray(); + if (isPrivateArea) + return ActiveQuests.FindAll(quest => quest.IsQuestENPC(player, npc) && quest.GetSequence() != Quest.SEQ_NOT_STARTED).ToArray(); + else + return ActiveQuests.FindAll(quest => quest.IsQuestENPC(player, npc)).ToArray(); } public byte[] GetCompletionSliceBytes(ushort from, ushort to) diff --git a/Map Server/WorldManager.cs b/Map Server/WorldManager.cs index 70444f01..872fcd72 100644 --- a/Map Server/WorldManager.cs +++ b/Map Server/WorldManager.cs @@ -144,9 +144,8 @@ namespace Meteor.Map privateAreaName, privateAreaType, className, - dayMusic, - nightMusic, - battleMusic + canExitArea, + music FROM server_zones_privateareas WHERE privateAreaName IS NOT NULL"; @@ -161,7 +160,7 @@ namespace Meteor.Map if (zoneList.ContainsKey(parentZoneId)) { Zone parent = zoneList[parentZoneId]; - PrivateArea privArea = new PrivateArea(parent, reader.GetString("className"), reader.GetString("privateAreaName"), reader.GetInt32("privateAreaType"), reader.GetUInt16("dayMusic"), reader.GetUInt16("nightMusic"), reader.GetUInt16("battleMusic")); + PrivateArea privArea = new PrivateArea(parent, reader.GetString("className"), reader.GetString("privateAreaName"), reader.GetInt32("privateAreaType"), reader.GetBoolean("canExitArea"), reader.GetUInt16("music")); parent.AddPrivateArea(privArea); } else @@ -939,7 +938,7 @@ namespace Meteor.Map DoZoneChange(player, player.CurrentArea.ZoneId, null, 0, 15, x, y, z, rotation); } - public void WarpToPosition(Player player, float x, float y, float z, float rotation) + public void WarpToPosition(Player player, float x, float y, float z, float rotation, bool debugInstant = false) { //Remove player from currentZone if transfer else it's login if (player.CurrentArea != null) @@ -956,13 +955,18 @@ namespace Meteor.Map //Send packets player.playerSession.QueuePacket(_0xE2Packet.BuildPacket(player.Id, 0x10)); - player.playerSession.QueuePacket(player.CreateSpawnTeleportPacket(0)); + player.playerSession.QueuePacket(player.CreateSpawnTeleportPacket(debugInstant ? (ushort) 0x0 : (ushort) 0xF)); player.playerSession.LockUpdates(false); player.SendInstanceUpdate(); } } - + + public void WarpToCharaPosition(Player player, Character target) + { + WarpToPosition(player, target.positionX, target.positionY, target.positionZ, target.rotation); + } + //Moves actor to new zone, and sends packets to spawn at the given coords. public void DoZoneChangeContent(Player player, PrivateAreaContent contentArea, float spawnX, float spawnY, float spawnZ, float spawnRotation, ushort spawnType = SetActorPositionPacket.SPAWNTYPE_WARP_DUTY) {