HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

The new lightning script feedback thread

06-16-2009, 09:41 AM#1
0zyx0
After trying to optimize my old script, I decided to do something more similar to moyack's original script. This version does not support fading yet. It does however support connecting lightning effects to units, and if more than one lightning effect are connected to the same unit, GetUnitX/Y/Z() will only be called once anyway, so this script is good for doing that. It uses an internal struct called LightningLoc, which has an x, y, and z member. These values will be updated periodically, if the LightningLoc is attached to a unit. Since this update have to happen before the update of the actual lightnings, I couldn't use TimedLoop as I originally had planned. As a side effect of that, the update period can be specified.

One of the cons are the fact that you have to call the public function UnitLeave, when a unit which has a lightning connected to it, dies or leaves the map. This will be done automatically through function hooks, when they are added.

For a usage example, this is what I used to test the script:
Collapse JASS:
private function Actions takes nothing returns nothing
 local lightning l = AddLightning("CLPB", false, 0., 0., 0., 0.)
    call Lightning.create(l, 3., Lightning.atUnit(gg_unit_o000_0163), Lightning.atUnit(gg_unit_o000_0162))
    set l = AddLightning("DRAL", false, 0., 0., 0., 0.)
    call Lightning.create(l, 5., Lightning.atUnit(gg_unit_o000_0163), Lightning.atCoords(0., 0., 256.))
 set l = null
endfunction

private function Actions2 takes nothing returns nothing
    call Lightning.create(AddLightning("CLPB", false, 0., 0., 0., 0.), 2., Lightning.atUnit(Heroes[1].hero), Lightning.atCoords(0., 0., 256.))
endfunction
As you can see, a lightning effect is passed to .create(). Lightning.atUnit() and Lightning.atCoords() returns a LightningLoc, mentioned above. The number passed to .create() after the lightning is the duration of the lightning. And finally, here is the script itself:
Expand JASS:
I have not decided a library name yet, and the library requires some form of unit indexing, which use the function name GetUnitId, so both UnitIndexingUtils and AutoIndex will work.
06-16-2009, 09:50 AM#2
Flame_Phoenix
Mmm according to some people AutoIndex uses UnitData... is that happens then it probably means I won't be able to use it with UnitIdexing by Dusk if I am not mistaken ...
You know, you could just use Table xD
You may also want to check TimedHandles, since it also has a similar functionality to lightnings, although I think it is more limited.
06-16-2009, 10:47 AM#3
Fledermaus
Quote:
Originally Posted by Flame_Phoenix
Mmm according to some people AutoIndex uses UnitData... is that happens then it probably means I won't be able to use it with UnitIdexing by Dusk if I am not mistaken ...
You know, you could just use Table xD
How do you not understand that the Unit Indexing System doesn't matter?
Seriously, if something is made using AutoIndex, you can use UnitIndexingUtils or PUI (if you've modded it to use GetUnitId rather than GetUnitIndex) instead.
They all use UserData so there is no problem which one you use.

EDIT: at least you're using one now :p
06-16-2009, 11:26 AM#4
Anitarf
Besides, there's an alternative version of AutoIndex that uses Table instead of unit user data. Next time, read the resource thread instead of saying bullshit like:
Quote:
Originally Posted by Flame_Phoenix
according to some people AutoIndex uses UnitData
I mean, seriously, "according to some people"? What kind of statement is that?
06-16-2009, 11:34 AM#5
Flame_Phoenix
Quote:
I mean, seriously, "according to some people"? What kind of statement is that?
Not bullshit, in fact it this AutoIndex thing was written in another system submitted to resources section... the system in cause got deleted if I am not mistaken, don't as me why... ( I think that's because it was not much good ) ....
06-16-2009, 01:05 PM#6
Fledermaus
Quote:
Originally Posted by Flame_Phoenix
Not bullshit, in fact it this AutoIndex thing was written in another system submitted to resources section... the system in cause got deleted if I am not mistaken, don't as me why... ( I think that's because it was not much good ) ....
...I can't understand this at all =/
06-16-2009, 01:50 PM#7
Flame_Phoenix
Quote:
...I can't understand this at all =/
+1 ...
06-16-2009, 05:24 PM#8
akolyt0r
AutoIndex has a table implementation aswell.
06-21-2009, 06:51 PM#9
0zyx0
I made an updated version of the script, this one requires ListModule by grim001, I did it to make destruction of internal objects easier, this makes the code more readable, and there is also a small speed gain, creation of lightnings is possibly slightly slower however. The destruction could use some improvements, please give me suggestions on how to do it.

Collapse JASS:
globals
    public constant real PERIOD = 0.050     // The rate at which the lightnings updates.
    private constant integer MAX_UNIT_INDEX = 8190
    // The max number of unit that can be indexed at the same time.
    // Lower values could reduce memory usage by some fraction of a kilobyte.

// ===================== End of Configuration Zone =====================

    private location loc = Location(0., 0.) // For getting location height
    private timer T = CreateTimer()         // The timer doing all the work
    private code TimerExpire                // This has to be here, else .evaluate() would have to be here.
    private integer TimerUsageCount = 0     // If this reaches 0, the timer will pause.
    
    private keyword LightningLoc
    
    private LightningLoc array UnitLightningLoc // Stores all LightningLocs attached to units
    private boolean array HasLightningLoc       // True if the unit with that id has a lighting connected to it.
endglobals
private function GetUnitZ takes unit u returns real  // Returns the Z coordinate of a unit
    call MoveLocation(loc, GetUnitX(u), GetUnitY(u)) // Move the location to where the height should be read
    return GetLocationZ(loc) + GetUnitFlyHeight(u)   // Fly height + Terrain height to get the unit's actual height
endfunction

// After this code is executed, the instance will be updated every time the timer expires.
//! textmacro LightningUtils__AddToTimer
    call this.addList()
    set TimerUsageCount = TimerUsageCount + 1
    if TimerUsageCount == 1 then
        call TimerStart(T, PERIOD, true, TimerExpire)
    endif
//! endtextmacro

// These textmacros appear in periodically called functions.
//! textmacro LightningUtils__Updater
 local thistype this = thistype.getFirst()
 local boolean toDestroy = false
    loop
//! endtextmacro
//! textmacro LightningUtils__EndUpdater
        if toDestroy then // Destruction is ugly this way, could anybody please suggest something better?
            if this == thistype.getLast() then
                call this.destroy()
                exitwhen true
            endif
            set this = this.getNext()
            call this.getPrev().destroy()
            set toDestroy = false
        else
            exitwhen this == thistype.getLast()
            set this = this.getNext()
        endif
    endloop
//! endtextmacro

//! textmacro LightningUtils__RemoveFromQueue
    if this.removeList() then
        set TimerUsageCount = TimerUsageCount - 1
    endif
//! endtextmacro

// =======================================================
// Code to decide where to move lightnings:
private struct LightningLoc
    implement List
    
    integer refCount = 0 // DO NOT TOUCH THIS!
    // This counts how many Lightning instances uses this instance,
    // so it will be destroyed when, and only when, it is not needed any more.
    
    private unit u = null
    
    real x = 0.
    real y = 0.
    real z = 0.
    
    method onDestroy takes nothing returns nothing
        if .u != null then
            set HasLightningLoc[GetUnitId(.u)] = false
            set .u = null
        endif
        //! runtextmacro LightningUtils__RemoveFromQueue()
    endmethod
    
    static method fromCoords takes real x, real y, real z returns LightningLoc
     local LightningLoc this = LightningLoc.allocate()
        set this.x = x
        set this.y = y
        set this.z = z
     return this
    endmethod
    
    static method fromUnit takes unit u returns LightningLoc
     local integer id = GetUnitId(u)
     local LightningLoc this
        if u == null then // There can't be any lightning attached to null.
            return LightningLoc.fromCoords(0., 0., 0.)
        endif
        if HasLightningLoc[id] then     // If the unit already has a LightningLoc, return it.
            return UnitLightningLoc[id]
        else
            set this = LightningLoc.allocate() // Else, create a new one.
            set UnitLightningLoc[id] = this
            set HasLightningLoc[id] = true
            set this.u = u
            //! runtextmacro LightningUtils__AddToTimer()
            return this
        endif
        return 0
    endmethod
    
    method release takes nothing returns nothing
        if .u != null then
            set HasLightningLoc[GetUnitId(.u)] = false
            set .u = null
            //! runtextmacro LightningUtils__RemoveFromQueue()
        endif
    endmethod
    
    static method update takes nothing returns nothing
        //! runtextmacro LightningUtils__Updater()
        if this.refCount == 0 then
            set toDestroy = true
        else
            set this.x = GetUnitX(this.u)
            set this.y = GetUnitY(this.u)
            set this.z = GetUnitZ(this.u)
        endif
        //! runtextmacro LightningUtils__EndUpdater()
    endmethod
endstruct
// =======================================================
// The struct that does the stuff with the lightnings.

struct Lightning
    implement List
    
    private lightning l         // The lightning itself.
    private integer iterations  // How many PERIOD the lightning have to live through.
    private integer i = 0       // How many PERIOD the lightning has lived through.
    private LightningLoc start  // Where the lighting should start...
    private LightningLoc end    // ... and where it should end.
    
    static method create takes lightning l, real time, LightningLoc start, LightningLoc end returns Lightning
     local Lightning this = Lightning.allocate()
        if l == null then   // Return the error code -1 if the lightning wasn't created properly.
            return -1
        endif
        set this.l = l
        set this.start = start
        set start.refCount = start.refCount + 1
        set this.end = end
        set end.refCount = end.refCount + 1
        set this.iterations = R2I(time/PERIOD) // Calculates how many PERIOD the lightning should stay.
        //! runtextmacro LightningUtils__AddToTimer()
     return this
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyLightning(.l)
        set .start.refCount = .start.refCount - 1
        set .end.refCount = .end.refCount - 1
        //! runtextmacro LightningUtils__RemoveFromQueue()
        if .start.refCount == 0 then // If the reference count is 0...
            call .start.destroy()    // ...destroy the LightiningLoc.
        endif
        if .end.refCount == 0 then
            call .end.destroy()
        endif
    endmethod
    
    static method update takes nothing returns nothing
        //! runtextmacro LightningUtils__Updater()
        if this.i == this.iterations then
            set toDestroy = true
        else
            set this.i = this.i + 1
            call MoveLightningEx(this.l, false, this.start.x, this.start.y, this.start.z, this.end.x, this.end.y, this.end.z)
        endif
        //! runtextmacro LightningUtils__EndUpdater()
    endmethod
    
    static method atUnit takes unit u returns LightningLoc // Grants acsess to the LightningLoc struct.
        return LightningLoc.fromUnit(u) // Inline friendly
    endmethod
    
    static method atCoords takes real x, real y, real z returns LightningLoc
        return LightningLoc.fromCoords(x, y, z) // Inline friendly
    endmethod
endstruct

public function UnitLeave takes unit u returns nothing // This will probably be replaced in the future.
    call UnitLightningLoc[GetUnitId(u)].release()
endfunction

private function OnTimerExpire takes nothing returns nothing
    if TimerUsageCount == 0 then // If there is nothing to update...
        call PauseTimer(T)       // ...pause the timer.
        return
    endif
    call LightningLoc.update() // First the locations of the lightnings have to be updated
    call Lightning.update()    // And then the lightnings themselves have to be updated
endfunction

private function init takes nothing returns nothing
 local integer i = -1
    loop
        set i = i + 1
        set HasLightningLoc[i] = false
        exitwhen i == MAX_UNIT_INDEX
    endloop
    set TimerExpire = function OnTimerExpire
endfunction