• 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.

[JASS] homing target spell

Status
Not open for further replies.
Level 3
Joined
Jun 6, 2007
Messages
48
Hey guys, my spell is called Landslide and what it does when my hero (the Earthfather) casts the spell on a target it creates dust effects up until it reaches the target however at the moment it just goes in a straight line and if the target is moving i think the line gets messed up. Anyway heres my code, i tried to remove leaks, replace BJs and comment my code as much as possible before i submitted it:

JASS:
function Landslide_C takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A009' ) ) then
        return false
    endif
    return true
endfunction

function Landslide_A takes nothing returns nothing

    local unit          l_caster            =   GetSpellAbilityUnit()
    local unit          l_target            =   GetSpellTargetUnit()
    local location      l_rockLoc
    local location      l_casterLoc         =   GetUnitLoc(l_caster)
    local location      l_targetLoc         =   GetUnitLoc(l_target)
    local real          l_casterFacing
    local real          l_loop              =   0.00
    local real          l_distance          =   DistanceBetweenPoints(l_casterLoc, l_targetLoc)
    local real          l_result            =   23.32
    
    // Simple wait for unit to face target
    call TriggerSleepAction(0.6)
    
    // We set the facing now rather then instantly
    set l_casterFacing = GetUnitFacing(l_caster)
    
    loop
        // Max distance is 880
        exitwhen l_loop == 880
        // Check if we are close enough to the target to damage
        if (l_loop > l_distance) then
            // Check if the distance is REALLY close
            set l_result = l_distance / l_loop
        endif
        if (l_result < 1) then
            // Effect has reached target
            // --
            // Make sure our loop doesn't run after this
            set l_loop = 880
            // Create our dust effect at target and make a Rock shoot out of the ground
            call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.25)
            call CreateNUnitsAtLoc( 1, 'A010', Player(9), l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 1.75)
            call SetUnitFlyHeight(GetLastCreatedUnit(), 0, 1000)
        else
            // Effect has yet to reach target
            // --
            // Set a gap between the effects so they dont appear instantly
            call TriggerSleepAction(0.01)
            // Establish our location and create an effect at it for a really short time
            set l_rockLoc = PolarProjectionBJ(l_casterLoc, l_loop, l_casterFacing)
            call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_rockLoc, GetRandomReal(0, 360) )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.05 )
            // Set our loop so the effect advances forward
            set l_loop = l_loop + 80
        endif
    endloop
    
    // Remove our variables until next time
    call RemoveLocation(l_rockLoc)
    call RemoveLocation(l_casterLoc)
    call RemoveLocation(l_targetLoc)
    set     l_casterFacing  = 0.00
    set     l_caster        = null
    set     l_target        = null
    set     l_result        = 23.32
    set     l_loop          = 0.00
    set     l_distance      = 0.00
    
endfunction

//===========================================================================
function InitTrig_LandslideL takes nothing returns nothing
    local trigger Landslide = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( Landslide, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( Landslide, Condition( function Landslide_C ) )
    call TriggerAddAction( Landslide, function Landslide_A )
endfunction

What i would be really greatful for is if anyone could help me make the dust kinda chase the enemy during the loop but only so it goes to a distance of 880-900 (Hence the loop)
 
Level 13
Joined
Mar 16, 2008
Messages
941
First of all:
Try to keep things as simple as possible. The GUI convert stuff suxx ;D

JASS:
function Landslide_C takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A009' ) ) then
        return false
    endif
    return true
endfunction
Is exactly the same like
JASS:
function Landslide_C takes nothing returns boolean
    return GetSpellAbilityId() == 'A009'
endfunction

Then I would recommend to you to take the function list of JNGP or JassCraft and get rid of BJs, wrapper functions and locations, but thats of no importance.

Then you should take a look on timers ;)

In THIS case the solution is quite easy.
Instand of using the caster position, use the position of the last created unit:

JASS:
function Landslide_C takes nothing returns boolean
    return GetSpellAbilityId() == 'A009'
endfunction

function Landslide_A takes nothing returns nothing

    local unit l_caster = GetTriggerUnit()
    //GetSpellAbilityUnit() just returns GetTriggerUnit()
    local unit l_target = GetSpellTargetUnit()
    local location l_rockLoc = null
    local location l_casterLoc = GetUnitLoc(l_caster)
    local location l_targetLoc = GetUnitLoc(l_target)
    local location l_tempLoc = GetUnitLoc(l_target)
    //Added a new location, why? Search for it :P
    //btw the GetUnitLoc(l_target) is for the 1. execution of the loop
    local real l_facing = 0
    local real l_loop = 0.00
    local real l_distance = DistanceBetweenPoints(l_casterLoc, l_targetLoc)
    local real l_result = 23.32
 

    loop
        exitwhen l_loop == 880
        if (l_loop > l_distance) then
            set l_result = l_distance / l_loop
        endif
        if (l_result < 1) then
            set l_loop = 880
            call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.25)
            call CreateNUnitsAtLoc( 1, 'A010', Player(9), l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 1.75)
            call SetUnitFlyHeight(GetLastCreatedUnit(), 0, 1000)
            //Why this?
        else
            call TriggerSleepAction(0.01)
            //Is at least ~0.25, thats why you should use timers
            set l_facing = AngleBetweenPoints(l_casterLoc, l_tempLoc)
            set l_rockLoc = PolarProjectionBJ(l_tempLoc, l_loop, l_facing)
            call RemoveLocation(l_tempLoc)
            set l_tempLoc = l_rockLoc
            call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_rockLoc, GetRandomReal(0, 360) )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.05 )
            set l_loop = l_loop + 80
        endif
    endloop

    //You don't have to '0' reals, they don't leak
    //but you have to 'null' locations
    call RemoveLocation(l_tempLoc)
    call RemoveLocation(l_rockLoc)
    call RemoveLocation(l_casterLoc)
    call RemoveLocation(l_targetLoc)
    set l_tempLoc = null
    set l_rockLoc = null
    set l_casterLoc = null
    set l_targetLoc = null
    set l_tempLoc = null
    set l_caster = null
    set l_target = null    
endfunction

//===========================================================================
function InitTrig_LandslideL takes nothing returns nothing
    local trigger Landslide = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( Landslide, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    //Rather takes SPELL_EFFECT then SPELL_CAST, since effect is when it realy casts. CAST can cause problems
    //I'm also using it sometimes, but just when you want be able to cancel the cast (with the order "stop")
    call TriggerAddCondition( Landslide, Condition( function Landslide_C ) )
    call TriggerAddAction( Landslide, function Landslide_A )
endfunction

I used a new location for the position of the last created unit and calculated the angle each time again.
Btw, why do you make it for player(9) and not for GetOwningPlayer(l_caster)?

Think that's all, hope this works^^

Greets, Justify
 
Level 3
Joined
Jun 6, 2007
Messages
48
First of all:
Try to keep things as simple as possible. The GUI convert stuff suxx ;D

Haha, thanks i'll keep that in mind. Does that work for some of the non BJ functions like if i were returning a GetUnitState like so?

JASS:
function Landslide_C takes nothing returns boolean
    return GetUnitState(someUnit, UNIT_STATE_LIFE) <= 0
endfunction

Also, about this:
JASS:
call SetUnitFlyHeight(GetLastCreatedUnit(), 0, 1000)
//Why this?

Without that my rock just appears there, what that seems to do is create the rock underground and make it popup, i don't know how because if i put (1000, 0) it doesn't appear then go underground but since it works i just leave it there. xD

With regards to the natives, BJs, Wrappers etc i've learnt alot from JassCraft and JassNewGen but some things still bother me, Is there a native or simpler method then using PolarProjectionBJ? I've seen alot of tutorials that tell learners not to use BJs but this is the only one that has me puzzled since i know that i don't have to use TriggerRegisterAnyUnitEventBJ, i can just use TriggerRegisterAnyUnitEvent and specify a player.

JASS:
function PolarProjectionBJ takes location source, real dist, real angle returns location
    local real x = GetLocationX(source) + dist * Cos(angle * bj_DEGTORAD)
    local real y = GetLocationY(source) + dist * Sin(angle * bj_DEGTORAD)
    return Location(x, y)
endfunction

Since i'm only creating one unit at a time i've replaced the BJ: CreateNUnitsAtLoc with CreateUnitAtLoc which makes my final coding: (I plan to read up on Timers and improving my code in the very near future)

JASS:
function Landslide_C takes nothing returns boolean
    return GetSpellAbilityId() == 'A009'
endfunction

function Landslide_A takes nothing returns nothing

    local unit l_caster = GetTriggerUnit()
    //GetSpellAbilityUnit() just returns GetTriggerUnit()
    local unit l_target = GetSpellTargetUnit()
    local location l_rockLoc = null
    local location l_casterLoc = GetUnitLoc(l_caster)
    local location l_targetLoc = GetUnitLoc(l_target)
    local location l_tempLoc = GetUnitLoc(l_target)
    //Added a new location, why? Search for it :P
    //btw the GetUnitLoc(l_target) is for the 1. execution of the loop
    local real l_facing = 0
    local real l_loop = 0.00
    local real l_distance = DistanceBetweenPoints(l_casterLoc, l_targetLoc)
    local real l_result = 23.32


    loop
        exitwhen l_loop == 880
        if (l_loop > l_distance) then
            set l_result = l_distance / l_loop
        endif
        if (l_result < 1) then
            set l_loop = 880
            //call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_targetLoc, bj_UNIT_FACING )
            call CreateUnitAtLoc(Player(9), 'A011', l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.25)
            //call CreateNUnitsAtLoc( 1, 'A010', Player(9), l_targetLoc, bj_UNIT_FACING )
            call CreateUnitAtLoc(Player(9), 'A010', l_targetLoc, bj_UNIT_FACING )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 1.75)
            call SetUnitFlyHeight(GetLastCreatedUnit(), 0, 1000)
            //Why this?
        else
            call TriggerSleepAction(0.01)
            //Is at least ~0.25, thats why you should use timers
            set l_facing = AngleBetweenPoints(l_casterLoc, l_tempLoc)
            set l_rockLoc = PolarProjectionBJ(l_tempLoc, l_loop, l_facing)
            call RemoveLocation(l_tempLoc)
            set l_tempLoc = l_rockLoc
            //call CreateNUnitsAtLoc( 1, 'A011', Player(9), l_rockLoc, GetRandomReal(0, 360) )
            call CreateUnitAtLoc(Player(9), 'A011', l_rockLoc, GetRandomReal(0, 360) )
            call UnitApplyTimedLife(GetLastCreatedUnit(), 'BTLF', 0.05 )
            set l_loop = l_loop + 80
        endif
    endloop

    //You don't have to '0' reals, they don't leak
    //but you have to 'null' locations
    call RemoveLocation(l_tempLoc)
    call RemoveLocation(l_rockLoc)
    call RemoveLocation(l_casterLoc)
    call RemoveLocation(l_targetLoc)
    set l_tempLoc = null
    set l_rockLoc = null
    set l_casterLoc = null
    set l_targetLoc = null
    set l_tempLoc = null
    set l_caster = null
    set l_target = null
endfunction

//===========================================================================
function InitTrig_LandslideL takes nothing returns nothing
    local trigger Landslide = CreateTrigger( )
    call TriggerRegisterAnyUnitEventBJ( Landslide, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    //Rather takes SPELL_EFFECT then SPELL_CAST, since effect is when it realy casts. CAST can cause problems
    //I'm also using it sometimes, but just when you want be able to cancel the cast (with the order "stop")
    call TriggerAddCondition( Landslide, Condition( function Landslide_C ) )
    call TriggerAddAction( Landslide, function Landslide_A )
endfunction

Will definitely +Rep again for the help and tips when i've spread my share of rep among others however the code doesn't work, as soon as my hero casts it the effects all appear at the target, the code i posted in my first post makes dust clouds appear between the caster and the target starting at the caster and moving towards the caster until the dust hits the unit then makes a rock popup and once i get it right i'll add damage. The problem with the first post code was that if the unit moved while the dust traveled to its location, the dust would travel to where the unit had been and then a rock would popup from under nothing. :(
 
Level 13
Joined
Mar 16, 2008
Messages
941
Haha, thanks i'll keep that in mind. Does that work for some of the non BJ functions like if i were returning a GetUnitState like so?

JASS:
function Landslide_C takes nothing returns boolean
    return GetUnitState(someUnit, UNIT_STATE_LIFE) <= 0
endfunction
You could use GetWidgetLife, but its not that important.

With regards to the natives, BJs, Wrappers etc i've learnt alot from JassCraft and JassNewGen but some things still bother me, Is there a native or simpler method then using PolarProjectionBJ? I've seen alot of tutorials that tell learners not to use BJs but this is the only one that has me puzzled since i know that i don't have to use

JASS:
function PolarProjectionBJ takes location source, real dist, real angle returns location
    local real x = GetLocationX(source) + dist * Cos(angle * bj_DEGTORAD)
    local real y = GetLocationY(source) + dist * Sin(angle * bj_DEGTORAD)
    return Location(x, y)
endfunction
You postet the anwser yourself.
After a time you'll learn how annoying locations are and switch to coordinates (and then you need THIS calculations).
Sounds harder than it is.


Will definitely +Rep again for the help and tips when i've spread my share of rep among others however the code doesn't work
Give me some minuts, I'll copy it and try to fix it.
I don't get it :S I'll edit it if I find it^^
 
Status
Not open for further replies.
Top