Skip to content
Slashscreen edited this page Jul 4, 2023 · 3 revisions

Introduction

TODO: For now, refer to "Editor Tools and creating NPC and Items".

Data

Flags

Schedule

Combat Info

The AI System

The AI system is made up of two parts - the AI Modules, and the GOAP system. The best way to think of it is that the AI Modules determine what to do, and the GOAP system determines how to do it. The modules take in data from the NPC and its surroundings, and then give the GOAP system objectives to complete.

Modules

Modules determine how the NPC should react to the world around it. The basic idea is that it listens to the NPC's signals and values, and determines how it should act. The built in AI Modules, in addons/skelerealms/scripts/ai/AIModules, should give you a solid idea of how these can be written. It is a good idea to keep different kinds of responses in different modules; for example, dialogue response vs. damage response should be separate.

GOAP

GOAP is an alternative AI system to behavior trees. Explaining what it is and the planning algorithm is out of the scope of this article, but in short, you give the AI a pool of actions (walk here, melee attack, etc.) and an objective (ex. kill enemy) and it will pull from the action pool to create a sequence of actions that will achieve that goal.

No GOAP Behavior are provided to you, because the nature of them is very game-specific. Here is, however, the general structure of a GOAP Behavior.

the "GOAPAction" class and the "GOAPBehavior" class are distinct in that the Action is simply a node that wraps around a reusable Behavior resource. Behavior actually has the logic in it, and is the one that should be overwritten. The names confuse me, too, and may be changed in the future.

Values

Prerequisites: What goals must be complete to perform this action. Effects: What goals are changed after this action. Cost: What the cost for this action is. The higher the cost, the less likely it will be chosen for a task. This can be used to have "preferences" for certain actions. Duration: how much time the NPC will wait between target_reached and post_perform. You can use this to simulate the NPC performing the task.

Functions

These functions can be overridden to define the logic of the behavior, and is divided into steps. For all of the functions except for is_achievable and is_target_reached, returning false will force a plan recalculation.

is_achievable

This function determines whether an action is currently achievable. For example, a perform_ranged_attack behavior would only be achievable if there is a ranged weapon in the inventory.

pre_perform

What must be done before the NPC performs this action. For our ranged attack example, the NPC would equip a bow.

target_reached

What must be done once the target is reached, defined by is_target_reached. In our example, shooting the bow.

post_perform

What must be done once the action is complete. For example, putting away the bow.

is_target_reached

Determines whether a target has been reached. The agent is provided under the assumption that the target is a point in physical space, but it can be anything, like waiting until a certain value passes a threshold.

interrupt

Use this function to clean up anything if the NPC is interrupted by a forced plan recalculation, like putting away the bow.

Combat responses

Opinions and how the NPC determines its opinions

  • NPCs have a concept called opinions, which are a value from -100 to 100 reflecting what this NPC thinks of an entity, from -100 being mortal enemies to 100 being best of friends. 0 is a special value meaning no opinion at all, or "neutral". This system is almost entirely different from Creation Kit's combat response system.
  • This system is separate from Relationships, but will possibly be integrated more closely in the future.
  • The scale roughly follows this guideline:
100
 Allies / BFFs
80
 Friends
20
 Acquaintance
0 - NEUTRAL
 Acquaintance
-20
 Enemies
-80
 Nemesis
-100
  • This value is calculated by, essentially, taking the Covens this entity is a part of, gathering all non-neutral opinions of the other entity's Covens (defined in the Coven's "inter-faction relations" dictionary), tacking on the NPC's personal opinion of the entity (this can change during runtime), and averaging all of them together.
  • NPCs also have a flag determining if the average is weighted towards the covens' opinions or the personal opinions (or neither). This simulates an NPC being more loyal to the group or having their own personal opinion be more important than the group.
  • The opinion is always 0 for entities that aren't of a type that can pose a threat, like containers, or items. This is so NPCs don't try to fight a glass bottle lying on the floor, although that would admittedly be pretty funny. Entities are deemed a possible threat by having one of any components defined under NPCComponent.THREATENING_ENTITY_TYPES - By default, NPCComponent and PlayerComponent.
  • Here is code written in Ruby reflecting the algorithm, for those that learn easier by example than by words:
# get the weights of opinion categories, taking preferences into account
covens_weight = if favors_covens then 2 else 1
personal_weight = if favors_self then 2 else 1

opinions = get_coven_opinions.select{|x| not x == 0} # get all coven opinions that aren't 0, to total later
total_elements = coven_opinions.length * covens_weight # count up total coven elements, multiplied by covens weight

	# If the personal opinion is not neutral,  
unless personal_opinion == 0 then
	opinions << personal_opinions # add it to the opinions to total
	total_elements += 1 * personal_weight # add to total elements, taking the weight into account
end

opinion = (opinions.reduce(0) {|sum, num| sum + num}) / total_elements # sum all opinions, divide by opinion count with weights, return value

(Why Ruby? Because I like Ruby, and it's close enough to English to be a bit like functional pseudocode.)

The Opinion system is used for dialog and combat responses, but you can use it for whatever.