• 🏆 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!

[Script] UAS - Unit Aggro System

Level 3
Joined
Apr 24, 2008
Messages
53
UAS - Unit Aggro System 1.9

This is Unit Aggro Script, it takes over the normal computer AI and delegates a units target based on the current threat the units attacking it have.

Requires:
PUI | Perfect Unit Indexing - Wc3campaigns
GroupUtils | GroupUtils - Wc3campaigns

UPDATE: Version 1.9 now up.

Test Map: View attachment Aggrosystem1.9.w3x


[jass="Unit Setup"]
function SetupUnits takes nothing returns nothing
local group G = NewGroup()
local unit U

//Grab all units on the map and enter them into the system.
call GroupEnumUnitsInRect(G,GetPlayableMapRect(),null)

loop
set U = FirstOfGroup(G)
exitwhen U == null
call SetupAggro(U)
//Checking if the unit type is the sorceress and if it is, setting her attack code to the spell Lightning, which will make her cast
//lightning whenever it's ready on the highest threat unit.
if GetUnitTypeId(U) == 'hsor' then
set UnitAggro[GetUnitIndex(U)].Order = "chainlightning"
endif
call GroupRemoveUnit(G,U)
endloop
call ReleaseGroup(G)
set U = null
endfunction
[/code]

[jass="Applying Aggro"]
function Trig_Attack_Actions takes nothing returns nothing
local unit Attacker = GetAttacker()
local unit Attacked = GetTriggerUnit()

//When a unit attacks it applys aggro based off it's unit point value, you can set it up however you want.
call ApplyAggro(Attacked, Attacker,GetUnitPointValue(Attacker),false)
set Attacker = null
set Attacked = null
endfunction
[/code]

[jass="Aggro based spells"]
function Trig_Cast_Actions takes nothing returns nothing
local unit Attacker = GetTriggerUnit()
local unit Attacked
local integer SpellCast = GetSpellAbilityId()
local group G
local unit U


//AOE Taunt spell that applies aggro to surrounding units in a 400 AOE
if SpellCast == 'A000' then
set G = NewGroup()
call GroupEnumUnitsInRange(G,GetUnitX(Attacker),GetUnitY(Attacker),450,null)
loop
set U = FirstOfGroup(G)
exitwhen U == null
if IsPlayerEnemy(GetOwningPlayer(Attacker),GetOwningPlayer(U)) == true and GetUnitState(U,UNIT_STATE_LIFE) > 0 then
call ApplyAggro(U,Attacker,300,false)
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl",GetUnitX(U),GetUnitY(U)))
endif
call GroupRemoveUnit(G,U)
endloop
endif

//Single Target spell that removes 200 aggro from the target unit.
if SpellCast == 'A001' then
set Attacked = GetSpellTargetUnit()
call RemoveAggro(Attacked,Attacker,200,false)
endif
set Attacker = null
set Attacked = null
call ReleaseGroup(G)
set U = null
endfunction
[/code]



JASS:
library UAS initializer SystemUpdate requires PUI, GroupUtils
//================================================================================================================
//                                   UAS - Unit Aggro System 1.9
//                             Created by Dustin "TEC_Ghost" Hendrickson
//
// 1.) WHAT DOES IT DO?
//     This is a system designed for realtime aggro management, giving your computer players a
//     system in which they target units.
//
// 2.) WHAT DOES IT REQUIRE?
//     vJass
//     PUI
//     GroupUtils
//     A basic understanding of jass.
//
// 3.) HOW DO I USE IT?
//     Just copy and paste the library into a custom text trigger.
//     The system automatically updates aggro and sorting.
//     All you have to do is setup the unit to put it in the system.
//
//     There are also a few functions you'll need to use...
//
//      SetupAggro(Unit)
//           - You'll need to run this for every unit you want in the system.
//
//      RemoveAggroUnit(Unit)
//           - Removes the unit from the system.
//
//      ApplyAggro(UnitA,UnitB,Ammount,Boolean)
//           - This function applys aggro to UnitA from UnitB, the Ammount is how much aggro to apply,
//             how much aggro to apply, True if you want it to apply to non computers, False if you want
//             to apply to only computers.
//                                     
//      RemoveAggro(UnitA,UnitB,Ammount,Boolean)
//           - Same as the ApplyAggro() function, but removes instead of adds.
//
//      SetUnitOrder(Unit,String,String)
//           - Will control the AI's commands when the system cycles, you can for instance change 
//             a unit to cast a spell instead of attack SetUnitOrder(Unit1,"polymorph","unit") which will
//             cast polymorph on the highest aggro unit. Or you can do an AOE spell like
//             SetUnitOrder(Unit1,"flamestrike","location") will cast flamestrike on the are of the 
//             highest aggro unit.
//
//      ClearAggro(UnitA,UnitB)    
//           - Does the same as RecycleAggro() but is for manual purposes incase you want to remove all
//             of a units aggro from a certain other unit. Clears UnitB's aggro from UnitA.
//
//                                     
//---------------------------------------------------------------------------------------
//    Some other variables that can be used for displaying information are as follows...
//---------------------------------------------------------------------------------------
//
//      GetAggroSlotUnit(Unit,Integer)returns Unit  
//          - The array for the sorted aggro list "integer" 1 would be the highest aggro, 2 would be lower
//            3 would be lower yet. "Unit" is the the unit you're pulling the array for. Returns
//            a unit.
// 
//      GetAggroSlotAmmount(Unit,Integer)returns Integer
//          - Same as the above, but returns the aggro ammount for the particular slot.
//                                                                   
//      TotalUnits 
//          - Returns an integer for the total number of units in the system.
//
//
//
//================================================================================================================
//                 SCRIPT IS BELOW - DO NOT MODIFY ANY OF THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
//================================================================================================================

globals
// CONFIGURABLES ( You can edit these ) ===========================================================================================
private constant real INTERVAL = .30           // Interval for the system to update Recyciling and aggro sorting.
private constant integer MAXAGGROLIST = 20     // The max number of units that can be in a units aggro table, keep this number low.
private string DefaultCode = "attack"          // The default order code for units on setup. Can be changed with SetUnitOrder()
private string DefaultType = "unit"            // The type of order being issued, much be either "unit" or "location".
//=================================================================================================================================
UnitAggroArray array UnitAggro                 // Struct array for the system.
private timer Update = CreateTimer()           // SystemUpdate timer.
private group SystemUnits                      // Group of all the units in the system.
integer TotalUnits = 0                         // Total units in system.
endglobals

struct UnitAggroArray
    unit Unit                                                    // The unit.
    unit Target                                                  // The current target.
    string Order                                                 // The order string the AI uses when aggroed. ("attack","polymorph". etc)
    string Type                                                  // The order type of the AI order. ("unit" or "location")
    unit array unit_AggroTable[MAXAGGROLIST]                     // Unit Refrence.
    integer array ammount_AggroTable[MAXAGGROLIST]               // Aggro Ammount.
    unit array sortedunits_AggroTable[MAXAGGROLIST]              // The array for sorted units.
    integer array sortedaggroammount_AggroTable[MAXAGGROLIST]    // The array for aggro totals of sorted slots.
    group group_AggroTable                                       // Group of all units aggroed.
    boolean FlaggedForRemoval                                    // Boolean set to remove unit from system.
    
    method onDestroy takes nothing returns nothing
    local integer i = 0
        set this.Unit = null
        set this.Target = null
        set this.Order = "attack"
        set this.Type = "unit"
        set this.FlaggedForRemoval = false
        
        loop
        exitwhen i > MAXAGGROLIST
            set this.unit_AggroTable[i] = null
            set this.ammount_AggroTable[i] = 0
            set this.sortedunits_AggroTable[i] = null
            set this.sortedaggroammount_AggroTable[i] = 0
            set i = i + 1
        endloop
        
        call ReleaseGroup(this.group_AggroTable)
    endmethod
endstruct

//============================================
//Setup Struct for unit. ---> SetupAggro(Unit)
//============================================
function SetupAggro takes unit U returns nothing
    local integer Index = GetUnitIndex(U)
    set UnitAggro[Index] = UnitAggroArray.create()
    set UnitAggro[Index].Unit = U
    set UnitAggro[Index].Target = null
    set UnitAggro[Index].Order = DefaultCode
    set UnitAggro[Index].Type = DefaultType
    set UnitAggro[Index].group_AggroTable = NewGroup()
    call GroupAddUnit(SystemUnits,U)
endfunction

//====================================================================
//Set AI unit order and type. ---> IssueOrder(Unit,String,Unit,String)
//====================================================================
function SetUnitOrder takes unit U, string Code, string Type returns nothing
    local integer Index = GetUnitUserData(U)
    set UnitAggro[Index].Order = Code
    set UnitAggro[Index].Type = Type
endfunction

//===========================================================
//Issue order to AI. ---> IssueOrder(Unit,String,Unit,String)
//===========================================================
function IssueOrder takes unit U, string Code, unit U2, string Type returns nothing
    local location L = GetUnitLoc(U2)

    if Type == "unit" then
        call IssueTargetOrder(U,Code,U2)
    endif
    if Type == "location" then
        call IssuePointOrderLoc(U,Code,L)
    endif
    
    call RemoveLocation(L)
    set L = null
endfunction


//=================================================================================================
//Gets the unit in a certain slot from a units aggro table. ---> GetAggroSlotUnit(Unit,Integer)Unit
//=================================================================================================
function GetAggroSlotUnit takes unit U, integer i returns unit
    local integer Index = GetUnitUserData(U)
    return UnitAggro[Index].sortedunits_AggroTable[i]
endfunction

//====================================================================================================================
//Gets the ammount of aggro in a certain slot from a unit's aggro table. ---> GetAggroSlotAmmount(Unit,Integer)Integer
//====================================================================================================================
function GetAggroSlotAmmount takes unit U, integer i returns integer
    local integer Index = GetUnitUserData(U)
    return UnitAggro[Index].sortedaggroammount_AggroTable[i]
endfunction

//==================================================================================================
//Checks the precise slot of UnitB in UnitA's aggro table. ---> GetUnitAggroSlot(UnitA,UnitB)Integer
//==================================================================================================
function GetUnitAggroSlot takes unit Ua, unit Ub returns integer
    local integer Index = GetUnitUserData(Ua)
    local integer l = 1
    local integer Total = CountUnitsInGroup(UnitAggro[Index].group_AggroTable)

    loop
    exitwhen l > Total
    if UnitAggro[Index].sortedunits_AggroTable[l] == Ub then
        return l
    endif

    set l = l + 1
    endloop

    return 0
endfunction

//===================================================
//Clear UnitB aggro from UnitA. ---> ClearAggro(Unit)
//===================================================
function ClearAggro takes unit Ua, unit Ub returns nothing
    local integer IndexA = GetUnitUserData(Ua)
    local integer IndexB = GetUnitUserData(Ub)
    local integer Index = 0

    set Index = GetUnitAggroSlot(Ua,Ub)
    set UnitAggro[IndexA].unit_AggroTable[IndexB] = null
    set UnitAggro[IndexA].ammount_AggroTable[IndexB] = 0
    set UnitAggro[IndexA].sortedunits_AggroTable[Index] = null
    set UnitAggro[IndexA].sortedaggroammount_AggroTable[Index] = 0
    
    call GroupRemoveUnit(UnitAggro[IndexA].group_AggroTable,Ub)
endfunction


//=================================================================================
//Checks if any units are dead and removes them from the group. ---> RecycleAggro()
//=================================================================================
function RecycleAggro takes nothing returns nothing
    local group TempGroup = NewGroup()
    local group Group = NewGroup()
    local unit Unit = null
    local unit Unit2 = null
    local integer Index = 0
    
    call GroupAddGroup(SystemUnits,TempGroup)

    loop
        set Unit = FirstOfGroup(TempGroup)
    exitwhen Unit == null
        set Index = GetUnitUserData(Unit)
        call GroupAddGroup(UnitAggro[Index].group_AggroTable,Group)
    loop
        set Unit2 = FirstOfGroup(Group)
    exitwhen Unit2 == null
    if GetUnitState(Unit2, UNIT_STATE_LIFE) <= 0 or UnitAggro[GetUnitUserData(Unit2)].FlaggedForRemoval == true then
        set Index = GetUnitUserData(Unit)
        call ClearAggro(Unit,Unit2)
    endif
        call GroupRemoveUnit(Group,Unit2)
    endloop
        call GroupRemoveUnit(TempGroup,Unit)
    endloop

    
    
    call GroupAddGroup(SystemUnits,TempGroup)
    
    
    
    loop
        set Unit = FirstOfGroup(TempGroup)
    exitwhen Unit == null
        if GetUnitState(Unit, UNIT_STATE_LIFE) <= 0 or UnitAggro[GetUnitUserData(Unit)].FlaggedForRemoval == true then
            set Index = GetUnitUserData(Unit)
            call GroupRemoveUnit(SystemUnits,Unit)
            call UnitAggro[Index].destroy()
        endif
    call GroupRemoveUnit(TempGroup,Unit)
    endloop

    call ReleaseGroup(TempGroup)
    call ReleaseGroup(Group)
endfunction

//==============================================
//Remove unit from system. ---> RemoveUnit(Unit)
//==============================================
function RemoveAggroUnit takes unit U returns nothing
    local integer Index = GetUnitIndex(U)

    set UnitAggro[Index].FlaggedForRemoval = true
endfunction

//=============================================================
//Sorts aggro into table based on ammount. ---> SortAggro(Unit)
//=============================================================
function SortAggro takes unit U returns nothing
    local integer Index = GetUnitUserData(U)
    local integer Rows = CountUnitsInGroup(UnitAggro[Index].group_AggroTable)
    local unit Tempunit = null
    local group Tempgroup = NewGroup()
    local group Tempgroup2 = NewGroup()
    local integer l = 1

    call GroupAddGroup(UnitAggro[Index].group_AggroTable,Tempgroup)
    loop
    exitwhen l > Rows
    set UnitAggro[Index].sortedaggroammount_AggroTable[l] = 0
    loop
        set Tempunit = FirstOfGroup(Tempgroup) 
    exitwhen Tempunit == null
    if UnitAggro[Index].ammount_AggroTable[GetUnitUserData(Tempunit)] > UnitAggro[Index].sortedaggroammount_AggroTable[l] then
        set UnitAggro[Index].sortedunits_AggroTable[l] = Tempunit
        set UnitAggro[Index].sortedaggroammount_AggroTable[l] = UnitAggro[Index].ammount_AggroTable[GetUnitUserData(Tempunit)]
    endif
        call GroupAddUnit(Tempgroup2,Tempunit)
        call GroupRemoveUnit(Tempgroup,Tempunit)
    endloop

    call GroupRemoveUnit(Tempgroup2,UnitAggro[Index].sortedunits_AggroTable[l])
    call GroupAddGroup(Tempgroup2,Tempgroup)
    call GroupClear(Tempgroup2)
  
    set l=l+1
    endloop
    call ReleaseGroup(Tempgroup)
    call ReleaseGroup(Tempgroup2)
endfunction



//=============================================================================================================================
//Apply aggro to Unit A from Unit B. Allow user controlled players to be affected. ---> ApplyAggro(UnitA,UnitB,Ammount,Boolean)
//=============================================================================================================================
function ApplyAggro takes unit Ua, unit Ub, integer Ammount, boolean AllowUser returns nothing
    local integer IndexA = GetUnitIndex(Ua)
    local integer IndexB = GetUnitIndex(Ub)
    local boolean NewAdd = false

    if AllowUser == false then
    if GetPlayerController(GetOwningPlayer(Ua)) == MAP_CONTROL_USER then
        return
    endif
    endif

    if IsUnitInGroup(Ub,UnitAggro[IndexA].group_AggroTable) == true then
        set UnitAggro[IndexA].ammount_AggroTable[IndexB] = UnitAggro[IndexA].ammount_AggroTable[IndexB] + Ammount
    else
        call GroupAddUnit(UnitAggro[IndexA].group_AggroTable,Ub)
        set UnitAggro[IndexA].unit_AggroTable[IndexB] = Ub
        set UnitAggro[IndexA].ammount_AggroTable[IndexB] = UnitAggro[IndexA].ammount_AggroTable[IndexB] + Ammount
    endif
endfunction

//=========================================================================
//Remove aggro on Unit A from Unit B. ---> RemoveAggro(UnitA,UnitB,Ammount,Boolean)
//=========================================================================
function RemoveAggro takes unit Ua, unit Ub, integer Ammount, boolean AllowUser returns nothing
    local integer IndexA = GetUnitIndex(Ua)
    local integer IndexB = GetUnitIndex(Ub)

    if AllowUser == false then
    if GetPlayerController(GetOwningPlayer(Ua)) == MAP_CONTROL_USER then
        return
    endif
    endif

    if IsUnitInGroup(Ub,UnitAggro[IndexA].group_AggroTable) == true then
        set UnitAggro[IndexA].ammount_AggroTable[IndexB] = UnitAggro[IndexA].ammount_AggroTable[IndexB] - Ammount
    else
        call GroupAddUnit(UnitAggro[IndexA].group_AggroTable,Ub)
        set UnitAggro[IndexA].unit_AggroTable[IndexB] = Ub
        set UnitAggro[IndexA].ammount_AggroTable[IndexB] = UnitAggro[IndexA].ammount_AggroTable[IndexB] - Ammount
    endif

    if UnitAggro[IndexA].ammount_AggroTable[IndexB] < 0 then
        set UnitAggro[IndexA].ammount_AggroTable[IndexB] = 0
    endif
endfunction



//=======================================================
//Updates the aggro table with target. ---> UpdateAggro()
//=======================================================
function UpdateAggro takes nothing returns nothing
    local group TempGroup = NewGroup()
    local unit Unit = null
    local integer Index = 0
    
    call GroupAddGroup(SystemUnits,TempGroup)
    
    loop
    set Unit = FirstOfGroup(TempGroup)
    exitwhen Unit == null
        set Index = GetUnitUserData(Unit)
        if GetUnitState(Unit, UNIT_STATE_LIFE) > 0 and UnitAggro[GetUnitUserData(Unit)].FlaggedForRemoval == false then
            call SortAggro(UnitAggro[Index].Unit)
            set UnitAggro[Index].Target = UnitAggro[Index].sortedunits_AggroTable[1]
        if IsUnitInRange(UnitAggro[Index].Target,UnitAggro[Index].Unit,500) == true then
            call IssueOrder(UnitAggro[Index].Unit,UnitAggro[Index].Order,UnitAggro[Index].Target,UnitAggro[Index].Type)
        else
            call IssueTargetOrder(UnitAggro[Index].Unit,"move",UnitAggro[Index].Target)
        endif
        endif
    call GroupRemoveUnit(TempGroup,Unit)
    endloop

    call ReleaseGroup(TempGroup)
    set TotalUnits = CountUnitsInGroup(SystemUnits)
endfunction

//==============
//System Update.
//==============
private function UpdateCycle takes nothing returns nothing
    call UpdateAggro()
    call RecycleAggro()
endfunction

function SystemUpdate takes nothing returns nothing
    set SystemUnits = NewGroup()
    call TimerStart(Update,INTERVAL,true,function UpdateCycle)
endfunction

endlibrary
 

Attachments

  • UAS.jpg
    UAS.jpg
    20.4 KB · Views: 139
Last edited:
Top