private Motive ChooseBestMotive(Motive[] motives) { if (motives == null) { return(null); } Motive bestMotive = null; float bestAlignment = -Mathf.Infinity; for (int i = 0; i < motives.Length; i++) { var motive = motives[i]; if (motive == null) { continue; } var alignment = GetDriveAlignment(motive.driveValues); if (detailedDebug) { Debug.Log("Quest Machine: [Generator] Motive Alignment: entity=" + ((entity != null) ? entity.name : "null") + " motive=" + motive.text + " alignment=" + alignment, entity); } if (alignment > bestAlignment) { bestAlignment = alignment; bestMotive = motive; } } return(bestMotive); }
/// <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)); }
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); } }
/// <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()); }