• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

AI Behavior Controller v1.3 [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 circunstances.

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.
  • 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 returns nothing
    // Adds a custom Behavior to a single unit.

static method registerBehaviorType takes integer uTypeId, Behavior b returns nothing
    // Adds a custom Behavior to an unit type.

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)

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)
     
            call o.registerInstantOrder("berserk", 10, 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)
        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 will be the one with higher priority if not in cooldown
            call o.registerTargetOrderById(852274, attacker, 30, 15, 50)
            call o.registerTargetOrder("shockwave", target, 20, 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)
        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)
     
            // A condition has been added here, and if it is met then the order will be assigned
            if GetUnitTypeId(enemy) == 'unec' then
                call o.registerTargetOrder("frostnova", enemy, 30, 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)
     
            if GetUnitTypeId(enemy) == 'unec' then
                call o.registerTargetOrder("frostnova", enemy, 30, 8, 75)
            endif
        endmethod
 
        // Creating the second 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)
        endmethod

    endstruct

endlibrary

vJASS:
library PaladinExample requires AIBehaviorController

    private struct Paladin extends Behavior

        // Using an event fired by a nearby friendly unit.
        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) <= 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 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 not UnitHasBuffBJ(attacker, 'Binf') then
                call o.registerTargetOrder("innerfire", attacker, 15, 3, 35)
            endif
     
            set u = null
        endmethod
 
        private static method onInit takes nothing returns nothing
            local Behavior b = Paladin.create()
            call Controller.registerBehaviorType('Hpal', b)
        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 3rd example, all Paladins will have 2 custom Behaviors (Individual and Unit Type)

    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)
        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

    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)
        endmethod
 
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod

    endstruct

    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)
        endmethod
 
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        endmethod

    endstruct

    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)
        endmethod
 
        private static method onInit takes nothing returns nothing
            call OnUnitIndexerInitialized(function thistype.register)
        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_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 = 10
      • 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 = 20
      • 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_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 = 10
      • 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_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 = 30
      • 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 = 20
      • 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_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 = 30
      • 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_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_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_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_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_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)


  • 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.
Contents

AI Behavior Controller v1.3 (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 15
Joined
Feb 2, 2009
Messages
154
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.​
 
Level 27
Joined
May 18, 2018
Messages
397
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.
 
Top