//* //* DummyUnitStack is a library designed to give spell developers immediate //* access to any number of dummy units at any time by hacking around the //* engine limitations associated with CreateUnit() - essentially, all we do //* is create units in a deferred manner which not only caches dummies, but //* also prevents simultaneous unit instantiations on a more global scale. //* In other words, we create a few units, very often, until we have a lot. //* //* The public API for DummyUnitStack is simple - just get and release: //* unit u = DummyUnitStack.get() //* DummyUnitStack.release(u) //* //* The more complex part is the preloading options available. For most cases //* the default options will more than suffice, but if you really want to get //* aggressive with your optimizations, you can do some maths yourself and //* decide on every limit used in the system. //* //* Enjoy, //* -Cokemonkey11 //* library DummyUnitStack initializer ini struct UnitAlloc unit u end // Just a shim for the API, the struct is not actually instanciated. public struct DummyUnitStack //********************************************************************* //* CUSTOMIZABLE SECTION //********************************************************************* // This bit is important - make sure this refers to the dummy unit // in the test map private static constant integer DUMMY_UNIT_ID = 'u000' // Whenever the preloading process turns on, we a minimum number // of units to load, to prevent turning the system on and off too // often - here you can choose the minimum count (default 10) private static constant integer MIN_PRELOAD_BLOCK_SIZE = 10 // This value is the initialization preload value. Higher numbers // will increase map load time, but give the best performance. The // ideal value should be something between the number of dummies used // in the first few minutes, and the maximum number of dummies used // in theory. Additionally, you can turn the feature off entirely. // (default 200) static constant integer PRELOAD_INIT_COUNT = 200 static constant boolean DO_PIC = true // When a series of dummies are retrived using .get(), the stack size // falls and approaches 0 - with this setting, we can turn on the // preloader whenever the stack size falls to an arbitrary number. // Again, this feature can be turned off. (default 20) private static constant integer DYNAMIC_DEFERRED_PRELOAD_COUNT = 10 private static constant boolean DO_DDPC = true // When the map starts, the preloader turns on right away, preloading // more units - this can be tweaked to reduce loading time without // losing dummies available at the beginning of the game. (default 20) static constant integer INITIAL_DEFERRED_PRELOAD_COUNT = 20 static constant boolean DO_IDPC = false // If a dummy unit is released and the stack exceeds this size, the unit // will instead be removed. private static constant integer MAX_PRELOADED_UNITS = 5000 private static constant boolean DO_MPU = true // Here we have another "hack-like" design choice. The developer must // specify an (x,y) coordinate pair where dummy units can be moved on // release - we do this because there is a bug associaed with ShowUnit() // and the locust ability. :( private static constant real SAFE_LOC_X = -5650. private static constant real SAFE_LOC_Y = 5565. // Normally a not-so-useful tweaking variable, clock period in dummy unit // stack is actually quite powerful because we're not relying on any // kind of smooth movement for releasing/getting. (default 1./60.) static constant real CLOCK_PERIOD = 1./60. //********************************************************************* //* END CUSTOMIZABLE SECTION //********************************************************************* private static unit array dummyStack private static integer stackIndex = -1 static integer deferredTodoCount = 0 static timer clock = CreateTimer() //* Private method used for creating a unit by the system static method add takes boolean preloading returns unit bj_lastCreatedUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_UNIT_ID, SAFE_LOC_X, SAFE_LOC_Y, 0.) UnitAddAbility(bj_lastCreatedUnit, 'Aloc') UnitAddAbility(bj_lastCreatedUnit, 'Arav') if preloading then stackIndex++ dummyStack[stackIndex] = bj_lastCreatedUnit else return bj_lastCreatedUnit end return null end //* Private periodic method used for preloading static method deferredPreloader takes nothing returns nothing deferredTodoCount-- add(true) if deferredTodoCount < 1 then PauseTimer(clock) end end //* Public method used to get a dummy. Use this at any time, as much //* as you want. static method get takes nothing returns unit unit result if stackIndex != -1 then stackIndex-- result = dummyStack[stackIndex+1] else result = add(false) end if DO_DDPC then if stackIndex < (DYNAMIC_DEFERRED_PRELOAD_COUNT - 1) and deferredTodoCount<1 then deferredTodoCount = MIN_PRELOAD_BLOCK_SIZE TimerStart(clock, CLOCK_PERIOD, true, function deferredPreloader) end end return result end //* Public method for releasing a dummy. Make sure you don't release //* units that don't come from DummyUnitStack. Make sure you destroy //* effects and anything else attached the unit because its handle //* will be RECYCLED. static method release takes unit u returns nothing if DO_MPU then if stackIndex >= MAX_PRELOADED_UNITS then RemoveUnit(u) return end end SetUnitFlyHeight(u, 0., 0.) SetUnitScale(u, 1., 1., 1.) SetUnitOwner(u, Player(PLAYER_NEUTRAL_PASSIVE), true) SetUnitX(u, SAFE_LOC_X) SetUnitY(u, SAFE_LOC_Y) stackIndex++ dummyStack[stackIndex] = u end private static method afterDelay takes nothing returns nothing timer tim = GetExpiredTimer() UnitAlloc u = tim.getData() castTo UnitAlloc release(u.u) destroy u DestroyTimer(tim) end static method delayedRelease takes unit u, real delay returns nothing UnitAlloc ua = new UnitAlloc timer tim = CreateTimer() ua.u = u tim.setData(ua castTo integer) TimerStart(tim, delay, false, function afterDelay) end end function ini takes nothing returns nothing integer index = 1 if DummyUnitStack.DO_PIC then loop exitwhen index > DummyUnitStack.PRELOAD_INIT_COUNT DummyUnitStack.add(true) index++ end end if DummyUnitStack.DO_IDPC then DummyUnitStack.deferredTodoCount = DummyUnitStack.INITIAL_DEFERRED_PRELOAD_COUNT TimerStart(DummyUnitStack.clock, DummyUnitStack.CLOCK_PERIOD, true, function DummyUnitStack.deferredPreloader) end end endlibrary