9 years
edited 8 years
A useful library which allows you to recycle units (even dead ones, but they must leave a corpse), avoiding yet another permanent 0.04kb memory leak for each future CreateUnit() call.
Script
Script
1 2 library UnitRecycler /* v1.3b
3
4
5 |=============|6 | Author: AGD |7 |=============|8
9 */requires /*
10
11 */ReviveUnit /* https://wc3modding.info/4469/snippet-reviveunit/
12 */UnitDex /* http://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
13 */optional Table /* https://wc3modding.info/4611/snippet-new-table/
14 */optional TimerUtils /* https://wc3modding.info/5352/hibrid-timerutils/
15 */optional RegisterPlayerUnitEvent /* https://wc3modding.info/4907/snippet-registerplayerunitevent/
16
17 This system is important because CreateUnit() is one of the most processor-intensive function in18 the game and there are reports that even after they are removed, they still leave some bit of memory19 consumption (0.04 KB) on the RAM. Therefore it would be very helpful if you can minimize unit20 creation or so. This system also allows you to recycle dead units to avoid permanent 0.04 KB memory21 leak for each future CreateUnit() call. */22 23 //! novjass24 25 [Credits]
26 Aniki - For suggesting ideas on further improvements27 28 29 |-----|
30 | API |
31 |-----|
32 33 function GetRecycledUnit takes player owner, integer rawCode, real x, real y, real facing returns unit/*
34 - Returns unit of specified ID from the stock of recycled units. If there's none in the stock that35 matched the specified unit's rawcode, it will create a new unit instead36 - Returns null if the rawcode's unit-type is a hero or non-existent37
38 */function GetRecycledUnitEx takes player owner, integer rawCode, real x, real y, real facing returns unit/*
39 - Works similar to GetRecycledUnit() except that if the input rawcode's unit-type is a hero, it will40 be created via CreateUnit() instead41 - You can use this as an alternative to CreateUnit()42
43 */function RecycleUnit takes unit u returns boolean/*
44 - Recycles the specified unit and returns a boolean value depending on the success of the operation45 - Does nothing to hero units46
47 */function RecycleUnitEx takes unit u returns boolean/*
48 - Works similar to RecycleUnit() except that if <u> is not recyclable, it will be removed via49 RemoveUnit() instead50 - You can use this as an alternative to RemoveUnit()51
52 */function RecycleUnitDelayed takes unit u, real delay returns nothing/*
53 - Recycles the specified unit after <delay> seconds54
55 */function RecycleUnitDelayedEx takes unit u, real delay returns nothing/*
56 - Works similar to RecycleUnitDelayed() except that it calls RecycleUnitEx() instead of RecycleUnit()57
58 */function UnitAddToStock takes integer rawCode returns boolean/*
59 - Creates a unit of type ID and adds it to the stock of recycled units then returns a boolean value60 depending on the success of the operation61
62 *///! endnovjass
63 64 //CONFIGURATION SECTION65 66 67 globals68 69 /* The owner of the stocked/recycled units70 */ private constant player OWNER = Player(15)
71 72 /* Determines if dead units will be automatically recycled73 after a delay designated by the <constant function74 DeathTime below>75 */ private constant boolean AUTO_RECYCLE_DEAD = true
76 77 /* Error debug message prefix78 */ private constant string ERROR_PREFIX = "|CFFFF0000Operation Failed: "
79 80 endglobals81 82 /* The delay before dead units will be recycled in case AUTO_RECYCLE_DEAD == true */83 static if AUTO_RECYCLE_DEAD then
84 private constant function DeathTime takes unit u returns real
85 /*if <condition> then86 return someValue87 elseif <condition> then88 return someValue89 endif */90 return 8.00
91 endfunction92 endif93 94 /* When recycling a unit back to the stock, these resets will be applied to the95 unit. You can add more actions to this or you can delete this textmacro if you96 don't need it. */97 //! textmacro_once UNIT_RECYCLER_RESET98 call SetUnitScale(u, 1, 0, 0)
99 call SetUnitVertexColor(u, 255, 255, 255, 255)
100 call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), 0)
101 //! endtextmacro102 103 104 //END OF CONFIGURATION105 106 /*==== Do not do changes below this line if you're not so sure on what you're doing ====*/107 native UnitAlive takes unit u returns boolean
108 109 globals110 private keyword S
111 private integer count = 0
112 private real unitCampX
113 private real unitCampY
114 private integer array stack
115 private boolean array stacked
116 endglobals117 118 private function GetIndex takes integer rawCode returns integer
119 static if LIBRARY_Table then
120 local integer i = S.table.integer[rawCode]
121 if i == 0 then
122 set count = count + 1
123 set S.table.integer[rawCode] = count
124 set i = count
125 endif126 else127 local integer i = LoadInteger(S.hash, -1, rawCode)
128 if i == 0 then
129 set count = count + 1
130 call SaveInteger(S.hash, -1, rawCode, count)
131 set i = count
132 endif133 endif134 return i135 endfunction136 137 static if DEBUG_MODE then
138 private function Debug takes string msg returns nothing
139 call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|CFFFFCC00[Unit Recycler]|R " + msg)
140 endfunction141 endif142 143 function GetRecycledUnit takes player owner, integer rawCode, real x, real y, real facing returns unit
144 local integer i
145 if not IsHeroUnitId(rawCode) then
146 set i = GetIndex(rawCode)
147 if stack[i] == 0 then
148 set bj_lastCreatedUnit = CreateUnit(owner, rawCode, x, y, facing)
149 debug call Debug(GetUnitName(bj_lastCreatedUnit) + " stock is empty, creating new " + GetUnitName(bj_lastCreatedUnit))
150 else151 static if LIBRARY_Table then
152 set bj_lastCreatedUnit = S.hash[i].unit[stack[i]]
153 else154 set bj_lastCreatedUnit = LoadUnitHandle(S.hash, i, stack[i])
155 endif156 set stacked[GetUnitId(bj_lastCreatedUnit)] = false
157 call PauseUnit(bj_lastCreatedUnit, false)
158 call SetUnitOwner(bj_lastCreatedUnit, owner, true)
159 call SetUnitPosition(bj_lastCreatedUnit, x, y)
160 call SetUnitFacing(bj_lastCreatedUnit, facing)
161 set stack[i] = stack[i] - 1
162 debug call Debug("Retrieving " + GetUnitName(bj_lastCreatedUnit) + " from stock")
163 endif164 debug if bj_lastCreatedUnit == null then
165 debug call Debug(ERROR_PREFIX + "Specified unit-type does not exist")
166 debug endif
167 else168 debug call Debug(ERROR_PREFIX + "Attemp to retrieve a hero unit")
169 return null
170 endif171 return bj_lastCreatedUnit
172 endfunction173 174 function GetRecycledUnitEx takes player owner, integer rawCode, real x, real y, real facing returns unit
175 if not IsHeroUnitId(rawCode) then
176 return GetRecycledUnit(owner, rawCode, x, y, facing)
177 endif178 debug call Debug("Cannot retrieve a hero unit, creating new unit")
179 return CreateUnit(owner, rawCode, x, y, facing)
180 endfunction181 182 function RecycleUnit takes unit u returns boolean
183 local integer rawCode = GetUnitTypeId(u)
184 local integer uDex = GetUnitId(u)
185 local integer i
186 if not IsHeroUnitId(rawCode) and not stacked[uDex] and u != null then
187 set i = GetIndex(rawCode)
188 if not UnitAlive(u) and not ReviveUnit(u) then
189 debug call Debug(ERROR_PREFIX + "Unable to recycle unit: Unable to revive dead unit")
190 return false
191 endif192 set stacked[uDex] = true
193 call PauseUnit(u, true)
194 call SetUnitOwner(u, OWNER, true)
195 call SetUnitX(u, unitCampX)
196 call SetUnitY(u, unitCampY)
197 call SetUnitFacing(u, 270)
198 call SetWidgetLife(u, GetUnitState(u, UNIT_STATE_MAX_LIFE))
199 call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MAX_MANA))
200 //! runtextmacro optional UNIT_RECYCLER_RESET()201 set stack[i] = stack[i] + 1
202 static if LIBRARY_Table then
203 set S.hash[i].unit[stack[i]] = u
204 else205 call SaveUnitHandle(S.hash, i, stack[i], u)
206 endif207 debug call Debug("Successfully recycled " + GetUnitName(u))
208 return true
209 debug else
210 debug if stacked[uDex] then
211 debug call Debug(ERROR_PREFIX + "Attempt to recycle an already recycled unit")
212 debug elseif u == null then
213 debug call Debug(ERROR_PREFIX + "Attempt to recycle a null unit")
214 debug else
215 debug call Debug(ERROR_PREFIX + "Attempt to recycle a hero unit")
216 debug endif
217 endif218 return false
219 endfunction220 221 function RecycleUnitEx takes unit u returns boolean
222 if not RecycleUnit(u) then
223 call RemoveUnit(u)
224 debug call Debug("Cannot recycle the specified unit, removing unit")
225 return false
226 endif227 return true
228 endfunction229 230 //! textmacro DELAYED_RECYCLE_TYPE takes EX231 private function RecycleTimer$EX$ takes nothing returns nothing
232 local timer t = GetExpiredTimer()
233 static if LIBRARY_TimerUtils then
234 call RecycleUnit$EX$(GetUnitById(GetTimerData(t)))
235 call ReleaseTimer(t)
236 else237 local integer key = GetHandleId(t)
238 static if LIBRARY_Table then
239 call RecycleUnit$EX$(S.hash[0].unit[key])
240 call S.hash[0].remove(key)
241 else242 call RecycleUnit$EX$(LoadUnitHandle(S.hash, 0, key))
243 call RemoveSavedHandle(S.hash, 0, key)
244 endif245 call DestroyTimer(t)
246 endif247 set t = null
248 endfunction249 250 function RecycleUnitDelayed$EX$ takes unit u, real delay returns nothing
251 static if LIBRARY_TimerUtils then
252 call TimerStart(NewTimerEx(GetUnitId(u)), delay, false, function RecycleTimer$EX$)
253 else254 local timer t = CreateTimer()
255 static if LIBRARY_Table then
256 set S.hash[0].unit[GetHandleId(t)] = u
257 else258 call SaveUnitHandle(S.hash, 0, GetHandleId(t), u)
259 endif260 call TimerStart(t, delay, false, function RecycleTimer$EX$)
261 set t = null
262 endif263 endfunction264 //! endtextmacro265 266 //! runtextmacro DELAYED_RECYCLE_TYPE("")267 //! runtextmacro DELAYED_RECYCLE_TYPE("Ex")268 269 function UnitAddToStock takes integer rawCode returns boolean
270 local unit u
271 local integer i
272 if not IsHeroUnitId(rawCode) then
273 set u = CreateUnit(OWNER, rawCode, unitCampX, unitCampY, 270)
274 if u != null then
275 set i = GetIndex(rawCode)
276 call SetUnitX(u, unitCampX)
277 call SetUnitY(u, unitCampY)
278 call PauseUnit(u, true)
279 set stacked[GetUnitId(u)] = true
280 set stack[i] = stack[i] + 1
281 static if LIBRARY_Table then
282 set S.hash[i].unit[stack[i]] = u
283 else284 call SaveUnitHandle(S.hash, i, stack[i], u)
285 endif286 debug call Debug("Adding " + GetUnitName(u) + " to stock")
287 return true
288 debug else
289 debug call Debug(ERROR_PREFIX + "Attemp to stock a null unit")
290 endif291 set u = null
292 debug else
293 debug call Debug(ERROR_PREFIX + "Attemp to stock a hero unit")
294 endif295 return false
296 endfunction297 298 static if AUTO_RECYCLE_DEAD then
299 private function OnDeath takes nothing returns nothing
300 local unit u = GetTriggerUnit()
301 if not IsUnitType(u, UNIT_TYPE_HERO) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
302 call RecycleUnitDelayed(u, DeathTime(u))
303 endif304 set u = null
305 endfunction306 endif307 308 private module Init
309 310 static if LIBRARY_Table then
311 static TableArray hash312 static Table table313 else314 static hashtable hash = InitHashtable()
315 endif316 317 private static method onInit takes nothing returns nothing
318 local rect bounds = GetWorldBounds()
319 static if AUTO_RECYCLE_DEAD then
320 static if LIBRARY_RegisterPlayerUnitEvent then
321 static if RPUE_VERSION_NEW then
322 call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
323 else324 call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
325 endif326 else327 local trigger t = CreateTrigger()
328 local code c = function OnDeath
329 local integer i = 16
330 loop331 set i = i - 1
332 call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
333 exitwhen i == 0
334 endloop335 call TriggerAddCondition(t, Filter(c))
336 endif337 endif338 static if LIBRARY_Table then
339 set hash = TableArray[0x2000]
340 set table = Table.create()
341 endif342 // Hides recycled units at the top of the map beyond reach of the camera343 set unitCampX = 0.00
344 set unitCampY = GetRectMaxY(bounds) + 1000.00
345 call RemoveRect(bounds)
346 set bounds = null
347 endmethod348 349 endmodule350 351 private struct S extends array
352 implement Init353 endstruct354 355 356 endlibrary
