| 09-11-2007, 03:28 AM | #1 |
For the past few days I've been working on a complex chain lightning, which allows you to easily create custom chain lightning effects with many possibilities. Below is the almost finalized product of my work, but before I submit it to any sites I want to have it looked over by people. If you have any suggestions or ideas of what I can add to it, or suggestions to make my code more efficient (im going to have alot of those), feel free to post. Requires vJass and CSCache in a library called CSCache JASS:library ChainLightning requires CSCache //Requires vJass to compile //Requires CSCache system in a library called CSCache //Use GetLightningTarget() to refer to the unit being hit by lightning in any handlerFuncs you use //Use GetLightningCaster() to refer to the casting unit //Use GetLightningBounceNumber() to refer to how many bounces there have been so far //Use GetLightning() to refer to the lightning connecting the last unit to the current unit //Use 0 for bouncesbetweenbouncetwice to make it so you can never bounce twice //Currently there is a 99 bounce limit if you want bouncesbetweenbouncetwice to work //ChainLightning takes unit caster, unit target,real maxbouncedist, string lightningtype,real lightningduration, integer numberofbounces,integer bouncesbetweenbouncetwice,real timebetweenbounces,filterfunc filter, code handlerFunc returns nothing struct ChainData integer maxbounces integer sofar unit target unit caster unit lastunit string ltype real maxdist trigger t real time group alreadyhit = CreateGroup() filterfunc filter unit array units[100] integer bounces real ldur static lightning Lightning static unit LightningTarget static unit LightningCaster static integer LightningBounceNumber endstruct struct LightningAttachData lightning l unit u1 unit u2 real h1 real h2 real dur real dursofar endstruct function GetLightning takes nothing returns lightning return ChainData.Lightning endfunction function GetLightningTarget takes nothing returns unit return ChainData.LightningTarget endfunction function GetLightningCaster takes nothing returns unit return ChainData.LightningCaster endfunction function GetLightningBounceNumber takes nothing returns integer return ChainData.LightningBounceNumber endfunction function AttachTempLightning_Timer takes nothing returns nothing local LightningAttachData la = LightningAttachData(GetAttachedInt(GetExpiredTimer(),"la")) call MoveLightningEx(la.l,true,GetUnitX(la.u1),GetUnitY(la.u1),la.h1,GetUnitX(la.u2),GetUnitY(la.u2),la.h2) set la.dursofar = la.dursofar + 0.04 if la.dursofar >= la.dur then call PauseTimer(GetExpiredTimer()) set la.u1 = null set la.u2 = null call DestroyLightning(la.l) set la.l = null call LightningAttachData.destroy(la) call CleanAttachedVars(GetExpiredTimer()) call DestroyTimer(GetExpiredTimer()) endif endfunction function AttachTempLightning takes string s,unit u1, unit u2, real dur, real height1, real height2 returns lightning local LightningAttachData la = LightningAttachData.create() local timer t = CreateTimer() set la.u1 = u1 set la.u2 = u2 set la.l = AddLightningEx(s,true,GetUnitX(u1),GetUnitY(u1),height1,GetUnitX(u2),GetUnitY(u2),height2) set la.dur = dur set la.dursofar = 0 set la.h1 = height1 set la.h2 = height2 call AttachInt(t,"la",la) call TimerStart(t,0.04,true,function AttachTempLightning_Timer) set t = null return la.l endfunction function DuplicateUnit takes unit u returns unit return u endfunction function ChainLightning_Timer takes nothing returns nothing local ChainData cd = ChainData(GetAttachedInt(GetExpiredTimer(),"cd")) local group g = CreateGroup() local group g2 = CreateGroup() local unit u local integer i local lightning l local timer t = GetExpiredTimer() set cd.sofar = cd.sofar + 1 set cd.units[cd.sofar] = cd.target if cd.bounces > 0 then set i = cd.sofar - cd.bounces if i > 0 and cd.units[i] != null then call GroupRemoveUnit(cd.alreadyhit,cd.units[i]) set cd.units[i] = null endif endif set l = AttachTempLightning(cd.ltype,cd.target,cd.lastunit,cd.ldur,GetUnitFlyHeight(cd.target) + 50,GetUnitFlyHeight(cd.lastunit) + 50) set ChainData.LightningTarget = cd.target set ChainData.LightningCaster = cd.caster set ChainData.LightningBounceNumber = cd.sofar set ChainData.Lightning = l call TriggerExecute(cd.t) call GroupAddUnit(cd.alreadyhit,cd.target) call GroupEnumUnitsInRange(g,GetUnitX(cd.target),GetUnitY(cd.target),cd.maxdist,cd.filter) call GroupAddGroup(g,g2) loop set u = FirstOfGroup(g2) call GroupRemoveUnit(g2,u) if IsUnitInGroup(u,cd.alreadyhit) then call GroupRemoveUnit(g,u) endif exitwhen u == null endloop set cd.lastunit = DuplicateUnit(cd.target) set cd.target = GroupPickRandomUnit(g) if cd.target == null then set cd.sofar = cd.maxbounces endif call DestroyGroup(g) set g = null if cd.sofar >= cd.maxbounces then call PauseTimer(t) set cd.target = null set cd.caster = null set cd.lastunit = null set cd.ltype = null set cd.t = null set cd.filter = null call DestroyGroup(cd.alreadyhit) set cd.alreadyhit = null call ChainData.destroy(cd) call CleanAttachedVars(t) call DestroyTimer(t) endif if cd.sofar == 1 then call TimerStart(GetExpiredTimer(),cd.time,true,function ChainLightning_Timer) endif call DestroyGroup(g2) set g2 = null set u = null set l = null endfunction function ChainLightning takes unit caster, unit target,real maxbouncedist, string lightningtype,real lightningdur, integer numberofbounces,integer bouncesbetweenbouncetwice,real timebetweenbounces,filterfunc filter, code handlerFunc returns nothing local ChainData cd = ChainData.create() local timer t = CreateTimer() local trigger trig = CreateTrigger() call TriggerAddAction(trig,handlerFunc) set cd.ldur = lightningdur set cd.bounces = bouncesbetweenbouncetwice set cd.filter = filter set cd.t = trig set cd.target = target set cd.caster = caster set cd.lastunit = caster set cd.maxdist = maxbouncedist set cd.ltype = lightningtype set cd.maxbounces = numberofbounces set cd.t = trig set cd.time = timebetweenbounces set cd.sofar = 0 call GroupAddUnit(cd.alreadyhit,target) call AttachInt(t,"cd",cd) call TimerStart(t,0.00,false,function ChainLightning_Timer) set trig = null set t = null endfunction endlibrary You can also download the map below to see a few sample spells. |
| 09-11-2007, 06:02 AM | #2 |
Why require CSCache when TimerAttach/GetTimerInt will do it just as fast (if not faster) and without needing CSCache to be implemented (since most people with vJASS won't have CSCache, I presume). |
| 09-11-2007, 06:16 AM | #3 |
You're going to run into problems when using this on flying units. Getting a unit's flyheight is just simply not accurate for placement of lightning effects. The only way to be sure is to use a dummy unit to cast a modified Finger of Death on the target... which seems like something you're not going to want to do. |
| 09-11-2007, 05:06 PM | #4 |
There is a much easier way to make chain effects. Just have a dummy unit cast an 0.01 damage chain lightning and detect when units are damaged. No need to make all this code when war3 already includes capabilities for chain effects. http://www.jx3.net/TDG/forum/viewtopic.php?f=16&t=877 |
| 09-11-2007, 08:53 PM | #5 |
humm i suppose this could be useful in certain situations because with a little modifying you could make it target all heroes first then normal units or w/e... but generally ya go with the built in wc3 stuff. |
| 09-11-2007, 10:23 PM | #6 |
This is meant to be easily modifyable. You only need a few lines of code to make a good-looking new spell. Botanic, thats a good idea. I need to decide how to do it- 1.Put booleans when calling the function (function ChaiNlighting takes boolean.. etc) or have it take something like TARGET_HEROS_FIRST, TARGET_WEAKEST_FIRST, tc. If you have any better suggestions of how to do it, or any ideas of other kingds of things mappers will want to target first, tell me. |
| 09-11-2007, 10:43 PM | #7 |
Your chain lightning has an infamous GroupEnum leak. http://wc3campaigns.net/showthread.php?t=90186 When using GroupEnum be sure to use a global group that is only ever created once and always use a filter. Just call GroupClear before you use it. Its minor and leaks once per bounce of lightning but its the leak I hate the most. Instead of GroupPickRandomUnit(g) to pick the next target, you should make it a function interface in the struct so that the person making a spell can create a function which takes a group and returns a unit from that group according to the desires of the spell at hand. Suppose I wanted to remake chain heal because I have too much time on my hands. I'd make a function that returned the unit with the lowest percentage health from the group. Suppose I wanted to make a chain mana burn, I'd want the chain to pick the unit with the highest mana percentage from the group. |
| 09-11-2007, 11:05 PM | #8 |
I didnt look through your code but it would simply be a set Hero_T_F = Hero_First() pick unit matching condition (matching unit is a hero = Hero_T_F) |
| 09-12-2007, 02:01 AM | #9 | |
Quote:
But what is the advantage over using the existing chain functionality within war3? |
| 09-12-2007, 03:32 AM | #10 |
Its easier to say 1 line of code than create a long code and a few custom abilities, and a dummy to cast them. Plus, if you had taken the time to read the top of the code, you would realize you have the ability to change several other things: 1. the duration of the lightnings 2. the time between bounces 3. the possibility to bounce multiple times on the same unit 4. the possibility to use filters to select specific units, for example a single unit or units in a group 5. the upcoming possibility to give the lightning it own target preferences. Anyways, I posted this to have my code scrutinized and to have suggestions to what i could add; not to have people question why I made it in the first place.. |
| 09-12-2007, 04:51 AM | #11 |
Hmmm: Regarding detecting chain lightning: If there are two ways to do something, and one involves detecting something the game doesn't allow you to detect directly, pick the way that doesn't require to detect things. regarding the code: what the hell is this?: JASS:function DuplicateUnit takes unit u returns unit return u endfunction Regarding timerattach: The very fact you are attaching things to timers is proof you got bad design, replacing CSCache with yet another return bug dependant thing will not help. It is better to fix the issue from the inside than to get ineffective patch solutions. |
| 09-12-2007, 04:58 AM | #12 |
Vex: That piece of code is because i needed to point one unit variable to another, and I thought if i directly did that and then changed the original, the second variable would change also. Reguarding timerattach: And how do I fix this? I'm still learning the complexities of vJass. |
| 09-12-2007, 11:13 AM | #13 |
I'm confused now, aren't these things supposed to be in Submit Resource section? |
| 09-12-2007, 11:31 AM | #14 | |
Quote:
He's trying to improve it first. |
| 09-12-2007, 01:33 PM | #15 | |
Quote:
-- timers are fun, we've been doing attaching to them for years and it turns out attaching info to timers is not only risky but totally unnecessary. Specially if you are using the timer for a multi instance 0.04 seconds timer: JASS:globals timer myGlobalTimer mycustomtype array V integer N=0 endglobals function expire takes nothing returns nothing local integer i=0 if(N==0) then call PauseTimer(myGlobalTimer) return endif loop exitwhen(i>=N) if(V[i].process() ) then // returning true means it should stay... set i=i+1 else call V[i].destroy() set V[i]=V[N-1] set N=N-1 endif endloop endfunction function addtotimer takes mycustomtype t returns nothing if(N==0) then set V[0]=t set N=1 call TimerStart(myGlobalTimer, function expire) else set V[N]=t set N=N+1 endif endfunction function init takes nothing returns nothing set myGlobalTimer=CreateTimer() endfunction |
