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

Charged Lightning Shield v1.1

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Charged Lightning Shield is based off the Shaman - Lightning Shield spell.
This spell is self-cast, and has a limited number of charges. Instead of damaging all units around, it is like thorns - it damages units that attack it (even ranged).

The number of charges, damage amount, and damage type can be changed in the global constants section of the code.

Charges can optionally stack (default is unstackable) when lightning shield is recast.

This was made to fill a request.
Requester didn't specify whether he only wants melee attacks to take the damage.

Requires vJASS.

Keywords:
charged, lightning shield
Contents

Just another Warcraft III map (Map)

Reviews
12th Dec 2015 IcemanBo: Too long time as NeedsFix. Rejected. 16:37, 18th Mar 2010 TriggerHappy: Do not create your own damage detection, use an existing (approved) one.

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long time as NeedsFix. Rejected.

16:37, 18th Mar 2010
TriggerHappy:

Do not create your own damage detection, use an existing (approved) one.
 
Level 3
Joined
Sep 21, 2008
Messages
48
Here it is.

Code:
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ChargedLightningShield initializer init

    globals
        // Change this to the number of charges you want the lightning shield to have
        private constant integer MAX_CHARGES = 3
        // Change this to the base damage of the orbs
        private constant integer ORB_DAMAGE = 200
        // Change this to the damage type of the lightning shield. You might want to make it ATTACK_TYPE_MAGIC
        private constant attacktype CLS_DAMAGE_CLASS = ATTACK_TYPE_NORMAL
        private hashtable chargesHashtable
        private trigger castsCLSTrig = CreateTrigger()
    endglobals
    
    private function unitIsAttacked takes nothing returns nothing
        local unit attackee = GetTriggerUnit()
        local unit attacker = GetAttacker()
        local integer charges
        
        set charges = LoadInteger(chargesHashtable, 0, GetHandleId(attackee))
        call UnitDamageTarget(attackee, attacker, ORB_DAMAGE, true, false, CLS_DAMAGE_CLASS, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS)
        set charges = charges - 1
        if (charges == 0) then
            call UnitRemoveAbility(attackee, 'B000')
            call DestroyTrigger(GetTriggeringTrigger())
        else
            call SaveInteger(chargesHashtable, 0, GetHandleId(attackee), charges)
        endif
    
        set attackee = null
        set attacker = null
    endfunction
    
    private function unitGainsCLS takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local trigger t = CreateTrigger()
        
        call SaveInteger(chargesHashtable, 0, GetHandleId(u), MAX_CHARGES)
        call TriggerAddAction(t, function unitIsAttacked)
        call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ATTACKED)
        
        set t = null
        set u = null
    endfunction
    
    private function unitGainsCLSFilter takes nothing returns boolean
        if (GetSpellAbilityId() == 'A000') then
            return true
        endif
        return false
    endfunction

    private function init takes nothing returns nothing
        set chargesHashtable = InitHashtable()
        call TriggerRegisterAnyUnitEventBJ(castsCLSTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(castsCLSTrig, function unitGainsCLSFilter)
        call TriggerAddAction(castsCLSTrig, function unitGainsCLS)
    endfunction
    
endlibrary
 
checked the code... seems okay though it would damage the unit once it starts to attack so even if it stops the attack before it can even damage the user, the attacker will still get damaged, but I dont think that's some big issue...

also each cast of this would reset the charges to 3, I think it would be better if you make the charges stackable by doing this...

JASS:
private function unitGainsCLS takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local trigger t = CreateTrigger()
        local integer charge = LoadInteger(chargesHashtable, 0, GetHandleId(u))
        
        call SaveInteger(chargesHashtable, 0, GetHandleId(u), MAX_CHARGES + charge)
        call TriggerAddAction(t, function unitIsAttacked)
        call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ATTACKED)
        
        set t = null
        set u = null
    endfunction

or even better, add a boolean in the globals section to check whether he wants the charges to stack or not...
JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ChargedLightningShield initializer init

    globals
        // Change this to the number of charges you want the lightning shield to have
        private constant integer MAX_CHARGES = 3
        // Change this to the base damage of the orbs
        private constant integer ORB_DAMAGE = 200
        // Settings this to true will stack the number of charges
        private constant boolean CHARGE_STACK = true
        // Change this to the damage type of the lightning shield. You might want to make it ATTACK_TYPE_MAGIC
        private constant attacktype CLS_DAMAGE_CLASS = ATTACK_TYPE_NORMAL
        private hashtable chargesHashtable
        private trigger castsCLSTrig = CreateTrigger()
    endglobals
    
    private function unitIsAttacked takes nothing returns nothing
        local unit attackee = GetTriggerUnit()
        local unit attacker = GetAttacker()
        local integer charges
        
        set charges = LoadInteger(chargesHashtable, 0, GetHandleId(attackee))
        call UnitDamageTarget(attackee, attacker, ORB_DAMAGE, true, false, CLS_DAMAGE_CLASS, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS)
        set charges = charges - 1
        if (charges == 0) then
            call UnitRemoveAbility(attackee, 'B000')
            call DestroyTrigger(GetTriggeringTrigger())
        else
            call SaveInteger(chargesHashtable, 0, GetHandleId(attackee), charges)
        endif
    
        set attackee = null
        set attacker = null
    endfunction
    
    private function unitGainsCLS takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local trigger t = CreateTrigger()
        local integer charge = LoadInteger(chargesHashtable, 0, GetHandleId(u))

        if CHARGE_STACK then
           call SaveInteger(chargesHashtable, 0, GetHandleId(u), MAX_CHARGES + charge)
        else
           call SaveInteger(chargesHashtable, 0, GetHandleId(u), MAX_CHARGES)
        endif
        call TriggerAddAction(t, function unitIsAttacked)
        call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ATTACKED)
        
        set t = null
        set u = null
    endfunction
    
    private function unitGainsCLSFilter takes nothing returns boolean
        if (GetSpellAbilityId() == 'A000') then
            return true
        endif
        return false
    endfunction

    private function init takes nothing returns nothing
        set chargesHashtable = InitHashtable()
        call TriggerRegisterAnyUnitEventBJ(castsCLSTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(castsCLSTrig, function unitGainsCLSFilter)
        call TriggerAddAction(castsCLSTrig, function unitGainsCLS)
    endfunction
    
endlibrary
 
Level 12
Joined
May 21, 2009
Messages
994
JASS:
set chargesHashtable = InitHashtable()
should be declared when you make the global.
JASS:
private hashtable chargesHashtable = InitHashtable()

And instead of this
JASS:
    private function unitGainsCLSFilter takes nothing returns boolean
        if (GetSpellAbilityId() == 'A000') then
            return true
        endif
        return false
    endfunction
Could be much easier.
JASS:
    private function unitGainsCLSFilter takes nothing returns boolean
        return GetSpellAbilityId() == 'A000' then
    endfunction
Now you also know that for next time. :)

Also why do you use a global trigger? Why not:

JASS:
    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, function unitGainsCLSFilter)
        call TriggerAddAction(t, function unitGainsCLS)
    endfunction

'B000' should also be editable as a global integer.

Hope I helped, and I hope you learned something.
 
I think you should also check whether the unit still has the buff before you do the damage because of the fact that some spells can remove buff... I cannot add it now coz I cant afford to put a wrong code since I'm not with my WE right now...

Also, move the SaveInteger to outside the if-then-else because initially you placed it in the else which would bug since it would not save the value 0...

One more thing, you made a new trigger for each unit, with each cast... you can use only one trigger that would already work for all units but you would need to add a filter to the action that would check the remaining charges and if the unit still has buff (cause of the possibility of being purged/dispelled) before you do the damage...
 
Level 6
Joined
Nov 3, 2008
Messages
117
there is a difference between attacking a unit and damaging a unit. Thats why there are 2 Events.

Attacking a unit means giving the order to a unit to attack another, and when the attackanimation starts. Thats an attack.

Damaging a unit is right in that moment a unit takes damage.

So 'Damaging' not equal 'Attack'.

Means if you spam S ( Stop ) , each time the unit starts a new Attack(animation) the unit will be damaged (by your spell). If you know 'Berserker Call' from DotA's Axe, maybe you know that if you spam the s button you make him spin even more because that spinning skill allways triggers if an attacking unit starts an attack.

I hope i made this clear o_O

~Inferior
 
Level 10
Joined
Mar 31, 2009
Messages
732
So... should I be using "a unit is damaged" instead of "a unit is attacked"?

Uploaded v1.1.
Changes:
- When a unit is attacked, it will be checked if it has the buff still. If the buff is gone, all data will be removed, and the attacker won't be damaged.
- Added the option to allow the charges to stack when you recast Charged Lightning Shield.

JASS:
library ChargedLightningShield initializer init

    globals
        // Change this to the number of charges you want the lightning shield to have when it is cast
        private constant integer CLS_ONCAST_CHARGES = 3
        // Change this to the maximum number of charges the lightning shield can have (-1 for unlimited)
        private constant integer CLS_MAX_CHARGES = 10
        // Change this to the base damage of the orbs
        private constant integer ORB_DAMAGE = 200
        // Change this to the damage type of the lightning shield. You might want to make it ATTACK_TYPE_MAGIC
        private constant attacktype CLS_DAMAGE_CLASS = ATTACK_TYPE_NORMAL
        // Change this to true if you want the shield charges to stack
        private constant boolean CLS_STACKS = false
        // Change this to the ability code of the lightning shield
        private constant integer CLS_ABILITY_CODE = 'A000'
        // Change this to the buff code of the lightning shield
        private constant integer CLS_BUFF_CODE = 'B000'
        private hashtable chargesHashtable = InitHashtable()
        private hashtable isAttackedHashtable = InitHashtable()
    endglobals
    
    private function unitIsAttacked takes nothing returns nothing
        local unit attackee = GetTriggerUnit()
        local unit attacker = GetAttacker()
        local integer charges = LoadInteger(chargesHashtable, 0, GetHandleId(attackee))
        local boolean hasBuff = (GetUnitAbilityLevel(attackee, CLS_BUFF_CODE) > 0)
        
        set charges = charges - 1
        
        if (not hasBuff or charges == 0) then
            // The buff was dispelled or expired or it should be dispelled now
            call SaveInteger(chargesHashtable, 0, GetHandleId(attackee), 0)
            call DestroyTrigger(GetTriggeringTrigger())
            call SaveTriggerHandle(isAttackedHashtable, 0, GetHandleId(attackee), null)
        endif
        
        if (hasBuff and charges == 0) then
            call UnitRemoveAbility(attackee, CLS_BUFF_CODE)
        endif
        
        if (hasBuff) then
            call UnitDamageTarget(attackee, attacker, ORB_DAMAGE, true, false, CLS_DAMAGE_CLASS, DAMAGE_TYPE_LIGHTNING, WEAPON_TYPE_WHOKNOWS)
        endif
        
        if (charges > 0) then
            call SaveInteger(chargesHashtable, 0, GetHandleId(attackee), charges)
        endif
    
        set attackee = null
        set attacker = null
    endfunction
    
    private function unitGainsCLS takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local trigger t
        local integer charges = CLS_ONCAST_CHARGES
        
        set t = LoadTriggerHandle(isAttackedHashtable, 0, GetHandleId(u))
        if (t == null) then
            set t = CreateTrigger()
            call TriggerAddAction(t, function unitIsAttacked)
            call TriggerRegisterUnitEvent(t, u, EVENT_UNIT_ATTACKED)
            call SaveTriggerHandle(isAttackedHashtable, 0, GetHandleId(u), t)
        endif
        
        if (CLS_STACKS and GetUnitAbilityLevel(u, CLS_BUFF_CODE) > 0) then
            set charges = charges + LoadInteger(chargesHashtable, 0, GetHandleId(u))
        endif
        call SaveInteger(chargesHashtable, 0, GetHandleId(u), charges)
        
        set t = null
        set u = null
    endfunction
    
    private function unitGainsCLSFilter takes nothing returns boolean
        return (GetSpellAbilityId() == CLS_ABILITY_CODE)
    endfunction

    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, function unitGainsCLSFilter)
        call TriggerAddAction(t, function unitGainsCLS)
        set t = null
    endfunction
    
endlibrary
 
Level 10
Joined
Mar 31, 2009
Messages
732
I believe if (not hasBuff or charges == 0) then is working exactly as I want it to.

JASS:
if (hasBuff and charges == 0) then
    call UnitRemoveAbility(attackee, CLS_BUFF_CODE)
endif
is removing the buff when the charges are zero.
 
Last edited:
Top