library SummonedEscort /* v1.5
***************************************************************************
* by mckill2009
***************************************************************************
*
* */ uses /*
* */ GetClosestWidget /* www.hiveworkshop.com/forums/jass-resources-412/snippet-getclosestwidget-204217
* */ Table /* www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084
*
***************************************************************************
*
* Features:
* - Allows your summoned units to follow and guards the summoner.
* - If the summoner dies, the summoned unit searches for a new ally or
* returns to it's original location.
* - Used also to focus attack an enemy hero or unit.
*
***************************************************************************
*
* Installation:
* Copy and paste the required libraries and this code to your map
*
***************************************************************************
*
* API:
* static method summoned takes unit summoningUnit, unit summonedUnit returns nothing
* - call SE.summoned(GetSummoningUnit(),GetTriggerUnit())
* - If AUTO is true, it will do automatically for you.
* - AUTO only works for summoned unit
*
* static method remove takes unit escortUnit returns nothing
* - Removes the escort from the system
*
***************************************************************************/
globals
/**************************************************************************
* Auto registers ALL summoned units in map, it is recommended to set this
* to false if you want a unit to escort an ally or attack an enemy hero/unit
***************************************************************************/
private constant boolean AUTO = true
/**************************************************************************
* Searches for closest ally hero if main hero is dead, if FOLLOW_ONLY_HEROES
* is false, the escort will follow a closest ally
***************************************************************************/
private constant boolean ALLY_IN_RANGE = true
/**************************************************************************
* The unit only follows heroes when the main unit dies
***************************************************************************/
private constant boolean FOLLOW_ONLY_HEROES = true
/**************************************************************************
* This is the offset distance from the unit to his master
***************************************************************************/
private constant real OFFSET = 200
/**************************************************************************
* Searches for closest ally if main ally unit is dead
* ALLY_IN_RANGE must be true
***************************************************************************/
private constant real CLOSEST_ALLY = 600
/**************************************************************************
* Targets/attacks closest enemy in range of master
***************************************************************************/
private constant real CLOSEST_ENEMY = 400
endglobals
struct SE
private unit master
private unit sum
private real xUnit
private real yUnit
private static integer DATA
private static constant integer ATTACK = 851983
private static timer t = CreateTimer()
private static integer instance = 0
private static integer array insAR
private static unit TempUnit = null
private static Table tb
private static method UnitAlive takes unit u returns boolean
return not IsUnitType(u,UNIT_TYPE_DEAD) and u!=null
endmethod
private static method closestEnemy takes nothing returns boolean
local thistype this = DATA
set TempUnit = GetFilterUnit()
return UnitAlive(TempUnit) and IsUnitEnemy(TempUnit, GetOwningPlayer(.sum))
endmethod
private static method closestAlly takes nothing returns boolean
local thistype this = DATA
set TempUnit = GetFilterUnit()
if UnitAlive(TempUnit) and GetOwningPlayer(TempUnit)==GetOwningPlayer(.sum) and TempUnit!=.sum /*
*/ and not IsUnitType(TempUnit,UNIT_TYPE_STRUCTURE) and GetUnitMoveSpeed(TempUnit)>0 then
static if FOLLOW_ONLY_HEROES then
return IsUnitType(TempUnit, UNIT_TYPE_HERO)
endif
return true
endif
return false
endmethod
private method destroy takes nothing returns nothing
set .master = null
set .sum = null
call .deallocate()
endmethod
private static method looper takes nothing returns nothing
local thistype this
local unit target
local integer orderSum
local integer index = 0
local real angle
local real xMaster
local real yMaster
local real xSummoned
local real ySummoned
loop
set index = index+1
set this = insAR[index]
if UnitAlive(.sum) and tb.has(GetHandleId(.sum)) then
set angle = GetRandomReal(0,6.28)
set orderSum = GetUnitCurrentOrder(.sum)
if UnitAlive(.master) then
if orderSum==0 then
set xMaster = GetUnitX(.master)+OFFSET*Cos(angle)
set yMaster = GetUnitY(.master)+OFFSET*Sin(angle)
call IssuePointOrderById(.sum,ATTACK,xMaster,yMaster)
set DATA = this
set target = GetClosestUnitInRange(xMaster,yMaster,CLOSEST_ENEMY,Filter(function thistype.closestEnemy))
if target!=null then
if IsUnitType(target,UNIT_TYPE_SLEEPING) then
call IssueTargetOrderById(.sum,ATTACK,target)
else
call IssuePointOrderById(.sum,ATTACK,GetUnitX(target),GetUnitY(target))
endif
set target = null
endif
endif
else
set DATA = this
set xSummoned = GetUnitX(.sum)
set ySummoned = GetUnitY(.sum)
static if ALLY_IN_RANGE then
set .master = GetClosestUnitInRange(xSummoned,ySummoned,CLOSEST_ALLY,Filter(function thistype.closestAlly))
else
set .master = GetClosestUnit(xSummoned,ySummoned,Filter(function thistype.closestAlly))
endif
if .master==null and orderSum==0 then
call IssuePointOrderById(.sum,ATTACK,.xUnit+OFFSET*Cos(angle),.yUnit+OFFSET*Sin(angle))
endif
endif
else
call .destroy()
set insAR[index] = insAR[instance]
set insAR[instance] = this
set index = index - 1
set instance = instance - 1
if instance==0 then
call PauseTimer(t)
endif
endif
exitwhen index==instance
endloop
endmethod
private static method create takes unit summoningUnit, unit summonedUnit returns thistype
local thistype this
if instance==8190 then
call BJDebugMsg("Library_SummonedEscort ERROR: Too many instances!")
else
set this = allocate()
set .master = summoningUnit
set .sum = summonedUnit
set .xUnit = GetUnitX(summonedUnit)
set .yUnit = GetUnitY(summonedUnit)
set tb[GetHandleId(.sum)] = 0
if instance==0 then
call TimerStart(t,1.0,true,function thistype.looper)
endif
set instance = instance + 1
set insAR[instance] = this
call RemoveGuardPosition(summonedUnit)
endif
return this
endmethod
private static method fire takes nothing returns boolean
call thistype.create(GetSummoningUnit(),GetTriggerUnit())
return false
endmethod
private static method onInit takes nothing returns nothing
static if AUTO then
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SUMMON)
call TriggerAddCondition(t,function thistype.fire)
set t = null
endif
set tb = Table.create()
endmethod
//API:======================================
static method summoned takes unit summoningUnit, unit summonedUnit returns nothing
call thistype.create(summoningUnit,summonedUnit)
endmethod
static method remove takes unit escortUnit returns nothing
call tb.remove(GetHandleId(escortUnit))
endmethod
endstruct
endlibrary