| 03-31-2008, 09:41 PM | #1 |
I came up with a neat concept and I wanted to see if there was demand for a cleaned-up system. I also have a question (at the end of the post). Essentially I can take any single unit event and turn it into an any unit event. Ok, that's actually really easy, but: - only uses 3 triggers - only does O(1) work per added unit - does no work at all when a unit is killed - doesn't leak The basic idea is that units are rotated across the three triggers as more are added. The most obvious application is a damage system, but I actually wrote the system so I could avoid unnecessary work when towers attack in a TD. The main issue I have is storing the functions which need to be called when events fire. I can't store them in the three triggers because those are destroyed now and then. I can't storm them in an array because you can't have code arrays. That leaves storing the calls in another trigger, which is called when the originals fire. But then I need to know what trigger to activate given the triggering trigger. Right now I'm using HAIL to attach the value, but I was wondering if there was a faster way. Damage events obviously get a ton of calls and I want the best performance I can get. JASS:library SpreadUnitEventLib requires HAIL globals private constant boolean SHOW_DEBUG_MESSAGES = true endglobals //! runtextmacro HAIL_CreateProperty("TriggerTrigger", "trigger", "private") private function callThatTrigger takes nothing returns nothing call TriggerExecute(GetTriggerTrigger(GetTriggeringTrigger())) endfunction private struct UnitStack readonly unit u readonly UnitStack next public static method create takes unit u, UnitStack next returns UnitStack local UnitStack s = UnitStack.allocate() set s.u = u set s.next = next return s endmethod private method onDestroy takes nothing returns nothing set .u = null set .next = 0 endmethod public method recursiveDestroy takes nothing returns nothing if .next != 0 then call .next.recursiveDestroy() endif call .destroy() endmethod endstruct struct AnyUnitEvent private unitevent unitEventId private trigger t = CreateTrigger() private UnitStack stackReceive = 0 private UnitStack stackLose = 0 private UnitStack stackTake = 0 private trigger trigReceive = CreateTrigger() private trigger trigLose = CreateTrigger() private trigger trigTake = CreateTrigger() private integer numReceive = 0 private integer numLose = 0 private integer numTake = 0 public static method create takes unitevent unitEventId returns AnyUnitEvent local AnyUnitEvent e = AnyUnitEvent.allocate() call TriggerAddAction(e.trigReceive, function callThatTrigger) call TriggerAddAction(e.trigLose, function callThatTrigger) call SetTriggerTrigger(e.trigReceive, e.t) call SetTriggerTrigger(e.trigLose, e.t) call SetTriggerTrigger(e.trigTake, e.t) set e.unitEventId = unitEventId return e endmethod public method register takes code c returns nothing call TriggerAddAction(.t, c) endmethod public method toString takes nothing returns string local string s = "" set s = s + "Receive=|cFFFFCC00"+I2S(.numReceive)+"|r" set s = s + ", Lose=|cFFFF8000"+I2S(.numLose) + "|r" set s = s + ", Take=|cFF00FF00"+I2S(.numTake)+"|r" set s = s + ", Total="+I2S(.numTake+.numLose+.numReceive) return s endmethod ///Rotates the rolls of the groups ///[Receive] -> [Lose] -> [Take] -> [Receive] private method rotate takes nothing returns nothing local UnitStack stackTemp local trigger trigTemp local integer numTemp if .numTake + .numReceive + .numLose <= 0 then return endif //clean trigger call ResetTriggerTrigger(.trigLose) call DestroyTrigger(.trigLose) set .trigLose = CreateTrigger() call SetTriggerTrigger(.trigLose, .t) call TriggerAddAction(.trigTake, function callThatTrigger) //rotate set numTemp = .numLose set trigTemp = .trigLose set stackTemp = .stackLose set .numLose = .numReceive set .trigLose = .trigReceive set .stackLose = .stackReceive set .numReceive = .numTake set .trigReceive = .trigTake set .stackReceive = .stackTake set .numTake = numTemp set .trigTake = trigTemp set .stackTake = stackTemp set trigTemp = null debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("rotate") debug call BJDebugMsg(.toString()) debug endif endmethod ///Moves a unit from [lose] to [take] ///Removes the unit if it is dead ///Rotates if there are no units to transfer private method transfer takes nothing returns nothing local unit u local UnitStack s if .numLose <= 0 then call .rotate() return endif set s = .stackLose set u = s.u set .stackLose = .stackLose.next call s.destroy() set .numLose = .numLose - 1 if GetUnitState(u, UNIT_STATE_LIFE) > 0 then set .stackTake = UnitStack.create(u, .stackTake) call TriggerRegisterUnitEvent(.trigTake, u, .unitEventId) set .numTake = .numTake + 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("take") debug call BJDebugMsg(.toString()) debug endif else debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("remove") debug call BJDebugMsg(.toString()) debug endif endif endmethod public method addUnit takes unit u returns nothing set .stackReceive = UnitStack.create(u, .stackReceive) set .numReceive = .numReceive + 1 debug if SHOW_DEBUG_MESSAGES then debug call BJDebugMsg("add") debug call BJDebugMsg(.toString()) debug endif call TriggerRegisterUnitEvent(.trigReceive, u, .unitEventId) call .transfer() call .transfer() endmethod private method onDestroy takes nothing returns nothing call ResetTriggerTrigger(.trigReceive) call ResetTriggerTrigger(.trigLose) call ResetTriggerTrigger(.trigTake) call DestroyTrigger(.trigReceive) call DestroyTrigger(.trigLose) call DestroyTrigger(.trigTake) call DestroyTrigger(.t) call .stackReceive.recursiveDestroy() call .stackLose.recursiveDestroy() call .stackTake.recursiveDestroy() set .trigReceive = null set .trigLose = null set .trigTake = null set .t = null endmethod endstruct endlibrary |
| 03-31-2008, 10:09 PM | #2 |
Ah, it turns out it is possible to have code arrays. You just have to store them as integers and do some acrobatics to get the code value back. JASS:
///Yes, this function is necessary
private function Code2Code takes code c returns code
return c
return null
endfunction
///Yes, this function is also necessary
private function I2CodeHelper takes integer i returns code
return i
return null
endfunction
///Yes, this has to work this way
private function I2Code takes integer i returns code
return Code2Code(I2CodeHelper(i))
endfunction
private function Code2I takes code c returns integer
return c
return 0
endfunction
|
| 04-01-2008, 12:15 AM | #3 |
Apparently Int2Code is very dangerous, so be careful. |
| 04-01-2008, 12:24 AM | #4 |
It's been working fine so far- *BLARGH* AAAUUUUGGHGHH I'll definitely be submitting an improved version of this as a system. |
