| 08-30-2006, 03:31 PM | #1 |
Summon Leviathan - Morphs a leviathan out of water, that quickly disappears if standing on land. With level, new abilities are unlocked. If cast from water, abilities have various advantages. Aqua Rhapsody [buff spell] - If target should die while under the effect of the spell, it will be fully healed but will have its mana pool halved and will no longer be able to attack. Coral Wisdom [chain+buff spell] - Links a couple of units together. If one of the units loses mana, the loss is split to all the units. Sea Chant [AoE spell] - Sacrifices the caster by creating a triangle of arcane energy that drains mana from spellcasters and life from the rest. If casters are killed or channeling is stopped before spell finishes, the life&mana stolen is taken by the units around them.Comments and suggestions like always are welcome! :) ~Daelin |
| 08-30-2006, 06:06 PM | #2 |
ok great spells.. I've seen them on the contest and I'am sure they will be approved BUT ONCE FOR ALL!! JASS:function SeaChant_PointInTriangle takes real x1, real y1, real x2, real y2, real x3, real y3, real x, real y returns boolean local real calc1 = (y-y1)*(x2-x1) - (x-x1)*(y2-y1) local real calc2 = (y-y3)*(x1-x3) - (x-x3)*(y1-y3) local real calc3 = (y-y2)*(x3-x2) - (x-x2)*(y3-y2) return (calc1*calc2>0) and (calc3*calc2>0) endfunction is pain in my eyes and should it be for averyone who thinks he is programmer! JASS:function IsOriginInTriangle takes real x1,real y1,real x2,real y2,real x3,real y3 returns boolean local real a=x1*y2-x2*y1 local real b=x2*y3-x3*y2 local real c=x3*y1-x1*y3 return (a>=0 and b>=0 and c>=0)!=(a<=0 and b<=0 and c<=0) endfunction function IsPointInTriangle takes real x,real y,real x1,real y1,real x2,real y2,real x3,real y3 returns boolean return IsOriginInTriangle(x1-x,y1-y,x2-x,y2-y,x3-x,y3-y) endfunction ok yours needs 15 times subtraction, 8 times real multiplication and 3comperisons mine need 9 times subtraction, 6 times multiplication and 7 comperisons guess whichs faster, even if you not consider that real-multi needs some* more steps then a comparison I've seen this way of checking first done by GRATER he used it for a (convex) quad. i needed it for a triangle and was kinda unsatisfied by vex's one (ugh tan()) there are many ways to interpret those calculation (e.g. angles,vector-product..) the main results are: one value == 0 and the others have the same sign: the point is one a side of the triangle two value == 0: the point is in a corner of the triangle all three value == 0 the point is on a line going through all 3 corners -> no triangle otherwise the point is inside the triangle if a,b and c have the same sign if you only want to know if it really inside and touch a line, you can even save some steps. |
| 08-30-2006, 08:38 PM | #3 |
Unfortunately your speculation is wrong, wrong, wrong. Some benchmarks on my machine: JASS:function IsPointInTri takes real x1, real y1, real x2, real y2, real x3, real y3, real px, real py returns boolean // local real ux = x2-x1 //u is vector from 1 to 2 // local real uy = y2-y1 // local real vx = x3-x1 //v is vector from 1 to 3 // local real vy = y3-y1 local real ax = px-x1 //a is vector from p to 1 local real ay = py-y1 local real bx = px-x2 //b is vector from p to 2 local real by = py-y2 local real cx = px-x3 //c is vector from p to 3 local real cy = py-y3 //A = 1/2 | u cross v | //Drop factor of 1/2 since we're doing a comparison local real area_tri = (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) local real area1 = ax*by-ay*bx //area between 1 2 p local real area2 = bx*cy-by*cx //area between 2 3 p local real area3 = cx*ay-cy*ax //area between 3 1 p if area_tri < 0. then set area_tri = -area_tri endif if area1 < 0. then set area1 = -area1 endif if area2 < 0. then set area2 = -area2 endif if area3 < 0. then set area3 = -area3 endif return area1+area2+area3 <= area_tri*1.001 //some TOL for rounding endfunction function SeaChant_PointInTriangle takes real x1, real y1, real x2, real y2, real x3, real y3, real x, real y returns boolean local real calc1 = (y-y1)*(x2-x1) - (x-x1)*(y2-y1) local real calc2 = (y-y3)*(x1-x3) - (x-x3)*(y1-y3) local real calc3 = (y-y2)*(x3-x2) - (x-x2)*(y3-y2) return (calc1*calc2>0) and (calc3*calc2>0) endfunction function IsOriginInTriangle takes real x1,real y1,real x2,real y2,real x3,real y3 returns boolean local real a=x1*y2-x2*y1 local real b=x2*y3-x3*y2 local real c=x3*y1-x1*y3 return (a>=0 and b>=0 and c>=0)!=(a<=0 and b<=0 and c<=0) endfunction function IsPointInTriangle takes real x,real y,real x1,real y1,real x2,real y2,real x3,real y3 returns boolean return IsOriginInTriangle(x1-x,y1-y,x2-x,y2-y,x3-x,y3-y) endfunction function Trig_testtimer_Actions takes nothing returns nothing local integer stopwatch local integer i = 0 local real t call TriggerSleepAction(0.) call BJDebugMsg("\nTimer test") set stopwatch = StopWatchCreate() loop exitwhen i >= 10000 call IsPointInTriangle(0.,0.,0.,0.,0.,0.,0.,0.) call IsPointInTriangle(0.,0.,0.,0.,0.,0.,0.,0.) call IsPointInTriangle(0.,0.,0.,0.,0.,0.,0.,0.) call IsPointInTriangle(0.,0.,0.,0.,0.,0.,0.,0.) call IsPointInTriangle(0.,0.,0.,0.,0.,0.,0.,0.) set i = i + 1 endloop call BJDebugMsg(R2S(StopWatchMark(stopwatch)*1000.)+"ms") call StopWatchDestroy(stopwatch) endfunction Code:
50,000 iterations units are milliseconds empty loop: 6 7 6 Daelin SeaChant_PointInTriangle: 502 492 496 PipeDream IsPointInTri: 761 759 763 FireFreak IsPointInTriangle: 630 635 634 Daelin's implementation is clearly fastest. You would probably be equally fast if you used local variables instead of an extra function call. Mine would be even slower in practice since the final sets would run 50% of the time. I'll look at the spells later. -- benchmarking is so much more fun with no execution limit and timing natives. Lesson here is that counting arithmetic operations is a stupid way to compare efficiency when you're within an order of magnitude. -- The other probable reason daelin's is faster is that "and" is a bunch of ops as it's an if/then/else. a single integer multiply is pretty much irrelevantly fast, as a typical run through the VM probably has a dozen other multiplies. Same deal with those comparisons, bunch more runs through the VM. Finally using integer arithmetic is only a percent faster. You lose all that if you try to R2I anyway, but the point is that chugging through the VM is mostly slower than any arithmetic. |
| 08-31-2006, 11:33 AM | #4 |
thats really surprising for me, but does what u say mean the != operator is slower than "and not() " ? im busy right now, but has anyone checked how fast vex version is ? NO i dont fear that to calls of tan beat us all |
| 08-31-2006, 02:12 PM | #5 | |
Quote:
Aham... I never assumed that it was perfect or that it was the fastest way to do it, but clearly PipeDream's checkings proves it. How about checking it compared to Vex's too? ;) ~Daelin |
| 08-31-2006, 06:29 PM | #6 |
Hrmm, hrmm. Geometrical benchmarks aside... Summon Leviathan: JESP requires that the spell uses no spell-specific globals. Your array fits that description, no? I'm sure you could store the abilities in gamecache or something. I'll plug myself again, and say that you could combine your triggers to prevent casting on non-water areas and the actual spell effects into one. Example: http://www.wc3campaigns.net/showthread.php?t=83132. Aqua Rhapsody: All things attached to public objects (eg the target of the spell) must use your spell prefix followed by "_". Eg: JASS:call SetHandleInt(targ, "AquaRhapsodyOn", 1) //Spell works //Should be: call SetHandleInt(targ, "AquaRhapsody_AquaRhapsodyOn", 1) //Spell works //Or if that was already your prefix: call SetHandleInt(targ, "AquaRhapsody_On", 1) //Spell works A possible better method of cleaning up the Unit Damaged trigger would be to have a condition inside the action that checks if the unit has the buff, and cleans itself up if it doesn't (so it would clean up the first time the unit is damaged without the buff). Coral Wisdom: Same public naming thing applies here. I'm not sure if your chain search function is supposed to find the nearest unit, but it currently won't because you don't update the distance when you find a unit within it. You leak a handle reference in the target search function. An easy fix in this instance is: JASS:set u = targ //u is a parameter so it doesn't leak set targ = null return u endfunction Sea Chant: You used the correct public naming on private objects in this one :P |
| 08-31-2006, 06:59 PM | #7 |
JASS:set u = targ //u is a parameter so it doesn't leak set targ = null return u endfunction |
| 08-31-2006, 07:48 PM | #8 |
Hmm, ok. Integer return bug it would be then. |
| 09-02-2006, 11:27 AM | #9 |
Fixed all stuff except the damage condition stuff which I won't search for it right now. But I find that it is fast enough. And I like the way I check if spell is not cast on water, but thanks for the tip. I'll keep it in mind. ~Daelin |
| 09-02-2006, 06:57 PM | #10 |
great job on these spells, they are pretty clean. just a few potential problems: - Leviathan valid displays the error message to all players - Lots of timer nulling - Frequently you use a TriggerSleepAction for delays, for example AquaRhapsody_Main to wait for a buff to take effect. This may work ok in single player but it will be a mixed bag online. |
| 09-02-2006, 08:16 PM | #11 | |||
Quote:
Quote:
Quote:
Thanks for the info! ~Daelin |
| 09-02-2006, 09:47 PM | #12 |
You can't expect TriggerSleepAction to do better than 50-200% of the entered time. So if you hand tuned those numbers for single player, they'll be wildly different in different multiplayer games. Maybe this doesn't matter for the spells. TriggerSleepAction has a number of other problems- time elapses while the game is paused which is ok for short waits. A more serious problem is in one map I have seen TriggerSleepAction break down and not delay at all after thirty minutes of play. But I've never seen that anywhere else, so, *shrug*. The only solution we know of is timers/time triggers, both of which are cumbersome. --- The least invasive method for avoiding timer nulling is something like: JASS:local integer it = HtoI(CreateTimer()) local string key = I2S(it) //or use griffen's or karukef's timer attach methods call StoreInteger(GC(),key,"somedata",39) call TimerStart(ItoT(it),...) |
| 09-03-2006, 07:17 AM | #13 | |
Quote:
Jesus christ... does this mean that I would have to use in timer's function something like this?? JASS:local integer t = H2I(GetExpiredTimer()) local unit u = GetHandleUnit(I2T(t), "u") if GetTimerElapsed(I2T(t))>0 then //blah blah endif set u = null This is not only super unefficient (considering that the function executed by a timer is executed a LOT of times) but it also looks odd. Or, doesn't this bug manifest in timer's function but only in any other function? Please be more specific here.~Daelin |
| 09-03-2006, 07:31 AM | #14 |
Actually the unusual way you've written things may make it all work out ok. The problems should only occur if you increase the reference count above zero by assigning and then at some time reduce it to zero while you still intend to use it. Since you typically keep the parent thread reference around and use a sleep loop to determine when to destroy, such that the reference never drops below one, you'll probably be ok, but that's speculation. Anywhere that you are doing the more classical orphaned timer thread with no references to the timer, the alternative to ItoT is a trick discovered by Cubasis: JASS:function Timer_cb_authentic takes timer t returns nothing //Spell code goes here. don't null t. endfunction function Timer_cb takes nothing returns nothing call Timer_cb_authentic(GetExpiredTimer()) endfunction And return bug is plenty fast in all my measurements, so fast that in a spell I don't think you would ever notice a difference. |
| 09-03-2006, 08:26 AM | #15 |
So where exactly can this bug occur? Anywhere you nullify a timer? Don't point me to the thread which explains this bug cuz I obviously did not get this aspect. ~Daelin |
