Spellpack Overheat, Hailstorm and Summon Abyss!

Vestras

Retired
Reaction score
248
Vestras' Entry for the Spell Olympic Contest at Wc3campaigns.



Contains:

- Overheat (Fire)
- Hailstorm (Frost)
- Summon Abyss (Darkness)

They do all require HAIL and NewGen.
They are all completely MUI.
They are all in vJASS.

Overheat's Code:
JASS:
scope Overheat

//! runtextmacro HAIL_CreateProperty("Data","integer","private")

// C O N F I G U R A T I O N

globals
            private constant integer  SPELLID  = 'A000'
            // The spell raw code.
            private constant integer  DUMMYID  = 'h001'
            // The dummy raw code.
            private constant real       RANGE  = 800
            // The range of the spell.
            private constant real    DMGRANGE  = 170
            // The range in which units are damaged.
            private constant real       SPEED  = 1600
            // The movement speed of the wave. This is divided by the timer interval. The higher speed, the less concentrated is the fx.
            private constant real    INTERVAL  = 0.03
            // The timer interval. This is periodic time which the timer is ran.
            private constant real   MOVESPEED  = SPEED * INTERVAL
            // The movespeed of the wave. I don't think you should edit that one.
            private constant string    LAVAFX  = "Abilities\\Spells\\Other\\Volcano\\VolcanoMissile.mdl"
            // The path of the lava fx. (The actual wave missle.)
            private constant string    EXPLFX  = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
            // The path of the explosion fx. (The fx that comes when a unit is hit.)
            private constant string UBERSPLAT  = "DHSB"
            // The path of the ubersplat at the end of the wave.
            private constant string ATCHPOINT   = "chest"
            // The attach point of the model.
            private constant boolean  DOSPLAT  = true
            // Create the ubersplat or not?
            private constant boolean  PRELOAD  = true
            // Allow the trigger to preload? That will cause less lag when using the spell.
            private constant damagetype DTYPE  = DAMAGE_TYPE_FIRE
            // The damage type of the damage.
endglobals

private function DAMAGE takes unit u returns real
 // The damage calculate function. You can always add more levels
 // by adding elseif lvl==yourlevel then
 // return yourdamage
 // endif
 local integer lvl=GetUnitAbilityLevel(u,SPELLID)
 
   if lvl==1 then
     return 65.
   elseif lvl==2 then
     return 85.
   elseif lvl==3 then
     return 110.
   endif
 
 return 0. // Safety
endfunction

// The neccesary globals;

globals
 private player Owner
 private unit CastUnit
 private boolean done
 private group SafeGroup=CreateGroup()
endglobals

// The struct;

  private struct OverData
    real cx
    real cy
    real px
    real py
    real angle
    real trange
    timer tmr
    ubersplat splat
    unit caster
    unit dummy
      private method onDestroy takes nothing returns nothing
        call ResetData(.tmr)
        call DestroyTimer(.tmr)
        set .caster=null
        set .tmr=null
        set .cx=0
        set .cy=0
        set .px=0
        set .py=0
        set .angle=0
        set .trange=0
        set CastUnit=null
        set Owner=null
        set done=false
     endmethod
  endstruct
  
// End of the struct.

// And finaly, the real code;

private constant function Conditions takes nothing returns boolean
   return GetSpellAbilityId()==SPELLID
endfunction

private function FilterActs takes nothing returns boolean
   return IsUnitEnemy(GetFilterUnit(),Owner)==true
endfunction

private function EndActs takes nothing returns nothing
 local OverData d=GetData(GetExpiredTimer())
  call FinishUbersplat(d.splat)
  call DestroyUbersplat(d.splat)
  set d.splat=null
 call DestroyTimer(GetExpiredTimer())
endfunction

private function GroupDamage takes nothing returns nothing
   local unit s=GetEnumUnit()
   local real x=GetUnitX(s)
   local real y=GetUnitY(s)
    if IsUnitEnemy(s,GetOwningPlayer(CastUnit))==true and GetWidgetLife(s)>.405 and IsUnitType(s,UNIT_TYPE_GROUND)==true then
     if IsUnitInGroup(s,SafeGroup) then
     else
       call UnitDamageTarget(CastUnit,s,DAMAGE(CastUnit),false,true,ATTACK_TYPE_NORMAL,DTYPE,null)
       call DestroyEffect(AddSpecialEffect(EXPLFX,x,y))
       call GroupAddUnit(SafeGroup,s)
    endif
   endif
    if done==true then
      call GroupRemoveUnit(SafeGroup,s)
    endif
   set s=null
endfunction

private function TimerActs takes nothing returns nothing
 local OverData d=GetData(GetExpiredTimer())
 local real x
 local real y
 local real x1=GetUnitX(d.dummy)
 local real y1=GetUnitY(d.dummy)
 local group grp=CreateGroup()
 local unit s
 local unit c
 
  local real px=x1+Cos(d.angle)*MOVESPEED
  local real py=y1+Sin(d.angle)*MOVESPEED
  set d.trange=d.trange+MOVESPEED
  
    set c=CreateUnit(GetOwningPlayer(d.caster),DUMMYID,px,py,d.angle)
    call DestroyEffect(AddSpecialEffectTarget(LAVAFX,c,ATCHPOINT))
    call UnitApplyTimedLife(c,'BTLF',5)
    call SetUnitX(d.dummy,px)
    call SetUnitY(d.dummy,py)
    call GroupEnumUnitsInRange(grp,px,py,DMGRANGE,Filter(function FilterActs))
    call ForGroup(grp,function GroupDamage)
    
        if d.trange>=RANGE then
          set done=true
          if DOSPLAT==true then
           set d.splat=CreateUbersplat(d.px,d.py,UBERSPLAT,255,255,255,PercentTo255(100.0-0),false,false)
            call SetUbersplatRenderAlways(d.splat,true)
            call ShowUbersplat(d.splat,true)
           call TimerStart(CreateTimer(),0.25,false,function EndActs)
          endif
          call d.destroy()
        endif
        
    call DestroyGroup(grp)
    set grp=null
    set c=null
    set s=null
endfunction

private function Actions takes nothing returns nothing
 local OverData d=OverData.create()
 local location l=GetSpellTargetLoc()
 local real x1=GetUnitX(GetTriggerUnit())
 local real y1=GetUnitY(GetTriggerUnit())
 local real x2=GetLocationX(l)
 local real y2=GetLocationY(l)
 
  set d.caster=GetTriggerUnit()
  set d.tmr=CreateTimer()
  set d.cx=GetUnitX(d.caster)
  set d.cy=GetUnitY(d.caster)
  set d.angle=Atan2(y2-y1,x2-x1)
  set d.px=d.cx+MOVESPEED*Cos(d.angle*bj_DEGTORAD)
  set d.py=d.cy+MOVESPEED*Sin(d.angle*bj_DEGTORAD)
  set Owner=GetOwningPlayer(d.caster)
  set CastUnit=d.caster
  set done=false
  
   call GroupClear(SafeGroup)
   set d.dummy=CreateUnit(GetOwningPlayer(d.caster),DUMMYID,d.px,d.py,0)
   call SetData(d.tmr,d)
   call TimerStart(d.tmr,INTERVAL,true,function TimerActs)
   
 call RemoveLocation(l)
 set l=null
endfunction

//===========================================================================
function InitTrig_Overheat takes nothing returns nothing
    local trigger t=CreateTrigger()
    local unit d=null
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
      
      if PRELOAD==true then
        set d=CreateUnit(Player(0),DUMMYID,0,0,0)
        call UnitAddAbility(d,SPELLID)
        call UnitRemoveAbility(d,SPELLID)
        call RemoveUnit(d)
          call Preload(LAVAFX)
          call Preload(EXPLFX)
          call Preload(UBERSPLAT)
      endif
    
    if d!=null then
      set d=null
    endif
endfunction

endscope

Hailstorm's Code:
JASS:
scope Hailstorm

//! runtextmacro HAIL_CreateProperty("Data","integer","private")

// C O N F I G U R A T I O N

globals
            private constant integer  SPELLID   = 'A001'
            // The spell raw code.
            private constant integer  DUMMYID   = 'h000'
            // The dummy raw code.
            private constant real         AOE   = 300
            // The AoE in which the hails will appear.
            private constant real       RANGE   = 10
            // The range the hails needs to be in before the damage takes place.
            private constant real    INTERVAL   = 0.01
            // The timer interval. This is periodic time which the timer is ran. 
            private real             DECREASE   = 5
            // The fly height which is decreased of the hails. 
            // This one is not constant, because that if it were, it would be impossible to reset it during onDestroy. If it weren't reset, the speed would increase and increase for each time casted.
            private constant real   TRGHEIGHT   = 300
            // The fly height which makes the hails search towards the target.
            private constant real  TRAVELDIST   = 5
            // The travel distance when the hails will search towards the target.
            private constant string    HAILFX   = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
            // The path of the hail model.
            private constant string    TAILFX   = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
            // The path of the "tail" on the hails.
            private constant string  IMPACTFX   = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
            // The path of the fx which comes on impact from the hails.
            private constant string ATCHPOINT   = "chest"
            // The attach point of the model.
            private constant damagetype DTYPE   = DAMAGE_TYPE_COLD
            // The damage type of the damage.
            private constant boolean  PRELOAD   = true
            // Allow the trigger to preload? It will cause less lag when casting the spell.
endglobals

private function DAMAGE takes unit u returns real
 // The damage calculate function. You can always add more levels
 // by adding elseif lvl==yourlevel then
 // return yourdamage
 // endif
 local integer lvl=GetUnitAbilityLevel(u,SPELLID)
 
   if lvl==1 then
     return 15.
   elseif lvl==2 then
     return 20.
   elseif lvl==3 then
     return 25.
   endif
 
 return 0. // Safety
endfunction

private function HAILS takes unit u returns integer
 // The number of hails calculate function. You can always add more levels
 // by adding elseif lvl==yourlevel then
 // return yourhails
 // endif
 local integer lvl=GetUnitAbilityLevel(u,SPELLID)
 
   if lvl==1 then
     return 6
   elseif lvl==2 then
     return 12
   elseif lvl==3 then
     return 18
   endif
 
 return 0 // Safety
endfunction

// The neccesary globals

globals
 private group SafeHails=CreateGroup()
endglobals

// The struct;

 private struct HailData
    unit caster
    unit target
    real tx
    real ty
    group dummies
    timer tmr
    trigger trg
    trigger trg2
    boolean done
       
       private method onDestroy takes nothing returns nothing
         set DECREASE=DECREASE
         call ResetData(.tmr)
         call ResetData(.trg)
         call ResetData(.trg2)
         call DestroyGroup(.dummies)
         call DestroyTimer(.tmr)
         call DestroyTrigger(.trg)
         call DestroyTrigger(.trg2)
         set .caster=null
         set .target=null
         set .dummies=null
         set .tmr=null
         set .trg=null
         set .trg2=null
       endmethod
  endstruct

// End of the struct

// And finaly, the real code;

private constant function Conditions takes nothing returns boolean
   return GetSpellAbilityId()==SPELLID
endfunction

private function SelectActs takes nothing returns boolean
 local HailData d=GetData(GetTriggeringTrigger())
   if IsUnitInGroup(GetTriggerUnit(),d.dummies)==true then
    if GetLocalPlayer()==GetOwningPlayer(GetTriggerUnit()) then
     call SelectUnit(GetTriggerUnit(),false)
    endif
   endif
 return false
endfunction

private function InRangeActs takes nothing returns boolean
 local HailData d=GetData(GetTriggeringTrigger())
 local unit u=GetEnteringUnit()
 
   call UnitDamageTarget(d.caster,d.target,DAMAGE(d.caster),false,true,ATTACK_TYPE_NORMAL,DTYPE,null)
   call DestroyEffect(AddSpecialEffect(IMPACTFX,d.tx,d.ty))
   call RemoveUnit(u)
   
 set u=null
 return false
endfunction

private function RemoveActs takes nothing returns nothing
 local real x=GetUnitX(GetEnumUnit())
 local real y=GetUnitY(GetEnumUnit())
  call DestroyEffect(AddSpecialEffect(IMPACTFX,x,y))
  call RemoveUnit(GetEnumUnit())
endfunction

private function SearchActs takes nothing returns nothing
  local HailData d=GetData(SafeHails)
  local real x1=GetUnitX(GetEnumUnit())
  local real y1=GetUnitY(GetEnumUnit())
  local real angle
  local real x2
  local real y2
  set d.tx=GetUnitX(d.target)
  set d.ty=GetUnitY(d.target)
  set angle=bj_RADTODEG*Atan2(d.ty-y1,d.tx-x1)
  set x2=x1+TRAVELDIST*Cos(angle*bj_DEGTORAD)
  set y2=y1+TRAVELDIST*Sin(angle*bj_DEGTORAD)
  
   if CountUnitsInGroup(d.dummies)<2 and d.done!=true then
    call ForGroup(d.dummies,function RemoveActs)
    call DestroyTimer(GetExpiredTimer())
    set d.done=true
     call d.destroy()
    return
   endif
  
   if GetWidgetLife(d.target)<.405 and d.done!=true then
    call ForGroup(d.dummies,function RemoveActs)
    call DestroyTimer(GetExpiredTimer())
    set d.done=true
     call d.destroy()
    return
   endif
  
    call SetUnitX(GetEnumUnit(),x2)
    call SetUnitY(GetEnumUnit(),y2)
endfunction

private function GroupHeight takes nothing returns nothing
 local unit s=GetEnumUnit()
 local real h=GetUnitFlyHeight(s)-DECREASE
  call SetUnitFlyHeight(s,h,0)
   if GetUnitFlyHeight(s)<=TRGHEIGHT then
    call GroupAddUnit(SafeHails,s)
   endif
 set s=null
endfunction

private function TimerActs takes nothing returns nothing
  local HailData d=GetData(GetExpiredTimer())
  call SetData(SafeHails,d)
  call ForGroup(d.dummies,function GroupHeight)
  call ForGroup(SafeHails,function SearchActs)
endfunction

private function Actions takes nothing returns nothing
  local HailData d=HailData.create()
  local integer i=1
  local unit n=GetSpellTargetUnit()
  local real x1=GetUnitX(n)
  local real y1=GetUnitY(n)
  local rect r=Rect(x1-AOE*0.5,y1-AOE*0.5,x1+AOE*0.5,y1+AOE*0.5)
  local real f
  local real x2
  local real y2
  local unit u
  set d.caster=GetTriggerUnit()
  set d.target=GetSpellTargetUnit()
  set d.dummies=CreateGroup()
  set d.tmr=CreateTimer()
  set d.trg=CreateTrigger()
  set d.trg2=CreateTrigger()
  set d.tx=GetUnitX(d.target)
  set d.ty=GetUnitY(d.target)
  set d.done=false
  call TriggerRegisterUnitInRange(d.trg,d.target,RANGE,null)
  call TriggerAddCondition(d.trg,Condition(function InRangeActs))
  call SetData(d.trg,d)
  call TriggerRegisterPlayerUnitEvent(d.trg2,GetOwningPlayer(d.caster),EVENT_PLAYER_UNIT_SELECTED,null)
  call TriggerAddCondition(d.trg2,Condition(function SelectActs))
  call SetData(d.trg2,d)
  
   loop
    exitwhen i>HAILS(d.caster)
     set f=GetRandomReal(0,360)
      set x2=GetRandomReal(GetRectMinX(r),GetRectMaxX(r))
       set y2=GetRandomReal(GetRectMinY(r),GetRectMaxY(r))
       set u=CreateUnit(GetOwningPlayer(d.caster),DUMMYID,x2,y2,f)
      call GroupAddUnit(d.dummies,u)
      call SetUnitFlyHeight(u,1000,0)
     call AddSpecialEffectTarget(HAILFX,u,ATCHPOINT)
     call AddSpecialEffectTarget(TAILFX,u,ATCHPOINT)
    set i=i+1
   endloop
  
   call TimerStart(d.tmr,INTERVAL,true,function TimerActs)
   call SetData(d.tmr,d)
   
  call RemoveRect(r)
  set r=null
  set n=null
  set u=null
endfunction

//===========================================================================
function InitTrig_Hailstorm takes nothing returns nothing
    local trigger t=CreateTrigger()
    local unit d=null
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
    call GroupClear(SafeHails)
    
      if PRELOAD==true then
        set d=CreateUnit(Player(0),DUMMYID,0,0,0)
        call UnitAddAbility(d,SPELLID)
        call UnitRemoveAbility(d,SPELLID)
        call RemoveUnit(d)
          call Preload(HAILFX)
          call Preload(TAILFX)
          call Preload(IMPACTFX)
      endif
    
    if d!=null then
      set d=null
    endif
endfunction

endscope

Summon Abyss' Code:
JASS:
scope SummonAbyss

//! runtextmacro HAIL_CreateProperty("Data","integer","private")

globals
            private constant integer  SPELLID   = 'A003'
            // The spell raw code.
            private constant integer  DUMMYID   = 'h002'
            // The dummy raw code.
            private constant integer    BANID   = 'A004'
            // The banish raw code.
            private constant real       RANGE   = 150
            // The range the opponents needs to be in before the damage and banish takes place.
            private constant real    INTERVAL   = 0.01
            // The timer interval. This is periodic time which the timer is ran. 
            private real           TRAVELDIST   = 5
            // The travel distance which the dragon will move each INTERVAl seconds.
            // Yes, it is intended that it isn't constant.
            private constant real  LIVINGTIME   = 1.25
            // The seconds the dragon can live.
            private constant string    DARKFX   = "units\\creeps\\NetherDragon\\NetherDragon.mdl"
            // The path of the dragon model.
            private constant string    EXPLFX   = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
            // The path of the explosion.
            private constant string  IMPACTFX   = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
            // The path of the fx which comes on impact from the hails.
            private constant string     ORDER   = "banish"
            // The order string. Can be useful if you want the targets to be stunned instead of banished.
            private constant string ATCHPOINT   = "chest"
            // The attach point of the model.
            private constant damagetype DTYPE   = DAMAGE_TYPE_DEATH
            // The damage type of the damage.
            private constant boolean  PRELOAD   = true
            // Allow the trigger to preload? It will cause less lag when casting the spell.
endglobals

private function DAMAGE takes unit u returns real
 // The damage calculate function. You can always add more levels
 // by adding elseif lvl==yourlevel then
 // return yourdamage
 // endif
 local integer lvl=GetUnitAbilityLevel(u,SPELLID)
 
   if lvl==1 then
     return 60.
   elseif lvl==2 then
     return 80.
   elseif lvl==3 then
     return 100.
   endif
 
 return 0. // Safety
endfunction

// The neccesary globals;

globals
endglobals

// The struct;

  private struct AbyssData
    unit caster
    unit dummy
    real angle
    timer tmr
    timer tmr2
    trigger trg
    
      private method onDestroy takes nothing returns nothing
        set TRAVELDIST=TRAVELDIST
        call ResetData(.tmr)
        call ResetData(.tmr2)
        call ResetData(.trg)
        call DestroyTimer(.tmr)
        call DestroyTimer(.tmr2)
        call DestroyTrigger(.trg)
        set .caster=null
        set .dummy=null
        set .tmr=null
        set .tmr2=null
        set .trg=null
      endmethod
  endstruct
  
// End of the struct.

// And finaly, the real code.

private constant function Conditions takes nothing returns boolean
   return GetSpellAbilityId()==SPELLID
endfunction

private function InRangeActs takes nothing returns boolean
 local AbyssData d=GetData(GetTriggeringTrigger())
 local real dx=GetUnitX(d.dummy)
 local real dy=GetUnitY(d.dummy)
 local unit u
  if IsUnitEnemy(GetTriggerUnit(),GetOwningPlayer(d.caster))==true then
   call UnitDamageTarget(d.caster,GetTriggerUnit(),DAMAGE(d.caster),false,true,ATTACK_TYPE_NORMAL,DTYPE,null)
   call DestroyEffect(AddSpecialEffectTarget(IMPACTFX,GetTriggerUnit(),"origin"))
   set u=CreateUnit(GetOwningPlayer(d.dummy),DUMMYID,dx,dy,0)
     call UnitAddAbility(u,BANID)
    call IssueTargetOrder(u,ORDER,GetTriggerUnit())
   call UnitApplyTimedLife(u,'BTLF',2)
  endif
 set u=null
 return false
endfunction

private function KillActs takes nothing returns nothing
 local AbyssData d=GetData(GetExpiredTimer())
 local real dx=GetUnitX(d.dummy)
 local real dy=GetUnitY(d.dummy)
  call DestroyEffect(AddSpecialEffect(EXPLFX,dx,dy))
  call RemoveUnit(d.dummy)
 call d.destroy()
endfunction

private function TimerActs takes nothing returns nothing
  local AbyssData d=GetData(GetExpiredTimer())
  local real dx=GetUnitX(d.dummy)
  local real dy=GetUnitY(d.dummy)
  local real px=dx+TRAVELDIST*Cos(d.angle*bj_DEGTORAD)
  local real py=dy+TRAVELDIST*Sin(d.angle*bj_DEGTORAD)

    call SetUnitPosition(d.dummy,px,py)
endfunction

private function Actions takes nothing returns nothing
  local AbyssData d=AbyssData.create()
  local location l=GetSpellTargetLoc()
  local real x1
  local real y1
  local real x2
  local real y2
  set d.caster=GetTriggerUnit()
  set d.tmr=CreateTimer()
  set d.trg=CreateTrigger()
  set d.tmr2=CreateTimer()
  set x1=GetUnitX(d.caster)
  set y1=GetUnitY(d.caster)
  set x2=GetLocationX(l)
  set y2=GetLocationY(l)
  set d.angle=bj_RADTODEG*Atan2(y2-y1,x2-x1)
  
    set d.dummy=CreateUnit(GetOwningPlayer(d.caster),DUMMYID,x1,y1,GetUnitFacing(d.caster))
    call AddSpecialEffectTarget(DARKFX,d.dummy,ATCHPOINT)
    call TimerStart(d.tmr,INTERVAL,true,function TimerActs)
    call SetData(d.tmr,d)
    call TimerStart(d.tmr2,LIVINGTIME,false,function KillActs)
    call SetData(d.tmr2,d)
    call TriggerRegisterUnitInRange(d.trg,d.dummy,RANGE,null)
    call TriggerAddCondition(d.trg,Condition(function InRangeActs))
    call SetData(d.trg,d)
    
  call RemoveLocation(l)
  set l=null
endfunction

//===========================================================================
function InitTrig_SummonAbyss takes nothing returns nothing
    local trigger t=CreateTrigger()
    local unit d=null
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
    
     if PRELOAD==true then
        set d=CreateUnit(Player(0),DUMMYID,0,0,0)
        call UnitAddAbility(d,SPELLID)
        call UnitRemoveAbility(d,SPELLID)
        call RemoveUnit(d)
          call Preload(DARKFX)
          call Preload(EXPLFX)
          call Preload(IMPACTFX)
      endif
    
    if d!=null then
      set d=null
    endif
endfunction

endscope

Enjoy.

Small notice; to those who thinks that Flare's Conflagrate and my Overheat looks very much the same; yes, but the funny thing is, that I started to work on that spell before Flare had submitted his. Period.
 

Flare

Stops copies me!
Reaction score
662
Overheat: A shockwave with fancy SFX, nothing special tbh :\

Hailstorm: Would be way nicer if the projectiles went straight down and dealt small-AoE damage, rather than just magically stopping and chasing down the target

Summon Abyss: Why does the dragon have a fiery explosion? That's not very dark...

JASS:
    if d!=null then
      set d=null
    endif

If PRELOAD is false, I'm 90% sure that you would get an uninitialized variable error, if Grimoire was enabled (anyone with WC3 v1.21b care to test it?) since you are comparing the value of the variable (which wouldn't have been initialized if PRELOAD was false) to null, but there would be no value to compare

JASS:
  call TriggerRegisterUnitInRange(d.trg,d.target,RANGE,null)

Nulling boolexpr arguments causes a leak I believe

Your indentation is...
JASS:
   loop
    exitwhen i>HAILS(d.caster)
     set f=GetRandomReal(0,360)
      set x2=GetRandomReal(GetRectMinX(r),GetRectMaxX(r))
       set y2=GetRandomReal(GetRectMinY(r),GetRectMaxY(r))
       set u=CreateUnit(GetOwningPlayer(d.caster),DUMMYID,x2,y2,f)
      call GroupAddUnit(d.dummies,u)
      call SetUnitFlyHeight(u,1000,0)
     call AddSpecialEffectTarget(HAILFX,u,"chest")
     call AddSpecialEffectTarget(TAILFX,u,"chest")
    set i=i+1
   endloop

bow-shaped?

JASS:
            // The fly height which is decreased of the hails. 
            // This one is not constant, because that if it were, it would be impossible to reset it during onDestroy. If it weren't reset, the speed would increase and increase for each time casted.

Hmmm... just SetUnitFlyHeight (whichUnit, GetUnitFlyHeight (whichUnit) - 5, 0)
<Or whatever the parameter order is>

JASS:
         set DECREASE=DECREASE

That's supposed to do what?

And why the f*ck are you attaching to the group? Just use a global struct var i.e.
JASS:
private keyword StructName //Somewhere near the top

globals
  private StructName globaldata //obviously shorten the name
endglobals

struct StructName
...
endstruct


Then set globaldata = localstructdata before the GroupEnum/ForGroup

Code:
local rect r=Rect(x1-AOE*0.5,y1-AOE*0.5,x1+AOE*0.5,y1+AOE*0.5)
The 0.5 should be a configurable

You're not pausing your timers before destroying them

JASS:
  set d.px=x1+Cos(d.angle)*MOVESPEED
  set d.py=y1+Sin(d.angle)*MOVESPEED

1) Why are you using a struct member for that?
2) You should set Cos (d.angle) and Sin (d.angle) to a pair of struct members, as Sin and Cos are math-heavy functions

EDIT: Yup, there's more
JASS:
  set x2=x1+500*Cos(GetUnitFacing(d.caster)*bj_DEGTORAD)

That 500 should be configurable

JASS:
    call AddSpecialEffectTarget(DARKFX,d.dummy,&quot;chest&quot;)

The "chest" should also be configurable

JASS:
    call TriggerRegisterUnitInRange(d.trg,d.dummy,RANGE,null)

Nulling another boolexpr

JASS:
    call IssueTargetOrder(u,&quot;banish&quot;,GetTriggerUnit())

"banish" should be configurable - what if a person wants to stun the targets, or cast Cripple, or do anything other than Banish?

JASS:
            private real           TRAVELDIST   = 5
            // The travel distance which the dragon will move each INTERVAl seconds.
            // Yes, it is intended that it isn&#039;t constant.

Same as what I said earlier

JASS:
        set TRAVELDIST=TRAVELDIST

Same as above - you're just setting it to it's current value (which doesn't change it at all)

EDIT AGAIN: But wait, there's more!
The ubersplat at the end of Overheat looks terrible IMO - a line of fairly small explosions, then BOOM! a big crater appears from nowhere?

JASS:
            private constant real    INTERVAL  = 0.01

0.03 to 0.035 is sufficient for the spell - 0.01 is liable to cause lag, especially with multiple instances running

JASS:
private function GroupDamage takes nothing returns nothing
   local unit s=GetEnumUnit()
   local real x=GetUnitX(s)
   local real y=GetUnitY(s)
    if IsUnitEnemy(s,GetOwningPlayer(CastUnit))==true and GetWidgetLife(s)&gt;.405 and IsUnitType(s,UNIT_TYPE_GROUND)==true then
     if IsUnitInGroup(s,SafeGroup) then
      set s=null
     else
       call UnitDamageTarget(CastUnit,s,DAMAGE(CastUnit),false,true,ATTACK_TYPE_NORMAL,DTYPE,null)
       call DestroyEffect(AddSpecialEffect(EXPLFX,x,y))
       call GroupAddUnit(SafeGroup,s)
      set s=null
    endif
   endif
    if done==true then
      call GroupRemoveUnit(SafeGroup,s)
    endif
endfunction

If s is an ally of the caster, you're causing a leak (since the first if will return false, and your nulling takes place within that if) - null it at the very end, and remove the nulling lines from within the if
 

Vestras

Retired
Reaction score
248
aw, I hoped to get some rep from this and then I get... leaks...

> Overheat: A shockwave with fancy SFX, nothing special tbh :\

But yours is too then?

> Hailstorm: Would be way nicer if the projectiles went straight down and dealt small-AoE damage, rather than just magically stopping and chasing down the target

That's a matter of taste.

> Summon Abyss: Why does the dragon have a fiery explosion? That's not very dark...

What dark explosions is there?

> If PRELOAD is false, I'm 90% sure that you would get an uninitialized variable error, if Grimoire was enabled (anyone with WC3 v1.21b care to test it?) since you are comparing the value of the variable (which wouldn't have been initialized if PRELOAD was false) to null, but there would be no value to compare

If I do
JASS:
local unit d

then it's =null right? And when I preload, I do
JASS:
set d=CreateUnit(...)

so that will make it non-null. Then I check if it's not equal to null, and if it is, I null it. KABOOM!! No leaks.

> Nulling boolexpr arguments causes a leak I believe

Everybody are doing it, even Blizzard.

> Your indentation is... bow shaped?

FTW!!! :D

> Hmmm... just SetUnitFlyHeight (whichUnit, GetUnitFlyHeight (whichUnit) - 5, 0)

It's for the users.. So they can configure how the spell should be. If higher, they fall quicklier, if less, slower.

> That's supposed to do what?

Without it, there is some strange bug. Without it, the falling speed becomes more and more for each cast.. Strangly..

> And why the f*ck are you attaching to the group? Just use a global struct var i.e.

Why not? And I cannot figure out that global struct thing.

> The 0.5 should be a configurable

Why?

> You're not pausing your timers before destroying them

Should I?

> 1) Why are you using a struct member for that?
2) You should set Cos (d.angle) and Sin (d.angle) to a pair of struct members, as Sin and Cos are math-heavy functions

1) Dunno.
2) Okay, I'll do that.

EDIT:

@EDIT:

> That 500 should be configurable

No it shouldn't, because I just need to figure out the angle. If it were 100 or 200, it wouldn't do any changes.

> The "chest" should also be configurable

Doesn't matter, you can't see the change anyways.

> "banish" should be configurable - what if a person wants to stun the targets, or cast Cripple, or do anything other than Banish?

Gonna add that.
 

Flare

Stops copies me!
Reaction score
662
aw, I hoped to get some rep from this and then I get... leaks...
What is the fascination with rep :confused:

But yours is too then?
Mine explodes corpses, which is something that a shockwave with fancy SFX doesn't do - you really should test it before saying things like that :rolleyes: To quote my tooltip
Summons a wave of fire in front of the caster. Living enemy units hit by the wave will take damage, dead units will explode, damaging all enemy units nearby.

That's a matter of taste.
Since when does hail chase after you? :p
Perhaps it should be made configurable (it's extra work, but it caters for people's tastes)

What dark explosions is there?
Use the dragon's death animation? Howl of Terror? Just take a look, anything is better than a fiery explosion (especially with no fire associated with the spell). If you can't find anything by default, there's a really nice purple-black explosion over at Hive Workshop by JetFangInfero (I think) which would be suitable (and if people don't want to import that, they can find something else that they like that's not imported)

so that will make it non-null. Then I check if it's not equal to null, and if it is, I null it. KABOOM!! No leaks.
You're not reading - I said you would get an uninitialized variable error if Grimoire was enabled since d is never unitialized UNLESS preloading is enabled (and if preloading is disabled, you're try to compare an uninitialized variable against something else, which will give you the error) - I never said that you were causing a leak

Everybody are doing it, even Blizzard.
"Everybody" isn't reading in that case - look around, you will see posts saying that nulling a boolexpr = leak. And to be perfectly honest, using Blizzard to back up your case isn't much good... PolledWait (and other functions) leaks as a result of Blizzard, so by your logic, leaks would be acceptable because Blizzard did it too?

It's for the users.. So they can configure how the spell should be. If higher, they fall quicklier, if less, slower.
I meant to say DECREASE instead of 5 there ^^

Without it, there is some strange bug. Without it, the falling speed becomes more and more for each cast.. Strangly..
You're obviously doing something wrong if setting a variable to it's current value 'fixes' stuff

EDIT: Ye, you're doing it wrong... I just made DECREASE constant, and removed the DECREASE = DECREASE from the onDestroy and projectiles are travelling at (what looks to be) the exact same speed as they always were

Why not? And I cannot figure out that global struct thing.
Well, you're not clearing the data from the group (yes, you must clear it, just like it must be cleared from any other handle) but that's where a problem arises... that group is global so you're able to attach multiple instances of the struct to the ONE group - what if you clear the data? What's stopping HAIL from (a) clearing ALL data attached to the group, or (b) clearing one instance, but not the correct instance?

No it shouldn't, because I just need to figure out the angle. If it were 100 or 200, it wouldn't do any changes.
Oh, I see what you're doing now (and it's worse than what I thought you were doing).

Try this - make your hero face north, and order the hero to cast the spell at a point almost directly south (it MUST be within casting range or the bug probably won't appear) and tell me what direction the dragon flies, and what direction it was supposed to fly towards. That'll might be a better way to point out the problem than writing about it

(Hero doesn't specifically have to be facing north, but the casting point must be BEHIND the hero i.e. hero faces west, you cast east)

Doesn't matter, you can't see the change anyways.
1) Anything that can be configurable should be configurable
2) If you can't see the effect, why the hell are you even using it?
 

Vestras

Retired
Reaction score
248
Updated.

> Well, you're not clearing the data from the group (yes, you must clear it, just like it must be cleared from any other handle) but that's where a problem arises... that group is global so you're able to attach multiple instances of the struct to the ONE group - what if you clear the data? What's stopping HAIL from (a) clearing ALL data attached to the group, or (b) clearing one instance, but not the correct instance?

Oh yeah. What if I made a trigger which triggered when the game ended, and there I would do call ResetData(...)?
 

Flare

Stops copies me!
Reaction score
662
Oh yeah. What if I made a trigger which triggered when the game ended, and there I would do call ResetData(...)?
Well, you're still taking up extra space in the hash everytime you attach a new struct. Anyway, you would still have a problem of "Which data is the correct data?" since you have X structs attached to the handle, but you can only assign one value to the struct variable (so spell instance A could end up with struct instance E) but you would be better off asking Strilanc about that

And, if you don't know how to use the global struct, just ask, or loop at some spells?

Also, you're leaking handles somewhere (I don't have a clue where exactly) - copy this (this is emjlr3's Handle Counter) into a trigger called HandleCounter and watch it increase with every spellcast
JASS:
scope HandleCounter

globals
//=====Config. Options=====\\
    private constant string Title = &quot;Handle Counter&quot; // Title of your leaderboard
    private constant string HandlesTitle = &quot;Handles&quot; // Text to display in handle count row
    private constant string SecondsTitle = &quot;Time (sec.)&quot; // Text to display in time row
    private constant real Timeout = 1. // Interval to update at ( I recommend a whole number, or your time elapsed displayed could bug)
    private constant player DisplayTo = Player(0) // What player to display the leaderboard to
    private constant player Row1 = Player(0) // The time text will be displayed in this players color
    private constant player Row2 = Player(1) // The handle count text will be displayed in this players color
    private constant boolean Single_Only = true // Whether to display the Handle Counter leaderboard in single player only

//=====No Touching Past This Point!!!!=====\\
    private trigger Trigger = CreateTrigger()
    private leaderboard Lead = null
    s
    private integer Seconds = 0
    private location L = null
endglobals

private function H2I takes handle h returns integer
    return h
    return 0
endfunction

private function HandleCounter_Conditions takes nothing returns boolean
    return not Single_Only or bj_isSinglePlayer
endfunction
private function Update takes nothing returns nothing
    set Seconds = Seconds + R2I (Timeout)
    call LeaderboardSetItemValue(Lead,0,Seconds)
    set L = Location(5.,5.)
    call LeaderboardSetItemValue(Lead,1,H2I(L)-0x100000)
    call RemoveLocation(L)
endfunction
private function HandleCounter_Actions takes nothing returns nothing
    call DestroyTrigger(Trigger)
    set Trigger = CreateTrigger()
    call TriggerRegisterTimerEvent(Trigger,Timeout,true)
    call TriggerAddAction(Trigger,function Update)
    
    set Lead = CreateLeaderboard()
    call LeaderboardSetLabel(Lead, Title)
    call PlayerSetLeaderboard(DisplayTo,Lead)
    call LeaderboardDisplay(Lead,true)
    call LeaderboardAddItem(Lead,SecondsTitle,Seconds,Row1)
    call LeaderboardAddItem(Lead,HandlesTitle,0,Row2)
    call LeaderboardSetSizeByItemCount(Lead,2)
endfunction

//===========================================================================
public function InitTrig takes nothing returns nothing
    call TriggerRegisterTimerEvent(Trigger,.001,false)
    call TriggerAddCondition(Trigger,Condition(function HandleCounter_Conditions))
    call TriggerAddAction( Trigger, function HandleCounter_Actions )
endfunction

endscope
 

Vestras

Retired
Reaction score
248
Well, it started going from 902 to 904 all the time. Then when I used Overheat it started going from 880 to 944. What does that mean?
 

Vestras

Retired
Reaction score
248
> Flare and TheDamien

Damn. It's actually pretty strange, because I have looked through them about 10 times each. Didn't find anything. I guess I just need to do it again.

> XxShadyxX

Thanks :)
 
Reaction score
333
Damn. It's actually pretty strange, because I have looked through them about 10 times each. Didn't find anything. I guess I just need to do it again.

Yep.

On cursory inspection, the dummy unit created in Actions does not appear to be getting removed or killed, are you sure it isn't leaking? Also you can get rid of the ForGroup and merge its actions into the GroupEnum loop (the filter looks redundant anyway?), then you can use a global group as the argument in the Enum (instead of having to create a new group each time).

Timers should be recycled, not destroyed. You should also try integrating all instances of the spell into a single timer callback, instead of creating a new timer for each cast.

This also doesn't appear to be MUI. 'Owner' and 'CastUnit' appear to be set only in actions, but are used in the timer callback.

The indenting is a bit fruity. Don't indent lines for the sake of it. Your code might look prettier (?) with those large curves, but it doesn't really help readability.

EDIT: (These points apply mainly to Overheat, I'll look at the others in a bit)
 

Tukki

is Skeleton Pirate.
Reaction score
29
Just read through your code and found this:

JASS:
set angle=bj_RADTODEG*Atan2(d.ty-y1,d.tx-x1)
  set x2=x1+TRAVELDIST*Cos(angle*bj_DEGTORAD)
  set y2=y1+TRAVELDIST*Sin(angle*bj_DEGTORAD)


- You are using RADTODEG * Atan2, why not simply use Atan2BJ instead if that's what you want.
- Why use RADTODEG at all, since you use Jass anyway.
 

T.s.e

Wish I was old and a little sentimental
Reaction score
133
As people have pointed out a gazillion times to me;
JASS:
      private method onDestroy takes nothing returns nothing
        call ResetData(.tmr)
        call DestroyTimer(.tmr)
        set .caster=null
        set .tmr=null
        set .cx=0
        set .cy=0
        set .px=0
        set .py=0
        set .angle=0
        set .trange=0
        set CastUnit=null
        set Owner=null
        set done=false
     endmethod

You don't need to null struct members, as they are global arrays.

And again:

JASS:
       private method onDestroy takes nothing returns nothing
         set DECREASE=DECREASE
         call ResetData(.tmr)
         call ResetData(.trg)
         call ResetData(.trg2)
         call DestroyGroup(.dummies)
         call DestroyTimer(.tmr)
         call DestroyTrigger(.trg)
         call DestroyTrigger(.trg2)
         set .caster=null
         set .target=null
         set .dummies=null
         set .tmr=null
         set .trg=null
         set .trg2=null
       endmethod


And again!

JASS:
      private method onDestroy takes nothing returns nothing
        set TRAVELDIST=TRAVELDIST
        call ResetData(.tmr)
        call ResetData(.tmr2)
        call ResetData(.trg)
        call DestroyTimer(.tmr)
        call DestroyTimer(.tmr2)
        call DestroyTrigger(.trg)
        set .caster=null
        set .dummy=null
        set .tmr=null
        set .tmr2=null
        set .trg=null
      endmethod
 

Flare

Stops copies me!
Reaction score
662
Did a quick playtest with new version, more stuff needs to be fixed/changed

1) Do this
Try this - make your hero face north, and order the hero to cast the spell at a point almost directly south (it MUST be within casting range or the bug probably won't appear)
and see what way the dummy faces - it's not facing the way it's supposed to

2)
Code:
Copy the dummy unit (The one with (Overheat) suffix)
Copy the dummy unit (The one with (Hailstorm) suffix)
Copy the dummy unit (The one with (Summon Abyss) suffix)
Those dummy units are identical - you only need one of them

3) You're still not pausing the timers... how many times must it be said before you actually do it? It's been said here, here and, technically, here - it's a very simple thing, but it avoids any bugs that may occur

4)
JASS:
   if lvl==1 then
     return 6
   elseif lvl==2 then
     return 12
   elseif lvl==3 then
     return 18
   endif

JASS:
return lvl*6 //Alternatively, you could make that 6 a configurable constant, and do
return lvl*MULTIPLIER


JASS:
   if lvl==1 then
     return 60.
   elseif lvl==2 then
     return 80.
   elseif lvl==3 then
     return 100.
   endif

JASS:
return 40 + 20*lvl //Again, the 40 and 20 can be configurable constants, or, you could do what I usually do:
local real base = 40
local real m1 = GetUnitAbilityLevel (whichUnit, whichAbil) //This can be swapped out for anything you like
local real m2 = 20
return base + m1 * m2
//Which is easily readable, and lets you easily make up your own formula


5) The RADTODEG -> DEGTORAD that Tukki pointed out is still there - you're not even using the angle when it's in degrees...

6) You still haven't fixed the uninitialized variable when PRELOAD==false thing - null d outside the if - it'll null the variable just fine without any issues, and you won't get an uninitialized variable error (just so you know, variables -AREN'T- initialized to 'null', they have absolutely no value when declared)
 
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