1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-20 19:57:46 +00:00

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.

This commit is contained in:
Filip Maj 2022-03-11 02:17:46 -05:00
parent ebba56602c
commit 65ee91e49c
14 changed files with 864 additions and 38 deletions

View file

@ -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

View file

@ -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);

View file

@ -0,0 +1,140 @@
require("global");
properties = {
permissions = 0,
parameters = "ss",
description =
[[
Positions a targeted npc by some <distance>, defaults to 5 yalms.
!nudge |
!nudge <distance> |
!nudge <distance> <up/down> |
!nudge <distance> <left/right> |
!nudge <distance> <rotate> |
]],
}
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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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));

View file

@ -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;

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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());
}

View file

@ -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)

View file

@ -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)
{