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
Last edited: