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

[Solved] Need help learning JASS script/writing function related to lightning.

Status
Not open for further replies.
Level 27
Joined
Nov 25, 2021
Messages
481
I've been using GUI for a long time but when I start using custom scripts related to lightning, I notice the ones like AddLightningEx and MoveLightningEx can be quite cumbersome to type out everytime I want to create a lightning effect between two units.

So, I want to use this opportunity to learn about JASS, and to create a JASS function. I have a flowchart but I don't know where to start with the code:
I'll call this function LN2U (lightning between 2 units) for short.

=> call LN2U(Lightning Type, Unit1, Unit2, Duration)
=> Find Location X,Y,Z of Unit1 and Unit2
(Z = Z + Flying Height if Unit1 or Unit2 is Air Unit)
=> Create Lightning Effect of Lightning Type (visibility default to true) at the two units' locations.
=> Each 0.5s tick, loop until end of Duration:
=> Find new location of Unit1 and Unit2
=> Move Lightning to new location
=> Destroy Lightning when duration ends.

I would appreciate it if someone can help me learn how to make it / make it for me so I can dissect the code later.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,546
Note that Lightning is buggy on the more recent patches (I want to say 1.31+).
 
Level 27
Joined
Nov 25, 2021
Messages
481
This is more complex than I thought, so it's going to take a while for me to figure out which one's the best.

As for now, I'm using the third one, and it works well for my basic needs. Thank you for your help, again. You always appear super fast whenever I make a post here.
 
Note that Lightning is buggy on the more recent patches (I want to say 1.31+).
I'm very interested, buggy how?
Buggy as in "Randomly doesn't show"?

---

Anyways, I have a system where few units can chain other units to them, and when they spawn they have 2 units attached to them.
The master and move about as usual, the target gets dragged along and cannot move away by themselves (unless teleported outside break distance).

This is not a "lightning system" it's how I made a system with lightning that connects 2 units (and does few other things).

Note that the lightning effect I use is custom (Spellbound's Custom Lightning Effects), so you'll need to import in order to test it with the current lightning type.

This is mostly for reference. You might find something useful here.
JASS:
scope DiabolicSpecialHoundMaster
    globals
        constant real CHAIN_MAX_DISTANCE = 550.0
        constant real CHAIN_BREAK_DISTANCE = 625.0
        constant group diabolicSpecialHoundMastersLookingToChain = CreateGroup()
    endglobals
    public keyword DiabolicSpecialHoundMaster_LookForChainTargetsFor
    private struct Chain
        private static timer          t         = CreateTimer()
        private static thistype array chains
        static group chainedUnits = CreateGroup()
        private unit master
        private unit target
        private lightning vfx
        public static method IsUnitChained takes unit target returns boolean
            return chains[GetUnitUserData(target)].target == target
        endmethod
        method destroy takes nothing returns nothing
            //clean up everything
            call GroupRemoveUnit(chainedUnits, target)
            set chains[GetUnitUserData(target)] = 0
            set master = null
            set target = null
            call DestroyLightning(vfx)
            set vfx = null
            call deallocate()
        endmethod
        method update takes nothing returns nothing
            local real mx = GetUnitX(master)
            local real my = GetUnitY(master)
            local real mz = BlzGetUnitZ(master)
            local real tx = GetUnitX(target)
            local real ty = GetUnitY(target)
            local real tz = BlzGetUnitZ(target)
            local real dx = tx - mx
            local real dy = ty - my
            local real distance = SquareRoot(dx * dx + dy * dy)
            local real angle
            
            //call BJDebugMsg("Updateing Chain to " + GetUnitName(target))
            if UnitAlive(master) and UnitAlive(target) then
                call MoveLightningEx(vfx, true, mx, my, mz + 60, tx, ty, tz + 60)
                if distance > CHAIN_MAX_DISTANCE then
                    //call BJDebugMsg("OUTSIDE MAX RANGE, target=" + GetUnitName(target))
                    if distance > CHAIN_BREAK_DISTANCE and GetUnitTypeId(target) != HOUND_MASTER_WOLF_UNIT then
                        call DiabolicSpecialHoundMaster_LookForChainTargetsFor.evaluate(master)
                        call destroy()
                    else
                        set angle = Atan2(dy, dx)
                        call SetUnitX(target, mx + CHAIN_MAX_DISTANCE * Cos(angle))
                        call SetUnitY(target, my + CHAIN_MAX_DISTANCE * Sin(angle))
                    endif
                    //set udg_Knockback2DUnit = target
                    //set udg_Knockback2DAngle = bj_RADTODEG * Atan2(dy, dx)
                    //set udg_Knockback2DDistance = (distance - 500)
                    //set udg_Knockback2DTime = 0.10 + udg_Knockback2DDistance / 650.0
                    //set udg_Knockback2DCollision = 8.0
                    //set udg_Knockback2DFX = ""
                    //set udg_Knockback2DUnbiasedCollision = true
                    //call TriggerExecute(gg_trg_Knockback_2D)
                endif
            else
                call destroy()
            endif
        endmethod
        static method updateAll takes nothing returns nothing
            local group tempGroup
            local unit u
            local integer numberOfChains = BlzGroupGetSize(chainedUnits)
            //call BJDebugMsg("Chains Updating: Count=" + I2S(numberOfChains))
            if numberOfChains == 0 then
                //call BJDebugMsg("No chained units still exists, pausing...")
                call PauseTimer(t)
                return
            endif
            set tempGroup = CreateGroup()
            call BlzGroupAddGroupFast(chainedUnits, tempGroup)
            loop
                set u = FirstOfGroup(tempGroup)
                exitwhen u == null
                call chains[GetUnitUserData(u)].update()
                call GroupRemoveUnit(tempGroup, u)
            endloop
            call DestroyGroup(tempGroup)
            set tempGroup = null
        endmethod
        public static method ChainUnit takes unit master, unit target returns nothing
            local thistype this = thistype.allocate()
            set .master = master
            set .target = target
            set .vfx = AddLightningEx("WHCA", true, GetUnitX(master), GetUnitY(master), BlzGetUnitZ(master) + 60, GetUnitX(target), GetUnitY(target), BlzGetUnitZ(target) + 60)
            call SetLightningColor(.vfx, 0.25, 0.25, 0.25, 1.0) //Gray, solid, chain
            if BlzGroupGetSize(chainedUnits) == 0 then
                call TimerStart(t, 1. / 50., true, function thistype.updateAll)
            endif
            call GroupAddUnit(chainedUnits, target)
            set chains[GetUnitUserData(target)] = this
        endmethod
    endstruct
    function DiabolicSpecialChainUnit takes unit master, unit target returns nothing
        //call BJDebugMsg(GetUnitName(master) + " chaining " + GetUnitName(target))
        call Chain.ChainUnit(master, target)
    endfunction
    function DiabolicSpecialsHoundMasterLookForTargets_SpecificUnit takes unit master returns nothing
        local real x = GetUnitX(master)
        local real y = GetUnitY(master)
        local group tempGroup = CreateGroup()
        local unit u
        local player owner = GetOwningPlayer(master)
        call GroupEnumUnitsInRange(tempGroup, x, y, CHAIN_MAX_DISTANCE - 125.0, null)
        loop
            set u = FirstOfGroup(tempGroup)
            exitwhen u == null
            if IsUnitType(u, UNIT_TYPE_HERO) and not IsUnitAlly(u, owner) and not Chain.IsUnitChained(u) then
                call DiabolicSpecialChainUnit(master, u)
                call GroupRemoveUnit(diabolicSpecialHoundMastersLookingToChain, master)
                set u = null
                exitwhen true
            endif
            call GroupRemoveUnit(tempGroup, u)
        endloop
        call DestroyGroup(tempGroup)
        set owner = null
        set tempGroup = null
    endfunction

    function DiabolicSpecialsHoundMasterLookForTargets takes nothing returns nothing
        local group tempGroup
        local unit u
        if BlzGroupGetSize(diabolicSpecialHoundMastersLookingToChain) == 0 then
            call ReleaseTimer(GetExpiredTimer())
        else
            set tempGroup = CreateGroup()
            call BlzGroupAddGroupFast(diabolicSpecialHoundMastersLookingToChain, tempGroup)
            loop
                set u = FirstOfGroup(tempGroup)
                exitwhen u == null
                if UnitAlive(u) then
                    call DiabolicSpecialsHoundMasterLookForTargets_SpecificUnit(u)
                else
                    call GroupRemoveUnit(diabolicSpecialHoundMastersLookingToChain, u)
                endif
                call GroupRemoveUnit(tempGroup, u)
            endloop
            call DestroyGroup(tempGroup)
            set tempGroup = null
        endif
    endfunction

    public function DiabolicSpecialHoundMaster_LookForChainTargetsFor takes unit master returns nothing
        if BlzGroupGetSize(diabolicSpecialHoundMastersLookingToChain) == 0 then
            call TimerStart(NewTimer(), 0.67, true, function DiabolicSpecialsHoundMasterLookForTargets)
        endif
        call GroupAddUnit(diabolicSpecialHoundMastersLookingToChain, master)
    endfunction

    function DiabolicSpecialHoundMaster_CreateHound takes unit master returns nothing
        local unit hound = CreateUnit(GetOwningPlayer(master), HOUND_MASTER_WOLF_UNIT, GetUnitX(master) + GetRandomReal(-100, 100), GetUnitY(master) + GetRandomReal(-100, 100), 0)
        //Math: https://www.wolframalpha.com/input/?i=x+%3D+1%2C10%2C20%2C30%2C40+for+%28120+%2B+7.5+*+%281+%2B+x+*+0.04%29+*+x%29
        call BlzSetUnitMaxHP(hound, R2I(120 + 7.5 * (1 + udg_current_wave_nr * 0.04) * udg_current_wave_nr))
        call BlzSetUnitArmor(hound, 25 + 1.75 * udg_current_wave_nr)
        call BlzSetUnitBaseDamage(hound, R2I(14 + 1.75 * (1 + udg_current_wave_nr * 0.04) * udg_current_wave_nr), 0)
        call SetUnitState(hound, UNIT_STATE_LIFE, MaxHp(hound))
        call DiabolicSpecialChainUnit(master, hound)
        call IssuePointOrderLoc( hound, "attack", udg_center_player_spawn_point )
        set hound = null
    endfunction
endscope
 
I believe you can't Move lightning in 1.31+ or maybe only 1.32+. The solution is to Destroy the Lightning and then Recreate it at it's new position to simulate movement.
When playing single player, it seems to work (at least most of the time). And sometimes in multiplayer too (about 40% of the time).
My problem is that "sometimes" makes it pretty unusable.
When it works, it works for the whole game, when it doesn't, it doesn't for the whole game.

Thanks for the info, will try if creating/destroying all the time works better or not :)
 
Status
Not open for further replies.
Top