Goal Oriented Action Planning, also know as GOAP. Is used for determining what actions an AI should take to reach a goal. It was originally used not for game AI but for robotics, but was later popularized by Monolith Productions, in their use of it in the game F.E.A.R. they later also used it in Shadow of Mordor and Shadow of War.
GOAP is made of five parts, an actor to use the plan, goals we want the actor to achieve, actions to achieve the goals, a world state describing the world and actor for the planner and the planner to figure out what actions to take to achieve the goal.
Each action and goal have requirements to preform, actions also have an effect when preformed as well as a cost to perform. A requirement is considered met if the value in the world state is greater or equals to the requirement .
For the implementation to test if it works i defined two goals at the start, Eat Food and Wander. Which you can see in the first image.
To achive the test goals I defined four actions, Eat Food, Go To Food, Find Food and Wander. On the second image you can see how I define the preconditions to perform the action as well as the effects the action has.
Each action had a function to Update State which updated the world state, to determine the new state of the actor after the action been performed. It also had en Execute function that was used to perform the action. Which the actor called upon the perform the planned action. As well as updating some specific info about the actor itself for exampel setting a target or interacting with a target.
When making the actions specifically the GoTo action I debated about making an abstract action or have it more static.
Abstract actions are actions where we feed in some information under runtime, an example would be to have a single GoTo action where we give it a target entity or tag.
In the end I decided to let it be static which means that for every GoTo I would need to define a sperate condition and action for example Actot_AtFood.
While making abstract actions would be better in the long run, I determined that it would be to time consuming, implementing and getting right then letting it be static.
Since our GOAP is more static we store the World Info in an array using integers since we can store values such as hunger and use it as bools.
We then update the world state both through actions but also based of the world itself. Where we check what exists as well as checking their current state.
Later I also added storage for tracking entities we have found, including the latest positions and tags it had. For simplicity sake we just do a distance check to determine if we can "see" the entity or not, if we can "see" it, we add or update it's info in our world state. If we go past the last known position, and don't detect it we remove it. Since it has either moved or is gone and we no longer know where it is.
Having defined actions and goals, we also need a planner to actually make the plan we're supposed to solve.
When making a plan we go through each goal in the order they were added, and get the missing requirements for that goal. If we're not missing any requirements for the goal we go on to the next goal instead, this is allows us to prevent the AI getting stuck in a state where it has an achieved goal and just stands in place instead of going for other goals.
When we have the missing requirements we can start making an actual plans by going through our actions until we find a solution or we determine that the goal can't be reached and we go to the next goal.
Having gotten found at least one action to fulfills at least one requirement of the goal the planner then starts making the plan using A*, to determine the cheapest route to get the goal.
Since the cost of an action is determined by a set value but gotten through a function we could have it virtual if we want to implement dynamic costs, often used in things such as GoTo actions.