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

Holy Birds v2.1b

Discription:
Summons 5 holy birds, flying in a cricle arround the caster. When a bird hits an enemy unit, it will be damaged but if it hits an allied unit, it will be healed and the caster receives 50% of that heal. A unit can only be damaged or healed one time by each bird.

Credits:
Vexorian: vJass, TimerUtils and xe
Rising_Dusk: GroupUtils

Works properly with patch 1.24.
This Spell follows the JESP standart, fully MUI and leak- and laggless.


v2.0:
- Now uses xe for the missiles
- Alot of code changes
- Added more user configurations
v2.1:
- added xedamage options textmacros
v2.1b:
- fixed bug with xedamage textmacros *facepalm*
v2.2:
- added the possibility to add attachment points fot created effects
- small fix in the register method


JASS:
//***********************************************************************************************************\\
//*                                                                                                         *\\
//*                                                HOLY BIRDS                                               *\\
//*                                                  v2.1b                                                  *\\
//*                                                                                                         *\\
//*                                          Made by: Kricz & Cedi                                          *\\
//*                                                                                                         *\\
//*                                              Description:                                               *\\
//*                                 Summons 5 birds, surrounding the caster,                                *\\ 
//*                each bird damages enemy units on contact or heals allied units on contact                *\\
//*                                     (caster gets 50% of that heal)                                      *\\
//*                                                                                                         *\\
//*                                                Requires:                                                *\\
//*                                         TimerUtils by Vexorian                                          *\\
//*                                             xe by Vexorian                                              *\\
//*                                        GroupUtils by Rising_Dusk                                        *\\
//***********************************************************************************************************\\


scope HolyBirds //requires TimerUtils, GroupUtils, xebasic, xefx, xedamage, xepreload
 
globals
    //Rawcode of the spell
    private constant integer SPELL_ID			= 'A000'
    //Raw-Code of the created buff
    private constant integer BUFF_ID			= 'B000'
    
    //The missile model
    private constant string MISSILE_MODEL 	    = "Abilities\\Weapons\\SorceressMissile\\SorceressMissile.mdl"
    //The missile size
    private constant real MISSILE_SIZE          = 1.7
    //The missile heigh
    private constant real MISSILE_HEIGH         = 60.0
    //The distance between missiles and caster
    private constant real MISSILE_DISTANCE      = 225.   
    //The speed of the missiles
    private constant real MISSILE_SPEED         = 200.
    //Number of created missile
    private constant integer MISSILES           = 5
    
    //Missile Color Setups
    private constant integer MISSILE_RED        = 255
    private constant integer MISSILE_GREEN      = 200
    private constant integer MISSILE_BLUE       = 255
    private constant integer MISSILE_ALPHA      = 145
    
    //How much percent should the caster get when a target is healed
    private constant real CASTER_HEAL_PERCENT   = 0.5
    //The AoE of the missile
    private constant real AOE				    = 65.0
    //when collides with an ally-unit:
    private constant string HEAL_EFFECT			= "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"     
    //when collides with an enemy-unit:
    private constant string DAMAGE_EFFECT		= "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
    //effect created on the caster when an allied unit was healed
    private constant string CASTER_HEAL_EFFECT  = "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl" 
    
    //dont chance!
    private real array DURATION
    private real array DAMAGE
    private real array HEAL
endglobals

    //The duration of each level
private function SETUP_DURATION takes nothing returns nothing
    set DURATION[1] = 7.5
    set DURATION[2] = 10.0
    set DURATION[3] = 12.5
endfunction

    //The Damage to enemys
private function SETUP_DAMAGE takes nothing returns nothing
    set DAMAGE[1] = 20.0
    set DAMAGE[2] = 40.0
    set DAMAGE[3] = 60.0
endfunction

    //The Heal to allies
private function SETUP_HEAL takes nothing returns nothing
    set HEAL[1] = 15
    set HEAL[2] = 30
    set HEAL[3] = 45
endfunction

//! textmacro HB_SETUP_DAMAGE
    set .dtype = DAMAGE_TYPE_MAGIC
    set .atype = ATTACK_TYPE_MAGIC
    //set .wtype = WEAPON_TYPE_WHOKNOWS  //By default
//! endtextmacro

//Everything below here is the spellcode. If you can not vJass, don't change here anything!
private struct spelldata
    unit caster = null              //The caster
    group array targets[MISSILES]   //The group for each missile to check if a unit was already hit
    xefx array missile[MISSILES]    //The birds
    real duration = 0.00            //The duration the spell has (will be changed)
    integer lvl = 0                 //The level of the ability
    timer t = null                  //The timer which is used for the update
    static group group              //Used for filtering units
    static boolexpr filter          //This is the filter
    static delegate xedamage dmg    //The xedamager used to deal damage

    //creates everything the struct requires
    static method create takes unit caster returns thistype
        
        local thistype this = thistype.allocate()
        local integer i = 0
        local real pos = (GetUnitFacing(caster) + 360 / MISSILES + 90) * bj_DEGTORAD
        
        //fills the struct datas 
        set .caster = caster
        set .lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
        set .t = NewTimer()
        
        //creates the birds
        loop
            set .missile[i] =xefx.create(GetUnitX(.caster) + MISSILE_DISTANCE * Cos(pos), GetUnitY(.caster) + MISSILE_DISTANCE * Sin(pos), pos + bj_PI / 2)
            set .missile[i].fxpath = MISSILE_MODEL
            set .missile[i].scale = MISSILE_SIZE
            set .missile[i].z = MISSILE_HEIGH
            call .missile[i].recolor(MISSILE_RED, MISSILE_GREEN, MISSILE_BLUE, MISSILE_ALPHA)
            set pos = pos + (2 * bj_PI / MISSILES)
            set .targets[i] = NewGroup()
            set i = i + 1
            exitwhen i >= MISSILES
        endloop
        
        call SetTimerData(.t, this)
        call TimerStart(.t, XE_ANIMATION_PERIOD, true, function thistype.periodic)        
        return this
    endmethod

    //destroys the missiles if the duration is over or if the caster is dead
    private method onDestroy takes nothing returns nothing
        local integer i = 0
        call UnitRemoveAbility(.caster, BUFF_ID)       
        
        loop
            call .missile[i].destroy()
            call ReleaseGroup(.targets[i])
            set i = i + 1
            exitwhen i >= MISSILES
        endloop      
    endmethod
    
    //The filterfunction checks which units can be added to the group
    private static method FilterFunc takes nothing returns boolean
        return GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Amim') <= 0 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
    endmethod
    
    
    //This method damages or heals unit in the group
    private method DamageHeal takes unit u returns nothing
        //If ally, heal the target and caster
        if IsUnitAlly(u, GetOwningPlayer(.caster)) and u != .caster then         
            call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, u, "origin"))             
            call SetWidgetLife(u, GetWidgetLife(u) + HEAL[.lvl])
            call DestroyEffect(AddSpecialEffectTarget(CASTER_HEAL_EFFECT, .caster, "origin"))
            call SetWidgetLife(.caster, GetWidgetLife(.caster) + HEAL[.lvl] * CASTER_HEAL_PERCENT)
        //If enemy then damage the unit
        elseif IsUnitEnemy(u, GetOwningPlayer(.caster)) then            
            call DestroyEffect(AddSpecialEffectTarget(DAMAGE_EFFECT, u, "origin"))
            call .damageTarget(.caster, u, DAMAGE[.lvl])
        endif
    endmethod
    
    
    //This method is called every INTERVAL and will update the birds position and checks for units in range
    private static method periodic takes nothing returns nothing
        local thistype this = thistype(GetTimerData(GetExpiredTimer()))
        local integer i = 0
        local unit u = null
        
        set .duration = .duration + XE_ANIMATION_PERIOD
               
        //loop through every missile
        loop
            exitwhen i >= MISSILES
            set .missile[i].xyangle = .missile[i].xyangle + (MISSILE_SPEED * XE_ANIMATION_PERIOD * XE_ANIMATION_PERIOD)
            set .missile[i].x = GetUnitX(.caster) + MISSILE_DISTANCE * Cos(.missile[i].xyangle - bj_PI / 2)
            set .missile[i].y = GetUnitY(.caster) + MISSILE_DISTANCE * Sin(.missile[i].xyangle - bj_PI / 2)
            
            call GroupEnumUnitsInRange(.group, .missile[i].x, .missile[i].y, AOE, .filter)
                
            //loop through the unitgroup and damage/heal them
            loop
                set u = FirstOfGroup(.group)
                exitwhen u == null
                
                if not IsUnitInGroup(u, .targets[i]) then 
                    call GroupAddUnit(.targets[i], u)
                    call .DamageHeal(u)
                endif
                   
                call GroupRemoveUnit(.group, u)
            endloop 
            set i = i + 1    
        endloop
        
        if .duration >= DURATION[.lvl] or GetUnitAbilityLevel(.caster, BUFF_ID) <= 0 or GetWidgetLife(.caster) <= 0.405 then
            call .destroy()
            call ReleaseTimer(GetExpiredTimer())
        endif
        
    endmethod 
    
    //This method is called, whenever an ability is casted, and checks if that ability is the for this spell.
    private static method register takes nothing returns boolean
        if GetSpellAbilityId() == SPELL_ID and GetUnitAbilityLevel(GetTriggerUnit(), BUFF_ID) <= 0 then 
            call thistype.create(GetTriggerUnit())
            return true
        endif
        return false
    endmethod
    
    //Initializates everything
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddCondition(t, Condition(function thistype.register))
        call SETUP_DURATION()
        call SETUP_DAMAGE()
        call SETUP_HEAL()
        set .dmg = xedamage.create()
        //! rentextmacro HB_SETUP_DAMAGE()
        set .group = NewGroup()
        set .filter = Condition(function thistype.FilterFunc)
        call XE_PreloadAbility(SPELL_ID)
        set t = null
    endmethod
endstruct

endscope

Keywords:
holy, birds, heal, damage, yellow, 1.24, priest, surround, circle, missiles
Contents

Holy Birds (Map)

Reviews
19:05, 4th Oct 2009 TriggerHappy187: This spell is well coded and good enough for hive's database.

Moderator

M

Moderator

19:05, 4th Oct 2009
TriggerHappy187:

This spell is well coded and good enough for hive's database.
 
Level 11
Joined
Jul 2, 2008
Messages
601
Rising_Duks? Ducks? :) Lol I'm pretty sure, he is Rising_Dusk :)

Can't check the spell since I have 1.23 and my knowledge in Jass... well, it's really poor ;) So I can't fix the things to work properly for my version. But from description it's okay. Only I don't have the numbers of heal / damage (However, they are easily changeable, I'm sure), so I can't tell, if the spell overpowered or not. :)
 
Level 2
Joined
Feb 13, 2009
Messages
20
Rising_Duks? Ducks? :) Lol I'm pretty sure, he is Rising_Dusk :)

Can't check the spell since I have 1.23 and my knowledge in Jass... well, it's really poor ;) So I can't fix the things to work properly for my version. But from description it's okay. Only I don't have the numbers of heal / damage (However, they are easily changeable, I'm sure), so I can't tell, if the spell overpowered or not. :)

I don't see anything in the spell that makes it 1.24 only
 
Level 8
Joined
Aug 2, 2008
Messages
193
@Aspard:
Yes you right, I meant Rising_Dusk will fix this.
And the spell isn't overpowerd, you can easily set up the summoned birds/missiles and setup heal and damage per bird, so you can balance it very good how you want it

@ravery12:
Only the TimerUtils Version works with 1.24 ^^
The spell itself should work properly with 1.23
 
Pretty nice spell, coding is nice. The suggestions below are only minor.

  • Why not create some algorithm in constant functions, that way your spell can support all levels of spells without having to add a new array per level.
  • You don't need to null the trigger in your init function.
  • In your register method you could store GetTriggerUnit() inside a variable, saving a function call. Preferably a global, this way you don't need to clean up a local.
  • You may want to make attachment point ("origin") configurable.
 
Level 8
Joined
Aug 2, 2008
Messages
193
Hi,

Algorithm... never used that I think, don't know exactly how to use it and how it works :)
I always null local vars like units, items, triggers and so on :)
Wouldn't change something if I wouldn'z null it.
Okay, will use a local unit in next update in register method
And also add attachment points for the effects ;)
 
Last edited:
Top