• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Attack is Launched Event

Level 12
Joined
Jan 2, 2016
Messages
973
This Event happens between "A unit is attacked" and "A unit is damaged" - when the unit launches a projectile.
It can't be abused by spamming stop.
It can be used if you want an event to run before the unit deals damage to the target (for example: you have some unit that has 'special' attacks, and you want to pause the targets of the attack, until the shot reaches them, or you want to create a special effect on them, or you want to launche more than one attack, ect)
JASS:
function EventReg takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    set udg_Attacked = LoadUnitHandle(udg_Attack_Table, id, 'atkd')
    set udg_Attacker = LoadUnitHandle(udg_Attack_Table, id, 'atkr')
    set udg_Attack_Launched = 1.00
    set udg_Attack_Launched = 0.00
    set t = null
endfunction

function Delay takes nothing returns boolean
    local integer tid = GetUnitTypeId(GetAttacker())
    local integer uid = GetHandleId(GetAttacker())
    local boolean configured = LoadBoolean(udg_Attack_Table, tid, 'conf')
    local real del_time
    local boolean b
    local timer t
    if configured then
        set del_time = LoadReal(udg_Attack_Table, tid , 'dely')
        set b = LoadBoolean(udg_Attack_Table, uid , 'bull')
        if b then
            set t = LoadTimerHandle(udg_Attack_Table, uid , 'time')
            call TimerStart(t, del_time, false, function EventReg)
            call SaveUnitHandle(udg_Attack_Table, GetHandleId(t), 'atkd', GetTriggerUnit())
        else
            set t = CreateTimer()
            call TimerStart(t, del_time, false, function EventReg)
            call SaveTimerHandle(udg_Attack_Table, uid, 'time', t)
            call SaveBoolean(udg_Attack_Table, uid, 'bull', true)
            call SaveUnitHandle(udg_Attack_Table, GetHandleId(t), 'atkr', GetAttacker())
            call SaveUnitHandle(udg_Attack_Table, GetHandleId(t), 'atkd', GetTriggerUnit())
        endif
        set t = null
    endif
    return false
endfunction

function AttCancel takes nothing returns boolean
    local boolean event_runs = LoadBoolean(udg_Attack_Table, GetUnitTypeId(GetTriggerUnit()), 'conf')
    local boolean b
    local timer t
    if event_runs then
        set b = LoadBoolean(udg_Attack_Table, GetHandleId(GetTriggerUnit()), 'bull')
        if b then
            set t = LoadTimerHandle(udg_Attack_Table, GetHandleId(GetTriggerUnit()), 'time')
            call TimerStart(t , 0.00, false, null)
            set t = null
            call SaveBoolean(udg_Attack_Table, GetHandleId(GetTriggerUnit()), 'bull', false)
        endif
    endif
    return false
endfunction

function UnitDied takes nothing returns boolean
    local boolean ev_runs = LoadBoolean(udg_Attack_Table, GetUnitTypeId(GetTriggerUnit()), 'conf')
    local boolean b
    local timer t
    if ev_runs then
        set b = LoadBoolean(udg_Attack_Table, GetHandleId(GetTriggerUnit()), 'bull')
        if b then
            set t = LoadTimerHandle(udg_Attack_Table, GetHandleId(GetTriggerUnit()), 'time')
            call FlushChildHashtable(udg_Attack_Table, GetHandleId(t))
            call DestroyTimer(t)
            set t = null
            call FlushChildHashtable(udg_Attack_Table, GetHandleId(GetTriggerUnit()))
        endif
    endif
    return false
endfunction

//===========================================================================
function InitTrig_Damage_Delay takes nothing returns nothing
    local trigger t = CreateTrigger()
    local trigger r = CreateTrigger()
    set gg_trg_Damage_Delay = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Damage_Delay, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddCondition( gg_trg_Damage_Delay, Condition( function Delay ) )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddCondition( t, Condition( function AttCancel ))
    call TriggerRegisterAnyUnitEventBJ( r , EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( r, Condition( function UnitDied ))
    set t = null
    set r = null
endfunction
You need a configuarations trigger too.. Just set the Animation Damage Point value into the unit-type, that is going to use this event, into its hashtable.
  • Configurations
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set Attack_Table = (Last created hashtable)
      • Set U_Type = Archer
      • Custom script: call SaveReal(udg_Attack_Table, udg_U_Type, 'dely' , 0.720)
      • Custom script: call SaveBoolean(udg_Attack_Table, udg_U_Type, 'conf' , true)
      • Set U_Type = Rifleman
      • Custom script: call SaveReal(udg_Attack_Table, udg_U_Type, 'dely' , 0.170)
      • Custom script: call SaveBoolean(udg_Attack_Table, udg_U_Type, 'conf' , true)
Known issues:
- If the unit has some kind of attack speed bonus, or attack speed reduction - the event doesn't run when it should.
~May be fixed in future versions~

UPDATED:
1) Fixed the bug with the event not runing the 1-st time.
2) Made the event run only for units, that have been configured.
3) Instead of creating new timers for every attack - I'm just re-using 1 timer for each unit.
4) Removed leaks - started clearing the unit's hashtable upon its death. (I am clearing the unit's timer hashtable upon the unit's death too. I am doing this, so people may load the last attacked unit at any point)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
The way to do this is to give a unit 100000 attack speed on its projectile, use a DDS to store the damage and nullify it for the time being, use a missile system to handle the visuals, finally apply the original damage once the projectile reaches the target.

The problem with this method is it needs every unit to be registered with it, meaning it moght as well not exist since it requires too much configuration.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Instant attack works way better than 100000 missile speed (if that's what you meant).
Anyways.. I'm just offering and alternative :p

I made this trigger for 30 mins today. I will upgrade it in the coming days, until it's as good, or even better than a DDS.
 
I guess this has it's uses, but feels limited if you can't control the missile itself or catch the damage. I use the method Bribe describes in my Volley Attack system, with a missile system, and it allows me to pretty much customise ranged attack the way I wan't to. The only main downside is that performance will die easily if you have a lot of custom missiles on the map. There's also the small issue of units aggroing you the second your missile launches because nullified damage was technically damage at one point.

Don't get me wrong though, I like that your resource exists :)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I think it's best to just link the attack with the damage via attack indexing. You reference the attack's index when the damage occurs to retrieve the data. You also do attack overwriting when attacks overflow in case damage never occurs + timeouts. This lets you calculate damage and the like when the attack is actually created and calculate defenses and the like when the attack actually impacts the target.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Yeah, a DDS is full of advantages, but this system has things to offer as well.

For example - a DDS doesn't detect the event when a unit misses, while this system does.
You don't need to give your units 100% hit chance.

And another example - as mentioned earlier, doing it with DDS requires setting the unit's attack to "instant", which may be little disturbing if you want to give your units the ability later on in the game (not letting them have it as default).
Ofcourse there are workarounds for this, but it would still be easier with this system.

But yeah, my system is NOT a DDS, so it doesn't detect damage dealt.
However, you can still make it deal some pre-set damage, and you don't need to nullify the original damage (unless you want to replace it, but then you'd need a DDS).

EDIT: I forgot to mention - you need to trigger Immolation (and other similar spells/passives) if you are using DDS, which could be really annoying :p
 
Last edited:
Level 18
Joined
Nov 21, 2012
Messages
835
Can we get unit's current animation speed? I can't find such a function, but if so, DamagePiont-depended variable can be adjusted on every EVENT_PLAYER_UNIT_ATTACKED. And you start timer on that adjusted value, not DP. That way system could support AS changes.
Btw I noticed that unit is issued instant order 851974 when auto-attack hostile. (I dont know what it is, maybe auto aquire target or unit notice enemy in range?). You may want to exclude this from function AttCancel. Or AttCancel wont allow to fire an event on auto attack.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Well, I don't know if there is a function to find unit's current animation speed. It could help indeed :p

Anyways... The logic its using now is:
A unit is attacked.
If you order that unit to attack THE SAME unit - no order is given.
If you order the unit to attack ANOTHER unit - it's given an order.

I don't know what's this order 851974, but it'd most likely follow the same logic xP
(I can't be quite sure tho)

Anyways... I will have internet for less than 2 more weeks, and then I wouldn't be able to fix/update anything for ~4 months.
Hope I can at least make it work with AS bonuses until then (if its possible to get unit's animation speed) :p
 
Level 12
Joined
Jan 2, 2016
Messages
973
Again, not worth it as an instant projectile will be easier to debug than having to find an infinite number of unpredictable variables.

That's like saying "Having an indexing system is not worth it as you can just save things into a hashtable, with unit's/timer's/trigger's (etc..) Handle Id as parent key".

Just because there is an easier way of doing things, doesn't mean that "this way" is pointless.

For example: I am using instant attack, and event "On Damage" to run my multishot, but 1 of the units using multishot has Immolation, and I need to trigger it (since otherwise multishot gets triggered by the immolation damage), which is quite uncomfortable.
 
It's not written very handy in my eyes, and some good critique has been made by others in thread.
The system basicly starts a timer for a unit, for which the duration needs to be defined by user for each unit type,
which is not bugfree enough and requires much effort for configuration.
There is some redundancy with function calls, names are generic, target group seems to be more gui than jass.

I don't see it too useful for jass section at the moment, and would graveyard it soon.
 
Level 12
Joined
Jan 2, 2016
Messages
973
It's not written very handy in my eyes, and some good critique has been made by others in thread.
The system basicly starts a timer for a unit, for which the duration needs to be defined by user for each unit type,
which is not bugfree enough and requires much effort for configuration.
There is some redundancy with function calls, names are generic, target group seems to be more gui than jass.

I don't see it too useful for jass section at the moment, and would graveyard it soon.
Yeah... I kind a wrote this about a week after I started scripting in JASS, so no wonder it seems GUI.
I could try to rewrite it now, but I don't really think I can make it bug free and easier to configure.
I once thought that I can use the ODE (object data extractor) to get the units' attack delay, but ODE bugged my map and destroyed it xP
4 months ago some guy claimed he can get WC to run bit code, thus making everything possible, but he doesn't want to share the knowledge (as he's afraid it can be misused), so I can't really finish this.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Although it's a bit of a dead-end because these natives don't take into account damage speed bonuses, a much simpler solution for this system is:

BlzGetUnitWeaponRealField(unit, UNIT_WEAPON_RF_ATTACK_DAMAGE_POINT, 0)

The solution I was proposing some years ago is this:

BlzSetUnitWeaponRealField(unit, UNIT_WEAPON_RF_ATTACK_PROJECTILE_SPEED, 0, 999999)
 
Top