diff --git a/Data/scripts/base/chara/npc/object/BgKeepout.lua b/Data/scripts/base/chara/npc/object/BgKeepout.lua index 08e2ea14..385410a7 100644 --- a/Data/scripts/base/chara/npc/object/BgKeepout.lua +++ b/Data/scripts/base/chara/npc/object/BgKeepout.lua @@ -1,5 +1,19 @@ require ("global") +--[[ + +BgKeepout Script + +Used to send a msg to the player that they cannot proceed passed this point. Invisible walls are +linked to this object. + +]] + function init(npc) return false, false, 0, 0; -end \ No newline at end of file +end + +function onEventStarted(player, npc, triggerName) + player:SendGameMessage(player, GetWorldMaster(), 60001, 0x20); + player:EndEvent(); +end diff --git a/Data/scripts/base/chara/npc/object/MarketEntrance.lua b/Data/scripts/base/chara/npc/object/MarketEntrance.lua index e157a777..23b45be6 100644 --- a/Data/scripts/base/chara/npc/object/MarketEntrance.lua +++ b/Data/scripts/base/chara/npc/object/MarketEntrance.lua @@ -82,15 +82,19 @@ city = { [1500394] = 3, -- Ul'dah : Edine } +ENTRANCE_LIMSA_DT = 1090238; -- Main Limsa Entrance +ENTRANCE_LIMSA_ALT = 1500392; -- M'septha Alternate +ENTRANCE_GRIDANIA_DT = 1090264; -- Main Grid Entrance +ENTRANCE_GRIDANIA_ALT = 1500393; -- Torsefers Alternate +ENTRANCE_ULDAH_DT = 1090265; -- Main Uldah Entrance +ENTRANCE_ULDAH_ALT = 1500394; -- Edine Alternate - -function onEventStarted(player, npc, triggerName) - +function onEventStarted(player, npc, eventType, eventName) local npcCity = city[npc:GetActorClassId()] or 1; - local wardPlaceName = CITY_INFO[npcCity][1]; -- Market Wards category name. Identical in all languages except Japanese + local marketPlaceName = CITY_INFO[npcCity][1]; -- Market Wards category name. Identical in all languages except Japanese local exitPlaceName = CITY_INFO[npcCity][2]; -- Central Limsa Lominsa / Heartstream / The Fronds local gcHQPlaceName = CITY_INFO[npcCity][3]; -- Maelstrom Command / Adders' Nest / Hall of Flames - local questAreaName = 0; --CITY_INFO[npcCity][4]; -- Sailors Ward / Peasants Ward / Merchants Ward + local questPlaceName = CITY_INFO[npcCity][4]; -- Sailors Ward / Peasants Ward / Merchants Ward local wardListStart = CITY_INFO[npcCity][5]; -- Starting id for the market wards local wardListCount = CITY_INFO[npcCity][6]; -- Amount of wards in the list local showItemSearchCounter = false; @@ -98,19 +102,30 @@ function onEventStarted(player, npc, triggerName) local worldMaster = GetWorldMaster(); local pos = player:GetPos(); - local currZone = pos[4]; + local currZone = pos[5]; + local currRegion = player.CurrentArea.RegionId; + local quests = player:GetQuestsForNpc(npc); - if (currZone == 133 or currZone == 230 or currZone == 155 or currZone == 206 or currZone == 175 or currZone == 209) then + -- City entrance specific stuff + if (currRegion == 101 or currRegion == 103 or currRegion == 104) then exitPlaceName = 0; -- If in city, hide city menu option - elseif (currZone == 232 or currZone == 234 or currZone == 233) then - gcHQPlaceName = 0; -- If in GC Office, hide office menu option + + -- If no quests attached to this entrence, don't show quest area + if (#quests == 0) then + questPlaceName = 0; + end + end + + -- If in GC Office, hide office menu option + if (currZone == 232 or currZone == 234 or currZone == 233) then + gcHQPlaceName = 0; end - choice = callClientFunction(player, "eventPushChoiceAreaOrQuest", exitPlaceName, wardPlaceName, gcHQPlaceName, questAreaName, showItemSearchCounter, itemSearchId); + choice = callClientFunction(player, "eventPushChoiceAreaOrQuest", exitPlaceName, marketPlaceName, gcHQPlaceName, questPlaceName, showItemSearchCounter, itemSearchId); while (true) do - if choice == wardPlaceName then -- Market Wards + if choice == marketPlaceName then -- Market Wards wardSelect = callClientFunction(player, "eventPushStepPrvMarket", wardListStart, wardListCount, 0); if wardSelect and (wardSelect >= wardListStart and wardSelect <= (wardListStart+wardListCount)) then @@ -139,11 +154,14 @@ function onEventStarted(player, npc, triggerName) wait(1); GetWorldManager():DoZoneChange(player, warp[1], nil, 0, 0x02, warp[2], warp[3], warp[4], warp[5]); break; + elseif (choice == 2095 or choice == 3095) then -- Quest + quests[1]:OnPush(player, npc, eventName); + return; elseif (choice == 0 or choice == -3) then -- Menu Closed break; end - choice = callClientFunction(player, "eventPushChoiceAreaOrQuest", exitPlaceName, wardPlaceName, gcHQPlaceName, questAreaName, showItemSearchCounter, itemSearchId); + choice = callClientFunction(player, "eventPushChoiceAreaOrQuest", exitPlaceName, marketPlaceName, gcHQPlaceName, questAreaName, showItemSearchCounter, itemSearchId); end diff --git a/Data/scripts/base/chara/npc/object/ObjectEventDoor.lua b/Data/scripts/base/chara/npc/object/ObjectEventDoor.lua index 8118453b..a8af6c98 100644 --- a/Data/scripts/base/chara/npc/object/ObjectEventDoor.lua +++ b/Data/scripts/base/chara/npc/object/ObjectEventDoor.lua @@ -15,10 +15,37 @@ function init(npc) end function onEventStarted(player, npc, triggerName) - choice = callClientFunction(player, "eventDoorMoveAsk"); + local choice = callClientFunction(player, "eventDoorMoveAsk"); if (choice == 1) then + local activeQuests = player:GetQuestsForNpc(npc); + -- Either let the player choose the quest or start it if it's the only one. + local chosenQuest; + if (#activeQuests > 1) then + local currentPage = 0; + local numPages = math.floor((#activeQuests-1)/4) + 1; + + while (true) do + local page, index = callClientFunction(player, "switchEvent", activeQuests[currentPage * 4 + 1], activeQuests[currentPage * 4 + 2], possibleQuests[currentPage * 4 + 3], possibleQuests[currentPage * 4 + 4], currentPage + 1, numPages, 0x3F1); + + if (page == 0) then + chosenQuest = activeQuests[(currentPage * 4) + index]; + break; + elseif (page > 0) then + currentPage = page - 1; + else + player:EndEvent(); + return; + end + end + elseif (#activeQuests == 1) then + chosenQuest = activeQuests[1]; + end + if (chosenQuest ~= nil) then + chosenQuest:OnPush(player, npc, eventName); + return; + end end player:EndEvent(); diff --git a/Data/scripts/quests/man/man200.lua b/Data/scripts/quests/man/man200.lua new file mode 100644 index 00000000..dd4ebed9 --- /dev/null +++ b/Data/scripts/quests/man/man200.lua @@ -0,0 +1,376 @@ +require("global"); + +--[[ + +Quest Script + +Name: Fade to White +Code: Man200 +Id: 110013 +Prereq: Etc2l0 or Etc2g0 or Etc2u0. Level 18, any class. + +]] + +-- Sequence Numbers +SEQ_000 = 0; -- Go to the event door +SEQ_005 = 5; -- Talk to Minfilia to use the echo on her. +SEQ_010 = 10; -- Talk to Minfilia to join the Path of the Twelve. +SEQ_020 = 20; -- Path companion selection sequence. +SEQ_025 = 25; -- Wait for the linkpearl message. +SEQ_027 = 27; -- Pray return to the Waking Sands + +-- Quest Actors +MINFILIA = 1000843; +TATARU = 1001046; +SATZFLOH = 1001228; +PERCEVAINS = 1001229; +UNA_TAYUUN = 1001230; +ROUGH_SPOKEN_FELLOW = 1001274; +RED_SHOED_RASCAL = 1001275; +ABSTRACTED_GLADIATOR = 1001276; +CHAPEAUED_CHAP = 1001277; +BARRATROUS_BUCCANEER = 1001278; +SOFTHEARTED_SEPTUAGEN = 1001279; +INDIGO_EYED_ARCHER = 1001280; +LOAM_SCENETED_LADY = 1001281; +UNCOMFORTABLE_BRUTE = 1001282; +SAHJA_ZHWAN = 1001373; +NENEKANI = 1001374; +GODFREY = 1001375; +FENANA = 1001376; +NONORU = 1001377; +SERANELIEN = 1001378; + +SNPC_START = 1070001; +SNPC_END = 1070166; + +MARKET_ENTRENCE = 1090265; +EVENT_DOOR_EXIT = 1090160; +EVENT_DOOR_OFFICE_W = 1090161; +EVENT_DOOR_OFFICE_E = 1090162; + +MOMODI = 1000841; + +-- Quest Markers +MRKR_STEP1 = 11001301; +MRKR_STEP2 = 11001302; +MRKR_STEP3 = 11001303; +MRKR_STEP4 = 11001304; +MRKR_STEP5 = 11001305; +MRKR_STEP6 = 11001306; + +-- Quest Flags +FLAG_VISITED = 0; +FLAG_TALKED_TATARU = 1; +FLAG_DUTY_COMPLETE = 2; + +function onStart(player, quest) + quest:StartSequence(SEQ_000); +end + +function onFinish(player, quest) +end + +function onStateChange(player, quest, sequence) + local data = quest:GetData(); + + -- Sequence changing ENpcs + if (sequence == SEQ_000) then + quest:SetENpc(EVENT_DOOR_OFFICE_W, QFLAG_MAP, false, true); + quest:SetENpc(TATARU); + elseif (sequence == SEQ_005) then + quest:SetENpc(MINFILIA, QFLAG_PLATE); + quest:SetENpc(EVENT_DOOR_OFFICE_W, QFLAG_MAP, false, true); + quest:SetENpc(EVENT_DOOR_OFFICE_E, QFLAG_NONE, false, true); + quest:SetENpc(TATARU); + elseif (sequence == SEQ_010) then + quest:SetENpc(MINFILIA, QFLAG_PLATE); + quest:SetENpc(EVENT_DOOR_OFFICE_W, QFLAG_MAP, false, true); + quest:SetENpc(EVENT_DOOR_OFFICE_E, QFLAG_NONE, false, true); + quest:SetENpc(TATARU); + elseif (sequence == SEQ_020) then + quest:SetENpc(TATARU, QFLAG_PLATE); + quest:SetENpc(MINFILIA); + elseif (sequence == SEQ_025) then + quest:SetENpc(TATARU); + elseif (sequence == SEQ_027) then + if (quest:GetData():GetFlag(FLAG_DUTY_COMPLETE)) then + quest:SetENpc(MOMODI, QFLAG_PLATE); + quest:SetENpc(TATARU); + else + quest:SetENpc(TATARU, QFLAG_PLATE); + quest:SetENpc(SNPC_START + player:GetSNpcSkin()); + end + end + + -- All the other ENpcs in the Waking Sands + quest:SetENpc(MARKET_ENTRENCE, QFLAG_NONE, false, true); + quest:SetENpc(EVENT_DOOR_EXIT, QFLAG_MAP, false, true); + quest:SetENpc(SATZFLOH); + quest:SetENpc(PERCEVAINS); + quest:SetENpc(UNA_TAYUUN); + quest:SetENpc(ROUGH_SPOKEN_FELLOW); + quest:SetENpc(RED_SHOED_RASCAL); + quest:SetENpc(ABSTRACTED_GLADIATOR); + quest:SetENpc(CHAPEAUED_CHAP); + quest:SetENpc(BARRATROUS_BUCCANEER); + quest:SetENpc(SOFTHEARTED_SEPTUAGEN); + quest:SetENpc(INDIGO_EYED_ARCHER); + quest:SetENpc(LOAM_SCENETED_LADY); + quest:SetENpc(UNCOMFORTABLE_BRUTE); + quest:SetENpc(SAHJA_ZHWAN); + quest:SetENpc(NENEKANI); + quest:SetENpc(GODFREY); + quest:SetENpc(FENANA); + quest:SetENpc(NONORU); + quest:SetENpc(SERANELIEN); + +end + +function onTalk(player, quest, npc) + local sequence = quest:getSequence(); + local classId = npc:GetActorClassId(); + + if (sequence == SEQ_000) then + if (classId == TATARU) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_2"); + end + elseif (sequence == SEQ_005) then + if (classId == MINFILIA) then + callClientFunction(player, "delegateEvent", player, quest, "pE20", "???", 1, 1, 1, player:GetInitialTown()); + quest:StartSequence(SEQ_010); + elseif (classId == TATARU) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_2"); + end + elseif (sequence == SEQ_010) then + if (classId == MINFILIA) then + local didJoinPoT = callClientFunction(player, "delegateEvent", player, quest, "pE25", "???", 1, 1, 1, player:GetInitialTown()); + if (didJoinPoT == 1) then + player:EndEvent(); + GetWorldManager():WarpToPosition(player, -142.75, 1, -160, -1.6); + quest:StartSequence(SEQ_020); + return; + end + elseif (classId == TATARU) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_2"); + end + elseif (sequence == SEQ_020) then + if (classId == TATARU) then + -- Selecting a Path Companion + local sNpcActorClassId, sNpcPersonality = callClientFunction(player, "delegateEvent", player, quest, "processSnpcSelect", "???", 1, 1, 1, player:GetInitialTown()); + if (sNpcActorClassId != -1 and sNpcPersonality != -1) then + player:SetSNpc("???", sNpcActorClassId, sNpcPersonality); + player:AddNpcLs(6); + quest:StartSequence(SEQ_025); + end + player:EndEvent(); + return; + end + elseif (sequence == SEQ_025) then + if (classId == TATARU) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent040_2"); + quest:NewNpcLsMsg(6); + end + elseif (sequence == SEQ_027) then + if (classId == MOMODI) then + callClientFunction(player, "delegateEvent", player, quest, "pE25", "???", 1, 1, 1, player:GetInitialTown()); + elseif (classId == TATARU) then + if (not quest:GetData():GetFlag(FLAG_DUTY_COMPLETE)) then + startMan20001Content(player, quest); + return; + else + callClientFunction(player, "delegateEvent", player, quest, "pE050_2", player:GetSNpcNickname(), player:GetSNpcSkin(), player:GetSNpcPersonality(), player:GetSNpcCoordinate(), player:GetInitialTown()); + end + elseif (classId > SNPC_START and classId < SNPC_END) then + local name = callClientFunction(player, "delegateEvent", player, quest, "pEN", player:GetSNpcPersonality()); + if (not name == nil) then + player:SetSNpc(name, player:GetSNpcSkin(), player:GetSNpcPersonality()); + --player:GetDirector("QuestEventMan20001"):SetData("NameSet", true); + end + end + end + + -- Other ENpcs + if (sequence < SEQ_010) then + seq000_onTalkOtherNpcs(player, quest, npc); + else + seq010_onTalkOtherNpcs(player, quest, npc); + end + + -- Regardless of sequence + if (classId == EVENT_DOOR_OFFICE) then + elseif (classId == EVENT_DOOR_EXIT) then + elseif (classId == NONORU) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_8"); + elseif (classId == CHAPEAUED_CHAP) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_9"); + elseif (classId == ROUGH_SPOKEN_FELLOW) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_10"); + elseif (classId == SATZFLOH) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_18"); + elseif (classId == PERCEVAINS) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_19"); + elseif (classId == UNA_TAYUUN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_20"); + end + + player:EndEvent(); + quest:UpdateENPCs(); +end + +function seq000_onTalkOtherNpcs(player, quest, npc) + local classId = npc:GetActorClassId(); + + if (classId == SERANELIEN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_3"); + elseif (classId == SAHJA_ZHWAN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_4"); + elseif (classId == NENEKANI) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_5"); + elseif (classId == GODFREY) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_6"); + elseif (classId == FENANA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_7"); + elseif (classId == LOAM_SCENETED_LADY) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_11"); + elseif (classId == SOFTHEARTED_SEPTUAGEN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_12"); + elseif (classId == INDIGO_EYED_ARCHER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_13"); + elseif (classId == BARRATROUS_BUCCANEER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_14"); + elseif (classId == UNCOMFORTABLE_BRUTE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_15"); + elseif (classId == RED_SHOED_RASCAL) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_16"); + elseif (classId == ABSTRACTED_GLADIATOR) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent000_17"); + end +end + +function seq010_onTalkOtherNpcs(player, quest, npc) + local classId = npc:GetActorClassId(); + + if (classId == SERANELIEN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_3"); + elseif (classId == SAHJA_ZHWAN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_4"); + elseif (classId == NENEKANI) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_5"); + elseif (classId == GODFREY) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_6"); + elseif (classId == FENANA) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_7"); + elseif (classId == LOAM_SCENETED_LADY) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_8"); + elseif (classId == SOFTHEARTED_SEPTUAGEN) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_9"); + elseif (classId == INDIGO_EYED_ARCHER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_10"); + elseif (classId == BARRATROUS_BUCCANEER) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_11"); + elseif (classId == UNCOMFORTABLE_BRUTE) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_12"); + elseif (classId == RED_SHOED_RASCAL) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_13"); + elseif (classId == ABSTRACTED_GLADIATOR) then + callClientFunction(player, "delegateEvent", player, quest, "processEvent020_14"); + end +end + +function onPush(player, quest, npc) + local data = quest:GetData(); + local sequence = quest:getSequence(); + local classId = npc:GetActorClassId(); + + if (classId == MARKET_ENTRENCE) then + if (sequence == SEQ_000 and not data:GetFlag(FLAG_VISITED)) then + data:SetFlag(FLAG_VISITED); + callClientFunction(player, "delegateEvent", player, quest, "pE00", "???", 1, 1, 1, player:GetInitialTown()); + elseif (sequence == SEQ_000 and not data:GetFlag(FLAG_VISITED)) then + end + player:EndEvent(); + GetWorldManager():DoZoneChange(player, 181, nil, 0, 15, -205.25, 0, -160, 1.55); + return; + end + + if (classId == EVENT_DOOR_EXIT) then + player:EndEvent(); + GetWorldManager():DoZoneChange(player, 175, nil, 0, 15, -216.52, 190, 30.5, 2.32); + elseif (classId == EVENT_DOOR_OFFICE_W) then + if (sequence == SEQ_000) then + callClientFunction(player, "delegateEvent", player, quest, "pE10", "???", 1, 1, 1, player:GetInitialTown()); + quest:StartSequence(SEQ_005); + end + player:EndEvent(); + GetWorldManager():WarpToPosition(player, -126.2, 1.2, -160, 1.6); + return; + elseif (classId == EVENT_DOOR_OFFICE_E) then + player:EndEvent(); + GetWorldManager():WarpToPosition(player, -142.75, 1, -160, -1.6); + return; + end + + player:EndEvent(); + quest:UpdateENPCs(); +end + +function onNpcLS(player, quest, from, msgStep) + local sequence = quest:getSequence(); + + if (from == 6) then + player:SendGameMessageLocalizedDisplayName(quest, 435, MESSAGE_TYPE_NPC_LINKSHELL, 1500054); + quest:EndOfNpcLsMsgs(); + quest:StartSequence(SEQ_027); + end + + player:EndEvent(); +end + +function getJournalInformation(player, quest) + if (quest:GetData():GetFlag(FLAG_DUTY_COMPLETE)) then + return 1, 0, 0, 0, 0, player:GetSNpcNickname(); + end +end + +function getJournalMapMarkerList(player, quest) + local sequence = quest:getSequence(); + + if (sequence == SEQ_000) then + return MRKR_STEP1; + elseif (sequence == SEQ_005) then + return MRKR_STEP2; + elseif (sequence == SEQ_010) then + return MRKR_STEP3; + elseif (sequence == SEQ_020) then + return MRKR_STEP4; + elseif (sequence == SEQ_025) then + return MRKR_STEP5; + elseif (sequence == SEQ_027) then + return MRKR_STEP6; + end +end + +function startMan20001Content(player, quest) + local result = callClientFunction(player, "delegateEvent", player, quest, "contentsJoinAskInBasaClass"); + if (result == 1) then + callClientFunction(player, "delegateEvent", player, quest, "pE050", player:GetSNpcNickname(), player:GetSNpcSkin(), player:GetSNpcPersonality(), player:GetSNpcCoordinate(), player:GetInitialTown()); + -- DO NAMING DUTY HERE + -- For now just skip the sequence + player:EndEvent(); + GetWorldManager():DoZoneChange(player, 230, nil, 0, 0, -639.325, 1, 403.967, 1.655); + + local contentArea = player.CurrentArea:CreateContentArea(player, "/Area/PrivateArea/Content/PrivateAreaMasterSimpleContent", "man20001", "SimpleContent30002", "Quest/QuestDirectorEventMan20001"); + + if (contentArea == nil) then + return; + end + + director = contentArea:GetContentDirector(); + player:AddDirector(director); + director:StartDirector(false); + GetWorldManager():DoZoneChangeContent(player, contentArea, -200, 0 -160, -1.6, 2); + return; + end + player:EndEvent(); +end \ No newline at end of file diff --git a/Map Server/Actors/Chara/Player/Player.cs b/Map Server/Actors/Chara/Player/Player.cs index b0359bd4..a79367aa 100644 --- a/Map Server/Actors/Chara/Player/Player.cs +++ b/Map Server/Actors/Chara/Player/Player.cs @@ -167,6 +167,12 @@ namespace Meteor.Map.Actors private List ownedDirectors = new List(); private Director loginInitDirector = null; + //SNpc (Path Companion) + public string SNpcNickname { set; get; } + public byte SNpcSkin { set; get; } + public byte SNpcPersonality { set; get; } + public short SNpcCoordinate { set; get; } + List hotbarSlotsToUpdate = new List(); public PlayerWork playerWork = new PlayerWork(); @@ -270,6 +276,11 @@ namespace Meteor.Map.Actors charaWork.parameterTemp.tp = 0; + SNpcNickname = "???"; + SNpcSkin = 1; + SNpcPersonality = 1; + SNpcCoordinate = 1; + Database.LoadPlayerCharacter(this); lastPlayTimeUpdate = Utils.UnixTimeStampUTC(); @@ -408,7 +419,7 @@ namespace Meteor.Map.Actors bool[] testComplete = new bool[2048]; //TODO: Change to playerwork.scenarioComplete for (int i = 0; i < 2048; i++) testComplete[i] = true; - QueuePacket(cutsceneBookPacket.BuildPacket(Id, "", 11, 1, 1, testComplete)); + QueuePacket(cutsceneBookPacket.BuildPacket(Id, SNpcNickname, SNpcSkin, SNpcPersonality, SNpcCoordinate, testComplete)); QueuePacket(SetPlayerDreamPacket.BuildPacket(Id, 0x16, GetInnCode())); } @@ -1729,6 +1740,85 @@ namespace Meteor.Map.Actors } #endregion + #region SNpc Functions (Path Companion) + public void SetSNpc(string nickname, uint actorClassId, byte classType) + { + // Set name and appearance + SNpcNickname = nickname; + SNpcSkin = (byte) (actorClassId - 1070000); + + switch (SNpcSkin % 16) + { + // Hyur Male + case 1: + SNpcPersonality = 1; + break; + // Hyur Female + case 2: + case 16: + SNpcPersonality = 2; + break; + // Elezen Male + case 3: + case 4: + SNpcPersonality = 3; + break; + // Elezen Female + case 5: + case 6: + SNpcPersonality = 4; + break; + // Lalafel Male + case 7: + case 8: + SNpcPersonality = 5; + break; + // Lalafel Female + case 9: + case 10: + SNpcPersonality = 6; + break; + // Miqo'te + case 11: + case 12: + SNpcPersonality = 8; + break; + // Roegadyn + case 13: + case 14: + SNpcPersonality = 7; + break; + // Highlander + case 15: + SNpcPersonality = 9; + break; + } + + // Save to DB + Database.CreateOrUpdateSNpc(this, SNpcNickname, SNpcSkin, SNpcPersonality); + } + + public string GetSNpcNickname() + { + return SNpcNickname ?? "???"; + } + + public byte GetSNpcSkin() + { + return SNpcSkin; + } + + public byte GetSNpcPersonality() + { + return SNpcPersonality; + } + + public short GetSNpcCoordinate() + { + return SNpcCoordinate; + } + #endregion + #region Guildleves public void AddGuildleve(uint id) { @@ -1867,49 +1957,58 @@ namespace Meteor.Map.Actors return false; } - public void SetNpcLs(uint npcLSId, uint state) + public void AddNpcLs(uint npcLsId) + { + if (playerWork.npcLinkshellChatExtra[npcLsId] == false && playerWork.npcLinkshellChatCalling[npcLsId] == false) + { + SetNpcLs(npcLsId, NPCLS_INACTIVE); + SendGameMessage(Server.GetWorldManager().GetActor(), 25118, 0x20, npcLsId); // " linkpearl obtained." + } + } + + public void SetNpcLs(uint npcLsId, uint state) { bool isCalling, isExtra; isCalling = isExtra = false; - if (npcLSId < 1 || npcLSId > 40) + if (npcLsId < 1 || npcLsId > 40) return; - npcLSId--; + npcLsId--; switch (state) { case NPCLS_INACTIVE: - if (playerWork.npcLinkshellChatExtra[npcLSId] == true && playerWork.npcLinkshellChatCalling[npcLSId] == false) + if (playerWork.npcLinkshellChatExtra[npcLsId] == true && playerWork.npcLinkshellChatCalling[npcLsId] == false) return; isExtra = true; break; case NPCLS_ACTIVE: - if (playerWork.npcLinkshellChatExtra[npcLSId] == false && playerWork.npcLinkshellChatCalling[npcLSId] == true) + if (playerWork.npcLinkshellChatExtra[npcLsId] == false && playerWork.npcLinkshellChatCalling[npcLsId] == true) return; isCalling = true; break; case NPCLS_ALERT: - if (playerWork.npcLinkshellChatExtra[npcLSId] == true && playerWork.npcLinkshellChatCalling[npcLSId] == true) + if (playerWork.npcLinkshellChatExtra[npcLsId] == true && playerWork.npcLinkshellChatCalling[npcLsId] == true) return; isExtra = isCalling = true; break; } - playerWork.npcLinkshellChatExtra[npcLSId] = isExtra; - playerWork.npcLinkshellChatCalling[npcLSId] = isCalling; + playerWork.npcLinkshellChatExtra[npcLsId] = isExtra; + playerWork.npcLinkshellChatCalling[npcLsId] = isCalling; - Database.SaveNpcLS(this, npcLSId, isCalling, isExtra); + Database.SaveNpcLS(this, npcLsId, isCalling, isExtra); ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("playerWork/npcLinkshellChat", this); - propPacketUtil.AddProperty(String.Format("playerWork.npcLinkshellChatExtra[{0}]", npcLSId)); - propPacketUtil.AddProperty(String.Format("playerWork.npcLinkshellChatCalling[{0}]", npcLSId)); + propPacketUtil.AddProperty(String.Format("playerWork.npcLinkshellChatExtra[{0}]", npcLsId)); + propPacketUtil.AddProperty(String.Format("playerWork.npcLinkshellChatCalling[{0}]", npcLsId)); QueuePackets(propPacketUtil.Done()); } @@ -2069,7 +2168,7 @@ namespace Meteor.Map.Actors return; List lParams = LuaUtils.CreateLuaParamList(parameters); - SubPacket spacket = KickEventPacket.BuildPacket(Id, actor.Id, eventName, 0, lParams); + SubPacket spacket = KickEventPacket.BuildPacket(Id, actor.Id, eventName, 5, lParams); spacket.DebugPrintSubPacket(); QueuePacket(spacket); } diff --git a/Map Server/Actors/EventList.cs b/Map Server/Actors/EventList.cs index e0b58ceb..2c21d07e 100644 --- a/Map Server/Actors/EventList.cs +++ b/Map Server/Actors/EventList.cs @@ -82,7 +82,7 @@ namespace Meteor.Map.actors public class PushBoxEventCondition { public bool isEnabled = true; - public uint bgObj; + public uint instance; public uint layout; public string conditionName = ""; public string reactName = ""; diff --git a/Map Server/Database.cs b/Map Server/Database.cs index 7eaabf8c..42bc97ff 100644 --- a/Map Server/Database.cs +++ b/Map Server/Database.cs @@ -1326,6 +1326,28 @@ namespace Meteor.Map } } + //Load Path Companion + query = @" + SELECT + nickname, + skin, + personality, + coordinate + FROM characters_snpc WHERE characterId = @charId"; + + cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charId", player.Id); + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + player.SNpcNickname = reader.GetString("nickname"); + player.SNpcSkin = reader.GetByte("skin"); + player.SNpcPersonality = reader.GetByte("personality"); + player.SNpcCoordinate = reader.GetInt16("coordinate"); + } + } + player.GetItemPackage(ItemPackage.NORMAL).InitList(GetItemPackage(player, 0, ItemPackage.NORMAL)); player.GetItemPackage(ItemPackage.KEYITEMS).InitList(GetItemPackage(player, 0, ItemPackage.KEYITEMS)); player.GetItemPackage(ItemPackage.CURRENCY_CRYSTALS).InitList(GetItemPackage(player, 0, ItemPackage.CURRENCY_CRYSTALS)); @@ -1347,6 +1369,40 @@ namespace Meteor.Map } + public static void CreateOrUpdateSNpc(Player player, string nickname, uint skin, byte personality) + { + using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD))) + { + try + { + conn.Open(); + string query = @" + INSERT INTO characters_snpc + (characterId, nickname, skin, personality) + VALUES + (@charId, @nickname, @skin, @personality) + ON DUPLICATE KEY UPDATE + nickname = @nickname, skin = @skin, personality = @personality + "; + + MySqlCommand cmd = new MySqlCommand(query, conn); + cmd.Parameters.AddWithValue("@charId", player.Id); + cmd.Parameters.AddWithValue("@nickname", nickname); + cmd.Parameters.AddWithValue("@skin", skin); + cmd.Parameters.AddWithValue("@personality", personality); + cmd.ExecuteNonQuery(); + } + catch (MySqlException e) + { + Program.Log.Error(e.ToString()); + } + finally + { + conn.Dispose(); + } + } + } + public static InventoryItem[] GetEquipment(Player player, ushort classId) { InventoryItem[] equipment = new InventoryItem[player.GetEquipment().GetCapacity()]; diff --git a/Map Server/Lua/LuaEngine.cs b/Map Server/Lua/LuaEngine.cs index a3311530..0b07e4af 100644 --- a/Map Server/Lua/LuaEngine.cs +++ b/Map Server/Lua/LuaEngine.cs @@ -42,6 +42,7 @@ using Meteor.Map.DataObjects.chara; using Meteor.Map.actors.chara; using Meteor.Map.Actors.QuestNS; using Meteor.Map.actors.group; +using static Meteor.Map.LuaUtils; namespace Meteor.Map.lua { @@ -82,6 +83,12 @@ namespace Meteor.Map.lua UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); @@ -643,6 +650,9 @@ namespace Meteor.Map.lua public void EventStarted(Player player, Actor target, EventStartPacket eventStart) { + if (eventStart.luaParams == null) + return; + List lparams = new List(); lparams.AddRange(eventStart.luaParams); lparams.Insert(0, new LuaParam(0, eventStart.eventType)); diff --git a/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithCircle.cs b/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithCircle.cs index cda3d9ca..693c9623 100644 --- a/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithCircle.cs +++ b/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithCircle.cs @@ -45,7 +45,7 @@ namespace Meteor.Map.packets.send.actor.events binWriter.Write((UInt32)0x44533088); binWriter.Write((Single)100.0f); binWriter.Seek(4, SeekOrigin.Current); - binWriter.Write((Byte)(condition.outwards ? 0x10 : 0x1)); //If == 0x10, Inverted Bounding Box + binWriter.Write((Byte)(condition.outwards ? 0x10 : 0x0)); //If == 0x10, Inverted Bounding Box binWriter.Write((Byte)0); binWriter.Write((Byte)(condition.silent ? 0x1 : 0x0)); //Silent Trigger binWriter.Write(Encoding.ASCII.GetBytes(condition.conditionName), 0, Encoding.ASCII.GetByteCount(condition.conditionName) >= 0x24 ? 0x24 : Encoding.ASCII.GetByteCount(condition.conditionName)); diff --git a/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithTriggerBox.cs b/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithTriggerBox.cs index f0644d21..198fedb2 100644 --- a/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithTriggerBox.cs +++ b/Map Server/Packets/Send/Actor/Events/SetPushEventConditionWithTriggerBox.cs @@ -41,7 +41,7 @@ namespace Meteor.Map.packets.send.actor.events { using (BinaryWriter binWriter = new BinaryWriter(mem)) { - binWriter.Write((UInt32)condition.bgObj); // bgObj + binWriter.Write((UInt32)condition.instance); // bgObj binWriter.Write((UInt32)condition.layout); // Layout binWriter.Write((UInt32)4); // Actor? Always 4 in 1.23 binWriter.Seek(8, SeekOrigin.Current); // Unknowns diff --git a/Map Server/Packets/Send/Player/SetCutsceneBookPacket.cs b/Map Server/Packets/Send/Player/SetCutsceneBookPacket.cs index 4050f87a..5dac993b 100644 --- a/Map Server/Packets/Send/Player/SetCutsceneBookPacket.cs +++ b/Map Server/Packets/Send/Player/SetCutsceneBookPacket.cs @@ -79,7 +79,7 @@ namespace Meteor.Map.packets.send.player public const ushort OPCODE = 0x01A3; public const uint PACKET_SIZE = 0x150; - public SubPacket BuildPacket(uint sourceActorId, string sNpcName, short sNpcActorIdOffset, byte sNpcSkin, byte sNpcPersonality, bool[] completedQuests) + public SubPacket BuildPacket(uint sourceActorId, string sNpcName, byte sNpcSkin, byte sNpcPersonality, short sNpcCoordinate, bool[] completedQuests) { byte[] data = new byte[PACKET_SIZE - 0x20]; @@ -93,9 +93,9 @@ namespace Meteor.Map.packets.send.player binWriter.Seek(0x01 ,SeekOrigin.Begin); binWriter.Write((Int16)2); binWriter.Write((Byte)0); - binWriter.Write((Int16)sNpcActorIdOffset); - binWriter.Write((Byte)sNpcSkin); + binWriter.Write((Int16)sNpcSkin); binWriter.Write((Byte)sNpcPersonality); + binWriter.Write((Byte)sNpcCoordinate); if (binStream.Length <= PACKET_SIZE - 0x20) binWriter.Write(binStream);