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: 275

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
248
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
  • WildTurkey WildTurkey:
    is there a stephen green in the house?
    +1
  • The Helper The Helper:
    What is up WildTurkey?
  • The Helper The Helper:
    Looks like Google fixed whatever mistake that made the recipes on the site go crazy and we are no longer trending towards a recipe site lol - I don't care though because it motivated me to spend alot of time on the site improving it and at least now the content people are looking at is not stupid and embarrassing like it was when I first got back into this like 5 years ago.
  • The Helper The Helper:
    Plus - I have a pretty bad ass recipe collection now! That section of the site is 10 thousand times better than it was before
  • The Helper The Helper:
    We now have a web designer at my job. A legit talented professional! I am going to get him to redesign the site theme. It is time.
  • Varine Varine:
    I got one more day of community service and then I'm free from this nonsense! I polished a cop car today for a funeral or something I guess
  • Varine Varine:
    They also were digging threw old shit at the sheriff's office and I tried to get them to give me the old electronic stuff, but they said no. They can't give it to people because they might use it to impersonate a cop or break into their network or some shit? idk but it was a shame to see them take a whole bunch of radios and shit to get shredded and landfilled
  • The Helper The Helper:
    whatever at least you are free
  • Monovertex Monovertex:
    How are you all? :D
    +1
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top