public Node(Node parent, List_GOAP_Worldstate required, GOAP_Action action, float estimatedPathCost) { this.parent = parent; this.estimatedPathCost = estimatedPathCost; this.required = required; this.action = action; }
/// <summary> /// Tries to apply the action onto the activeNode to see if it results in a valid neighbor /// </summary> /// <param name="activeNode"></param> /// <param name="action"></param> /// <param name="planningWorldState">worldstate at the current stage of planning</param> /// <param name="agent">currently planning agent</param> /// <returns></returns> private static List <Node> GetValidNeighborNodeVariations(Node activeNode, GOAP_Action action, List_GOAP_Worldstate planningWorldState, GOAP_Agent agent) { if (action == null) { return(null); } List <Node> nodes = new List <Node>(); if (action.HasVariations()) { for (int i = 0; i < action.variations.Count; i++) { if (action.variations[i] != null) { nodes.Add(GetValidNeighborNode(activeNode, action.GetVariation(i), planningWorldState, agent)); } } } else { nodes.Add(GetValidNeighborNode(activeNode, action, planningWorldState, agent)); } return(nodes); }
public void SaveQuestPlan(int questID) { questPlans.Add(questID, new Queue <GOAP_Action>(currentActions)); Character.Log("<color=#0000cc>" + character.characterData.characterName + "</color> Saved a new QuestPlan. It includes " + questPlans.Last().Value.Count + " actions and starts with " + questPlans.Last().Value.Peek()); currentActions.Clear(); activeAction = null; activeGoal.Clear(); }
/// <summary> ///Cancel the action queue but keep the goal/quest and try planning again immediately /// </summary> public void Replan() { activePlanInfo = null; activeAction = null; currentActions.Clear(); actionCompleted = true; ChangeState(FSM_State.PLANNING); return; }
public static void ShowWindow(ref GOAP_Character.CharacterData characterData) { AvailableActionsEditorWindow window = (AvailableActionsEditorWindow)EditorWindow.GetWindow(typeof(AvailableActionsEditorWindow)); window.characterData = characterData; //Add all actions to the list window.allActions = GOAP_Action.GetAllActionNames(); characterData.InitBaseActions(window.allActions); characterData.RemoveWrongActions(window.allActions); }
private void InitWindow() { for (int i = 0; i < allActions.Length; i++) { string actionName = allActions[i].ToString(); if (GOAP_Action.IsQuestActionID(actionName)) { continue; } AvailableActionWindowPanel actionPanel = Instantiate(actionPanelPrefab, actionsParent).GetComponent <AvailableActionWindowPanel>(); actionPanel.SetContent(actionName, characterData.availableActions.Contains(actionName)); } }
private void Awake() { if (instance != null) { Destroy(this); } instance = this; if (allActions == null) { //Add all actions to the list allActions = GOAP_Action.GetAllActionNames(); } InitWindow(); }
public void VisualizeAction(GOAP_Action action) { if (action == null) { transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0); } else if (action.ActionID == "Sleep") { transform.eulerAngles = new Vector3(90, transform.eulerAngles.y, 0); } else { } }
private void UpdateWindowAndActions() { for (int i = 0; i < allActions.Length; i++) { string actionName = allActions[i].ToString(); if (GOAP_Action.IsQuestActionID(actionName)) { continue; } AvailableActionWindowPanel actionPanel = actionsParent.GetChild(i).GetComponent <AvailableActionWindowPanel>(); actionPanel.SetContent(actionName, characterData.availableActions.Contains(actionName)); if (GOAP_Action.baseActions.Contains(actionName)) { actionPanel.buttonImage.GetComponent <Button>().interactable = false; actionPanel.buttonImage.color = Color.grey; actionPanel.buttonLabel.text = "BASE"; actionPanel.transform.SetAsFirstSibling(); if (!characterData.availableActions.Contains(actionName)) { characterData.availableActions.Add(actionName); } } else { actionPanel.buttonImage.GetComponent <Button>().interactable = true; } } //remove all actions not on the list from available for (int i = characterData.availableActions.Count - 1; i >= 0; i--) { string action = characterData.availableActions[i]; if (!allActions.Contains(action) || action == "Action_CompleteQuest" || action == "Action_WaitForQuest" || action == "Action_PostQuest") { characterData.availableActions.RemoveAt(i); } } }
/// <summary> /// Cancel the actionqueue and forget the plan /// </summary> public void CancelPlan() { if (activeQuest != null) { checkedQuestIds.Remove(activeQuest.id); } else { //if this was a personal goal, remove it from the checked list if (activeGoal.Count == 1) { checkedCharacterGoals.Remove(activeGoal[0]); } } activeQuest = null; activeGoal.Clear(); activePlanInfo = null; activeAction = null; currentActions.Clear(); actionCompleted = true; ChangeState(FSM_State.IDLE); return; }
public SimulationStep(GOAP_StatesList worldState, GOAP_Action action, int depth) { this.worldState = worldState; this.action = action; this.depth = depth; }
private void Plan() { for (int i = 0; i < goals.Count; i++) { if (goals[i].isValid(this)) { goal = goals[i]; break; } } if (worldState.CompareState(goal.goalStates) == 0) { return; } currentPlan.Clear(); Stack <SimulationStep> sim = new Stack <SimulationStep>(); GOAP_Action[] simPlan = new GOAP_Action[maxPlanDepth]; int minDepth = int.MaxValue; sim.Push(new SimulationStep(new GOAP_StatesList(worldState), null, 0)); List <GOAP_States> targetStates = new List <GOAP_States>(goal.goalStates.states); while (sim.Count != 0) { currentSimData = sim.Pop(); simPlan[currentSimData.depth] = currentSimData.action; if (currentSimData.depth > minDepth) { continue; } if (currentSimData.worldState.CompareState(goal.goalStates) == 0 || currentSimData.depth >= maxPlanDepth) { if (currentSimData.depth < minDepth) { currentPlan.Clear(); for (int i = 0; i <= currentSimData.depth; i++) { if (simPlan[i] != null) { if (!currentPlan.Contains(simPlan[i])) { currentPlan.Enqueue(simPlan[i]); } } } minDepth = currentSimData.depth; } } else { for (int i = 0; i < actions.Count; i++) { if (actions[i].isValid(this)) { GOAP_StatesList newSimState = new GOAP_StatesList(currentSimData.worldState); newSimState.AddStates(actions[i].resultStates); sim.Push(new SimulationStep(newSimState, actions[i], (currentSimData.depth + 1))); } } } } }
public void OnEnable() { character = (GOAP_Character)target; character.characterData.InitBaseActions(GOAP_Action.GetAllActionNames()); character.characterData.RemoveWrongActions(GOAP_Action.GetAllActionNames()); }
private void PerformUpdate(float deltaTime) { //Get the next currentAction if (actionCompleted) { if (currentActions.Count > 1 || (currentActions.Count > 0 && activeAction == null)) { //Remove the old active action if (activeAction != null) { currentActions.Dequeue(); } //Get the new active action activeAction = currentActions.Peek(); actionCompleted = false; } else { activeAction = null; } } if (activeAction != null) { if (activeQuest != null) { if (!GOAP_QuestBoard.instance.quests.ContainsKey(activeQuest.id)) { Character.Log("<color=#0000cc>" + Character.characterData.characterName + "</color> can't complete quest, already finished"); CancelPlan(); return; } } if (activeAction.CheckRequirements(this)) { if (activeAction.SatisfyWorldstates.Count > 0 && IsSatisfiedInCurrentWorldstate(activeAction.SatisfyWorldstates)) { actionCompleted = true; Character.Log("<color=#0000cc>" + character.characterData.characterName + "</color> didn't need to perform <color=#cc0000>" + activeAction.ActionID + "</color> anymore."); } else { if (!activeAction.IsInRange(this)) { View.PrintMessage("MoveTo " + activeAction.ActionID); ChangeState(FSM_State.MOVETO); return; } else { actionCompleted = activeAction.Perform(this, deltaTime); View.VisualizeAction(activeAction); } } } else { Character.Log("<color=#0000cc>" + character.characterData.characterName + "</color> cannot perform <color=#cc0000>" + activeAction.ActionID + "</color> anymore."); Replan(); } } else { //if this was a personal goal, remove it from the checked list if (activeQuest != null) { activeQuest = null; } else { if (activeGoal.Count == 1) { checkedCharacterGoals.Remove(activeGoal[0]); } } Character.Log("<color=#0000cc>" + character.characterData.characterName + "</color> has completed his action queue for Goal " + planMemory[(int)activePlanInfo].goalInfo); activeGoal.Clear(); activePlanInfo = null; ChangeState(FSM_State.IDLE); return; } }
private static Node GetValidNeighborNode(Node activeNode, GOAP_Action action, List_GOAP_Worldstate planningWorldState, GOAP_Agent agent) { bool isUsefulAction = false; List_GOAP_Worldstate newRequired = new List_GOAP_Worldstate(activeNode.required); //Actions need to fulfill at least one required Worldstate to result in a valid neighbor foreach (GOAP_Worldstate state in activeNode.required) { if (action.SatisfyWorldstates.ContainsExactly(state)) { if (state.key == WorldStateKey.bWasFieldTended && state.value == 0) { Debug.LogError(action.SatisfyWorldstates.ToString()); } newRequired.Remove(state); isUsefulAction = true; } } //if this action does not help the plan, return null if (!isUsefulAction) { return(null); } //If the actions proceduralConditions are not met, we can't perform it anyways //if (!action.CheckProceduralConditions(agent)) return null; //add the actions own required worldstates to the Node foreach (GOAP_Worldstate state in action.RequiredWorldstates) { if (!planningWorldState.ContainsExactly(state)) { //If the state is an observable one and the agent does not have any memory of it, they just assume that it is in their favor if (state.IsObservableState && !agent.currentWorldstates.ContainsKey(state)) { Debug.Log("<color=#cc00cc>" + agent.Character.characterData.characterName + "</color> assumes state:" + state.ToString()); agent.ChangeCurrentWorldState(state); } else { newRequired.Add(state); } } } //Apply skillmodification onto the neighbor if it is valid float skillModifier = 1f; if (action.BenefitingSkill != Skills.None) { GOAP_Skill skill = agent.Character.characterData.skills.Find(x => x.id == action.BenefitingSkill); if (skill != null) { //If the character is actually skilled in this action, adjust the skillmodifier skillModifier /= skill.level; } else { //If the character is not skilled in this action, the skillmodifier is set to 5. This only comes into play, when global knowledge planning is used. skillModifier = 1f; } } //Change the skillmodifier on the action action.ApplySkillModifier(skillModifier); return(new Node(activeNode, newRequired, action, newRequired.Count * heuristicFactor + action.ActionCost + activeNode.estimatedPathCost)); }