• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

AI Behavior Controller v1.41 [GUI-friendly]

AI Behavior Controller
by Riki

This system was created on WCIII patch 1.27b, so it should be compatible with (almost) any patch.

Special credits to Kingz for his Behaviour AI System, which inspired me and from which I
based to create this system. Find it here: Behaviour AI System



A system designed to make it easy for users to create AI by giving them the option to define custom behaviors for specific units or unit types as required. These behaviors include following a specified order under certain circumstances, using custom spells and abilities (including those based on Channel), etc.

Sometimes it is frustrating to have to deal with an AI that does not cast certain spells because they are based on Channel or some other ability that the AI would not use under normal circumstances.

The goal of this system in principle is to provide a tool that allows AI units to use any spell that the user wishes to create, and it has been extended to include even more options.

Please note that this system is not designed to directly create custom AI, but to support AI creation.


Features

How to Import

API

Examples

Credits

Changelog


  • Creation and modification of custom behaviors for individual units or Unit Types.
  • Support for Computer-owned units and Player-owned units, allowing automation of certain player units if necessary or exploring possible Autocast systems.
  • Multiple behaviors can be defined for each unit / Unit Type, each assigned action operating in order according to the priorities assigned by the user.
  • Custom behaviors can fire on periodic events or when the unit performs certain actions (casts spells, attacks, deals damage, etc). There are also optional events that can fire when an event from nearby friendly / enemy units is detected.
  • A custom order can be registered using its order string or id.

For users who want to use the system in vJass, follow steps from 1 to 3. Those users who need the GUI version must follow all the steps listed.
  1. Copy all the Necessary Requirements from the test map into your map. If you have newer versions of one of the listed requirements, you can skip it, but remember that you will need all of them.
  2. Copy the AI Behavior Controller library into your map. After this you will be ready to use this system.
  3. I recommend copying the Optional Requirements as well, but you can skip this step if you want.
  4. To use the GUI version, you must first follow all the steps above. After that, you need to copy all the content within the AIBC GUI category to your map.

vJASS:
static method registerBehavior takes unit u, Behavior b, boolean isUserBehavior returns nothing
    // Adds a custom Behavior to a single unit.
    // If isUserBehavior is false, it will fire only for Computer owned units.
    // If isUserBehavior is true, it will fire only for Player owned units.

static method registerBehaviorType takes integer uTypeId, Behavior b, boolean isUserBehavior returns nothing
    // Adds a custom Behavior to an unit type.
    // If isUserBehavior is false, it will fire only for Computer owned units.
    // If isUserBehavior is true, it will fire only for Player owned units.

static method removeBehavior takes unit u, Behavior b returns nothing
    // Removes a custom Behavior from a single unit.

static method removeBehaviorType takes integer uTypeId, Behavior b returns nothing
    // Removes a custom Behavior from an unit type.

vJASS:
method onPeriod takes unit u returns nothing
    // Fires on every interval. Use u to access the unit.

method onCast takes unit caster, unit target returns nothing
    // Fires when the unit casts an ability. Use caster to access the unit.

method onTargeted takes unit target, unit caster returns nothing
    // Fires when the unit is targeted with an ability. Use target to access the unit.

method onAttack takes unit attacker, unit target returns nothing
    // Fires when the unit attacks. Use attacker to access the unit.

method onAttacked takes unit target, unit attacker returns nothing
    // Fires when the unit is attacked. Use target to access the unit.

method onDamageDealt takes unit source, unit target returns nothing
    // Fires when the unit deals damage. Use source to access the unit.

method onDamageTaken takes unit target, unit source returns nothing
    // Fires when the unit receives damage. Use target to access the unit.

method onKill takes unit killer, unit victim returns nothing
    // Fires when the unit kills another. Use killer to access the unit.

method onAllyCasts takes unit u, unit ally, unit target returns nothing
method onAllyTargeted takes unit u, unit ally, unit caster returns nothing
method onAllyAttacks takes unit u, unit ally, unit target returns nothing
method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
method onAllyDmgDealt takes unit u, unit ally, unit target returns nothing
method onAllyDmgTaken takes unit u, unit ally, unit source returns nothing
method onAllyKills takes unit u, unit ally, unit victim returns nothing
method onAllyDeath takes unit u, unit ally, unit killer returns nothing
    // Events that will fire when a nearby friendly unit performs a certain action.
    // These events will only activate if OTHER_UNITS_EVENTS is set to true.
    // Use u to access the unit with custom behavior.
    // Use ally to access the allied unit that fires the event.

method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
method onEnemyTargeted takes unit u, unit enemy, unit caster returns nothing
method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
method onEnemyAttacked takes unit u, unit enemy, unit attacker returns nothing
method onEnemyDmgDealt takes unit u, unit enemy, unit target returns nothing
method onEnemyDmgTaken takes unit u, unit enemy, unit source returns nothing
method onEnemyKills takes unit u, unit enemy, unit victim returns nothing
method onEnemyDeath takes unit u, unit enemy, unit killer returns nothing
    // Events that will fire when a nearby enemy unit performs a certain action.
    // These events will only activate if OTHER_UNITS_EVENTS is set to true.
    // Use u to access the unit with custom behavior.
    // Use enemy to access the enemy unit that fires the event.

vJASS:
method create takes unit u returns thistype
    // Registers the unit so it can use custom orders given by its Behavior.

method checkOrder takes string orderStr returns boolean
    // Checks if an orders is already registered.

method checkOrderById takes integer orderId returns boolean
    // Checks if an orders is already registered using integers for order id.

method isOrderInCooldown takes string orderStr returns boolean
    // Checks if an orders is in cooldown.

method isOrderInCooldownById takes integer orderId returns boolean
    // Checks if an orders is in cooldown using integers for order id.

method registerTargetOrder takes string orderStr, widget target, integer priority, real cooldown, integer manaCost returns nothing
method registerPointOrder takes string orderStr, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
method registerInstantOrder takes string orderStr, integer priority, real cooldown, integer manaCost returns nothing
    // Registers an order using strings.

method registerTargetOrderById takes integer orderId, widget target, integer priority, real cooldown, integer manaCost returns nothing
method registerPointOrderById takes integer orderId, real locX, real locY, integer priority, real cooldown, integer manaCost returns nothing
method registerInstantOrderById takes integer orderId, integer priority, real cooldown, integer manaCost returns nothing
    // Registers an order using its order id

  • Trigger - Run RegisterBehavior <gen> (ignoring conditions)
  • -------- Adds a custom Behavior to a single unit --------
  • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • -------- Adds a custom Behavior to an unit type --------

  • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Trigger - Run RegisterPointOrder <gen> (ignoring conditions)
  • Trigger - Run RegisterInstantOrder <gen> (ignoring conditions)
  • -------- Registers an order using strings --------
  • Trigger - Run RegisterTargetOrderById <gen> (ignoring conditions)
  • Trigger - Run RegisterPointOrderById <gen> (ignoring conditions)
  • Trigger - Run RegisterInstantOrderById <gen> (ignoring conditions)
  • -------- Registers an order using order Id --------
  • Trigger - Run CheckCooldown <gen> (ignoring conditions)
  • Trigger - Run CheckCooldownById <gen> (ignoring conditions)
  • -------- Checks if an order is on cooldown --------
  • -------- This is called first, then you can use the variable CustomO_IsInCooldown --------

vJASS:
1. Used to register behaviors:
   -Behavior_Unit (Unit that will be registered for an individual behavior)
   -Behavior_UnitType (Used for Unit Type behaviors)
   -Behavior_IsPlayerBehavior (If false, it will fire only for Computer owned units. If true, it will fire only for Player owned units)

2. Used to register events for the unit in a behavior:
   -Behavior_OnPeriod (Registers a trigger that will fire on every interval. Use "Event_Unit" to access the unit)
   -Behavior_OnCast (Registers a trigger that will fire when the unit casts an ability. Use "Event_Caster" to access the unit and "Event_Target" to access the target)
   -Behavior_OnTargeted (Registers a trigger that will fire when the unit is targeted with an ability. Use "Event_Target" to access the unit and "Event_Caster" to access the caster)
   -Behavior_OnAttack (Registers a trigger that will fire when the unit attacks. Use "Event_Attacker" to access the unit and "Event_Target" to access the target)
   -Behavior_OnAttacked (Registers a trigger that will fire when the unit is attacked. Use "Event_Target" to access the unit and "Event_Attacker" to access the attacker)
   -Behavior_OnDamageDealt (Registers a trigger that will fire when the unit deals damage. Use "Event_Source" to access the unit and "Event_Target" to access the target)
   -Behavior_OnDamageTaken (Registers a trigger that will fire when the unit receives damage. Use "Event_Target" to access the unit and "Event_Source" to access the source of damage)
   -Behavior_OnKill (Registers a trigger that will fire when the unit kills another. Use "Event_Killer" to access the unit and "Event_Victim" to access the dying unit)

3. Used to register events of nearby friendly units in a behavior:
   -Behavior_OnAllyCasts (Registers a trigger that will fire when a nearby friendly unit casts an ability. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Target" to access the target)
   -Behavior_OnAllyTargeted (Registers a trigger that will fire when a nearby friendly unit is targeted with an ability. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Caster" to access the caster)
   -Behavior_OnAllyAttacks (Registers a trigger that will fire when a nearby friendly unit attacks. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Target" to access the target)
   -Behavior_OnAllyAttacked (Registers a trigger that will fire when a nearby friendly unit is attacked. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Attacker" to access the attacker)
   -Behavior_OnAllyDamageDealt (Registers a trigger that will fire when a nearby friendly unit deals damage. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Target" to access the target)
   -Behavior_OnAllyDamageTaken (Registers a trigger that will fire when a nearby friendly unit receives damage. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Source" to access the source of damage)
   -Behavior_OnAllyKills (Registers a trigger that will fire when a nearby friendly unit kills another. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Victim" to access the dying unit)
   -Behavior_OnAllyDeath (Registers a trigger that will fire when a nearby friendly unit is killed. Use "Event_Unit" to access the unit with custom behavior, "Event_Ally" to access the ally and "Event_Killer" to access the killer)

4. Used to register events of nearby enemy units in a behavior:
   -Behavior_OnEnemyCasts (Registers a trigger that will fire when a nearby enemy unit casts an ability. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Target" to access the target)
   -Behavior_OnEnemyTargeted (Registers a trigger that will fire when a nearby enemy unit is targeted with an ability. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Caster" to access the caster)
   -Behavior_OnEnemyAttacks (Registers a trigger that will fire when a nearby enemy unit attacks. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Target" to access the target)
   -Behavior_OnEnemyAttacked (Registers a trigger that will fire when a nearby enemy unit is attacked. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Attacker" to access the attacker)
   -Behavior_OnEnemyDamageDealt (Registers a trigger that will fire when a nearby enemy unit deals damage. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Target" to access the target)
   -Behavior_OnEnemyDamageTaken (Registers a trigger that will fire when a nearby enemy unit receives damage. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Source" to access the source of damage)
   -Behavior_OnEnemyKills (Registers a trigger that will fire when a nearby enemy unit kills another. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Victim" to access the dying unit)
   -Behavior_OnEnemyDeath (Registers a trigger that will fire when a nearby enemy unit is killed. Use "Event_Unit" to access the unit with custom behavior, "Event_Enemy" to access the enemy and "Event_Killer" to access the killer)

5. Used to register custom orders:
   -CustomO_Order (The order that will be used)
   -CustomO_OrderId (For those abilities which must be cast using their Order Id. In those cases, use this instead of CustomO_Order)
   -CustomO_Unit (The unit that will use the order. Check the different events to see which unit you should assign here)
   -CustomO_Target (The unit that will be used as target of the order. Check the different events to see which unit you should assign here)
   -CustomO_Location (The location to be used as target in Point-based abilities)
   -CustomO_Priority (Priority assigned for the custom order. This will define the order in which the abilities will be used)
   -CustomO_Cooldown (Cooldown time for the order)
   -CustomO_ManaCost (Mana cost for the order)

vJASS:
library Example1 requires AIBehaviorController

    // Defining a new Behavior
    private struct Knight extends Behavior
      
        // This one will fire when the Knight attacks another unit
        method onAttack takes unit attacker, unit target returns nothing
            local unit u        = attacker
            local CustomOrder o = CustomOrder.create(u)
          
            // No target has been defined since the order to be registered will be an Instant Order
            // Defining the order, priority, cooldown and mana cost
            call o.registerInstantOrder("berserk", 100, 12, 0)
          
            set u = null
        endmethod
      
        // Creating the behavior and attaching it to all units of this type
        private static method onInit takes nothing returns nothing
            local Behavior b = Knight.create()
            call Controller.registerBehaviorType('hkni', b, false)
        endmethod
      
    endstruct

endlibrary

vJASS:
library Example2 requires AIBehaviorController

    // Defining the first Behavior
    private struct Sorceress1 extends Behavior
      
        // This one will fire when the Sorceress attacks another unit
        method onAttack takes unit attacker, unit target returns nothing
            local unit u        = attacker
            local CustomOrder o = CustomOrder.create(u)
          
            // Registering 2 orders inside the same event.
            // In this case, the order that will be used first will be the one with higher priority if not in cooldown
          
            // For the first order, we will use its Id because the order is based on Wand of Illusion
            // The unit will cast this ability on itself
            call o.registerTargetOrderById(852274, u, 100, 15, 50)
          
            // Registering the second order
            call o.registerTargetOrder("shockwave", target, 50, 10, 100)
          
            set u = null
        endmethod
      
        // Creating the behavior and attaching it to all units of this type
        private static method onInit takes nothing returns nothing
            local Behavior b = Sorceress1.create()
            call Controller.registerBehaviorType('hsor', b, false)
        endmethod
      
    endstruct
  
    // Defining a second Behavior
    private struct Sorceress2 extends Behavior
      
        // Using an event fired by a nearby enemy unit. In this case, it will run when an enemy unit uses an ability
        method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            // Checking if the enemy unit is a Necromancer
            if GetUnitTypeId(enemy) == 'unec' then
                call o.registerTargetOrder("frostnova", enemy, 100, 8, 75)
            endif
        endmethod
      
        // Adding a second event to the Behavior. This one will run when a nearby enemy attacks another unit
        method onEnemyAttacks takes unit u, unit enemy, unit attacker returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            // Checking if the enemy unit is a Necromancer
            if GetUnitTypeId(enemy) == 'unec' then
                call o.registerTargetOrder("frostnova", enemy, 100, 8, 75)
            endif
        endmethod
      
        // Creating the behavior and attaching it to all units of this type
        private static method onInit takes nothing returns nothing
            local Behavior b = Sorceress2.create()
            call Controller.registerBehaviorType('hsor', b, false)
        endmethod
    endstruct

endlibrary

vJASS:
library PaladinExample requires AIBehaviorController

    // Defining a new Behavior
    private struct Paladin extends Behavior
      
        // Using 2 events fired by a nearby friendly unit and another event fired when a Paladin attacks
        method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            // Check if ally is below 50% hit points
            if GetUnitStatePercent(ally, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) <= 50. then
                call o.registerTargetOrder("holybolt", ally, 30, 5, 60)
            endif
        endmethod
      
        method onAllyAttacks takes unit u, unit ally, unit target returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            // If ally does not have the buff, then cast the ability
            if not UnitHasBuffBJ(ally, 'Binf') then
                call o.registerTargetOrder("innerfire", ally, 20, 3, 35)
            endif
        endmethod
      
        method onAttack takes unit attacker, unit target returns nothing
            local unit u        = attacker
            local CustomOrder o = CustomOrder.create(u)
          
            // If the Paladin does not have the buff, then cast the ability on himself
            if not UnitHasBuffBJ(u, 'Binf') then
                call o.registerTargetOrder("innerfire", u, 15, 3, 35)
            endif
          
            set u = null
        endmethod
      
        // Creating the behavior and attaching it to all units of this type
        private static method onInit takes nothing returns nothing
            local Behavior b = Paladin.create()
            call Controller.registerBehaviorType('Hpal', b, false)
        endmethod
      
    endstruct

endlibrary

vJASS:
library PaladinSpc requires AIBehaviorController

    // This will be a more complex example. Here I will define 4 custom behaviors for 4 different Paladins
    // Each Paladin will only use the Behavior that will be asigned to him
    // If combined with the example above, all Paladins will have 2 custom Behaviors (Individual and Unit Type)
  
    // Northwest Paladin
    private struct PaladinNW extends Behavior
      
        method onAttack takes unit attacker, unit target returns nothing
            local unit u        = attacker
            local CustomOrder o = CustomOrder.create(u)
          
            call o.registerTargetOrder("acidbomb", target, 25, 12, 75)
          
            set u = null
        endmethod
      
        // Creating the behavior and attaching it to an specific Paladin
        // Unlike the behaviors assigned to an Unit Type, the behaviors attached to an individual unit
        // must be registered after all units have been indexed, so that custom values are available
        private static method register takes nothing returns nothing
            local Behavior b = PaladinNW.create()
            call Controller.registerBehavior(gg_unit_Hpal_0038, b, false)
        endmethod
      
        // Once all the units are indexed, the behavior will be registered
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod
      
    endstruct
  
    // Northeast Paladin
    private struct PaladinNE extends Behavior
      
        method onAttacked takes unit target, unit attacker returns nothing
            local unit u        = target
            local CustomOrder o = CustomOrder.create(u)
          
            call o.registerInstantOrder("divineshield", 30, 25, 50)
          
            set u = null
        endmethod
      
        private static method register takes nothing returns nothing
            local Behavior b = PaladinNE.create()
            call Controller.registerBehavior(gg_unit_Hpal_0037, b, false)
        endmethod
      
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod
      
    endstruct
  
    // Southwest Paladin
    private struct PaladinSW extends Behavior
      
        method onEnemyCasts takes unit u, unit enemy, unit target returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            if GetUnitTypeId(enemy) == 'Udea' then
                call o.registerTargetOrder("soulburn", enemy, 20, 12, 85)
            endif
        endmethod
      
        method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            if GetUnitTypeId(enemy) == 'Udea' then
                call o.registerTargetOrder("soulburn", enemy, 20, 12, 85)
            endif
        endmethod
      
        private static method register takes nothing returns nothing
            local Behavior b = PaladinSW.create()
            call Controller.registerBehavior(gg_unit_Hpal_0041, b, false)
        endmethod
      
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod
      
    endstruct
  
    // Southeast Paladin
    private struct PaladinSE extends Behavior
      
        method onAllyAttacked takes unit u, unit ally, unit attacker returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            if GetUnitStatePercent(ally, UNIT_STATE_LIFE, UNIT_STATE_MAX_LIFE) <= 70. then
                call o.registerTargetOrder("healingwave", ally, 25, 9, 90)
            endif
        endmethod
      
        private static method register takes nothing returns nothing
            local Behavior b = PaladinSE.create()
            call Controller.registerBehavior(gg_unit_Hpal_0046, b, false)
        endmethod
      
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod
      
    endstruct
  
endlibrary

vJASS:
library DKExample requires AIBehaviorController
  
    // Defining a new Behavior for a Player-owned unit, in this case for our Death Knight
    private struct DK extends Behavior
        // This event will fire when a nearby enemy attacks
        method onEnemyAttacks takes unit u, unit enemy, unit target returns nothing
            local CustomOrder o = CustomOrder.create(u)
          
            // If our Death Knight is low on mana, he will automatically use one of the available mana potions
            if UnitHasItemOfTypeBJ(u, 'pman') and GetUnitState(u, UNIT_STATE_MANA) <= 80. then
                call UnitUseItem(u, GetItemOfTypeFromUnitBJ(u, 'pman'))
            endif
          
            // Ordering our Death Knight to automatically use Death Coil against the attacking enemy
            call o.registerTargetOrder("deathcoil", enemy, 100, 6, 75)
        endmethod
      
        // Creating the behavior and attaching it to all units of this type
        private static method onInit takes nothing returns nothing
            local Behavior b = DK.create()
            call Controller.registerBehaviorType('Udea', b, true) // If true, this Behavior will trigger only for Player units
        endmethod
      
    endstruct
  
endlibrary

  • Footman GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Creating a new Behavior and attaching it to all units of this type --------
      • -------- Also adding actions that will be activated in the events below --------
      • Set Behavior_UnitType = Footman
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnDamageDealt = Footman OnDamageDealt <gen>
      • Set Behavior_OnEnemyAttacks = Footman OnEnemyAttacks <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Footman OnDamageDealt
    • Events
    • Conditions
    • Actions
      • -------- This trigger will fire when the Footman deals damage --------
      • -------- Registering the "thunderbolt" order that will be used by the unit --------
      • Set CustomO_Order = thunderbolt
      • -------- This will be the unit that will respond to the order, that is, the one that has a custom behavior --------
      • Set CustomO_Unit = Event_Source
      • -------- This will be the target unit --------
      • Set CustomO_Target = Event_Target
      • -------- Defining the order's priority, cooldown and mana cost --------
      • Set CustomO_Priority = 50
      • Set CustomO_Cooldown = 7.00
      • Set CustomO_ManaCost = 0
      • -------- Finally, this trigger will be called to register the order --------
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Footman OnEnemyAttacks
    • Events
    • Conditions
      • (Unit-type of Event_Enemy) Equal to Necromancer
    • Actions
      • -------- This trigger will fire when a nearby enemy attacks, checking first if the enemy unit is a Necromancer --------
      • Set CustomO_Order = thunderbolt
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Enemy
      • Set CustomO_Priority = 100
      • Set CustomO_Cooldown = 7.00
      • Set CustomO_ManaCost = 0
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)

  • Knight GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Creating a new Behavior and attaching it to all units of this type --------
      • -------- A single event has been assigned to this behavior --------
      • Set Behavior_UnitType = Knight
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAttack = Knight OnAttack <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Knight OnAttack
    • Events
    • Conditions
    • Actions
      • -------- This one will fire when the Knight attacks another unit --------
      • -------- No target has been defined since the order to be registered will be an Instant Order --------
      • Set CustomO_Order = berserk
      • Set CustomO_Unit = Event_Attacker
      • Set CustomO_Priority = 100
      • Set CustomO_Cooldown = 12.00
      • Set CustomO_ManaCost = 0
      • -------- Registering the instant order --------
      • Trigger - Run RegisterInstantOrder <gen> (ignoring conditions)

  • Sorceress 1 GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Defining the first Behavior and attaching it to all units of this type --------
      • Set Behavior_UnitType = Sorceress
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAttack = Sorceress 1 OnAttack <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Sorceress 1 OnAttack
    • Events
    • Conditions
    • Actions
      • -------- Registering 2 orders inside the same event --------
      • -------- In this case, the order that will be used first will be the one with higher priority if not in cooldown --------
      • -------- For the first order, I will use its Id because the order is based on Wand of Illusion --------
      • Set CustomO_OrderId = 852274
      • Set CustomO_Unit = Event_Attacker
      • -------- The unit will cast this ability on itself, so both variables will be the same --------
      • Set CustomO_Target = Event_Attacker
      • Set CustomO_Priority = 100
      • Set CustomO_Cooldown = 15.00
      • Set CustomO_ManaCost = 50
      • -------- Registering the order using Id --------
      • Trigger - Run RegisterTargetOrderById <gen> (ignoring conditions)
      • -------- ------------------ --------
      • -------- Second order --------
      • Set CustomO_Order = shockwave
      • Set CustomO_Unit = Event_Attacker
      • Set CustomO_Target = Event_Target
      • Set CustomO_Priority = 50
      • Set CustomO_Cooldown = 10.00
      • Set CustomO_ManaCost = 100
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Sorceress 2 GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Defining a second Behavior --------
      • -------- Using events fired by a nearby enemy unit --------
      • Set Behavior_UnitType = Sorceress
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnEnemyCasts = Sorceress 2 OnEnemyCasts <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Sorceress 2 OnEnemyCasts
    • Events
    • Conditions
      • (Unit-type of Event_Enemy) Equal to Necromancer
    • Actions
      • Set CustomO_Order = frostnova
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Enemy
      • Set CustomO_Priority = 100
      • Set CustomO_Cooldown = 8.00
      • Set CustomO_ManaCost = 75
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)

  • Paladins Gen GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Creating a new Behavior and attaching it to all units of this type --------
      • -------- Using 2 events fired by a nearby friendly unit and another event fired when a Paladin attacks --------
      • Set Behavior_UnitType = Paladin
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAllyAttacked = Paladins Gen OnAllyAttacked <gen>
      • Set Behavior_OnAllyAttacks = Paladins Gen OnAllyAttacks <gen>
      • Set Behavior_OnAttack = Paladins Gen OnAttack <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Paladins Gen OnAllyAttacked
    • Events
    • Conditions
      • (Percentage life of Event_Ally) Less than or equal to 50.00
    • Actions
      • Set CustomO_Order = holybolt
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Ally
      • Set CustomO_Priority = 30
      • Set CustomO_Cooldown = 5.00
      • Set CustomO_ManaCost = 60
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Paladins Gen OnAllyAttacks
    • Events
    • Conditions
      • (Event_Ally has buff Inner Fire) Equal to False
    • Actions
      • Set CustomO_Order = innerfire
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Ally
      • Set CustomO_Priority = 20
      • Set CustomO_Cooldown = 3.00
      • Set CustomO_ManaCost = 35
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Paladins Gen OnAttack
    • Events
    • Conditions
      • (Event_Attacker has buff Inner Fire) Equal to False
    • Actions
      • Set CustomO_Order = innerfire
      • Set CustomO_Unit = Event_Attacker
      • Set CustomO_Target = Event_Attacker
      • Set CustomO_Priority = 15
      • Set CustomO_Cooldown = 3.00
      • Set CustomO_ManaCost = 35
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)

  • Paladins Specific GUI
    • Events
      • Game - UnitIndexEvent becomes Equal to 3.00
    • Conditions
    • Actions
      • -------- This will be a more complex example. Here I will define 4 custom behaviors for 4 different Paladins --------
      • -------- Each Paladin will only use the Behavior that will be asigned to him --------
      • -------- If combined with the example above, all Paladins will have 2 custom Behaviors (Individual and Unit Type) --------
      • -------- ------------------ --------
      • -------- Unlike the behaviors assigned to an Unit Type, the behaviors attached to an individual unit --------
      • -------- must be registered after all units have been indexed, so that custom values are available. --------
      • -------- For that reason, I have used Game - Unit IndexEvent becomes Equal to 3.00 in Event --------
      • -------- ------------------ --------
      • -------- Northwest Paladin --------
      • -------- Creating the behavior and attaching it to an specific Paladin --------
      • Set Behavior_Unit = Paladin 0038 <gen>
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAttack = Paladin NW OnAttack <gen>
      • Trigger - Run RegisterBehavior <gen> (ignoring conditions)
      • -------- Northeast Paladin --------
      • -------- Creating the behavior and attaching it to an specific Paladin --------
      • Set Behavior_Unit = Paladin 0037 <gen>
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAttacked = Paladin NE OnAttacked <gen>
      • Trigger - Run RegisterBehavior <gen> (ignoring conditions)
      • -------- Southwest Paladin --------
      • -------- Creating the behavior and attaching it to an specific Paladin --------
      • Set Behavior_Unit = Paladin 0041 <gen>
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnEnemyAttacks = Paladin SW OnEnemyAttacks <gen>
      • Trigger - Run RegisterBehavior <gen> (ignoring conditions)
      • -------- Southeast Paladin --------
      • -------- Creating the behavior and attaching it to an specific Paladin --------
      • Set Behavior_Unit = Paladin 0046 <gen>
      • Set Behavior_IsPlayerBehavior = false
      • Set Behavior_OnAllyAttacked = Paladin SE OnAllyAttacked <gen>
      • Trigger - Run RegisterBehavior <gen> (ignoring conditions)
  • Paladin NW OnAttack
    • Events
    • Conditions
    • Actions
      • Set CustomO_Order = acidbomb
      • Set CustomO_Unit = Event_Attacker
      • Set CustomO_Target = Event_Target
      • Set CustomO_Priority = 25
      • Set CustomO_Cooldown = 12.00
      • Set CustomO_ManaCost = 75
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Paladin NE OnAttacked
    • Events
    • Conditions
    • Actions
      • Set CustomO_Order = divineshield
      • Set CustomO_Unit = Event_Target
      • Set CustomO_Priority = 30
      • Set CustomO_Cooldown = 25.00
      • Set CustomO_ManaCost = 50
      • Trigger - Run RegisterInstantOrder <gen> (ignoring conditions)
  • Paladin SW OnEnemyAttacks
    • Events
    • Conditions
      • (Unit-type of Event_Enemy) Equal to Death Knight
    • Actions
      • Set CustomO_Order = soulburn
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Enemy
      • Set CustomO_Priority = 20
      • Set CustomO_Cooldown = 12.00
      • Set CustomO_ManaCost = 85
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)
  • Paladin SE OnAllyAttacked
    • Events
    • Conditions
      • (Percentage life of Event_Ally) Less than or equal to 70.00
    • Actions
      • Set CustomO_Order = healingwave
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Ally
      • Set CustomO_Priority = 25
      • Set CustomO_Cooldown = 9.00
      • Set CustomO_ManaCost = 90
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)

  • Death Knight GUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Defining a new Behavior for a Player-owned unit, in this case for our Death Knight --------
      • -------- If Behavior_IsPlayerBehavior is true, this Behavior will trigger only for Player units --------
      • Set Behavior_UnitType = Death Knight
      • Set Behavior_IsPlayerBehavior = True
      • Set Behavior_OnEnemyAttacks = Death Knight OnEnemyAttacks <gen>
      • Trigger - Run RegisterBehaviorType <gen> (ignoring conditions)
  • Death Knight OnEnemyAttacks
    • Events
    • Conditions
    • Actions
      • -------- If our Death Knight is low on mana, he will automatically use one of the available mana potions --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Mana of Event_Unit) Less than or equal to 80.00
          • (Event_Unit has an item of type Potion of Mana) Equal to True
        • Then - Actions
          • Hero - Order Event_Unit to use (Item carried by Event_Unit of type Potion of Mana)
        • Else - Actions
      • -------- Ordering our Death Knight to automatically use Death Coil against the attacking enemy --------
      • Set CustomO_Order = deathcoil
      • Set CustomO_Unit = Event_Unit
      • Set CustomO_Target = Event_Enemy
      • Set CustomO_Priority = 100
      • Set CustomO_Cooldown = 6.00
      • Set CustomO_ManaCost = 75
      • Trigger - Run RegisterTargetOrder <gen> (ignoring conditions)


  • Kingz (The Behavior AI System served as the inspiration and base for this new system)
  • Bribe (Unit Indexer, Damage Engine, Table)
  • AGD (Multidimensional Array)
  • Bannar (RegisterEvent pack)

-Released

General:
-GUI-friendly version released.
-Added a new method to detect if an order is currently on cooldown.

Test map:
-Better descriptions added in vJass examples.
-Fixed some minor bugs with test spells.

General:
-Added a timer that fires when an order is used to prevent the unit from skipping registered orders when multiple events of the same type activate in a short period of time.
-Some small improvements in the main code.

General:
-Array initializer moved to a new module to avoid possible access before they are actually initialized.
-Redundant triggers in the initializer method have now been properly cleaned up when the optional requirements are used.
-The custom functions GetUnitsInRect and IsAlive have been replaced by the native functions GroupEnumUnitsInRect and UnitAlive respectively.
-Variable assignment in the GUI Adapter is done directly now.

General:
-The system now actually checks if the unit can use the ability assigned to the order by directly checking its availability on the conditions before triggering the custom order cooldown.
-Increased maximum order priority value from 30 to 100.
-Some small optimizations on the code.
-Added support for Player controlled units.

Test map:
-Improved some descriptions.

General:
-Fixed a bug that arose from the last update after allowing the use of Player units, especially in periodic events.
Contents

AI Behavior Controller v1.41 (Map)

Deleted member 247165

D

Deleted member 247165

Would you be able to create a GUI alternate version of this? It would really help me improve the current AI I have. Besides, it's good to learn something new everyday! :D
 

Deleted member 247165

D

Deleted member 247165

Amazing! It would be of great help! :D
 

Deleted member 247165

D

Deleted member 247165

Updated:

General:
-GUI-friendly version released.
-Added a new method to detect if an order is currently on cooldown.

Test map:
-Better descriptions added in vJass examples.
-Fixed some minor bugs with test spells.
Really good! :D
 
Level 16
Joined
Feb 2, 2009
Messages
162
This is really, really neat! I hope that we can use Hive resources like this in the follow up contest to the boss arenas. I would love to be able to use this for running an add fight phase for example.

Super cool work, and thanks for making it GUI friendly!
 
Impressive work! This system definitely opens up a lot of possibilities for the user.
"Channel"-based spells can now stand together with the regular-based custom spells as equals.

AI Behavior Controller

Though it is particularly unlikely to happen, access to the public Multidimensional Array objects might be​
possible before they are initialized. Just to be safe, you'll have to change the library initializer to a​
module initializer. (Example below)​
JASS:
[/INDENT]
[INDENT]library AIBehaviorController // ...[/INDENT]
[INDENT][/INDENT]
[INDENT]private module Init[/INDENT]
[INDENT]    private static method onInit takes nothing returns nothing[/INDENT]
[INDENT]        call thistype.init()[/INDENT]
[INDENT]    endmethod[/INDENT]
[INDENT]endmodule[/INDENT]
[INDENT][/INDENT]
[INDENT]// ...[/INDENT]
[INDENT]struct Controller extends array[/INDENT]
[INDENT]    private static method initObjects takes nothing returns nothing[/INDENT]
[INDENT]    // ... Initialization of Multidimensional Array objects moved here.[/INDENT]
[INDENT]    endmethod[/INDENT]
[INDENT][/INDENT]
[INDENT]    // private static method onInit takes nothing returns nothing[/INDENT]
[INDENT]    private static method init takes nothing returns nothing[/INDENT]
[INDENT]        // ...[/INDENT]
[INDENT]        call thistype.initObjects()[/INDENT]
[INDENT]    endmethod[/INDENT]
[INDENT]    implement Init[/INDENT]
[INDENT]endstruct[/INDENT]
[INDENT][/INDENT]
[INDENT]// private function Init takes nothing returns nothing[/INDENT]
[INDENT]// ... Initialization of Multidimensional Array objects moved[/INDENT]
[INDENT]// endfunction[/INDENT]
[INDENT]
Also, in the struct initializer method, there are three redundant triggers which need to be​
cleaned up if the optional requirements are used in the map. These are:​
  • castEvent
  • damageEvent
  • killEvent
The GetUnitsInRect function appears to be a wrapper function that just calls​
GroupEnumUnitsInRange. Unless this is meant to be a configurable part of​
the system, I would recommend calling the native function directly.​
The ai native function UnitAlive can be declared on top of the configuration tab so​
that the IsAlive function uses it instead.​
JASS:
[/INDENT]
[INDENT]native UnitAlive takes unit id returns boolean[/INDENT]
[INDENT]

AIBC Adapter GUI
In the assignVariables method, there's a lot of if-then comparisons being made that essentially checks​
whether each GUI trigger variable used by the system has a value, then assigns the struct member to these​
triggers. Since the methods in the struct check for their presence anyways, I think these comparisons​
are redundant. Just assign the member variables directly.​
 
Updated:

General:
-Array initializer moved to a new module to avoid possible access before they are actually initialized.
-Redundant triggers in the initializer method have now been properly cleaned up when the optional requirements are used.
-The custom functions GetUnitsInRect and IsAlive have been replaced by the native functions GroupEnumUnitsInRect and UnitAlive respectively.
-Variable assignment in the GUI Adapter is done directly now.
 
Does the system work when a unit transforms into a unit type with an AI behavior even if they didn't have a behavior in their previous form? I was thinking about trying to use this as the basis for an advanced autocast logic setup.

edit: Tested this and its already present.
 
Last edited:
I thought of an optimization:

Instead of iterating over every unit in the map every period for periodic behaviors, why not add units to a global unit group as they get behaviors assigned? If you have 2 units with AI behaviors out of 100 units that's a lot of processing saved.
 
This isn't working for me in the latest patch for reforged.

View attachment 474538
It appears to be a problem with one of the required libraries not with my system. I think MultidimensionalArray stopped being compatible with latest versions of Bribe's Table. Can I ask what version of Table are you using?

Anyway, for whoever is reading this, this system was designed in patch 1.27b and I am not aware of what problems it may cause in current versions of Reforged. If anyone is feeling brave and is interested in giving this system an update to ensure its complete compatibility with modern versions of the game, you can contact me since I can't do it at the moment.
 
Level 5
Joined
Jan 16, 2016
Messages
114
It appears to be a problem with one of the required libraries not with my system. I think MultidimensionalArray stopped being compatible with latest versions of Bribe's Table. Can I ask what version of Table are you using?

Anyway, for whoever is reading this, this system was designed in patch 1.27b and I am not aware of what problems it may cause in current versions of Reforged. If anyone is feeling brave and is interested in giving this system an update to ensure its complete compatibility with modern versions of the game, you can contact me since I can't do it at the moment.
Hey, sorry it was a fault on my end, it seems that I needed to have the "Enable optimizer" checkbox ticked. After that, I was able to save the map and test it properly.

1716751199648.png
 
Really cool and useful system for custom spells. Is it possible to register behavior for abilities/carried items rather than unit types? Many different heroes/units can use the same custom spells. Besides, items have these custom spells. They would have the same behavior. For example, I have created a mass holy bolt spell which can be used to heal friendly units or damage undead units in an area. The same behavior should be applied to all heroes/units with this custom spell and all heroes who carry an item with this custom spell, so it would make more sense to register it based on the ability IDs.
 
Really cool and useful system for custom spells. Is it possible to register behavior for abilities/carried items rather than unit types? Many different heroes/units can use the same custom spells. Besides, items have these custom spells. They would have the same behavior. For example, I have created a mass holy bolt spell which can be used to heal friendly units or damage undead units in an area. The same behavior should be applied to all heroes/units with this custom spell and all heroes who carry an item with this custom spell, so it would make more sense to register it based on the ability IDs.
It could be an option for the future, yes. This system could use some optimizations and new functionalities.

Anyway your example with that holy bolt spell could be easily done with the current system, defining a global behavior for that custom spell and then register it for all units that will use it, something like this:

1719251285718.png


The same could work with GUI as well.
 
Huh, only find out this existed. Quick question, can you use this to make ai players hire heroes from taverns?
Tbh, I never tried using tavern heroes for AI before, with or without this system, so I'm not sure about that.

I don't think it's as easy as just having the AI hire the hero from the tavern, I guess you have to also add it to your race's AI script so that it recognizes the hero as usable.
 
Level 16
Joined
Dec 20, 2012
Messages
213
Tbh, I never tried using tavern heroes for AI before, with or without this system, so I'm not sure about that.

I don't think it's as easy as just having the AI hire the hero from the tavern, I guess you have to also add it to your race's AI script so that it recognizes the hero as usable.
Yea, I was afraid of that. Looks like I need to deep dive into AMAI eventually. Thanks for answering, useful system by the way since I used Channel custom spells too which I would like ai to use in my altered melee.
 
Yea, I was afraid of that. Looks like I need to deep dive into AMAI eventually. Thanks for answering, useful system by the way since I used Channel custom spells too which I would like ai to use in my altered melee.
For a demonstration of this system being used on a real map, the heroes of my recent Qiraji race use it for their abilities.

I see this system is being used a lot lately, I think I will have to release a new update with some changes and optimizations.
 
Updated:

General:
-The system now actually checks if the unit can use the ability assigned to the order by directly checking its availability on the conditions before triggering the custom order cooldown.
-Increased maximum order priority value from 30 to 100.
-Some small optimizations on the code.
-Added support for Player controlled units.

Test map:
-Improved some descriptions.
 
I thought of an optimization:

Instead of iterating over every unit in the map every period for periodic behaviors, why not add units to a global unit group as they get behaviors assigned? If you have 2 units with AI behaviors out of 100 units that's a lot of processing saved.
Nice system but I'm disappointment that this wasn't added.
 
Is it possible to use this system to give ai/autocast functionality to units controlled by the player?
Yes, with this current version is now possible to create autocast abilities for player units using the correct settings.

Nice system but I'm disappointment that this wasn't added.
I know, but that's something that could possibly take more time than I have available at the moment. If the system worked only with individual units it would be easy to handle periodic events through a global unit group, but I still think what would be the most optimal and feasible option to add all units that belong to a unit-type to that global group.



Updated:

General:
-Fixed a bug that arose from the last update after allowing the use of player units, especially in periodic events.
 
Last edited:
Level 2
Joined
Mar 15, 2021
Messages
8
I just got back into WC3 modding and I'm unearthing my previous project - this is really useful for creating just a tad bit more challenging AI on a dungeon crawling map! :p I have a question though, when checking for a situation where a "nearby unit" does something, I'm assuming "UNIT_SEARCH_RANGE = 700" is the default range - so would there be a way to specify that in GUI separately for each event? Like maybe sometimes I'd like it to be within 350 range and other times 1000 range. I'm very rusty with triggering so bear with me!
 
I just got back into WC3 modding and I'm unearthing my previous project - this is really useful for creating just a tad bit more challenging AI on a dungeon crawling map! :p I have a question though, when checking for a situation where a "nearby unit" does something, I'm assuming "UNIT_SEARCH_RANGE = 700" is the default range - so would there be a way to specify that in GUI separately for each event? Like maybe sometimes I'd like it to be within 350 range and other times 1000 range. I'm very rusty with triggering so bear with me!
Yes, UNIT_SEARCH_RANGE is the default value used to search for nearby units and you can adjust that value to your needs.

You can also define a different range in the behavior conditions that will work separately from the default range (as long as it has a value less than UNIT_SEARCH_RANGE).
 
Top