Custom AI

From Dragon Age Toolset Wiki
Jump to: navigation, search

Custom AI allows the creature's event script to handle the creature's combat activities directly and prevents the usual default combat AI from running for it.

Custom AI state variable

Custom AI is enabled via the AI_CUSTOM_AI_ACTIVE variable in a creature's variable table, which also keeps track of the current 'state' the custom AI is in.

When the game completes the command that a creature is currently executing, a creature whose AI_CUSTOM_AI_ACTIVE variable is set to a non-zero value will be sent an EVENT_TYPE_CUSTOM_COMMAND_COMPLETE event (older documentation said EVENT_TYPE_HANDLE_CUSTOM_AI, but this doesn't seem to be the case).

The event is sent after rules_core has processed the regular EVENT_TYPE_COMMAND_COMPLETE event, which may mean that Ambient behaviour has already issued another command.

These are the available value ranges and predefined constants for AI_CUSTOM_AI_ACTIVE:

0 - 999 Reserved for Basic Custom AI Modes
1000 - 9999 Free to use for Local Custom AI
10000 - * Reserved for Global Custom AI

Basic custom AI modes that have been defined are:

  • CAI_DISABLED - Custom AI is disabled (normal AI takes over again)
  • CAI_STASIS - Creature will become dormant and issue no commands
  • CAI_INACTIVE -
  • CAI_INITIATE - A default "starting state" that can be used for initialization.

Global custom AI constants that have been defined are:

  • CAI_RUNNER_RUN - used by a creature who flees from the PC in the middle of combat for some reason or another.
  • CAI_USEPLACEABLE_LYRIUM_VEIN
  • CAI_USEPLACEABLE_BALISTA

Custom AI creature variables

The default var_creature variable table has three variables that are intended for use by designers writing custom AI scripts. They are:

Variable name Type Default Description
AI_CUSTOM_AI_VAR_FLOAT float A float value that can be used by custom AI scripts for any purpose.
AI_CUSTOM_AI_VAR_INT int An int value that can be used by custom AI scripts for any purpose.
AI_CUSTOM_AI_VAR_STRING string A string value that can be used by custom AI scripts for any purpose

To make using them easier, the following functions are defined:

  • CAI_SetCustomAIFloat
  • CAI_SetCustomAIInteger
  • CAI_SetCustomAIString
  • CAI_GetCustomAIFloat
  • CAI_GetCustomAIInteger
  • CAI_GetCustomAIString

Giving creatures commands

It's very important that you ensure that every time the custom AI code is called the creature has a command in its command queue when the code is finished running. If it doesn't, the creature's AI code won't be called again and it will cease performing any actions from that point onward.

The function AI_DetermineCombatRound(); is very useful and important when designing a custom AI. This function issues the creature a "default" command that's based on its capabilities and perceptions. For example, in a monster's custom AI this will generally mean attacking the player in some way.

Example code with comments

Here is the skeleton of a basic EVENT_TYPE_HANDLE_CUSTOM_AI case for an event script. This code is for an extremely simple custom AI that causes a creature to do nothing except run an animation every five seconds during combat.

#include "cai_h" //Include this file to use custom AI support functions
 
const int CAI_USER_DEFINED_STATE_ANIMATION = 1000;
const int CAI_USER_DEFINED_STATE_DELAY = 1001;
//and so forth - use more descriptive names for your state constants to make the code easier to understand.
 
...
 
       case EVENT_TYPE_SPAWN:
        {
            //Setting the custom AI to "initiate" means the custom AI will be called
            //when the creature enters combat. You can use one of your own custom states
            //as the starting point instead, CAI_INITIATE is only 'special' in that it has
            //had a constant pre-defined for it already in the core library.
            CAI_SetCustomAI(OBJECT_SELF, CAI_INITIATE);
            break;
        }
 
 
        case EVENT_TYPE_HANDLE_CUSTOM_AI:
        {
            // Determine which custom AI state the creature is in.
            int nCustomAI = CAI_GetCustomAI(OBJECT_SELF);
 
            Log_Trace(LOG_CHANNEL_TEMP, GetCurrentScriptName(), "Firing custom AI event: " + ToString(nCustomAI));
 
            //Fall-through for if the custom AI command failed.
            //This addresses issues with creatures that run appear/disappear animations
            //when they're made active or inactive.
            if (GetEventInteger(ev,2)==COMMAND_FAILED)
            {
                WR_AddCommand(OBJECT_SELF,CommandWait(0.01f),TRUE);
                return;
            }
 
            switch ( nCustomAI )
            {
                case CAI_INITIATE:
                {
                    //insert whatever initialization code you need here, and then set the custom
                    //AI to another state.
 
                    CAI_SetCustomAI(OBJECT_SELF, CAI_USER_DEFINED_STATE_ANIMATION);
                    AI_DetermineCombatRound(); //we didn't give the creature a command in this state, so call this to determine a default action.
                    bActionPerformed = TRUE;
                    break;
                }
 
                case CAI_USER_DEFINED_STATE_ANIMATION:
                {
                    //creates a command to play an animation (index is to a row defined in the ANIM_base 2DA)
                    command cPlayAnim = CommandPlayAnimation(247);
 
                    //Puts the animation at the front of the creature's command queue.
                    //Setting it as "static" means nothing will interrupt it, which
                    //is particularly useful for animations.
                    WR_AddCommand(OBJECT_SELF, cPlayAnim, TRUE, TRUE);
 
                    //Set the next state we want to be in
                    CAI_SetCustomAI(OBJECT_SELF, CAI_USER_DEFINED_STATE_DELAY);
 
                    break;
                }
 
                case CAI_USER_DEFINED_STATE_DELAY:
                {
                     AddCommand(OBJECT_SELF, CommandWait(5)); //adds a wait command.
                     CAI_SetCustomAI(OBJECT_SELF, CAI_USER_DEFINED_STATE_ANIMATION); //we'll do this state next
                     break;
                }
 
                //Include this default case just in case something weird happens and the custom AI event is
                //run with an unexpected state.
                default: AI_DetermineCombatRound();
 
                break;
            }
            break;
        }
 
...