1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  4. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  5. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  6. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  7. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Mystic Storm v1.1i.w3x
Variables
Initialization
Init
Untitled Trigger 001
Spell
Instruction
Mystic Storm
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.

		
Name Type Is Array Initial Value
MS_Hashtable hashtable No
Init
  Events
    Map initialization
  Conditions
  Actions
    Visibility - Disable fog of war
    Visibility - Disable black mask
    Game - Set the time of day to 12
Just for testing
Untitled Trigger 001
  Events
    Unit - A unit Dies
  Conditions
    (Triggering unit) Equal to Mountain King 0023 <gen>
  Actions
    Wait 2 seconds
    Hero - Instantly revive Mountain King 0023 <gen> at (Position of (Triggering unit)), Show revival graphics
Installation prequisites:
+ A hashtable, in this case we will need udg_MS_Hashtable
+ A basic understanding of JASS.

How to import:
Step 1: Create a hashtable, the dummy units.
Step 2: Copy the spell triggers (Mystic Storm).
Step 3: Modify the values in the configuration part of the spell.

Give proper credit where it is due if you use my resources. Thanks.

Credits to the original owner(s) of respective resources used in the making of this spell.
// CONFIGURATION

// Constants

// Main Spell raw code
constant function MS_GetSpellID takes nothing returns integer
    return 'A000'
endfunction

// Dummy raw code
constant function MS_DummyID takes nothing returns integer
    return 'h001'
endfunction

// Dummy Lightning Spell raw code
constant function MS_LightningID takes nothing returns integer
    return 'A001'
endfunction

// Armor (-1) ability raw code
constant function MS_Armor1ID takes nothing returns integer
    return 'A002'
endfunction

// Armor (-10) ability raw code
constant function MS_Armor10ID takes nothing returns integer
    return 'A003'
endfunction

// Armor (-100) ability raw code
constant function MS_Armor100ID takes nothing returns integer
    return 'A009'
endfunction

// Spell Book raw code
constant function MS_SpellBookID takes nothing returns integer
    return 'A007'
endfunction

// Dummy lightning base order string
constant function MS_LightningOrder takes nothing returns string
    return "fingerofdeath"
endfunction

// Booleans

// Can attack structures or not?
constant function MS_AttackStrucs takes nothing returns boolean
    return true
endfunction

// Can reduce structures' armor or not?
constant function MS_ReduceStrucArmor takes nothing returns boolean
    return true
endfunction

// True = target the unit with lowest HP percentage, false = target unit with lowest HP amount
constant function MS_HPPercentage takes nothing returns boolean
    return true
endfunction

// Enable random interval or not?
constant function MS_RandomInterval takes nothing returns boolean
    return false
endfunction

// Minimum value for random interval
constant function MS_RandomIntervalMin takes nothing returns real
    return 1.00
endfunction

// Maximum value for random interval
constant function MS_RandomIntervalMax takes nothing returns real
    return 2.00
endfunction

// Filter valid units

function MS_FilterUnit takes unit caster, unit u returns boolean
    local boolean b = IsUnitEnemy(u, GetOwningPlayer(caster)) and GetWidgetLife(u) > 0.405 and GetUnitAbilityLevel(u, 'Avul') == 0 and not IsUnitType (u, UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(u, GetOwningPlayer(caster))
    if MS_AttackStrucs() then
        return b
    endif

    return b and not IsUnitType (u, UNIT_TYPE_STRUCTURE)
endfunction

// Get Abilities Level

// Get the spell level of the caster
function MS_SpellLvl takes unit caster returns integer
    return GetUnitAbilityLevel (caster, MS_GetSpellID())
endfunction

// Get the armor (-1) level of the target
function MS_Armor1Lvl takes unit u returns integer
    return GetUnitAbilityLevel (u, MS_Armor1ID())
endfunction

// Get the armor (-10) level of the target
function MS_Armor10Lvl takes unit u returns integer
    return GetUnitAbilityLevel (u, MS_Armor10ID())
endfunction

// Get the armor (-100) level of the target
function MS_Armor100Lvl takes unit u returns integer
    return GetUnitAbilityLevel (u, MS_Armor100ID())
endfunction

// Level dependable values

// Spell Duration
function MS_Duration takes integer level returns real
    return 20 + level*10.
endfunction

// Damage per strike
function MS_Damage takes integer level returns real
    return 50 + level*10.
endfunction

// Armor reduced per strike
function MS_ArmorReduced takes integer level returns integer
    return level
endfunction

// Max attack range of the storm
function MS_Range takes integer level returns real
    return 400 + level*100.
endfunction

// Damage intensification rate i.e the increment of damage percentage with each successive attack
function MS_DmgPlusRate takes integer level returns real
    return 0.0
endfunction

// Cooldown between each strike
function MS_Cooldown takes integer level returns real
    return 1.25 - level*0.15
endfunction

// Max amount of units attacked per strike
function MS_MaxStrike takes integer level returns integer
    return level
endfunction

// Attack - Damage - Weapon type

function MS_AttackType takes nothing returns attacktype
    return ATTACK_TYPE_HERO
endfunction

function MS_DamageType takes nothing returns damagetype
    return DAMAGE_TYPE_NORMAL
endfunction

function MS_WeaponType takes nothing returns weapontype
    return WEAPON_TYPE_WHOKNOWS
endfunction

// END CONFIGURATION

// Preload

function MS_Preload takes nothing returns nothing
    local unit dummy
    local integer i = 0

    set dummy  = CreateUnit(Player(0), MS_DummyID(), 0, 0, 0.00)
    call UnitAddAbility (dummy, MS_GetSpellID())
    call UnitAddAbility (dummy, MS_Armor10ID())
    call UnitAddAbility (dummy, MS_Armor10ID())
    call UnitAddAbility (dummy, MS_Armor100ID())
    call UnitAddAbility (dummy, MS_SpellBookID())

    loop
        exitwhen i > 15
        call SetPlayerAbilityAvailable(Player(i), MS_SpellBookID(), false) // Disable the spell book
        set i = i + 1
    endloop

    call RemoveUnit (dummy)
    set dummy = null
endfunction

// End preload

// SPELL FUNCTIONS

// Modify the armor reduction value of the affected unit

function MS_ModifyArmor takes unit target, integer armorreduction returns nothing
    local integer array armor
    local integer level1 = MS_Armor1Lvl(target)
    local integer level2 = MS_Armor10Lvl(target)
    local integer level3 = MS_Armor100Lvl(target)
    local real armorreduce = level1+level2*10+level3*100
    local real armorcalc
    local integer tempdivider
    local integer tempint
    local integer i

    set armor[0] = MS_Armor100ID()
    set armor[1] = MS_Armor10ID()
    set armor[2] = MS_Armor1ID()
    if armorreduce == 0 then
         call UnitAddAbility(target, MS_SpellBookID()) // Add the spell book
         set armorcalc = armorreduce + armorreduction // Set the amount of armor to be modified
         if armorcalc == 0 then
             call UnitRemoveAbility (target, MS_SpellBookID())
         endif
         set i = 0
         set tempint = 0
         set tempdivider = 100
         loop
         exitwhen i == 3
              set tempint = R2I(armorcalc/tempdivider)
              call SetUnitAbilityLevel(target, armor[i], tempint+1)
              set armorcalc = armorcalc - I2R(tempint*tempdivider)
              set tempdivider = tempdivider/10
              set i = i + 1
         endloop
    else
         set armorreduce = level1+level2*10+level3*100-111 // Calculate the current armor reduction rate
         set armorcalc = armorreduce + armorreduction
         if armorcalc == 0 then
             call UnitRemoveAbility (target, MS_SpellBookID()) // Remove the spell book if the unit's armor reduction reaches 0
         endif
         set i = 0
         set tempint = 0
         set tempdivider = 100
         loop
         exitwhen i == 3
              set tempint = R2I(armorcalc/tempdivider)
              call SetUnitAbilityLevel(target, armor[i], tempint+1)
              set armorcalc = armorcalc - I2R(tempint*tempdivider)
              set tempdivider = tempdivider/10
              set i = i + 1
         endloop
    endif
endfunction

// Damage the target and initiate the armor reduction

function MS_DamageTarget takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer timerID = GetHandleId(t)
    local unit caster = LoadUnitHandle(udg_MS_Hashtable, timerID, 0)
    local unit target = LoadUnitHandle(udg_MS_Hashtable, timerID, 1)
    local integer id = LoadInteger (udg_MS_Hashtable, timerID, 2)
    local group g = LoadGroupHandle(udg_MS_Hashtable, id, 3)
    local real dmg = LoadReal (udg_MS_Hashtable, timerID, 3)
    local integer unitId = GetHandleId(target)
    local integer count = LoadInteger (udg_MS_Hashtable, unitId, id)
    local integer i = MS_SpellLvl(caster)

    // Damage the target and check how many armor should be reduced
    call UnitDamageTarget(caster, target, dmg, false, false, MS_AttackType(), MS_DamageType(), MS_WeaponType())

    if MS_ReduceStrucArmor() and IsUnitType(target, UNIT_TYPE_STRUCTURE) then
        call MS_ModifyArmor (target, MS_ArmorReduced(i))
        set count = count + MS_ArmorReduced(i)
        call SaveInteger (udg_MS_Hashtable, unitId, id, count)
    else

        if not IsUnitType (target, UNIT_TYPE_STRUCTURE) then
            call MS_ModifyArmor (target, MS_ArmorReduced(i))
            set count = count + MS_ArmorReduced(i)
            call SaveInteger (udg_MS_Hashtable, unitId, id, count)
        endif

    endif

    if IsUnitType (target, UNIT_TYPE_DEAD) then
        call UnitRemoveAbility (target, MS_SpellBookID())
    endif

    if g != null then
        call GroupAddUnit (g, target)
    else
        set g = CreateGroup()
        call GroupAddUnit (g, target)
    endif

    // Increase the damage for the next hit
    set dmg = dmg*(1.00 + MS_DmgPlusRate(i)/100)
    call SaveGroupHandle(udg_MS_Hashtable, id, 3, g)
    call SaveReal (udg_MS_Hashtable, id, 4, dmg)

    // Clean Up
    call FlushChildHashtable(udg_MS_Hashtable, timerID)
    call PauseTimer(t)
    call DestroyTimer(t)

    set caster = null
    set target = null
    set g = null
    set t = null
endfunction

// Function to loop through units and choose the one with the lowest HP percentage or amount

function MS_LifeCheck takes unit caster, unit stormfx, integer id, real dmg returns nothing
    local real lifecheck
    local group g = CreateGroup()
    local real x = GetUnitX(caster)
    local real y = GetUnitY(caster)
    local unit target
    local timer t
    local unit u
    local integer timerID
    local real r

    // Group units for valid units
    call GroupEnumUnitsInRange(g, x, y, MS_Range(MS_SpellLvl(caster)), null)

    loop
        set target = FirstOfGroup(g)
        exitwhen target == null or MS_FilterUnit(caster, target) and not IsUnitInGroup (target, LoadGroupHandle(udg_MS_Hashtable, id, 5))
        call GroupRemoveUnit(g, target)
    endloop

    // Loops through the chosen units for the target
    if target != null then
        set lifecheck = GetUnitState(target, UNIT_STATE_LIFE)

        if MS_HPPercentage() then
            set lifecheck = lifecheck/GetUnitState(target, UNIT_STATE_MAX_LIFE)
        endif

        loop
            set u = FirstOfGroup(g)
            exitwhen u == null

            if MS_FilterUnit(caster, u) and not IsUnitInGroup (u, LoadGroupHandle(udg_MS_Hashtable, id, 5)) then
                set r = GetUnitState(u, UNIT_STATE_LIFE)

                if MS_HPPercentage() then
                    set r = r/GetUnitState(u, UNIT_STATE_MAX_LIFE)
                endif

                if r<lifecheck then
                    set lifecheck = r
                    set target = u
                endif

            endif

            call GroupRemoveUnit(g, u)
        endloop

        set t = CreateTimer ()
        set timerID = GetHandleId(t)
        call SaveUnitHandle(udg_MS_Hashtable, timerID, 0, caster)
        call SaveUnitHandle(udg_MS_Hashtable, timerID, 1, target)
        call SaveInteger(udg_MS_Hashtable, timerID, 2, id)
        call SaveReal(udg_MS_Hashtable, timerID, 3, dmg)
        call TimerStart (t, 0.3, false, function MS_DamageTarget)
        call GroupAddUnit (LoadGroupHandle(udg_MS_Hashtable, id, 5), target)
        call IssueTargetOrder(stormfx, MS_LightningOrder(), target)

        set target  = null
        set t = null
    endif

    call DestroyGroup (g)
    set g = null
endfunction

// Periodical trigger for moving the dummy and initialize stuffs when the spell ends

function MS_Periodic takes nothing returns nothing
    local trigger trig = GetTriggeringTrigger()
    local integer trigID = GetHandleId (trig)
    local unit caster = LoadUnitHandle(udg_MS_Hashtable, trigID, 1)
    local unit stormfx = LoadUnitHandle(udg_MS_Hashtable, trigID, 2)
    local integer evalcount = GetTriggerEvalCount(trig)
    local real x = GetUnitX(caster)
    local real y = GetUnitY(caster)
    local group g
    local real dmg = LoadReal (udg_MS_Hashtable, trigID, 4)
    local timer t
    local integer count
    local unit u
    local integer unitid
    local integer i = 0

    // Check if the caster is dead or the spell duration expired to remove the dummy
    if(GetTriggerEventId()==EVENT_UNIT_DEATH)then

        if(GetDyingUnit() == caster)then
            call KillUnit (stormfx) // We kill the dummy if the caster is dead
        endif

        call DestroyGroup (LoadGroupHandle(udg_MS_Hashtable, trigID, 5))
        call KillUnit (stormfx)

        // Remove the armor reduction induced by the spell instance
        set g = LoadGroupHandle(udg_MS_Hashtable, trigID, 3)

        loop
            set u = FirstOfGroup (g)
            exitwhen u == null
            set unitid = GetHandleId (u)
            set count = LoadInteger (udg_MS_Hashtable, unitid, trigID)
            call MS_ModifyArmor (u, (-count))
            call GroupRemoveUnit(g, u)
        endloop

        call DestroyGroup (g)
        call FlushChildHashtable(udg_MS_Hashtable, trigID)
        set g = null
        call DisableTrigger (trig)
        call TriggerRemoveAction (trig, LoadTriggerActionHandle(udg_MS_Hashtable, trigID, 0))
        set trig = null
        call DestroyTrigger (GetTriggeringTrigger())

    else
        call GroupClear (LoadGroupHandle(udg_MS_Hashtable, trigID, 5))

        if evalcount == 1 or evalcount >= LoadInteger(udg_MS_Hashtable, trigID, 6) then

            loop
                exitwhen i == MS_MaxStrike(MS_SpellLvl(caster))
                call MS_LifeCheck(caster, stormfx, trigID, dmg)
                set i = i + 1
            endloop

            if MS_RandomInterval() then
                call SaveInteger (udg_MS_Hashtable, trigID, 6, evalcount + GetRandomInt(R2I(MS_RandomIntervalMin()/0.03125), R2I(MS_RandomIntervalMax()/0.03125)))
            else
                call SaveInteger (udg_MS_Hashtable, trigID, 6, evalcount + R2I(MS_Cooldown(MS_SpellLvl(caster))/0.03125))
            endif
        endif

        call SetUnitPosition (stormfx, x, y)
    endif

    set caster = null
    set stormfx = null
    set trig = null
endfunction

// Initiate function

function Trig_MS_Conditions takes nothing returns boolean
    local trigger trig
    local unit caster
    local unit stormfx
    local real dmg
    local integer trigID

    if GetSpellAbilityId() == MS_GetSpellID() then
        set caster = GetTriggerUnit()
        set trig = CreateTrigger()
        set trigID = GetHandleId (trig)
        set stormfx = CreateUnit(GetTriggerPlayer(), MS_DummyID(), GetUnitX(caster), GetUnitY(caster), bj_UNIT_FACING)
        call UnitAddAbility(stormfx, MS_LightningID())
        call UnitApplyTimedLife (stormfx, 'BTLF', MS_Duration(MS_SpellLvl(caster)))
        call TriggerRegisterTimerEvent(trig, 0.03125, true)
        call TriggerRegisterUnitEvent(trig, caster, EVENT_UNIT_DEATH)
        call TriggerRegisterUnitEvent(trig, stormfx, EVENT_UNIT_DEATH)
        call SaveTriggerActionHandle(udg_MS_Hashtable , trigID, 0, TriggerAddAction( trig, function MS_Periodic ))
        call SaveUnitHandle(udg_MS_Hashtable, trigID, 1, caster)
        call SaveUnitHandle(udg_MS_Hashtable, trigID, 2, stormfx)
        call SaveReal(udg_MS_Hashtable, trigID, 4, MS_Damage(MS_SpellLvl(caster)))
        call SaveGroupHandle(udg_MS_Hashtable, trigID, 5, CreateGroup())
        call SaveInteger(udg_MS_Hashtable, trigID, 6, GetTriggerEvalCount(trig) + GetRandomInt(R2I(MS_RandomIntervalMin()/0.03125), R2I(MS_RandomIntervalMax()/0.03125)))
        set trig = null
        set stormfx = null
    endif
 
    set caster = null
    return false
endfunction

//===========================================================================
function InitTrig_Mystic_Storm takes nothing returns nothing
    local trigger MS = CreateTrigger()
    set udg_MS_Hashtable = InitHashtable ()
    call MS_Preload()
    call TriggerRegisterAnyUnitEventBJ(MS, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition( MS, Condition( function Trig_MS_Conditions ) )
    set MS = null
endfunction