• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Trigger] Multiple Target Knockback To a Specific Point

Status
Not open for further replies.
Level 3
Joined
Feb 13, 2008
Messages
65
Hey there. So basically, I need help creating a trigger; this is basically what the trigger has to do:

1. Select every allied unit around the enemy unit that the allied units are attacking.

2. Calculate the distance between wherever the allied unit is and the predetermined never-changing point that all the allied units are supposed to be knockbacked too. This distance will be divided by 5.

3. Move the allied units every 1 second for 5 seconds, until they reach the predetermined - point.

I've looked over every knockback spell guide on here, and they are all either focusing on only one target or have no predetermined point in which the knockbacked units have to reach.
 
Level 5
Joined
Nov 14, 2007
Messages
161
i kinda had this same sort of problem, all the knockback scripts i tried to use didnt seem to work etc. but i posted and aznricepuff showed me one that i really do like.

if you have vJASS/NewGen World Editor this will work, if not then you will need to find other means or wait for another response.

Put both of these into your "header" (click the map at the top when looking at triggers, and paste it in custom scripts)

JASS:
library Geometry

struct Point
    real x
    real y
    
    static method create takes real x, real y returns Point
        local Point p = Point.allocate()
        set p.x = x
        set p.y = y
        
        return p
    endmethod
    
    method getDistanceToPoint takes Point p returns real
        local real dx = p.x - .x
        local real dy = p.y - .y
        
        return SquareRoot( dx * dx + dy * dy )
    endmethod
    
    method getAngletoPoint takes Point p returns real
        local real dx = p.x - .x
        local real dy = p.y - .y
        
        return Atan2( dy, dx )
    endmethod
    
    method calculateDisplacement takes real distance, real angle returns Point
        return Point.create( .x + distance * Cos( angle ), .y + distance * Sin( angle ) )
    endmethod
    
    static method getDistanceBetweenPoints takes Point p1, Point p2 returns real
        local real dx = p2.x - p1.x
        local real dy = p2.y - p1.y
        
        return SquareRoot( dx * dx + dy * dy )
    endmethod
    
    static method getAngleBetweenPoints takes Point p1, Point p2 returns real
        local real dx = p2.x - p1.x
        local real dy = p2.y - p1.y
        
        return Atan2( dy, dx )
    endmethod
    
    static method pointFromLoc takes location loc returns Point
        return Point.create( GetLocationX( loc ), GetLocationY( loc ) )
    endmethod
    
    static method pointFromUnit takes unit u returns Point
        return Point.create( GetUnitX( u ), GetUnitY( u ) )
    endmethod
    
endstruct

endlibrary

JASS:
library Knockback initializer init requires Geometry

globals
    private constant real TIMEOUT = 0.035
    
    private Knockback array knockbackArray
    private integer knockbackArraySize = 0
    private timer t
endglobals

struct Knockback

    unit target                    //unit being knocked back
    real angle                     //angle (equal to theta in 3D polar coords) of movement direction in radians
    real increment                 //distance to move unit with each loop of timer
    real distance                  //total distance to knock back
    real distanceRemaining         //distance remaining to knock back
    boolean toDestroy = false      //whether or not struct needs to be destroyed
    
    //whenever you need to knock back a unit, just call this method
    //unit target: unit being knocked back
    //real angle: angle (theta) in radians of the direction of the knockback
    //real speed: speed (wc3 distance units/second) of knockback
    //real distance: distance to knock back
    static method create takes unit target, real angle, real speed, real distance returns Knockback
        local Knockback temp = Knockback.allocate()
        
        set temp.target = target
        set temp.angle = angle
        set temp.increment = TIMEOUT * speed
        set temp.distance = distance
        set temp.distanceRemaining = distance
        
        set knockbackArray[knockbackArraySize] = temp
        set knockbackArraySize = knockbackArraySize + 1
        
        if ( knockbackArraySize == 1 ) then
            call TimerStart( t, TIMEOUT, true, function Knockback.executeKnockback )
        endif
        
        return temp
    endmethod

    private static method executeKnockback takes nothing returns nothing
        local Knockback data
        local Point p
        local integer i = 0
    
        loop
            exitwhen ( i >= knockbackArraySize )
        
            set data = knockbackArray[i]
        
            if ( data.toDestroy ) then //struct needs to be destroyed
                set knockbackArray[i] = knockbackArray[knockbackArraySize - 1] //we copy the struct at the last index of the array to the current index
                set knockbackArraySize = knockbackArraySize - 1 //decrease size of array by one (technically just decreasing the maximum index of the array that this loop will read
                set i = i - 1 //since we decreased the array size by one, we need to decrease i by one otherwise that struct that we copied would not get read by this loop (loop would stop one short of the index for that struct), but since we COPIED that struct to the current index, by decreasing i by one, the next iteration of this loop would result in the loop reading the same index as this iteration, and now that index contains the copied struct
        
                call data.destroy() //destroy the struct to free up memory
            else
                set p = Point.pointFromUnit( data.target ) //get location of the unit being knocked back
                set p = p.calculateDisplacement( data.angle, data.increment ) //calculate the point with a displacement from p equal to the given angle and distance (think of it as a vector in polar form), and save that point in p
                call SetUnitPosition( data.target, p.x, p.y ) //set unit position
                set data.distanceRemaining = data.distanceRemaining - data.increment //update the distance remaining to knockback
            
                call p.destroy() //destroy the point struct to free up memory
            
                if ( data.distanceRemaining <= 0 ) then //unit has been knocked back far enough
                    set data.toDestroy = true //slate struct for destruction on next iteration of the timer
                endif
            endif
        
            set i = i + 1
        endloop
    
        if ( knockbackArraySize == 0 ) then
            call PauseTimer( t )
        endif
    endmethod

endstruct

private function init takes nothing returns nothing
    set t = CreateTimer()
endfunction

endlibrary

and without testing, i believe the code used would be:

  • Knockback to Point
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to <Your Ability>
    • Actions
      • Set AAA_Point = (Center of Your_Region <gen>)
      • Unit Group - Pick every unit in (Units within 600.00 of (Position of (Triggering unit)) matching (((Matching unit) belongs to an enemy of (Owner of (Triggering unit))) Equal to True)) and do (Actions)
        • Loop - Actions
          • Custom script: local unit picked_unit = GetEnumUnit()
          • Set AAA_Real_Distance = ((Distance between AAA_Point and (Position of (Picked unit))) / 5.00)
          • Set AAA_Real_Angle = (Angle from (Position of (Picked unit)) to AAA_Point)
          • Set AAA_Real_Speed = (AAA_Real_Distance / 5.00)
          • Custom script: call Knockback.create(picked_unit, udg_AAA_Real_Angle, udg_AAA_Real_Speed, udg_AAA_Real_Distance)
          • Custom script: set picked_unit = null
take note anything that usually leaks does in this case.... i cleaned up the local unit that was made, but the rest leaks. unit group and 3 points i believe.
 
Last edited:
Level 11
Joined
Dec 31, 2007
Messages
780
  • Set AAA_Real_Distance = ((Distance between AAA_Point and (Position of (Picked unit))) / 5.00)
  • Set AAA_Real_Angle = (Angle from (Position of (Picked unit)) to AAA_Point)
2 leaks of the same position... just set a variable with position of picked unit and use it... then (inside the unit group) clear it by using call RemoveLocation(udg_YOURPOINTVARIABLE)
 
Level 3
Joined
Feb 13, 2008
Messages
65
i kinda had this same sort of problem, all the knockback scripts i tried to use didnt seem to work etc. but i posted and aznricepuff showed me one that i really do like.

if you have vJASS/NewGen World Editor this will work, if not then you will need to find other means or wait for another response.

Put both of these into your "header" (click the map at the top when looking at triggers, and paste it in custom scripts)

JASS:
library Geometry

struct Point
    real x
    real y
    
    static method create takes real x, real y returns Point
        local Point p = Point.allocate()
        set p.x = x
        set p.y = y
        
        return p
    endmethod
    
    method getDistanceToPoint takes Point p returns real
        local real dx = p.x - .x
        local real dy = p.y - .y
        
        return SquareRoot( dx * dx + dy * dy )
    endmethod
    
    method getAngletoPoint takes Point p returns real
        local real dx = p.x - .x
        local real dy = p.y - .y
        
        return Atan2( dy, dx )
    endmethod
    
    method calculateDisplacement takes real distance, real angle returns Point
        return Point.create( .x + distance * Cos( angle ), .y + distance * Sin( angle ) )
    endmethod
    
    static method getDistanceBetweenPoints takes Point p1, Point p2 returns real
        local real dx = p2.x - p1.x
        local real dy = p2.y - p1.y
        
        return SquareRoot( dx * dx + dy * dy )
    endmethod
    
    static method getAngleBetweenPoints takes Point p1, Point p2 returns real
        local real dx = p2.x - p1.x
        local real dy = p2.y - p1.y
        
        return Atan2( dy, dx )
    endmethod
    
    static method pointFromLoc takes location loc returns Point
        return Point.create( GetLocationX( loc ), GetLocationY( loc ) )
    endmethod
    
    static method pointFromUnit takes unit u returns Point
        return Point.create( GetUnitX( u ), GetUnitY( u ) )
    endmethod
    
endstruct

endlibrary

JASS:
library Knockback initializer init requires Geometry

globals
    private constant real TIMEOUT = 0.035
    
    private Knockback array knockbackArray
    private integer knockbackArraySize = 0
    private timer t
endglobals

struct Knockback

    unit target                    //unit being knocked back
    real angle                     //angle (equal to theta in 3D polar coords) of movement direction in radians
    real increment                 //distance to move unit with each loop of timer
    real distance                  //total distance to knock back
    real distanceRemaining         //distance remaining to knock back
    boolean toDestroy = false      //whether or not struct needs to be destroyed
    
    //whenever you need to knock back a unit, just call this method
    //unit target: unit being knocked back
    //real angle: angle (theta) in radians of the direction of the knockback
    //real speed: speed (wc3 distance units/second) of knockback
    //real distance: distance to knock back
    static method create takes unit target, real angle, real speed, real distance returns Knockback
        local Knockback temp = Knockback.allocate()
        
        set temp.target = target
        set temp.angle = angle
        set temp.increment = TIMEOUT * speed
        set temp.distance = distance
        set temp.distanceRemaining = distance
        
        set knockbackArray[knockbackArraySize] = temp
        set knockbackArraySize = knockbackArraySize + 1
        
        if ( knockbackArraySize == 1 ) then
            call TimerStart( t, TIMEOUT, true, function Knockback.executeKnockback )
        endif
        
        return temp
    endmethod

    private static method executeKnockback takes nothing returns nothing
        local Knockback data
        local Point p
        local integer i = 0
    
        loop
            exitwhen ( i >= knockbackArraySize )
        
            set data = knockbackArray[i]
        
            if ( data.toDestroy ) then //struct needs to be destroyed
                set knockbackArray[i] = knockbackArray[knockbackArraySize - 1] //we copy the struct at the last index of the array to the current index
                set knockbackArraySize = knockbackArraySize - 1 //decrease size of array by one (technically just decreasing the maximum index of the array that this loop will read
                set i = i - 1 //since we decreased the array size by one, we need to decrease i by one otherwise that struct that we copied would not get read by this loop (loop would stop one short of the index for that struct), but since we COPIED that struct to the current index, by decreasing i by one, the next iteration of this loop would result in the loop reading the same index as this iteration, and now that index contains the copied struct
        
                call data.destroy() //destroy the struct to free up memory
            else
                set p = Point.pointFromUnit( data.target ) //get location of the unit being knocked back
                set p = p.calculateDisplacement( data.angle, data.increment ) //calculate the point with a displacement from p equal to the given angle and distance (think of it as a vector in polar form), and save that point in p
                call SetUnitPosition( data.target, p.x, p.y ) //set unit position
                set data.distanceRemaining = data.distanceRemaining - data.increment //update the distance remaining to knockback
            
                call p.destroy() //destroy the point struct to free up memory
            
                if ( data.distanceRemaining <= 0 ) then //unit has been knocked back far enough
                    set data.toDestroy = true //slate struct for destruction on next iteration of the timer
                endif
            endif
        
            set i = i + 1
        endloop
    
        if ( knockbackArraySize == 0 ) then
            call PauseTimer( t )
        endif
    endmethod

endstruct

private function init takes nothing returns nothing
    set t = CreateTimer()
endfunction

endlibrary

and without testing, i believe the code used would be:

  • Knockback to Point
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to <Your Ability>
    • Actions
      • Set AAA_Point = (Center of Your_Region <gen>)
      • Unit Group - Pick every unit in (Units within 600.00 of (Position of (Triggering unit)) matching (((Matching unit) belongs to an enemy of (Owner of (Triggering unit))) Equal to True)) and do (Actions)
        • Loop - Actions
          • Custom script: local unit picked_unit = GetEnumUnit()
          • Set AAA_Real_Distance = ((Distance between AAA_Point and (Position of (Picked unit))) / 5.00)
          • Set AAA_Real_Angle = (Angle from (Position of (Picked unit)) to AAA_Point)
          • Set AAA_Real_Speed = (AAA_Real_Distance / 5.00)
          • Custom script: call Knockback.create(picked_unit, udg_AAA_Real_Angle, udg_AAA_Real_Speed, udg_AAA_Real_Distance)
          • Custom script: set picked_unit = null
take note anything that usually leaks does in this case.... i cleaned up the local unit that was made, but the rest leaks. unit group and 3 points i believe.


Thanks for trying, but I dont use vJass/NewGen WE.

But I did understand the just of the trigger you posted and i think i can use that in a different way to make it work, thanks.
 
Last edited:
Status
Not open for further replies.
Top