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

Guardian Angel v1.01c

Thanks to dead-man-walking for the ideas :)

Guardian Angel

Calls upon a Guardian Angel on a target area. Heals all allied units inside AoE and give them bonus armor.
JASS:
library GuardianAngel requires xefx, TimerUtils, GroupUtils, xedamage, BonusMod optional BoundSentinel
//===============================================================================================
//Guardian Angel v1.01c by scorpion182
//
//requires:
//- xe, TimerUtils, BoundSentinel by vexorian
//- GroupUtils by Rising_Dusk
//- BonusMod by Earth-Fury
//
//===============================================================================================
//============CALIBRATION SECTION================================================================
    globals
        private keyword  data //don't touch this
        private constant integer    SPELL_ID        = 'A000' //ability rawcode
        private constant real       CASTING_TIME    = 1.8 //ability casting time
        private constant string     ANGEL_PATH      = "Abilities\\Spells\\Human\\Resurrect\\ResurrectCaster.mdl" //path for angel fx
        private constant string     HEAL_PATH       = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl" //path for healing missile
        private constant string     HEAL_FX         = "Abilities\\Spells\\Items\\ResourceItems\\ResourceEffectTarget.mdl" //path for on-heal fx
        private constant string     HEAL_ATTCH      = "origin" //heal fx attachment point
        private constant real       SCALE           = 1. //missile scale
        private constant real       MIN_DIST        = 80. //how close the missile to heal the target
        private constant real       INIT_H          = 500. //initial height for the angel
        private constant integer    RED             = 128 //vertex coloring in RGB
        private constant integer    GREEN           = 255
        private constant integer    BLUE            = 128
        private constant integer    ALPHA           = 255
        private constant boolean    BONUS_STACK     = true //armor bonuses are stack if true
        private constant boolean    PRELOAD_FX      = true //preload the fx?
    endglobals

    private constant function GetHeal takes integer lvl returns real
        return 100. + lvl * 0. //heal amount of hp
    endfunction
    
    private constant function GetBonusArmor takes integer lvl returns integer
        return 3 + lvl * 0 //add bonus armor 
    endfunction
    
    private constant function GetDuration takes integer lvl returns real
        return 10. + lvl * 0. //bonus armor duration in seconds
    endfunction
    
    private constant function GetSpeed takes integer lvl returns real
        return 800. * XE_ANIMATION_PERIOD + lvl * 0. //missile speed 
    endfunction
    
    private constant function GetAoE takes integer lvl returns real
        return 400. + lvl * 0. //aoe must match the object editor value
    endfunction
    
    private function DamageOptions takes xedamage spellDamage returns nothing
        set spellDamage.dtype = DAMAGE_TYPE_UNIVERSAL
        set spellDamage.atype = ATTACK_TYPE_NORMAL
        set spellDamage.exception = UNIT_TYPE_STRUCTURE
        set spellDamage.visibleOnly = true 
        set spellDamage.damageSelf = true
        set spellDamage.damageAllies = true //heals for amount of
        set spellDamage.allyfactor = -1.0   //hitpoints to allies.
    endfunction
    //filter the targets, should match the value from DamageOptions
    private function IsValidTarget takes unit u, data s returns boolean
        return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 and IsUnitType(u,UNIT_TYPE_STRUCTURE)==false and IsUnitAlly(u,GetOwningPlayer(s.caster))==true and IsUnitVisible(u,GetOwningPlayer(s.caster))==true
    endfunction
//==========END OF CALIBRATION SECTION===========================================================
    globals
        private xedamage xed
    endglobals
    
    //struct for the missile
    private struct heal
        timer t
        unit caster
        unit target
        xefx fx
        real x
        real y
        integer lvl
        boolean bonus=false
        
        static method create takes unit c, unit tr, real x, real y, integer lvl returns thistype
            local thistype this = heal.allocate()
            local real a = Atan2(y-GetUnitY(tr),x-GetUnitX(tr))
            
            set .x = x
            set .y = y
            set .caster = c
            set .target = tr
            set .lvl = lvl
            set .t = NewTimer()
            set .fx = xefx.create(x,y,a)
            set .fx.fxpath = HEAL_PATH
            set .fx.z = INIT_H
            set .fx.scale = SCALE
            call .fx.recolor(RED, GREEN, BLUE, ALPHA)
            call SetTimerData(.t, this)
            call TimerStart(.t, XE_ANIMATION_PERIOD, true, function heal.onMove)
            
            return this
        endmethod
        
        static method onMove takes nothing returns nothing
            local thistype this = thistype(GetTimerData(GetExpiredTimer()))
            
            local real x = GetUnitX(.target) - .fx.x
            local real y = GetUnitY(.target) - .fx.y
            local real z = (GetUnitFlyHeight(.target)-.fx.z) + GetSpeed(.lvl)
                 
            local real distance = SquareRoot(x*x + y*y + z*z)
                
            local real angle1 = Atan2(y, x)
            local real angle2 = Acos(z / distance)
            local real angle3 = Atan2(z, SquareRoot(x * x + y * y))
            
            if not IsUnitType(.target, UNIT_TYPE_DEAD) then
            
                if (distance > MIN_DIST) then
                    set .fx.xyangle = angle1
                    set .fx.x = .fx.x + GetSpeed(.lvl) * Cos(angle1) * Sin(angle2)
                    set .fx.y = .fx.y + GetSpeed(.lvl) * Sin(angle1) * Sin(angle2)
                    set .fx.z = .fx.z + GetSpeed(.lvl) * Cos(angle2)
                    set .fx.zangle = angle3
                else
                    if not BONUS_STACK then //static ifs doesn't work here --a
                        if GetUnitBonus(.target, BONUS_ARMOR)==0 then
                            call AddUnitBonus(.target, BONUS_ARMOR, GetBonusArmor(.lvl))
                            set .bonus=true
                        endif
                    else
                        call AddUnitBonus(.target, BONUS_ARMOR, GetBonusArmor(.lvl))
                        set .bonus = true
                    endif
                    call xed.damageTarget(.caster, .target, GetHeal(.lvl))
                    call DestroyEffect(AddSpecialEffectTarget(HEAL_FX, .target, HEAL_ATTCH))
                    call .fx.destroy()
                    call SetTimerData(.t, this)
                    call TimerStart(.t, GetDuration(.lvl), false, function thistype.onFinish)
                endif
            else
                call .fx.destroy()
                call .destroy()
            endif
        endmethod
        
        static method onFinish takes nothing returns nothing
            local thistype this = thistype(GetTimerData(GetExpiredTimer()))
            
            if .bonus then
                call SetUnitBonus(.target, BONUS_ARMOR, GetUnitBonus(.target, BONUS_ARMOR) - GetBonusArmor(.lvl))
            endif
            
            call .destroy()
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(.t)
        endmethod
    endstruct
    //struct for the angel
    private struct data
        unit caster
        timer t
        xefx fx
        real x
        real y
        integer lvl
        private static thistype temp
        
        static method create takes unit c, real x, real y returns thistype
            local thistype this = data.allocate()
            local real a = Atan2(y - GetUnitY(c), x - GetUnitX(c))
            
            set .caster = c
            set .lvl = GetUnitAbilityLevel(c,SPELL_ID)
            set .x = x
            set .y = y
            set .t = NewTimer()
            set .fx = xefx.create(x,y,a)
            set .fx.fxpath = ANGEL_PATH
            //set .fx.z = INIT_H //the angel already fly?
            set .fx.scale = SCALE
            call SetTimerData(.t, this)
            call TimerStart(.t, CASTING_TIME, false, function thistype.onFinish)
            
            return this
        endmethod
        
        static method onFinish takes nothing returns nothing
            local thistype this = thistype(GetTimerData(GetExpiredTimer()))
            
            set temp = this
            call GroupEnumUnitsInRange(ENUM_GROUP, .x, .y, GetAoE(.lvl), function data.VictimFilter)
            
            call .destroy()
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(.t)
            call .fx.destroy()
        endmethod
        
        static method VictimFilter takes nothing returns boolean
            local unit u = GetFilterUnit()
            local heal h
            
            if IsValidTarget(u, temp) then
                set h = heal.create(temp.caster, u, temp.x, temp.y, temp.lvl)
            endif
            
            set u = null
            return false
        endmethod
        
        static method SpellEffect takes nothing returns boolean
            local thistype this
            
            if GetSpellAbilityId()==SPELL_ID then
                set this = thistype.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
            endif
            
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, function thistype.SpellEffect)
            
            //init xedamage
            set xed = xedamage.create()
            call DamageOptions(xed)
            
            static if PRELOAD_FX then
                call Preload(ANGEL_PATH)
                call Preload(HEAL_PATH)
                call Preload(HEAL_FX)
            endif
        endmethod
        
    endstruct


endlibrary


Requires:
- TimerUtils by Vexorian
- xe by Vexorian
- BoundSentinel (Optional) by Vexorian
- GroupUtils by Rising_Dusk
- BonusMod by Earth-Fury

History
~ v1.00 First release.
~ v1.01 Fixed minor bugs.
~ v1.01b Fixed the z formula :D.
~ v1.01c Fixed and optimized z formula (again).


Keywords:
paladin, heal, guardian, angel, aoe, area, armor, bonus, group, holy, self, whatever
Contents

Guardian Angel v1.01c (Map)

Reviews
17:58, 1st Feb 2010 The_Reborn_Devil: The coding looks good, and the bug has been fixed. Status: Approved Rating: Useful

Moderator

M

Moderator

17:58, 1st Feb 2010
The_Reborn_Devil:
The coding looks good, and the bug has been fixed.

Status: Approved
Rating: Useful
 
Top