Пример #1
0
        /// <summary>
        /// Adds quest heading text to the main quest's Dialogue, Journal, and HUD categories.
        /// </summary>
        /// <param name="questBuilder">QuestBuilder.</param>
        /// <param name="goal">Goal step, which may contain completion text.</param>
        protected virtual void AddQuestHeadings(QuestBuilder questBuilder, PlanStep goal)
        {
            var hasSuccessfulDialogueText = !StringField.IsNullOrEmpty(goal.action.actionText.completedText.dialogueText);
            var hasSuccessfulJournalText  = !StringField.IsNullOrEmpty(goal.action.actionText.completedText.journalText);

            AddQuestHeading(questBuilder, QuestContentCategory.Dialogue, hasSuccessfulDialogueText);
            AddQuestHeading(questBuilder, QuestContentCategory.Journal, hasSuccessfulJournalText);
            AddQuestHeading(questBuilder, QuestContentCategory.HUD, false);
        }
Пример #2
0
        public WorldModel worldModel; // Resulting world model.

        public Plan(Plan plan, PlanStep step, WorldModel worldModel)
        {
            this.steps = new List <PlanStep>();
            if (plan != null)
            {
                this.steps.AddRange(plan.steps);
            }
            if (step != null)
            {
                steps.Add(step);
            }
            this.worldModel = worldModel;
        }
Пример #3
0
        private PlanStep GetStep(Fact fact, Action action)
        {
            foreach (var step in masterStepList)
            {
                if (step.fact == fact && step.action == action)
                {
                    return(step);
                }
            }
            var newStep = new PlanStep(fact, action);

            masterStepList.Add(newStep);
            return(newStep);
        }
Пример #4
0
        protected virtual void AddTagsToDictionary(TagDictionary tagDictionary, PlanStep goal)
        {
            if (tagDictionary == null || goal == null)
            {
                return;
            }
            tagDictionary.dict.Add(QuestMachineTags.DOMAIN, StringField.GetStringValue(goal.fact.domainType.displayName));
            tagDictionary.dict.Add(QuestMachineTags.ACTION, StringField.GetStringValue(goal.action.displayName));
            tagDictionary.dict.Add(QuestMachineTags.TARGET, StringField.GetStringValue(goal.fact.entityType.displayName));
            tagDictionary.dict.Add(QuestMachineTags.TARGETS, StringField.GetStringValue(goal.fact.entityType.pluralDisplayName));
            tagDictionary.dict.Add(QuestMachineTags.TARGETDESCRIPTOR, goal.fact.entityType.GetDescriptor(goal.requiredCounterValue));
            var completion = goal.action.completion;

            if (completion.mode == ActionCompletion.Mode.Counter)
            {
                tagDictionary.dict.Add(QuestMachineTags.COUNTERGOAL, goal.requiredCounterValue.ToString());
            }
        }
Пример #5
0
        // Currently use a BFS rather than A* because it produces better results since there's no good heuristic yet.
        private IEnumerator BFS(WorldModel initialWorldModel, PlanStep goal)
        {
            yield return(null);

            var Q = new Queue <Plan>();

            // Queue the initial state:
            Q.Enqueue(new Plan(null, null, initialWorldModel));

            int numStepsChecked = 0;
            int safeguard       = 0;

            while (Q.Count > 0 && safeguard < 1000)
            {
                safeguard++;

                numStepsChecked++;
                if (numStepsChecked > maxStepsPerFrame)
                {
                    numStepsChecked = 0;
                    yield return(null);
                }

                var current = Q.Dequeue();

                //--- For debugging:
                //var indent = string.Empty;
                //for (int i = 0; i < current.steps.Count; i++) { indent += "        "; }
                //var lastStep = (current.steps.Count > 0) ? current.steps[current.steps.Count - 1] : null;
                //Debug.Log(indent + "Goal met (lastStep=" + lastStep + ")? " + current.worldModel.AreRequirementsMet(goal.action.requirements));

                // If the current state meets the goal requirements, return a finished plan:
                if (current.worldModel.AreRequirementsMet(goal.action.requirements))
                {
                    plan = new Plan(current, goal, null);
                    yield break;
                }

                // Otherwise queue up the actions that are valid in the current state:
                var lastStep = (current.steps.Count > 0) ? current.steps[current.steps.Count - 1] : null;
                foreach (var fact in current.worldModel.facts)
                {
                    var actions = GetEntityActions(fact.entityType);
                    if (actions == null)
                    {
                        continue;
                    }
                    foreach (var action in actions)
                    {
                        if (fact == null || fact.entityType == null || action == null)
                        {
                            continue;
                        }
                        if (lastStep != null && fact == lastStep.fact && action == lastStep.action)
                        {
                            continue;                                                                         // Don't repeat last action.
                        }
                        if (!current.worldModel.AreRequirementsMet(action.requirements))
                        {
                            continue;                                                              // If not valid, don't queue it.
                        }
                        //---Debug.Log(indent + action.name + " " + fact.entityType.name);

                        var newWorldModel = new WorldModel(current.worldModel);
                        newWorldModel.ApplyAction(fact, action);
                        var newPlan = new Plan(current, GetStep(fact, action), newWorldModel);
                        Q.Enqueue(newPlan);
                    }
                }
            }
            if (QuestMachine.debug || detailedDebug)
            {
                Debug.Log("Quest Machine: [Generator] Could not create quest. Exceeded safeguard while generating plan to " +
                          goal.action.name + " " + goal.fact.entityType.name + ".", entity);
            }
        }
Пример #6
0
        private IEnumerator DetermineGoal()
        {
            // Search world model for most urgent fact:
            Fact[] mostUrgentFacts;
            var    cumulativeUrgency = worldModel.ComputeUrgency(goalSelectionMode, out mostUrgentFacts, ignoreList, detailedDebug);
            var    mostUrgentFact    = ChooseWeightedMostUrgentFact(mostUrgentFacts);

            if (mostUrgentFact == null)
            {
                yield break;
            }
            if (cumulativeUrgency <= 0)
            {
                if (QuestMachine.debug || detailedDebug)
                {
                    Debug.Log("Quest Machine: [Generator] " + entity.displayName + ": No facts are currently urgent for " + entity.displayName + ". Not generating a new quest.", entity);
                }
                yield break;
            }
            if (QuestMachine.debug || detailedDebug)
            {
                Debug.Log("Quest Machine: [Generator] " + entity.displayName + ": Most urgent fact: " + mostUrgentFact.count + " " +
                          mostUrgentFact.entityType.name + " in " + mostUrgentFact.domainType.name, entity);
            }

            // Choose goal action to perform on that fact:
            float  bestUrgency = Mathf.Infinity;
            Action bestAction  = null;
            Motive bestMotive  = null;
            var    actions     = GetEntityActions(mostUrgentFact.entityType);

            if (actions == null)
            {
                yield break;
            }
            int numChecks = 0;

            for (int i = 0; i < actions.Count; i++)
            {
                numChecks++;
                if (numChecks > maxGoalActionChecksPerFrame)
                {
                    numChecks = 0;
                    yield return(null);
                }
                var action = actions[i];
                if (action == null)
                {
                    continue;
                }
                var wm = new WorldModel(worldModel);
                wm.ApplyAction(mostUrgentFact, action);
                Fact newMostUrgentFact;
                var  newUrgency          = wm.ComputeUrgency(out newMostUrgentFact);
                var  bestMotiveForAction = ChooseBestMotive(action.motives);
                var  weightedUrgency     = (bestMotiveForAction != null) ? (newUrgency - (GetDriveAlignment(bestMotiveForAction.driveValues) * newUrgency)) : newUrgency;
                if (weightedUrgency < bestUrgency) // Select goal action based on resulting urgency weighted by how well the motive aligns with the giver's drives.
                {
                    bestUrgency = weightedUrgency;
                    bestAction  = action;
                    bestMotive  = bestMotiveForAction;
                }
            }
            if (bestAction == null)
            {
                yield break;
            }
            motive = bestMotive;
            goal   = new PlanStep(mostUrgentFact, bestAction, Mathf.CeilToInt(mostUrgentFact.entityType.maxCountInAction.Evaluate(mostUrgentFact.count)));
            if (QuestMachine.debug || detailedDebug)
            {
                Debug.Log("Quest Machine: [Generator] " + entity.displayName + ": Goal: " + bestAction.name + " " +
                          mostUrgentFact.count + " " + mostUrgentFact.entityType.name, entity);
            }
        }
Пример #7
0
        /// <summary>
        /// Adds the quest's offer text .
        /// </summary>
        /// <param name="questBuilder">QuestBuilder.</param>
        /// <param name="mainTargetEntity">Target of the quest.</param>
        /// <param name="mainTargetDescriptor">Descriptor of the target (with count).</param>
        /// <param name="domainName">Domain where the target is located.</param>
        /// <param name="goal">Final step to complete quest.</param>
        /// <param name="motive">Motive for goal.</param>
        protected virtual void AddOfferText(QuestBuilder questBuilder, string mainTargetEntity, string mainTargetDescriptor, string domainName, PlanStep goal, Motive motive)
        {
            // Offer motive:
            var motiveText = (motive != null) ? StringField.GetStringValue(motive.text)
                : (goal.action.motives.Length > 0) ? StringField.GetStringValue(goal.action.motives[0].text)
                : StringField.GetStringValue(goal.action.actionText.activeText.dialogueText);

            motiveText = ReplaceStepTags(motiveText, mainTargetEntity, mainTargetDescriptor, domainName, string.Empty, 0);
            questBuilder.AddOfferContents(questBuilder.CreateTitleContent(), questBuilder.CreateBodyContent(motiveText));
        }
Пример #8
0
 /// <summary>
 /// Sets the quest's main info .
 /// </summary>
 /// <param name="questBuilder">QuestBuilder</param>
 /// <param name="questID">Quest ID.</param>
 /// <param name="title">Quest title.</param>
 /// <param name="group">Quest group.</param>
 /// <param name="goal">Final step to accomplish the plan.</param>
 protected virtual void SetMainInfo(QuestBuilder questBuilder, string questID, string title, StringField group, PlanStep goal)
 {
     questBuilder.quest.isTrackable        = true;
     questBuilder.quest.showInTrackHUD     = true;
     questBuilder.quest.icon               = goal.fact.entityType.image;
     questBuilder.quest.group              = new StringField(group);
     questBuilder.quest.goalEntityTypeName = goal.fact.entityType.name;
 }
Пример #9
0
        /// <summary>
        /// Adds UI content to the return node.
        /// </summary>
        protected virtual void AddReturnNodeText(QuestBuilder questBuilder, QuestNode returnNode, QuestGiver questGiver, string mainTargetEntity, string mainTargetDescriptor, string domainName, PlanStep goal, string hudText)
        {
            var stateInfo = returnNode.stateInfoList[(int)QuestNodeState.Active];

            QuestStateInfo.ValidateCategorizedContentListCount(stateInfo.categorizedContentList);
            var successText  = ReplaceStepTags(StringField.GetStringValue(goal.action.actionText.successText), mainTargetEntity, mainTargetDescriptor, domainName, string.Empty, 0);
            var bodyText     = questBuilder.CreateBodyContent(successText);
            var dialogueList = returnNode.stateInfoList[(int)QuestNodeState.Active].categorizedContentList[(int)QuestContentCategory.Dialogue];

            dialogueList.contentList.Add(bodyText);

            var jrlText     = "{Return to} " + questGiver.displayName;
            var jrlBodyText = questBuilder.CreateBodyContent(jrlText);
            var journalList = returnNode.stateInfoList[(int)QuestNodeState.Active].categorizedContentList[(int)QuestContentCategory.Journal];

            journalList.contentList.Add(jrlBodyText);

            var hudBodyText = questBuilder.CreateBodyContent(hudText);
            var hudList     = returnNode.stateInfoList[(int)QuestNodeState.Active].categorizedContentList[(int)QuestContentCategory.HUD];

            hudList.contentList.Add(hudBodyText);
        }
Пример #10
0
        /// <summary>
        /// Adds a final "return to quest giver" step.
        /// </summary>
        /// <returns>The return node.</returns>
        protected virtual QuestNode AddReturnNode(QuestBuilder questBuilder, QuestNode previousNode, QuestEntity entity, string mainTargetEntity, string mainTargetDescriptor, string domainName, PlanStep goal, int rewardsContentIndex = 9999) // Default to 9999 to not break any customer code using old signature.
        {
            var questGiver = entity.GetComponent <QuestGiver>();
            var giverID    = (questGiver != null) ? questGiver.id : ((entity != null) ? entity.displayName : null);
            var returnNode = questBuilder.AddDiscussQuestNode(previousNode, QuestMessageParticipant.QuestGiver, giverID);

            returnNode.id = new StringField("Return");
            previousNode  = returnNode;

            QuestStateInfo.ValidateStateInfoListCount(returnNode.stateInfoList);
            var hudText = "{Return to} " + questGiver.displayName;

            // Text when active:
            AddReturnNodeText(questBuilder, returnNode, questGiver, mainTargetEntity, mainTargetDescriptor, domainName, goal, hudText);

            // Add rewards content:
            var dialogueList     = returnNode.stateInfoList[(int)QuestNodeState.Active].categorizedContentList[(int)QuestContentCategory.Dialogue];
            var offerContentList = questBuilder.quest.offerContentList;

            for (int i = rewardsContentIndex; i < offerContentList.Count; i++)
            {
                var original = offerContentList[i];
                var copy     = QuestContent.Instantiate(original) as QuestContent;
                original.CloneSubassetsInto(copy);
                dialogueList.contentList.Add(copy);
            }

            var actionList = returnNode.GetStateInfo(QuestNodeState.Active).actionList;

            // Alert when active:
            AddReturnNodeAlert(questBuilder, returnNode, actionList, hudText);

            // Indicators:
            AddReturnNodeIndicators(questBuilder, returnNode, actionList, entity);

            return(previousNode);
        }
Пример #11
0
        /// <summary>
        /// Adds the text for a step.
        /// </summary>
        protected virtual void AddStepNodeText(QuestBuilder questBuilder, QuestNode conditionNode, QuestStateInfo activeState, string targetEntity, string targetDescriptor, string domainName, string counterName, int requiredCounterValue, PlanStep step)
        {
            // Text for condition node's Active state:
            var taskText     = ReplaceStepTags(step.action.actionText.activeText.dialogueText.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue);
            var bodyText     = questBuilder.CreateBodyContent(taskText);
            var dialogueList = activeState.categorizedContentList[(int)QuestContentCategory.Dialogue];

            dialogueList.contentList.Add(bodyText);

            var jrlText     = ReplaceStepTags(step.action.actionText.activeText.journalText.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue);
            var jrlbodyText = questBuilder.CreateBodyContent(jrlText);
            var journalList = activeState.categorizedContentList[(int)QuestContentCategory.Journal];

            journalList.contentList.Add(jrlbodyText);

            var hudText     = ReplaceStepTags(step.action.actionText.activeText.hudText.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue);
            var hudbodyText = questBuilder.CreateBodyContent(hudText);
            var hudList     = activeState.categorizedContentList[(int)QuestContentCategory.HUD];

            hudList.contentList.Add(hudbodyText);
        }
Пример #12
0
        /// <summary>
        /// Converts a plan generated by QuestGenerator into a quest.
        /// </summary>
        /// <param name="entity">Entity generating the quest (e.g., QuestGiver).</param>
        /// <param name="group">Optional quest group under which to categorize this quest.</param>
        /// <param name="goal">Final step to accomplish quest.</param>
        /// <param name="motive">Motive for the goal.</param>
        /// <param name="plan">Steps required to complete the goal step.</param>
        /// <param name="requireReturnToComplete">If true, add a quest node that requires quester to return to entity.</param>
        /// <param name="rewardsUIContents">UI content to show in the rewards offer section.</param>
        /// <param name="rewardSystems">Reward systems to use to </param>
        /// <returns></returns>
        public virtual Quest ConvertPlanToQuest(QuestEntity entity, StringField group, PlanStep goal, Motive motive, Plan plan, bool requireReturnToComplete, List <QuestContent> rewardsUIContents, List <RewardSystem> rewardSystems)
        {
            // Build title:
            var mainTargetEntity     = goal.fact.entityType.name;
            var mainTargetDescriptor = goal.fact.entityType.GetDescriptor(goal.requiredCounterValue);
            var title      = goal.action.displayName + " " + mainTargetDescriptor;
            var questID    = title + " " + System.Guid.NewGuid();
            var domainName = StringField.GetStringValue(goal.fact.domainType.displayName);

            // Start QuestBuilder:
            var questBuilder = new QuestBuilder(title, questID, title);

            SetMainInfo(questBuilder, questID, title, group, goal);
            AddTagsToDictionary(questBuilder.quest.tagDictionary, goal);

            // Offer:
            AddOfferText(questBuilder, mainTargetEntity, mainTargetDescriptor, domainName, goal, motive);
            var rewardsContentIndex = questBuilder.quest.offerContentList.Count;

            AddRewards(questBuilder, entity, goal, rewardsUIContents, rewardSystems);

            // Quest headings:
            AddQuestHeadings(questBuilder, goal);

            // Successful text (shown in journal / when talking again about successful quest):
            AddSuccessfulText(questBuilder, mainTargetEntity, mainTargetDescriptor, domainName, goal);

            // Add steps:
            var previousNode = AddSteps(questBuilder, domainName, goal, plan);

            // Add "return to giver" node:
            if (requireReturnToComplete)
            {
                previousNode = AddReturnNode(questBuilder, previousNode, entity, mainTargetEntity, mainTargetDescriptor, domainName, goal, rewardsContentIndex);
            }

            // Success node:
            questBuilder.AddSuccessNode(previousNode);

            return(questBuilder.ToQuest());
        }
Пример #13
0
        /// <summary>
        /// Adds the plan's steps.
        /// </summary>
        /// <param name="questBuilder">QuestBuilder.</param>
        /// <param name="domainName">Main target's domain.</param>
        /// <param name="goal">Goal step.</param>
        /// <param name="plan">List of steps that end with goal step.</param>
        /// <returns>The last node added.</returns>
        protected virtual QuestNode AddSteps(QuestBuilder questBuilder, string domainName, PlanStep goal, Plan plan)
        {
            var previousNode = questBuilder.GetStartNode();
            var counterNames = new HashSet <string>();

            for (int i = 0; i < plan.steps.Count; i++)
            {
                var step = plan.steps[i];

                // Create next condition node:
                var targetEntity     = step.fact.entityType.name;
                var targetDescriptor = step.fact.entityType.GetDescriptor(step.requiredCounterValue);
                var id            = (i + 1).ToString();
                var internalName  = step.action.displayName + " " + targetDescriptor;
                var conditionNode = questBuilder.AddConditionNode(previousNode, id, internalName, ConditionCountMode.All);
                previousNode = conditionNode;

                // Variables for node text tag replacement:
                var counterName          = string.Empty;
                int requiredCounterValue = 0;

                var completion = step.action.completion;
                if (completion.mode == ActionCompletion.Mode.Counter)
                {
                    // Setup counter condition:
                    counterName = goal.fact.entityType.pluralDisplayName.value + completion.baseCounterName.value;
                    if (!counterNames.Contains(counterName))
                    {
                        var counter = questBuilder.AddCounter(counterName, completion.initialValue, completion.minValue, completion.maxValue, false, completion.updateMode);
                        foreach (var messageEvent in completion.messageEventList)
                        {
                            var counterMessageEvent = new QuestCounterMessageEvent(messageEvent.targetID, messageEvent.message,
                                                                                   new StringField(StringField.GetStringValue(messageEvent.parameter).Replace("{TARGETENTITY}", targetEntity)),
                                                                                   messageEvent.operation, messageEvent.literalValue);
                            counter.messageEventList.Add(counterMessageEvent);
                        }
                    }
                    counterName          = goal.fact.entityType.pluralDisplayName.value + completion.baseCounterName.value;
                    requiredCounterValue = Mathf.Min(step.requiredCounterValue, step.fact.count);
                    questBuilder.AddCounterCondition(conditionNode, counterName, CounterValueConditionMode.AtLeast, requiredCounterValue);
                    // Consider: Add action to reset counter to zero in case future nodes repeat the same counter?
                }
                else
                {
                    // Setup message condition:
                    questBuilder.AddMessageCondition(conditionNode, QuestMessageParticipant.Any, completion.senderID, QuestMessageParticipant.Any, completion.targetID,
                                                     completion.message, new StringField(StringField.GetStringValue(completion.parameter).Replace("{TARGETENTITY}", targetEntity)));
                }

                var activeState = conditionNode.stateInfoList[(int)QuestNodeState.Active];

                AddStepNodeText(questBuilder, conditionNode, activeState, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue, step);

                // Actions when active:
                if (!StringField.IsNullOrEmpty(step.action.actionText.activeText.alertText))
                {
                    // Alert action:
                    var alertAction = questBuilder.CreateAlertAction(ReplaceStepTags(step.action.actionText.activeText.alertText.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue));
                    activeState.actionList.Add(alertAction);
                }

                // Send message action:
                if (!StringField.IsNullOrEmpty(step.action.sendMessageOnActive))
                {
                    var messageAction = questBuilder.CreateMessageAction(ReplaceStepTags(step.action.sendMessageOnActive.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue));
                    activeState.actionList.Add(messageAction);
                }

                // Actions when completed:
                if (!StringField.IsNullOrEmpty(step.action.sendMessageOnCompletion))
                {
                    var trueState     = conditionNode.stateInfoList[(int)QuestNodeState.True];
                    var messageAction = questBuilder.CreateMessageAction(ReplaceStepTags(step.action.sendMessageOnCompletion.value, targetEntity, targetDescriptor, domainName, counterName, requiredCounterValue));
                    trueState.actionList.Add(messageAction);
                }
            }
            return(previousNode);
        }
Пример #14
0
        /// <summary>
        /// Adds text to show in UIs after a quest has been successfully completed.
        /// </summary>
        /// <param name="questBuilder">QuestBuilder.</param>
        /// <param name="mainTargetEntity">Main target entity that quest is about.</param>
        /// <param name="mainTargetDescriptor">Target descriptor (with count).</param>
        /// <param name="domainName">Target's domain.</param>
        /// <param name="goal">Goal step in quest.</param>
        protected virtual void AddSuccessfulText(QuestBuilder questBuilder, string mainTargetEntity, string mainTargetDescriptor, string domainName, PlanStep goal)
        {
            var successful = questBuilder.quest.GetStateInfo(QuestState.Successful);
            var hasSuccessfulDialogueText = !StringField.IsNullOrEmpty(goal.action.actionText.completedText.dialogueText);
            var hasSuccessfulJournalText  = !StringField.IsNullOrEmpty(goal.action.actionText.completedText.journalText);

            if (hasSuccessfulDialogueText)
            {
                var dlgText = questBuilder.CreateBodyContent(ReplaceStepTags(goal.action.actionText.completedText.dialogueText.value, mainTargetEntity, mainTargetDescriptor, domainName, string.Empty, 0));
                questBuilder.AddContents(successful.GetContentList(QuestContentCategory.Dialogue), dlgText);
            }
            if (hasSuccessfulJournalText)
            {
                var jrlText = questBuilder.CreateBodyContent(ReplaceStepTags(goal.action.actionText.completedText.journalText.value, mainTargetEntity, mainTargetDescriptor, domainName, string.Empty, 0));
                questBuilder.AddContents(successful.GetContentList(QuestContentCategory.Journal), jrlText);
            }
        }
Пример #15
0
        /// <summary>
        /// Uses a list of reward systems to add rewards to the quest builder
        /// that are commensurate with the difficulty of the quest.
        /// </summary>
        /// <param name="questBuilder">QuestBuilder to receive reward offers.</param>
        /// <param name="entity">Quest giver entity.</param>
        /// <param name="goal">Goal step (determines quest difficulty).</param>
        /// <param name="rewardsUIContents">Text to introduce rewards.</param>
        /// <param name="rewardSystems">Reward systems from which to get rewards.</param>
        protected virtual void AddRewards(QuestBuilder questBuilder, QuestEntity entity, PlanStep goal, List <QuestContent> rewardsUIContents, List <RewardSystem> rewardSystems)
        {
            if (QuestGenerator.detailedDebug)
            {
                Debug.Log("Quest Machine: [Generator] Checking " + rewardSystems.Count + " reward systems for " + goal.fact.count + " " + goal.fact.entityType.name +
                          " (level " + goal.fact.entityType.level + ") on " + entity.name, entity);
            }
            questBuilder.AddOfferContents(QuestContent.CloneList <QuestContent>(rewardsUIContents).ToArray());

            var pointsRemaining = goal.fact.entityType.level * goal.fact.count;

            for (int i = 0; i < rewardSystems.Count; i++)
            {
                var rewardSystem = rewardSystems[i];
                if (rewardSystem == null)
                {
                    continue;
                }
                if (UnityEngine.Random.value > rewardSystem.probability)
                {
                    continue;
                }
                pointsRemaining = rewardSystem.DetermineReward(pointsRemaining, questBuilder.quest, goal.fact.entityType);
                if (pointsRemaining <= 0)
                {
                    break;
                }
            }
        }