Ejemplo n.º 1
0
        /// <summary>
        /// Checks evaluation chain built at factory time.
        /// Assuming simple left-to-right short-circuit evaluation.
        /// </summary>
        /// <returns>Result of chained evaluations.</returns>
        bool CheckEvals()
        {
            // Must have at least one evaluation by this point
            if (evaluations.Count == 0)
            {
                throw new Exception("WhenTask condition has no evaluations.");
            }

            // Evaluate conditions left to right
            bool left = false, right = false;

            for (int i = 0; i < evaluations.Count; i++)
            {
                // Get task state based on operator
                Operator op         = evaluations[i].op;
                Symbol   taskSymbol = new Symbol(evaluations[i].task);
                switch (op)
                {
                case Operator.When:
                case Operator.And:
                case Operator.Or:
                    right = IsTaskSet(taskSymbol);
                    break;

                case Operator.WhenNot:
                case Operator.AndNot:
                case Operator.OrNot:
                    right = !IsTaskSet(taskSymbol);
                    break;

                default:
                    return(false);
                }

                // Evaluate short-circuit conditions
                switch (op)
                {
                // Handle single-task condition
                case Operator.When:
                case Operator.WhenNot:
                    if (evaluations.Count == 1 && !right)
                    {
                        return(false);
                    }
                    break;

                // Both sides must be true
                case Operator.And:
                case Operator.AndNot:
                    if (!left || !right)
                    {
                        // Allow avaluation to continue unless at the end of chain
                        if (i < evaluations.Count - 1)
                        {
                            right = false;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        right = true;
                    }

                    // Exit true when right is true and next condition is an Or/OrNot
                    if (right == true && i < evaluations.Count - 1)
                    {
                        if (evaluations[i + 1].op == Operator.Or ||
                            evaluations[i + 1].op == Operator.OrNot)
                        {
                            return(true);
                        }
                    }

                    break;

                // Either side can be true to result as true
                case Operator.Or:
                case Operator.OrNot:
                    if (!left && !right)
                    {
                        // Allow avaluation to continue unless at the end of chain
                        if (i < evaluations.Count - 1)
                        {
                            right = false;
                        }
                        else
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        right = true;
                    }
                    break;

                default:
                    return(false);
                }

                // Move to the right
                left = right;
            }

            // All conditions have evaluated
            return(true);
        }
Ejemplo n.º 2
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.º 3
0
 public Foe GetFoe(Symbol symbol)
 {
     return(GetResource(symbol) as Foe);
 }
Ejemplo n.º 4
0
 public Item GetItem(Symbol symbol)
 {
     return(GetResource(symbol) as Item);
 }
Ejemplo n.º 5
0
 public Person GetPerson(Symbol symbol)
 {
     return(GetResource(symbol) as Person);
 }
Ejemplo n.º 6
0
 public Place GetPlace(Symbol symbol)
 {
     return(GetResource(symbol) as Place);
 }
Ejemplo n.º 7
0
 public Clock GetClock(Symbol symbol)
 {
     return(GetResource(symbol) as Clock);
 }