Snippet MissileDamage

Reaction score
91
Basically, this snippet was inspired by (if I'm ot mistaking) ReVolver's WaitMissileSpeedDamage snippet. This is supposed to be an improved version simply because his one was inaccurate when calculating distance between the caster and target - it could damage units a bit late if they are moving. Though this requires TimerTicker system by Cohadar, it correctly damages units even if they're moving due to a simple check and recalculating distance again.

JASS:

//===========================================================================
// Missile Damage snippet by tyrande_ma3x
//===========================================================================
// This snippet provides you only one function: MD_Start(). 
// However, it requires the TimerTicker system by Cohadar (v4.0).
//
// The function takes six (6) arguments followed in this order:
// unit a - The unit that casts the spell (will also be the damager)
// unit b - The target that is going to be damaged after the missile reaches
// real c - The damage dealt after the missile reaches its target
// real e - The missile's speed
// attacktype f - The attack type of the damaging (i.e. ATTACK_TYPE_MAGIC)
// damagetype g - The damage type of the damaging (i.e. DAMAGE_TYPE_UNIVERSAL)
//===========================================================================
// In this order the function would look something like this:
// call MD_Start(caster, target, 100., 800., ATTACK_TYPE_MAGIC, DAMAGE_TYPE_UNIVERSAL)
//===========================================================================
// What the snippet does is that it damages the target when the missile
// reaches its target, unlike WaitMissileSpeedDamage (a function by ReVolver 
// if I'm not mistaking) which does damage earlier/later because of a sigle
// distance check. 
//===========================================================================
// Credits to ReVolver for the original idea.
//===========================================================================

library MD requires TT

globals
// Minimum damage required for the unit to be damaged.
// If it's less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns boolean
    local Data d = TT_GetData()
    local real x1 = d.x
    local real y1 = d.y
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s
    if r < LEAST_RANGE then
        if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
            if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                if GetWidgetLife(d.targ) > 0.405 then
                    call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call d.destroy()
    else
        set d.x = x2
        set d.y = y2
        call TT_Once(function Callback, d, tr)
    endif
    return true
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    set d.x = x2
    set d.y = y2
    call TT_Once(function Callback, d, tr)
endfunction

endlibrary


TT v4.0
JASS:

//==============================================================================
//  TT -- TIMER TICKER SYSTEM BY COHADAR -- v4.0
//==============================================================================
//
//  PURPOUSE OF TT:
//       * Passing data to timers
//       * Avoiding direct use of timer handles
//
//  PROS: 
//       * It is easier than using attaching
//       * It is optimized to use only one timer on default high frequency
//       * GetData method is the same for all timer frequencies
//
//  CONS:
//       * You must remember to always return true from your function
//         when you want to stop timer, even if it is of one-shot type.
//         (otherwise it will leak)
//
//  START FUNCTIONS:
//       * TT_Start(userFunc, struct)
//       * TT_StartEx(userFunc, struct, period)
//       * TT_Once(userFunc, struct, timeout)
//       * TT_StartTimerDialog(userFunc, struct, timeout) -> timerdialog
//
//       * userFunc is a user function that takes nothing and returns boolean
//         it will be periodically called by the system until it returns true.
//
//  GET FUNCTIONS:
//       * TT_GetData() -> struct
//       * TT_GetTimerDialog() -> timerdialog
//
//       * These functions can only be called from inside userFunc
//         TT_GetData() will return struct passed to any of the start functions
//         TT_GetTimerDialog() returns timerdialog created by TT_StartTimerDialog
//
//  DETAILS:
//       * On default frequency all user functions are stored in an array.
//         Timer will call all those functions each period.
//
//       * While user function returns false timer will continue to call it each period
//         Once user function returns true it will be removed from system
//
//       * TT is using smart timer preloading and simple hash
//         When colliding timer handle is found that timer is simply discarded
//
//  REQUIREMENTS:
//       * NewGen v4c and above (there might be some problems with older NewGen's)  
//
//  HOW TO IMPORT:
//       * Just create a trigger named TT
//       * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library TT initializer Init

//==============================================================================
//  Configuration
//==============================================================================
globals
    // List of recommended periods for high-frequency timer:
    // 0.04    = 25 calls per second
    // 0.03125 = 32 calls per second
    // 0.025   = 40 calls per second
    // 0.02    = 50 calls per second
    public constant real PERIOD = 0.03125
    
    // how many low-frequency timers to preload
    // system can safely extend beyond this limit
    private constant integer PRELOAD = 32
endglobals
//==============================================================================
//  End of Configuration
//==============================================================================


//==============================================================================
globals
    // "frames per second" of high-frequency timer
    public constant integer FPS = R2I(1.0/PERIOD)

    // globals for passing data to userFunc
    private integer Data
    private timerdialog timerDialog
    
    // One Timer to rule them all, One Timer to find them,
    // One Timer to call them all and in the jass bind them
    // In the land of warcraft where the desyncs lie.
    private timer   HF_Timer = CreateTimer()
    private integer HF_Counter = 0
    private trigger array HF_Triggz
    private integer array HF_Dataz
    
    // we can safely use dummy hashing here because timers are preloaded
    private constant integer LF_HASH = 8191
    
    private integer array LF_Dataz
    private trigger array LF_Triggz
    private timer array LF_Timerz
    private timerdialog array LF_Dialogz
    
    // recycling
    private integer array LF_Indexz
    private integer LF_Counter = PRELOAD    
endglobals

//==============================================================================
private constant function H2I takes handle h returns integer
    return h
    return 0
endfunction

//==============================================================================
// note how colliding timer handles are discarded
// so TT would work properly even after preload limit break
//==============================================================================
private function NewIndex takes nothing returns integer
    local integer i
    local timer t
    if (LF_Counter==0) then  
        loop
            debug call BJDebugMsg("WARNING: TT reached preloaded timer limit!")
            set t = CreateTimer()
            set i = H2I(t)
            set i = i - (i / LF_HASH) * LF_HASH // dummy modulo hash
            if LF_Timerz<i> == null then
                set LF_Timerz<i> = t
                set LF_Triggz<i> = CreateTrigger()
                return i
            endif
        endloop
    endif  
    set LF_Counter = LF_Counter - 1 
    return LF_Indexz[LF_Counter]
endfunction

//==============================================================================
private function HF_Handler takes nothing returns nothing
    local trigger swap
    local integer i = HF_Counter
    loop
        exitwhen i&lt;=0
        set Data = HF_Dataz<i>
        if TriggerEvaluate(HF_Triggz<i>) then
            set swap = HF_Triggz<i>
            call TriggerClearConditions(swap)
            set HF_Triggz<i> = HF_Triggz[HF_Counter]
            set HF_Triggz[HF_Counter] = swap
            set HF_Dataz<i> = HF_Dataz[HF_Counter]
            set HF_Counter = HF_Counter - 1
        endif
        set i = i - 1
    endloop
    // who can guess why am I not nulling swap here?
endfunction

//==============================================================================
private function LF_Handler takes nothing returns nothing
    local integer i = H2I(GetExpiredTimer())
    set i = i - (i / LF_HASH) * LF_HASH // dummy modulo hash
    set Data = LF_Dataz<i>
    if TriggerEvaluate(LF_Triggz<i>) then
        // recycle the trigger and timer
        call TriggerClearConditions(LF_Triggz<i>)
        call PauseTimer(LF_Timerz<i>)
        set LF_Indexz[LF_Counter] = i
        set LF_Counter = LF_Counter + 1
    endif
endfunction

//==============================================================================
//  Periodic timer that runs on TT_PERIOD
//==============================================================================
public function Start takes code userFunc, integer data returns nothing
    debug if userFunc == null then
    debug    call BJDebugMsg(&quot;ERROR: TT_Start - null userFunc&quot;)
    debug    return
    debug endif

    set HF_Counter = HF_Counter + 1    
    
    if HF_Triggz[HF_Counter] == null then
        set HF_Triggz[HF_Counter] = CreateTrigger()
    endif
    
    set HF_Dataz[HF_Counter] = data
    call TriggerAddCondition(HF_Triggz[HF_Counter], Condition(userFunc))
endfunction

//==============================================================================
//  Periodic timer with custom period
//==============================================================================
public function StartEx takes code userFunc, integer data, real period returns nothing
    local integer i
    debug if userFunc == null then
    debug     call BJDebugMsg(&quot;ERROR: TT_StartEx - null userFunc&quot;)
    debug     return
    debug endif
    set i = NewIndex()
    call TriggerAddCondition(LF_Triggz<i>, Condition(userFunc))
    set LF_Dataz<i> = data
    call TimerStart(LF_Timerz<i>, period, true, function LF_Handler)
endfunction

//==============================================================================
//  One shot timer, remember to return true in userFunc
//==============================================================================
public function Once takes code userFunc, integer data, real timeout returns nothing
    local integer i
    debug if userFunc == null then
    debug     call BJDebugMsg(&quot;ERROR: TT_Once - null userFunc&quot;)
    debug     return
    debug endif
    set i = NewIndex()
    call TriggerAddCondition(LF_Triggz<i>, Condition(userFunc))
    set LF_Dataz<i> = data
    call TimerStart(LF_Timerz<i>, timeout, false, function LF_Handler)
endfunction

//==============================================================================
public function StartTimerDialog takes code userFunc, integer data, real timeout returns timerdialog
    local integer i
    debug if userFunc == null then
    debug     call BJDebugMsg(&quot;ERROR: TT_StartTimerDialog - null userFunc&quot;)
    debug     return null
    debug endif
    set i = NewIndex()
    call TriggerAddCondition(LF_Triggz<i>, Condition(userFunc))
    set LF_Dataz<i> = data
    call TimerStart(LF_Timerz<i>, timeout, false, function LF_Handler)
    set bj_lastCreatedTimerDialog = CreateTimerDialog(LF_Timerz<i>)
    set LF_Dialogz<i> = bj_lastCreatedTimerDialog
    return bj_lastCreatedTimerDialog
endfunction

//==============================================================================
//  Call this function only inside the userFunc
//==============================================================================
public function GetData takes nothing returns integer
    return Data
endfunction

//==============================================================================
//  Call this function only inside the userFunc
//==============================================================================
public function GetTimerDialog takes nothing returns timerdialog
    local integer i = H2I(GetExpiredTimer())
    set i = i - (i / LF_HASH) * LF_HASH // dummy modulo hash
    return LF_Dialogz<i> 
endfunction

//==============================================================================
//  Preload LF timers and start HF timer.
//==============================================================================
private function Init takes nothing returns nothing
    local integer i
    local timer t
    local integer j = 0
    loop
        exitwhen j&gt;=PRELOAD
        set t = CreateTimer()
        set i = H2I(t)
        set i = i - (i / LF_HASH) * LF_HASH // dummy modulo hash        
        if LF_Timerz<i> == null then
            set LF_Timerz<i> = t
            set LF_Triggz<i> = CreateTrigger()
            set LF_Indexz[j] = i
            set j = j + 1
        endif        
    endloop

    call TimerStart(HF_Timer, PERIOD, true, function HF_Handler)  
endfunction

endlibrary

//==============================================================================
//  END OF TIMER TICKER SYSTEM
//==============================================================================
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


If someone prefers another system, I've written for TimerUtils, ABC, HSAS and CSData.
TimerUtils:
JASS:

library MD requires TimerUtils

globals
// Minimum damage required for the unit to be damaged.
// If it&#039;s less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Data d = GetTimerData(t)
    local real x1 = d.x
    local real y1 = d.y
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s
    if r &lt; LEAST_RANGE then
        if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
            if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                if GetWidgetLife(d.targ) &gt; 0.405 then
                    call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call ReleaseTimer(t)
        call d.destroy()
    else
        set d.x = x2
        set d.y = y2
        call ReleaseTimer(t)
        set t = NewTimer()
        call SetTimerData(t, d)
        call TimerStart(t, tr, false, function Callback)
    endif
    set t = null
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    local timer t = NewTimer()
    set d.x = x2
    set d.y = y2
    call SetTimerData(t, d)
    call TimerStart(t, tr, false, function Callback)
    set t = null
endfunction

endlibrary
ABC:
JASS:

library MD requires ABC

globals
// Minimum damage required for the unit to be damaged.
// If it&#039;s less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Data d = GetTimerStructA(t)
    local real x1 = d.x
    local real y1 = d.y
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s
    if r &lt; LEAST_RANGE then
        if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
            if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                if GetWidgetLife(d.targ) &gt; 0.405 then
                    call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call ClearTimerStructA(t)
        call PauseTimer(t)
        call DestroyTimer(t)
        call d.destroy()
    else
        set d.x = x2
        set d.y = y2
        call PauseTimer(t)
        call DestroyTimer(t)
        set t = CreateTimer()
        call SetTimerStructA(t, d)
        call TimerStart(t, tr, false, function Callback)
    endif
    set t = null
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    local timer t = CreateTimer()
    set d.x = x2
    set d.y = y2
    call SetTimerStructA(t, d)
    call TimerStart(t, tr, false, function Callback)
    set t = null
endfunction

endlibrary
HSAS:
JASS:

library MD requires HSAS

//! runtextmacro HSAS_Static(&quot;MDmg&quot;, &quot;32760&quot;, &quot;private&quot;)

globals
// Minimum damage required for the unit to be damaged.
// If it&#039;s less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Data d = GetAttachedStructMDmg(t)
    local real x1 = d.x
    local real y1 = d.y
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s
    if r &lt; LEAST_RANGE then
        if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
            if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                if GetWidgetLife(d.targ) &gt; 0.405 then
                    call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call PauseTimer(t)
        call DestroyTimer(t)
        call d.destroy()
    else
        set d.x = x2
        set d.y = y2
        call PauseTimer(t)
        call DestroyTimer(t)
        set t = CreateTimer()
        call AttachStructMDmg(t, d)
        call TimerStart(t, tr, false, function Callback)
    endif
    set t = null
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    local timer t = CreateTimer()
    set d.x = x2
    set d.y = y2
    call AttachStructMDmg(t, d)
    call TimerStart(t, tr, false, function Callback)
    set t = null
endfunction

endlibrary
CSData:
JASS:

library MD requires CSData

globals
// Minimum damage required for the unit to be damaged.
// If it&#039;s less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Data d = GetCSData(t)
    local real x1 = d.x
    local real y1 = d.y
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s
    if r &lt; LEAST_RANGE then
        if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
            if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                if GetWidgetLife(d.targ) &gt; 0.405 then
                    call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                endif
            endif
        endif
        call PauseTimer(t)
        call DestroyTimer(t)
        call d.destroy()
    else
        set d.x = x2
        set d.y = y2
        call PauseTimer(t)
        call DestroyTimer(t)
        set t = CreateTimer()
        call SetCSData(t, d)
        call TimerStart(t, tr, false, function Callback)
    endif
    set t = null
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    local timer t = CreateTimer()
    set d.x = x2
    set d.y = y2
    call SetCSData(t, d)
    call TimerStart(t, tr, false, function Callback)
    set t = null
endfunction

endlibrary

Changes:
- Included a test map.
- Fixed inaccurate calculations because d.y received the value y1 instead of y2.
- Now uses TT_Once() instead of TT_StartEx().
- Added a global for better configuration.
 
Reaction score
91
> Ever heard of ands?
Ever heard of better readability?

> You could make this system independent.
I know, but I have no idea how to make it like that...

EDIT: T.s.e. basically said it.
 

T.s.e

Wish I was old and a little sentimental
Reaction score
133
That way looks cleaner than massively long and-conditions. It's also easier to modify.
 

Vestras

Retired
Reaction score
248
> Ever heard of better readability?

You can read and conditions easily.

> I know, but I have no idea how to make it like that...

A counter that is set to COUNTER = COUNTER + 1 everytime a struct is created, a global struct that is set to MYSTRUCT[COUNTER] = d and some loops.
 

T.s.e

Wish I was old and a little sentimental
Reaction score
133
Maybe I'm not fitting into your stereotype, but I don't think that a line over 30 centimeters long is easier to read than indented if-blocks.

Vestras, if you can make things system-independent, then why don't you do so? (Incinerate)
 
Reaction score
91
> A counter that is set to COUNTER = COUNTER + 1 everytime a struct is created, a global struct that is set to MYSTRUCT[COUNTER] = d and some loops.
That certainly helped me a lot... >_>

Ok, I tried what you said, however I'm not sure it is the correct way. Mind looking it and telling if I did something wrong? I tested it and it seems to work fine but I'm not sure if there are several function calls it would still work.

JASS:

library MD

private keyword Data

globals
    private Data array D
    private integer I = 0
    private timer t = CreateTimer()
endglobals
    

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit a, unit b, real c, real e, attacktype f, damagetype g returns Data
        local Data d = Data.allocate()
        set d.cast = a
        set d.targ = b
        set d.dmg = c
        set d.s = e
        set d.at = f
        set d.dt = g
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local Data d 
    local integer i = 0
    local real x1
    local real y1
    local real x2     
    local real y2 
    local real xx
    local real yy 
    local real r 
    local real tr 
    loop
        exitwhen i &gt; I
        set d = D<i>
        set x1 = d.x
        set y1 = d.y
        set x2 = GetUnitX(d.targ)
        set y2 = GetUnitX(d.targ)
        set xx = x1 - x2
        set yy = y1 - y2
        set r = SquareRoot(xx * xx + yy * yy)
        set tr = r / d.s
        if r &lt; 100. then
            if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
                if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                    if GetWidgetLife(d.targ) &gt; 0.405 then
                        call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                    endif
                endif
            endif
            call d.destroy()
            set D<i> = D[ I ]
            set I = I - 1
        else
            set d.x = x2
            set d.y = y1
        endif
        if I == 0 then
            call PauseTimer(t)
        endif
        set i = i + 1
    endloop
endfunction

public function Start takes unit a, unit b, real c, real e, attacktype f, damagetype g returns nothing
    local Data d = Data.create(a, b, c, e, f, g)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    set d.x = x2
    set d.y = y2
    set I = I + 1
    set D[ I ] = d
    if I == 1 then
        call TimerStart(t, 1., true, function Callback)
    endif
endfunction

endlibrary
</i></i>


EDIT: Fixed arrays, they don't get capitalized >_>
 

Gwypaas

hook DoNothing MakeGUIUsersCrash
Reaction score
50
Using the global struct method can fail if the amount of times the timer runs isn't constant.

An example that would cause it to fail:
A Starts
B Starts
C Starts

What happens if C ends before B or A? Everything would fuckup. B would try to use A's struct and A would just use the 0 struct used by JAsshelper to determine a few things.. Major fuckup.
 

Flare

Stops copies me!
Reaction score
662
set d = D[ I ]
That should be D[ i ] - what you're doing now is only grabbing the top instance, everytime the loop runs

@Gwypaas: Single timer fucks up if you don't know what you're doing - if you know how it's done, it's difficult to go wrong :p

And your example is crap - if a problem were to arise, that situation would be the easiest to handle :p An 'in-between' instance isn't really problematic either (unless you really enjoy breaking stuff), but it'd be the more difficult of the two

Let's make it numerical, for more sense

When C (3) ends, you just reduce the instance counter by 1 - N is now equal to 2 (since there was 3 instances running before C ended)

So, you've got 2 instances left - A (1) and B (2). Your loop will be going from N (2) to 1. Loop runs where i == 2, instance B is handled - loop runs when i == 1, instance A is handled - loop exits at i == 0, no more instances left to be handled for that execution
 
Reaction score
91
> That should be D[ i ] - what you're doing now is only grabbing the top instance, everytime the loop runs
So that's my only mistake?
 

Trollvottel

never aging title
Reaction score
262
if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then


not really neccessary to do so? 1 check is enough.
 

Blackrage

Ultra Cool Member
Reaction score
25
You should make it so that when the missle hits, it returns a boolean to the original function that the user used this snippet in.
 

Kenny

Back for now.
Reaction score
202
JASS:
if I == 1 then
    call TimerStart(t, 1., true, function Callback)
endif


Shouldn't the interval be smaller? What happens when i have a missile that takes 0.25 seconds to reach the target (hypothetically).

JASS:
    if r &lt; 100. then // &lt;---------
            if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
                if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                    if GetWidgetLife(d.targ) &gt; 0.405 then
                        call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                    endif
                endif
            endif


Maybe make the 100. either a global or an agrument for your function, so it can be changed by the user. At least a global would be good.

JASS:
if I == 0 then
    call PauseTimer(t)
endif


Can be put outside the loop.

And also maybe add an MD_StartEx() function that uses the other arguments of UnitDamageTarget(), such as weapontype, for configurability.
 
Reaction score
91
So that's my only mistake?
Nobody answered this one so I assume there's still a mistake, however I can't find it. I can't do suggested changes until someone can confirm that this new method works as intended (I mean the part where I loop through the structs)...
 

Kenny

Back for now.
Reaction score
202
From the looks of it, everything is okay.

JASS:
loop
    exitwhen i &gt; I
    set d = D<i>
    set x1 = d.x // &lt;-------
    set y1 = d.y // &lt;-------
    set x2 = GetUnitX(d.targ)
    set y2 = GetUnitX(d.targ)
    set xx = x1 - x2
    set yy = y1 - y2
    set r = SquareRoot(xx * xx + yy * yy)
</i>


Can't you just use d.x and d.y directly?

Also:

JASS:
loop
    exitwhen i &gt; I
    set d = D<i> // &lt;-----
</i>


It all comes down to preference, but you can just use D.x D.y and so on instead of setting d = D then using the struct instance, saves a local but its no big deal.

And if your not sure if its working or not, just test it out, get like 10 different skills using this and spam them over and over. If something happens then theres something wrong. But as i said before, it looks okay.
 
Reaction score
91
Now the snippet does absolutely nothing...

JASS:

library MD

//===========================================================================
// CONFIGURE IF NECESSARY
//===========================================================================
globals
// Period of the timer.
    private constant real PERIOD = 0.03125 
// Minimum damage required for the unit to be damaged.
// If it&#039;s less than 100 then the unit will be hurt,
// else the damaging will be delayed until the condition is met.
    private constant real LEAST_RANGE = 100.
endglobals
//===========================================================================

private keyword Data

globals
    private Data array D
    private integer I = 0
    private timer t = CreateTimer()
endglobals
    

private struct Data
    unit cast
    unit targ
    real dmg
    real x
    real y
    real s
    attacktype at
    damagetype dt
    
    static method create takes unit cast, unit targ, real dmg, real speed, attacktype at, damagetype dt returns Data
        local Data d = Data.allocate()
        set d.cast = cast
        set d.targ = targ
        set d.dmg = dmg
        set d.s = speed
        set d.at = at
        set d.dt = dt
        return d
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local Data d 
    local integer i = 0
    local real x1
    local real y1
    local real x2     
    local real y2 
    local real xx
    local real yy 
    local real r 
    local real tr 
    loop
        exitwhen i &gt; I
        set d = D<i>
        set x1 = d.x
        set y1 = d.y
        set x2 = GetUnitX(d.targ)
        set y2 = GetUnitX(d.targ)
        set xx = x1 - x2
        set yy = y1 - y2
        set r = SquareRoot(xx * xx + yy * yy)
        set tr = r / d.s
        call BJDebugMsg(&quot;calculated range, ifs&quot;)
        if r &lt; LEAST_RANGE then
            if IsUnitVisible(d.targ, GetOwningPlayer(d.cast)) then
                if not IsUnitInvisible(d.targ, GetOwningPlayer(d.cast)) then
                    if GetWidgetLife(d.targ) &gt; 0.405 then
                        call UnitDamageTarget(d.cast, d.targ, d.dmg, true, true, d.at, d.dt, WEAPON_TYPE_WHOKNOWS)
                    endif
                endif
            endif
            call BJDebugMsg(&quot;d.destroy()&quot;)
            call d.destroy()
            set D<i> = D<i>
            set I = I - 1
        else
            call BJDebugMsg(&quot;d.x = x2; d.y = y2&quot;)
            set d.x = x2
            set d.y = y2
        endif
        if I == 0 then
            call BJDebugMsg(&quot;Paused timer&quot;)
            call PauseTimer(t)
        endif
        call BJDebugMsg(&quot;i = i + 1&quot;)
        set i = i + 1
    endloop
endfunction

public function Start takes unit cast, unit targ, real dmg, real speed, attacktype at, damagetype dt returns nothing
    local Data d = Data.create(cast, targ, dmg, speed, at, dt)
    local real x1 = GetUnitX(d.cast)
    local real y1 = GetUnitY(d.cast)
    local real x2 = GetUnitX(d.targ)
    local real y2 = GetUnitY(d.targ)
    local real xx = x1 - x2
    local real yy = y1 - y2
    local real r  = SquareRoot(xx * xx + yy * yy)
    local real tr = r / d.s    
    set d.x = x2
    set d.y = y2
    set I = I + 1
    set D<i> = d
    if I == 1 then
        call BJDebugMsg(&quot;Timer started&quot;) // &lt;------
        call TimerStart(t, PERIOD, false, function Callback)
    endif
endfunction

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


Only this debug message appears: call BJDebugMsg("Timer started"). I'm totally lost now...
 

Akolyt0r

New Member
Reaction score
33
you have a divide by zero in this line:
JASS:
        set tr = r / d.s


at least this crashes the thread ....i will continue looking for the original reason :>
got it:
JASS:
//in function Start
    set I = I + 1
    set D<i> = d</i>

JASS:
//should be
    set D<i> = d
    set I = I + 1
</i>
 

Kenny

Back for now.
Reaction score
202
JASS:
    set I = I + 1
    set D<i> = d

    set D<i> = d
    set I = I + 1

</i></i>


You can either switch that, or:

JASS:
private function Callback takes nothing returns nothing
    local Data d 
    local integer i = 0 // &lt;------


Set i = 1

Right now your first struct instance is at 1 and your beginning your loop at 0, maybe that has something to do with it.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    The bots will show up as users online in the forum software but they do not show up in my stats tracking. I am sure there are bots in the stats but the way alot of the bots treat the site do not show up on the stats
  • Varine Varine:
    I want to build a filtration system for my 3d printer, and that shit is so much more complicated than I thought it would be
  • Varine Varine:
    Apparently ABS emits styrene particulates which can be like .2 micrometers, which idk if the VOC detectors I have can even catch that
  • Varine Varine:
    Anyway I need to get some of those sensors and two air pressure sensors installed before an after the filters, which I need to figure out how to calculate the necessary pressure for and I have yet to find anything that tells me how to actually do that, just the cfm ratings
  • Varine Varine:
    And then I have to set up an arduino board to read those sensors, which I also don't know very much about but I have a whole bunch of crash course things for that
  • Varine Varine:
    These sensors are also a lot more than I thought they would be. Like 5 to 10 each, idk why but I assumed they would be like 2 dollars
  • Varine Varine:
    Another issue I'm learning is that a lot of the air quality sensors don't work at very high ambient temperatures. I'm planning on heating this enclosure to like 60C or so, and that's the upper limit of their functionality
  • Varine Varine:
    Although I don't know if I need to actually actively heat it or just let the plate and hotend bring the ambient temp to whatever it will, but even then I need to figure out an exfiltration for hot air. I think I kind of know what to do but it's still fucking confusing
  • The Helper The Helper:
    Maybe you could find some of that information from AC tech - like how they detect freon and such
  • Varine Varine:
    That's mostly what I've been looking at
  • Varine Varine:
    I don't think I'm dealing with quite the same pressures though, at the very least its a significantly smaller system. For the time being I'm just going to put together a quick scrubby box though and hope it works good enough to not make my house toxic
  • Varine Varine:
    I mean I don't use this enough to pose any significant danger I don't think, but I would still rather not be throwing styrene all over the air
  • The Helper The Helper:
    New dessert added to recipes Southern Pecan Praline Cake https://www.thehelper.net/threads/recipe-southern-pecan-praline-cake.193555/
  • The Helper The Helper:
    Another bot invasion 493 members online most of them bots that do not show up on stats
  • Varine Varine:
    I'm looking at a solid 378 guests, but 3 members. Of which two are me and VSNES. The third is unlisted, which makes me think its a ghost.
    +1
  • The Helper The Helper:
    Some members choose invisibility mode
    +1
  • The Helper The Helper:
    I bitch about Xenforo sometimes but it really is full featured you just have to really know what you are doing to get the most out of it.
  • The Helper The Helper:
    It is just not easy to fix styles and customize but it definitely can be done
  • The Helper The Helper:
    I do know this - xenforo dropped the ball by not keeping the vbulletin reputation comments as a feature. The loss of the Reputation comments data when we switched to Xenforo really was the death knell for the site when it came to all the users that left. I know I missed it so much and I got way less interested in the site when that feature was gone and I run the site.
  • Blackveiled Blackveiled:
    People love rep, lol
    +1
  • The Helper The Helper:
    The recipe today is Sloppy Joe Casserole - one of my faves LOL https://www.thehelper.net/threads/sloppy-joe-casserole-with-manwich.193585/
  • The Helper The Helper:
    Decided to put up a healthier type recipe to mix it up - Honey Garlic Shrimp Stir-Fry https://www.thehelper.net/threads/recipe-honey-garlic-shrimp-stir-fry.193595/
  • The Helper The Helper:
    Here is another comfort food favorite - Million Dollar Casserole - https://www.thehelper.net/threads/recipe-million-dollar-casserole.193614/

      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