using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class QuestPrerequisites { public int minimumLevel = 1; public List requiredQuests; public QuestPrerequisites(int minimumLevel, List requiredQuests) { this.minimumLevel = minimumLevel; this.requiredQuests = requiredQuests; } } public class QuestRequirements { public Hashtable requiredMonsters; // Hashtable for #monsters required public Hashtable requiredItems; // Hashtable public List requiredDialogue; public int requiredLevel = 1; public bool isLevelUpQuest = false; public QuestRequirements(Hashtable requiredMonsters, Hashtable requiredItems, List requiredDialogue = null, int requiredLevel = 1, bool isLevelUpQuest = false) { this.requiredMonsters = requiredMonsters; this.requiredItems = requiredItems; this.requiredDialogue = requiredDialogue; this.requiredLevel = requiredLevel; this.isLevelUpQuest = isLevelUpQuest; if (requiredMonsters == null) this.requiredMonsters = new Hashtable(); if (requiredItems == null) this.requiredItems = new Hashtable(); if (requiredDialogue == null) this.requiredDialogue = new List(); } } public class QuestReward { public int exp; public int statPoints; public int money; public List items; public QuestReward(int exp, int statPoints, int money, List items) { this.exp = exp; this.statPoints = statPoints; this.money = money; this.items = items; if (items == null) items = new List(); } } public class QuestProgress { public QuestID questID; public QuestRequirements requirements; public Hashtable monsterKills; // Hashtable public Hashtable itemProgress; // Hashtable public static QuestProgress FromQuest(Quest quest) { QuestProgress progress = new QuestProgress { questID = quest.questID, requirements = quest.requirements, monsterKills = new Hashtable(), itemProgress = new Hashtable() }; return progress; } // Returns true iff you've completed the quest. public bool CheckComplete() { foreach (string line in WriteUp()) { Debug.Log(line); } foreach (DictionaryEntry KV in requirements.requiredMonsters) { if (!monsterKills.ContainsKey(KV.Key) || (int)monsterKills[KV.Key] < (int)KV.Value) { return false; } } foreach (DictionaryEntry KV in requirements.requiredItems) { if (!itemProgress.ContainsKey(KV.Key) || (int)itemProgress[KV.Key] < (int)KV.Value) { return false; } } foreach (DialogueID dialogueID in requirements.requiredDialogue) { if (!State.state.npcState.completedDialogues.Contains(dialogueID)) { return false; } } return State.state.stats.level >= requirements.requiredLevel; } public void RegisterMonsterKill(MonsterInfo.MonsterID monsterID) { if (requirements.requiredMonsters.ContainsKey(monsterID)) { if (monsterKills.ContainsKey(monsterID)) { monsterKills[monsterID] = (int)monsterKills[monsterID] + 1; } else { monsterKills[monsterID] = 1; } } } public void RefreshItemProgress() { Hashtable currentProgress = new Hashtable(); foreach (Item item in State.state.inventory.items) { if (item != null && requirements.requiredItems.ContainsKey(item.subType)) { if (currentProgress.ContainsKey(item.subType)) { currentProgress[item.subType] = (int)currentProgress[item.subType] + item.count; } else { currentProgress[item.subType] = item.count; } } } itemProgress = currentProgress; } // Write up the quest progress, one requirement at a time. public List WriteUp() { List writeup = new List(); if (requirements.isLevelUpQuest) { writeup.Add("Level: " + State.state.stats.level.ToString() + " / " + requirements.requiredLevel.ToString()); } foreach (DictionaryEntry KV in requirements.requiredMonsters) { int numKills = 0; if (monsterKills.ContainsKey(KV.Key)) { numKills = (int)monsterKills[KV.Key]; } writeup.Add("Defeat " + ((MonsterInfo)MonsterInfo.MonsterInfoMap[KV.Key]).name + ": " + numKills.ToString() + " / " + requirements.requiredMonsters[KV.Key].ToString()); } foreach (DictionaryEntry KV in requirements.requiredItems) { int numItems = 0; if (itemProgress.ContainsKey(KV.Key)) { numItems = (int)itemProgress[KV.Key]; } writeup.Add("Collect " + ((ItemData)ItemData.itemData[KV.Key]).name + ": " + numItems.ToString() + " / " + requirements.requiredItems[KV.Key].ToString()); } return writeup; } } public class Quest { public QuestID questID; public string name; public QuestPrerequisites prerequisites; public QuestRequirements requirements; public QuestReward reward; public string description; public Quest(QuestID questID, string name, QuestPrerequisites prerequisites, QuestRequirements requirements, QuestReward reward, string description) { this.questID = questID; this.name = name; this.prerequisites = prerequisites; this.requirements = requirements; this.reward = reward; this.description = description; } } public class QuestProgressData { // Update on monster kill. public Hashtable monsterToQuestID = new Hashtable(); // Hashtable> // Update on any item change. public Hashtable itemToQuestID = new Hashtable(); // Hashtable> public HashSet levelBasedQuests = new HashSet(); public Hashtable allQuestProgress = new Hashtable(); // Hashtable public HashSet availableQuests = new HashSet(); public HashSet completedQuests = new HashSet(); public bool CheckPrerequisites(QuestID questID) { Quest quest = (Quest)QuestInfo.quests[questID]; if (State.state.stats.level < quest.prerequisites.minimumLevel) { return false; } foreach (QuestID requiredQuest in quest.prerequisites.requiredQuests) { if (!completedQuests.Contains(requiredQuest)) { return false; } } return true; } public bool CheckNpcShopPrerequisites(Npc npc) { foreach(QuestID questID in npc.utilityRequirements) { if (!completedQuests.Contains(questID)) { return false; } } return true; } public bool AcceptQuest(QuestID questID) { Quest quest = (Quest)QuestInfo.quests[questID]; if (availableQuests.Contains(questID) && CheckPrerequisites(questID)) { availableQuests.Remove(questID); allQuestProgress.Add(questID, QuestProgress.FromQuest(quest)); if (quest.requirements.isLevelUpQuest) { levelBasedQuests.Add(questID); } foreach (DictionaryEntry KV in quest.requirements.requiredMonsters) { if (monsterToQuestID.ContainsKey(KV.Key)) { ((HashSet)monsterToQuestID[KV.Key]).Add(questID); } else { monsterToQuestID[KV.Key] = new HashSet() { questID }; } } foreach (DictionaryEntry KV in quest.requirements.requiredItems) { if (itemToQuestID.ContainsKey(KV.Key)) { ((HashSet)itemToQuestID[KV.Key]).Add(questID); } else { itemToQuestID[KV.Key] = new HashSet() { questID }; } } RefreshAvailableQuests(); return true; } return false; } public bool CompleteQuest(QuestID questID) { QuestProgress progress = (QuestProgress)allQuestProgress[questID]; if (progress == null || !progress.CheckComplete()) { return false; } Quest quest = (Quest)QuestInfo.quests[questID]; // Issue quest reward. Debug.Log("Gimme!"); // Check for sufficient open slots. int numOpenSlots = 0; foreach (Item item in State.state.inventory.items) { if (item == null || item.count == 0) { numOpenSlots++; } } if (numOpenSlots < quest.reward.items.Count) { Debug.Log("You need " + (quest.reward.items.Count - numOpenSlots).ToString() + " more open slots!"); return false; } // Cache inventory. Item[] inventoryCache = new Item[State.state.inventory.items.Length]; Array.Copy(State.state.inventory.items, inventoryCache, inventoryCache.Length); HashSet itemsForQuestUpdate = new HashSet(); // Disburse items. foreach (Item item in quest.reward.items) { Debug.Log("Disbursing " + item.subType.ToString()); // Items remaining from what was gained >0 means that the gain failed. if (State.state.inventory.GainItem(item, false) > 0) { Debug.LogError("Failed to gain item " + item.data.name + "! Reverting to inventory cache."); Array.Copy(inventoryCache, State.state.inventory.items, inventoryCache.Length); return false; } itemsForQuestUpdate.Add(item.subType); } // Take what the quest wants (items). Debug.Log("Here you go!"); Hashtable requiredItems = (Hashtable)quest.requirements.requiredItems.Clone(); for (int i = 0; i < State.state.inventory.items.Length; i++) { Item item = State.state.inventory.items[i]; if (item != null && requiredItems.ContainsKey(item.subType)) { // Drop (destroy) Item droppedItem = State.state.inventory.DropItem(i, Mathf.Min((int)requiredItems[item.subType], item.count), false); requiredItems[item.subType] = (int)requiredItems[item.subType] - droppedItem.count; if ((int)requiredItems[item.subType] == 0) { requiredItems.Remove(item.subType); } } } // Having required items remaining means something went wrong destroying the items. if (requiredItems.Count > 0) { string remaining = ""; foreach(DictionaryEntry KV in requiredItems) { remaining += ((ItemData)ItemData.itemData[KV.Key]).name + ", "; } Debug.LogError("Failed to delete required items: " + remaining); Array.Copy(inventoryCache, State.state.inventory.items, inventoryCache.Length); return false; } // Update state. State.state.stats.updateCache(); foreach(ItemSubType type in itemsForQuestUpdate) { State.state.inventory.UpdateQuests(type); } // Remove quest from progress trackers. foreach (DictionaryEntry KV in monsterToQuestID) { HashSet quests = (HashSet)KV.Value; if (quests.Contains(questID)) { quests.Remove(questID); } } foreach (DictionaryEntry KV in itemToQuestID) { HashSet quests = (HashSet)KV.Value; if (quests.Contains(questID)) { quests.Remove(questID); } } completedQuests.Add(questID); allQuestProgress.Remove(questID); levelBasedQuests.Remove(questID); RefreshAvailableQuests(); return true; } public void RefreshAvailableQuests() { foreach (QuestID questID in Enum.GetValues(typeof(QuestID))) { if (!completedQuests.Contains(questID) && !allQuestProgress.ContainsKey(questID) && !availableQuests.Contains(questID) && CheckPrerequisites(questID)) { availableQuests.Add(questID); } } } } public enum QuestID { QUEST_KARA_MAGIC_MUSHROOM, QUEST_KARA_BULKY_MUSHROOM, QUEST_KARA_LITTLE_CRITTERS, QUEST_KARA_BANDITS, QUEST_KARA_PLUCKING_HARPIES, QUEST_ESTER_ALL_ABOUT_STATS, QUEST_ESTER_ALL_ABOUT_EQUIPS, QUEST_ESTER_PREEMPTIVE_STRIKE, QUEST_ESTER_EMPTIVE_STRIKE, QUEST_ESTER_TINY_LEADER, QUEST_ESTER_LITTLE_NIGHTMARE, QUEST_ESTER_THE_JOURNEY_BEGINS, QUEST_LYN_GETTING_THE_GOODS, QUEST_LYN_BEAUTIFUL_LANDSCAPES, } public class QuestInfo { // Hashtable public static Hashtable quests = new Hashtable() { { QuestID.QUEST_KARA_MAGIC_MUSHROOM, new Quest( QuestID.QUEST_KARA_MAGIC_MUSHROOM, "Magic Mushrooms?", new QuestPrerequisites(1, new List()), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.BROWN_MUSHROOM, 10 } }, new Hashtable() { { ItemSubType.GREEN_MUSHROOM_SAMPLE, 3 } }), new QuestReward(50, 0, 10, new List() { Lottery.generateItem(ItemSubType.MUSHROOM_SOUP, 3) }), "Kara wants me to get a closer look at those giant mushrooms floating " + "around the valley... I wonder what they taste like.") }, { QuestID.QUEST_KARA_BULKY_MUSHROOM, new Quest( QuestID.QUEST_KARA_BULKY_MUSHROOM, "Bulky Mushrooms", new QuestPrerequisites(2, new List() {QuestID.QUEST_KARA_MAGIC_MUSHROOM}), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.RED_MUSHROOM, 10 } }, new Hashtable() { { ItemSubType.RED_MUSHROOM_SAMPLE, 3 } }), new QuestReward(80, 0, 20, new List() { Lottery.generateItem(ItemSubType.MUSHROOM_SOUP, 5) }), "Kara was roughed up by some big red mushrooms. " + "It's time for some target practice.") }, { QuestID.QUEST_KARA_LITTLE_CRITTERS, new Quest( QuestID.QUEST_KARA_LITTLE_CRITTERS, "Little Critters", new QuestPrerequisites(3, new List() {QuestID.QUEST_KARA_BULKY_MUSHROOM}), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.RED_FOX, 10 }, { MonsterInfo.MonsterID.WHITE_FOX, 10 }, }, new Hashtable() { { ItemSubType.FOX_FUR, 6 } }), new QuestReward(200, 0, 50, new List() { Lottery.generateItem(ItemSubType.ARCHER_TUNIC) }), "Kara spotted some foxes stealing crops in my field. " + "I need to run them out of town!") }, { QuestID.QUEST_KARA_BANDITS, new Quest( QuestID.QUEST_KARA_BANDITS, "Bandits!", new QuestPrerequisites(5, new List() {QuestID.QUEST_KARA_LITTLE_CRITTERS}), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HARPY_F, 10 }, { MonsterInfo.MonsterID.HARPY_F_2, 10 }, }, new Hashtable() { { ItemSubType.HARPY_FEATHER, 6 } }), new QuestReward(400, 0, 200, new List() { Lottery.generateItem(ItemSubType.MUSHROOM_SOUP, 10) }), "Looks like we have some visitors. " + "Aren't harpies usually pretty rare?") }, { QuestID.QUEST_KARA_PLUCKING_HARPIES, new Quest( QuestID.QUEST_KARA_PLUCKING_HARPIES, "Plucking Harpies", new QuestPrerequisites(10, new List() {QuestID.QUEST_KARA_BANDITS}), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HARPY_M, 10 }, { MonsterInfo.MonsterID.HARPY_M_2, 10 }, }, new Hashtable() { { ItemSubType.LARGE_HARPY_FEATHER, 10 } }), new QuestReward(500, 0, 500, new List() { Lottery.generateItem(ItemSubType.MUSHROOM_SOUP, 20) }), "Kara wants me to gether some Large Harpy Feathers. " + "Weird, but alright.") }, // Ester { QuestID.QUEST_ESTER_ALL_ABOUT_STATS, new Quest( QuestID.QUEST_ESTER_ALL_ABOUT_STATS, "All About Stats", new QuestPrerequisites(1, new List() {}), new QuestRequirements( new Hashtable(), new Hashtable()), new QuestReward(50, 1, 0, new List()), "Ester wants to teach me about latent power.") }, { QuestID.QUEST_ESTER_ALL_ABOUT_EQUIPS, new Quest( QuestID.QUEST_ESTER_ALL_ABOUT_EQUIPS, "All About Equips", new QuestPrerequisites(2, new List() { QuestID.QUEST_ESTER_ALL_ABOUT_STATS }), new QuestRequirements( new Hashtable(), new Hashtable()), new QuestReward(60, 1, 0, new List()), "Ester wants to teach me about gear! I'm all ears.") }, { QuestID.QUEST_ESTER_PREEMPTIVE_STRIKE, new Quest( QuestID.QUEST_ESTER_PREEMPTIVE_STRIKE, "Pre-emptive Strike", new QuestPrerequisites(10, new List() { QuestID.QUEST_ESTER_ALL_ABOUT_EQUIPS, QuestID.QUEST_KARA_BANDITS }), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HALFLING_ROGUE, 10 }, { MonsterInfo.MonsterID.HALFLING_ARCHER, 10 }, { MonsterInfo.MonsterID.HALFLING_SWORDWIELDER, 5 }, { MonsterInfo.MonsterID.HARPY_F_2, 5 }, }, new Hashtable() { { ItemSubType.BREASTPLATE, 5 }, { ItemSubType.FUZZY_SCARF, 5 }, { ItemSubType.SOFT_FABRIC, 1 }, }), new QuestReward(1000, 1, 100, new List()), "It's time to knock some sense into those halflings on the other side of town.") }, { QuestID.QUEST_ESTER_EMPTIVE_STRIKE, new Quest( QuestID.QUEST_ESTER_EMPTIVE_STRIKE, "Emptive Strike", new QuestPrerequisites(10, new List() { QuestID.QUEST_ESTER_PREEMPTIVE_STRIKE, }), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HALFLING_ROGUE_2, 10 }, { MonsterInfo.MonsterID.HALFLING_ARCHER_2, 10 }, { MonsterInfo.MonsterID.HALFLING_SWORDWIELDER_2, 5 }, { MonsterInfo.MonsterID.HARPY_M, 5 }, { MonsterInfo.MonsterID.HARPY_M_2, 5 }, }, new Hashtable() { { ItemSubType.BREASTPLATE, 5 }, { ItemSubType.FUZZY_SCARF, 5 }, { ItemSubType.SOFT_FABRIC, 1 }, { ItemSubType.LARGE_HARPY_FEATHER, 1 }, }), new QuestReward(2000, 1, 200, new List()), "There's way more bandits than I thought! I'll push my way into their camp.") }, { QuestID.QUEST_ESTER_TINY_LEADER, new Quest( QuestID.QUEST_ESTER_TINY_LEADER, "Tiny Leader", new QuestPrerequisites(15, new List() { QuestID.QUEST_ESTER_EMPTIVE_STRIKE, }), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HALFLING_ROGUE_3, 10 }, { MonsterInfo.MonsterID.HALFLING_ARCHER_3, 10 }, { MonsterInfo.MonsterID.HALFLING_SWORDWIELDER_3, 10 }, }, new Hashtable()), new QuestReward(3000, 1, 300, new List()), "The leader of the bandits is somewhere in the center of their camp. " + "Maybe some underling pummelling will prepare me for the fight.") }, { QuestID.QUEST_ESTER_LITTLE_NIGHTMARE, new Quest( QuestID.QUEST_ESTER_LITTLE_NIGHTMARE, "Tiny Leader", new QuestPrerequisites(15, new List() { QuestID.QUEST_ESTER_TINY_LEADER, }), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HALFLING_LEADER, 1 }, }, new Hashtable()), new QuestReward(5000, 1, 500, new List()), "It's time to take on the leader of the bandits. Maybe they'll go away " + "after this, or at least leave the townsfolk alone.") }, { QuestID.QUEST_ESTER_THE_JOURNEY_BEGINS, new Quest( QuestID.QUEST_ESTER_THE_JOURNEY_BEGINS, "Tiny Leader", new QuestPrerequisites(15, new List() { QuestID.QUEST_ESTER_LITTLE_NIGHTMARE, }), new QuestRequirements( new Hashtable() { { MonsterInfo.MonsterID.HALFLING_LEADER, 1 }, }, new Hashtable()), new QuestReward(5000, 1, 500, new List()), "It's time to head into the forest for the first time in months. " + "I wonder how Hestus City is doing.") }, // Lyn { QuestID.QUEST_LYN_GETTING_THE_GOODS, new Quest( QuestID.QUEST_LYN_GETTING_THE_GOODS, "Getting the Goods", //new QuestPrerequisites(5, new List() { QuestID.QUEST_KARA_LITTLE_CRITTERS }), new QuestPrerequisites(1, new List() { }), new QuestRequirements(new Hashtable(), new Hashtable()), new QuestReward(50, 0, 0, new List()), "There's a new face around town! What's a demon doing here?") }, { QuestID.QUEST_LYN_BEAUTIFUL_LANDSCAPES, new Quest( QuestID.QUEST_LYN_GETTING_THE_GOODS, "Getting the Goods", new QuestPrerequisites(60, new List() { QuestID.QUEST_LYN_GETTING_THE_GOODS }), new QuestRequirements(new Hashtable(), new Hashtable()), new QuestReward(0, 0, 0, new List()), "I didn't know Lyn could paint so well!") }, }; }