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

Justifys (small) Survival Spellpack v1.3

I've finished my first spellpack.
It contains 4 spells that're all used to defend yourself,
keep you alive or just save you.



JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Retardation v1.1                                                            //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Provides the unit with a restricting                                        //
//~ protection, limiting the damage                                             //
//~ he can suffer in a period of time.                                          //
//~                                                                             //
//~ Level 1: 500 damage in 2 seconds.                                           //
//~ Level 2: 450 damage in 3 seconds.                                           //
//~ Level 3: 400 damage in 4 seconds.                                           //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ It's what the tooltip says. No matter what happens (okay... bugs occur)     //
//~ it's impossible that the unit takes more then 500/450/400 damage            //
//~ in 2/3/4 seconds. If there are very mighty neutrals, care that they can't   //
//~ deal such an amount of damage, you'd be invulnurable.                       //
//~                                                                             //
//~ I though of it as a nuke-protection :)                                      //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Utilities:                                                                  //
//~             -TimerUtils                                                     //
//~             -InlinedHashtable                                               //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

scope Retardation initializer InitRetard //No comment on this pls :D

    globals
        //The rawcode of "Retardation" [Spell]
        private constant integer SPELLID = 'A000'
        
        //The sfx is played whenever damage is absorbed (cap broken)
        private constant string SFX = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl"
        private constant string ATTACH = "chest"
        
        //Please, don't touch these globals, they're needed for the spell
        private trigger DAMAGED
        private integer KEY
        private unit TEMPUNIT
        private real TEMPREAL
        //The next two are combinations: if b_HEROCAP is true, only the hero damage will be counted
        //for the cap. If false, creepdamage is included.
        //If b_HEROABSORB is true, only the hero damage will be absorbed after reaching the cap.
        //This means, that you could set b_HEROCAP to false but b_HEROABSORB to true.
        //Now creeps would reach the cap to, but their damage isn't absorbed, making the spell
        //against heros very strong if you're surrounded by creeps.
        private constant boolean HEROCAP = false
        private constant boolean HEROABSORB = false
    endglobals
    
    //The duration in which the damage must be done to reach the cap
    private constant function GetDuration takes integer i_lvl returns real
        return 2.+i_lvl
    endfunction
    //The cap. If more damage then this is done in the time from above, the damage is prevented
    private constant function GetDamageCap takes integer i_lvl returns real
        return 550.-i_lvl*50.
    endfunction
    //---------NO MORE TOUCHING PLS, I'M SHY! :P
    
    //This struct is used to store all the damage that's done to the "caster"
    //Each single piece of damage will be stored and reset after some time
    private struct TempDamage
        unit u_hero
        real r_damage
        
        static method Create takes unit u_hero, real r_damage returns TempDamage
            local TempDamage s_dat = TempDamage.allocate()
            set s_dat.u_hero = u_hero
            set s_dat.r_damage = r_damage
            return s_dat
        endmethod    
    endstruct
    
    //This function removes the damage out of the entire damage (from the cap)
    //and releases the timer+struct instance
    private function DecreaseDamage takes nothing returns nothing
        local timer t_decrease = GetExpiredTimer()
        local TempDamage s_dat = GetTimerData(t_decrease)
        
        call SetHandleReal(s_dat.u_hero, KEY, GetHandleReal(s_dat.u_hero, KEY)-s_dat.r_damage)
        call ReleaseTimer(t_decrease)
        
        set s_dat.u_hero = null
        call s_dat.destroy()
    endfunction
    //For the null timer
    private function HealEx takes nothing returns nothing
        call SetWidgetLife(TEMPUNIT, GetWidgetLife(TEMPUNIT)+TEMPREAL)
        call ReleaseTimer(GetExpiredTimer())
    endfunction
    //Heal function, since normal "heal" with SetWidgetLife
    //is done before the damage, causing damage at full life not to
    //be healed -> null-timer.
    private function Heal takes unit u_target, real r_amount returns nothing
        local timer t_null
        
        call DestroyEffect(AddSpecialEffectTarget(SFX, u_target, ATTACH))
        
        if GetUnitState(u_target, UNIT_STATE_MAX_LIFE) - GetWidgetLife(u_target) < r_amount then
            set t_null = NewTimer()
            set TEMPUNIT = u_target
            set TEMPREAL = r_amount
            call TimerStart(t_null, 0., false, function HealEx)
        else
            call SetWidgetLife(u_target, GetWidgetLife(u_target)+r_amount)
        endif
    endfunction
    
    //The function of the damage trigger, this registers every piece of damage
    private function PreventDamage takes nothing returns boolean
        local unit u_damaged
        local timer t_decrease
        local real r_damage
        local real r_taken = GetEventDamage()
        local integer i_level
        local TempDamage s_dat
        
        //Just to filter the millions of sources out that don't deal damage
        //like AI-stuff and buffs
        if r_taken <= 0 then
            return false
        endif
        
        set u_damaged = GetTriggerUnit()
        set t_decrease = NewTimer()
        set r_damage = GetHandleReal(u_damaged, KEY)
        set i_level = GetUnitAbilityLevel(u_damaged, SPELLID)
        set s_dat = TempDamage.Create(u_damaged, r_taken)
        
        static if HEROABSORB then
            if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then
                //If the cap is already reached, just heal
                if r_damage >= GetDamageCap(i_level) then
                    call Heal(u_damaged, r_taken)
                //If the cap will be reached with this damage,
                //heal the cap-overflow
                elseif r_damage+r_taken >= GetDamageCap(i_level) then
                    call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
                endif
            endif
        else
            //If the cap is already reached, just heal
            if r_damage >= GetDamageCap(i_level) then
                call Heal(u_damaged, r_taken)
            //If the cap will be reached with this damage,
            //heal the cap-overflow
            elseif r_damage+r_taken >= GetDamageCap(i_level) then
                call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
            endif 
        endif
        //Saving the damage and attaching it to the timer
        static if HEROCAP then
            if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then 
                call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
                call SetTimerData(t_decrease, s_dat)
                call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
            endif
        else
            call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
            call SetTimerData(t_decrease, s_dat)
            call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
        endif
        
        set u_damaged = null
        
        return false
    endfunction
    //Registers all units that get the skill    
    private function AddToDAMAGED takes nothing returns boolean
        if GetLearnedSkill() == SPELLID and GetLearnedSkillLevel() == 1 then
            call TriggerRegisterUnitEvent(DAMAGED, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
        endif
        return false
    endfunction

    private function InitRetard takes nothing returns nothing
        local trigger t_init = CreateTrigger()
        
        set DAMAGED = CreateTrigger()        
        call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_HERO_SKILL)
    
        call TriggerAddCondition(t_init, Condition(function AddToDAMAGED))
        call TriggerAddCondition(DAMAGED, Condition(function PreventDamage))
        
        set KEY = StringHash("Retardation")
        
        call RegisterStringHash("Retardation")
        call Preload(SFX)
    endfunction
endscope


JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                        //
//~ Protective Barrier v1.0                                //
//~                                                        //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                        //
//~ Justify creates several rocks                          //
//~ in front of him, blocking his enemies.                 //
//~ To ensure his safety, he is pushed backwards.          //
//~ Rocks last 5 seconds.                                  //
//~                                                        // 
//~ Level 1: 300 range behind 3 rocks.                     //
//~ Level 2: 500 range behind 4 rocks.                     //
//~ Level 3: 700 range behind 5 rocks.                     //
//~                                                        //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                        //
//~ Another protective spell. The rocks aren't that big,   //
//~ but you can block small paths. Even if the block isn't //
//~ fully blocking, the pushback can save your life :)     //
//~                                                        //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                        //
//~ Utilities:                                             //
//~         - TimerUtils                                   //
//~         - Pathability                                  //
//~                                                        //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

scope ProtectiveBarrier initializer InitBarrier

    globals
        //Rawcode of "Protective Barrier" [Spell]
        private constant integer SPELLID = 'A001'
        //Rawcode of "Barrier(Protective Barrier)" [Unit]
        private constant integer BARRIERID = 'h000'
        
        //Sfx shown at the caster
        private constant string CASTERSFX = "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl"
        //Sfx attachment point
        private constant string CASTERATTACH = "chest"
        //Sfx fpr the barriers
        private constant string BARRIERSFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
        
        //Duration of the barrier
        private constant real BARDUR = 5.
        //Distance away in front of the caster
        private constant real BARDIST = 150.
        //Sidedistance between the middle of each barrier
        private constant real SIDEDIST = 100.
        
        //Knockback speed
        private constant real SPEED = 1000.
        //Timerperiod (unimportant)
        private constant real PERIOD = 0.03
        //If false, units will be dodged (uses SetUnitPosition instead of SetUnitX/Y)
        //This looks realy silly, however, someone might find it usefull.
        private constant boolean IGNOREUNITS = true
    endglobals
    
    //Overall distance knocked back
    private constant function GetDistance takes integer i_lvl returns real
        return 100.+i_lvl*200.
    endfunction
    //Number of barriers
    private constant function GetNumber takes integer i_lvl returns integer
        return 2+i_lvl
    endfunction

    private struct PushBack
        //Caster
        unit u_caster
        //His position
        real r_x
        real r_y
        //Movementvalues
        real r_cos
        real r_sin
        //Saves distance values to find the end
        real r_step
        real r_done
        real r_max
        
        //Movemethod, callback of the timer
        static method Move takes nothing returns nothing
            local timer t_push = GetExpiredTimer()
            local PushBack s_dat = GetTimerData(t_push)
            
            //Moving backwards
            set s_dat.r_x = s_dat.r_x-s_dat.r_cos
            set s_dat.r_y = s_dat.r_y-s_dat.r_sin
            if IsTerrainWalkable(s_dat.r_x, s_dat.r_y) then
                static if b_IGNOREUNITS then
                    call SetUnitX(s_dat.u_caster, s_dat.r_x)
                    call SetUnitY(s_dat.u_caster, s_dat.r_y)
                else
                    call SetUnitPosition(s_dat.u_caster, s_dat.r_x, s_dat.r_y)
                endif
            else
                //In this case, collision hit something that's NOT a unit-> will never move away... just end it
                set s_dat.r_done = s_dat.r_max
            endif
            
            set s_dat.r_done = s_dat.r_done+s_dat.r_step
            if s_dat.r_done >= s_dat.r_max then
                call ReleaseTimer(t_push)
                set s_dat.u_caster = null
                call s_dat.destroy()
            endif
        endmethod
            
        static method Create takes nothing returns boolean
            local PushBack s_dat
            local real r_x
            local real r_y
            local real r_angle
            local unit u_temp
            local player p_temp
            local timer t_push
            local integer i_number
            local integer i_loop
            
            if GetSpellAbilityId() == SPELLID then
                //Allocating, caster coordinates and facing and the rest of the struct
                set s_dat = PushBack.allocate()
                set s_dat.u_caster = GetTriggerUnit()                
                set r_angle = GetUnitFacing(s_dat.u_caster)*bj_DEGTORAD
                set s_dat.r_x = GetUnitX(s_dat.u_caster)
                set s_dat.r_y = GetUnitY(s_dat.u_caster)
                
                set s_dat.r_max = GetDistance(GetUnitAbilityLevel(s_dat.u_caster, SPELLID))
                set s_dat.r_step = SPEED*PERIOD
                set s_dat.r_cos = s_dat.r_step*Cos(r_angle)
                set s_dat.r_sin = s_dat.r_step*Sin(r_angle)
                set s_dat.r_done = 0.
                //SFX :D
                call DestroyEffect(AddSpecialEffectTarget(CASTERSFX, s_dat.u_caster, CASTERATTACH))
                //Getting the first rocks point and the amount of rocks
                //The point is the "most left one" regarding the casters facing
                set i_number = GetNumber(GetUnitAbilityLevel(s_dat.u_caster, SPELLID))
                set r_x = s_dat.r_x+BARDIST*Cos(r_angle)+(i_number-1)/2*SIDEDIST*Cos(r_angle-bj_PI/2)
                set r_y = s_dat.r_y+BARDIST*Sin(r_angle)+(i_number-1)/2*SIDEDIST*Sin(r_angle-bj_PI/2)
                //We're starting at one side, going to the other (barrier-creation)
                set r_angle = r_angle+bj_PI/2
                
                //Tempplayer and the rocks
                set p_temp = GetOwningPlayer(s_dat.u_caster)
                set i_loop = 0
                loop
                    exitwhen i_loop >= i_number
                    //SFX :D
                    call DestroyEffect(AddSpecialEffect(BARRIERSFX, r_x, r_y))
                    set u_temp = CreateUnit(p_temp, BARRIERID, r_x, r_y, GetRandomReal(0., 359.9))
                    //CreateUnit cares for collision, SetUnitX/Y not :)
                    call SetUnitX(u_temp, r_x)
                    call SetUnitY(u_temp, r_y)
                    call UnitApplyTimedLife(u_temp, 'BTLF', BARDUR)
                    set r_x = r_x+SIDEDIST*Cos(r_angle)
                    set r_y = r_y+SIDEDIST*Sin(r_angle)
                    set i_loop = i_loop+1
                endloop
                //And the timer
                set t_push = NewTimer()
                call SetTimerData(t_push, s_dat)
                call TimerStart(t_push, PERIOD, true, function PushBack.Move)
            endif
        
            return false
        endmethod            
    endstruct

    private function InitBarrier takes nothing returns nothing
        local trigger t_init = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t_init, Condition(function PushBack.Create))
        
        call Preload(CASTERSFX)
        call Preload(BARRIERSFX)
    endfunction

endscope


JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Soil Totem v1.2                                                             //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Summons an immobile Soil Totem which                                        //
//~ calls a wave of explosions after a random                                   //
//~ amount of time, dealing damage to each                                      //
//~ enemy within a certain range.                                               //
//~ Damage increases the lower Justifys                                         //
//~ hitpoints are, while low hitpoints causing                                  //
//~ the speed of the explosions to increase.                                    //
//~                                                                             //
//~ Level 1: 60 maxdamage in 550 range every 2-6 seconds.                       //
//~ Level 2: 120 maxdamage in 600 range every 1-3 seconds.                      //
//~ Level 3: 180 maxdamage in 650 range every 0.7-2 seconds.                    //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ The aggressiv way of protection. The                                        //
//~ damage at low life is heavy but weak                                        //
//~ with a lot of hitpoints.                                                    //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Utilities:                                                                  //
//~             -TimerUtils                                                     //
//~             -GroupUtils                                                     //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

scope SoilTotem initializer InitTotem

    globals
        //The rawcode of "SoilTotem" [Spell]
        private constant integer SPELLID = 'A002'
        //The rawcode of "Totem (SoilTotem)" [Unit]
        private constant integer TOTEMID = 'o000'
        
        //Sfx path of the effect at the totem while ticking (ground)
        private constant string TOTEMSFX = "Abilities\\Spells\\Human\\Flare\\FlareCaster.mdl"
        //Sfx path of the effect at hit units
        private constant string HITSFX = "Abilities\\Weapons\\SteamTank\\SteamTankImpact.mdl"
        //Attachmentpoint of HITSFX
        private constant string HITATTACH = "chest"
        
        //Speed-increase hitpoint cap. 1=100%, 0.5 = 50% ...
        private constant real HPCAP = 0.5
        //Speed-increase of the cap ;) 2 = double, 3 = triple ...
        private constant real CAPINCREASE = 2
                     
        private player TEMPPLAYER
        private integer TEMPINT
        private real FACTOR
    endglobals

    //Feel free to change to formulas below this comment (just the stuff behind the "return")
    //How long the totem will live
    private function GetTotemDuration takes integer i_lvl returns real
        return 10.*i_lvl
    endfunction
    //How many ticks a totem can do at maximum
    private function GetTotemTicks takes integer i_lvl returns integer
        return 2*i_lvl
    endfunction
    //Time until the next tick, GetRandomReal(4.,8.) is a random value between 4 and 8
    private function GetNextTick takes integer i_lvl returns real
        return GetRandomReal(2., 6.)/I2R(i_lvl)
    endfunction
    //The damage done per tick
    private function GetSpellDamage takes integer i_lvl returns real
        return 120.*i_lvl
    endfunction
    //The radius of the totem ticks
    private function GetTotemRadius takes integer i_lvl returns real
        return 500.+50.*i_lvl
    endfunction

    //Don't touch the stuff below THIS comment ;) it is the spell core and you can't change any values
    //here. Only change things if you want to change the way the spell works and you have enough knowledge of vJass.
    private struct TotemStruct
        //The caster
        unit u_caster
        //The totem
        unit u_totem
        //The totems position
        real r_totemx
        real r_totemy
        //The casters owner
        player p_owner
        //Level of SoilTotem
        integer i_abillvl
        //Damage done by the totem
        real r_damage
        //AOE for the damage
        real r_radius
        //Duration of the totem
        real r_maxtime
        //Max ticks of the totem. If this or maxtime is reached, the spell is finished
        integer r_maxcount
        
        //Counts the time/ticks
        real r_acttime
        integer r_actcount
        
        //Timer for the ticks
        timer t_tick
        
        //Enemy filter method, filters non-enemies, dead ones and magic-immunes
        private static method Enemys takes nothing returns boolean
            return IsUnitEnemy(GetFilterUnit(), TEMPPLAYER) and GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.405 and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)
        endmethod
        //Damage method for the ForGroup function
        private static method Damage takes nothing returns nothing
            local TotemStruct s_dat = TEMPINT
            call UnitDamageTarget(s_dat.u_caster, GetEnumUnit(), s_dat.r_damage*FACTOR, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            //SFX :D
            call DestroyEffect(AddSpecialEffectTarget(HITSFX, GetEnumUnit(), HITATTACH))
        endmethod
        //Function called by the timer (callback)
        private static method RunAbility takes nothing returns nothing
            local TotemStruct s_dat = GetTimerData(GetExpiredTimer())
            local real r_tick
            local real r_life
            //Alive-check of the totem
            if GetUnitState(s_dat.u_totem, UNIT_STATE_LIFE) > 0.405 then
                //SFX :D
                call DestroyEffect(AddSpecialEffect(TOTEMSFX, s_dat.r_totemx, s_dat.r_totemy))
                set TEMPPLAYER = s_dat.p_owner
                //Pick units in AOE
                call GroupEnumUnitsInRange(ENUM_GROUP, s_dat.r_totemx, s_dat.r_totemy, s_dat.r_radius, Condition(function TotemStruct.Enemys))
                set TEMPINT = s_dat
                //A factor between 0 and which is multiplied with the damage
                //So, if you got full hitpoints, this won't deal ANY damage!
                set r_life = GetUnitState(s_dat.u_caster, UNIT_STATE_MAX_LIFE)
                set FACTOR = (r_life-GetWidgetLife(s_dat.u_caster))/r_life
                //Damage units with ForGroup
                call ForGroup(ENUM_GROUP, function TotemStruct.Damage)
            elseif not (s_dat.t_tick == null) then
                //The totem is dead, reset the timer ....
                call s_dat.destroy()
                return
            endif
            
            //If this if is true, either maxticks or maxtime is reached
            if s_dat.r_acttime >= s_dat.r_maxtime or s_dat.r_actcount >= s_dat.r_maxcount then
                call s_dat.destroy()
            else
                //If not, we'll deal with the next tick in a random time
                if GetWidgetLife(s_dat.u_caster)/r_life <= HPCAP then
                    set FACTOR = 1/CAPINCREASE
                else
                    set FACTOR = 1.
                endif
                set r_tick = GetNextTick(s_dat.i_abillvl)*FACTOR
                set s_dat.r_acttime = s_dat.r_acttime+r_tick
                set s_dat.r_actcount = s_dat.r_actcount+1
                call TimerStart(s_dat.t_tick, r_tick, false, function TotemStruct.RunAbility) 
            endif
        endmethod
        //Simple create method that gets all important values        
        static method Create takes nothing returns boolean
            local TotemStruct s_dat
            local integer i_lvl
            
            if GetSpellAbilityId() == SPELLID then
                set s_dat = TotemStruct.allocate()
                set s_dat.u_caster = GetTriggerUnit()
                set s_dat.p_owner = GetOwningPlayer(s_dat.u_caster)
                set s_dat.u_totem = CreateUnit(s_dat.p_owner, TOTEMID, GetSpellTargetX(), GetSpellTargetY(), GetRandomReal(0., 360.))
                //To avoid that the sfx isn't at the totem in the case
                //that the totem is missplaced tue to unpathability
                set s_dat.r_totemx = GetUnitX(s_dat.u_totem)
                set s_dat.r_totemy = GetUnitY(s_dat.u_totem)
                set s_dat.i_abillvl = GetUnitAbilityLevel(s_dat.u_caster, SPELLID)
                set s_dat.r_damage = GetSpellDamage(s_dat.i_abillvl)
                set s_dat.r_radius = GetTotemRadius(s_dat.i_abillvl)
                set s_dat.r_maxtime = GetTotemDuration(s_dat.i_abillvl)
                set s_dat.r_maxcount = GetTotemTicks(s_dat.i_abillvl)
                
                set s_dat.r_acttime = GetNextTick(s_dat.i_abillvl)
                set s_dat.r_actcount = 1
                
                set s_dat.t_tick = NewTimer()
                call SetTimerData(s_dat.t_tick, s_dat)
                call TimerStart(s_dat.t_tick, s_dat.r_acttime, false, function TotemStruct.RunAbility)
            endif
            return false
        endmethod
        
        private method onDestroy takes nothing returns nothing
            //Leak and timer removal
            set .u_caster = null
            call KillUnit(.u_totem)
            set .u_totem = null
            call ReleaseTimer(.t_tick)
        endmethod
            
    endstruct
    
    private function InitTotem takes nothing returns nothing
        local trigger t_init = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t_init, Condition(function TotemStruct.Create))
        
        call Preload(TOTEMSFX)
        call Preload(HITSFX)
    endfunction
    
endscope


JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Last Stand v1.0                                                             //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Releasing his inner will to survive,                                        //
//~ Justify fully heals himself instantly.                                      //
//~ The extra amount of hitpoints                                               //
//~ don't last very long, damaging                                              //
//~ him as soon as his will tends to break,                                     //
//~ causing him to stop a moment.                                               //
//~ Justify can't die due to this damage.                                       //
//~                                                                             //
//~ Level 1: 3.0x healed life as damage after 8 seconds.                        //
//~ Level 2: 2.5x healed life as damage after 11 seconds.                       //
//~ Level 3: 2.0x healed life as damage after 14 seconds.                       //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ A rather simple spell which is rather strong.                               //
//~ Even 8 seconds should be enough to head back from                           //
//~ the enemies and rescue yourself.                                            //
//~ Also, when the damage option is enabled, it is a                            //
//~ pretty decent combo with "Retardation".                                     //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~                                                                             //
//~ Utilities:                                                                  //
//~             -TimerUtils                                                     //
//~                                                                             //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

scope LastStand initializer InitStand

    globals
        //Rawcode of "Last Stand" [Spell]
        private constant integer SPELLID = 'A003'
        
        //SFX that's attached to the caster until the spell is over
        private constant string SFX = "Abilities\\Spells\\Orc\\Voodoo\\VoodooAuraTarget.mdl"
        //Point for SFX
        private constant string ATTACH = "chest"
        //SFX that's palyed when the spells finished (and the damage done)
        private constant string ENDSFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
        
        //Damage constants for the UnitDamageFunction (only important when b_DAMAGE is true)
        //Like this it will always deal full damage, to regard armor and types you need
        //to change a_ATTACK and d_DAMAGE
        private constant boolean ISATTACK = true
        private constant boolean ISRANGED = true
        private constant attacktype ATTACK = ATTACK_TYPE_CHAOS
        private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
        private constant weapontype WEAPON = WEAPON_TYPE_WHOKNOWS
        
        //If true, the loss of life is damage, causing damage detection systems
        //to fire. If CANTDIE is false, he can kill himself with kill credits
        //of him. If not, it's "SetWidgetLife" without any references to the damage source.
        private constant boolean USEDAMAGE = true
        //If true, the caster cant die due to this damage and will remain with 1hp if the damage
        //kills him.
        private constant boolean CANTDIE = true
    endglobals

    //Formula for the received damage
    private constant function GetDamageFactor takes integer i_lvl returns real
        return 3.5-i_lvl/2.
    endfunction
    //Function for the duration until the damage is done
    private constant function GetDuration takes integer i_lvl returns real
        return 5.+i_lvl*3.
    endfunction

    private struct LifeHolder
        //The caster
        unit u_caster
        //The damage he'll suffer
        real r_damage
        //Timer to end the spell
        timer t_end
        //Sfx anyone?
        effect e_sfx
        
        static method End takes nothing returns nothing
            local LifeHolder s_dat = GetTimerData(GetExpiredTimer())
            local real r_life
            
            //Reducing the damage, so the caster will survive it
            static if CANTDIE then
                set r_life = GetWidgetLife(s_dat.u_caster)
                if GetWidgetLife(s_dat.u_caster) - s_dat.r_damage < 1 then
                    set s_dat.r_damage = GetWidgetLife(s_dat.u_caster)-1
                endif
            endif
            
            //Choosing between the damage types
            static if USEDAMAGE then
                call UnitDamageTarget(s_dat.u_caster, s_dat.u_caster, s_dat.r_damage, ISATTACK, ISRANGED, ATTACK, DAMAGE, WEAPON)  
            else
                call SetWidgetLife(s_dat.u_caster, GetWidgetLife(s_dat.u_caster)-s_dat.r_damage)
            endif
            //Stopping the caster. Interrupts channelings, order chains... care ;)
            call IssueImmediateOrder(s_dat.u_caster, "stop")
            //SFX again :D
            call DestroyEffect(AddSpecialEffect(ENDSFX, GetUnitX(s_dat.u_caster), GetUnitY(s_dat.u_caster)))
            
            //Leak removal
            call DestroyEffect(s_dat.e_sfx)
            set s_dat.e_sfx = null
            set s_dat.u_caster = null
            call ReleaseTimer(s_dat.t_end)
            
            call s_dat.destroy()
        endmethod            
            
        static method Create takes nothing returns boolean
            local LifeHolder s_dat
            local real r_life
            local integer i_lvl
            
            if GetSpellAbilityId() == SPELLID then
                //Allocation, sfx, variable sets
                set s_dat = LifeHolder.allocate()
                set s_dat.u_caster = GetTriggerUnit()
                set s_dat.e_sfx = AddSpecialEffectTarget(SFX, s_dat.u_caster, ATTACH)
                set r_life = GetUnitState(s_dat.u_caster, UNIT_STATE_MAX_LIFE)
                set i_lvl = GetUnitAbilityLevel(s_dat.u_caster, SPELLID)
                //His new life is mirrored into the negativ: The life he already lost
                //(MaxLife-ActualLife) will be substracted again after the spell, 
                //and this three times with the normal options 
                set s_dat.r_damage = (r_life-GetWidgetLife(s_dat.u_caster))*GetDamageFactor(i_lvl)
                
                //Setting his life to max!
                call SetWidgetLife(s_dat.u_caster, r_life)
                
                set s_dat.t_end = NewTimer()
                call SetTimerData(s_dat.t_end, s_dat)
                call TimerStart(s_dat.t_end, GetDuration(i_lvl), false, function LifeHolder.End)
            endif
            
            return false
        endmethod
    endstruct

    private function InitStand takes nothing returns nothing
        local trigger t_init = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)    
        call TriggerAddCondition(t_init, Condition(function LifeHolder.Create))
    endfunction

endscope



Changelog:

v1.3 Changed pathability script, rewrote variable names and "uninlined" the event

v1.2 Added my old but rewritten spell "Soil Totem"

v1.1 Added hero options to "Retardation"

v1.0 Release

Credits:

- Vexorian for the JNGP, TimerUtils
- Rising_Dusk for GroupUtils, TerrainPathability
- Me for the spells and InlinedHashtable

Keywords:
pack, spells, stones, rock, pushback, knockback, prevent, damage prevention, soil, totem, prevent, rescue, save
Contents

Just another Warcraft III map (Map)

Reviews
11:40, 4th Jan 2010 TriggerHappy: Personally I dislike you using _'s everywhere while naming variables but I suppose that is your personal preference. But for constant globals, please use all capitals. You should probably be using Rising_Dusks...

Moderator

M

Moderator

11:40, 4th Jan 2010
TriggerHappy:

Personally I dislike you using _'s everywhere while naming variables but I suppose that is your personal preference. But for constant globals, please use all capitals.

You should probably be using Rising_Dusks pathablity script.
And for the love of god, don't inline the spell event.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
I first must say, "Triggers & Scripts" froum would just left him without any feedback.

I had my post there for several weeks, ~400 views and barely a few comments so contrary to the belief that you will get some feedback there, you won't.

Therefore i don't mind this spellpack being uploaded here for feedback, since it sounds like he will be updating it.

EDIT:

You can get stuck in trees using Protective barier.

Retardation seems a bit OP, you should either limit it to hero damage, or make it not work on spell damage type(my opinion that is).

Also the shield barrier effects aren't so great :p
 
Level 13
Joined
Mar 16, 2008
Messages
941
Well, I would put it in the trigger & scripts forum and a week later I could put it here because I got enough spells, don't likte this idea. Just a mess around with threads.
Also, this are completly finished spells, I could've published them as single spells and I'd get feedback, only wanted to create the pack for future needs.

@Protective Barrier: I know that you can get stuck, but I can't change it.
I use SetUnitX/Y to avoid creeps like a normal pushback, but I use Vexorians Pathability function and stop the pushback as soon as it's not pathable.
The problem are the steps used to push, the timer already has a lower period to avoid such problems, but I don't want to kill performance. Any ideas how to avoid this?
And to the effects: I wanted stones to block, just naturaly. They got no spawn or death animations, so the flashy lights should distract the user from the stones in the first moment. Again: good suggestions for better effects?

@Retardation: Yeah, I though so when two creepcamps hit me at once and I didn't took any damage. Since it was thought as a nuke-protection a limitation against spell damage would rather kill it I think. But since Vexorian invented static ifs, I can give such options quite easily :) EDIT: Erm, spell/physical damage block? That would require orbs or for example the intuitive damage system?
Thanks :D
 
Top