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

Summon Blood Cloud

  • Like
Reactions: Losam
This is another spell I made for a contest. It is not as impressive as other spells but it still may be usefull.

In-game description :

Drains the blood and 10% of allies' life points in the area in order to summon a blood cloud. The cloud slows the close ennemies' move speed and attack speed by 20% and deals damage over time. Also, when it dies, the cloud heals close allies by an amount equal to 20% of initially drained life.
level 1 - 15 damage + 10% of initially drained life points per second for 12 seconds.
level 2 - 20 damage + 15% of initially drained life points per second for 16 seconds.
level 3 - 25 damage + 20% of initially drained life points per second for 20 seconds.


The icon was made by CRAZZYRUSSIAN, the model was made by me.

I guess you can change the purpose of the aura but it has to apply on ennemies. It shouldn't affect a large area either (less than 300 AoE).

Here's the code :
JASS:
scope BloodCloud
// recquires CTL
//  http://www.hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/

//=====================================//
//            INSTRUCTIONS             //
//=====================================//
// How to import :
// 1) Download the attached map.
// 2) Export-import the blood cloud model and the icon (made by CRAZZYRUSSIAN).
// 3) Copy-paste the unit "Blood Cloud", the buff "Blood Cloud (Aura of Slow)"
//  and the spells "Summon Blood Cloud" and "Aura of Slow (Blood Cloud)".
// 4) Update the unit's ability list and the aura spell's buff.
// 5) Copy-paste the following script in an empty converted trigger.
// 6) Edit the datas in the CONFIGURATION BLOC below accordingly to your tastes and the Objects' ID.
//
//=====================================//

//=====================================//
//                                     //
//         CONFIGURATION BLOC          //
//                                     //
//=====================================//

    globals
        private constant integer SPELL_ID        = 'A000'   // The castable spell
        private constant integer SPELL_AURA_ID   = 'A001'   // The aura spell
        private constant integer BUFF_ID         = 'B000'   // The aura's buff
        private constant integer CLOUD_ID        = 'n000'   // The invocation unit
        private constant attacktype ATTACK       = ATTACK_TYPE_MAGIC       // For the DoT
        private constant damagetype DAMAGE       = DAMAGE_TYPE_UNIVERSAL   // For the DoT
        private constant string DRAIN_SFX        = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
        private constant string DRAIN_ATTACHMENT = "chest"
        private constant string HEAL_SFX         = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
        private constant string HEAL_ATTACHMENT  = "chest"
        private constant real TIMEOUT            = 0.03125  // CTL Timeout
        private constant boolean USE_AURA_LEVELS = false    // Set to true if your aura spell uses several levels

        private real array BaseDamageOverTime       // Base DoT for units affected by the rain
        private real array LifeDrainAmount          // Percentage of current life drained for summoning
        private real array BonusDamageOverTime      // Factor of life drained turned into DoT
        private real array HealFactor               // Factor of life drained occasionally healed
        private real array LifeDrainRadius          // Radius of the spell
        private real array CloudDuration            // Duration of the invocation
    endglobals

    private function SetupDatas takes nothing returns nothing
        // Level 1
        set BaseDamageOverTime[1]=15*TIMEOUT     // = 15 damage per second
        set LifeDrainAmount[1]=0.1               // = 10% of current life drained
        set BonusDamageOverTime[1]=0.1*TIMEOUT   // = 10% of life drained dealt per second
        set HealFactor[1]=0.2
        set LifeDrainRadius[1]=150
        set CloudDuration[1]=12
        // Level 2
        set BaseDamageOverTime[2]=20*TIMEOUT
        set LifeDrainAmount[2]=0.1
        set BonusDamageOverTime[2]=0.15*TIMEOUT
        set HealFactor[2]=0.2
        set LifeDrainRadius[2]=250
        set CloudDuration[2]=16
        // Level 3
        set BaseDamageOverTime[3]=25*TIMEOUT
        set LifeDrainAmount[3]=0.1
        set BonusDamageOverTime[3]=0.2*TIMEOUT
        set HealFactor[3]=0.2
        set LifeDrainRadius[3]=350
        set CloudDuration[3]=20
    endfunction

    // Filter for units affected by the blood drain
    private function DrainFilter takes unit target,unit caster,integer lvl returns boolean
        return GetOwningPlayer(target)==GetOwningPlayer(caster) and /*
            */ not IsUnitType(target,UNIT_TYPE_DEAD) and /*
            */ not IsUnitType(target,UNIT_TYPE_MECHANICAL) and /*
            */ not IsUnitType(target,UNIT_TYPE_MAGIC_IMMUNE)
    endfunction

    // Filter for units affected by the heal on cloud's death
    private function HealFilter takes unit target,unit cloud,integer lvl returns boolean
        return IsUnitAlly(target,GetOwningPlayer(cloud)) and /*
            */ not IsUnitType(target,UNIT_TYPE_DEAD) and /*
            */ not IsUnitType(target,UNIT_TYPE_MECHANICAL)
    endfunction
    
    // Note: the filter for units affected by the damage over time is the aura spell's filter

//=====================================//
//                                     //
//     END OF CONFIGURATION BLOC       //
//                                     //
//=====================================//

    globals
        private group TmpGroup=CreateGroup()
    endglobals

    private struct SpellData extends array
        unit invoc
        integer lvl
        real lifedrained
    implement CTL
        local unit target
    implement CTLExpire
        if IsUnitType(this.invoc,UNIT_TYPE_DEAD) or GetUnitTypeId(this.invoc)==0 then
            if GetUnitTypeId(this.invoc)!=0 then
                call GroupEnumUnitsInRange(TmpGroup,GetUnitX(this.invoc),GetUnitY(this.invoc),600,null)
                loop
                    set target=FirstOfGroup(TmpGroup)
                    exitwhen target==null
                    call GroupRemoveUnit(TmpGroup,target)
                    if HealFilter(target,this.invoc,this.lvl) and IsUnitInRangeXY(target,GetUnitX(this.invoc),GetUnitY(this.invoc),400) then
                        call SetWidgetLife(target,GetWidgetLife(target)+this.lifedrained*HealFactor[this.lvl])
                        call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,target,HEAL_ATTACHMENT))
                    endif
                endloop
            endif
            call this.destroy()
        else
            call GroupEnumUnitsInRange(TmpGroup,GetUnitX(this.invoc),GetUnitY(this.invoc),300,null)
            loop
                set target=FirstOfGroup(TmpGroup)
                exitwhen target==null
                call GroupRemoveUnit(TmpGroup,target)
                if GetUnitAbilityLevel(target,BUFF_ID)>0 then
                    call UnitDamageTarget(this.invoc,target,BaseDamageOverTime[this.lvl]+BonusDamageOverTime[this.lvl]*this.lifedrained,true,false,ATTACK,DAMAGE,null)
                endif
            endloop
        endif
    implement CTLNull
    implement CTLEnd

    private static method SpellCast takes nothing returns nothing
        local thistype this=create()
        local real x=GetUnitX(GetSpellTargetUnit())
        local real y=GetUnitY(GetSpellTargetUnit())
        local unit caster=GetTriggerUnit()
        local unit tmp
        set this.invoc=CreateUnit(GetOwningPlayer(caster),CLOUD_ID,x,y,GetUnitFacing(caster))
        set this.lvl=GetUnitAbilityLevel(caster,SPELL_ID)
        set this.lifedrained=0
        call GroupEnumUnitsInRange(TmpGroup,x,y,LifeDrainRadius[this.lvl]+200,null)
        loop
            set tmp=FirstOfGroup(TmpGroup)
            exitwhen tmp==null
            call GroupRemoveUnit(TmpGroup,tmp)
            if DrainFilter(tmp,caster,this.lvl) and IsUnitInRangeXY(tmp,x,y,LifeDrainRadius[this.lvl]) then
                set this.lifedrained=this.lifedrained+LifeDrainAmount[this.lvl]*GetWidgetLife(tmp)
                call SetWidgetLife(tmp,GetWidgetLife(tmp)*(1-LifeDrainAmount[this.lvl]))
                call DestroyEffect(AddSpecialEffectTarget(DRAIN_SFX,tmp,DRAIN_ATTACHMENT))
            endif
        endloop
        call UnitApplyTimedLife(this.invoc,'BTLF',CloudDuration[this.lvl])
        call SetUnitAnimation(this.invoc,"birth")
        call QueueUnitAnimation(this.invoc,"stand")
        static if USE_AURA_LEVELS then
            call SetUnitAbilityLevel(this.invoc,SPELL_AURA_ID,this.lvl)
        endif
        set caster=null
    endmethod

    private static method SpellCondition takes nothing returns boolean
        if GetSpellAbilityId()==SPELL_ID then
            call SpellCast()
        endif
        return false
    endmethod

    private static method onInit takes nothing returns nothing
        local trigger t=CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function thistype.SpellCondition))
        call SetupDatas()
    endmethod
    endstruct

endscope
[/HIDDEN]

Keywords:
Blood, Cloud, Rain, Summon, Invocation, Slow, Damage over-time, DoT, Drain
Contents

Péninsule infernale (Map)

Reviews
14:41, 28th Oct 2012 Magtheridon96: Approved. - TmpRadius is not being used anywhere - The AttackType/DamageType could be configurable - WEAPON_TYPE_WHOKNOWS can be replaced with null

Moderator

M

Moderator

14:41, 28th Oct 2012
Magtheridon96: Approved.

- TmpRadius is not being used anywhere
- The AttackType/DamageType could be configurable
- WEAPON_TYPE_WHOKNOWS can be replaced with null
 
Level 12
Joined
Jul 11, 2010
Messages
422
I swaped to CTL. I find all the modern generation of timer system incredibly constraining, though.

I already saw your way of doing settings. I prefer this method only because it looks more like what you do in the object editor, I guess... If there are really 50+ levels, it's not a big deal to make a loop.
 
Top