Ejemplo n.º 1
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            ReputeExceedsDo action = new ReputeExceedsDo(parentQuest);

            action.npcSymbol     = new Symbol(match.Groups["npcSymbol"].Value);
            action.taskSymbol    = new Symbol(match.Groups["taskSymbol"].Value);
            action.minReputation = Parser.ParseInt(match.Groups["minReputation"].Value);

            return(action);
        }
Ejemplo n.º 2
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            ItemUsedDo action = new ItemUsedDo(parentQuest);

            action.itemSymbol = new Symbol(match.Groups["anItem"].Value);
            action.taskSymbol = new Symbol(match.Groups["aTask"].Value);
            action.textID     = Parser.ParseInt(match.Groups["textID"].Value);

            return(action);
        }
Ejemplo n.º 3
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            TeleportPc action = new TeleportPc(parentQuest);

            action.targetPlace = new Symbol(match.Groups["aPlace"].Value);
            if (match.Groups["marker"].Success)
            {
                action.targetMarker = Parser.ParseInt(match.Groups["marker"].Value);
            }

            return(action);
        }
Ejemplo n.º 4
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            CreateFoe action = new CreateFoe(parentQuest);

            action.foeSymbol     = new Symbol(match.Groups["symbol"].Value);
            action.spawnInterval = (uint)Parser.ParseInt(match.Groups["minutes"].Value) * 60;
            action.spawnMaxTimes = Parser.ParseInt(match.Groups["count"].Value);
            action.spawnChance   = Parser.ParseInt(match.Groups["percent"].Value);
            action.lastSpawnTime = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToSeconds();

            // Handle infinite
            if (!string.IsNullOrEmpty(match.Groups["infinite"].Value))
            {
                action.spawnMaxTimes = -1;
            }

            // Handle "send" variant
            if (!string.IsNullOrEmpty(match.Groups["send"].Value))
            {
                action.isSendAction = true;

                // "send" without "count" implies infinite
                if (action.spawnMaxTimes == 0)
                {
                    action.spawnMaxTimes = -1;
                }
            }

            return(action);
        }
Ejemplo n.º 5
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            DailyFrom action   = new DailyFrom(parentQuest);
            int       hours1   = Parser.ParseInt(match.Groups["hours1"].Value);
            int       minutes1 = Parser.ParseInt(match.Groups["minutes1"].Value);
            int       hours2   = Parser.ParseInt(match.Groups["hours2"].Value);
            int       minutes2 = Parser.ParseInt(match.Groups["minutes2"].Value);

            action.minDailySeconds = ToDailySeconds(hours1, minutes1);
            action.maxDailySeconds = ToDailySeconds(hours2, minutes2);

            return(action);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Parse optional message tags from this resource.
        /// </summary>
        /// <param name="line"></param>
        protected void ParseMessageTags(string line)
        {
            string matchStr = @"anyInfo (?<info>\d+)|used (?<used>\d+)|rumors (?<rumors>\d+)";

            // Get message tag matches
            MatchCollection matches = Regex.Matches(line, matchStr);

            if (matches.Count == 0)
            {
                return;
            }

            // Grab tag values
            foreach (Match match in matches)
            {
                // Match info message id
                Group info = match.Groups["info"];
                if (info.Success)
                {
                    infoMessageID = Parser.ParseInt(info.Value);
                }

                // Match used message id
                Group used = match.Groups["used"];
                if (used.Success)
                {
                    usedMessageID = Parser.ParseInt(used.Value);
                }

                // Match rumors message id
                Group rumors = match.Groups["rumors"];
                if (rumors.Success)
                {
                    rumorsMessageID = Parser.ParseInt(rumors.Value);
                }
            }
        }
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);

            if (!match.Success)
            {
                return(null);
            }

            // Factory new action
            WhenAttributeLevel action        = new WhenAttributeLevel(parentQuest);
            string             attributeName = match.Groups["attributeName"].Value;

            if (!Enum.IsDefined(typeof(DFCareer.Stats), attributeName))
            {
                SetComplete();
                throw new Exception(string.Format("WhenAttributeLevel: Attribute name {0} is not a known Daggerfall attribute", attributeName));
            }
            action.attribute         = (DFCareer.Stats)Enum.Parse(typeof(DFCareer.Stats), attributeName);
            action.minAttributeValue = Parser.ParseInt(match.Groups["minAttributeValue"].Value);

            return(action);
        }
Ejemplo n.º 8
0
        public override IQuestAction CreateNew(string source, Quest parentQuest)
        {
            // Source must match pattern
            Match match = Test(source);
            if (!match.Success)
                return null;

            // Get signed value
            int value;
            string sign = match.Groups["sign"].Value;
            if (sign == "+")
                value = Parser.ParseInt(match.Groups["amount"].Value);
            else if (sign == "-")
                value = -Parser.ParseInt(match.Groups["amount"].Value);
            else
                throw new System.Exception("Invalid sign encountered by ChangeReputeWith action");

            // Factory new action
            ChangeReputeWith action = new ChangeReputeWith(parentQuest);
            action.target = new Symbol(match.Groups["target"].Value);
            action.amount = value;

            return action;
        }
Ejemplo n.º 9
0
        void AssignHomeTown(string scopeString)
        {
            const string houseString = "house";

            Place  homePlace;
            string symbolName = string.Format("_{0}_home_", Symbol.Name);

            // If this is a Questor or individual NPC then use current location of player using a special helper
            if (isQuestor || (IsIndividualNPC && isIndividualAtHome))
            {
                homePlace = new Place(ParentQuest);
                if (GameManager.Instance.PlayerGPS.HasCurrentLocation)
                {
                    if (!homePlace.ConfigureFromPlayerLocation(symbolName))
                    {
                        throw new Exception("AssignHomeTown() could not configure questor/individual home from current player location.");
                    }

                    homePlaceSymbol = homePlace.Symbol;
                    ParentQuest.AddResource(homePlace);
                    LogHomePlace(homePlace);
                    return;
                }
            }

            // If this is an individual NPC who is not at home, don't place for now
            // as this has to be done by the "place _person_ at" command
            if (IsIndividualNPC)
            {
                return;
            }

            // For other NPCs use the given scope if any
            if (string.IsNullOrEmpty(scopeString))
            {
                // Else generate it at random only if there are local buildings
                if (GameManager.Instance.PlayerGPS.HasCurrentLocation &&
                    GameManager.Instance.PlayerGPS.CurrentLocation.Exterior.BuildingCount > 0)
                {
                    scopeString = UnityEngine.Random.Range(0.0f, 1.0f) < 0.5f ? "local" : "remote";
                }
                else
                {
                    scopeString = "remote";
                }
            }

            // Adjust building type based on faction hints
            string buildingTypeString = houseString;
            int    p1 = 0, p2 = 0, p3 = 0;

            if (!string.IsNullOrEmpty(factionTableKey))
            {
                // Get faction parameters
                p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", factionTableKey));
                p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", factionTableKey));
                p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", factionTableKey));
                if (p1 == 0 && p2 >= 0 && p2 <= 20 && p3 == 0)
                {
                    // Set to a specific building type
                    buildingTypeString = QuestMachine.Instance.PlacesTable.GetKeyForValue("p2", p2.ToString());
                }
            }

            // Create the home location - this will try to match NPC group (e.g. a Noble will select a Palace)
            try
            {
                // Try preferred location type
                string source = string.Format("Place {0} {1} {2}", symbolName, scopeString, buildingTypeString);
                homePlace = new Place(ParentQuest, source);
            }
            catch
            {
                // Otherwise try to use a generic house
                // If this doesn't work for some reason then next exception will prevent quest from starting
                string source = string.Format("Place {0} {1} {2}", symbolName, scopeString, houseString);
                homePlace = new Place(ParentQuest, source);
            }

            // Complete assigning home place
            homePlaceSymbol = homePlace.Symbol.Clone();
            ParentQuest.AddResource(homePlace);
            LogHomePlace(homePlace);
        }
Ejemplo n.º 10
0
        // Gets factionID of a career NPC
        int GetCareerFactionID(string careerAllianceName)
        {
            const int magesGuild    = 40;
            const int nobles        = 242;
            const int genericTemple = 450;
            const int merchants     = 510;

            // P2 is careerID
            int   careerID;
            Table factionsTable = QuestMachine.Instance.FactionsTable;

            if (factionsTable.HasValue(careerAllianceName))
            {
                careerID = Parser.ParseInt(factionsTable.GetValue("p2", careerAllianceName));
            }
            else
            {
                Debug.LogErrorFormat("Could not find careerAllianceName {0}", careerAllianceName);
                return(-1);
            }

            // Initial handling for Local_X.X career groups pending further review and information
            // These appear to use P3 for career association and will all resolve to Merchants for now
            // P3=0 (Apothecary), P3=1 (Town1), P3=2 (Armory), P3=3 (Bank), P3=10000 (unused in any quests)
            if (careerID < 0)
            {
                careerID = Parser.ParseInt(factionsTable.GetValue("p3", careerAllianceName));
            }

            // Handle Local_4.10k - unused in any quests and will default to Merchants for now
            if (careerID == 10000)
            {
                careerID = 0;
            }

            // Assign factionID based on careerID
            // How Daggerfall links these is not 100% confirmed, some guesses below
            // Most of these NPC careers seem to be aligned with faction #510 Merchants
            switch (careerID)
            {
            case 0:
            case 1:
            case 2:
            case 3:
            case 5:
            case 6:
            case 7:
            case 8:
            case 9:
            case 10:
            case 12:
            case 13:
            case 15:
                return(merchants);                          // Merchants

            case 11:
                return(magesGuild);                         // Mages Guild

            case 14:
                return(genericTemple);                      // Generic Temple seems to link all the temples together

            case 16:
                return(nobles);                             // Random Noble

            case 17:
            case 18:
            case 19:
            case 20:
            // Default for everything else will just be fairly generic "people of" faction
            // This at least ensures the object will compile to something valid
            default:                                        // Not sure if "Resident1-4" career really maps to regional "people of" in classic
                return(GameManager.Instance.PlayerGPS.GetPeopleOfCurrentRegion());
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Parse optional message tags from this resource.
        /// </summary>
        /// <param name="line"></param>
        protected void ParseMessageTags(string line)
        {
            string matchStr = @"anyInfo (?<info>\d+)|used (?<used>\d+)|rumors (?<rumors>\d+)|" +
                              @"anyInfo (?<infoName>\w+)|used (?<usedName>\w+)|rumors (?<rumorsName>\w+)";

            // Get message tag matches
            MatchCollection matches = Regex.Matches(line, matchStr);

            if (matches.Count == 0)
            {
                return;
            }

            // Grab tag values
            foreach (Match match in matches)
            {
                //
                // info
                //

                // Match info message ID
                Group info = match.Groups["info"];
                if (info.Success)
                {
                    infoMessageID = Parser.ParseInt(info.Value);
                }

                // Resolve info message name back to ID
                string infoName = match.Groups["infoName"].Value;
                if (infoMessageID == -1 && !string.IsNullOrEmpty(infoName))
                {
                    Table table = QuestMachine.Instance.StaticMessagesTable;
                    infoMessageID = Parser.ParseInt(table.GetValue("id", infoName));
                }

                //
                // used
                //

                // Match used message ID
                Group used = match.Groups["used"];
                if (used.Success)
                {
                    usedMessageID = Parser.ParseInt(used.Value);
                }

                // Resolve used message name back to ID
                string usedName = match.Groups["usedName"].Value;
                if (usedMessageID == -1 && !string.IsNullOrEmpty(usedName))
                {
                    Table table = QuestMachine.Instance.StaticMessagesTable;
                    usedMessageID = Parser.ParseInt(table.GetValue("id", usedName));
                }

                //
                // rumors
                //

                // Match rumors message ID
                Group rumors = match.Groups["rumors"];
                if (rumors.Success)
                {
                    rumorsMessageID = Parser.ParseInt(rumors.Value);
                }

                // Resolve rumors message name back to ID
                string rumorsName = match.Groups["rumorsName"].Value;
                if (rumorsMessageID == -1 && !string.IsNullOrEmpty(rumorsName))
                {
                    Table table = QuestMachine.Instance.StaticMessagesTable;
                    rumorsMessageID = Parser.ParseInt(table.GetValue("id", rumorsName));
                }
            }
        }
Ejemplo n.º 12
0
        void AssignHomeTown()
        {
            const string houseString = "house";

            Place  homePlace;
            string symbolName = string.Format("_{0}_home_", Symbol.Name);

            // If this is a Questor or individual NPC then use current location of player using a special helper
            if (isQuestor || (IsIndividualNPC && isIndividualAtHome))
            {
                homePlace = new Place(ParentQuest);
                if (GameManager.Instance.PlayerGPS.HasCurrentLocation)
                {
                    if (!homePlace.ConfigureFromPlayerLocation(symbolName))
                    {
                        throw new Exception("AssignHomeTown() could not configure questor/individual home from current player location.");
                    }

                    homePlaceSymbol = homePlace.Symbol;
                    ParentQuest.AddResource(homePlace);
                    LogHomePlace(homePlace);
                    return;
                }
            }

            // For other NPCs use default scope and building type
            Place.Scopes scope = Place.Scopes.Remote;
            string       buildingTypeString = houseString;

            // Adjust scope and building type based on faction hints
            int p1 = 0, p2 = 0, p3 = 0;

            if (!string.IsNullOrEmpty(factionTableKey))
            {
                // Get faction parameters
                p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", factionTableKey));
                p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", factionTableKey));
                p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", factionTableKey));

                // Set based on parameters
                if (p1 == 0 && p2 < -2 && p2 != -6)
                {
                    // From usage in the quests it appears -3 and lower are local.
                    // Referencing quest Sx009 where player must locate and click an NPC with only a home location to go by
                    // and K0C00Y04 where two Group_7 npcs are local.
                    // Interkarma Note: -6 is used by Thieves Guild introduction quest O0A0AL00 and should be a remote NPC. Treating -6 as remote.
                    scope = Place.Scopes.Local;
                }
                else if (p1 == 0 && p2 >= 0 && p2 <= 20 && p3 == 0)
                {
                    // Set to a specific building type
                    buildingTypeString = QuestMachine.Instance.PlacesTable.GetKeyForValue("p2", p2.ToString());
                }
            }

            // Get scope string - must be "local" or "remote"
            string scopeString = string.Empty;

            if (scope == Place.Scopes.Local)
            {
                scopeString = "local";
            }
            else if (scope == Place.Scopes.Remote)
            {
                scopeString = "remote";
            }
            else
            {
                throw new Exception("AssignHomeTown() scope must be either 'local' or 'remote'.");
            }

            // Create the home location - this will try to match NPC group (e.g. a Noble will select a Palace)
            try
            {
                // Try preferred location type
                string source = string.Format("Place {0} {1} {2}", symbolName, scopeString, buildingTypeString);
                homePlace = new Place(ParentQuest, source);
            }
            catch
            {
                // Otherwise try to use a generic house
                // If this doesn't work for some reason then next exception will prevent quest from starting
                string source = string.Format("Place {0} {1} {2}", symbolName, scopeString, houseString);
                homePlace = new Place(ParentQuest, source);
            }

            // Complete assigning home place
            homePlaceSymbol = homePlace.Symbol.Clone();
            ParentQuest.AddResource(homePlace);
            LogHomePlace(homePlace);
        }
Ejemplo n.º 13
0
        public override void SetResource(string line)
        {
            base.SetResource(line);

            string declMatchStr = @"(Item|item) (?<symbol>[a-zA-Z0-9_.-]+) (?<artifact>artifact) (?<itemName>[a-zA-Z0-9_.-]+)|(Item|item) (?<symbol>[a-zA-Z0-9_.-]+) (?<itemName>[a-zA-Z0-9_.-]+)";

            string optionsMatchStr = @"range (?<rangeLow>\d+) to (?<rangeHigh>\d+)|" +
                                     @"item class (?<itemClass>\d+) subclass (?<itemSubClass>\d+)";

            // Try to match source line with pattern
            string itemName     = string.Empty;
            int    itemClass    = -1;
            int    itemSubClass = -1;
            bool   isGold       = false;
            int    rangeLow     = -1;
            int    rangeHigh    = -1;
            Match  match        = Regex.Match(line, declMatchStr);

            if (match.Success)
            {
                // Store symbol for quest system
                Symbol = new Symbol(match.Groups["symbol"].Value);

                // Item or artifact name
                itemName = match.Groups["itemName"].Value;

                // Artifact status
                if (!string.IsNullOrEmpty(match.Groups["artifact"].Value))
                {
                    artifact = true;
                }

                // Set gold - this is not in the lookup table
                if (itemName == "gold")
                {
                    isGold = true;
                }

                // Split options from declaration
                string optionsLine = line.Substring(match.Length);

                // Match all options
                MatchCollection options = Regex.Matches(optionsLine, optionsMatchStr);
                foreach (Match option in options)
                {
                    // Range low value
                    Group rangeLowGroup = option.Groups["rangeLow"];
                    if (rangeLowGroup.Success)
                    {
                        rangeLow = Parser.ParseInt(rangeLowGroup.Value);
                    }

                    // Range high value
                    Group rangeHighGroup = option.Groups["rangeHigh"];
                    if (rangeHighGroup.Success)
                    {
                        rangeHigh = Parser.ParseInt(rangeHighGroup.Value);
                    }

                    // Item class value
                    Group itemClassGroup = option.Groups["itemClass"];
                    if (itemClassGroup.Success)
                    {
                        itemClass = Parser.ParseInt(itemClassGroup.Value);
                    }

                    // Item subclass value
                    Group itemSubClassGroup = option.Groups["itemSubClass"];
                    if (itemClassGroup.Success)
                    {
                        itemSubClass = Parser.ParseInt(itemSubClassGroup.Value);
                    }
                }

                // Create item
                if (!string.IsNullOrEmpty(itemName) && !isGold)
                {
                    item = CreateItem(itemName);                        // Create by name of item in lookup table
                }
                else if (itemClass != -1 && !isGold)
                {
                    item = CreateItem(itemClass, itemSubClass);         // Create item by class and subclass (a.k.a ItemGroup and GroupIndex)
                }
                else if (isGold)
                {
                    item = CreateGold(rangeLow, rangeHigh);             // Create gold pieces of amount by level or range values
                }
                else
                {
                    throw new Exception(string.Format("Could not create Item from line {0}", line));
                }

                // add conversation topics from anyInfo command tag
                AddConversationTopics();
            }
        }
        void AssignHomeTown()
        {
            Place  homePlace;
            string symbolName = string.Format("_{0}_home_", Symbol.Name);

            // If this is a Questor or individual NPC then use current location of player using a special helper
            if (isQuestor || (IsIndividualNPC && isIndividualAtHome))
            {
                homePlace = new Place(ParentQuest);
                if (GameManager.Instance.PlayerGPS.HasCurrentLocation)
                {
                    if (!homePlace.ConfigureFromPlayerLocation(symbolName))
                    {
                        throw new Exception("AssignHomeTown() could not configure questor/individual home from current player location.");
                    }

                    homePlaceSymbol = homePlace.Symbol;
                    ParentQuest.AddResource(homePlace);
                    LogHomePlace(homePlace);
                    return;
                }
            }

            // For other NPCs use default scope and building type
            Place.Scopes scope = Place.Scopes.Remote;
            string       buildingTypeString = "house2";

            // Adjust scope and building type based on faction hints
            int p1 = 0, p2 = 0, p3 = 0;

            if (!string.IsNullOrEmpty(factionTableKey))
            {
                // Get faction parameters
                p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", factionTableKey));
                p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", factionTableKey));
                p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", factionTableKey));

                // Set based on parameters
                if (p1 == 0 && p2 == -3 || p1 == 0 && p2 == -4)
                {
                    // For local types set to local place
                    // This will support Local_3.0 - Local_4.10k
                    // Referencing quest Sx009 where player must locate and click an NPC with only a home location to go by
                    scope = Place.Scopes.Local;
                }
                else if (p1 == 0 && p2 >= 0 && p2 <= 20 && p3 == 0)
                {
                    // Set to a specific building type
                    buildingTypeString = QuestMachine.Instance.PlacesTable.GetKeyForValue("p2", p2.ToString());
                }
            }

            // Get scope string - must be "local" or "remote"
            string scopeString = string.Empty;

            if (scope == Place.Scopes.Local)
            {
                scopeString = "local";
            }
            else if (scope == Place.Scopes.Remote)
            {
                scopeString = "remote";
            }
            else
            {
                throw new Exception("AssignHomeTown() scope must be either 'local' or 'remote'.");
            }

            // Create the home location
            string source = string.Format("Place {0} {1} {2}", symbolName, scopeString, buildingTypeString);

            homePlace       = new Place(ParentQuest, source);
            homePlaceSymbol = homePlace.Symbol.Clone();
            ParentQuest.AddResource(homePlace);
            LogHomePlace(homePlace);


            //
            // NOTE: Keeping the below for reference only at this time
            //

            //const string blank = "BLANK";

            //// If this is a Questor or individual NPC then use current location name
            //// Person is being instantiated where player currently is
            //if (isQuestor || (IsIndividualNPC && isIndividualAtHome))
            //{
            //    if (GameManager.Instance.PlayerGPS.HasCurrentLocation)
            //    {
            //        homeTownName = GameManager.Instance.PlayerGPS.CurrentLocation.Name;
            //        homeRegionName = GameManager.Instance.PlayerGPS.CurrentLocation.RegionName;
            //        homeBuildingName = blank;
            //        return;
            //    }
            //}

            //// Handle specific home Place assigned at create time
            //if (homePlaceSymbol != null)
            //{
            //    Place home = ParentQuest.GetPlace(homePlaceSymbol);
            //    if (home != null)
            //    {
            //        homeTownName = home.SiteDetails.locationName;
            //        homeRegionName = home.SiteDetails.regionName;
            //        homeBuildingName = home.SiteDetails.buildingName;
            //    }
            //}
            //else
            //{
            //    // Find a random location name from town types for flavour text
            //    // This might take a few attempts but will very quickly find a random town name
            //    int index;
            //    bool found = false;
            //    int regionIndex = GameManager.Instance.PlayerGPS.CurrentRegionIndex;
            //    DFRegion regionData = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetRegion(regionIndex);
            //    while (!found)
            //    {
            //        index = UnityEngine.Random.Range(0, regionData.MapTable.Length);
            //        DFRegion.LocationTypes locationType = regionData.MapTable[index].LocationType;
            //        if (locationType == DFRegion.LocationTypes.TownCity ||
            //            locationType == DFRegion.LocationTypes.TownHamlet ||
            //            locationType == DFRegion.LocationTypes.TownVillage)
            //        {
            //            homeTownName = regionData.MapNames[index];
            //            homeRegionName = regionData.Name;
            //            homeBuildingName = blank;
            //            found = true;
            //        }
            //    }
            //}

            //// Handle Local_3.x group NPCs (limited)
            //// These appear to be a special case of assigning a residential person who is automatically instantiated to home Place
            //// Creating a full target Place for this person automatically and storing in Quest
            //// NOTE: Understanding is still being developed here, likely will need to rework this later
            //if (QuestMachine.Instance.FactionsTable.HasValue(careerAllianceName))
            //{
            //    // Get params for this case
            //    int p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", careerAllianceName));
            //    int p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", careerAllianceName));
            //    //int p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", careerAllianceName));

            //    // Only supporting specific cases for now - can expand later based on testing and iteration of support
            //    // This will support Local_3.0 - Local_3.3
            //    // Referencing quest Sx009 here where player must locate and click an NPC with only a home location to go by
            //    if (p1 == 0 && p2 == -3)
            //    {
            //        // Just using "house2" here as actual meaning of p3 unknown
            //        string homeSymbol = string.Format("_{0}_home_", Symbol.Name);
            //        string source = string.Format("Place {0} remote house2", homeSymbol);
            //        Place home = new Place(ParentQuest, source);
            //        homePlaceSymbol = home.Symbol.Clone();
            //        ParentQuest.AddResource(home);
            //    }
            //    else if (p1 == 0 && p2 >= 0)
            //    {
            //        // Handle standard building types
            //        string buildingSymbol = string.Format("_{0}_building_", Symbol.Name);
            //        string buildingType = QuestMachine.Instance.PlacesTable.GetKeyForValue("p2", p2.ToString());
            //        string source = string.Format("Place {0} remote {1}", buildingSymbol, buildingType);
            //        Place building = new Place(ParentQuest, source);
            //        homePlaceSymbol = building.Symbol.Clone();
            //        ParentQuest.AddResource(building);
            //    }
            //}
        }
Ejemplo n.º 15
0
        public override void SetResource(string line)
        {
            string individualNPCName   = string.Empty;
            string factionTypeName     = string.Empty;
            string factionAllianceName = string.Empty;
            string careerAllianceName  = string.Empty;
            string genderName          = string.Empty;
            int    faceIndex           = -1;
            bool   atHome = false;

            base.SetResource(line);

            // Match strings
            string declMatchStr    = @"(Person|person) (?<symbol>[a-zA-Z0-9'_.-]+)";
            string optionsMatchStr = @"named (?<individualNPCName>[a-zA-Z0-9'_.-]+)|" +
                                     @"face (?<faceIndex>\d+)|" +
                                     @"(factionType|factiontype) (?<factionType>[a-zA-Z0-9'_.-]+)|" +
                                     @"faction (?<factionAlliance>[a-zA-Z0-9'_.-]+)|" +
                                     @"group (?<careerAlliance>[a-zA-Z0-9'_.-]+)|" +
                                     @"(?<gender>female|male)|" +
                                     @"(?<atHome>(atHome|athome))";

            // Try to match source line with pattern
            Match match = Regex.Match(line, declMatchStr);

            if (match.Success)
            {
                // Store symbol for quest system
                Symbol = new Symbol(match.Groups["symbol"].Value);

                // Match all options
                MatchCollection options = Regex.Matches(line, optionsMatchStr);
                foreach (Match option in options)
                {
                    // Individual NPC
                    Group individualNPCNameGroup = option.Groups["individualNPCName"];
                    if (individualNPCNameGroup.Success)
                    {
                        individualNPCName = individualNPCNameGroup.Value;
                    }

                    // Face
                    Group faceGroup = option.Groups["faceIndex"];
                    if (faceGroup.Success)
                    {
                        faceIndex = Parser.ParseInt(faceGroup.Value);
                    }

                    // Faction type
                    Group factionTypeGroup = option.Groups["factionType"];
                    if (factionTypeGroup.Success)
                    {
                        factionTypeName = factionTypeGroup.Value;
                    }

                    // Faction alliance
                    Group factionAllianceGroup = option.Groups["factionAlliance"];
                    if (factionAllianceGroup.Success)
                    {
                        factionAllianceName = factionAllianceGroup.Value;
                    }

                    // Group
                    Group careerAllianceGroup = option.Groups["careerAlliance"];
                    if (careerAllianceGroup.Success)
                    {
                        careerAllianceName = careerAllianceGroup.Value;
                    }

                    // Gender
                    Group genderGroup = option.Groups["gender"];
                    if (genderGroup.Success)
                    {
                        genderName = genderGroup.Value;
                    }

                    // At home
                    Group atHomeGroup = option.Groups["atHome"];
                    atHome = atHomeGroup.Success;
                }

                // Setup NPC based on input parameters
                if (!string.IsNullOrEmpty(individualNPCName))
                {
                    SetupIndividualNPC(individualNPCName);
                }
                else if (!string.IsNullOrEmpty(careerAllianceName))
                {
                    SetupCareerAllianceNPC(careerAllianceName);
                }
                else if (!string.IsNullOrEmpty(factionTypeName))
                {
                    SetupFactionTypeNPC(factionTypeName);
                }
                else if (!string.IsNullOrEmpty(factionAllianceName))
                {
                    SetupFactionAllianceNPC(factionAllianceName);
                }
                else
                {
                    throw new Exception(string.Format("Person resource could not identify NPC from line {0}", line));
                }

                // Assign NPC details
                AssignRace();
                AssignGender(genderName);
                AssignHUDFace(faceIndex);
                AssignDisplayName();
                AssignHomeTown();
                AssignGod();

                // Is NPC at home?
                isIndividualAtHome = atHome;

                // Done
                Debug.LogFormat("Created NPC {0} with FactionID #{1}.", displayName, factionData.id);
            }
        }
Ejemplo n.º 16
0
        // Gets factionID of a faction type NPC
        int GetFactionTypeFactionID(string factionTypeName)
        {
            // Only allowing a small range of random faction types for now
            FactionFile.FactionTypes[] randomFactionTypes = new FactionFile.FactionTypes[]
            {
                FactionFile.FactionTypes.Courts,
                FactionFile.FactionTypes.Province,
                FactionFile.FactionTypes.People,
                FactionFile.FactionTypes.Temple,
            };

            // P3 is faction type
            int   factionType;
            Table factionsTable = QuestMachine.Instance.FactionsTable;

            if (factionsTable.HasValue(factionTypeName))
            {
                factionType = Parser.ParseInt(factionsTable.GetValue("p3", factionTypeName));
            }
            else
            {
                Debug.LogErrorFormat("Could not find factionTypeName {0}", factionTypeName);
                return(-1);
            }

            // Handle random faction type
            // This selects from a restricted pool to ensure vital NPCs don't get randomly selected
            if (factionType == -1)
            {
                factionType = (int)UnityEngine.Random.Range(0, randomFactionTypes.Length);
            }

            // Assign factionID based on factionType
            // This value is 0-15 and maps to "type:" in faction.txt
            // Daggerfall seems to largely select from selected pool of factionType objects at random
            // But some factionTypes do not exist in file and suspect special handling for others
            // Treating on a case-by-case basis for now
            switch ((FactionFile.FactionTypes)factionType)
            {
            // These faction types do not generally have a specific region associated with them
            // Select from pool of all objects this faction type
            case FactionFile.FactionTypes.Daedra:
            case FactionFile.FactionTypes.Group:
            case FactionFile.FactionTypes.Subgroup:
            case FactionFile.FactionTypes.Official:
            case FactionFile.FactionTypes.Temple:
                return(GetRandomFactionOfType(factionType));

            // Faction type of God is never used in quests
            // Many of these factions do not have an NPC flat
            // Recommend never using - not sure how to redirect to working state yet
            case FactionFile.FactionTypes.God:
                return(GetRandomFactionOfType(factionType));

            // The individual type is not used by any canonical quests
            // It is more or less equivalent to reserving an individual NPC
            // Not recommended to use this in quests
            // Just returning a random person to ensure NPC is created
            case FactionFile.FactionTypes.Individual:
                return(GetRandomFactionOfType(factionType));

            // Not sure how to use vampire clans yet
            // These are *mostly* used by vampire quests where its assumed the player's vampire faction will be used
            // It wouldn't make sense for player to gain reputation with another vampire clan after all
            // As vampire factions not in game yet, just select one at random to ensure NPC is created
            case FactionFile.FactionTypes.VampireClan:
                return(GetRandomFactionOfType(factionType));

            // Assign an NPC from current player region
            case FactionFile.FactionTypes.Province:
                return(GetCurrentRegionFaction());

            // Not all regions have a witches coven associated
            // Just select a random coven for now
            case FactionFile.FactionTypes.WitchesCoven:
                return(GetRandomFactionOfType(factionType));

            // Type 10 Knightly_Guard does not exist in FACTION.TXT but IS used in some quests
            // Redirecting this to "Generic Knightly Order" #844 to ensure NPC is created
            case FactionFile.FactionTypes.KnightlyGuard:
                return(844);

            // Type 11 Magic_User does not exist in FACTION.TXT and is not used in any quests
            // Redirecting this to "Mages Guild" #40 to ensure NPC is created
            case FactionFile.FactionTypes.MagicUser:
                return(40);

            // Type 12 Generic_Group does not exist in FACTION.TXT and is not used in any quests
            // Redirecting this to a random choice between "Generic Temple" #450 and "Generic Knightly Order" #844 to ensure NPC is created
            case FactionFile.FactionTypes.Generic:
                return((UnityEngine.Random.Range(0f, 1f) < 0.5f) ? 450 : 844);

            // Type 13 Thieves_Den does not exist in FACTION.TXT and is not used in any quests
            // Redirecting this to "Thieves Guild" #42 to ensure NPC is created
            case FactionFile.FactionTypes.Thieves:
                return(42);

            // Get "court of" current region
            case FactionFile.FactionTypes.Courts:
                return(GetCourtOfCurrentRegion());

            // Get "people of" current region
            case FactionFile.FactionTypes.People:
                return(GameManager.Instance.PlayerGPS.GetPeopleOfCurrentRegion());

            // Give up
            default:
                return(-1);
            }
        }
Ejemplo n.º 17
0
        public override void SetResource(string line)
        {
            base.SetResource(line);

            string declMatchStr = @"(Clock|clock) (?<symbol>[a-zA-Z0-9_.-]+)";

            string optionsMatchStr = @"(?<ddhhmm>)\d+.\d+:\d+|" +
                                     @"(?<hhmm>)\d+:\d+|" +
                                     @"(?<mm>)\d+|" +
                                     @"flag (?<flag>\d+)|" +
                                     @"range (?<minRange>\d+) (?<maxRange>\d+)";

            // Try to match source line with pattern
            Match match = Regex.Match(line, declMatchStr);

            if (match.Success)
            {
                // Seed random
                //UnityEngine.Random.InitState(Time.renderedFrameCount);

                // Store symbol for quest system
                Symbol = new Symbol(match.Groups["symbol"].Value);

                // Split options from declaration
                string optionsLine = line.Substring(match.Length);

                // Match all options
                // TODO: Work out meaning of "flag" and "range" values
                int             timeValue0       = -1;
                int             timeValue1       = -1;
                int             currentTimeValue = 0;
                MatchCollection options          = Regex.Matches(optionsLine, optionsMatchStr);
                foreach (Match option in options)
                {
                    // Match any possible time value syntax
                    Group ddhhmmGroup = option.Groups["ddhhmm"];
                    Group hhmmGroup   = option.Groups["hhmm"];
                    Group mmGroup     = option.Groups["mm"];
                    if (ddhhmmGroup.Success || hhmmGroup.Success || mmGroup.Success)
                    {
                        // Get time value
                        int timeValue = MatchTimeValue(option.Value);

                        // Assign time value
                        if (currentTimeValue == 0)
                        {
                            timeValue0 = timeValue;
                            currentTimeValue++;
                        }
                        else if (currentTimeValue == 1)
                        {
                            timeValue1 = timeValue;
                            currentTimeValue++;
                        }
                        else
                        {
                            throw new Exception("Clock cannot specify more than 2 time values.");
                        }
                    }

                    // Unknown flag value
                    Group flagGroup = option.Groups["flag"];
                    if (flagGroup.Success)
                    {
                        flag = Parser.ParseInt(flagGroup.Value);
                    }

                    // Unknown minRange value
                    Group minRangeGroup = option.Groups["minRange"];
                    if (minRangeGroup.Success)
                    {
                        minRange = Parser.ParseInt(minRangeGroup.Value);
                    }

                    // Unknown maxRange value
                    Group maxRangeGroup = option.Groups["maxRange"];
                    if (maxRangeGroup.Success)
                    {
                        maxRange = Parser.ParseInt(maxRangeGroup.Value);
                    }
                }

                // Set total clock time based on values
                int clockTimeInSeconds = 0;
                if (currentTimeValue == 0)
                {
                    // No time value specifed: "clock _symbol_"
                    // Clock timer starts at a random value between 1 week and 1 minute
                    // TODO: Work out the actual range Daggerfall uses here
                    int minSeconds = GetTimeInSeconds(0, 0, 1);
                    int maxSeconds = GetTimeInSeconds(7, 0, 0);
                    clockTimeInSeconds = FromRange(minSeconds, maxSeconds);
                }
                else if (currentTimeValue == 1)
                {
                    // One time value specified: "clock _symbol_ dd.hh:mm"
                    // Clock timer starts at this value
                    clockTimeInSeconds = timeValue0;
                }
                else if (currentTimeValue == 2)
                {
                    // Two time values specified: "clock _symbol_ dd.hh:mm dd.hh:mm"
                    // Clock timer starts at a random point between timeValue0 (min) and timeValue1 (max)
                    // But second value must be greater than first to be a valid range
                    if (timeValue1 > timeValue0)
                    {
                        clockTimeInSeconds = FromRange(timeValue0, timeValue1);
                    }
                    else
                    {
                        clockTimeInSeconds = timeValue0;
                    }
                }

                // Flag & 16 seems to indicate clock should be
                // set to 2.5x cautious travel time of first Place resource specificed in quest script
                // Quests using this configuration will usually end once timer elapsed
                if ((flag & 16) == 16)
                {
                    clockTimeInSeconds = GetTravelTimeInSeconds();
                }

                // HACK: Force another travel time check when flag & 1, clock type involves some target, and clockTimeInSeconds is still 0
                // This ensures player has travel time from automatic NPC quests such as A0C00Y17 and quest does not end instantly
                if ((flag & 1) == 1 && maxRange > 0 && clockTimeInSeconds == 0)
                {
                    clockTimeInSeconds = GetTravelTimeInSeconds();
                }

                // TODO: Improve clock types using available information
                // Note that TEMPLATE misinterprets upper byte of flag value as "range min" and clock type as "range max"
                // And unfortunately some information about targets is lost by TEMPLATE, but this does not seem critical in most cases
                // Decompiled quest scripts can be fixed where necessary without re-decompiling all quests to a new timer format
                // Thanks and credit to ELENWEL and PANGO for discovering more information about timers
                // Refer to following links for more detail:
                //  https://en.uesp.net/wiki/Daggerfall:Quest_hacking_guide#Timers_Section
                //  https://forums.dfworkshop.net/viewtopic.php?f=23&t=1655&start=10#p19163
                //  https://forums.dfworkshop.net/viewtopic.php?f=23&t=1655&start=10#p19262
                // Timer "type" values that are currently stored in "range max":
                //  0   Random duration (Random time from min to max)
                //  1   Fixed duration (timer duration = min)
                //  2   One location or NPC (one location duration will be travelTime( here(), link1) * 1.5)
                //  3   Two locations/ NPCs, only one dungeon(gives extra week in some circumstances) (two locations (from=>To quest) duration will be travelTime(link1, link2)*1.5)
                //  4   Same as 2 ?
                //  5   Two locations / NPCs, both are dungeons(gives up to 2 extra weeks in some circumstances) (two locations duration will be travelTime(here(), link1)*1,5 + travelTime(link1, link2)*1.5)
                // Notes on flags:
                //  for destination based timer, flags & 0x100 : link1 is a NPC, if not link1 is a location
                //  for destination based timer, flags & 0x200 : link2 is a NPC, if not link1 is a location
                //  flags & 0x10 double timer’s duration(there and back)
                //  flags & 8 : timer can fire multiple time(unsure ? )
                //  flags & 4 : timer will set state to 0 upon expiration(unsure?)
                //  flags & 2 : timer will set state to 1 upon expiration +something(todo)
                //  flags & 1 : timer will set state to 1 upon expiration
                //  other flags are reserved for internal uses (timer state)

                // Set timer value in seconds
                InitialiseTimer(clockTimeInSeconds);
            }
        }
Ejemplo n.º 18
0
        public override void SetResource(string line)
        {
            base.SetResource(line);

            string declMatchStr = @"(Clock|clock) (?<symbol>[a-zA-Z0-9_.-]+)";

            string optionsMatchStr = @"(?<ddhhmm>)\d+.\d+:\d+|" +
                                     @"(?<hhmm>)\d+:\d+|" +
                                     @"(?<mm>)\d+|" +
                                     @"flag (?<flag>\d+)|" +
                                     @"range (?<minRange>\d+) (?<maxRange>\d+)";

            // Try to match source line with pattern
            Match match = Regex.Match(line, declMatchStr);

            if (match.Success)
            {
                // Seed random
                //UnityEngine.Random.InitState(Time.renderedFrameCount);

                // Store symbol for quest system
                Symbol = new Symbol(match.Groups["symbol"].Value);

                // Split options from declaration
                string optionsLine = line.Substring(match.Length);

                // Match all options
                // TODO: Work out meaning of "flag" and "range" values
                int             timeValue0       = -1;
                int             timeValue1       = -1;
                int             currentTimeValue = 0;
                MatchCollection options          = Regex.Matches(optionsLine, optionsMatchStr);
                foreach (Match option in options)
                {
                    // Match any possible time value syntax
                    Group ddhhmmGroup = option.Groups["ddhhmm"];
                    Group hhmmGroup   = option.Groups["hhmm"];
                    Group mmGroup     = option.Groups["mm"];
                    if (ddhhmmGroup.Success || hhmmGroup.Success | mmGroup.Success)
                    {
                        // Get time value
                        int timeValue = MatchTimeValue(option.Value);

                        // Assign time value
                        if (currentTimeValue == 0)
                        {
                            timeValue0 = timeValue;
                            currentTimeValue++;
                        }
                        else if (currentTimeValue == 1)
                        {
                            timeValue1 = timeValue;
                            currentTimeValue++;
                        }
                        else
                        {
                            throw new Exception("Clock cannot specify more than 2 time values.");
                        }
                    }

                    // Unknown flag value
                    Group flagGroup = option.Groups["flag"];
                    if (flagGroup.Success)
                    {
                        flag = Parser.ParseInt(flagGroup.Value);
                    }

                    // Unknown minRange value
                    Group minRangeGroup = option.Groups["minRange"];
                    if (minRangeGroup.Success)
                    {
                        minRange = Parser.ParseInt(minRangeGroup.Value);
                    }

                    // Unknown maxRange value
                    Group maxRangeGroup = option.Groups["maxRange"];
                    if (maxRangeGroup.Success)
                    {
                        maxRange = Parser.ParseInt(maxRangeGroup.Value);
                    }
                }

                // Set total clock time based on values
                int clockTimeInSeconds = 0;
                if (currentTimeValue == 0)
                {
                    // No time value specifed: "clock _symbol_"
                    // Clock timer starts at a random value between 1 week and 1 minute
                    // TODO: Work out the actual range Daggerfall uses here
                    int minSeconds = GetTimeInSeconds(0, 0, 1);
                    int maxSeconds = GetTimeInSeconds(7, 0, 0);
                    clockTimeInSeconds = FromRange(minSeconds, maxSeconds);
                }
                else if (currentTimeValue == 1)
                {
                    // One time value specified: "clock _symbol_ dd.hh:mm"
                    // Clock timer starts at this value
                    clockTimeInSeconds = timeValue0;
                }
                else if (currentTimeValue == 2)
                {
                    // Two time values specified: "clock _symbol_ dd.hh:mm dd.hh:mm"
                    // Clock timer starts at a random point between timeValue0 (min) and timeValue1 (max)
                    // But second value must be greater than first to be a valid range
                    if (timeValue1 > timeValue0)
                    {
                        clockTimeInSeconds = FromRange(timeValue0, timeValue1);
                    }
                    else
                    {
                        clockTimeInSeconds = timeValue0;
                    }
                }

                // Flag & 16 seems to indicate clock should be
                // set to 2.5x cautious travel time of first Place resource specificed in quest script
                // Quests using this configuration will usually end once timer elapsed
                if ((flag & 16) == 16)
                {
                    clockTimeInSeconds = GetTravelTimeInSeconds();
                }

                // HACK: Add range of time in days when flag & 1 and maxRange > 0
                // Still not positive this is the correct usage of minRange - maxRange
                if ((flag & 1) == 1 && maxRange > 0)
                {
                    // Perform another check for travel time if total time 0
                    // This ensures player has travel time from automatic NPCs
                    if (clockTimeInSeconds == 0)
                    {
                        clockTimeInSeconds = GetTravelTimeInSeconds();
                    }

                    // Add range
                    int randomDays = UnityEngine.Random.Range(minRange, maxRange);
                    clockTimeInSeconds += randomDays * DaggerfallDateTime.SecondsPerDay;
                }

                // Set timer value in seconds
                InitialiseTimer(clockTimeInSeconds);
            }
        }
Ejemplo n.º 19
0
        // Creates a career-based NPC like a Shopkeeper or Banker
        void SetupCareerAllianceNPC(string careerAllianceName)
        {
            // Special handling for Questor class
            // Will revisit this later when guilds are more integrated
            if (careerAllianceName.Equals("Questor", StringComparison.InvariantCultureIgnoreCase))
            {
                if (SetupQuestorNPC())
                {
                    return;
                }
            }

            // Handle Local_3.x group NPCs (limited)
            // These appear to be a special case of assigning a residential person who is automatically instantiated to home Place
            // Creating a full target Place for this person automatically and storing in Quest
            // NOTE: Understanding is still being developed here, likely will need to rework this later
            if (QuestMachine.Instance.FactionsTable.HasValue(careerAllianceName))
            {
                // Get params for this case
                int p1 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p1", careerAllianceName));
                int p2 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p2", careerAllianceName));
                //int p3 = Parser.ParseInt(QuestMachine.Instance.FactionsTable.GetValue("p3", careerAllianceName));

                // Only supporting specific cases for now - can expand later based on testing and iteration of support
                // This will support Local_3.0 - Local_3.3
                // Referencing quest Sx009 here where player must locate and click an NPC with only a home location to go by
                if (p1 == 0 && p2 == -3)
                {
                    // Just using "house2" here as actual meaning of p3 unknown
                    string homeSymbol = string.Format("_{0}_home_", Symbol.Name);
                    string source     = string.Format("Place {0} remote house2", homeSymbol);
                    Place  home       = new Place(ParentQuest, source);
                    homePlaceSymbol = home.Symbol.Clone();
                    ParentQuest.AddResource(home);
                }
                else if (p1 == 0 && p2 >= 0)
                {
                    // Handle standard building types
                    string buildingSymbol = string.Format("_{0}_building_", Symbol.Name);
                    string buildingType   = QuestMachine.Instance.PlacesTable.GetKeyForValue("p2", p2.ToString());
                    string source         = string.Format("Place {0} remote {1}", buildingSymbol, buildingType);
                    Place  building       = new Place(ParentQuest, source);
                    homePlaceSymbol = building.Symbol.Clone();
                    ParentQuest.AddResource(building);
                }
            }

            // Get faction data
            int factionID = GetCareerFactionID(careerAllianceName);

            if (factionID != -1)
            {
                FactionFile.FactionData factionData = GetFactionData(factionID);

                // Setup Person resource
                this.factionData = factionData;
            }
            else
            {
                Debug.LogErrorFormat("SetupCareerAllianceNPC() failed to setup {0}", careerAllianceName);
            }
        }