• 🏆 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] using a unit taking damage as an event

Status
Not open for further replies.
Level 6
Joined
Sep 9, 2006
Messages
92
Im trying to recreate a gui spell as jass; the spell simply damages the target every second over 9 seconds, but when the target is damaged by the caster (from damage other than the ability itself) there is a chance that the spell will last a second longer (this is timed so theoretically the spell will only last around 15 seconds max). I'm not too good at jass; here is the basic damage "trigger" that will last 9 seconds:

JASS:
scope Rend initializer Init

globals
    //ability id of Rend
    private constant integer ABILITY_ID = 'A005'
    //refresh rate of timer
    private constant real INTERVAL = 0.01
    //total ticks of spell
    private constant integer TICKS = 9
    //number of seconds between ticks
    private constant real SECONDS = 1.00
    //damage attack type
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
    //damage damage type
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
    //damage weapon type
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
    //blood effect when dealing damage
    private constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
    //attach point
    private constant string EFFECT_ATTACH = "chest"
    //do or dont display floating damage text
    private constant boolean DISPLAY_DAMAGE = true
    
    
    private hashtable h = InitHashtable()
endglobals

private function Damage takes integer level, integer agi, integer str returns real
    return I2R(level) * ( I2R(agi) + 3.00 * (I2R(str)) ) / 5.00
endfunction

private struct data
    unit c
    unit t
    timer tim = CreateTimer()
    integer lvl
    integer ticks = TICKS
    integer time = 0
    integer agi
    integer str
endstruct

private function Text takes unit u, real d returns nothing
    local texttag text = CreateTextTag()
    local string s = I2S(R2I(d)) + "!"
    call SetTextTagText(text, s, .018)
    call SetTextTagPosUnit(text, u, 24.00)
    call SetTextTagColor(text, 255, 0, 0, 255)
    call SetTextTagVelocity( text, 0.00, .089 )
    call SetTextTagPermanent( text, false )
    call SetTextTagLifespan( text, 1.50 )
    call SetTextTagFadepoint( text, 1.00 )
endfunction

private function periodic takes nothing returns nothing
    local data dat = LoadInteger(h,GetHandleId(GetExpiredTimer()),0)
    local real damage
    if GetUnitState(dat.t, UNIT_STATE_LIFE) > 0 then
        if dat.time >= 0 then
            set dat.time = dat.time - 1
        else
            if dat.ticks > 0 then
                set dat.ticks = dat.ticks - 1
                set damage = Damage(dat.lvl, dat.agi, dat.str)
                call UnitDamageTarget(dat.c, dat.t, damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                if DISPLAY_DAMAGE == true then
                    call Text( dat.t, damage )
                endif
                call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT, dat.t, EFFECT_ATTACH))
                if dat.ticks > 0 then
                    set dat.time = R2I(SECONDS / INTERVAL)
                else
                    call PauseTimer(dat.tim)
                    call DestroyTimer(dat.tim)
                    call FlushChildHashtable(h,GetHandleId(dat.tim))
                    call dat.destroy()
                endif
            endif
        endif
    else
        call PauseTimer(dat.tim)
        call DestroyTimer(dat.tim)
        call FlushChildHashtable(h,GetHandleId(dat.tim))
        call dat.destroy()
    endif
endfunction

private function Conditions takes nothing returns boolean
    local data dat
    if GetSpellAbilityId() == ABILITY_ID then
        set dat = data.create()
        set dat.t = GetSpellTargetUnit()
        set dat.c = GetTriggerUnit()
        set dat.lvl = GetUnitAbilityLevel(dat.c, ABILITY_ID)
        set dat.agi = GetHeroAgi(dat.c, true)
        set dat.str = GetHeroStr(dat.c, true)
        call SaveInteger(h,GetHandleId(dat.tim),0,dat)
        call TimerStart(dat.tim,INTERVAL,true,function periodic)
    endif
    return false
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Conditions ) )
    call Preload(BLOOD_EFFECT)
    set t = null
endfunction

endscope

I need help turning
  • Unit Group - Pick every unit in AllUnits and do (Actions)
    • Loop - Actions
      • Trigger - Add to Rend AddTicks <gen> the event (Unit - (Picked unit) Takes damage)
into jass
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
For the love of God though do not use Dusk's IDDS. I don't know how that is an approved resource it is a bloated mess of crap and it still has bugs with fundamental mechanisms that cause the system to fail miserably. I also do not want to be troubleshooting problems with people that come here saying that their damage detection isn't working, which happens way too often for an approved resource of wc3c.net. Sometimes I think that they just accept anything with certain people's names on it.

Also, Bribe, what he was doing was displaying the damage as an integer such as "50!", while yours would yield something like: "50.000!" which looks ugly.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
JASS:
/////////////////////////////////////////////////
native UnitAlive takes unit id returns boolean //
/////////////////////////////////////////////////
 
scope Rend
 
globals
    private constant integer ABILITY_ID = 'A005'     //ability id of Rend
    private constant integer TICKS = 9               //total iterations of spell
    private constant real TIMEOUT = 0.01             //refresh rate of timer
    private constant integer INTERVAL = 1            //number of seconds between ticks
    private constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
    private constant string EFFECT_ATTACH = "chest"
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
    private constant boolean DISPLAY_DAMAGE = true
endglobals
 
    private function Damage takes integer level, integer agi, integer str returns real
        return I2R( level * ( agi + 3. * str )) / 5.
    endfunction
 
    private function Text takes texttag text,unit u, real d returns nothing
        set text = CreateTextTag()
        call SetTextTagPermanent(text,false)
        call SetTextTagLifespan(text, 1.5)
        call SetTextTagFadepoint(text,1.)
        call SetTextTagPosUnit(text,  u,24.)
        call SetTextTagText(text,     I2S(R2I(d))+"!",0.018)
        call SetTextTagColor(text,    255,0,0,255)
        call SetTextTagVelocity(text, 0.,0.089)
    endfunction
 
private struct data
    static hashtable h = InitHashtable()
    unit c
    unit t
    integer lvl
    integer agi
    integer str
    timer tim = CreateTimer()
    integer ticks = TICKS
    integer time = 0
 
    method onDestroy takes integer id returns nothing
        call DestroyTimer(.tim)
        call FlushChildHashtable(.h,id)
    endmethod
 
    method DoDamage takes nothing returns nothing
        local real damage = Damage(.lvl,.agi,.str)
        call UnitDamageTarget(.c,.t,damage,true,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
        call DestroyEffect(AddSpecialEffectTarget(BLOOD_EFFECT,.t,EFFECT_ATTACH))
        if(DISPLAY_DAMAGE)then
            call Text(null,.t,damage)
        endif
    endmethod
 
    static method periodic takes nothing returns nothing
        local integer id = GetHandleId(GetExpiredTimer())
        local data this = LoadInteger(.h,id,0)
        local boolean b = UnitAlive(.t)
        if(b and .time >= 0)then
            set .time = .time - 1
        elseif(b and .ticks > 0)then
            call .DoDamage()
            set .ticks = .ticks - 1
            if(.ticks > 0)then
                set .time = INTERVAL
            else
                call .destroy(id)
            endif
        else
            call .destroy(id)
        endif
    endmethod
 
    static method create takes nothing returns nothing
        local data this = data.allocate()
        set .t = GetSpellTargetUnit()
        set .c = GetTriggerUnit()
        set .lvl = GetUnitAbilityLevel(.c, ABILITY_ID)
        set .agi = GetHeroAgi(.c,true)
        set .str = GetHeroStr(.c,true)
        call SaveInteger(.h,GetHandleId(.tim),0,this)
        call TimerStart(.tim,TIMEOUT,true,function periodic)
    endmethod
 
    static method Conditions takes nothing returns nothing
        if GetSpellAbilityId() == ABILITY_ID then
            call .create()
        endif
    endmethod
 
    static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddAction( t, function Conditions )
        call Preload(BLOOD_EFFECT)
    endmethod
 
endstruct
endscope
 
Level 6
Joined
Sep 9, 2006
Messages
92
Why don't you use a Damage Detection system? Then you can handle everything from there just fine.

So i looked at ZTSThreat which has a damage detection system... can you not do the damage detection in jass? or is it just easier in gui? because he had the same triggers like i did but when the unit was attacked it just called a function through custom script..

also bribe ty for helping out; woulda been better if you had reasoning for changes because im still pretty new but i think i just need to go over it again and see exactly what you did. and also a question; using "data.allocate()" that means you don't have to have data.whatever, just whats after the dot? ill start using that now, and why make the text function take a texttag also when you just say its null anyway?


also, i get an error saying that "static method onInit takes nothing returns nothing" must return Rend_data
 
Last edited:
For the error, this:
JASS:
static method create takes nothing returns nothing
        local data this = data.allocate()
        set .t = GetSpellTargetUnit()
        set .c = GetTriggerUnit()
        set .lvl = GetUnitAbilityLevel(.c, ABILITY_ID)
        set .agi = GetHeroAgi(.c,true)
        set .str = GetHeroStr(.c,true)
        call SaveInteger(.h,GetHandleId(.tim),0,this)
        call TimerStart(.tim,TIMEOUT,true,function periodic)
    endmethod

Should be:
JASS:
static method create takes nothing returns data
        local data this = data.allocate()
        set .t = GetSpellTargetUnit()
        set .c = GetTriggerUnit()
        set .lvl = GetUnitAbilityLevel(.c, ABILITY_ID)
        set .agi = GetHeroAgi(.c,true)
        set .str = GetHeroStr(.c,true)
        call SaveInteger(.h,GetHandleId(.tim),0,this)
        call TimerStart(.tim,TIMEOUT,true,function periodic)
        return this
    endmethod
 
Level 6
Joined
Sep 9, 2006
Messages
92
So now im getting an error that says "Unexpected: ()" for the line with the local:
JASS:
    private function Text takes texttag text, unit u, real d returns nothing
        local text = CreateTextTag()
        call SetTextTagText(text,      I2S(R2I(d)) + "!", .018)
        call SetTextTagPosUnit(text,   u,24.)
        call SetTextTagColor(text,     255, 0, 0, 255)
        call SetTextTagVelocity( text, 0., .089 )
        call SetTextTagPermanent( text,false )
        call SetTextTagLifespan( text, 1.5 )
        call SetTextTagFadepoint( text,1. )
    endfunction
however then if i take that out it causes an error saying its undefined (and another error dealing with syntax)
 
Level 6
Joined
Sep 9, 2006
Messages
92
You need to write "local texttag text=CreateTextTag()"

if thats the problem then ty; i assumed that bribe assumed that if its in the "takes" part it would automatically create the variable so you just needed to create the text itself



edit just realized that in mine i had "local" instead of "set" that part works now..


now i get an error that says "Wrong onDestroy definition"
method onDestroy takes integer id returns nothing

(the call being "call .destroy(id)")
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
The onDestroy method cannot have parameters, or a return. If you're using it as call StructName.destroy(structInstance) then it already includes a parameter (this is more internal stuff) but if you're doing it like structInstance.destroy() then it will always take nothing.

By the way, don't forget that there is the thistype operator, which returns the type of the struct that it is being called from. You should never reference the name within a struct, only by using thistype, because otherwise when you change your name you have to go through everywhere that you used the name and manually change it, which sucks. Plus it makes a lot of sense.

JASS:
static method create takes integer par1, real par2, real par3, boolean par4 returns thistype
    local thistype t=allocate()
    //set your variables
    //...
    return t
endmethod
 
Status
Not open for further replies.
Top