library PullPushSystem
//------------------------------------------------------//
// Default values(change if you want) //
//------------------------------------------------------//
globals
constant real PULL_PERIOD = 0.03125 // time period - dose the units positioning 32 times in a sec
constant real PULL_SPEED_DEFAULT = 320. // pulled units movespeed
constant real PULL_HIT_RANGE = 100. // default range of hitting
constant real PULL_SLOWSTART = 1. // default time needs to pass before it starts changing the units movespeed
constant real PULL_SLOWING = 0.625 // 0.625 / 0,03125 = 20 movespeed decrease per second->
// -> for x movespeed increase you need to set this to x*0.03125->
// ->setting this to a negative amount will occure ms increasing
constant real PULL_TOSS_STARTVELOCITY = 2000. // default value of how fast the pulled unit will be tossed up
constant integer PULL_TOSSNUMBER = 1 // default value of how many times the pulled unit will be tossed after eachother
endglobals
//----------------DO NOT CHANGE THESE METHODS RIGHT HERE BUT IN YOUR OWN SCRIPTS------------
//----------------------------LOOK FOR THE DEMO SPELLS HOW TO DO THAT-----------------------
module Events
//this will fire only once when the unit is prepared to be pulled
//( SET HERE THE VALUES SEEN UNDER )-----------------------------------------------------
stub method onPullStart takes nothing returns nothing //
endmethod //
//
//this event will be called in every time period //
//( YOU ARE FREE TO CHANGE VALUES HERE WHILE THE UNIT MOVES ) //
stub method onAction takes unit target returns nothing //
endmethod //
//
//this event will be called when the moving unit touches a destructable //
// isDestHit must be enabled (set true) to use this //
stub method onDestHit takes destructable d returns nothing //
endmethod //
//
//this event will be called when the moving unit touches a unit //
// isUnitHit must be enabled (set true) to use this //
stub method onUnitHit takes unit u returns nothing //
endmethod //
//
//if your moving unit has a UNIT as Destination //
//this event will be called when the Target unit touches the Unit //
stub method onAtDestUnit takes unit DestUnit ,unit Target returns nothing //
endmethod //
//
//this event will be called when the Targets movespeed becomes <=0 (stops:P) //
stub method onStop takes unit Target returns nothing //
endmethod //
//
//if your moving unit has a POINT(x,y coordinates not location) as Destination //
//this event will be called when the Target unit reaches the point //
stub method onAtDestPoint takes unit Target returns nothing //
endmethod //
//
//this event will be called if the pulled unit was tossed when it reaches the ground //
stub method onLanding takes unit Target returns nothing //
endmethod //
//
//this event will be called when the pulled unit bounces off something //
//.isBouncing have to be ture //
stub method onBounce takes unit Target returns nothing //
endmethod //
//
endmodule //------------------------------------------------------//
// values that should be set when "onPullStart" fires //
//------------------------------------------------------//------------------------------------------------------//
// these are just the defaults,set them in your trigger //
// With onPullStart adn/or onAction //
struct Pulling
implement Events
//-----Generic stuff-------------------------------------------------
public real pullSpeed = PULL_SPEED_DEFAULT // pulled units movespeed
public boolean pausing = true // set this false to pull units without pausing theme
public boolean isPulled = true // set it true to pull, false to push the unit
public boolean disablePathing = true // set false to NOT disable pathing for unit
//-----Hitting and pathing-------------------------------------------
public boolean isCheckPathing = true // set this false to don't check pathing ( walkability )
public boolean hitDests = true // set this false to don't hit destructables
public boolean hitUnits = true // set this false to don't hit units
public real hitRange = PULL_HIT_RANGE // range between units counts as a hit
//-----Slowing (Speeding)--------------------------------------------
public boolean isSlowing = false // set true to turn slowing option on
public real slowStart = PULL_SLOWSTART // needed time to pass before start the slowing
public real slowAmount = PULL_SLOWING // for x movespeed decrease you need to set this to x*0.03125
// negative value will couse movespeed increase
//-----Area of Effect------------------------------------------------
public boolean hasAOE = false // set true to get your spell an area of effect
public real aoeRadius = 200. // the radius of the spells AoE
//-----Units---------------------------------------------------------
readonly unit destUnit = null // if the destination is a unit this variable stores it
readonly unit victim = null // target unit , moved unit
//-----Offset--------------------------------------------------------
readonly real tossOrdinalNumber = 0. // contains the ordinal number of the actual toss
public real tossStartVelocity = PULL_TOSS_STARTVELOCITY // base value of how fast the pulled unit will be tossed at the first time when the pull starts
public boolean isTossed = false // set this true to enable tossing
public boolean isJump = false // if isJump is true system calculates variables for a big leap insted of the normal toss effect
public integer tossNumber = PULL_TOSSNUMBER // sets the amount how much times the pulled unit will be tossed after eachother
//-----Bouncing------------------------------------------------------
public boolean isBouncing = false // turn this true if you want your target bouncing off cliffs or map boarder
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//------------------------------------------------------//
// Values used by the system . SHOULDN'T BE CHANGED //
//------------------------------------------------------//
private static boolean refreshedPathings = false //prevents enabling pathing for pulled units that have pathing disabled if more then one pullorpush-spells were casted at the same time
private boolean isPaused = false //turns to true when the unit is paused
private real pulledX = 0. //pulled units x coord
private real pulledY = 0. //pulled units y coord
private real pulledZ = 0. //pulled units z coord
private real pulltoX = 0. //destinations x coord
private real pulltoY = 0. //destinations y coord
private real calcX = 0. //periodic pulling vectors x coord
private real calcY = 0. //prriodic pulling vectors y coord
private real calcZ = 0. //prriodic pulling vectors z coord
private integer slowTick = 0 //period counter for slowstart
private boolean hasUnitDest = false //turns true if the targeted destination is a unit
private static timer t = null //period timer
private static integer count = 0 //instance counter
private integer tossCount = 0 //Tosses made already
private real tossVelocity = 0 //Tosses made already
private real gravity = 150
private boolean active = false //turns true when the pulling begins
private static thistype curPull = 0 //equals to the current running instance
private boolean isPulReg = false //turns true when instance registered to the struct array
private static rect rec = null //hitting detection rect
private boolean notPathed = false //turns true when units pathing turned off
private real pullAngle = 0. //stores the pulling Angle in radians
private integer dummySpellLvl = 0
private thistype next
private thistype prev
private static thistype first
private static thistype last
private static thistype thiss
private static unit dummy
//-------------------------------------------------------------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////////////
// //
// SYSTEM METHODS. DON'T EDIT ANYTHING UNDER WITHOUT PERMISSION //
// //
///////////////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------
//---------PULLED AND (IF NEEDED)DESTINATION UNIT SAVING--------------
//-------------------CAN AND *SHOULD* BE USED FREELY------------------
public method SetDestUnit takes unit u returns nothing
set .destUnit = u
set .hasUnitDest = true
endmethod
public method SetPullUnit takes unit u returns nothing
if IsUnitPaused(.victim) then
call PauseUnit(.victim,false)
call PauseUnit(u,true)
endif
if .disablePathing then
call SetUnitPathing(.victim,true)
call SetUnitPathing(u,false)
endif
set .victim = u
set .pulledX = GetUnitX(u)
set .pulledY = GetUnitY(u)
//! runtextmacro SetCollisionMacro()
endmethod
//---- STOP METHOD ----------------------------------------
public method SuddenStop takes nothing returns nothing
set .active = false
if .isTossed then
set .isTossed = false
call SetUnitFlyHeight(.victim,0.,1000)
endif
if .notPathed then
call SetUnitPathing(.victim,true)
set thistype.refreshedPathings = false
endif
if .isPaused then
call PauseUnit(.victim,false)
endif
if GetWidgetLife(.victim) > 0.405 then
call .onStop(.victim)
endif
endmethod
//--------------------------------------------------------------------
//---------calculating periodic x and y movement----------------------
//--------------------------------------------------------------------
private method calcXY takes nothing returns nothing
local real velocity = .pullSpeed * PULL_PERIOD
set .calcX = velocity * Cos(.pullAngle)
set .calcY = velocity * Sin(.pullAngle)
endmethod
//--------------------------------------------------------------------
//---------TOSS CALCULATIONS------------------------------------------
//--------------------------------------------------------------------
private method calculateZ takes nothing returns nothing
if .tossCount < .tossNumber then
set .tossVelocity = .tossVelocity - .gravity
if GetWidgetLife(.victim) > 0.405 then
set .calcZ = .calcZ + (.tossVelocity * PULL_PERIOD)
else
set .calcZ = 0.
call SetUnitFlyHeight(.victim,0.,100000)
endif
else
set .calcZ = 0.
set .isTossed = false
endif
if .calcZ < 0 then
set .calcZ = 0.
endif
endmethod
private method setUpJumpProperties takes nothing returns nothing
local real s = SquareRoot((.pulltoX-.pulledX)*(.pulltoX-.pulledX)+(.pulltoY-.pulledY)*(.pulltoY-.pulledY))
local real t
if s <= 800 then
set t = 1. + ((s - 800.)/10000)
else
set t = 1. + ((s - 800.)/5000)
endif
set .isPulled = true
set .hitRange = 0.01
set .isSlowing = false
set .isTossed = true
set .tossNumber = 1
set .isBouncing = false
set .isCheckPathing = false
set .pullSpeed = s/t
set .tossStartVelocity = ((t/PULL_PERIOD)*.gravity)/2
endmethod
//--------------------------------------------------------------------
//---------HIT DETECTIONING , EVENT RELEASING-------------------------
//--------------------------------------------------------------------
//-----dest hit-----
private static method HitDests takes nothing returns boolean
local thistype this = thistype.curPull
if not .isPaused then
call PauseUnit(.victim,true)
endif
if .onDestHit.exists then
call .onDestHit(GetFilterDestructable())
endif
call PauseUnit(.victim,false)
return false
endmethod
//-----unit hit-----
//! textmacro UnitHitMacro takes UNIT, X, Y, RADIUS
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .$X$, .$Y$, .$RADIUS$ + 172, null)
loop
set $UNIT$ = FirstOfGroup(bj_lastCreatedGroup)
exitwhen $UNIT$ == null
call GroupRemoveUnit(bj_lastCreatedGroup, $UNIT$)
if $UNIT$ != .victim then
if IsUnitInRangeXY($UNIT$, .$X$, .$Y$, .$RADIUS$) then
//! endtextmacro
//! textmacro UnitHitMacroEnd
endif
endif
endloop
//! endtextmacro
//-------------------------------------------------------------------
//---------Pathing Location Calculation (is it pathable ahead?)------
//-------------------------------------------------------------------
private method calcModAngleXY takes nothing returns boolean
local real modX = .pulledX + (.dummySpellLvl*16+1) * Cos(.pullAngle)
local real modY = .pulledY + (.dummySpellLvl*16+1) * Sin(.pullAngle)
return not IssuePointOrderById(.dummy,0xD0100,modX,modY)
endmethod
//--------------------------------------------------------------------
//---------UNIT MOVEMENT , POSITIONING--------------------------------
//--------------------------------------------------------------------
private method positioning takes nothing returns nothing
local unit u = null
local real lx
local real ly
local real atan
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----The units death will break the instance----//
if GetUnitState(.victim,UNIT_STATE_LIFE) > 0.405 then
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Setting the units pathing-----------------//
if .disablePathing then
if not thistype.refreshedPathings then
set .notPathed = thistype.refreshedPathings
set thistype.refreshedPathings = true
endif
if not .notPathed then
set .notPathed = true
call SetUnitPathing(.victim,false)
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Pausing the unit for the pulls duration----//
if .pausing then
set .isPaused = IsUnitPaused(.victim)
if not (.isPaused) then
call PauseUnit(.victim,true)
set .isPaused = true
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Refreshing pull Angle----------------------//
//--refreshing the units location--
set .pulledX = GetUnitX(.victim)
set .pulledY = GetUnitY(.victim)
if .hasUnitDest then
if .destUnit != null then
set .pulltoX = GetUnitX(.destUnit)
set .pulltoY = GetUnitY(.destUnit)
if .isPulled then
set .pullAngle = Atan2(.pulltoY-.pulledY,.pulltoX-.pulledX)
else
set .pullAngle = Atan2(.pulledY-.pulltoY,.pulledX-.pulltoX)
endif
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Periodic Rising (Z value)------------------//
if .isTossed then
call .calculateZ()
call SetUnitFlyHeight(.victim,.calcZ,10000)
if .calcZ == 0 then
call .onLanding(.victim)
if .isJump then
call SuddenStop()
endif
set .tossCount = .tossCount+1
set .tossOrdinalNumber = .tossCount
set .tossStartVelocity = .tossStartVelocity*2/3
set .tossVelocity = .tossStartVelocity
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Pathing check------------------------------//
if .isCheckPathing then
//----Save Collision modifiers for victim----
//! textmacro SetCollisionMacro
if not IsUnitInRangeXY(.victim, .pulledX + 17, .pulledY, 0.) then
set .dummySpellLvl = 1
elseif not IsUnitInRangeXY(.victim, .pulledX + 33, .pulledY, 0.) then
set .dummySpellLvl = 2
elseif not IsUnitInRangeXY(.victim, .pulledX + 49, .pulledY, 0.) then
set .dummySpellLvl = 3
else
set .dummySpellLvl = 4
endif
//! endtextmacro
call PauseUnit(.dummy,false)
call SetUnitAbilityLevel(.dummy,'ppds',.dummySpellLvl)
if .calcModAngleXY() then
if not .isPulled and not .hasUnitDest then
set .pullAngle = (360.-(.pullAngle*bj_RADTODEG)) * bj_DEGTORAD
if .calcModAngleXY() then
set .pullAngle = (ModuloReal(180.+(.pullAngle*bj_RADTODEG),360.)) * bj_DEGTORAD
if .calcModAngleXY() then
set .pullAngle = (360.-(.pullAngle*bj_RADTODEG)) * bj_DEGTORAD
if .isBouncing then
call .onBounce(.victim)
else
call .SuddenStop()
endif
else
if .isBouncing then
call .onBounce(.victim)
else
call .SuddenStop()
endif
endif
else
if .isBouncing then
call .onBounce(.victim)
else
call .SuddenStop()
endif
endif
else
call .SuddenStop()
endif
endif
call PauseUnit(.dummy,true)
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----X and Y vector calc for a period-----------//
call .calcXY()
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Placing the unit to the next point---------//
if .active then
set .pulledX = .pulledX+.calcX
set .pulledY = .pulledY+.calcY
call SetUnitPosition(.victim,.pulledX,.pulledY)
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Hit detection for units and destructables--//
if .hitDests then
call SetRect(thistype.rec, .pulledX-.hitRange , .pulledY-.hitRange , .pulledX+.hitRange , .pulledY+.hitRange)
call EnumDestructablesInRect(thistype.rec, Condition(function thistype.HitDests), null)
endif
if .hitUnits then
//! runtextmacro UnitHitMacro("u","pulledX","pulledY","hitRange")
if .onUnitHit.exists then
call .onUnitHit(u)
endif
//! runtextmacro UnitHitMacroEnd()
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Speed reduction/increase-------------------//
if .isSlowing then
if .slowStart <= .slowTick*PULL_PERIOD then
set .pullSpeed = .pullSpeed - .slowAmount
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Checks if the unit reached its destination-//
if .isPulled then
if .active then
if (.pulltoX-.pulledX)*(.pulltoX-.pulledX)+(.pulltoY-.pulledY)*(.pulltoY-.pulledY) <= .hitRange*.hitRange then
set .active = false
if .notPathed then
call SetUnitPathing(.victim,true)
set thistype.refreshedPathings = false
endif
if .isPaused then
call PauseUnit(.victim,false)
endif
if .hasUnitDest then
call .onAtDestUnit(.destUnit,.victim)
else
call .onAtDestPoint(.victim)
endif
endif
endif
endif
//ooooooooooooooooooooooooooooooooooooooooooooooo//
//----Checks if a unit has 0 moving speed--------//
if .active then
if .pullSpeed <= 0. then
call .SuddenStop()
endif
endif
//--------------counting--------------------------
set .slowTick = .slowTick + 1
else
//--------------dead unit brakes the instance-----
call .SuddenStop()
endif
endmethod
//--------------------------------------------------------------------
//STRUCT CREATION , REGISTRATION , INSTANCE HANDLING FOR MULTI STRUCTS
//--------------------------------------------------------------------
//-------------EVENT RELEASING IN EVERY TIME PERIOD---------------
private method action takes nothing returns nothing
call .onAction(.victim)
call .positioning()
endmethod
//----------------STRUCT LIST HANDLING---------------------------
static method pull takes nothing returns nothing
local thistype trash
set thistype.thiss = thistype.first.next
loop
exitwhen thistype.thiss == thistype.last
if thistype.thiss.active and thistype.thiss != 0 then
set thistype.curPull = thistype.thiss
call thistype.thiss.action()
else
if thistype.thiss != 0 then
set thistype.thiss.next.prev = thistype.thiss.prev
set thistype.thiss.prev.next = thistype.thiss.next
set trash = thistype.thiss
set thistype.thiss = thistype.thiss.prev
call trash.destroy()
endif
set thistype.count = thistype.count -1
endif
set thistype.thiss = thistype.thiss.next
endloop
if thistype.count <= 0 then
set thistype.count = 0
call PauseTimer(thistype.t)
endif
endmethod
//-------------------REGISTERING-----LINKING IN THE LIST--------------------
public method start takes real x, real y, real x2, real y2 returns nothing
local thistype lth = thistype.last
set .pulledX = x
set .pulledY = y
set .pulltoX = x2
set .pulltoY = y2
set thistype.refreshedPathings = false
call .onPullStart()
if .isPulled then
set .pullAngle = Atan2(y2-y,x2-x)
else
set .pullAngle = Atan2(y-y2,x-x2)
endif
if .isJump then
call setUpJumpProperties()
endif
if .isTossed then
call UnitAddAbility(.victim,'Amrf')
call UnitRemoveAbility(.victim,'Amrf')
set .tossVelocity = .tossStartVelocity
endif
if not .isPulReg then
set .active = true
set .isPulReg = true
//this goes here and there and that goes there and here...
set .prev = lth.prev
set .next = lth
set .prev.next = this
set lth.prev = this
set thistype.count = thistype.count + 1
if thistype.count == 1 then
call TimerStart(thistype.t, PULL_PERIOD, true, function thistype.pull)
endif
endif
endmethod
//----------------------------------------------------------------------------------------------------------
//-------------INITIALIZATIONS------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------
private static method onInit takes nothing returns nothing
set thistype.t = CreateTimer()
set thistype.rec = Rect(0.,0.,0.,0.)
set thistype.dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),'psdu',0.,0.,0.)
call PauseUnit(thistype.dummy,true)
set thistype.first = .create()
set thistype.last = .create()
set thistype.first.next = thistype.last
set thistype.last.prev = thistype.first
endmethod
endstruct
endlibrary