HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Jass spell question..

09-20-2007, 08:15 AM#1
StRoNgFoE_2000
Ok so I finally figured out the local handle vars, they have been a blessing thus far. Ive been working on a spell called "Da Bomb", which is basically an exploding bomb nova. It works exactly as intended, except I can't think of a good way to check to see if the units that it is supposed to damage is an enemy. Here it is..

Collapse JASS:
function DaBombCond takes nothing returns boolean
    local integer i = GetSpellAbilityId()
    return (i == 'A03T') or (i == 'A03V') or (i == 'A03W') or (i == 'A03X') or (i == 'A03Y')
endfunction
function DaBombEnemyCheck takes nothing returns boolean
    local unit u = GetFilterUnit()
    local player p = GetTriggerPlayer()
    if IsUnitEnemy(u, p) then
        set u = null
        set p = null
        return true
    endif
    set u = null
    set p = null
    return false
endfunction
function DaBombExplosions takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit v
    local unit u = GetHandleUnit(t, "caster")
    local integer i = 1
    local integer loops = GetHandleInt(t, "loops")
    local integer distance = GetHandleInt(t, "distance")
    local integer angle = GetHandleInt(t, "angle")
    local integer damage = GetHandleInt(t, "damage")
    local player p = GetOwningPlayer(u)
    local location l = GetUnitLoc(u)
    local group g = CreateGroup()
    loop
        exitwhen i > loops
        set l = PolarProjectionBJ(l, distance, angle * I2R(i))
        call DestroyEffect(AddSpecialEffectLoc("Abilities\\Weapons\\FragDriller\\FragDriller.mdl" , l))
        set g = GetUnitsInRangeOfLocMatching(90, l, Condition(function DaBombEnemyCheck))
        loop
            set v = FirstOfGroup(g)
            exitwhen v == null
            call UnitDamageTarget(u, v, damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            call GroupRemoveUnit(g, v)
        endloop
        set l = GetUnitLoc(u)    
        set i = i + 1
    endloop
    call FlushHandleLocals(t)
    call RemoveLocation(l)
    call DestroyGroup(g)
    call DestroyTimer(t)
    set t = null
    set v = null
    set u = null
    set p = null
    set l = null
    set g = null
endfunction
function StartExplosions takes unit caster, real timerstart, integer loops, integer distance, integer angle, integer damage returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, timerstart, true, function DaBombExplosions)
    call SetHandleHandle(t, "caster", caster)
    call SetHandleInt(t, "loops", loops)
    call SetHandleInt(t, "distance", distance)
    call SetHandleInt(t, "angle", angle)
    call SetHandleInt(t, "damage", damage)
    set t = null
endfunction
function DaBomb takes nothing returns nothing
    local unit u = GetSpellAbilityUnit()
    local integer i = GetSpellAbilityId()
    local integer d
    if (i == 'A03T') then
        set d = 250
    elseif (i == 'A03V') then
        set d = 650
    elseif (i == 'A03W') then
        set d = 1950
    elseif (i == 'A03X') then
        set d = 5750
    elseif (i == 'A03Y') then
        set d = 17200
    endif
    call StartExplosions(u, .15, 6, 110, 60, d)
    call StartExplosions(u, .30, 10, 220, 36, d)
    call StartExplosions(u, .45, 20, 330, 18, d)
    call StartExplosions(u, .60, 24, 440, 15, d)
    call StartExplosions(u, .75, 30, 550, 12, d)
    set u = null
endfunction
function InitTrig_BombDaBombCast takes nothing returns nothing
    set gg_trg_BombDaBombCast = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_BombDaBombCast, EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(gg_trg_BombDaBombCast, Condition(function DaBombCond))
    call TriggerAddAction(gg_trg_BombDaBombCast, function DaBomb)
endfunction

Everything works great and it's really fast. The problem is with the function "DaBombEnemyCheck" when creating the group to damage around the explosion. In it's current state, this trigger hurts player units. Obviously, using a timer would not take the triggering player with it. Since it's a function outside of the timer function, what would be a good method to check if the unit is an enemy? The simple problems are always the hardest for me...
09-20-2007, 11:34 AM#2
Anitarf
Well, one option would be to pick all units in range, without DaBombEnemyCheck, but instead put that check into the loop where you damage your units and only damage them if they're an enemy of p.
09-20-2007, 02:28 PM#3
StRoNgFoE_2000
Haha yeah I missed that last night, after I woke up the same answer came to me. After staring at this stuff for hours it sometimes starts to scramble my brain.
09-20-2007, 03:28 PM#4
Silvenon
That's well coded.....

Just get rid of locations and start using coordinates

Also, GetSpellAbilityUnit() is same as GetTriggerUnit()
It's kinda weird seeing someone who actually clean leaks and use natives.....I haven't seen that for a long time :)

Well, except location BJs, because locations can only be handled with those.....

Collapse JASS:
function DaBombCond takes nothing returns boolean
    local integer i = GetSpellAbilityId()
    return (i == 'A03T') or (i == 'A03V') or (i == 'A03W') or (i == 'A03X') or (i == 'A03Y')
endfunction

Omg I'm used to seeing:

Collapse JASS:
if ( not ( something ) ) then
    return false
endif
return true

all the time.......it's very new to me seeing someone who can actually code well........ (I'm used to noobs)

Well this was a useless post.......
09-20-2007, 07:06 PM#5
StRoNgFoE_2000
No no it makes me feel like I'm on the right track to this whole JASS jazz. And to think I just learned how to use handle vars like 2 days ago.

But of course I couldn't have done anything like this without reading the 1000s of examples the past (God only knows) how many hours. Before I tried using all kinds of crazy methods to attempt to achieve something like this... let me tell ya didnt' work...

Here's the final version in case anyone ever stumbles across the thread and wants to use it. Of course they would have to understand how to alter it. The downside is it uses 5 different abilities instead of 5 levels of an ability, for reasons I don't feel like explaining. But that wouldn't be too hard to alter anyway....

Collapse JASS:
function BombDaBombCond takes nothing returns boolean
    local integer i = GetSpellAbilityId()
    return (i == 'A03T') or (i == 'A03V') or (i == 'A03W') or (i == 'A03X') or (i == 'A03Y')
endfunction
function DaBombExplosions takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit v
    local unit u = GetHandleUnit(t, "caster")
    local integer i = 1
    local integer loops = GetHandleInt(t, "loops")
    local integer distance = GetHandleInt(t, "distance")
    local integer angle = GetHandleInt(t, "angle")
    local integer damage = GetHandleInt(t, "damage")
    local player p = GetOwningPlayer(u)
    local location l = GetUnitLoc(u)
    local group g = CreateGroup()
    loop
        exitwhen i > loops
        set l = PolarProjectionBJ(l, distance, angle * I2R(i))
        call DestroyEffect(AddSpecialEffectLoc("Abilities\\Weapons\\FragDriller\\FragDriller.mdl" , l))
        set g = GetUnitsInRangeOfLocMatching(90, l, null)
        loop
            set v = FirstOfGroup(g)
            exitwhen v == null
            if IsUnitEnemy(v, p) then
                call UnitDamageTarget(u, v, damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            endif
            call GroupRemoveUnit(g, v)
        endloop
        set l = GetUnitLoc(u)    
        set i = i + 1
    endloop
    call FlushHandleLocals(t)
    call RemoveLocation(l)
    call DestroyGroup(g)
    call PauseTimer(t)
    call DestroyTimer(t)
    set t = null
    set v = null
    set u = null
    set p = null
    set l = null
    set g = null
endfunction
function StartExplosions takes unit caster, real timerstart, integer loops, integer distance, integer angle, integer damage returns nothing
    local timer t = CreateTimer()
    call TimerStart(t, timerstart, true, function DaBombExplosions)
    call SetHandleHandle(t, "caster", caster)
    call SetHandleInt(t, "loops", loops)
    call SetHandleInt(t, "distance", distance)
    call SetHandleInt(t, "angle", angle)
    call SetHandleInt(t, "damage", damage)
    set t = null
endfunction
function BombDaBomb takes nothing returns nothing
    local unit u = GetSpellAbilityUnit()
    local integer i = GetSpellAbilityId()
    local integer d
    if (i == 'A03T') then
        set d = 250
    elseif (i == 'A03V') then
        set d = 650
    elseif (i == 'A03W') then
        set d = 1950
    elseif (i == 'A03X') then
        set d = 5750
    elseif (i == 'A03Y') then
        set d = 17200
    endif
    call StartExplosions(u, .15, 6, 110, 60, d)
    call StartExplosions(u, .30, 10, 220, 36, d)
    call StartExplosions(u, .45, 20, 330, 18, d)
    call StartExplosions(u, .60, 24, 440, 15, d)
    call StartExplosions(u, .75, 30, 550, 12, d)
    set u = null
endfunction
function InitTrig_BombDaBombCast takes nothing returns nothing
    set gg_trg_BombDaBombCast = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_BombDaBombCast, EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(gg_trg_BombDaBombCast, Condition(function BombDaBombCond))
    call TriggerAddAction(gg_trg_BombDaBombCast, function BombDaBomb)
endfunction
09-20-2007, 07:20 PM#6
Silvenon
You are on the right track with JASS, you actually have a good piece of JASS knowledge, things will go pretty smooth from now on, the hardest part was starting to learn JASS and memory leaks.

A little more practice, and you may go to vJass maybe......

EDTI1: Now I have to catch up with your edit :)

Collapse JASS:
function InitTrig_BombDaBombCast takes nothing returns nothing
    set gg_trg_BombDaBombCast = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_BombDaBombCast, EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(gg_trg_BombDaBombCast, Condition(function BombDaBombCond))
    call TriggerAddAction(gg_trg_BombDaBombCast, function BombDaBomb)
endfunction

Yeah! That's what I like to see, the removal of those ugly GUI-to-JASS spacings.

Remember what I said?

Quote:
GetSpellAbilityUnit() is same as GetTriggerUnit()

That means that you should replace local unit u = GetSpellAbilityUnit() with local unit u = GetTriggerUnit() in BombDaBomb function.

GetSpellAbilityUnit() should by all means be a BJ, but for some reason it's not.....but it's still slower than GetTriggerUnit, which is the fastest event response (you know there is casting unit, attacked unit, entering unit...... they can all be replaced with triggering unit, because that is the fastest).

I can point you to a tutorial about coordinates, it's really not hard, it's very useful and efficient. When you learn them, you can completely avoid BJs :). Though there are useful BJs which are better to leave the way they are (like GroupAddGroup, CinematicModeBJ, CreateQuestBJ, those darn multiboard functions......)

EDIT2: Omg you're even pausing the timer before destroying it......nice job! :)

EDIT3: Found a leak! Well, I guess nobody is perfect......

Collapse JASS:
    loop
        exitwhen i > loops
        set l = PolarProjectionBJ(l, distance, angle * I2R(i))
        call DestroyEffect(AddSpecialEffectLoc("Abilities\\Weapons\\FragDriller\\FragDriller.mdl" , l))
        set g = GetUnitsInRangeOfLocMatching(90, l, null)
        loop
            set v = FirstOfGroup(g)
            exitwhen v == null
            if IsUnitEnemy(v, p) then
                call UnitDamageTarget(u, v, damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            endif
            call GroupRemoveUnit(g, v)
        endloop
        set l = GetUnitLoc(u)    
        set i = i + 1
    endloop


Put call RemoveLocation(l) before call PolarProjectionBJ(l, distance, angle * I2R(i))

If you were using coordinates, you wouldn't have to worry about leaks like that :) (because they don't leak)

EDIT4: Also, when using integers in calculations with a real result, there is no need to I2R them, because that happens by itself. If it was other way around, then you would have to R2I it.

Darn, to many edits :)
09-20-2007, 08:41 PM#7
StRoNgFoE_2000
Yeah i read if you dont pause a timer before you destroy it, it can cause some "mysterious, undesirable effects".

I have a question on that leak.. I'll copy the part from where my concern starts to where it ends.
Collapse JASS:
    local location l = GetUnitLoc(u)
    set l = PolarProjectionBJ(l, distance, angle * I2R(i))

You say to remove the location before the polarproject, but won't it be needed in the that function? If I remove the location, "l" doesn't exist anymore. Or does it now I'm confused on something, something that I should have gotten cleared up long ago.

I do remove the location at the end of the function, but from what you are saying I'm assuming that every time you set a location pointer, you have to remove that particular one... I can't explain very well without an example.

Collapse JASS:
//right??
local location l = GetUnitLoc(u)
--Actions with l
call RemoveLocation(l)
set l = GetUnitLoc(v)
--Actions with l
call RemoveLocation(l)
set l = GetUnitLoc(w)
--Actions with l
call RemoveLocation(l)

//wrong??
local location l = GetUnitLoc(u)
--Actions with l
set l = GetUnitLoc(v)
--Actions with l
set l = GetUnitLoc(w)
--Actions with l
call RemoveLocation(l)

I tried something similar to the "right" above in, as a matter of fact thats how I did it for the longest time, lots of RemoveLocations everytime I set it and I'm done with it. But then when I test the map in newgen editor I get error messages saying "Double free of location "l" in function XX". So I figured whoever designed the error checking knew what he was doing... now I'm confused all over again.

Maybe I should just learn about coords now...
09-20-2007, 09:00 PM#8
TaintedReality
Your assumption is right, as both of those functions creates a new location, so you have to destroy it both times. To make that using locations, you would have to use two locations, one with the intial point and one with the polar projection, and then destroy them both.

The best solution would be to just use X and Y coordinates, and use cosine/sine instead of PolarProjection. It's faster and you don't have to mess with locations.