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

Lightning Cloud v2.0

Summons a thick cloud that follows the caster and discharges bolts of lightning to nearby enemy units. Cloud lasts for 20 seconds.
Level 1 - Lightning strike deals 20 damage.
Level 2 - Lightning strike deals 40 damage.
Level 3 - Lightning strike deals 60 damage.
Level 4 - Lightning strike deals 80 damage.
Level 5 - Lightning strike deals 100 damage.

Credits for helping me improve the spell:
- baassee
- Maker
- Adiktuz
- Bribe


JASS:
//Spell Name: Lightning Cloud v2.0
//Made by: Mckill2009

//NOTE:
//- To all of you who are thinking that this is a rip-off dota's Razor's ultimate spell
// well, you are wrong, I even dont know that spell coz I really dont play DotA

//===HOW TO USE:
//- Make a new trigger and convert to custom text via EDIT >>> CONVERT CUSTOM TEXT
//- The trigger name MUST be >>> InitTrig_LightningCloud (see the name below)
//- Copy ALL that is written here and overwrite the existing texts in the custom text
//- Copy the Dummy/custom abilities/buffs etc... to your object editor
//- Make sure you inputed the correct raw codes of the base spell/buffs/dummy etc...
//- If your raw code is different, you MUST CHANGE IT...
//- You can view the raw codes by pressing CTRL+B in the object editor
//- Examples of raw codes are 'A000', 'h000' etc... 

//===REQUIRED VARIABLES:
//- HASH = Hashtable
//- LC_Cloud = Unit
//- LC_TimerN - Integer
//- LC_Timers - Timer Array

//===CONFIGURABLES:
constant function LC_SpellId takes nothing returns integer
    return 'A000' //Raw code for the Hero ability
endfunction 

constant function LC_DummySpellId takes nothing returns integer
    return 'A001' //Raw code for the Lightning Strike ability
endfunction                          

constant function LC_CloudUnit takes nothing returns integer
    return 'h001' //Raw code for the Cloud Unit
endfunction 

constant function LC_Damage takes integer i returns real
    return i * 20. //This is the ability level multiply by base damage
endfunction

constant function LC_Range takes nothing returns real
    return 500. //Maximum range is 3000 or you can adjust it via object editor 
endfunction

constant function LC_Duration takes nothing returns real
    return 20. //This is the duration of the cloud and spell
endfunction

constant function LC_TimerSpeed takes nothing returns real
    return 0.05
endfunction

constant function LC_ATT takes nothing returns attacktype
    return ATTACK_TYPE_MAGIC
endfunction

constant function LC_DAM takes nothing returns damagetype
    return DAMAGE_TYPE_LIGHTNING 
endfunction
//===END OF CONFIGURABLES===

function LC_RTimer takes timer t returns nothing
    if t != null then
        call PauseTimer(t)
        set udg_LC_Timers[udg_LC_TimerN] = t
        set udg_LC_TimerN = udg_LC_TimerN + 1
    endif
endfunction

//and get a timer
function LC_NTimer takes nothing returns timer
    if udg_LC_TimerN == 0 then
        return CreateTimer()
    endif
    set udg_LC_TimerN = udg_LC_TimerN - 1
    return udg_LC_Timers[udg_LC_TimerN]
endfunction
                                                                         
function LC_FilterThem takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, GetOwningPlayer(udg_LC_Cloud)) and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false
    set u = null
    return b
endfunction 
          
function LC_Loop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer parent = GetHandleId(t)
    local integer cloudkey
    local unit caster = LoadUnitHandle(udg_HASH, parent, 1)
    local unit cloud = LoadUnitHandle(udg_HASH, parent, 2)
    local real dur = LoadReal(udg_HASH, parent, 3)
    local real range = LoadReal(udg_HASH, parent, 4)
    set cloudkey = GetHandleId(cloud)
    set udg_LC_Cloud = cloud //should be transfered to a global variable
    if dur > 0 and GetWidgetLife(caster) > 0.405 then 
        call SaveReal(udg_HASH, parent, 3 , dur - LC_TimerSpeed())
        call SetUnitX(cloud, GetUnitX(caster))
        call SetUnitY(cloud, GetUnitY(caster))
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(cloud), GetUnitY(cloud), range, Filter(function LC_FilterThem))
        call IssueTargetOrder(cloud, "chainlightning" , FirstOfGroup(bj_lastCreatedGroup)) //852119       
    else
        call UnitApplyTimedLife(cloud, 'BTLF', 0.01)
        call LC_RTimer(t)
        call FlushChildHashtable(udg_HASH, parent)
        call FlushChildHashtable(udg_HASH, cloudkey)    
    endif
    set t = null
    set cloud = null
    set caster = null  
endfunction 

function LC_Cast takes nothing returns boolean
    local timer t
    local unit caster
    local unit cloud
    local integer cloudkey
    local integer parent
    local integer abilitylevel
    if GetSpellAbilityId()==LC_SpellId() then
        set caster = GetTriggerUnit()
        set abilitylevel = GetUnitAbilityLevel(caster, LC_SpellId())  
        set t = LC_NTimer()
        set parent = GetHandleId(t)
        set cloud = CreateUnit(GetTriggerPlayer(), LC_CloudUnit() , GetUnitX(caster), GetUnitY(caster), 0)
        set cloudkey = GetHandleId(cloud)
        //===This portion saves the caster so that he will deal damage, not the cloud
        call SaveUnitHandle(udg_HASH, cloudkey, 1, caster)
        call SaveReal(udg_HASH, cloudkey, 2 , LC_Damage(abilitylevel))
        //===This portion saves the caster/cloud/duration and range
        call SaveUnitHandle(udg_HASH, parent, 1, caster) 
        call SaveUnitHandle(udg_HASH, parent, 2, cloud)
        call SaveReal(udg_HASH, parent, 3, LC_Duration()) //See Function    
        call SaveReal(udg_HASH, parent, 4, LC_Range()) //See Function 
        call TimerStart(t, LC_TimerSpeed(), true, function LC_Loop) 
    elseif GetSpellAbilityId()==LC_DummySpellId() then
        set cloudkey = GetHandleId(GetTriggerUnit())
        call UnitDamageTarget(LoadUnitHandle(udg_HASH, cloudkey, 1), GetSpellTargetUnit(), LoadReal(udg_HASH, cloudkey, 2), false, false, LC_ATT(), LC_DAM(), null)
    endif 
    set t = null
    set caster = null
    set cloud = null
    return false
endfunction 
       
function InitTrig_LightningCloud takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function LC_Cast))
    set udg_HASH = InitHashtable()
    set t = null
endfunction



167625-albums4356-picture47040.jpg

167625-albums4356-picture47041.jpg




02 July 2011
- Function and Variables of Timers are prefixed with LC to avoid conflicts
- Hashtable is initialized in the code

10 June 2011
- Conditions merged
- Use release timer instead of destroy

09 June 2011
- Triggers reduced from 2 to 1
- Indenting fixed
- Codes reduced

28 May 2011
- Code is greatly optimized and reduced
- Stringhashes removed
- Constant functions applied
- Merged with condition
- Global variables reduced from 4 to 2

30 April 2011
- Most global variables replaced by fuctions
- More configurables
- Hashtables flushed

27 April 2011
- Made in JASS
- Attack & Damage type is now configurable
- Damage has been reduced but configurable
- Range/AoE has been reduced but configurable
- Speed when lightning strike is configurable
- Caster is now who deals damage
- Conditions for UnitDamageTarget is set
- Conditions for ability being cast is now separate
- Triggers reduced
- Special thanks to Maker and Adiktuz

09 April 2011
- New improved movement using SetUnitX and SetUnitY
- Triggers greatly reduced
- Dummy lightning caster removed
- AoE reduced from 3000 to 700

11 Feb 2011
- Added configuration timer and range of the cloud strike
- Cleaned group leaks
- Trigger optimized

04 Jan 2011
- Lightning hit is now random
- Lightning damage increased
- Lightning level damage optimized
- Icon changed from passive to active


Keywords:
lightning, cloud, strike, gust, light, rain, AoS, melee, rpg, JASS, vJASS
Contents

Lightning Cloud v2.0 (Map)

Reviews
19 October 2011 Bribe: Approved. But I still think if you make the origin of each lightning a random point in the cloud it will make it look more realistic.
Level 22
Joined
Nov 14, 2008
Messages
3,256
-This:

JASS:
function LC_FilterThem takes nothing returns boolean
    return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(udg_LC_Cloud)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)==false
endfunction

Should be this:

JASS:
function LC_FilterThem takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, GetOwningPlayer(udg_LC_Cloud)) and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false
    set u = null
    return b
endfunction

-Why are you still destroying timers :(

-In the LC_Cast function, it should return false always.

-Same with DummyCast.

This is way better than before I have to say! Good work!
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Right you recycle them in another spell of yours and not in this one, doesn't make any sense at all.

Alright I don't get Bribe's suggestion, adding both conditions to the trigger? Then you can really just merge those functions into one and you will get rid of a function as well :D (Bribe, I think you will catch my point)

This function still need to be changed to my suggestion in a previous comment.

JASS:
function LC_FilterThem takes nothing returns boolean
    return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(udg_LC_Cloud)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)==false
endfunction

into this

JASS:
function LC_FilterThem takes nothing returns boolean
    local unit u = GetFilterUnit()
    local boolean b = GetWidgetLife(u) > 0.405 and IsUnitEnemy(u, GetOwningPlayer(udg_LC_Cloud)) and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false
    set u = null
    return b
endfunction

Anyway looks better and better every single time I see it but you will probably need to update this a bit further to get it approved (Bribe has some high (very needed) standards (hopefully)).
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
You don't get it don't you? What I meant was that this:

JASS:
    call TriggerAddCondition(t, Condition(function LC_Cast))
    call TriggerAddCondition(t, Condition(function LC_DummyCast))

Can be just this

JASS:
    call TriggerAddCondition(t, Condition(function LC_Cast))

How? You merge those functions. They both contain ifs right? Then it won't be a problem to put the dummy if in the else part of the if the checks the spell id.
 
Level 9
Joined
May 14, 2011
Messages
524
Why make it so hard?
- Init : Make trigger dummy and set variables
- Loop : Make it 'jump' to the position of the caster until durationvariable is 0.

Now all you need is a dummy unit that looks like a cloud and has Phoenix Fire (Or even let it attack with Lightning Attack).
Only downside here is you either have to have a upgrade for the dummy's damage or have X different dummy units.

Also, the idea is kinda like Razor's ultimate (DotA).

3.5/5
That way,putting extra effects like knockback,etc would be harder
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
This is a bit lacking. I think I know how to make it really good:

Instead of always launching the lightning from the center of the cloud, launch the lightning from a random point in the cloud. This will give it a bit more realism.

I also recommend preloading the cloud special effect via the "Preload" native.

any ideas on how to generate lightning in random area around the cloud without making dummy units?...use lightning handle???...
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
its possible for modify this for makeing just a made cloud what moveing around the map (to random place) and rarly hit a unit who have user controller (so hit only the players and dont hit the creeps)?

create a dummy unit then make the dummy unit cast the cloud ability, order
your dummy unit to wander in map, the cloud will follow the dummy unit...

as for hitting non-creeps, you can edit the "LC_FilterThem", add the line "and GetOwningPlayer(u) != Player(PLAYER_NEUTRAL_AGGRESSIVE) "
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
create a dummy unit then make the dummy unit cast the cloud ability, order
your dummy unit to wander in map, the cloud will follow the dummy unit...

as for hitting non-creeps, you can edit the "LC_FilterThem", add the line "and GetOwningPlayer(u) != Player(PLAYER_NEUTRAL_AGGRESSIVE) "

thx, +1 noob question, where i must set the duration (coz i think to put a longer duration for cloud)
 
Level 7
Joined
Feb 13, 2022
Messages
86
Howdy I'm trying to implement this spell into my map, I have followed each step very carefully. Yet the power still doesn't work for me, all it does is create the cloud that follows the caster, but no lightning shoots out. Could anyone help with this issue, it would be deeply appreciated!?
 
Level 39
Joined
Feb 27, 2007
Messages
5,010
The author of this resource did something very stupid. Instead of creating an empty group variable to use for enumeration (finding chain lightning targets), they opted to just use bj_lastCreatedGroup. That's only usually an empty unit group, and in your case it sounds like that variable points to a group that has been destroyed. Replace the loop function with this edited version:
JASS:
function LC_Loop takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer parent = GetHandleId(t)
    local integer cloudkey
    local unit caster = LoadUnitHandle(udg_HASH, parent, 1)
    local unit cloud = LoadUnitHandle(udg_HASH, parent, 2)
    local real dur = LoadReal(udg_HASH, parent, 3)
    local real range = LoadReal(udg_HASH, parent, 4)
    local group g //added
    set cloudkey = GetHandleId(cloud)
    set udg_LC_Cloud = cloud //should be transfered to a global variable
    if dur > 0 and GetWidgetLife(caster) > 0.405 then 
        call SaveReal(udg_HASH, parent, 3 , dur - LC_TimerSpeed())
        call SetUnitX(cloud, GetUnitX(caster))
        call SetUnitY(cloud, GetUnitY(caster))
        set g = CreateGroup() //added
        call GroupEnumUnitsInRange(g, GetUnitX(cloud), GetUnitY(cloud), range, Filter(function LC_FilterThem)) //changed
        call IssueTargetOrder(cloud, "chainlightning" , FirstOfGroup(g)) //changed //852119
        call DestroyGroup(g) //added
        set g = null //added
    else
        call UnitApplyTimedLife(cloud, 'BTLF', 0.01)
        call LC_RTimer(t)
        call FlushChildHashtable(udg_HASH, parent)
        call FlushChildHashtable(udg_HASH, cloudkey)    
    endif
    set t = null
    set cloud = null
    set caster = null  
endfunction
 
Top