• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Why wont this spell work?

Status
Not open for further replies.
Level 11
Joined
Sep 12, 2008
Messages
657
Hey.. i was making a spell..
and for some reason sometimes it work.. sometimes it doesnt?

its suposed to drain a unit's mana. its not mui/mpi, just vjass..
1 unit has it all the game only.

JASS:
library Byakugan initializer init
    globals
    
        //* =============================================================== *
        //* ==================    Spell made by dardas    ================= *
        //* ======================    Integers    ========================= *
        private integer             ByakuganRawId                   = 'A002'
        //* ======================    Strings    ========================== *
        private string              ByakuganHitSFX                  = "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"
        private string              ByakuganHitAttachmentPoint      = "chest"
        //* =======================    Reals    =========================== *
        private real                ByakuganManaDrainPerHit         = 20
        private real                ByakuganDuration                = 15
        //* ================    Dont Edit Below This    =================== *
        private unit                ByakuganCaster                  = null
        private unit                ByakuganTarget                  = null
        private effect              ByakuganHitEffect               = null
        private real                ByakuganTimer                   = 0
        boolean                     ByakuganCasted                  = false
    endglobals
    
    private function ByakuganCond takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == ByakuganRawId ) ) then
        return false
    endif
    return true
    endfunction
    
    private function ByakuganHit takes nothing returns nothing
        if GetAttacker() == ByakuganCaster then
            set ByakuganTarget = GetTriggerUnit()
            call SetUnitState(ByakuganTarget, UNIT_STATE_MANA, (GetUnitState(ByakuganTarget, UNIT_STATE_MANA) - ByakuganManaDrainPerHit))
            set ByakuganHitEffect = AddSpecialEffectTarget(ByakuganHitSFX, ByakuganTarget, ByakuganHitAttachmentPoint)
            call DestroyEffect(ByakuganHitEffect)
            set ByakuganHitEffect = null
            set ByakuganTarget = null
        endif
    endfunction
    
    private function ByakuganCast takes nothing returns nothing
        set ByakuganCaster = GetTriggerUnit()
        set ByakuganCasted = true
    endfunction
    
    private function ByakuganTimeLoop takes nothing returns nothing
        if ByakuganTimer < ByakuganDuration then
            set ByakuganTimer = ByakuganTimer + 0.032
        else
            set ByakuganCaster = null
            set ByakuganTarget = null
            set ByakuganCasted = false
        endif
    endfunction
    
    private function init takes nothing returns nothing
        local trigger DM = CreateTrigger()
        local trigger DMM = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ(DM, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(DM, Condition(function ByakuganCond))
        call TriggerAddAction(DM, function ByakuganCast)
        
        call TriggerRegisterAnyUnitEventBJ(DMM, EVENT_PLAYER_UNIT_ATTACKED)
        call TriggerAddAction(DMM, function ByakuganHit)
        
        call TimerStart(CreateTimer(), 0.032, true, function ByakuganTimeLoop)
        
        set DM = null
        set DMM = null
    endfunction
endlibrary

it works 1 time, then it never works again.

thanks in advance.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
You never reset ByakuganTimer. By that, I mean you never set it back to 0 when it's done.

Also, ByakuganCasted isn't used.
Your condition function should be simplified to
JASS:
return GetSpellAbilityId() == ByakuganRawId
Your special effect should be destroyed like this:
JASS:
call DestroyEffect(AddSpecialEffectTarget(ByakuganHitSFX, ByakuganTarget, ByakuganHitAttachmentPoint))
You don't need to null global variables.
 
Level 21
Joined
Mar 19, 2009
Messages
444
If you really want to use a timer, try this:


JASS:
library Byakugan initializer init needs SpellEvent, TimerUtils, AutoIndex   //find those 3 libraries on wc3.net
    globals
    
        //* ==================    Spell made by dardas    ================= *
        private constant integer SPELL_ID = 'A002'     //A ti^p: for globals that never change, use constant variables
        private constant string SPELL_FX = "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"
        private constant string SPELL_ATTACHEMENT = "chest"
        private constant real SPELL_MANA_DRAINED = 20.
        private constant real SPELL_DURATION = 15.
        private constant real SPELL_TIMEOUT = 0.32
        //* ================    Dont Edit Below This    =================== *
        private integer SpellDuration
    endglobals

    private struct s_caster
        timer t
        unit caster
        boolean isCasting
        integer i

            //function Autoindex provides: structs will be created only for units matching this boolean
            //Here, i filter only unit which have the spell
            private static method filter takes unit u returns nothing
                    return GetUnitAbilityLevel(u,SPELL_ID)>=1
            endmethod 
            //Autoindex: a unit with my CreateFilter coundition which enters the map will have my struct "s_caster" assignated
            //Like any struct creation (str.create() ).
            //There, i create the variables for my unit.
            private method onCreate takes nothing returns nothing 
                       set .caster = me
                       set .t = null
                       set .isCasting = false
                       set .i = 0
            endmethod
            //VJass tips and tricks: i say to Autoindex to automatically create the struct when a unit enter the map
            //Actually its not compulsory, u can decide to create the struct manually when needed of course 
            implement AutoCreate 
            //the  Autoindex "remove" detection: generally when the unit is removed from the game (after its death)
            //Destroy the trigger.
            private method onDestroy takes nothing returns nothing
                    set .t = null
            endmethod
            //Same: implementation of the automatic destroy of the struct s_caster when unit leaves the map
            implement AutoDestroy
    endstruct
    //Update is the loop function done by the timer
    private function Update takes nothing returns nothing
        local timer t = GetExpiredTimer() //You get the timer used in the loop
        local s_caster dat = GetTimerData(t)//thanks to TimerUtils, you can get back the struct (and then all data) stored and attached to the timer
              if dat.i < SpellDuration and not(IsUnitType(dat.caster,UNIT_TYPE_DEAD) and dat.caster != null then
                    set dat.i = dat.i + 1 //This just increments the timer.
              else  //if the timer has reached the duration, or the caster is dead or not existing, the spell ends
                    set dat.IsCasting = false //we set the boolean to false: the unit is no more casting your spell
                    call ReleaseTimer(t)//timer utils allows to recycle timers (no leak and timer used for nothing)
              endif
    endfunction

    private function OnCast takes nothing returns nothing//function happening when spell is casted
        local s_caster dat = s_caster[SpellEvent.CastingUnit()]//the library SpellEvent makes u able topmargin find the caster with the SpellEvent.CastingUnit (there, it replaces GetTriggerUnit() ); with AutoIndex, you are able to find back the struct attached to the unit with the format: mystructname[myunit]
              set dat.t = NewTimer()//TimerUtils allows to run timer with more efficience than blizzard's native
              set dat.isCasting = true//your boolean to be sure the unit is casting the spell
              set dat.i = 0 //this integer will check the amount of loops made by the timer = the duration of the spell
              call SetTimerData(t,dat)//timer utils allows to attach a struct (=a metavariable grouping several variables) to the timer
              call TimerStart(t,SPELL_TIMEOUT,true,function Update)//we run the timer as usual
    endfunction

    private function OnAttack takes nothing returns nothing
        local s_caster dat = s_caster[GetAttacker()]//with AutoIndex, you are able to find back the struct attached to the unit with the format: mystructname[myunit]
        local unit target = GetTriggerUnit()//the attacked unit
              if dat.IsCasting == true then //with s_caster dat = s_caster[GetAttacker()] we get back the struct linked to the unit
                  call SetUnitState(target, UNIT_STATE_MANA,(GetUnitState(target, UNIT_STATE_MANA)-SPELL_MANA_DRAINED)) //your spell drains mana, nothing to say
                  call DestroyEffect(AddSpecialEffectTarget(SPELL_FX,target, SPELL_ATTACHEMENT))//if the effect has no "stand" animation, you can destroy it on creation
              endif
              set target = null//I guess you know u have to nullify most handle variables
    endfunction
    
    private function init takes nothing returns nothing
           local trigger t = CreateTrigger()
              set SpellDuration =  R2I(SPELL_DURATION/SPELL_TIMEOUT)//this will be the duration of your spell, depending the interval per timer loop and the duration in seconds u set for ur spell in the variables
              call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ATTACKED)
              call TriggerAddCondition(t,Condition(function OnAttack)) //Counditions are faster than actions  (and in some case, if u have to destroy the trigger, a remaining action is a leak, but not a coundition
              call RegisterSpellFinishResponse(SPELL_ID,OnCast)//SpellEvent is the sexiest way to detect a spell cast: only one line, a spell code and a function to set; more, it detect with more precision the way you want the trigger to work depending on the spell state (cast, channel, etc... are better done than natives)
    endfunction

endlibrary

Note: made with notepad, I'm at work and I do not get the editor on this computer :p
 
Last edited:
Level 11
Joined
Sep 12, 2008
Messages
657
gah.. thats like remaking whole code?.. ill just do what i did now and reset the timecount,
and make it better.. thanks anyways..

ill use watermelon's idea anyways.. thanks =]

edit: i just realised what axarion meant.. i think ill use his way instead.. alot easier.. and my other spells get extra effects if this is casted,
so its awesome, thanks =]
 
Status
Not open for further replies.
Top