How do I trigger stuff off of a unit being hit? (And more)

This is where all discussion of WC3 mapmaking should be held. This forum exists particularly for mapmaking needs. If you have a question on how something works or have need for some code review, this is the place to put it.
Post Reply
Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

How do I trigger stuff off of a unit being hit? (And more)

Post by Flak Maniak » Sat Apr 10, 2010 8:56 pm

So the other day I was making a counter ability where you cast (based off of Battle Roar), and then it leaves the self-buff on you for a couple seconds. The way I had it working was that if you were attacked during this time, you would do your counter move.

But I wasn't satisfied. One would activate it in the middle of an attack, and the enemy would hit you while you already had the buff, but you wouldn't counter. I knew it'd have this behavior going in, but it just... Didn't feel right at all. I knew I'd need some JASS to make it work the way I wanted, so I converted it to custom text and set to work on looking stuff up, because I really don't know JASS.

I found out how to get the source of damage, but I wanted the event to be right. So I changed it from EVENT_PLAYER_UNIT_ATTACKED to variants of EVENT_PLAYER_UNIT_DAMAGED and such. Every time the editor barfed that I'd done something wrong. So what's the event for this called? (Interestingly, I tried using EVENT_UNIT_DAMAGED, and it didn't object to that line, but to the one following it. Really weird.)

Now I know there's a takes damage event in GUI, but it only lets me use units already on the map, and that's a problem. Supposedly there's a way to let you use other units for this, but I've no idea how to, as it won't let me use my unit variables.

TL; DR: How do I trigger something off of a unit being damaged, or off of a unit being hit by an attack? (Do I need one of those fancy damage detection system triggers?)

Edit: The offending line looks like this once I replace the unit attacked with unit damaged:
call TriggerRegisterAnyUnitEventBJ( gg_trg_Counter_JASS, EVENT_PLAYER_UNIT_DAMAGED )

Edit again: On a semi-related note, I'd made the guy's cast point more than a second in on account of this spell, but I want it to be much sooner for other spells. Is there a way to change cast points? Or to at least fake it for one spell or the other, without letting people do cancel exploits?
Last edited by Flak Maniak on Thu Apr 22, 2010 6:28 pm, edited 1 time in total.

Greenspawn
Keeper of the Keys
Posts: 434
Joined: Fri Jan 18, 2008 4:50 pm
Location: Massachusetts

Re: How do I trigger stuff off of a unit being hit?

Post by Greenspawn » Sun Apr 11, 2010 2:00 pm

Use Dusk's damage detection system.
Math is # |e^iπ|
"I can't imagine getting hit by a giant rock and not being maimed or crippled or ruined" -Dusk

Logue: Please replace the toilet paper when you use it all. For some reason my 5 year old son believes if it's not there he does not have to wipe.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Sun Apr 11, 2010 2:37 pm

So exactly how would implementing that work? Do I just put in one damage detection trigger and reference it somehow? Basically, I've little to no idea what I'm doing in JASS, and while I've seen that code before, I'm completely befuddled.

Edit: So, in noobite fashion, I slotted in Table and IDDS with no modifications, and the editor barfed. I suppose I'll have to look through them and actually figure out what the code means, and how to make stuff interact with it once I can stop committing JASSeppuku.

Edit again: Oh crap, Table is taking issue with all my other triggers. This is bad.
...And so is IDDS.

Oh, and on a side note, is there a way to make Chaos damage from attacks ignore armor value? Would I have to use IDDS to trigger all damage to do that?

Greenspawn
Keeper of the Keys
Posts: 434
Joined: Fri Jan 18, 2008 4:50 pm
Location: Massachusetts

Re: How do I trigger stuff off of a unit being hit?

Post by Greenspawn » Tue Apr 13, 2010 9:05 pm

Flak Maniak wrote:So exactly how would implementing that work? Do I just put in one damage detection trigger and reference it somehow? Basically, I've little to no idea what I'm doing in JASS, and while I've seen that code before, I'm completely befuddled.
In your initialization function:

Code: Select all

scope Trg
public function InitTrig takes nothing returns nothing
    local trigger trg = CreateTrigger()
    call TriggerAddAction(trg, function Actions)
    call TriggerAddCondition(trg, Condition(function Conditions))
    call TriggerRegisterDamageEvent(trg)       **
    set trg = null
endfunction
endscope
** : This function adds this trigger to the list of triggers which run when any unit takes damage. You use conditions to specify when this trigger actually does something (like if a hero takes damage, and the hero belongs to player 1, or if the damage type is of a certain type, etc).

Then there are the functions:

GetTriggerDamageSource() which returns the damaging unit.
GetTriggerDamageTarget() which returns the damaged unit.
GetTriggerDamage() which returns the damage done to the the damaged unit.
GetTriggerDamageType() which returns the damage type of the damage dealt.
UnitDamageTargetEx(source, target, damage, AttackType, DamageType, ConsiderArmor) which causes the source unit to deal damage to the target unit with a certain attack type, damage type, and whether or not to consider armor.

Basically you put the IDDS into your map code as a trigger, and then in your triggers you use these functions to write your maps. It automatically detects when the damage is dealt, and you just have to write the triggers that react to the event. You do have to write your triggers in JASS though.
Last edited by Greenspawn on Fri Apr 30, 2010 10:37 pm, edited 1 time in total.
Math is # |e^iπ|
"I can't imagine getting hit by a giant rock and not being maimed or crippled or ruined" -Dusk

Logue: Please replace the toilet paper when you use it all. For some reason my 5 year old son believes if it's not there he does not have to wipe.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Tue Apr 13, 2010 10:47 pm

So is it necessary to have even my triggers that don't use this system in JASS? Because the system seems to barf at all my triggers (and when I added a blank trigger to the example map, the game disabled every single trigger on that map when I tried to save. Well, besides the blank trigger. And by blank I mean I added a trigger and didn't do anything. I didn't make it custom text and blank it out.)

Basically, how do I make my current triggers agreeable to Table and IDDS? Is just converting them to custom text enough? Or do I have to do something specific to them?

Edit: So after looking more, it's actually objecting to my variables, which I find really weird. I'll look for Table documentation.

Okay, so I got the NewGen stuff, and then re-enabled Table and IDDS, and now jasshelper is complaining. Argh. Specifically, I'm supposed to put in InitTrig Table and InitTrig IDDS somewhere, but I'm at a loss as to where...

Also, thanks for tolerating my JASS noobery. It's great to have helpful people I can ask about stuff.

Okay, so, after having looked at blank triggers JASSified, I figured out how to add InitTrig IDDS and InitTrig Table. Problem is, when I go to run the map, when I select it from the list it uses the player allotment from whatever map I last had selected, and when I actually try to play it, it acts as if it's gonna run, then sends me back to the map chooser. I've actually had this issue before, when I got Age of Myths off the 'net and tried to run it. Also, NewGen World Editor is working great, but NewGenWarcraft can't find game.dll. Oh well.

User avatar
Fledermaus
Keeper of the Keys
Posts: 354
Joined: Fri Feb 01, 2008 9:55 am
Location: New Zealand
Contact:

Re: How do I trigger stuff off of a unit being hit?

Post by Fledermaus » Wed Apr 14, 2010 8:46 pm

EDIT: I can't read, delete this post please.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Mon Apr 19, 2010 11:36 pm

Okay, so, progress: I put Table and IDDS in the "map-specific custom code" thingy, and the editor stopped complaining about them. But when I try to use the damage detection function in my counter trigger... JASSHelper barfs again. My trigger's script looks like this:

function Trig_Counter_JASS_Conditions takes nothing returns boolean
if ( not ( IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) == true ) ) then
return false
endif
if ( not ( UnitHasBuffBJ(GetTriggerUnit(), 'B00Q') == true ) ) then
return false
endif
return true
endfunction

function Trig_Counter_JASS_Actions takes nothing returns nothing
call PauseUnitBJ( true, GetTriggerUnit() )
call SetUnitInvulnerable( GetTriggerUnit(), true )
call SetUnitManaBJ( GetTriggerUnit(), ( GetUnitStateSwap(UNIT_STATE_MANA, GetTriggerUnit()) + 50.00 ) )
call RemoveLocation( udg_CounterStartPoint )
set udg_Counterer = GetTriggerUnit()
set udg_Counteree = GetEventDamageSource()
set udg_CounterStartPoint = GetUnitLoc(GetTriggerUnit())
call UnitRemoveBuffBJ( 'B00Q', GetTriggerUnit() )
call SetUnitAnimation( GetTriggerUnit(), "attack" )
call PolledWait( 0.01 )
set udg_TempPoint = GetUnitLoc(GetEventDamageSource())
set udg_TempPoint2 = PolarProjectionBJ(udg_TempPoint, 150.00, AngleBetweenPoints(udg_CounterStartPoint, udg_TempPoint))
call SetUnitPositionLoc( GetTriggerUnit(), udg_TempPoint2 )
call RemoveLocation( udg_TempPoint )
call RemoveLocation( udg_TempPoint2 )
call UnitDamageTargetBJ( udg_Counterer, udg_Counteree, ( I2R(GetHeroStatBJ(bj_HEROSTAT_AGI, GetTriggerUnit(), true)) + ( 40.00 * I2R(GetUnitAbilityLevelSwapped('A04I', udg_Counterer)) ) ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
call SetUnitInvulnerable( GetTriggerUnit(), false )
call PauseUnitBJ( false, GetTriggerUnit() )
endfunction

//===========================================================================
function InitTrig_Counter_JASS takes nothing returns nothing
set gg_trg_Counter_JASS = CreateTrigger( )
call TriggerRegisterDamageEvent takes trigger trg, integer priority returns boolean
call TriggerAddCondition( gg_trg_Counter_JASS, Condition( function Trig_Counter_JASS_Conditions ) )
call TriggerAddAction( gg_trg_Counter_JASS, function Trig_Counter_JASS_Actions )
endfunction

So... I think it's a problem with the TriggerRegisterDamageEvent line... What's wrong with it?

Again, thanks for the help.

User avatar
Fledermaus
Keeper of the Keys
Posts: 354
Joined: Fri Feb 01, 2008 9:55 am
Location: New Zealand
Contact:

Re: How do I trigger stuff off of a unit being hit?

Post by Fledermaus » Tue Apr 20, 2010 3:16 am

change it to "call TriggerRegisterDamageEvent(gg_trg_Counter_JASS, 5)" (without the "" obviously)

You could change to 5 to any number above 0. The lower the number is, the earlier that trigger will run whenever damage is done.



Your trigger (with the current setup) will run whenever the target deals any damage (not just attack damage).

To fix this to work like you want, you need to do 2 things.
The first is an easy fix to the conditions:

Code: Select all

function Trig_Counter_JASS_Conditions takes nothing returns boolean
    return IsUnitType(GetTriggerDamageTarget(), UNIT_TYPE_HERO) and GetUnitAbilityLevel(GetTriggerDamageTarget(), 'B00Q') > 0 and GetTriggerDamageType() == DAMAGE_TYPE_ATTACK
endif
return true
endfunction
The second thing you need to do make all damage in your map (that isn't caused by an attack) triggered using the UnitDamageTargetEx function provided in Dusk's Damage Detection system. If you don't there's no easy and accurate way to differentiate between attack and ability damage so your counter attack would fire on ability damage as well.


Since you seem to be a GUIer, you have 2 options: either say "twink it" and allow the ability to fire on any damage, or learn jass/vJass. I recommend the latter as it will greatly improve your map.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Tue Apr 20, 2010 11:05 pm

I do intend to make all the triggered damage in my map use that function. I'm slowly learning JASS. But at the same time, this ability is supposed to work off of all damage. Anyway, thanks, I'll try your fixes now.

Edit: It works! I AM A GOD. MY POWER IS LIMITLESS! *ahem* what I mean to say is, Dusk is a god for having made such a great system.

Edit again: I then tried to use this tech for another trigger, but... Again, the editor complains. What's wrong with this code?:

function Trig_Telling_Blow_Conditions takes nothing returns boolean
if ( not ( IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == true ) ) then
return false
endif
if ( not ( GetEventDamageSource() == udg_TellingBlowCaster ) ) then
return false
endif
if ( not ( udg_TellingBlowOn == true ) ) then
return false
endif
return true
endfunction

function Trig_Telling_Blow_Actions takes nothing returns nothing
set udg_TellingBlowOn = false
set udg_TempPoint = GetUnitLoc(GetEventDamageSource())
call AddSpecialEffectLocBJ( udg_TempPoint, "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl" )
call ConditionalTriggerExecute( gg_trg_Spell_Effects_Buffer )
call RemoveLocation( udg_TempPoint )
call SetUnitAnimation( GetEventDamageSource(), "attack" )
call PolledWait( 0.01 )
set udg_TempPoint = GetUnitLoc(GetTriggerUnit())
set udg_TempPoint2 = PolarProjectionBJ(udg_TempPoint, 150.00, AngleBetweenPoints(GetUnitLoc(GetEventDamageSource) udg_TempPoint))
call SetUnitPositionLoc( GetEventDamageSource(), udg_TempPoint2 )
call RemoveLocation( udg_TempPoint )
call RemoveLocation( udg_TempPoint2 )
call UnitDamageTargetBJ( GetEventDamageSource(), GetTriggerUnit(), ( 40.00 + ( 10.00 * I2R(GetUnitAbilityLevelSwapped('A04J', GetEventDamageSource())) ) ), ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
endfunction

//===========================================================================
function InitTrig_Telling_Blow takes nothing returns nothing
set gg_trg_Telling_Blow = CreateTrigger( )
call TriggerRegisterDamageEvent(gg_trg_Telling_Blow, 5)
call TriggerAddCondition( gg_trg_Telling_Blow, Condition( function Trig_Telling_Blow_Conditions ) )
call TriggerAddAction( gg_trg_Telling_Blow, function Trig_Telling_Blow_Actions )
endfunction

Basically, it's the going off trigger of an attack preparation. If you've got the preparation, when you deal damage, you do your attack. But... JASSHelper takes issue.

And one more thing: Could you give me an example of a valid UnitDamageTargetEx? (I'm trying to learn JASS by slotting stuff into my map and slowly learning to modify it.)

User avatar
Fledermaus
Keeper of the Keys
Posts: 354
Joined: Fri Feb 01, 2008 9:55 am
Location: New Zealand
Contact:

Re: How do I trigger stuff off of a unit being hit?

Post by Fledermaus » Wed Apr 21, 2010 6:21 pm

Oh, well if you want the first trigger to run off any damage, remove the and GetTriggerDamageType() == DAMAGE_TYPE_ATTACK" from the condition.

Also (still in the first trigger) you should be using the wrapper functions from Dusks system rather than these:
set udg_Counterer = GetTriggerUnit()
set udg_Counteree = GetEventDamageSource()

should be:
set udg_Counterer = GetTriggerDamageTarget()
set udg_Counteree = GetTriggerDamageSource()

You should use those 2 functions to get the target/source whenever you're using a DamageEvent (in the conditions and actions).


On to your new trigger, from just reading over it I can't see anything that would cause an error. What is jasshelper telling you is wrong?
There are some code improvements I would recommend though:
Again, merge the conditions into a 1 line return (and change it to use GetTriggerDamageSource/Target)

Code: Select all

function Trig_Telling_Blow_Conditions takes nothing returns boolean
    return IsUnitType(GetTriggerDamageTarget(), UNIT_TYPE_HERO)and GetTriggerDamageSource() == udg_TellingBlowCaster and udg_TellingBlowOn
endfunction
And some stuff to the actions

Code: Select all

function Trig_Telling_Blow_Actions takes nothing returns nothing
    local unit    s      = GetTriggerDamageSource()
    local unit    t      = GetTriggerDamageTarget()
    local real    x      = GetUnitX(s)
    local real    y      = GetUnitY(s)
    local real    a      = bj_RADTODEG * Atan2(GetUnitY(t) - y, GetUnitX(t) - x)
    local integer level  = GetUnitAbilityLevel(s, 'A04J')
    local real    damage = 40. + (10. * level)
    
    set udg_TellingBlowOn = false
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl", x, y))
    call ConditionalTriggerExecute(gg_trg_Spell_Effects_Buffer) //I'm not sure what this does, you might be able to put it into this trigger
    call SetUnitAnimation(s, "attack")
    call PolledWait(0.01) //PolledWait is terriable and should never be used once you get better at jass
    set x = x + 150. * Cos(a * bj_DEGTORAD) //This stuff is the same as PolarProjectionBJ but doesn't use a point
    set y = y + 150. * Sin(a * bj_DEGTORAD) //so it's better since you don't have to worry about leaking the handle
    call SetUnitPosition(s, x, y)
    call UnitDamageTargetEx(s, t, damage, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_SPELL, false)
    
    set s = null
    set t = null
endfunction

For an example on how to use UnitDamageTargetEx, you should just read through Dusk's documentation. It's the comments (green stuff) at the start of the code.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Wed Apr 21, 2010 8:00 pm

JASSHelper is actually very unhelpful. When it finds something wrong, it just... Sits there, its progress bar frozen. I have to use the Task Manager to kill JASSHelper. It's annoying. Anyway, the code stopped getting objected to, but your code seems to move the damaging unit 150 forwards, which is not what I want. I want it to go 150 past the target. So I replaced s with t in terms of setting x and y, and then it got weird, where he would go past from some angles but jump to the side from some others.

User avatar
Fledermaus
Keeper of the Keys
Posts: 354
Joined: Fri Feb 01, 2008 9:55 am
Location: New Zealand
Contact:

Re: How do I trigger stuff off of a unit being hit?

Post by Fledermaus » Wed Apr 21, 2010 10:31 pm

Ah right, just change

Code: Select all

    local real    x      = GetUnitX(s)
    local real    y      = GetUnitY(s)
    local real    a      = bj_RADTODEG * Atan2(GetUnitY(t) - y, GetUnitX(t) - x)
to

Code: Select all

    local real    x      = GetUnitX(t)
    local real    y      = GetUnitY(t)
    local real    a      = bj_RADTODEG * Atan2(GetUnitY(s) - y, GetUnitX(s) - x)
Jasshelper freezing up means that your anti-virus program is blocking it.

Flak Maniak
Noobite Warrior
Posts: 16
Joined: Sun Feb 28, 2010 1:05 am

Re: How do I trigger stuff off of a unit being hit?

Post by Flak Maniak » Wed Apr 21, 2010 11:46 pm

Must be Win7's inherent antivirus, because I don't have any specific antivirus software on here. Anyway, when I try modifying it like that, the guy doesn't teleport at all and the Thunder Clap appears on the target. I'll mess around more. I'm ashamed not to know enough calculus to make sense of the equations that make the angles right...

Edit: Got all the stuff to work right. Used some points, but oh well, I made sure they were cleaned up.

New question, though: Is there a way to move a unit with a trigger without resetting its animation? I want to have Telling Blow do the teleport behind them thing without stopping your attack animation. It'd look super awesome.

Another thing: If I want to have a spell like, say, Shockwave do some bonus thing to people it hits... I'd have to remake Shockwave from scratch, right? So I can tell its damage apart from attack damage?

Post Reply

Return to “Custom Map Creation”