コード例 #1
0
 internal HandlerRules(IHandlers <TObject, TBaseStatus> handlers, TBaseStatus status, TBaseStatus overridden, bool effect)
 {
     this.handlers   = handlers;
     this.status     = status;
     this.overridden = overridden;
     this.effect     = effect;
 }
コード例 #2
0
 void IHandlers <TObject> .SetHandler(TBaseStatus status, TBaseStatus overridden, bool increased, bool effect, OnChangedHandler <TObject> handler)
 {
     if (!onChangedHandlers.ContainsKey(status))
     {
         onChangedHandlers.Add(status, new DefaultValueDictionary <StatusChange, OnChangedHandler <TObject> >());
     }
     onChangedHandlers[status][new StatusChange(overridden, increased, effect)] = handler;
 }
コード例 #3
0
 /// <param name="status">The status to which this instance will add its value</param>
 /// <param name="value">The amount by which this instance will increase its status</param>
 /// <param name="cancelPriority">An instance with lower cancel priority will be cancelled before an instance with
 /// higher priority when Cancel() is called on this status.</param>
 /// <param name="type">
 /// The InstanceType determines whether this instance will feed, suppress, or prevent its status.
 /// (Feed is the default and most common. When a status is cancelled, its "Feed" StatusInstances are removed.)
 /// </param>
 public StatusInstance(TBaseStatus status, int value = 1, int cancelPriority = 0, InstanceType type = InstanceType.Feed, int?overrideSetIndex = null)
 {
     Status                = status;
     internalValue         = value;
     CancelPriority        = cancelPriority;
     InstanceType          = type;
     this.overrideSetIndex = overrideSetIndex;
 }
コード例 #4
0
 void IHandlers <TObject> .SetHandler(TBaseStatus ignored, TBaseStatus overridden, bool increased, bool effect, OnChangedHandler <TObject> handler)
 {
     if (onChangedOverrides == null)
     {
         onChangedOverrides = new DefaultValueDictionary <StatusChange, OnChangedHandler <TObject> >();
     }
     onChangedOverrides[new StatusChange(overridden, increased, effect)] = handler;
 }
コード例 #5
0
 OnChangedHandler <TObject> IHandlers <TObject> .GetHandler(TBaseStatus status, TBaseStatus overridden, bool increased, bool effect)
 {
     if (!onChangedHandlers.ContainsKey(status))
     {
         return(null);
     }
     return(onChangedHandlers[status][new StatusChange(overridden, increased, effect)]);
 }
コード例 #6
0
 OnChangedHandler <TObject> IHandlers <TObject> .GetHandler(TBaseStatus status, TBaseStatus ignored, bool increased, bool effect)
 {
     if (onChangedOverrides == null)
     {
         return(null);
     }
     return(onChangedOverrides[new StatusChange(status, increased, effect)]);
 }
コード例 #7
0
        /// <summary>
        /// Add a StatusInstance to this tracker, updating the value of the status associated with the given instance.
        /// </summary>
        public bool AddStatusInstance(StatusInstance <TObject> instance)
        {
            if (instance == null)
            {
                throw new ArgumentNullException();
            }
            if (instance.tracker != null && instance.tracker != this)
            {
                throw new InvalidOperationException("Already added to another tracker");
            }
            TBaseStatus  status = instance.Status;
            InstanceType type   = instance.InstanceType;

            if (type == InstanceType.Feed)
            {
                if (currentRaw[InstanceType.Prevent][status] > 0)
                {
                    return(false);
                }
                var preventableStatuses = new List <TBaseStatus> {
                    status
                }.Concat(rules.statusesExtendedBy[status]);
                foreach (var preventableStatus in preventableStatuses)
                {
                    if (rules.extraPreventionConditions.AnyValues(preventableStatus))
                    {
                        foreach (var condition in rules.extraPreventionConditions[preventableStatus])
                        {
                            if (condition(obj, preventableStatus))
                            {
                                return(false);
                            }
                        }
                    }
                }
                if (rules.SingleInstance[status])
                {
                    foreach (StatusInstance <TObject> removedInstance in statusInstances[InstanceType.Feed][status])
                    {
                        removedInstance.tracker = null;
                    }
                    statusInstances[InstanceType.Feed].Clear(status);
                }
            }
            if (statusInstances[type].AddUnique(status, instance))
            {
                instance.tracker = this;
                CheckInstanceChanged(instance);
                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #8
0
 /// <summary>
 /// Cancel the given status, removing all "Feed" StatusInstances that have been added to this tracker for this status.
 /// (This will return the value of this status to zero, unless other statuses are feeding this one.)
 /// </summary>
 public void Cancel(TBaseStatus status)
 {
     foreach (var instance in statusInstances[InstanceType.Feed][status].OrderBy(x => x.CancelPriority))
     {
         RemoveStatusInstance(instance);
     }
     foreach (TBaseStatus extendingStatus in rules.statusesThatExtend[status])
     {
         Cancel(extendingStatus);
     }
 }
コード例 #9
0
 internal Aggregator GetAggregator(TBaseStatus status, InstanceType type)
 {
     if (type == InstanceType.Feed)
     {
         Aggregator agg = valueAggs[status];
         if (agg != null)
         {
             return(agg);
         }
     }
     return(defaultAggs[type]);
 }
コード例 #10
0
        private OnChangedHandler <TObject> GetHandler(TBaseStatus status, bool increased, bool effect)
        {
            var change = new StatusChange(status, increased, effect);
            OnChangedHandler <TObject> result;

            foreach (var dict in changeStack)
            {
                if (dict.TryGetValue(change, out result))
                {
                    return(result);
                }
            }
            return(null);
        }
コード例 #11
0
            protected static TBaseStatus[] Convert <TStatus>(TStatus[] statuses) where TStatus : struct
            {
                if (statuses == null)
                {
                    return(null);
                }
                var result = new TBaseStatus[statuses.Length];

                for (int i = 0; i < statuses.Length; ++i)
                {
                    result[i] = BaseStatusSystem <TObject, TBaseStatus> .Convert(statuses[i]);
                }
                return(result);
            }
コード例 #12
0
        /// <summary>
        /// Create a new StatusInstance and add it to this tracker, updating the value of the given status.
        /// Returns the newly created instance, if successfully added, or null, if not.
        /// </summary>
        /// <param name="status">The status to which the instance will add its value</param>
        /// <param name="value">The amount by which the instance will increase the given status</param>
        /// <param name="cancelPriority">An instance with lower cancel priority will be cancelled before an instance with
        /// higher priority when Cancel() is called on its status.</param>
        /// <param name="type">
        /// The InstanceType determines whether the instance will feed, suppress, or prevent its status.
        /// (Feed is the default and most common. When a status is cancelled, its "Feed" StatusInstances are removed.)
        /// </param>
        public StatusInstance <TObject> Add(TBaseStatus status, int value = 1, int cancelPriority                   = 0,
                                            InstanceType type             = InstanceType.Feed, int?overrideSetIndex = null)
        {
            var instance = new StatusInstance <TObject>(status, value, cancelPriority, type, overrideSetIndex);

            if (AddStatusInstance(instance))
            {
                return(instance);
            }
            else
            {
                return(null);
            }
        }
コード例 #13
0
 /// <summary>
 /// Retrieve the current int value of the given status.
 /// The status's value can also be directly set, but only if the SingleInstance property is true for this status.
 /// </summary>
 public int this[TBaseStatus status] {
     get { return(currentActualValues[status]); }
     set {
         if (!rules.SingleInstance[status])
         {
             throw new InvalidOperationException("'SingleInstance' must be true in order to set a value directly.");
         }
         foreach (var instance in statusInstances[InstanceType.Feed][status])
         {
             instance.Value = value;
             return;                                                     // If any instances exist, change the value of the first one, then return.
         }
         AddStatusInstance(new StatusInstance <TObject>(status, value)); // Otherwise, create a new one.
     }
 }
コード例 #14
0
        private void CheckActualValueChanged(TBaseStatus status)
        {
            int newValue;

            if (currentRaw[InstanceType.Suppress][status] > 0)
            {
                newValue = 0;
            }
            else
            {
                newValue = currentRaw[InstanceType.Feed][status];
            }
            int oldValue = currentActualValues[status];

            if (newValue != oldValue)
            {
                currentActualValues[status] = newValue;
                bool increased = newValue > oldValue;
                if (!GenerateNoMessages)
                {
                    GetHandler(status, increased, false)?.Invoke(obj, status, oldValue, newValue);
                }
                if (!GenerateNoEffects)
                {
                    GetHandler(status, increased, true)?.Invoke(obj, status, oldValue, newValue);
                }
                UpdateFeed(status, InstanceType.Feed, newValue);
                if (increased)
                {
                    foreach (TBaseStatus cancelledStatus in rules.statusesCancelledBy[status])
                    {
                        var pair      = new StatusPair(status, cancelledStatus);
                        var condition = rules.cancellationConditions[pair];                         // if a condition exists, it must return true for the
                        if (condition == null || condition(newValue))
                        {
                            Cancel(cancelledStatus);                                                                  // status to be cancelled.
                        }
                    }
                }
                UpdateFeed(status, InstanceType.Suppress, newValue);                 // Cancellations happen before suppression to prevent some infinite loops
                UpdateFeed(status, InstanceType.Prevent, newValue);
            }
        }
コード例 #15
0
        private void ReadOtherRule(TBaseStatus status)
        {
            string token = tokens[idx];

            switch (token)
            {
            case "total":
            case "max":
            case "bool":
                SetAggregator(status, token);
                break;

            case "single":
            case "multiple":
                SetInstanceLimit(status, token);
                break;
            }
            ++idx;
        }
コード例 #16
0
        /// <summary>
        /// Remove a StatusInstance from this tracker, updating the value of the status associated with the given instance.
        /// Returns true if successful, or false if the instance wasn't in this tracker.
        /// </summary>
        public bool RemoveStatusInstance(StatusInstance <TObject> instance)
        {
            if (instance == null)
            {
                throw new ArgumentNullException();
            }
            TBaseStatus  status = instance.Status;
            InstanceType type   = instance.InstanceType;

            if (statusInstances[type].Remove(status, instance))
            {
                instance.tracker = null;
                CheckInstanceChanged(instance);
                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #17
0
        private void ReadBasicRule(TBaseStatus status)
        {
            string comparisonOperator = null, comparisonValue = null, verb = null, otherStatus = null, fedValue = null;

            if (TryConsume(StatusParser.IsComparisonOperator, ref comparisonOperator))
            {
                ConsumeOrError(StatusParser.IsNumber, ref comparisonValue);
            }
            ConsumeOrError(StatusParser.IsBasicVerb, ref verb);
            while (true)
            {
                ConsumeOrError(IsStatus, ref otherStatus);
                TryConsume(StatusParser.IsNumber, ref fedValue);
                SetRule(false, status, comparisonOperator, comparisonValue, verb, otherStatus, fedValue);
                if (!TryConsume(","))
                {
                    break;
                }
            }
        }
コード例 #18
0
 private bool TryParse(string token, out TBaseStatus status)
 {
     if (typeof(TBaseStatus).IsEnum && Enum.TryParse(token, true, out status))
     {
         return(true);
     }
     if (extraEnums != null)
     {
         if (baseTryParse == null)
         {
             foreach (var method in typeof(Enum).GetMethods())
             {
                 var parameters = method.GetParameters();
                 if (method.Name == "TryParse" && parameters.Length == 3 && method.IsGenericMethod &&
                     parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(bool) &&
                     parameters[2].ParameterType.GetElementType() == method.GetGenericArguments()[0])
                 {
                     baseTryParse = method;
                     break;
                 }
             }
             if (baseTryParse == null)
             {
                 throw new MissingMethodException("Enum.TryParse<TEnum>(string, bool, TEnum) seems to be missing.");
             }
         }
         foreach (Type t in extraEnums)
         {
             MethodInfo m       = baseTryParse.MakeGenericMethod(t);
             object[]   mParams = new object[] { token, true, null };
             bool       success = (bool)m.Invoke(null, mParams);
             if (success)
             {
                 status = (TBaseStatus)mParams[2];
                 return(true);
             }
         }
     }
     status = default(TBaseStatus);
     return(false);
 }
コード例 #19
0
        private void ReadRule(TBaseStatus status)
        {
            string token = tokens[idx];

            if (token.IsBasicVerb() || token.IsComparisonOperator())
            {
                ReadBasicRule(status);
            }
            else if (token.IsInvertedVerb() || token.IsNumber())
            {
                ReadInvertedRule(status);
            }
            else if (token.IsAggregator() || token.IsInstanceLimiter())
            {
                ReadOtherRule(status);
            }
            else
            {
                throw new InvalidDataException($"Syntax error: Unexpected {token}.");
            }
        }
コード例 #20
0
 protected StatusInstance(StatusInstance <TObject> copyFrom, int?value = null, int?cancelPriority = null, InstanceType?type = null, int?overrideSetIndex = null)
 {
     if (copyFrom == null)
     {
         throw new ArgumentNullException("copyFrom");
     }
     Status = copyFrom.Status;
     if (value == null)
     {
         internalValue = copyFrom.internalValue;
     }
     else
     {
         internalValue = value.Value;
     }
     if (cancelPriority == null)
     {
         CancelPriority = copyFrom.CancelPriority;
     }
     else
     {
         CancelPriority = cancelPriority.Value;
     }
     if (type == null)
     {
         InstanceType = copyFrom.InstanceType;
     }
     else
     {
         InstanceType = type.Value;
     }
     if (overrideSetIndex == null)
     {
         this.overrideSetIndex = copyFrom.overrideSetIndex;
     }
     else
     {
         this.overrideSetIndex = overrideSetIndex;
     }
 }
コード例 #21
0
        private void BeginBlock(TBaseStatus status)
        {
            while (idx < tokens.Count)
            {
                string token = tokens[idx];
                switch (token)
                {
                case "}":
                    ++idx;                             //consume the closing brace
                    return;                            // then we're done with this block.

                case ";":
                case "\r\n":                         //ignore semicolons and newlines.
                    ++idx;
                    break;

                default:
                    ReadRule(status);                             //Otherwise, it must be the start of a rule.
                    break;
                }
            }
            throw new EndOfStreamException("Unexpected end of file: Expected closing brace '}'.");
        }
コード例 #22
0
        private void SetAggregator(TBaseStatus status, string aggString)
        {
            Func <IEnumerable <int>, int> agg = null;

            if (rulesDefaultAggregator == null || rulesDefaultAggregator != aggString) //if the rules default agg is something unknown,
            {
                switch (aggString)                                                     // or just doesn't match the current one, set it.
                {
                case "total":
                    agg = rules.Total;
                    break;

                case "bool":
                    agg = rules.Bool;
                    break;

                case "max":
                    agg = rules.MaximumOrZero;
                    break;
                }
            }
            rules[status].Aggregator = agg;
        }
コード例 #23
0
 private void UpdateFeed(TBaseStatus status, InstanceType type, int newValue)
 {
     foreach (TBaseStatus fedStatus in rules.statusesFedBy[type][status])
     {
         int       newFedValue = newValue;
         var       pair        = new StatusPair(status, fedStatus);
         Converter conv;
         if (rules.converters[type].TryGetValue(pair, out conv))
         {
             newFedValue = conv(newFedValue);
         }
         int oldFedValue;
         Dictionary <TBaseStatus, int> fedValues;
         if (internalFeeds[type].TryGetValue(fedStatus, out fedValues))
         {
             fedValues.TryGetValue(status, out oldFedValue);
         }
         else
         {
             oldFedValue = 0;
         }
         if (newFedValue != oldFedValue)
         {
             if (fedValues == null)
             {
                 fedValues = new Dictionary <TBaseStatus, int>();
                 fedValues.Add(status, newFedValue);
                 internalFeeds[type].Add(fedStatus, fedValues);
             }
             else
             {
                 fedValues[status] = newFedValue;
             }
             CheckRawChanged(fedStatus, type);
         }
     }
 }
コード例 #24
0
 private void SetInstanceLimit(TBaseStatus status, string limitString)
 {
     rules[status].SingleInstance = (limitString == "single");
 }
コード例 #25
0
        private void CheckRawChanged(TBaseStatus status, InstanceType type)
        {
            bool stacked = false;

            if (rules.overrideSetsForStatuses.TryGetValue(status, out int overrideSetIndex))
            {
                stacked = true;
                OverrideSet <TObject> overrideSet = rules.overrideSets[overrideSetIndex];
                if (overrideSet == null)
                {
                    throw new InvalidOperationException($"Override set {overrideSetIndex} does not exist");
                }
                changeStack.Add(overrideSet.onChangedOverrides);
            }
            else if (rules.onChangedHandlers[status] != null)
            {
                stacked = true;
                changeStack.Add(rules.onChangedHandlers[status]);
            }
            var values = statusInstances[type][status].Select(x => x.Value);

            if (internalFeeds[type].ContainsKey(status))
            {
                values = values.Concat(internalFeeds[type][status].Values);
            }
            IEnumerable <TBaseStatus> upstreamStatuses;            // 'Upstream' and 'downstream' statuses change depending on the InstanceType.
            IEnumerable <TBaseStatus> downstreamStatuses;          // Value changes to a status are also applied to statuses that this one extends...

            if (type == InstanceType.Feed)
            {
                upstreamStatuses   = rules.statusesThatExtend[status];
                downstreamStatuses = rules.statusesExtendedBy[status];
            }
            else
            {
                upstreamStatuses   = rules.statusesExtendedBy[status];               // ...while negative changes to a status go the other way,
                downstreamStatuses = rules.statusesThatExtend[status];               // being applied to statuses that extend this one.
            }
            foreach (TBaseStatus otherStatus in upstreamStatuses)
            {
                values = values.Concat(statusInstances[type][otherStatus].Select(x => x.Value));
                if (internalFeeds[type].ContainsKey(otherStatus))
                {
                    values = values.Concat(internalFeeds[type][otherStatus].Values);
                }
            }
            int newValue = rules.GetAggregator(status, type)(values);
            int oldValue = currentRaw[type][status];

            if (newValue != oldValue)
            {
                currentRaw[type][status] = newValue;
                if (type == InstanceType.Feed || type == InstanceType.Suppress)
                {
                    CheckActualValueChanged(status);
                }
            }
            foreach (TBaseStatus otherStatus in downstreamStatuses)
            {
                CheckRawChanged(otherStatus, type);
            }
            if (stacked)
            {
                changeStack.RemoveAt(changeStack.Count - 1);
            }
        }
コード例 #26
0
 /// <summary>
 /// Override message or effect behavior whenever a change in *this* status or StatusInstance (i.e., the status
 /// or StatusInstance which is using this override set) causes a change in *another* status.
 /// </summary>
 /// <param name="overridden">The status whose message/effect behavior should be overridden</param>
 public StatusSystem <TObject> .StatusHandlers Overrides(TBaseStatus overridden)
 => new StatusSystem <TObject> .StatusHandlers(this, default(TBaseStatus), overridden);
コード例 #27
0
 internal StatusRules(BaseStatusSystem <TObject, TBaseStatus> rules, TBaseStatus status) : base(rules, status, status)
 {
     this.rules  = rules;
     this.status = status;
 }
コード例 #28
0
        private void SetRule(bool inverted, TBaseStatus status, string comparisonOperatorString,
                             string comparisonValueString, string verbString, string otherStatusString, string fedValueString)
        {
            TBaseStatus otherStatus;

            if (!TryParse(otherStatusString, out otherStatus))
            {
                throw new InvalidDataException($"Error: {otherStatusString} not recognized as any given type.");
            }
            TBaseStatus sourceStatus, targetStatus;

            if (inverted)
            {
                sourceStatus = otherStatus;
                targetStatus = status;
                verbString   = GetBasicVerb(verbString);
            }
            else
            {
                sourceStatus = status;
                targetStatus = otherStatus;
            }
            Func <int, bool> condition = GetCondition(comparisonOperatorString, comparisonValueString);

            if (verbString != "feeds")              // Only feeds can have fed values.
            {
                if (fedValueString != null)
                {
                    throw new InvalidDataException($"Unexpected value with {verbString}.");
                }
                if (verbString == "extends")
                {
                    if (comparisonOperatorString != null || comparisonValueString != null)                      // and no conditions for this one.
                    {
                        throw new InvalidDataException($"Unexpected condition with {verbString}.");
                    }
                }
            }
            try {
                switch (verbString)
                {
                case "foils":
                    rules[sourceStatus].Foils(condition, targetStatus);
                    break;

                case "cancels":
                    rules[sourceStatus].Cancels(condition, targetStatus);
                    break;

                case "extends":
                    rules[sourceStatus].Extends(targetStatus);
                    break;

                default:
                    if (fedValueString == null)
                    {
                        switch (verbString)
                        {
                        case "feeds":
                            rules[sourceStatus].Feeds(condition, targetStatus);
                            break;

                        case "suppresses":
                            rules[sourceStatus].Suppresses(condition, targetStatus);
                            break;

                        case "prevents":
                            rules[sourceStatus].Prevents(condition, targetStatus);
                            break;
                        }
                    }
                    else
                    {
                        int fedValue = int.Parse(fedValueString);
                        switch (verbString)
                        {
                        case "feeds":
                            rules[sourceStatus].Feeds(fedValue, condition, targetStatus);
                            break;

                            /*case "suppresses":
                             *      rules[sourceStatus].Suppresses(fedValue, condition, targetStatus);
                             *      break;
                             * case "prevents":
                             *      rules[sourceStatus].Prevents(fedValue, condition, targetStatus);
                             *      break;*/
                        }
                    }
                    break;
                }
            }
            catch (ArgumentException e) {
                throw new InvalidDataException("Likely illegal condition. See inner exception.", e);
            }
        }
コード例 #29
0
 /// <summary>
 /// Conveniently create a StatusInstance compatible with this tracker. Does not add the StatusInstance to the tracker automatically.
 /// </summary>
 /// <param name="status">The status to which the instance will add its value</param>
 /// <param name="value">The amount by which the instance will increase its status</param>
 /// <param name="cancelPriority">An instance with lower cancel priority will be cancelled before an instance with
 /// higher priority when Cancel() is called on this status.</param>
 /// <param name="type">
 /// The InstanceType determines whether the instance will feed, suppress, or prevent its status.
 /// (Feed is the default and most common. When a status is cancelled, its "Feed" StatusInstances are removed.)
 /// </param>
 public StatusInstance <TObject> CreateStatusInstance(TBaseStatus status, int value = 1, int cancelPriority                   = 0,
                                                      InstanceType type             = InstanceType.Feed, int?overrideSetIndex = null)
 {
     return(new StatusInstance <TObject>(status, value, cancelPriority, type, overrideSetIndex));
 }
コード例 #30
0
 /// <summary>
 /// Returns true if the current value of the given status is greater than zero.
 /// </summary>
 public bool HasStatus(TBaseStatus status) => currentActualValues[status] > 0;