Spell Grenade

DrEvil

FCRI Associate!
Reaction score
111
Grenade + Bouncy Grenade <updated>

I have re-coded my grenade spell to work with terrain heights,
I have created two, one that flies straight to the point, and another which bounces to the target point.

vJASS = yes
MUI = yes
Leakless = yes
Lagless = yes

GrenadeDisplay.jpg


Description of Grenade :
The caster throws a grenade towards the target point, as soon as it hits the ground it will explode, possibly damaging player units, allies and enemies.
Level 1 = 200 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 10-25
Level 2 = 250 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 25-40
Level 3 = 300 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 40-55
Description of Bouncy Grenade : ( Same stats of Grenade, but it bounces... )
The caster determines where to throw the grenade that it will bounce towards the target ( Results vary if launched from lower/higher terrain)
Level 1 = 200 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 10-25
Level 2 = 250 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 25-40
Level 3 = 300 Aoe / Minimum range = 400 / Maximum range = 1200 / Damage = 40-55
JASS:
scope Grenade initializer Init 
private keyword Data
globals 
    // Editable Globals
    private constant integer GRENADE_ID = &#039;gdum&#039;   // Grenade unit
    private constant integer GRENADE_SFX1 = &#039;xdum&#039; // SFX 1
    private constant integer GRENADE_SFX2 = &#039;xsfx&#039; // SFX 2
    private constant integer SPELL_ID = &#039;GAId&#039;  // Ability ID
    private constant real MAX_RANGE = 1000
    private constant real MIN_RANGE = 400
    private constant real MAX_HEIGHT = 400
    private constant attacktype AT = ATTACK_TYPE_NORMAL
    private constant damagetype DT = DAMAGE_TYPE_NORMAL
    private constant weapontype WT = WEAPON_TYPE_WHOKNOWS
    private constant boolean DAMAGE_SELF = false// Can it hurt player units ?
    private constant boolean DAMAGE_ALLY = false// Can it hurt ally units ?
    // Editable Globals
    
    // Don&#039;t change...
    private constant real PERIOD = 0.04 // Depends on what period you like ... don&#039;t moan at me if your .0001 period lags though...
    private constant real SPEED = 700 * PERIOD// How fast the grenade travels per second 
    private Data DATA //Temp Globals
    private real X //Temp Globals
    private real Y //Temp Globals
    private real DIST //Temp Globals
    private integer COUNT = 0
    private Data array GData
    private timer TIMER = CreateTimer()
    private location LOC = Location(0,0)
    //Don&#039;t change
endglobals

private constant function AOE_Range takes integer lvl returns real
    return 150 + lvl*50 *1.0
endfunction

private function MinMax takes real val,real min,real max returns real
    if(min&gt;val) then
        return min
    elseif(val&gt;max) then
        return max
    else
        return val
    endif
endfunction

private function GetParabolaZ takes real x,real d,real h returns real
    return 4 * h * x * (d - x) / (d * d)
endfunction // By AceHart

private function GetDamage takes integer level returns real
  return GetRandomReal (10,20) + 15 * (level-1)
endfunction// I guess Flare @ TH.net helped me with this <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite12" alt="o_O" title="Er... what?    o_O" loading="lazy" data-shortname="o_O" />...

private struct Data
    unit Grenade
    unit caster
    player p 
    real x
    real y
    real DistCur
    real DistMax
    real baseH
    real aoe
    integer level
    real cos
    real sin
    
    static group GROUP = CreateGroup()
    
    static method Damage takes nothing returns boolean
        local unit u = GetFilterUnit()
        local player p = GetOwningPlayer(u)
        local Data d = DATA
        local real damage = GetDamage(d.level)
        local boolean dmg = false
        
        if(DAMAGE_SELF and p==d.p)then
            set dmg = true
        elseif(DAMAGE_ALLY and IsPlayerAlly(p,d.p))then
            set dmg = true
        elseif(IsPlayerEnemy(p,d.p))then
            set dmg = true
        endif
        
        if(dmg) then
            call UnitDamageTarget(d.caster,u,damage,true,true,AT,DT,WT)
        endif
        
        set u = null
        
        return false
    endmethod
    
    static method InCircle takes nothing returns boolean
        local destructable d = GetFilterDestructable()
        local real x = DATA.x - GetDestructableX(d)
        local real y = DATA.y - GetDestructableY(d)
        local real dist = x*x+y*y
        
        set d = null
        
        if(dist&gt;DATA.aoe*DATA.aoe)then// or SquareRoot(dist) &gt; AOE_RANGE either workz...
            return false
        else
            return true
        endif        
    endmethod
    
    static method TreeDestroy takes nothing returns nothing 
        local destructable d = GetEnumDestructable()
        
        if IsDestructableTree(d) then
            call KillDestructable(d)
        endif
        
        set d = null
    endmethod
    
    method KillTrees takes real x,real y returns nothing
        local rect r = Rect(x-.aoe,y-.aoe,x+.aoe,y+.aoe)
        
        call EnumDestructablesInRect(r,Filter(function Data.InCircle),function Data.TreeDestroy)
        
        call RemoveRect(r)
        set r = null
    endmethod
    
    method Explode takes nothing returns nothing
        call UnitApplyTimedLife(CreateUnit(.p,GRENADE_SFX1,.x,.y,0),0,1)
        call UnitApplyTimedLife(CreateUnit(.p,GRENADE_SFX2,.x,.y,0),0,1)
        
        set DATA = this
        
        call GroupEnumUnitsInRange(.GROUP,.x,.y,this.aoe,Filter(function Data.Damage))
        
        call .KillTrees(.x,.y)
        call .destroy()
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ShowUnit(.Grenade,false)// Don&#039;t know wether this is neccesary ?
        call RemoveUnit(.Grenade)
    endmethod
endstruct

private function Execute takes nothing returns nothing
    local integer i = 0
    local Data d 
    local unit u 
    local boolean zhit 
    
    loop
        exitwhen i == COUNT
        
        set zhit = false
        
        set d = GData<i>
        set u = d.Grenade
            
        set d.DistCur = d.DistCur + SPEED
        set d.x = d.x + SPEED * d.cos
        set d.y = d.y + SPEED * d.sin
        
        if IsInMapXY(d.x,d.y) == true then
        
            call SetUnitX(u,d.x)
            call SetUnitY(u,d.y)
            
            call SetUnitZ(u,d.baseH+GetParabolaZ(d.DistCur,d.DistMax,MAX_HEIGHT))
            
            call MoveLocation(LOC,d.x,d.y)
            
            if(R2I(GetUnitZ(u))==R2I(GetLocationZ(LOC)))then
                // R2I because it gives me &#039;0.001&#039; and stuff ... so it isn&#039;t &#039;equal&#039; unit height
                set zhit=true
            endif
            
            if zhit==true then
                set COUNT = COUNT - 1
                set GData<i> = GData[COUNT]
                        
                call d.Explode()
            else
                set i = i + 1
            endif
            
        else
            set COUNT = COUNT - 1
            set GData<i> = GData[COUNT]
            call d.destroy()
        endif
    endloop
    
    set u = null    
    
    if COUNT == 0 then
        call PauseTimer(TIMER)
    endif
endfunction

private function Run takes nothing returns nothing
    local Data d = Data.create()
    local location l = GetSpellTargetLoc()
    local real x = GetLocationX(l)
    local real y = GetLocationY(l)
    local real dist = 0
    local real facing
    
    
    call RemoveLocation(l)// do I need to &#039;set l = null&#039; ?
    
    set d.caster = GetTriggerUnit()
    set l = GetUnitLoc(d.caster)
    set d.baseH = GetLocationZ(l)
    
    call RemoveLocation(l)
    set l = null
    
    set d.p = GetOwningPlayer(d.caster)
    set d.x = GetUnitX(d.caster)
    set d.y = GetUnitY(d.caster)
    set d.level = GetUnitAbilityLevel(d.caster,SPELL_ID)
    set d.aoe = AOE_Range(d.level)
    
    set x = x - d.x
    set y = y - d.y
    
    set facing = Atan2(y,x)
    set d.cos = Cos(facing)
    set d.sin = Sin(facing)
    set d.Grenade = CreateUnit(d.p,GRENADE_ID,d.x,d.y,facing)
    
    call UnitAddAbility(d.Grenade,&#039;Amrf&#039;)// I have to do this or else it goes buggy over raised terrain
    call UnitRemoveAbility(d.Grenade,&#039;Amrf&#039;)
        
    set d.DistMax = MinMax(SquareRoot(x*x+y*y),MIN_RANGE,MAX_RANGE)
    set d.DistCur = 0
    
    set GData[COUNT] = d
    set COUNT = COUNT + 1
    
    if COUNT == 1 then
        call TimerStart(TIMER,PERIOD,true,function Execute)
    endif
endfunction

private function Cond takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call Run()
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(t,Condition(function Cond))
endfunction

endscope</i></i></i>

JASS:
scope BouncyGrenade initializer Init 
private keyword Data
globals 
    // Editable Globals
    private constant integer GRENADE_ID = &#039;gdum&#039;   
    private constant integer GRENADE_SFX1 = &#039;xdum&#039; 
    private constant integer GRENADE_SFX2 = &#039;xsfx&#039; 
    private constant integer SPELL_ID = &#039;BGid&#039;
    private constant integer MAX_BOUNCES = 2// How many bounces ?
    private constant real BOUNCE_DECREASE = 1.5// How much the range divises by per bounce
    private constant real MAX_RANGE = 1200// max range of grenade
    private constant real MIN_RANGE = 400// min range of grenade
    private constant real MAX_HEIGHT = 400// maximum height reachable
    private constant attacktype AT = ATTACK_TYPE_NORMAL // attack type..
    private constant damagetype DT = DAMAGE_TYPE_NORMAL//  damage type..
    private constant weapontype WT = WEAPON_TYPE_WHOKNOWS// Weapon type..
    private constant boolean DAMAGE_SELF = false// Can it hurt player units ?
    private constant boolean DAMAGE_ALLY = false// Can it hurt ally units ?
    // 
    
    // Don&#039;t change...
    private constant real PERIOD = 0.04 // Depends on what period you like ... don&#039;t moan at me if your .0001 period lags though...
    private constant real SPEED = 700 *PERIOD // How fast the grenade travels per second 
    private Data DATA// Temp Globals
    private real X //Temp Globals
    private real Y //Temp Globals
    private real DIST //Temp Globals
    private integer COUNT = 0
    private Data array GData
    private timer TIMER = CreateTimer()
    //You can now continue to the rest of the code... 
endglobals

private constant function AOE_Range takes integer lvl returns real
    return 150 + lvl*50 *1.0// Find AoE for Grenade explosion
endfunction

private function MinMax takes real val,real min,real max returns real
    if(min&gt;val) then
        return min
    elseif(val&gt;max) then
        return max
    else
        return val
    endif
endfunction

private function GetRange takes real range  returns real
    return range/BOUNCE_DECREASE// devise the range
endfunction

private function GetSpeed takes real range returns real
    return (range/MAX_RANGE)*SPEED*2// find speed with this &#039;formula&#039;
endfunction

private function GetHeight takes real range returns real// find the max height for that bounce
    return MinMax((range/MAX_RANGE)*MAX_HEIGHT*3,0,MAX_HEIGHT)
endfunction

private function GetParabolaZ takes real x,real d,real h returns real
    return 4 * h * x * (d - x) / (d * d)
endfunction // By AceHart

private function GetDamage takes integer level returns real
  return GetRandomReal (10,20) + 15 * (level-1)
endfunction// I guess Flare @ TH.net helped me with this <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite12" alt="o_O" title="Er... what?    o_O" loading="lazy" data-shortname="o_O" />...

private struct Data
    unit Grenade
    unit caster
    player p 
    real x
    real y
    real DistCur
    real DistMax
    real HeightMax
    real heightBase
    real baseH
    real aoe
    real facing
    integer level
    real speed
    real cos
    real sin
    integer bounce
    boolean justBounced
    
    static group GROUP = CreateGroup()
    
    static method Damage takes nothing returns boolean// To damage the targets near grenade
        local unit u = GetFilterUnit()
        local player p = GetOwningPlayer(u)
        local Data d = DATA
        local real damage = GetDamage(d.level)
        local boolean dmg = false
        
        if(DAMAGE_SELF and p==d.p)then// damage the caster&#039;s and player units ... if possible ?
            set dmg = true
        elseif(DAMAGE_ALLY and IsPlayerAlly(p,d.p))then// hurt allies ?
            set dmg = true
        elseif(IsPlayerEnemy(p,d.p))then// or just enemies...
            set dmg = true
        endif
        
        if(dmg) then
            call UnitDamageTarget(d.caster,u,damage,true,true,AT,DT,WT)// the damage..
        endif
        
        set u = null
        
        return false// return false to not add to global group..
    endmethod
    
    static method InCircle takes nothing returns boolean
        local destructable d = GetFilterDestructable()
        local real x = DATA.x - GetDestructableX(d)
        local real y = DATA.y - GetDestructableY(d)
        local real dist = x*x+y*y
        // Check if the destructable is in range of the grenade AoE
        set d = null
        
        if(dist&gt;DATA.aoe*DATA.aoe)then// or SquareRoot(dist) &gt; AOE_RANGE either workz...
            return false
        else
            return true
        endif        
    endmethod
    
    static method TreeDestroy takes nothing returns nothing 
        local destructable d = GetEnumDestructable()
        
        if IsDestructableTree(d) then
            call KillDestructable(d)// Destroy the tree if its a tree... 
        endif
        
        set d = null//nulls...
    endmethod
    
    method KillTrees takes real x,real y returns nothing
        local rect r = Rect(x-.aoe,y-.aoe,x+.aoe,y+.aoe)
        // find trees within a rect then find within a circle... 
        call EnumDestructablesInRect(r,Filter(function Data.InCircle),function Data.TreeDestroy)
        
        call RemoveRect(r)// nulls...
        set r = null
    endmethod
    
    method Explode takes nothing returns nothing
        call UnitApplyTimedLife(CreateUnit(.p,GRENADE_SFX1,.x,.y,0),0,1)
        call UnitApplyTimedLife(CreateUnit(.p,GRENADE_SFX2,.x,.y,0),0,1)// Add each SFX for a second
        
        set DATA = this
        // global data for enums ...
        call GroupEnumUnitsInRange(.GROUP,.x,.y,this.aoe,Filter(function Data.Damage))
        
        call .KillTrees(.x,.y)
        call .destroy()// kill trees then destroy self..
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ShowUnit(.Grenade,false)// Don&#039;t know wether this is neccesary ?
        call RemoveUnit(.Grenade)
    endmethod
    
    method NewBounce takes nothing returns nothing// set new data for that bounce
        local location l = GetUnitLoc(.Grenade)
        set .bounce = .bounce + 1// increase bounce
        set .DistMax = .DistCur/BOUNCE_DECREASE// find the distmax from the distance traveled / decrement 
        set .DistCur = 0// reset dist traveled
        set .HeightMax = GetHeight(.DistMax)// formula for height
        set .speed = GetSpeed(.DistMax)// find the speed for new bounce
        set .baseH = GetLocationZ(l)//find &#039;this&#039; bounce height again..
        call RemoveLocation(l)// leaks
        set l = null
    endmethod
endstruct

private function Execute takes nothing returns nothing
    local integer i = 0
    local Data d 
    local unit u 
    local boolean zhit // Z - Hit if anyone has a weird font like me which looks like an s ...
    local location l = Location(0,0)
    
    loop
        exitwhen i == COUNT
        
        set zhit = false
        
        set d = GData<i>
        set u = d.Grenade
            
        set d.DistCur = d.DistCur + d.speed
        set d.x = d.x + d.speed * d.cos// moving data...
        set d.y = d.y + d.speed * d.sin
        
        if IsInMapXY(d.x,d.y) == true then// grenade in map
        
            call SetUnitX(u,d.x)
            call SetUnitY(u,d.y)// move the grenade
            
            call SetUnitZ(u,d.baseH+GetParabolaZ(d.DistCur,d.DistMax,d.HeightMax))
            // set unit Z 
            call MoveLocation(l,d.x,d.y)
            
            if(R2I(GetUnitZ(u))==R2I(GetLocationZ(l)))then
                // R2I because it gives me &#039;0.001&#039; and stuff ... so it isn&#039;t &#039;equal&#039; unit height
                set zhit=true
            endif
            
            // I dont think the dist current can go higher than the max... but just in case
            if  zhit==true then
                if d.bounce==2 then
                
                    set COUNT = COUNT - 1
                    set GData<i> = GData[COUNT]// next instance
                            
                    call d.Explode()
                else
                    call d.NewBounce()// new bounce because of zhit
                endif
            else
                set i = i + 1// just increase the value to the next instance
            endif
            
        else
            set COUNT = COUNT - 1// change to next instance
            set GData<i> = GData[COUNT]
            call d.destroy()
        endif
    endloop
    
    call RemoveLocation(l)
    set u = null// leaks
    set l = null    
    
    if COUNT == 0 then
        call PauseTimer(TIMER)// pause
    endif
endfunction

private function Run takes nothing returns nothing
    local Data d = Data.create()
    local location l = GetSpellTargetLoc()
    local real x = GetLocationX(l)
    local real y = GetLocationY(l)
    local real dist = 0// locals for data
    local real facing
    
    call RemoveLocation(l)// do I need to &#039;set l = null&#039; ?
    
    set d.caster = GetTriggerUnit()
    set l = GetUnitLoc(d.caster)
    set d.baseH = GetLocationZ(l)
    
    call RemoveLocation(l)
    set l = null// leaks
    
    set d.caster = GetTriggerUnit()
    set d.p = GetOwningPlayer(d.caster)
    set d.x = GetUnitX(d.caster)// caster data
    set d.y = GetUnitY(d.caster)
    set d.level = GetUnitAbilityLevel(d.caster,SPELL_ID)
    set d.aoe = AOE_Range(d.level)
    set d.bounce = 0
    
    set x = x - d.x
    set y = y - d.y
    
    set facing = Atan2(y,x)
    set d.cos = Cos(facing)
    set d.sin = Sin(facing)// facing,,,
    set d.Grenade = CreateUnit(d.p,GRENADE_ID,d.x,d.y,facing)
    
    call UnitAddAbility(d.Grenade,&#039;Amrf&#039;)// I have to do this or else it goes buggy over raised terrain
    call UnitRemoveAbility(d.Grenade,&#039;Amrf&#039;)
       
    
    set d.DistMax = MinMax(SquareRoot(x*x+y*y)/Pow(BOUNCE_DECREASE,MAX_BOUNCES),0,MAX_RANGE)
    set d.HeightMax = GetHeight(d.DistMax)// height, dist , speed data
    set d.speed = GetSpeed(d.DistMax)
    //MinMax(SquareRoot(x*x+y*y),MIN_RANGE,MAX_RANGE)
    set d.DistCur = 0
    
    set GData[COUNT] = d
    set COUNT = COUNT + 1
    
    if COUNT == 1 then
        call TimerStart(TIMER,PERIOD,true,function Execute)
    endif
endfunction

private function Cond takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call Run()
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(t,Condition(function Cond))
endfunction

endscope</i></i></i>


ScreenShots...
Grenade_.png

Grenade_Bouncy_.png

I didn't want my old grenade here.. So I changed it to the new and improved version(s)

Changes -

New code,
Now works with terrain heights,
The bounce height + speed + distance depend on how far it travelled first time,
Options to hurt allies and player units,
Most things on the spell are configurable,
Added documentation for both spells ( Although it's just a brief task of how to use in another map ).

Special thx to Romek & TriggerHappy for helping me along the way ;)
 

Attachments

  • Grenade ( normal ).w3x
    43.6 KB · Views: 277

Romek

Super Moderator
Reaction score
964
JASS:
//DO NOT TOUCH THE BELOW   
    private constant integer MAX_BOUNCES = 3 // CHANGE THIS AND THE JUMPS ARNT BIG AFTER 3RD BOUNCE

:rolleyes:

JASS:
loop
        exitwhen i &gt; 7
        call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,Filter(function True))
        set i = i + 1
    endloop

What about players 8-15?

JASS:
return TRUE

TRUE is a constant boolean that equals true. You may as well use 'true' directly.

JASS:
set d.caster = GetTriggerUnit()
    set d.p = p
    set d.x = GetUnitX(GetTriggerUnit())
    set d.y = GetUnitY(GetTriggerUnit())
    set d.level = GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID)

Change all of those "GetTriggerUnit()" calls after the set to "d.caster". No need for all those function calls.

You could use a static global group instead of a dynamic one in the 'Explode' method.
Destroying groups can cause bugs of some sort (Not sure how exactly). But it'd be better to just create a group at initialization, and then clear it when necessary.

> local real r = AOE_RANGE
Has AOE_RANGE offended you, so you can't use it directly?
 

DrEvil

FCRI Associate!
Reaction score
111
Ok , I think i fixed all what you said.

Thank you very much Romek.

( Now time to play the waiting game )
 

Kenny

Back for now.
Reaction score
202
JASS:
call d.destroy()
set GData<i> = GData[COUNT]
set COUNT = COUNT - 1
// set i = i - 1 &lt;---

</i>


I thought you had to decrease the loop increment by 1 as well, otherwise you would miss an array slot somewhere?

Also the way you have set out you Execute function seems a little funny. Does the grenade just explode when it hits the map bounds? I thought it would be better to use SafeX/Y functions and just keep it going on the map bounds.

JASS:
if IsInMapXY(d.x,d.y) == true then
    call SetUnitX(u,d.x)
    call SetUnitY(u,d.y)
    call SetUnitFlyHeight(u,GetParabolaZ(d.DistCur,d.DistMax,d.HeightMax),0)
    if d.DistCur &gt;= d.DistMax then
        if d.bounce == MAX_BOUNCES then
            set GData<i> = GData[COUNT]
            set COUNT = COUNT - 1
            call d.Explode()
        else
            call d.NewBounce()
        endif
    endif
            
    set i = i + 1
else
    call d.destroy()
    set GData<i> = GData[COUNT]
    set COUNT = COUNT - 1
endif

could be:

if d.bounce == MAX_BOUNCES then
    call d.Explode()
    call d.destroy()
    set GData<i> = GData[COUNT]
    set COUNT = COUNT - 1
    set i = i - 1
else
    call SetUnitX(u,d.x) // &lt;---- You would be using SafeX/Y functions for this to stay in bounds.
    call SetUnitY(u,d.y)
    call SetUnitFlyHeight(u,GetParabolaZ(d.DistCur,d.DistMax,d.HeightMax),0)
    if d.DistCur &gt;= d.DistMax then
        call d.NewBounce()
    endif
            
    set i = i + 1
endif

</i></i></i>


Thats just rough, but it could be something like that, which in my opinion looks better and is neater.
 

DrEvil

FCRI Associate!
Reaction score
111
Im using a bool check to check wether its in map bounds. ( Which you cant see )
The .Explode() method does its stuff then destroys the struct after.

> I thought you had to decrease the loop increment by 1 as well, otherwise you would miss an array slot somewhere?

ok so i have struct 1 , 2 , 3 , 4 & 5 running.
im on integer i = 4 for struct 4 .
its time to explode ( so it does ).
then i move max_index to the current index.
so now struct array 4 = struct 5.
if i increase the loop then and there it will loop past array 5 and end.
therefore struct 5 will not run ( that timer tick ).

Well i think that is how its running.


JASS:
if d.bounce == MAX_BOUNCES then
    call d.Explode()
    call d.destroy()
    set GData<i> = GData[COUNT]
    set COUNT = COUNT - 1
    set i = i - 1
else
</i>


How would i know wether the distance is near the end ? ( But i know icould use another 'if ' to check , i get the point of that ).
^^^ Ok just read the edit. ^^^ I suppose i could do a safeX/Y check anyway.

If the grenade reaches out of bounds , it dissapears as if it went off the map( which it does ).

Thanks Kenny! :)
 

Romek

Super Moderator
Reaction score
964
JASS:
if i == &#039;FTtw&#039; then

I presume this doesn't work for any other trees then? =|
And the one tree isn't even configurable.
 

Vestras

Retired
Reaction score
249
Hey, to check if the destructable is a tree you can just use this:
JASS:
library DestructableLib initializer Initialization
//* ============================================================================ *
//* Made by PitzerMike                                                           *
//*                                                                              *
//* I made this to detect if a destructable is a tree or not. It works not only  *
//* for the standard trees but also for custom destructables created with the    *
//* object editor. It uses a footie as a dummy with the goul&#039;s harvest ability.  *
//* The dummy ids can be changed though. I also added the IsDestructableDead     *
//* function for completeness.                                                   *
//* ============================================================================ *

globals
    private constant integer DUMMY_UNIT_ID = &#039;hfoo&#039; // footman
    private constant integer HARVEST_ID    = &#039;Ahrl&#039; // ghouls harvest
    private constant player OWNING_PLAYER  = Player(15)
    
    private unit dummy = null
endglobals

function IsDestructableDead takes destructable dest returns boolean
    return GetDestructableLife(dest) &lt;= 0.405
endfunction

function IsDestructableTree takes destructable dest returns boolean
    local boolean result = false
    if (dest != null) then
        call PauseUnit(dummy, false)
        set result = IssueTargetOrder(dummy, &quot;harvest&quot;, dest)
        call PauseUnit(dummy, true) // stops order
    endif
    return result
endfunction

private function Initialization takes nothing returns nothing
    set dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0.0, 0.0, 0.0)
    call ShowUnit(dummy, false) // cannot enumerate
    call UnitAddAbility(dummy, HARVEST_ID)
    call UnitAddAbility(dummy, &#039;Aloc&#039;) // unselectable, invulnerable
    call PauseUnit(dummy, true)
endfunction
endlibrary
 

DrEvil

FCRI Associate!
Reaction score
111
Thanks vestras , I was looking for something like that , otherwise there would be many comparisons for trees.

Apart from how to improove it , any comments : on the spell itself ?
 

Viikuna

No Marlo no game.
Reaction score
265
Looks solid and good. You might wanna add this though:
JASS:
    if COUNT == 0 then
        call PauseTimer(TIMER)
    endif


edit. It goes to function Execute, just after endloop
 

DrEvil

FCRI Associate!
Reaction score
111
Thanks .

> Looks solid and good.

WooHoo - First vJASS spell and its good :)
 

Trollvottel

never aging title
Reaction score
262
JASS:
    //DO NOT TOUCH THE BELOW   
    private constant real PERIOD = 0.04 // Do not change - For smooth speed and non-lag
    private constant real SPEED = 400 * PERIOD
    private integer LEVEL = 0
    private unit CASTER 
    private integer COUNT = 0
    private Data array GData
    private timer TIMER = CreateTimer()
    //DO NOT TOUCH THE ABOVE


why did you even put it there? put it below the struct declaration...


JASS:
    set dist = SquareRoot(x*x+y*y)
    set dist = RMaxBJ(dist,MIN_RANGE)
    set dist = RMinBJ(dist,MAX_RANGE)


->

JASS:
    set dist = RMinBJ(RMaxBJ(SquareRoot(x*x+y*y),MIN_RANGE),MAX_RANGE)


looks better imo and uses less operations.
 

DrEvil

FCRI Associate!
Reaction score
111
Updated some bits here and there ...

Seems no leaks :) , no lag :D ( for me ) , its a grenade :eek: ( :confused: )
 

DrEvil

FCRI Associate!
Reaction score
111
If someone could review my spell again.. I would be happy :)

And a [Spellpack] title... because there are two.. :)

Totally new spell... No more leaks or noobish-ness :D
 

Laiev

Hey Listen!!
Reaction score
188
description of skill?
mui/mpi?
gui/jass <i know, you post the code but some useful information is good>?
screen shot?
:rolleyes:
 

DrEvil

FCRI Associate!
Reaction score
111
Added description, vJASS, leakless, lagless, screenshots :)

Well 2nd time lucky ? :D

Any comments ? Anyone ? :)
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
GetSpellTargetLoc -> GetSpellTargetX, GetSpellTargetY
GetUnitLoc -> GetUnitX, GetUnitY

Use linked list on handler function, they are faster than struct array and does not need +1.
 

DrEvil

FCRI Associate!
Reaction score
111
I don't know how to use a linked list... And I'l change GetSpellTargetLoc... GetUnitLoc is used for GetLocationZ, for the unit's full Z.

What's so wrong about a struct loop anyway ? What's so good about a linked list ?
Wow... performance speed ? ;)

EDIT : Anyway I remove and null the location... So I didn't see a problem there xD
 

Romek

Super Moderator
Reaction score
964
There is nothing wrong with a struct array.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top