void CustomTalkShops() { foreach (var sCustomTalk in _builder.Sheet("CustomTalk")) { var instructions = ScriptInstruction.Read(sCustomTalk, 30); var shopInstructions = instructions.Where(i => i.Label.Contains("SHOP") && !i.Label.Contains("LOGMSG")).ToArray(); if (shopInstructions.Length == 0) { continue; } var sNpcs = sCustomTalk.Sheet.Collection.ENpcs.FindWithData(sCustomTalk.Key).ToArray(); if (sNpcs.Length == 0) { continue; } foreach (var shopInstruction in shopInstructions) { var shopKey = (int)shopInstruction.Argument; if (Hacks.ExcludedShops.Contains(shopKey)) { continue; } // Setup a disposal shop. if (shopInstruction.Label == "SHOP_DISPOSAL") { DisposalShops.BuildShop(_builder, sNpcs, (int)shopInstruction.Argument); continue; } // Missing shop definitions? if (!_shopsByKey.TryGetValue(shopKey, out var shop)) { // todo: print relevant NPCs, text? DatabaseBuilder.PrintLine($"Shop {shopKey} not found, skipping."); continue; } if (shop.Name == "Unknown Shop") { var name = Hacks.GetShopName((SaintCoinach.Text.XivString)sCustomTalk.GetRaw("Name"), shopInstruction); if (name.Contains("[not in Saint]")) { DatabaseBuilder.PrintLine($"{string.Join(", ", sNpcs.Select(e => e.Singular.ToString()))} has shop {name}"); continue; } shop.Name = name; } shop.ENpcs = sNpcs.Union(shop.ENpcs).Distinct().ToArray(); } } }
void BuildQuests() { var lQuestsByKey = _builder.Libra.Table <Libra.Quest>().ToDictionary(q => q.Key); foreach (var sQuest in _builder.Sheet <Saint.Quest>()) { if (sQuest.Key == 65536 || sQuest.Name == "") { continue; // Test quests } dynamic quest = new JObject(); quest.id = sQuest.Key; _builder.Localize.Strings((JObject)quest, sQuest, Utils.SanitizeQuestName, "Name"); quest.patch = PatchDatabase.Get("quest", sQuest.Key); quest.sort = sQuest.SortKey; // Quest location var questIssuer = (sQuest.IssuingENpc?.Locations?.Count() ?? 0) > 0 ? sQuest.IssuingENpc : null; var sPlaceName = sQuest.PlaceName; if (sPlaceName.Name == "" && questIssuer != null) { sPlaceName = questIssuer.Locations.First().PlaceName; } _builder.Localize.Column((JObject)quest, sPlaceName, "Name", "location", x => x == "" ? "???" : x.ToString()); // Repeatability if (sQuest.RepeatInterval == Saint.QuestRepeatInterval.Daily) { quest.interval = "daily"; } else if (sQuest.RepeatInterval == Saint.QuestRepeatInterval.Weekly) { quest.interval = "weekly"; } if (sQuest.IsRepeatable) { quest.repeatable = 1; } // Miscellaneous if (sQuest.Icon != null) { quest.icon = IconDatabase.EnsureEntry("quest", sQuest.Icon); } if (sQuest.BeastTribe.Key != 0) { quest.beast = sQuest.BeastTribe.Key; } ImportQuestEventIcon(quest, sQuest); // Quest issuer if (questIssuer != null) { var npc = AddQuestNpc(quest, questIssuer); if (npc != null) { quest.issuer = questIssuer.Key; } } // Quest target var questTarget = sQuest.TargetENpc; if (questTarget != null) { var npc = AddQuestNpc(quest, questTarget); if (npc != null) { quest.target = questTarget.Key; } } // Involved if (_npcsByQuestKey.TryGetValue(sQuest.Key, out var involvedNpcKeys)) { foreach (var npcKey in involvedNpcKeys) { var sInvolvedEnpc = _builder.Realm.GameData.ENpcs[npcKey]; if (sInvolvedEnpc == null || sInvolvedEnpc == questTarget || sInvolvedEnpc == questIssuer) { continue; } var npc = AddQuestNpc(quest, sInvolvedEnpc); if (npc == null) { continue; } if (quest.involved == null) { quest.involved = new JArray(); } quest.involved.Add(npcKey); } } // Journal Genre quest.genre = sQuest.JournalGenre.Key; // Rewards dynamic rewards = new JObject(); if (sQuest.Rewards.Gil > 0) { rewards.gil = sQuest.Rewards.Gil; } if (sQuest.Rewards.Emote.Key > 0) { rewards.emote = sQuest.Rewards.Emote.Name.ToString(); } if (sQuest.Rewards.ClassJob.Key > 0) { rewards.job = sQuest.Rewards.ClassJob.Key; } if (sQuest.AsInt32("CurrencyRewardCount") > 0) { rewards.gcseal = sQuest.AsInt32("CurrencyRewardCount"); } if (sQuest.Rewards.Action.Key > 0) { rewards.action = sQuest.Rewards.Action.Key; _builder.Db.AddReference(quest, "action", sQuest.Rewards.Action.Key, false); } var sInstanceContentReward = sQuest.Rewards.InstanceContent; if (sInstanceContentReward.Key > 0) { var instance = _builder.Db.Instances.FirstOrDefault(i => i.id == sInstanceContentReward.Key); if (instance != null) { instance.unlockedByQuest = sQuest.Key; rewards.instance = sInstanceContentReward.Key; _builder.Db.AddReference(quest, "instance", sInstanceContentReward.Key, false); _builder.Db.AddReference(instance, "quest", sQuest.Key, false); } } if (sQuest.Rewards.Reputation > 0) { rewards.reputation = sQuest.Rewards.Reputation; } if (sQuest.Rewards.QuestRewardOther.Name == "Aether Current") { rewards.aetherCurrent = 1; } foreach (var sQuestRewardItemGroup in sQuest.Rewards.Items) { foreach (var sQuestRewardItem in sQuestRewardItemGroup.Items) { if (rewards.items == null) { rewards.items = new JArray(); } var maxCount = sQuestRewardItem.Counts.Max(); dynamic o = new JObject(); if (maxCount > 1) { o.num = maxCount; } o.id = sQuestRewardItem.Item.Key; if (sQuestRewardItemGroup.Type == Saint.QuestRewardGroupType.One) { o.one = 1; } if (sQuestRewardItem.IsHq) { o.hq = 1; } rewards.items.Add(o); try { var item = _builder.Db.ItemsById[sQuestRewardItem.Item.Key]; if (item.quests == null) { item.quests = new JArray(); } JArray quests = item.quests; if (!quests.Any(id => ((int)id) == sQuest.Key)) { quests.Add(sQuest.Key); } _builder.Db.AddReference(item, "quest", sQuest.Key, false); } catch (KeyNotFoundException ignored) { DatabaseBuilder.PrintLine($"Reward item '{sQuestRewardItem.Item.Key}' not found for Quest '{quest.Key}'."); } _builder.Db.AddReference(quest, "item", sQuestRewardItem.Item.Key, false); } } // Libra supplemental rewards. if (lQuestsByKey.TryGetValue(sQuest.Key, out var lQuest)) { dynamic data = JsonConvert.DeserializeObject(lQuest.data); int xp = 0; if (data.exp != null && int.TryParse((string)data.exp, out xp)) { rewards.xp = xp; } } // Scripts var instructions = ScriptInstruction.Read(sQuest, 50); // Script instance unlocks. if (!sQuest.IsRepeatable) { var instanceReferences = instructions.Where(i => i.Label.StartsWith("INSTANCEDUNGEON")).ToArray(); foreach (var instanceReference in instanceReferences) { var key = (int)instanceReference.Argument; var instance = _builder.Db.Instances.FirstOrDefault(i => ((int)i.id) == key); if (instance == null) { continue; // Probably a guildhest. } if (instance.unlockedByQuest != null) { continue; } if (!instructions.Any(i => i.Label == "UNLOCK_ADD_NEW_CONTENT_TO_CF" || i.Label.StartsWith("UNLOCK_DUNGEON"))) { // Some quests reference instances for the retrieval of items. // Don't treat these as unlocks. if (instructions.Any(i => i.Label.StartsWith("LOC_ITEM"))) { continue; } } instance.unlockedByQuest = sQuest.Key; rewards.instance = key; _builder.Db.AddReference(quest, "instance", key, false); _builder.Db.AddReference(instance, "quest", sQuest.Key, false); } } // Used items. foreach (var instruction in instructions) { if (!instruction.Label.StartsWith("RITEM") && !instruction.Label.StartsWith("QUEST_ITEM")) { continue; } var key = (int)instruction.Argument; if (_builder.Db.ItemsById.TryGetValue(key, out var item)) { if (item.usedInQuest == null) { item.usedInQuest = new JArray(); } JArray usedInQuest = item.usedInQuest; if (usedInQuest.Any(i => (int)i == sQuest.Key)) { continue; } item.usedInQuest.Add(sQuest.Key); if (quest.usedItems == null) { quest.usedItems = new JArray(); } quest.usedItems.Add(key); _builder.Db.AddReference(item, "quest", sQuest.Key, false); _builder.Db.AddReference(quest, "item", key, false); } } ImportQuestLore(quest, sQuest, instructions); if (((JObject)rewards).Count > 0) { quest.reward = rewards; } ImportQuestRequirements(quest, sQuest); _builder.Db.Quests.Add(quest); _builder.Db.QuestsById[sQuest.Key] = quest; } }