public override void Handle(GameSession session, PacketReader packet) { byte function = packet.ReadByte(); switch (function) { case 0: // Cancel return; case 1: int objectId = packet.ReadInt(); if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out IFieldObject <Npc> npc)) { return; // Invalid NPC } // Stellar Chest: 11004215 session.Send(NpcTalkPacket.Respond(npc, 0)); break; case 2: // Continue chat? int index = packet.ReadInt(); // selection index session.Send(NpcTalkPacket.Close()); break; } }
private static void HandleBeauty(GameSession session) { MapPortal portal = MapEntityStorage.GetPortals(session.Player.MapId).FirstOrDefault(portal => portal.Id == 99); // unsure how the portalId is determined if (portal is null) { return; } session.Send(NpcTalkPacket.Action(ActionType.Portal, "", "", portal.Id)); NpcMetadata npcTarget = NpcMetadataStorage.GetNpcMetadata(session.Player.NpcTalk.Npc.Id); session.Player.ShopId = npcTarget.ShopId; switch (npcTarget.ShopId) { case 500: // Dr Dixon session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "face")); // unsure how these strings are determined break; case 501: // Dr Zenko session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "skin")); break; case 504: // Rosetta case 509: //Lolly session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,style")); break; case 505: // Ren session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "makeup")); break; case 506: // Douglas session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "itemcolor")); break; case 507: // Mirror session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "mirror")); break; case 508: // Paulie session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,random")); break; case 510: // Mino session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,styleSave")); break; } session.Send(UserMoveByPortalPacket.Move(session.Player.FieldPlayer, portal.Coord.ToFloat(), portal.Rotation.ToFloat())); }
public static void HandleNpcTalkEventType(GameSession session, NpcScript npcScript, int eventId) { ScriptEvent scriptEvent = npcScript.Contents.First().Events.FirstOrDefault(x => x.Id == eventId); if (scriptEvent is null) { return; } int indexContent = Random.Shared.Next(scriptEvent.Contents.Count); EventContent eventContents = scriptEvent.Contents[indexContent]; session.Send(NpcTalkPacket.CustomText(eventContents.Text, eventContents.VoiceId, eventContents.Illustration)); }
public static void HandleOpen(GameSession session, IFieldObject <Npc> npcFieldObject) { NpcMetadata metadata = NpcMetadataStorage.GetNpc(npcFieldObject.Value.Id); ShopMetadata shop = ShopMetadataStorage.GetShop(metadata.ShopId); if (shop == null) { Console.WriteLine($"Unknown shop ID: {metadata.ShopId}"); return; } session.Send(ShopPacket.Open(shop)); session.Send(ShopPacket.LoadProducts(shop.Items)); session.Send(ShopPacket.Reload()); session.Send(NpcTalkPacket.Respond(npcFieldObject, NpcType.Default, DialogType.None, 0)); }
public static void HandleOpen(GameSession session, IFieldObject <NpcMetadata> npcFieldObject) { NpcMetadata metadata = npcFieldObject.Value; Shop shop = DatabaseManager.Shops.FindById(metadata.ShopId); if (shop == null) { Logger.Warn($"Unknown shop ID: {metadata.ShopId}"); return; } session.Send(ShopPacket.Open(shop)); foreach (ShopItem shopItem in shop.Items) { session.Send(ShopPacket.LoadProducts(shopItem)); } session.Send(ShopPacket.Reload()); session.Send(NpcTalkPacket.Respond(npcFieldObject, NpcType.Default, DialogType.None, 0)); session.Player.ShopId = shop.Id; }
public override void Handle(GameSession session, PacketReader packet) { byte function = packet.ReadByte(); switch (function) { case 0: // Cancel return; case 1: int objectId = packet.ReadInt(); if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out IFieldObject <Npc> npc)) { return; // Invalid NPC } // If NPC is a shop, load and open the shop if (npc.Value.IsShop()) { ShopHandler.HandleOpen(session, npc); return; } else if (npc.Value.IsBank()) { session.Send(HomeBank.OpenBank()); return; } // Stellar Chest: 11004215 session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk2, DialogType.TalkOption, 0)); break; case 2: // Continue chat? int index = packet.ReadInt(); // selection index session.Send(NpcTalkPacket.Close()); break; } }
private static void HandleRespond(GameSession session, PacketReader packet) { List <QuestStatus> npcQuests = new List <QuestStatus>(); int objectId = packet.ReadInt(); if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out IFieldObject <Npc> npc)) { return; // Invalid NPC } foreach (QuestStatus item in session.Player.QuestList.Where(x => !x.Completed)) { if (npc.Value.Id == item.StartNpcId) { npcQuests.Add(item); } if (item.Started && npc.Value.Id == item.CompleteNpcId && !npcQuests.Contains(item)) { npcQuests.Add(item); } } session.Player.NpcTalk = new NpcTalk(npc.Value, npcQuests); // If NPC is a shop, load and open the shop if (npc.Value.IsShop()) { ShopHandler.HandleOpen(session, npc); return; } else if (npc.Value.IsBank()) { session.Send(HomeBank.OpenBank()); return; } else if (npc.Value.IsBeauty()) { session.Send(NpcTalkPacket.Respond(npc, NpcType.Default, DialogType.Beauty, 1)); return; } QuestHelper.UpdateExplorationQuest(session, npc.Value.Id.ToString(), "talk_in"); if (npcQuests.Count != 0) { session.Player.NpcTalk.ScriptId = 0; session.Send(QuestPacket.SendDialogQuest(objectId, npcQuests)); session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk2, DialogType.TalkOption, session.Player.NpcTalk.ScriptId)); } else { ScriptMetadata scriptMetadata = ScriptMetadataStorage.GetNpcScriptMetadata(npc.Value.Id); int firstScript = scriptMetadata.Options.First(x => x.Type == ScriptType.Script).Id; session.Player.NpcTalk.ScriptId = firstScript; Option option = scriptMetadata.Options.First(x => x.Id == firstScript); bool hasNextScript = option.Goto.Count != 0; DialogType dialogType = DialogType.CloseNext1; if (option.Goto.Count == 0) { session.Player.NpcTalk.ContentIndex++; dialogType = DialogType.CloseNext1; } if (!hasNextScript) { dialogType = DialogType.Close1; } if (option.AmountContent > 1) { dialogType = DialogType.CloseNext; } session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk3, dialogType, firstScript)); } }
private static void HandleContinue(GameSession session, PacketReader packet) { NpcTalk npcTalk = session.Player.NpcTalk; if (npcTalk.Npc.IsBeauty()) { HandleBeauty(session); return; } int index = packet.ReadInt(); // selection index // index is quest if (index <= npcTalk.Quests.Count - 1 && npcTalk.ScriptId == 0) { npcTalk.QuestId = npcTalk.Quests[index].Basic.Id; npcTalk.IsQuest = true; } ScriptMetadata scriptMetadata = npcTalk.IsQuest ? ScriptMetadataStorage.GetQuestScriptMetadata(npcTalk.QuestId) : ScriptMetadataStorage.GetNpcScriptMetadata(npcTalk.Npc.Id); ResponseType responseType = npcTalk.IsQuest ? ResponseType.Quest : ResponseType.Dialog; if (npcTalk.ScriptId != 0) { Option option = scriptMetadata.Options.First(x => x.Id == npcTalk.ScriptId); if (option.AmountContent <= npcTalk.ContentIndex && option.Goto.Count == 0) { session.Send(NpcTalkPacket.Close()); return; } } // Find next script id int nextScript = GetNextScript(scriptMetadata, npcTalk, index); Option option2 = scriptMetadata.Options.FirstOrDefault(x => x.Id == npcTalk.ScriptId); if (option2?.AmountContent > 1 && option2?.AmountContent > npcTalk.ContentIndex) { nextScript = npcTalk.ScriptId; } if (npcTalk.ScriptId != nextScript) { npcTalk.ContentIndex = 1; } else { npcTalk.ContentIndex++; } Option option1 = scriptMetadata.Options.First(x => x.Id == nextScript); bool hasNextScript = option1.Goto.Count != 0; if (option1.AmountContent > npcTalk.ContentIndex) { hasNextScript = true; } npcTalk.ScriptId = nextScript; DialogType dialogType = GetDialogType(scriptMetadata, npcTalk, hasNextScript); session.Send(NpcTalkPacket.ContinueChat(nextScript, responseType, dialogType, npcTalk.ContentIndex - 1, npcTalk.QuestId)); }
private static void HandleRespond(GameSession session, PacketReader packet) { List <QuestStatus> npcQuests = new List <QuestStatus>(); int objectId = packet.ReadInt(); // Find if npc object id exists in field manager if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out IFieldObject <Npc> npc)) { return; } // Get all quests for this npc foreach (QuestStatus item in session.Player.QuestList.Where(x => !x.Completed)) { if (npc.Value.Id == item.StartNpcId) { npcQuests.Add(item); } if (item.Started && npc.Value.Id == item.CompleteNpcId && !npcQuests.Contains(item)) { npcQuests.Add(item); } } session.Player.NpcTalk = new NpcTalk(npc.Value, npcQuests); ScriptLoader scriptLoader = new ScriptLoader($"Npcs/{npc.Value.Id}", session); // If NPC is a shop, load and open the shop if (npc.Value.IsShop()) { ShopHandler.HandleOpen(session, npc); return; } else if (npc.Value.IsBank()) { session.Send(HomeBank.OpenBank()); return; } else if (npc.Value.IsBeauty()) { NpcMetadata npcTarget = NpcMetadataStorage.GetNpc(session.Player.NpcTalk.Npc.Id); if (npcTarget.ShopId == 507) // mirror { session.Send(NpcTalkPacket.Respond(npc, NpcType.Default, DialogType.Beauty, 0)); HandleBeauty(session); return; } session.Send(NpcTalkPacket.Respond(npc, NpcType.Default, DialogType.Beauty, 1)); return; } // Check if npc has an exploration quest QuestHelper.UpdateExplorationQuest(session, npc.Value.Id.ToString(), "talk_in"); // If npc has quests, send quests and talk option if (npcQuests.Count != 0) { session.Player.NpcTalk.ScriptId = 0; session.Send(QuestPacket.SendDialogQuest(objectId, npcQuests)); session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk2, DialogType.TalkOption, session.Player.NpcTalk.ScriptId)); return; } ScriptMetadata scriptMetadata = ScriptMetadataStorage.GetNpcScriptMetadata(npc.Value.Id); if (!scriptMetadata.Options.Exists(x => x.Type == ScriptType.Script)) { return; } int firstScriptId = GetFirstScriptId(scriptLoader, scriptMetadata); session.Player.NpcTalk.ScriptId = firstScriptId; Option option = scriptMetadata.Options.First(x => x.Id == firstScriptId); DialogType dialogType = DialogType.None; if (option.Contents[0].Goto.Count == 0) { dialogType = DialogType.Close1; } else { dialogType = DialogType.CloseNextWithDistractor; } session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk3, dialogType, firstScriptId)); // If npc has buttonset roulette, send roulette id 13. // TODO: Send the correct roulette id if (scriptMetadata.Options.Any(x => x.ButtonSet == "roulette")) { session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "RouletteDialog", "13")); } }
private static void HandleContinue(GameSession session, int index) { NpcTalk npcTalk = session.Player.NpcTalk; if (npcTalk.Npc.IsBeauty()) { HandleBeauty(session); return; } ScriptLoader scriptLoader = new ScriptLoader($"Npcs/{npcTalk.Npc.Id}", session); // index is quest if (index <= npcTalk.Quests.Count - 1 && npcTalk.ScriptId == 0) { npcTalk.QuestId = npcTalk.Quests[index].Basic.Id; npcTalk.IsQuest = true; } ScriptMetadata scriptMetadata = npcTalk.IsQuest ? ScriptMetadataStorage.GetQuestScriptMetadata(npcTalk.QuestId) : ScriptMetadataStorage.GetNpcScriptMetadata(npcTalk.Npc.Id); ResponseType responseType = npcTalk.IsQuest ? ResponseType.Quest : ResponseType.Dialog; if (npcTalk.ScriptId != 0) { Option option = scriptMetadata.Options.First(x => x.Id == npcTalk.ScriptId); // Find if player has quest condition for type "talk_in" and option id QuestHelper.UpdateQuest(session, npcTalk.Npc.Id.ToString(), "talk_in", option.Id.ToString()); // If npc has no more options, close dialog if (option.Contents.Count <= npcTalk.ContentIndex + 1 && option.Contents[npcTalk.ContentIndex].Goto.Count == 0) { session.Send(NpcTalkPacket.Close()); return; } } int nextScriptId = GetNextScript(scriptMetadata, npcTalk, index, scriptLoader); // If last script is different from next, reset content index, else increment content index if (npcTalk.ScriptId != nextScriptId) { npcTalk.ContentIndex = 0; } else { npcTalk.ContentIndex++; } Option nextScript = scriptMetadata.Options.First(x => x.Id == nextScriptId); bool hasNextScript = nextScript.Contents[npcTalk.ContentIndex].Goto.Count != 0; if (nextScript.Contents.Count > npcTalk.ContentIndex + 1) { hasNextScript = true; } npcTalk.ScriptId = nextScriptId; DialogType dialogType = GetDialogType(scriptMetadata, npcTalk, hasNextScript); session.Send(NpcTalkPacket.ContinueChat(nextScriptId, responseType, dialogType, npcTalk.ContentIndex, npcTalk.QuestId)); // It appears if content has buttonset roulette, it's send again on every continue chat, unsure why since it doesn't break anything }
public void TalkFunction(GameSession session, int functionId, string function) { if (functionId == 0) { return; } List <ActionType> actions = new(); Script npcScript = ScriptLoader.GetScript($"Npcs/{Npc.Id}"); DynValue actionResults = npcScript?.RunFunction(function); if (actionResults == null) { return; } switch (actionResults.Type) { case DataType.Number: actions.Add((ActionType)actionResults.Number); break; case DataType.Tuple: foreach (DynValue value in actionResults.Tuple) { actions.Add((ActionType)value.Number); } break; default: return; } MapPortal portal = new(); foreach (ActionType action in actions) { switch (action) { case ActionType.OpenWindow: DynValue windowResults = npcScript.RunFunction("actionWindow"); session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, windowResults.Tuple[0].String, windowResults.Tuple[1].String)); break; case ActionType.Portal: DynValue portalResults = npcScript.RunFunction("actionPortal"); portal = MapEntityMetadataStorage.GetPortals(session.Player.MapId).FirstOrDefault(portal => portal.Id == portalResults.Number); if (portal is null) { return; } session.Send(NpcTalkPacket.Action(ActionType.Portal, "", "", portal.Id)); break; case ActionType.ItemReward: DynValue itemResults = npcScript.RunFunction("actionItemReward"); // TODO: Support > 1 item Item item = new(id : (int)itemResults.Tuple[0].Number, amount : (int)itemResults.Tuple[2].Number, rarity : (int)itemResults.Tuple[1].Number); session.Player.Inventory.AddItem(session, item, true); session.Send(NpcTalkPacket.Action(action, "", "", 0, item)); break; case ActionType.MoveMap: DynValue map = npcScript.RunFunction("actionMoveMap"); int mapId = (int)map.Tuple[0].Number; int portalId = (int)map.Tuple[1].Number; MapPortal portalDst = MapEntityMetadataStorage.GetPortals(mapId).FirstOrDefault(x => x.Id == portalId); if (portalDst is null) { session.Player.Warp(mapId); return; } session.Player.Warp(mapId, portalDst.Coord, portalDst.Rotation); break; } } // this needs to be sent after the UI window action if (actions.Contains(ActionType.Portal)) { session.Player.Move(portal.Coord.ToFloat(), portal.Rotation.ToFloat()); } }
public override void Handle(GameSession session, PacketReader packet) { byte function = packet.ReadByte(); switch (function) { case 0: // Cancel return; case 1: int objectId = packet.ReadInt(); if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out IFieldObject <Npc> npc)) { return; // Invalid NPC } session.Player.NpcTalk = npc; // If NPC is a shop, load and open the shop if (npc.Value.IsShop()) { ShopHandler.HandleOpen(session, npc); return; } else if (npc.Value.IsBank()) { session.Send(HomeBank.OpenBank()); return; } else if (npc.Value.IsBeauty()) { session.Send(NpcTalkPacket.Respond(npc, NpcType.Default, DialogType.Beauty, 1)); return; } // Stellar Chest: 11004215 session.Send(NpcTalkPacket.Respond(npc, NpcType.Unk2, DialogType.TalkOption, 0)); break; case 2: // Continue chat? int index = packet.ReadInt(); // selection index if (session.Player.NpcTalk.Value.IsBeauty()) // This may need a cleaner method { MapPortal portal = MapEntityStorage.GetPortals(session.Player.MapId).FirstOrDefault(portal => portal.Id == 99); // unsure how the portalId is determined session.Send(NpcTalkPacket.Action(ActionType.Portal, "", "", portal.Id)); NpcMetadata npcTarget = NpcMetadataStorage.GetNpc(session.Player.NpcTalk.Value.Id); switch (npcTarget.ShopId) { case 500: // Dr Dixon session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "face")); // unsure how these strings are determined break; case 501: // Dr Zenko session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "skin")); break; case 504: // Rosetta case 509: //Lolly session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,style")); break; case 505: // Ren session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "makeup")); break; case 506: // Douglas session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "itemcolor")); break; case 507: // Mirror session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "mirror")); break; case 508: // Paulie session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,random")); break; case 510: // Mino session.Send(NpcTalkPacket.Action(ActionType.OpenWindow, "BeautyShopDialog", "hair,styleSave")); break; } session.Send(UserMoveByPortalPacket.Move(session, portal.Coord.ToFloat(), portal.Rotation.ToFloat())); } session.Send(NpcTalkPacket.Close()); session.Player.NpcTalk = null; break; } }
private static void HandleBegin(GameSession session, PacketReader packet) { int objectId = packet.ReadInt(); List <QuestStatus> npcQuests = new(); int contentIndex = 0; // Find if npc object id exists in field manager if (!session.FieldManager.State.Npcs.TryGetValue(objectId, out Npc npc)) { return; } // Get all quests for this npc foreach (QuestStatus item in session.Player.QuestData.Values.Where(x => x.State is not QuestState.Completed)) { if (npc.Value.Id == item.StartNpcId) { npcQuests.Add(item); } if (item.State is QuestState.Started && npc.Value.Id == item.CompleteNpcId && !npcQuests.Contains(item)) { npcQuests.Add(item); } } session.Player.NpcTalk = new(npc.Value, npcQuests); NpcTalk npcTalk = session.Player.NpcTalk; ScriptMetadata scriptMetadata = ScriptMetadataStorage.GetNpcScriptMetadata(npc.Value.Id); NpcKind kind = npc.Value.NpcMetadataBasic.Kind; // need to find script properly before continuing NpcScript npcScript = GetFirstScript(session, scriptMetadata, npcTalk, npcQuests); switch (kind) { // reputation NPCs only have UI Dialog Type even if they have quests to accept/accepted. case NpcKind.SkyLumiknightCommander: case NpcKind.SkyGreenHoodCommander: case NpcKind.SkyDarkWindCommander: case NpcKind.SkyMapleAllianceCommander: case NpcKind.SkyRoyalGuardCommander: case NpcKind.KritiasLumiknightCommander: case NpcKind.KritiasGreenHoodCommander: case NpcKind.KritiasMapleAllianceCommander: case NpcKind.Humanitas: npcTalk.DialogType = DialogType.UI; ShopHandler.HandleOpen(session, npc, npc.Value.Id); break; case NpcKind.BalmyShop: case NpcKind.FixedShop: case NpcKind.RotatingShop: ShopHandler.HandleOpen(session, npc, npc.Value.Id); break; case NpcKind.Storage: case NpcKind.BlackMarket: case NpcKind.Birthday: // TODO: needs a special case? select if birthday. script if not npcTalk.DialogType = DialogType.UI; break; } npcTalk.ScriptId = npcScript?.Id ?? 0; ResponseSelection responseSelection = GetResponseSelection(kind, npcTalk.DialogType, contentIndex, npcScript); if (npcTalk.DialogType.HasFlag(DialogType.Quest)) { session.Send(QuestPacket.SendDialogQuest(objectId, npcQuests)); } QuestManager.OnTalkNpc(session.Player, npc.Value.Id, npcTalk.ScriptId); session.Send(NpcTalkPacket.Respond(npc, npcTalk.DialogType, contentIndex, responseSelection, npcTalk.ScriptId)); if (npcScript != null) { npcTalk.TalkFunction(session, npcScript.Contents[npcTalk.ContentIndex].FunctionId, "preTalkActions"); } }