• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Damage Detected Knockback System (DDKB)

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,547
This is an entirely self contained system which adds knockback whenever a unit receives damage. For demonstration purposes it is placed in a wc3 melee map. It's that simple.

Includes a format for organizing units that deal piercing damage. I hashed out all the melee ones myself, but custom units must be added manually. You can thank blizzard for this because they didn't include a way to GetUnitAttackType.

Because the system consists of multiple triggers (all 1 folder), I didn't include them.

The Code:


libIsUnitPiercing:
JASS:
globals
    integer array uIDs
    constant integer uIDI=139                   //See values below to see which units to include.
    constant boolean playersCanHaveCreeps=false //Change this to true if players can have creeps. Will add load to DDKB.
endglobals
    
library IsUnitPiercing initializer i
    function isPierce takes unit u returns boolean
        local integer i=0
        loop
            exitwhen i>uIDI
            if GetUnitTypeId(u)==uIDs[i] then
                return true
            endif
            if i>28 and GetPlayerId(GetOwningPlayer(u))<12 and playersCanHaveCreeps==false then
                return false
            endif
            set i=i+1
        endloop
        return false
    endfunction
    
    private function i takes nothing returns nothing
        //Human Melee
        set uIDs[0] = 'hrif' //Rifleman
        set uIDs[1] = 'hgyr' //Gyrocopter
        set uIDs[2] = 'hgry' //Gryphon Rider
        set uIDs[3] = 'hdhw' //Dragonhawk Rider
        set uIDs[4] = 'hgtw' //Guard Tower
        set uIDs[5] = 'hwat' //Water Elemental
        set uIDs[6] = 'hwt2' //Water Elemental 2
        set uIDs[7] = 'hwt3' //Water Elemental 3
        //Orc Melee
        set uIDs[8] = 'ohun' //Head Hunter
        set uIDs[9] = 'okod' //Kodo Beast
        set uIDs[10]= 'owyv' //Wind Rider/Wyvern
        set uIDs[11]= 'otrb' //Orc Burrow
        set uIDs[12]= 'owtw' //Watch Tower
        set uIDs[13]= 'osp1' //Serpant Ward Level 1
        set uIDs[14]= 'osp2' //Serpant Ward Level 2
        set uIDs[15]= 'osp3' //Serpant Ward Level 3
        set uIDs[16]= 'osp4' //Serpant Ward Level 4
        set uIDs[17]= 'otbk' //Berzerker
        //Undead Melee
        set uIDs[18]= 'ucry' //Crypt Fiend
        set uIDs[19]= 'ufro' //Frost Wyrm
        set uIDs[20]= 'unp1' //Halls of the Dead
        set uIDs[21]= 'unp2' //Black Citadel
        set uIDs[22]= 'uzg1' //Spirit Tower
        set uIDs[23]= 'uskm' //Skeletal Mage
        //Night Elf Melee
        set uIDs[24]= 'earc' //Archer
        set uIDs[25]= 'edry' //Dryad
        set uIDs[26]= 'ehpr' //Hippogryph Rider
        set uIDs[27]= 'efdr' //Faerie Dragon
        set uIDs[28]= 'even' //Spirit of Vengeance
        //Naga... Why not?
        set uIDs[29]= 'nwgs' //Cautl
        set uIDs[30]= 'nsnp' //Snap Dragon
        set uIDs[31]= 'nhyc' //Dragon Turtle
        set uIDs[32]= 'nntg' //Tidal Guardian
        //Creeps. Yea.
        set uIDs[33]= 'nanm' //Barbed Arachnathid
        set uIDs[34]= 'nanb' //Barbed Arachnathid
        set uIDs[35]= 'nane' //Arachnathid Earthborer
        set uIDs[36]= 'nbrg' //Brigand
        set uIDs[37]= 'nass' //Assassin
        set uIDs[38]= 'ncea' //Centaur Archer
        set uIDs[39]= 'ncim' //Centaur Impaler
        set uIDs[40]= 'ncks' //Centaur Sorcerer
        set uIDs[41]= 'ndtr' //Dark Troll
        set uIDs[42]= 'ndtp' //Dark Troll Shadow Priest
        set uIDs[43]= 'ndtt' //Dark Troll Trapper
        set uIDs[44]= 'ndtb' //Dark Troll Berzerker
        set uIDs[45]= 'ndth' //Dark Troll High Priest
        set uIDs[46]= 'ndtw' //Dark Troll Warlord
        set uIDs[47]= 'ndrm' //Draenei Desciple
        set uIDs[48]= 'ndrh' //Draenei Harbringer
        set uIDs[49]= 'ndrs' //Draenei Seer
        set uIDs[50]= 'nrdk' //Red Dragon Whelp
        set uIDs[51]= 'nrdr' //Red Drake
        set uIDs[52]= 'nbdr' //Black Dragon Whelp
        set uIDs[53]= 'nbdk' //Black Drake
        set uIDs[54]= 'nbzw' //Bronze Dragon Whelp
        set uIDs[55]= 'nbzk' //Bronze Drake
        set uIDs[56]= 'ngrw' //Green Dragon Whelp
        set uIDs[57]= 'ngdk' //Green Drake
        set uIDs[58]= 'nadw' //Blue Dragon Whelp
        set uIDs[59]= 'nadk' //Blue Drake
        set uIDs[60]= 'nnht' //Nether Dragon Hatchling
        set uIDs[61]= 'nndk' //Nether Drake
        set uIDs[62]= 'nrel' //Reef Elemental
        set uIDs[63]= 'nele' //Enraged Elemental
        set uIDs[64]= 'nsel' //Sea Elemental
        set uIDs[65]= 'nftr' //Forest Troll
        set uIDs[66]= 'nfsp' //Forest Troll Shadow Priest
        set uIDs[67]= 'nftt' //Forest Troll Trapper
        set uIDs[68]= 'nftb' //Forest Troll Berzerker
        set uIDs[69]= 'nfsh' //Forest Troll High Priest. Giggity.
        set uIDs[70]= 'nftk' //Forest Troll Warlord
        set uIDs[71]= 'nfrs' //Furbolg Shaman
        set uIDs[72]= 'ngh1' //Ghost
        set uIDs[73]= 'ngh2' //Wraith
        set uIDs[74]= 'nssp' //Spitting Spider
        set uIDs[75]= 'ngna' //Gnoll Poacher
        set uIDs[76]= 'ngns' //Gnoll Assassin
        set uIDs[77]= 'ngnw' //Gnoll Warden
        set uIDs[78]= 'nhar' //Harpy Scout
        set uIDs[79]= 'nhrr' //Harpy Rogue
        set uIDs[80]= 'nhrw' //Harpy Windwitch
        set uIDs[81]= 'nhrh' //Harpy Storm-hag
        set uIDs[82]= 'nhfp' //Fallen Priest
        set uIDs[83]= 'nhdc' //Deceiver. I before E except after C.
        set uIDs[84]= 'nhhr' //Heretic
        set uIDs[85]= 'nhyh' //Hydra Hatchling
        set uIDs[86]= 'nhyd' //Hydra
        set uIDs[87]= 'nitr' //Ice Troll
        set uIDs[88]= 'nitp' //Ice Troll Priest
        set uIDs[89]= 'nitt' //Ice Troll Trapper
        set uIDs[90]= 'nits' //Ice Troll Berzerker
        set uIDs[91]= 'nith' //Ice Troll High Priest
        set uIDs[92]= 'nitw' //Ice Troll Warlord
        set uIDs[93]= 'ninc' //Infernal Contraption
        set uIDs[94]= 'nkog' //Kobold Geomancer
        set uIDs[95]= 'nltl' //Lightning Lizard
        set uIDs[96]= 'nthl' //Thunder Lizard
        set uIDs[97]= 'nltc' //Makrura Tidecaller
        set uIDs[98]= 'nlds' //Makrura Deepseer
        set uIDs[99]= 'nwiz' //Apprentice Wizard
        set uIDs[100]='nwzr' //Rogue Wizard
        set uIDs[101]='nwzg' //Renegade Wizard
        set uIDs[102]='nmbg' //Mur'gul Blood-gill
        set uIDs[103]='nmsn' //Mur'gul Snarecaster
        set uIDs[104]='nnwl' //Nerubian Webspinner
        set uIDs[105]='nnwr' //Nerubian Seer
        set uIDs[106]='nfps' //Polar Furbolg Shaman
        set uIDs[107]='nrzt' //Quillboar
        set uIDs[108]='nqbh' //Quillboar Hunter
        set uIDs[109]='nslh' //Salamander Hatchling
        set uIDs[110]='nslr' //Salamander
        set uIDs[111]='nsat' //Satyr Trickster
        set uIDs[112]='nsts' //Satyr Shadowdancer
        set uIDs[113]='nslf' //Sludge Flinger
        set uIDs[114]='nsra' //Stormreaver Apprentice
        set uIDs[115]='nsrh' //Stormreaver Hermit
        set uIDs[116]='nsrn' //Stormreaver Necrolyte
        set uIDs[117]='ndqv' //Vile Tormenter
        set uIDs[118]='ntrh' //Sea Turtle Hatchling
        set uIDs[119]='ntrt' //Giant Sea Turtle
        set uIDs[120]='ntka' //Tuskarr Spearman
        set uIDs[121]='ntkt' //Tuskarr Trapper
        set uIDs[122]='ntks' //Tuskarr Sorcerer
        set uIDs[123]='nvdl' //Lesser Voidwalker
        set uIDs[124]='nvdw' //Voidwalker
        set uIDs[125]='nvdg' //Greater Voidwalker
        set uIDs[126]='nska' //Skeleton Archer
        set uIDs[127]='nskf' //Burning Archer
        set uIDs[128]='nskm' //Skeletal Marksman
        set uIDs[129]='nsns' //Watery Minion
        set uIDs[130]='nsca' //Skeleton Archer
        set uIDs[131]='nlv1' //Lava Spawn Level 1
        set uIDs[132]='nlv2' //Lava Spawn Level 2
        set uIDs[133]='nlv3' //Lava Spawn Level 3
        set uIDs[134]='npn2' //Storm (Storm/Earth/Fire)
        set uIDs[135]='npn5' //Storm (Storm/Earth/Fire) (Duplicate)
        set uIDs[136]='nqb1' //Quillbeast Level 1
        set uIDs[137]='nqb2' //Dire Quillbeast Level 2
        set uIDs[138]='nqb3' //Raging Quillbeast Level 3
        set uIDs[139]='nqb4' //BerzerkQuillbeast Level 4
        //Does not include campaign units
    endfunction
endlibrary

libIsUnitStuck:

JASS:
globals
    integer array boolIDs
    constant integer boolI=25 //Number of values in boolIDs, higher takes longer, but has a better result.
endglobals

library isUnitStuck initializer i
    function isStuck takes unit u returns boolean
        local integer i=0
        loop
            exitwhen i>boolI
            if GetUnitAbilityLevel(u,boolIDs[i])>0 then
                return true
            endif
            set i=i+1
        endloop
        return false
    endfunction
    
    private function i takes nothing returns nothing
        set boolIDs[0] ='BHbd' //Blizzard Target
        set boolIDs[1] ='BHfs' //Flame Strike Target
        set boolIDs[2] ='Bmlt' //Aerial Shackles
        set boolIDs[3] ='Bmlc' //Aerial Shackles Caster
        set boolIDs[4] ='BOvc' //Big Bad Voodoo Caster
        set boolIDs[5] ='Bena' //Ensnare (Air)
        set boolIDs[6] ='Bens' //Ensnare (General)
        set boolIDs[7] ='Beng' //Ensnare (Ground)
        set boolIDs[8] ='Bams' //Anti Magic Shell
        set boolIDs[9] ='Bam2' //Anti Magic Shell (Extra)
        set boolIDs[10]='Bfrz' //Freezing Breath
        set boolIDs[11]='Bwea' //Web (Air)
        set boolIDs[12]='Bweb' //Web (Ground)
        set boolIDs[13]='BEer' //Entangling Roots
        set boolIDs[14]='Bcyc' //Cyclone
        set boolIDs[15]='Bcy2' //Cyclone (Extra)
        //Special (After human, orc, undead, and night elf), you can set boolI to 15 to ignore these:
        set boolIDs[16]='BNcs' //Cluster Rockets
        set boolIDs[17]='BNvc' //Volcano
        set boolIDs[18]='BNva' //Volcano (Area)
        set boolIDs[19]='Bfre' //Freeze
        set boolIDs[20]='Bvul' //Invulnerable
        set boolIDs[21]='BNms' //Mana Shield
        set boolIDs[22]='BNrd' //Reign of Fire Target
        set boolIDs[23]='BNrf' //Reign of Fire Area
        set boolIDs[24]='BNsa' //Staff of Sanctuary Target
        set boolIDs[25]='Btsp' //Tornado (Spin)
    endfunction
endlibrary

init:

JASS:
scope init initializer i
    private function fg takes nothing returns nothing
        call TriggerRegisterUnitEvent(tDDKB,GetEnumUnit(),EVENT_UNIT_DAMAGED)
    endfunction
    
    private function a takes nothing returns nothing
        local group g=CreateGroup()
        call GroupEnumUnitsInRect(g,bj_mapInitialPlayableArea,null)
        call ForGroup(g,function fg)
        call GroupClear(g)
        call DestroyGroup(g)
    endfunction
    
    private function i takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterTimerEvent(t,1,false)
        call TriggerAddAction(t,function a)
    endfunction
endscope

entersMap:

JASS:
scope entersMap initializer i
    private function a takes nothing returns nothing
        call TriggerRegisterUnitEvent(tDDKB,GetEnteringUnit(),EVENT_UNIT_DAMAGED)
    endfunction
    
    private function i takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterEnterRectSimple(t,bj_mapInitialPlayableArea)
        call TriggerAddAction(t,function a)
    endfunction
endscope

ddkb:

JASS:
globals
    trigger tDDKB
endglobals

scope damageDetectedKnockback initializer i
    private struct kData
        unit u
        real direction
        integer power
    endstruct
    
    globals
        private timer time=CreateTimer()
        private kData array kDB
        private integer dbIndex=-1
    endglobals

    private function p takes nothing returns nothing
        local integer index=0
        local kData tempDat
        local real x
        local real y
        loop
            exitwhen index>dbIndex
            set tempDat=kDB[index]
            set x=GetUnitX(tempDat.u)+tempDat.power*Cos(tempDat.direction*bj_DEGTORAD)
            set y=GetUnitY(tempDat.u)+tempDat.power*Sin(tempDat.direction*bj_DEGTORAD)
            if IsTerrainWalkable(x,y) or IsUnitType(tempDat.u,UNIT_TYPE_FLYING) then
                call SetUnitX(tempDat.u,x)
                call SetUnitY(tempDat.u,y)
            else
                set tempDat.direction=0-tempDat.direction
                set x=GetUnitX(tempDat.u)+tempDat.power*Cos(tempDat.direction*bj_DEGTORAD)
                set y=GetUnitY(tempDat.u)+tempDat.power*Sin(tempDat.direction*bj_DEGTORAD)
                if IsTerrainWalkable(x,y) then
                    call SetUnitX(tempDat.u,x)
                    call SetUnitY(tempDat.u,y)
                else
                    set tempDat.direction=tempDat.direction-180
                endif
                set tempDat.power=tempDat.power*3/4
            endif
            if IsUnitType(tempDat.u,UNIT_TYPE_FLYING) then
                set tempDat.power=tempDat.power-R2I(GetUnitMoveSpeed(tempDat.u)/100)-1
            else
                set tempDat.power=tempDat.power-1
                if GetUnitMoveSpeed(tempDat.u)<270 then
                    set tempDat.power=tempDat.power-1
                endif
            endif
            if GetUnitState(tempDat.u,UNIT_STATE_LIFE)<1 then
                set tempDat.power=tempDat.power-1
            endif
            if tempDat.power<0 then
                call tempDat.destroy()
                set kDB[index]=kDB[dbIndex]
                set dbIndex=dbIndex-1
                if dbIndex==-1 then
                    call PauseTimer(time)
                endif
            endif
            set index=index+1
        endloop
    endfunction
    
    private function a takes nothing returns nothing
        local kData tempDat
        local unit source=GetEventDamageSource()
        local unit target=GetTriggerUnit()
        local location lS=GetUnitLoc(source)
        local location lT=GetUnitLoc(target)
        local real direction=AngleBetweenPoints(lS,lT)
        local real power=15+GetEventDamage()/3
        if power>60 then
            set power=60
        endif
        if isPierce(source) then
            set power=power/2
        endif
        if isStuck(target)!=true and GetUnitMoveSpeed(target)>0 and IsUnitType(target,UNIT_TYPE_STRUCTURE)!=true then
            set tempDat=kData.create()
            set tempDat.u=target
            set tempDat.direction=direction
            set tempDat.power=R2I(power)
            set dbIndex=dbIndex+1
            set kDB[dbIndex]=tempDat
            if dbIndex==0 then
                call TimerStart(time,.03,true,function p)
            endif
        endif
        call RemoveLocation(lS)
        call RemoveLocation(lT)
    endfunction
    
    private function i takes nothing returns nothing
        set tDDKB=CreateTrigger()
        call TriggerAddAction(tDDKB,function a)
    endfunction
endscope
 

Attachments

  • (6)StromguardeWithDDKB002.w3x
    231.4 KB · Views: 81
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
Graveyard this already.

There are better systems specialized in each of the two things this system couples. Its basically a no-brainer to couple two of them.
This uses locations for no apparent reason. This uses hardcoded values. This is far too much of a hassle to set up.

I dont know what kind of math youre using for this, but it doesnt look like anything id expect from a knockback system.
 
Level 11
Joined
Feb 22, 2006
Messages
752
and test the map before you just assume something based on the script

This isn't just "unit gets attacked, push it back"

The script tells me more than a demo map. And this basically IS "unit gets attacked, push it back". You have some hardcoded stuff there that adjusts knockback based on if the damage type is piercing and some other checks to see if the unit is entangled or cycloned or w/e, but anybody can do that. And if somebody makes a custom spell with a custom entangle or something, they would have to add their buff to that huge array, which is already pretty ugly.

And Deaod is right. This has almost no options for customization. Basically, the ONLY case where you could use this without having to rewrite a bunch of the code is in a wc3 melee map. And not many people want to put a knockback system into something that is otherwise identical to a melee map.

Graveyard warning.
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,547
The script tells me more than a demo map. And this basically IS "unit gets attacked, push it back". You have some hardcoded stuff there that adjusts knockback based on if the damage type is piercing and some other checks to see if the unit is entangled or cycloned or w/e, but anybody can do that. And if somebody makes a custom spell with a custom entangle or something, they would have to add their buff to that huge array, which is already pretty ugly.

And Deaod is right. This has almost no options for customization. Basically, the ONLY case where you could use this without having to rewrite a bunch of the code is in a wc3 melee map. And not many people want to put a knockback system into something that is otherwise identical to a melee map.

Graveyard warning.

No, it uses ALL units with a simple to use array system to allow custom units, stuns, etc with ease.

And this is registering a unit being damaged, not attacked, (that's why they call it damage detection), allowing custom damage systems to be used in fusion with this at ease.

And although this doesn't have standard JESP style customization options, it doesn't take a genius to edit a few multipliers and add conditions (as I helped just spectating do awhile back)
 
obviously u havent tried it right?

because this is actually quite literally for All things.....
(never read a damn book by its damn cover)

I don't have warcraft. I haven't read the code either. Maybe the OP should post a better description then, because it litterally says;

self contained system which adds knockback whenever a unit receives damage
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,547
The OP assumed people reading the thread could understand that said sentence literally refers to the events and conditions the system uses, because it literally knocks back whenever a unit receives damage.

If you have a custom damage system that is entirely spell based, guess what, "UnitDamageTarget()" will trigger this system.

Perhaps you should test it out.
 
1. Never make assumptions

:thumbs_down:

The OP assumed people reading the thread could understand that said sentence literally refers to the events and conditions the system uses, because it literally knocks back whenever a unit receives damage.

If you have a custom damage system that is entirely spell based, guess what, "UnitDamageTarget()" will trigger this system.

Thats what I thought it did -_-

Which is why I think it's useless since it's so static. It is only used for knocking a unit back when it is damaged. It would be better to just use a Damage Detection System and a KB System which both have more functioniality. You could easily combine those to do what I think this does in ~20 lines.

Perhaps you should test it out.

Me said:
I don't have warcraft.
 

Cokemonkey11

Spell Reviewer
Level 30
Joined
May 9, 2006
Messages
3,547
:thumbs_down:



Thats what I thought it did -_-

Which is why I think it's useless since it's so static. It is only used for knocking a unit back when it is damaged. It would be better to just use a Damage Detection System and a KB System which both have more functioniality. You could easily combine those to do what I think this does in ~20 lines.

Find me a system that detects piercing damage. I'd love to use it instead of my ugly ass array.
 
Top