Spell Heroic Guard

Romek

Super Moderator
Reaction score
963
Heroic Guard
By Romek

Code: vJASS
MUI / MPI: MUI
Leaks: None
Description: Makes this unit take damage for the target unit. Damage will be reduced to 100/80/60%.
Lasts 30 seconds.
Icon:

Thanks to Grymlax!

Notes:
I've remade this spell since last time. I said I would over Christmas, but I didn't have time. :D
The old version used a timer per instance, and dynamic triggers.
This uses no dynamic triggers, and 2 timers altogether. :thup:

Screenshot:
HeroicGuard.png


The Code:
JASS:
scope HeroicGuard initializer Init // requires PUI
// ______________________________________________________________________
// +--------------------------------------------------------------------+
// |                    H E R O I C   G U A R D                         |
// |____________________________________________________________________|
// +--------------------------------------------------------------------+
// |                 -== By Romek -- Version 2.2 ==-                    |
// |____________________________________________________________________|
// +--------------------------------------------------------------------+
// |  2.0:                                                              |
// |   - Remade the entire spell so it uses no dynamic triggers         |
// |     and 2 timers                                                   |
// |                                                                    |
// |  2.1:                                                              |
// |   - Fixed a critical error caused by casting the spell on the      |
// |     same unit twice                                                |
// |                                                                    |
// |  2.2:                                                              |
// |   - Made the spell use index 0.  (Uberplayer complained)           |
// |   - Prevented a critical crash which occured when the spell was    |
// |     cast by 2 heroes at eachother.                                 |
// |____________________________________________________________________|
// +--------------------------------------------------------------------+
// |  -== C o n f i g u r a t i o n: ==-                                |
// +--------------------------------------------------------------------+
globals
    
    // The raw code of the ability
    private constant integer ID = 'A000'
    
    // How often the lightning will be moved, the duration will also be accurate to this amount
    private constant real TIMEOUT = 0.03125
        
    // The raw code ot the lightning
    private constant string LIGHTNING = "DRAL"
            
    // How high from the units 'base' the lightning will be.
    private constant real LIGHT_OFFSET = 50.
            
    // The RGBA values of the lightning. 1 = 100%. 0 = 0%
    private constant real L_R = 1. // Red
    private constant real L_G = 1. // Green
    private constant real L_B = 1. // Blue
    private constant real L_A = 0.2 // Alpha
    
    // Should the effects be preloaded?
    private constant boolean PRELOAD = true
        
    // The effect created on the caster when the spell is cast
    private constant string CAST_EFFECT_C = "Abilities\\Spells\\Orc\\Disenchant\\DisenchantSpecialArt.mdl"
    // ...Attached to the casters:
    private constant string CAST_EFFECT_C_ATTACH = "chest"
        
    // The effect created on the target when the spell is cast
    private constant string CAST_EFFECT_T = "Abilities\\Spells\\Orc\\Disenchant\\DisenchantSpecialArt.mdl"     
    // ...Attached to the targets:
    private constant string CAST_EFFECT_T_ATTACH = "chest"
        
    // The effect created on the caster when the target is damaged
    private constant string DAMAGE_EFFECT_C = "Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl"
    // ...Attached to the casters:
    private constant string DAMAGE_EFFECT_C_ATTACH = "origin"
            
    // The effect created on the target when it is damaged
    private constant string DAMAGE_EFFECT_T = "Abilities\\Spells\\Undead\\ReplenishHealth\\ReplenishHealthCasterOverhead.mdl" 
    // ... Attached to the targets:
    private constant string DAMAGE_EFFECT_T_ATTACH = "overhead"
            
    // The raw code of an item ability which gives +999999 life
    private constant integer LIFEBONUS = 'A001'
                    
    // The attack, damage and weapon type of the damage done to the caster when the target is damaged
    private constant attacktype AT = ATTACK_TYPE_CHAOS // Attack type
    private constant damagetype DT = DAMAGE_TYPE_UNIVERSAL // Damage type
    private constant weapontype WT = WEAPON_TYPE_WHOKNOWS // Weapon type
    // Leaving these as the default values will ensure the caster takes the full damage the target did.
    // (Assuming MULTIPLIER returns 1.)
    
    // The message displayed to the owner of the caster when the target is already affected by the spell
    private constant string ERROR = "|cFFFFBF00This unit is already affected by Heroic Guard|r"
    
    // The message displayed when the target is already casting the spell
    private constant string ERRORB = "|cFFFFBF00This unit is currently casting Heroic Guard|r"
endglobals
    
    // What the damage is multiplied by before it is dealt to the caster.
    private constant function MULTIPLIER takes integer level returns real
        return 1 - ((level - 1) * 0.2) // 1, 0.8, 0.6, 0.4
        // Any lower should use '0.1', or IMaxBJ()
    endfunction
    
    // How long the spell lasts. Accurate to TIMEOUT.
    private constant function DURATION takes integer level returns real
        return 30.
    endfunction
    
    // The mana cost of the spell. This is used to restore mana if the target incase the spell fails.
    private constant function MANA takes integer level returns real
        return 150.
    endfunction
    
//  ____________________________________________________________________
// +--------------------------------------------------------------------+
// |        -== E n d   o f    C o n f i g u r a t i o n: ==-           |
// |____________________________________________________________________|
// +--------------------------------------------------------------------+
//            -== D O   N O T   T O U C H   B E L O W ==-              
        
    private keyword Data

    globals
        private trigger Trig = CreateTrigger()
        private timer T = CreateTimer()
        private Data array D
        private integer I = 0
        private group Registered = CreateGroup()
        private group Wanted = CreateGroup()
        private group Casters = CreateGroup()
        private Data array Z
        private timer X = CreateTimer()
        private integer Y = 0
    endglobals
    
    private struct Data
        //! runtextmacro PUI()
        unit caster
        unit target
        integer level
        lightning light
        
        real total
        real ticks = 0.
                
        real damage
        boolean abil
        
        method onDestroy takes nothing returns nothing
            call DestroyLightning(.light)
        endmethod
    endstruct
    
    private function HitEx takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i >= Y
                call SetWidgetLife(Z<i>.target, GetWidgetLife(Z<i>.target) + Z<i>.damage)
                if Z<i>.abil then
                    call UnitRemoveAbility(Z<i>.target, LIFEBONUS)
                    set Z<i>.abil = false
                endif
                set Z<i> = Z[Y]
                set Y = Y - 1
            set i = i + 1
        endloop
        if Y == 0 then
            call PauseTimer(X)
        endif
    endfunction
    
    private function Hit takes nothing returns nothing
        local Data d = Data[GetTriggerUnit()]
        set d.damage = GetEventDamage()
        call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT_T, d.target, DAMAGE_EFFECT_T_ATTACH))
        call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT_C, d.caster, DAMAGE_EFFECT_C_ATTACH))
        if GetWidgetLife(d.target) - d.damage &lt; 1 then
            call UnitAddAbility(d.target, LIFEBONUS)
            set d.abil = true
        else
            set d.abil = false
        endif
        set Z[Y] = d
        set Y = Y + 1
        if Y == 1 then
            call TimerStart(X, 0., true, function HitEx)
        endif
        call UnitDamageTarget(GetEventDamageSource(), d.caster, d.damage*MULTIPLIER(d.level), false, true, AT, DT, WT)
    endfunction
    
    private function HitCond takes nothing returns boolean
        return IsUnitInGroup(GetTriggerUnit(), Wanted)
    endfunction
    
    private function Expire takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i &gt;= I
                set D<i>.ticks = D<i>.ticks + TIMEOUT
                if D<i>.ticks &gt;= D<i>.total or IsUnitType(D<i>.caster, UNIT_TYPE_DEAD) then
                    call D<i>.release()
                    call GroupRemoveUnit(Casters, D<i>.caster)
                    call GroupRemoveUnit(Wanted, D<i>.target)
                    set D<i> = D[I ]
                    set I = I - 1
                else
                    call MoveLightningEx(D<i>.light, true, GetUnitX(D<i>.caster), GetUnitY(D<i>.caster), GetUnitFlyHeight(D<i>.caster) + LIGHT_OFFSET, GetUnitX(D<i>.target), GetUnitY(D<i>.target), GetUnitFlyHeight(D<i>.target) + LIGHT_OFFSET)
                endif
            set i = i + 1
        endloop
        if I == 0 then
            call PauseTimer(T)
        endif
    endfunction
    
    private function SpellRestore takes unit u, integer level returns nothing
        call TriggerSleepAction(0.)
        call UnitRemoveAbility(u, ID)
        call UnitAddAbility(u, ID)
        call SetUnitAbilityLevel(u, ID, level)
        call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MANA) + MANA(level))
    endfunction
    
    private function Act takes nothing returns nothing
        local Data d = Data.create()
        
        set d.target = GetSpellTargetUnit()
        set d.caster = GetTriggerUnit()
        set d.level = GetUnitAbilityLevel(d.caster, ID)
        
        if IsUnitInGroup(d.target, Wanted) then
            call DisplayTimedTextToPlayer(GetOwningPlayer(d.caster), 0., 0., 3., ERROR)
            call SpellRestore.execute(d.caster, d.level)
            call d.release()
            return
        elseif IsUnitInGroup(d.target, Casters) then
            call DisplayTimedTextToPlayer(GetOwningPlayer(d.caster), 0., 0., 3., ERRORB)
            call SpellRestore.execute(d.caster, d.level)
            call d.release()
            return
        endif
        
        call GroupAddUnit(Casters, d.caster)
        call DestroyEffect(AddSpecialEffectTarget(CAST_EFFECT_C, d.caster, CAST_EFFECT_C_ATTACH))
        call DestroyEffect(AddSpecialEffectTarget(CAST_EFFECT_T, d.target, CAST_EFFECT_T_ATTACH))
        
        set d.total = DURATION(d.level)
        set d.light = AddLightningEx(LIGHTNING, true, GetUnitX(d.caster), GetUnitY(d.caster), GetUnitFlyHeight(d.caster) + LIGHT_OFFSET, GetUnitX(d.target), GetUnitY(d.target), GetUnitFlyHeight(d.target) + LIGHT_OFFSET)
        call SetLightningColor(d.light, L_R, L_G, L_B, L_A)
        set D[I ] = d
        set I = I + 1
        if I == 1 then
            call TimerStart(T, TIMEOUT, true, function Expire)
        endif

        call GroupAddUnit(Wanted, d.target)
        if not IsUnitInGroup(d.target, Registered) then
            call TriggerRegisterUnitEvent(Trig, d.target, EVENT_UNIT_DAMAGED)
            call GroupAddUnit(Registered, d.target)
        endif
        set Data[d.target] = d
    endfunction
    
    private function Cond takes nothing returns boolean
        return GetSpellAbilityId() == ID
    endfunction

    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Filter(function Cond))
        call TriggerAddAction(t, function Act)
        call TriggerAddCondition(Trig, Filter(function HitCond))
        call TriggerAddAction(Trig, function Hit)
        if PRELOAD then
            call DestroyEffect(AddSpecialEffect(CAST_EFFECT_C, 0., 0.))
            call DestroyEffect(AddSpecialEffect(CAST_EFFECT_T, 0., 0.))
            call DestroyEffect(AddSpecialEffect(DAMAGE_EFFECT_C, 0., 0.))
            call DestroyEffect(AddSpecialEffect(DAMAGE_EFFECT_T, 0., 0.))
        endif
    endfunction

endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


Requires PUI

The Map:
 

Attachments

  • [Spell] Heroic Guard.w3x
    51 KB · Views: 482

iPeez

Hot food far all world wide!
Reaction score
165
Awesome :D works great! ^^ Combined with Taunt omfg! +rep!
 

Romek

Super Moderator
Reaction score
963
Thanks iPeez.
Although it would usually be used on a stronger hero with less HP in my opinion, so you essentially get another units Health for X seconds.
Or it could be used to save units :D

Oh, and [ I ] becomes on vB forums. It's awfully annoying. :p
 

Nexor

...
Reaction score
74
hm, nice spell, only bug I found was that casted twice on the same unit caused crit error :)
 

bananaHUNT

You can change this now in User CP
Reaction score
55
Nice spell, very useful! :D
You could maybe make it when targeting on enemy units to let them take 60/80/100% damage for the hero?
 

Romek

Super Moderator
Reaction score
963
> hm, nice spell, only bug I found was that casted twice on the same unit caused crit error
Fixed. v2.1 :)
It now stops the spell casting if the target is already affected. The cooldown is reset, and mana is restored.

> You could maybe make it when targeting on enemy units to let them take 60/80/100% damage for the hero?
That'd be quite a different spell :p

> Im not sure about this ( you need to test it ), but doesnt this return false when unit is still decaying?
I haven't tested this, but it's used often, and I doubt it.
 

Sim

Forum Administrator
Staff member
Reaction score
534
Nice spell

> works great! ^^

Agreed.

Approved!
 

Romek

Super Moderator
Reaction score
963
Thanks for the approval Daxtreme, and the nice comments everyone.

> You're still leaving index 0 unused .
End of the world. :(
I'll change it if this ever needs another update. Otherwise I'll leave it. ^_^
 

Romek

Super Moderator
Reaction score
963
Updated to 2.2.

There was a bug which caused a critical error when 2 heroes casted this on each other, then one of them took damage. The spell now won't cast on a hero that is already casting the spell.
Also uses index 0 now, so cheer up Uber. :D
 

Lehona

New Member
Reaction score
12
You should make the ability force channeling. Otherwise you can cast it on two units at the same time and one lightning won't get destroyed.
 

Lehona

New Member
Reaction score
12
The test map? I just casted it on one unit, press esc and then on the next one. The first lightning was there all the time and didn't get destroyed.
 

Furby

Current occupation: News poster
Reaction score
144
The test map? I just casted it on one unit, press esc and then on the next one. The first lightning was there all the time and didn't get destroyed.

He's right. :) You know you call struct with PUI (and all other attachment systems by unit that means you can cast only one spell at time.

That's why ABC (or some else system which attach structs to handles) would be better for this, even though .. dynamic triggers =/.

Btw, you should add there some maximal range, when the spell will be interrupted and stops when target and caster are out of this range.

Also, there should be way to stop casting spell.. if it isn't channeling ability, it should still have some way to stop from casting, to prevent dying of your hero if you don't want him to die.

EDIT: You should also use [lJASS]//! external ObjectMerger w3a[/lJASS] for that "dummy bonus hp" ability. :p
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top