| 10-03-2008, 07:55 AM | #1 |
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. 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 |
Test map please... |
| 10-19-2008, 12:19 PM | #3 |
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: 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 |
