| 06-16-2009, 09:41 AM | #1 |
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: 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 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 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 set TimerUsageCount = TimerUsageCount + 1 set thistype.UpdateQueue[thistype.QueueCount] = this set thistype.QueueCount = thistype.QueueCount + 1 if TimerUsageCount == 1 then call TimerStart(T, PERIOD, true, TimerExpire) endif //! endtextmacro // This code appears in functions called by the timer's expiration. //! textmacro LightningUtils__Updater local integer i = 0 local thistype current loop exitwhen i == thistype.QueueCount set current = thistype.UpdateQueue[i] //! endtextmacro // If condition is met, destroy the instance and remove it from the updates queue. // The actual destruction always happen here, to avoid O(n) searches when removing it // from the update queue. //! textmacro LightningUtils__DestroyIfNeeded takes condition, destruction if $condition$ then $destruction$ call current.destroy() set thistype.QueueCount = thistype.QueueCount - 1 set thistype.UpdateQueue[i] = thistype.UpdateQueue[thistype.QueueCount] set TimerUsageCount = TimerUsageCount - 1 else //! endtextmacro //! textmacro LightningUtils__RemoveFromQueue set thistype.QueueCount = thistype.QueueCount - 1 set thistype.UpdateQueue[i] = thistype.UpdateQueue[thistype.QueueCount] set TimerUsageCount = TimerUsageCount - 1 //! endtextmacro // Just something to make the code in the update methods look cooler. //! textmacro LightningUtils__EndUpdater set i = i + 1 endif endloop //! endtextmacro // ======================================================= private struct LightningLoc private static LightningLoc array UpdateQueue private static integer QueueCount = 0 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. boolean removeFromQueue = false private unit u = null real x = 0. real y = 0. real z = 0. 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 // If u isn't null, the instance will be destroyed at the next update. call .destroy() endif endmethod static method update takes nothing returns nothing //! runtextmacro LightningUtils__Updater() //! runtextmacro LightningUtils__DestroyIfNeeded("current.refCount == 0", "set HasLightningLoc[GetUnitId(current.u)] = false") if current.removeFromQueue then set current.u = null //! runtextmacro LightningUtils__RemoveFromQueue() endif set current.x = GetUnitX(current.u) set current.y = GetUnitY(current.u) set current.z = GetUnitZ(current.u) //! runtextmacro LightningUtils__EndUpdater() endmethod endstruct // ======================================================= struct Lightning private static Lightning array UpdateQueue private static integer QueueCount = 0 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 if .start.refCount == 0 then // If the reference count is 0... call .start.release() // ...tell the LightiningLoc to destroy itself. endif if .end.refCount == 0 then call .end.release() endif endmethod static method update takes nothing returns nothing //! runtextmacro LightningUtils__Updater() //! runtextmacro LightningUtils__DestroyIfNeeded("current.i == current.iterations", "") set current.i = current.i + 1 call MoveLightningEx(current.l, false, current.start.x, current.start.y, current.start.z, current.end.x, current.end.y, current.end.z) //! runtextmacro LightningUtils__EndUpdater() endmethod static method atUnit takes unit u returns LightningLoc 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 set UnitLightningLoc[GetUnitId(u)].removeFromQueue = true endfunction private function OnTimerExpire takes nothing returns nothing debug if TimerUsageCount < 0 then debug call BJDebugMsg(SCOPE_PREFIX + "Error: TimerUsageCount was less than 0 for some reason.") debug return debug endif if TimerUsageCount == 0 then // If there is nothing to update... call PauseTimer(T) // ...pause the timer. return endif call LightningLoc.update() call Lightning.update() 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 |
| 06-16-2009, 09:50 AM | #2 |
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 | |
Quote:
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 | |
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:
|
| 06-16-2009, 11:34 AM | #5 | |
Quote:
|
| 06-16-2009, 01:05 PM | #6 | |
Quote:
|
| 06-16-2009, 01:50 PM | #7 | |
Quote:
|
| 06-16-2009, 05:24 PM | #8 |
AutoIndex has a table implementation aswell. |
| 06-21-2009, 06:51 PM | #9 |
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. 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 |
