HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Criticism of AI executor

10-03-2008, 07:55 AM#1
fX_
redid it a bit. still no criticism?

note: only up to 2 AIs can ever be compared at one time: the unit's current ai and the triggered ai (the ai to be executed), or, in the case that the unit is not executing ai, only the latter.
no 2 event-condition combos can be the same. variation in actions that a unit is to execute for a particular event-condition occurs by randomization, w/c is a function of the action. so, there's always only 1 action (AI) for every trigger-condition combo.

Collapse JASS:
library UnitAISystem requires GeneralLibrary

globals
    //Values
    private constant real gR_DurationTimer = 0.10

    //Handler Objects
    AIGroup array gAIG
endglobals

interface AI
    //Gets the priority of an AI at a certain time.
    method GetPriority takes nothing returns integer

    //Clear data when AI is no longer in use. Can be called in an AIGroup when clearing a whole AIGroup.
    method Destroy takes nothing returns nothing

    //Secondary action for the trigger associated with the AI. Allows the use of data within the AI.
    //Trigger variables (e.g. GetTriggerUnit()) are passed by temporary global variables.
    method Action takes nothing returns nothing
endinterface

struct AIGroup

    //--------------------INSTANCE FUNCTIONS--------------------//
    //Only these functions are non-private. They're the only ones to be 'touched'/used. Other functions
    //are integral to the system and the struct and are static/constant.
    private integer intAIGIndex
    unit uSubject
    private integer intCountAI
    private AI array ai[100]
    private trigger array trigEvent[100]
    private string array strOrder[100]
    private integer intCurrentAIIndex

    //Constructor for AIGroups.
    static method Create takes unit subject returns AIGroup
        local AIGroup New = AIGroup.allocate()

        set New.intAIGIndex = AIGroup.intCountAIG
        set New.uSubject = subject
        set New.intCountAI = 0
        set New.intCurrentAIIndex = -1
        
        set gAIG[AIGroup.intCountAIG] = New

        set AIGroup.intCountAIG = AIGroup.intCountAIG + 1
        call AIGroup.SetTimer()

        return New
    endmethod

    //Used to add an AI to a particular AIGroup
    method AddAI takes AI newAI, trigger newTrig, string order returns nothing
        call TriggerAddCondition(newTrig, Condition(function AIGroup.AICondition))
        call TriggerAddAction(newTrig, function AIGroup.AIAction)

        set .ai[.intCountAI] = newAI
        set .trigEvent[.intCountAI] = newTrig
        set .strOrder[.intCountAI] = order

        set .intCountAI = .intCountAI + 1
    endmethod

    //Destroys a particular AIGroup.
    method Destroy takes nothing returns nothing
        set .uSubject = null
        loop
            exitwhen .intCountAI == 0
            set .intCountAI = .intCountAI - 1
            call .ai[.intCountAI].Destroy()
            call DestroyTrigger(.trigEvent[.intCountAI])
            set .trigEvent[.intCountAI] = null
            set .strOrder[.intCountAI] = null
        endloop

        set gAIG[AIGroup.intCountAIG].intAIGIndex = .intAIGIndex
        set gAIG[.intAIGIndex] = gAIG[AIGroup.intCountAIG]
        set gAIG[AIGroup.intCountAIG] = 0
        set AIGroup.intCountAIG = AIGroup.intCountAIG - 1
        call AIGroup.SetTimer()
    endmethod

    //Updates the data within a particular AIGroup.
    private method Update takes nothing returns nothing
        if GetUnitState(.uSubject, UNIT_STATE_LIFE) > 0.00 then
            if .intCurrentAIIndex != -1 and OrderId2String(GetUnitCurrentOrder(.uSubject)) != .strOrder[.intCurrentAIIndex] then
                call BJDebugMsg("The last AI had been executed.")
                set .intCurrentAIIndex = -1
            endif
        else
            call .Destroy()
        endif
    endmethod

    
    
    //--------------------COLLECTIVE-INSTANCE FUNCTIONS--------------------//
    private static integer intCountAIG = 0 //Number of existent AIGroups.
    private static integer intEventAIGIndex //Temp variable.
    private static integer intEventAIIndex //Temp variable.

    //Temporary Variables
    public static unit uTriggerUnit = null
    public static unit uAttacker = null
    public static unit uEventDamageSource = null
    public static player pTriggerPlayer = null
    public static string strEventPlayerChatString = null
    public static string strEventPlayerChatStringMatched = null
    public static real rEventDamage = 0.00
    
    //Condition that checks for an action's contingency (i.e. priority/relevance/exigency).
    private static method AICondition takes nothing returns boolean
        local trigger TRIG_Event = GetTriggeringTrigger()

        //Find the AI that is triggered.
        set AIGroup.intEventAIGIndex = 0
        loop
            set AIGroup.intEventAIIndex = gAIG[AIGroup.intEventAIGIndex].intCountAI - 1
            exitwhen gAIG[AIGroup.intEventAIGIndex].trigEvent[AIGroup.intEventAIIndex] == TRIG_Event
            loop
                exitwhen gAIG[AIGroup.intEventAIGIndex].trigEvent[AIGroup.intEventAIIndex] == TRIG_Event or AIGroup.intEventAIIndex < 0
                set AIGroup.intEventAIIndex = AIGroup.intEventAIIndex - 1
            endloop
        endloop

        set TRIG_Event = null

        //Execute the AI if it is substantive (it has the green light set), it is not already being executed,
        //and it has a higher priority than the currently-being-executed AI - or if this is null.
        if gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].GetPriority() != -1 and gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex != AIGroup.intEventAIIndex and (gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex == -1 or gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].GetPriority() > gAIG[AIGroup.intEventAIGIndex].ai[gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex].GetPriority()) then
            return true
        endif

        return false
    endmethod

    //Action that executes a secondary action, w/c has access to an AI's data.
    private static method AIAction takes nothing returns nothing
        set AIGroup.uTriggerUnit = GetTriggerUnit()
        set AIGroup.uAttacker = GetAttacker()
        set AIGroup.uEventDamageSource = GetEventDamageSource()
        set AIGroup.pTriggerPlayer = GetTriggerPlayer()
        set AIGroup.strEventPlayerChatString = GetEventPlayerChatString()
        set AIGroup.strEventPlayerChatStringMatched = GetEventPlayerChatStringMatched()
        set AIGroup.rEventDamage = GetEventDamage()

        set gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex = AIGroup.intEventAIIndex
        call gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].Action()
        
        set AIGroup.uTriggerUnit = null
        set AIGroup.uAttacker = null
        set AIGroup.uEventDamageSource  = null
        set AIGroup.pTriggerPlayer  = null
        set AIGroup.strEventPlayerChatString  = null
        set AIGroup.strEventPlayerChatStringMatched  = null
        set AIGroup.rEventDamage  = 0.00
    endmethod

    
    
    //--------------------STRUCT FUNCTIONS--------------------//
    private static timer tim = CreateTimer() //Timer than runs updates for data of AIGroups.

    //Manages the struct's general timer.
    private static method SetTimer takes nothing returns nothing
        if AIGroup.intCountAIG == 1 then
            call TimerStart(AIGroup.tim, gR_DurationTimer, true, function AIGroup.TimerCallback)
        elseif AIGroup.intCountAIG == 0 then
            call PauseTimer(AIGroup.tim)
        endif
    endmethod

    //Callback for the struct's general timer. Calls Update()s for every AIGroup.
    private static method TimerCallback takes nothing returns nothing
        local integer INT_CountAIG = 0

        loop
            exitwhen INT_CountAIG == AIGroup.intCountAIG
            call gAIG[INT_CountAIG].Update()
            set INT_CountAIG = INT_CountAIG + 1
        endloop
    endmethod
    
endstruct

endlibrary
10-13-2008, 01:40 PM#2
moyack
Test map please...
10-19-2008, 12:19 PM#3
fX_
edit: added test map. the paladin doesn't continue with his last "move" order - instead, he executes the new "move" order, w/c is triggered every 0.25s and occurs while he heals/attacks (w/c def takes more than 0.25s) - after healing/attacking...

here's the code with the queue system incorporated into it:

Collapse JASS:
library UnitAISystem requires GeneralLibrary

globals
    //Duration between intervals at which the system will check if a current AI has been executed and update itself.
    private constant real gR_DurationTimer = 0.10

    //AIGroup objects. Collation of AI w/c are executed in correspondence to each other's priorities/exigencies.
    AIGroup array gAIG
endglobals

//Interface for structs containing an AI's data.
interface AI
    //Gets the priority of an AI at a certain trigger event. May respond to trigger events.
    method GetOnEventPriority takes nothing returns integer
    
    //Gets the priority of an AI at a certain time after a trigger event.
    //May use data stored when the trigger event occured.
    method GetInQueuePriority takes nothing returns integer

    //Action for the trigger associated with the AI. Allows the use of data within the AI.
    //A 'secondary action'; executed by the trigger's triggeraction. Trigger variables
    //(e.g. GetTriggerUnit()) are passed by temporary global variables.
    method Action takes nothing returns nothing

    //Used to clear data when AI is no longer in use. Can be called in an AIGroup when clearing a whole AIGroup.
    method Destroy takes nothing returns nothing    
endinterface

struct AIGroup

    //--------------------STRUCT FUNCTIONS--------------------//
    private static timer tim = CreateTimer() //Timer than runs updates for data of AIGroups.

    //Manages the struct's general timer.
    private static method SetTimer takes nothing returns nothing
        if AIGroup.intCountAIG == 1 then
            call TimerStart(AIGroup.tim, gR_DurationTimer, true, function AIGroup.TimerCallback)
        elseif AIGroup.intCountAIG == 0 then
            call PauseTimer(AIGroup.tim)
        endif
    endmethod

    //Callback for the struct's general timer. Calls Update()s for every AIGroup.
    private static method TimerCallback takes nothing returns nothing
        local integer INT_CountAIG = 0

        loop
            exitwhen INT_CountAIG == AIGroup.intCountAIG
            call gAIG[INT_CountAIG].Update()
            set INT_CountAIG = INT_CountAIG + 1
        endloop
    endmethod

    //--------------------SYSTEM FUNCTIONS--------------------//
    private static integer intCountAIG = 0 //Number of existent AIGroups.
    private static integer intEventAIGIndex //Temp variable.
    private static integer intEventAIIndex //Temp variable.

    //Temporary Variables
    public static unit uTriggerUnit = null
    public static unit uAttacker = null
    public static unit uEventDamageSource = null
    public static player pTriggerPlayer = null
    public static string strEventPlayerChatString = null
    public static string strEventPlayerChatStringMatched = null
    public static real rEventDamage = 0.00

    private method QueueAI takes integer aiIndex returns nothing
    endmethod

    //Condition that checks for an action's contingency.
    private static method AICondition takes nothing returns boolean
        local trigger TRIG_Event = GetTriggeringTrigger()

        //Setup access to trigger variables.
        set AIGroup.uTriggerUnit = GetTriggerUnit()
        set AIGroup.uAttacker = GetAttacker()
        set AIGroup.uEventDamageSource = GetEventDamageSource()
        set AIGroup.pTriggerPlayer = GetTriggerPlayer()
        set AIGroup.strEventPlayerChatString = GetEventPlayerChatString()
        set AIGroup.strEventPlayerChatStringMatched = GetEventPlayerChatStringMatched()
        set AIGroup.rEventDamage = GetEventDamage()

        //Find the AI that is triggered.
        set AIGroup.intEventAIGIndex = 0
        loop
            set AIGroup.intEventAIIndex = gAIG[AIGroup.intEventAIGIndex].intCountAI - 1
            loop
                exitwhen gAIG[AIGroup.intEventAIGIndex].trigEvent[AIGroup.intEventAIIndex] == TRIG_Event or AIGroup.intEventAIIndex == 0
                set AIGroup.intEventAIIndex = AIGroup.intEventAIIndex - 1
            endloop
            exitwhen gAIG[AIGroup.intEventAIGIndex].trigEvent[AIGroup.intEventAIIndex] == TRIG_Event
            set AIGroup.intEventAIGIndex = AIGroup.intEventAIGIndex + 1
        endloop

        set TRIG_Event = null
        
        //If the AI-to-be-executed is non-substative/non-contingent, or it is already being executed, do not execute it.
        //Else, see below.
        if gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].GetOnEventPriority() == -1 or gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex == AIGroup.intEventAIIndex then
            return false
        else
            //If there is no AI currently being executed, then the AI-to-be-executed takes priority. Else, see below.
            if gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex == -1 then
                return true
            else
                //If the AI-to-be-executed takes priority, execute it and queue the currently-being-executed AI
                //(if it substantive/contingent); else, queue the former and do not execute it now.
                if gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].GetOnEventPriority() > gAIG[AIGroup.intEventAIGIndex].ai[gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex].GetInQueuePriority() then
                    if gAIG[AIGroup.intEventAIGIndex].ai[gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex].GetOnEventPriority() != -1 then
                        call gAIG[AIGroup.intEventAIGIndex].QueueAddAI(gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex)
                    endif
                    return true
                else
                    call gAIG[AIGroup.intEventAIGIndex].QueueAddAI(AIGroup.intEventAIIndex)
                    return false
                endif            
            endif
        endif

        return false
    endmethod

    //Action that executes a secondary action, w/c has access to an AI's data.
    private static method AIAction takes nothing returns nothing
        set gAIG[AIGroup.intEventAIGIndex].intCurrentAIIndex = AIGroup.intEventAIIndex
        call gAIG[AIGroup.intEventAIGIndex].ai[AIGroup.intEventAIIndex].Action()
    endmethod

    //Add an AI to the queue.
    private method QueueAddAI takes integer aiIndex returns nothing
        set .intQueueAIIndex[.intCountQueueAI] = aiIndex
        set .intCountQueueAI = .intCountQueueAI + 1
    endmethod

    //Remove an AI from the queue.
    private method QueueRemoveAI takes integer queueIndex returns nothing
        set .intQueueAIIndex[queueIndex] = .intQueueAIIndex[.intCountQueueAI - 1]
        set .intCountQueueAI = .intCountQueueAI - 1
    endmethod

    //Sorts the queue of AI indexes according to the priority of their corresponding AIs (priority+ : index-).
    //If an AI is unsubstantive/not-contingent (has a priority of -1), it is removed from the queue.
    //Sorting method: Makes comparisons between consequtive instances. To sort in scopes greater than
    //successions of 1, the process is repeated until no disparity is found.
    private method PrioritizeQueueAI takes nothing returns nothing
        local integer INT_QueueAIIndex = 0
        local integer INT
        local boolean BOOL_Repeat = false

        loop
            exitwhen INT_QueueAIIndex == .intCountQueueAI
            if .ai[INT_QueueAIIndex].GetInQueuePriority() == -1 then
                //Remove from queue.
                set BOOL_Repeat = true
                call .QueueRemoveAI(INT_QueueAIIndex)
            elseif .ai[INT_QueueAIIndex].GetInQueuePriority() < .ai[INT_QueueAIIndex + 1].GetInQueuePriority() then
                //Sort consecutive indexes.
                set BOOL_Repeat = true
                set INT = .intQueueAIIndex[INT_QueueAIIndex]
                set .intQueueAIIndex[INT_QueueAIIndex] = .intQueueAIIndex[INT_QueueAIIndex + 1]
                set .intQueueAIIndex[INT_QueueAIIndex + 1] = INT
            endif
            set INT_QueueAIIndex = INT_QueueAIIndex + 1
        endloop

        if BOOL_Repeat then
            call .PrioritizeQueueAI()
        endif
    endmethod

    //Refreshes the priorities of all AIs in the queue first, then executes the next queued AI (if there is any) and
    //removes it from the queue..
    private method ExecuteQueueAI takes nothing returns nothing
        call .PrioritizeQueueAI()

        if .intCountQueueAI > 0 then
            set .intCurrentAIIndex = .intQueueAIIndex[0]
            call .ai[.intQueueAIIndex[0]].Action()
            call .QueueRemoveAI(0)
        endif
    endmethod

    //Updates the data within a particular AIGroup.
    //If the last AI to be executed is accomplished, set data accordingly.
    //If there are AIs queued after it, execute them.
    private method Update takes nothing returns nothing
        if GetUnitState(.uSubject, UNIT_STATE_LIFE) > 0.00 then
            if .intCurrentAIIndex != -1 and OrderId2String(GetUnitCurrentOrder(.uSubject)) != .strOrder[.intCurrentAIIndex] then
                set .intCurrentAIIndex = -1
                if .intCountQueueAI > 0 then
                    call .ExecuteQueueAI()
                endif
            endif
        else
            call .Destroy()
        endif
    endmethod

    //--------------------INSTANCE FUNCTIONS--------------------//
    //Only these functions are non-private. They're the only ones to be 'touched'/used. Other functions
    //are integral to the system/struct and are static/constant.
    private integer intAIGIndex
    private unit uSubject
    private integer intCountAI
    
    private AI array ai[100]
    private trigger array trigEvent[100]
    private string array strOrder[100]

    private integer intCountQueueAI
    private integer array intQueueAIIndex[100]
    private integer intCurrentAIIndex

    //Constructor for AIGroups.
    static method Create takes unit subject returns AIGroup
        local AIGroup New = AIGroup.allocate()

        set New.intAIGIndex = AIGroup.intCountAIG
        set New.uSubject = subject
        set New.intCountAI = 0
        set New.intCurrentAIIndex = -1
        set New.intCountQueueAI = 0

        set gAIG[AIGroup.intCountAIG] = New

        set AIGroup.intCountAIG = AIGroup.intCountAIG + 1
        call AIGroup.SetTimer()

        return New
    endmethod

    //Used to add an AI to a particular AIGroup
    method AddAI takes AI newAI, trigger newTrig, string order returns nothing
        call TriggerAddCondition(newTrig, Condition(function AIGroup.AICondition))
        call TriggerAddAction(newTrig, function AIGroup.AIAction)

        set .ai[.intCountAI] = newAI
        set .trigEvent[.intCountAI] = newTrig
        set .strOrder[.intCountAI] = order

        set .intCountAI = .intCountAI + 1
    endmethod

    //Destroys a particular AIGroup.
    method Destroy takes nothing returns nothing
        set .uSubject = null
        loop
            exitwhen .intCountAI == 0
            set .intCountAI = .intCountAI - 1
            call .ai[.intCountAI].Destroy()
            call DestroyTrigger(.trigEvent[.intCountAI])
            set .trigEvent[.intCountAI] = null
            set .strOrder[.intCountAI] = null
        endloop

        set gAIG[AIGroup.intCountAIG].intAIGIndex = .intAIGIndex
        set gAIG[.intAIGIndex] = gAIG[AIGroup.intCountAIG]
        set gAIG[AIGroup.intCountAIG] = 0
        set AIGroup.intCountAIG = AIGroup.intCountAIG - 1
        call AIGroup.SetTimer()
    endmethod

endstruct

endlibrary
Attached Files
File type: w3xTEST.w3x (26.0 KB)