• 🏆 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!

trigger attacked using skill

Status
Not open for further replies.
Level 6
Joined
Aug 5, 2015
Messages
202
if unit A get attacked
when attacked or take damage i want this unit use example shockwave to attacker
but if atk spd too fast he will repeat cast till he dead
even with damage detection system this wont solve this problem
any clue to prevent repeat cast?
this prob didnt applied to no target skill (only apply to target skill)
 
Level 10
Joined
Jun 6, 2007
Messages
392
An easy solution would be to reduce unit's cast backswing point in object editor. However, that will affect all abilities.

I have used this vJass trigger for a similar purpose. It contains a lot of useless stuff as I've edited it from another spell, but basically you give the unit a buff placer ability (e.g. based on slow poison), and set the BUFF_ID accordingly. Alternatively you could change the trigger so, that it checks if damage source has a specific ability and damage is physical. Requires DDS.
JASS:
//#################################
//#Shockwave triggered version    #
//#Created by Erkki2              #
//#################################
scope LinearAttack

//  #################
//  # CONFIGURABLES #
//  #################
    globals
        
        //Raw code of the spell ability
        private constant integer ABIL_CODE = 'A034'
        
        private constant integer DUMMY_ID = 'n002'
        
        private constant integer BUFF_ID = 'B01C'
        
        private constant integer CHANCE = 20
        
        //Iteration interval
        private constant real TIMEOUT = 0.04
        
        //The missile effect
        private constant string MISSILE_EFFECT = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl"
        
        //Raw code of the dummy ability. This spell will be cast on all units hit by the spell. If no spell is wanted, set this to 0.
        private constant integer DUMMY_ABILITY = 0
        
        //Order string for dummy spell
        private constant string DUMMY_ORDER = ""
        
        //Speed of the projectile
        private constant real SPEED = 700.0
        
        //Set this to true, if you want a unit to be damaged only once by a single spell
        private constant boolean DAMAGE_ONLY_ONCE = true
        
        //Set this to true to enable terrain deformation
        private constant boolean TERRAIN_DEFORMATION = false
        
        //This determines how wide area is damaged. If < 1, the whole are isn't covered. If 2, every point in the path is damaged twice.
        private constant real DAMAGE_OVERLAP = 2.0
        
        //If true, the spell area is filled by selected special effects
        private constant boolean FILL_AREA = false
        
        //Distance between fill particles
        private constant real FILL_DISTANCE = 30.0
        
        //Special effect which is used for filling area
        private constant string FILL_EFFECT = ""
        
        //If true, fill effects are attached on dummy units for a more realistic effect. If that is not necessary,
        //it is advisable to have this option set to false for better performance.
        private constant boolean FILL_CORRECT_FACING = false
        
        //If true, secondary missiles are sent following the borders of the spell area
        private constant boolean BORDER_MISSILES = false
        
        //Art of the secondary missiles
        private constant string BORDER_EFFECT = ""

    endglobals

    //The spell distance
    private function setDistance takes unit caster returns real
        return 700.0
    endfunction
    
    //Cone width at caster
    private function setStartArea takes unit caster returns real
        return 100.0
    endfunction
    
    //Cone width at target
    private function setEndArea takes unit caster returns real
        return 100.0
    endfunction
    
    //Damage that is dealt by spell
    private function setDamage takes unit caster returns real
        return 50.0
    endfunction

    //Returns true, if target unit can be hit by the spell
    private function isTargetAllowed takes unit target, unit caster returns boolean
        return IsUnitEnemy(target, GetOwningPlayer(caster)) and GetUnitState(target, UNIT_STATE_LIFE) > 0
    endfunction
    
//  ############################################################
//  # CONFIGURABLES END, Don't touch anything after this line. #
//  ############################################################

    private struct Spell
    
        private thistype next
        private thistype prev
        private static timer iterator = CreateTimer()
        private static integer count = 0
        private static group enumGroup = CreateGroup()
        
        private unit caster
        private unit dummy
        private integer executions
        private effect eff
        private real dx
        private real dy
        private real tipX
        private real tipY
        private real sliceWidth
        private real maxArea
        private real damage
        private real rightAngle
        private real leftAngle
        private group damagedUnits
        private real x
        private real y
        
        private real currWidth
        private real widthInc
        private real angle
        
        private real border1X
        private real border1Y
        private real border2X
        private real border2Y
        private unit border1Unit
        private unit border2Unit
        private effect border1Eff
        private effect border2Eff
        
        private method destroy takes nothing returns nothing
            call this.deallocate()
            set this.next.prev = this.prev
            set this.prev.next = this.next
            set count = count - 1
            call KillUnit(this.dummy)
            call KillUnit(this.border1Unit)
            call KillUnit(this.border2Unit)
            call DestroyEffect(this.eff)
            call DestroyEffect(this.border1Eff)
            call DestroyEffect(this.border2Eff)
            call DestroyGroup(this.damagedUnits)
            set this.damagedUnits = null
            set this.border1Unit = null
            set this.border2Unit = null
            set this.border1Eff = null
            set this.border2Eff = null
            set this.eff = null
            set this.caster = null
            set this.dummy = null
        endmethod
        
        private static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            local unit u
            local real dummyDist
            local real targetDist
            local real diffX
            local real diffY
            local real angleToTip
            local real targetX
            local real targetY
            local unit dummyCaster
            local real width
            local real reverseAngle
                        
            loop
                exitwhen this == 0
            
                if this.executions == 0 then
                    call this.destroy()
                else
                
                    //Move the projectile
                    set this.x = GetUnitX(this.dummy) + this.dx
                    set this.y = GetUnitY(this.dummy) + this.dy
                    call SetUnitX(this.dummy, this.x)
                    call SetUnitY(this.dummy, this.y)
                    
                    //if border missiles are enabled, move them too
                    static if BORDER_MISSILES then
                        call SetUnitX(this.border1Unit, GetUnitX(this.border1Unit) + this.border1X)
                        call SetUnitY(this.border1Unit, GetUnitY(this.border1Unit) + this.border1Y)
                        call SetUnitX(this.border2Unit, GetUnitX(this.border2Unit) + this.border2X)
                        call SetUnitY(this.border2Unit, GetUnitY(this.border2Unit) + this.border2Y)
                    endif
                    
                    static if FILL_AREA then
                        set width = 0
                        set reverseAngle = this.angle + bj_PI/2
                        
                        //Create one effect at the middle line
                        static if FILL_CORRECT_FACING then
                            set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x, this.y, Rad2Deg(this.angle))
                            call DestroyEffect(AddSpecialEffectTarget(FILL_EFFECT, u, "origin"))
                            call KillUnit(u)
                        else
                            call DestroyEffect(AddSpecialEffect(FILL_EFFECT, this.x, this.y))
                        endif
                        
                        //Create N more effects vertical to the cast angle, with FILL_DISTANCE between each effect
                        loop
                            //Set offset from middle line, and create effect on each side
                            set width = width + FILL_DISTANCE
                            exitwhen width > this.currWidth
                            static if FILL_CORRECT_FACING then
                                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x + width*Cos(reverseAngle), this.y + width*Sin(reverseAngle), Rad2Deg(this.angle))
                                call DestroyEffect(AddSpecialEffectTarget(FILL_EFFECT, u, "origin"))
                                call KillUnit(u)
                                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x - width*Cos(reverseAngle), this.y - width*Sin(reverseAngle), Rad2Deg(this.angle))
                                call DestroyEffect(AddSpecialEffectTarget(FILL_EFFECT, u, "origin"))
                                call KillUnit(u)
                            else
                                call DestroyEffect(AddSpecialEffect(FILL_EFFECT, this.x + width*Cos(reverseAngle), this.y + width*Sin(reverseAngle)))
                                call DestroyEffect(AddSpecialEffect(FILL_EFFECT, this.x - width*Cos(reverseAngle), this.y - width*Sin(reverseAngle)))
                            endif
                        endloop
                        set this.currWidth = this.currWidth + this.widthInc
                    endif
                    
                    call GroupEnumUnitsInRange(enumGroup, this.x, this.y, this.maxArea, null)
                    loop
                        set u = FirstOfGroup(enumGroup)
                        exitwhen u == null
                                
                        set targetX = GetUnitX(u)
                        set targetY = GetUnitY(u)
                        
                        //Calculate target unit's distance from the tip of the cone
                        set diffX = targetX - tipX
                        set diffY = targetY - tipY
                        set targetDist = SquareRoot(diffX*diffX + diffY*diffY)
                        
                        //Calculate the distance between the projectile and tip
                        set diffX = this.x - tipX
                        set diffY = this.y - tipY
                        set dummyDist = SquareRoot(diffX*diffX + diffY*diffY)
                        
                        //Calculate target unit's direction from the tip
                        set angleToTip = Atan2(this.tipY - targetY, this.tipX - targetX)
                                
                        //The unit is in the right direction...
                        if ((angleToTip > this.leftAngle and angleToTip < this.rightAngle)                                      /* 
*/                      or (this.leftAngle > this.rightAngle and (angleToTip < this.leftAngle or angleToTip > this.rightAngle)))  /*
                        //...and at the right distance.
*/                      and targetDist - dummyDist < this.sliceWidth and dummyDist - targetDist < this.sliceWidth               /*
                        //Check allowed targets
*/                      and isTargetAllowed(u, this.caster) then

                            //If DAMAGE_ONLY_ONCE is enabled, only damage units which aren't in damagedUnits group, and add damaged units to group...
                            static if DAMAGE_ONLY_ONCE then
                                if not IsUnitInGroup(u, this.damagedUnits) then
                                    call UnitDamageTarget(this.caster, u, this.damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
                                    call GroupAddUnit(this.damagedUnits, u)
                                    if not (DUMMY_ABILITY == 0) then
                                        set dummyCaster = CreateUnit(GetOwningPlayer(this.caster), DUMMY_ID, this.x, this.y, 0)
                                        call UnitApplyTimedLife(dummyCaster, 'BTLF', 3)
                                        call ShowUnit(dummyCaster, false)
                                        call UnitAddAbility(dummyCaster, DUMMY_ABILITY)
                                        call SetUnitAbilityLevel(dummyCaster, DUMMY_ABILITY, GetUnitAbilityLevel(this.caster, ABIL_CODE))
                                        call IssueTargetOrder(dummyCaster, DUMMY_ORDER, u)
                                    endif
                                endif
                            //...else damage just damage units
                            else
                                call UnitDamageTarget(this.caster, u, this.damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
                                if not (DUMMY_ABILITY == 0) then
                                    set dummyCaster = CreateUnit(GetOwningPlayer(this.caster), DUMMY_ID, this.x, this.y, 0)
                                    call UnitApplyTimedLife(dummyCaster, 'BTLF', 3)
                                    call ShowUnit(dummyCaster, false)
                                    call UnitAddAbility(dummyCaster, DUMMY_ABILITY)
                                    call SetUnitAbilityLevel(dummyCaster, DUMMY_ABILITY, GetUnitAbilityLevel(this.caster, ABIL_CODE))
                                    call IssueTargetOrder(dummyCaster, DUMMY_ORDER, u)
                                endif
                            endif
                        endif  
                        call GroupRemoveUnit(enumGroup, u)
                    endloop
                    
                    set this.executions = this.executions - 1
                endif
            
                set this = this.next
            
            endloop
        
            //if there are no spell instances running, pause the timer
            if count == 0 then
                call PauseTimer(iterator)
            endif
            
            set u = null
            set dummyCaster = null
        endmethod

        private static method run takes nothing returns boolean
            local thistype this
            local real distPerIter
            local real distance
            local real startArea
            local real endArea
            local real distToTip
            local real targetX
            local real targetY
            local real areaOffsetX
            local real areaOffsetY
            
            if not (GetUnitAbilityLevel(udg_DamageEventTarget, BUFF_ID) == 0) then
        
                call UnitRemoveAbility(udg_DamageEventTarget, BUFF_ID)
                
                if GetRandomInt(0, 99) < CHANCE then
                    set this = thistype.allocate()
                    set this.next = 0
                    set this.prev = thistype(0).prev
                    set thistype(0).prev.next = this
                    set thistype(0).prev = this
                    set count = count + 1
                    
                    if count == 1 then
                        call TimerStart(iterator, TIMEOUT, true, function thistype.periodic)
                    endif

                    set this.caster = udg_DamageEventSource
                    
                    set this.x = GetUnitX(this.caster)
                    set this.y = GetUnitY(this.caster)
                    set targetX = GetUnitX(udg_DamageEventTarget)
                    set targetY = GetUnitY(udg_DamageEventTarget)
                    
                    set distance = setDistance(this.caster)
                    set this.executions = R2I(distance/SPEED/TIMEOUT)
                    
                    //Angle from cast point to target point
                    set this.angle = Atan2(targetY - this.y, targetX - this.x)
                    
                    //set target point to a "correct" value
                    set targetX = this.x + distance*Cos(this.angle)
                    set targetY = this.y + distance*Sin(this.angle)

                    //Calculate travelled distance per iteration
                    set distPerIter = distance/this.executions
                    set this.dx = distPerIter*Cos(this.angle)
                    set this.dy = distPerIter*Sin(this.angle)
                    
                    //Create projectile
                    set this.dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x, this.y, Rad2Deg(this.angle))
                    set this.eff = AddSpecialEffectTarget(MISSILE_EFFECT, this.dummy, "origin")
                    
                    //Calculate the width of an area, which is damaged per iteration
                    set this.sliceWidth = DAMAGE_OVERLAP*distPerIter/2
                    set startArea = setStartArea(this.caster)
                    set endArea = setEndArea(this.caster)
                    
                    //Cone is \/ shaped (cast upwards)
                    if startArea < endArea then
                    
                        //Tip of the cone is at cast point
                        if startArea == 0 then
                            set distToTip = 0
                        else
                            set distToTip = distance*startArea/(endArea - startArea)
                        endif
                        set this.tipX = this.x - distToTip*Cos(this.angle)
                        set this.tipY = this.y - distToTip*Sin(this.angle)
                        set this.maxArea = endArea
                        
                        //Calculate borderlines of the cone
                        set areaOffsetX = endArea*Cos(this.angle + bj_PI/2)
                        set areaOffsetY = endArea*Sin(this.angle + bj_PI/2)
                        set this.rightAngle = Atan2(this.tipY - (targetY + areaOffsetY), this.tipX - (targetX + areaOffsetX))
                        set this.leftAngle = Atan2(this.tipY - (targetY - areaOffsetY), this.tipX - (targetX - areaOffsetX))
                        
                    //The cone is /\ shaped (cast upwards)
                    elseif startArea > endArea then
                    
                        //Tip of the cone is at target point
                        if endArea == 0 then
                            set distToTip = 0
                        else
                            set distToTip = distance*endArea/(startArea - endArea)
                        endif
                        set this.tipX = targetX + distToTip*Cos(this.angle)
                        set this.tipY = targetY + distToTip*Sin(this.angle)
                        set this.maxArea = startArea
                        
                        //Calculate borderlines of the cone
                        set areaOffsetX = startArea*Cos(this.angle + bj_PI/2)
                        set areaOffsetY = startArea*Sin(this.angle + bj_PI/2)
                        set this.leftAngle = Atan2(this.tipY - (this.y + areaOffsetY), this.tipX - (this.x + areaOffsetX))
                        set this.rightAngle = Atan2(this.tipY - (this.y - areaOffsetY), this.tipX - (this.x - areaOffsetX))
                    
                    //The Cone is || shaped, in other words a line
                    else // startArea == endArea
                    
                        //Set tip of the cone very far behind the caster, which makes the cone to spread very little
                        set distToTip = 100000
                        set this.tipX = this.x - distToTip*Cos(this.angle)
                        set this.tipY = this.y - distToTip*Sin(this.angle)
                        set this.maxArea = startArea
                        
                        //Calculate borderlines of the cone
                        set areaOffsetX = endArea*Cos(this.angle + bj_PI/2)
                        set areaOffsetY = endArea*Sin(this.angle + bj_PI/2)
                        set this.rightAngle = Atan2(this.tipY - (this.y + areaOffsetY), this.tipX - (this.x + areaOffsetX))
                        set this.leftAngle = Atan2(this.tipY - (this.y - areaOffsetY), this.tipX - (this.x - areaOffsetX))
                    endif
                    
                    set this.damage = setDamage(this.caster)
                    set this.damagedUnits = CreateGroup()
                    
                    static if TERRAIN_DEFORMATION then
                        call TerrainDeformWave(this.x, this.y, targetX, targetY, distance, SPEED, startArea, 150, 2, 1)
                    endif
                    
                    static if FILL_AREA then
                        set this.currWidth = startArea
                        set this.widthInc = (endArea - startArea)/this.executions
                    endif
                    
                    static if BORDER_MISSILES then
                        set border1X = -distPerIter*Cos(this.rightAngle)
                        set border1Y = -distPerIter*Sin(this.rightAngle)
                        set border2X = -distPerIter*Cos(this.leftAngle)
                        set border2Y = -distPerIter*Sin(this.leftAngle)
                        set areaOffsetX = startArea*Cos(this.angle + bj_PI/2)
                        set areaOffsetY = startArea*Sin(this.angle + bj_PI/2)
                        set this.border1Unit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x + areaOffsetX, this.y + areaOffsetY, Rad2Deg(this.rightAngle))
                        set this.border2Unit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, this.x - areaOffsetX, this.y - areaOffsetY, Rad2Deg(this.leftAngle))
                        set this.border1Eff = AddSpecialEffectTarget(BORDER_EFFECT, this.border1Unit, "origin")
                        set this.border2Eff = AddSpecialEffectTarget(BORDER_EFFECT, this.border2Unit, "origin")
                    endif
                endif
            endif

            return false
        endmethod
    
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterVariableEvent( t, "udg_DamageEvent", EQUAL, 1.00 )
            call TriggerAddCondition(t, Condition(function thistype.run))
            set t = null
        endmethod
        
    endstruct

endscope
 
Level 6
Joined
Aug 5, 2015
Messages
202
wait that jass you showed me is for 1 ability
if the boss have 4 ability, thats mean i need that 4x of that jass?

btw right now im using trigger toying with boss hp and via turn off turn on trigger
hp less than 10k example
turn on the trigger: if unit attacked use shockwave to attacker then turn off trigger
 
Level 10
Joined
Jun 6, 2007
Messages
392
Well, on my map, I have a unit which has a chance to damage line on its basic attacks, and that's what that trigger does.

I reread your post, and it seems that I misunderstood what you meant. DEE-BOO's trigger should do what you're asking for.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
but if he get attacked multiple times he will keep recast shockwave right? keep change the target everytime he get attacked
No.

So in your 'Unit is Attacked Trigger' at the top of the trigger, Turn the trigger off, then order your unit to cast shockwave.

Make another trigger that when Shockwave is cast, it turns that trigger back on.
No.

then like i said, i use trigger after cast turn trigger off
then if hp less than xxx turn on that trigger again
No.

That unit will NOT restart casting because you check if he is already casting.
If the unit is doing something else than casting the ability, then you order him to cast.
 
Status
Not open for further replies.
Top