| 03-19-2009, 10:15 AM | #1 |
I took this spell from Wc3C, this spell was made by lars and I claim no credit whatsoever. This spell simply is a bouncing rubber ball which bounces when hittings units, or terrain. I took the code out and the bouncing part seems like gibberish to me or rather rubbish. It uses some strange functions like JASS:ModuloReal JASS:RAbsBJThe part of this JASS:local real angle2=bj_RADTODEG*Atan2(GetUnitY(ball)-ly,GetUnitX(ball)-lx) Why does he uses bj_RADTODEG? Isn't radians better? Help me fix or teach me how to fix this or help me understand this bouncing thing easier or just simplify the code and I will apply this. I don't get how did he make this, can this be simplified easier so I can understand and apply it to my spell? Thanks! JASS:function Trig_Rubber_Ball_Conditions takes nothing returns boolean return GetUnitTypeId(GetSummonedUnit()) == 'e000' endfunction function RubberBallLoop takes nothing returns nothing local timer tm=GetExpiredTimer() local string mis="Rubber Ball"+I2S(H2HI(tm)) local unit caster=HI2Unit(GetStoredInteger(udg_CC,mis,"Caster")) local unit ball=HI2Unit(GetStoredInteger(udg_CC,mis,"Ball")) local real lx=GetStoredReal(udg_CC,mis,"LastX") local real ly=GetStoredReal(udg_CC,mis,"LastY") local real angle=GetStoredReal(udg_CC,mis,"Angle") local real dam=GetStoredReal(udg_CC,mis,"Damage") local integer index=GetStoredInteger(udg_CC,mis,"Index") local real angle2=bj_RADTODEG*Atan2(GetUnitY(ball)-ly,GetUnitX(ball)-lx) local location loc1 local location loc2 if index<=0 or ball==null or IsUnitDeadBJ(ball) then call DestroyTimer(tm) call FlushStoredMission(udg_CC,mis) set tm=null set caster=null set ball=null return endif if (GetUnitX(ball)-lx)*(GetUnitX(ball)-lx)+(GetUnitY(ball)-ly)*(GetUnitY(ball)-ly)<100 then set angle2=ModuloReal(GetRandomReal(90,270)+angle,360) else //kernel of rubber ball, quite simple? :) set angle2=ModuloReal(2*angle2-angle,360) //enf of kernel endif set loc1 = GetUnitLoc(ball) set loc2 = PolarProjectionBJ(loc1, 30.00, angle2) call SetUnitPositionLoc( ball, loc2) if RAbsBJ(ModuloReal(angle2-angle,360)) >30 and RAbsBJ(ModuloReal(angle2-angle,360))<330 then call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl",GetUnitX(ball),GetUnitY(ball))) call FadingText( null, I2S(R2I(dam))+"!", 255,0,0, GetUnitX(ball),GetUnitY(ball) ) call UnitDamagePointLoc(caster,0,250,GetUnitLoc(ball),dam,ATTACK_TYPE_MELEE,DAMAGE_TYPE_NORMAL) endif call StoreInteger(udg_CC,mis,"Index",index-1) call StoreReal(udg_CC,mis,"Angle",angle2) call StoreReal(udg_CC,mis,"LastX",GetLocationX(loc1)) call StoreReal(udg_CC,mis,"LastY",GetLocationY(loc1)) call RemoveLocation(loc1) call RemoveLocation(loc2) set loc1=null set loc2=null endfunction function Trig_Rubber_Ball_Actions takes nothing returns nothing local timer tm=CreateTimer() local string mis="Rubber Ball"+I2S(H2HI(tm)) local unit caster=GetSummoningUnit() local unit ball=GetSummonedUnit() local real angle=bj_RADTODEG*Atan2(GetUnitY(ball)-GetUnitY(caster),GetUnitX(ball)-GetUnitX(caster)) call SetUnitTimeScale(ball,2) call UnitAddAbility(ball,'Aloc') call UnitRemoveAbility(ball,'Aloc') call SetUnitPathing(ball,true) call StoreInteger(udg_CC,mis,"Caster",H2HI(caster)) call StoreInteger(udg_CC,mis,"Ball",H2HI(ball)) call StoreReal(udg_CC,mis,"LastX",GetUnitX(caster)) call StoreReal(udg_CC,mis,"LastY",GetUnitY(caster)) call StoreInteger(udg_CC,mis,"Index",1000) call StoreReal(udg_CC,mis,"Angle",angle) call StoreReal(udg_CC,mis,"Damage",100) call TimerStart(tm,0.05,true,function RubberBallLoop) set tm=null set caster=null set ball=null set tm=null set caster=null set ball=null endfunction //=========================================================================== function InitTrig_Rubber_Ball takes nothing returns nothing set gg_trg_Rubber_Ball = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Rubber_Ball, EVENT_PLAYER_UNIT_SUMMON ) call TriggerAddCondition( gg_trg_Rubber_Ball, Condition( function Trig_Rubber_Ball_Conditions ) ) call TriggerAddAction( gg_trg_Rubber_Ball, function Trig_Rubber_Ball_Actions ) endfunction |
| 03-19-2009, 11:56 AM | #2 |
RAbsBJ returns the given value ..as a positive number ...(RAbsBJ(5)==RAbsBJ(-5)==5) ModuloReal returns ...the modulus (=rest of division) (5 modulo 3.2 == 1.8) |
| 03-19-2009, 01:51 PM | #3 | |
Quote:
Well, radians are faster in theory, and are more standard in math, all blizzard trigonometry natives are radian based, and this is also the case for trigonometry functions in other languages. However, some blizzard natives take degrees as arguments instead of radians, so we can't get rid of them. Degrees are also easier to understand for most people, in debug messages I often convert to degrees before displaying. And for configuration stuff I use degrees and then convert them to radians... Everything in that spell can be simplified, specially because of the use of gamecache... want me to get started ? |
| 03-19-2009, 02:00 PM | #4 |
Sure. |
| 03-19-2009, 02:22 PM | #5 |
Step 1. Don't attach so many stuff to gamecache, makes code look long , ugly, hard to read and etc. Just attach a single struct... Attaching handles to gamecache is very bad, if you need to use I2H your code is brokenly designed. So we see things like JASS:
local unit caster=HI2Unit(GetStoredInteger(udg_CC,mis,"Caster"))
local unit ball=HI2Unit(GetStoredInteger(udg_CC,mis,"Ball"))
local real lx=GetStoredReal(udg_CC,mis,"LastX")
local real ly=GetStoredReal(udg_CC,mis,"LastY")
local real angle=GetStoredReal(udg_CC,mis,"Angle")
local real dam=GetStoredReal(udg_CC,mis,"Damage")
local integer index=GetStoredInteger(udg_CC,mis,"Index")
JASS:struct RubberBallData unit Caster unit Ball real LastX real LastY real Angle real Damage integer Index endstruct JASS:function Trig_Rubber_Ball_Conditions takes nothing returns boolean return GetUnitTypeId(GetSummonedUnit()) == 'e000' endfunction struct RubberBallData unit Caster unit Ball real LastX real LastY real Angle real Damage integer Index endstruct function RubberBallLoop takes nothing returns nothing local timer tm=GetExpiredTimer() local string mis="Rubber Ball"+I2S(H2HI(tm)) local RubberBallData rbd = rbd( GetStoredInteger( udg_CC, mis, "data") ) //just retrieve the struct from the game cache local unit caster=rbd.Caster local unit ball=rbd.Ball local real lx= rbd.LastX local real ly= rbd.LastY /// Abstraction is your friend. local real angle= rbd.Angle local real dam= rbd.Damage local integer index= rbd.Index local real angle2=bj_RADTODEG*Atan2(GetUnitY(ball)-ly,GetUnitX(ball)-lx) local location loc1 local location loc2 if index<=0 or ball==null or IsUnitDeadBJ(ball) then call DestroyTimer(tm) call FlushStoredMission(udg_CC,mis) //also destroy the struct call rbd.destroy() set tm=null set caster=null set ball=null return endif if (GetUnitX(ball)-lx)*(GetUnitX(ball)-lx)+(GetUnitY(ball)-ly)*(GetUnitY(ball)-ly)<100 then set angle2=ModuloReal(GetRandomReal(90,270)+angle,360) else //kernel of rubber ball, quite simple? :) set angle2=ModuloReal(2*angle2-angle,360) //enf of kernel endif set loc1 = GetUnitLoc(ball) set loc2 = PolarProjectionBJ(loc1, 30.00, angle2) call SetUnitPositionLoc( ball, loc2) if RAbsBJ(ModuloReal(angle2-angle,360)) >30 and RAbsBJ(ModuloReal(angle2-angle,360))<330 then call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl",GetUnitX(ball),GetUnitY(ball))) call FadingText( null, I2S(R2I(dam))+"!", 255,0,0, GetUnitX(ball),GetUnitY(ball) ) call UnitDamagePointLoc(caster,0,250,GetUnitLoc(ball),dam,ATTACK_TYPE_MELEE,DAMAGE_TYPE_NORMAL) endif set rbd.Index = Index -1 set rbd.Angle = angle2 set rbd.LastX = GetLocationX(loc1) set rbd.LastY = GetLocationY(loc1) call RemoveLocation(loc1) call RemoveLocation(loc2) set loc1=null set loc2=null endfunction function Trig_Rubber_Ball_Actions takes nothing returns nothing local timer tm=CreateTimer() local string mis="Rubber Ball"+I2S(H2HI(tm)) local RubberBallData rbd = RubberBallData.create() local unit caster=GetSummoningUnit() local unit ball=GetSummonedUnit() local real angle=bj_RADTODEG*Atan2(GetUnitY(ball)-GetUnitY(caster),GetUnitX(ball)-GetUnitX(caster)) call SetUnitTimeScale(ball,2) call UnitAddAbility(ball,'Aloc') call UnitRemoveAbility(ball,'Aloc') call SetUnitPathing(ball,true) // Yeah <3 abstraction call StoreInteger(udg_CC, mis , integer(rbd) ) set rbd.Caster = caster set rbd.Ball = ball set rbd.LastX=GetUnitX(caster) set rbd.LastY=GetUnitY(caster) set rbd.Index=1000 set rbd.Angle=angle set rbd.Damage=100 call TimerStart(tm,0.05,true,function RubberBallLoop) set tm=null set caster=null set ball=null set tm=null set caster=null set ball=null endfunction //=========================================================================== function InitTrig_Rubber_Ball takes nothing returns nothing set gg_trg_Rubber_Ball = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Rubber_Ball, EVENT_PLAYER_UNIT_SUMMON ) call TriggerAddCondition( gg_trg_Rubber_Ball, Condition( function Trig_Rubber_Ball_Conditions ) ) call TriggerAddAction( gg_trg_Rubber_Ball, function Trig_Rubber_Ball_Actions ) endfunction That's step 1... |
| 03-19-2009, 02:41 PM | #6 | |
Quote:
What does angle2 do after this? It is used as a new direction for the ball. That also helps us understand the if-then else, it seems it is checking whether the square of the distance between the ball and its previous position is < 100.0 , this means it will do the actions is the distance is less than 10.0 - If the distance is less than 10, increase the angle by a random amount : JASS:set angle2=ModuloReal(GetRandomReal(90,270)+angle,360) ModuloReal is there just to ensure angle2 stays at range [0, 360.0) However, I don't think this is right per se... It seems that 270 is too big of a difference for an angle, in my opinion he wanted the angle to turn right or left sometimes, he should have used something like this: JASS:set angle2=ModuloReal(GetRandomReal(-90,90)+angle,360) What the hell does JASS://kernel of rubber ball, quite simple? :) set angle2=ModuloReal(2*angle2-angle,360) //enf of kernel Well, if angle2 is the new angle and angle1 was the last one., plus the distance has increased so absurdly, I think it means that the pathing caused a mistake in the ball's positioning, this most likely means that the ball hit something. ModuloReal(2*angle2-angle, 360) seems to be a trick to make the ball 'bounce', again ModuloReal is there just to ensure it stays in the correct range. |
| 03-19-2009, 02:43 PM | #7 |
JASS:Advise From Vile: The calculations are nice but there are a few problems with them. When casting on several angles, it just doesnt look very realistic. Sometimes the ball just bounces off the wall in the opposite direction, instead of changing it angle properly. I set the angle to minus if the terrain isnt pathable and it worked well. Try it. Forgot to include that, how do I apply it over there? I get the struct part but why retrieve it using gamecache? Could have used TimerUtils instead. can ModuleReal and RabsBJ be removed from the code? I don't see you using it. Never used them before and never had a need to. |
| 03-19-2009, 02:59 PM | #8 |
you need their "functionality" ...but of course you can inline them .. |
| 03-19-2009, 03:20 PM | #9 | ||
Quote:
Quote:
|
| 03-19-2009, 03:32 PM | #10 | |
Ok, continue on, I want to add something to my knockback system that when it reaches the end of the cliff using IsterrainWalkable by Anitarf, they bounce back realisticly. Quote:
Well that's fine. |
| 03-19-2009, 03:38 PM | #11 |
That's like an ancient spell, of course the code is ugly. We didn't know any better back then. The problem isn't in the spell, it's in you choosing to look at it's code. Doing bouncing behaviour with angles just seems silly, vector math seems like a lot better choice for the task. |
| 03-19-2009, 04:08 PM | #12 |
Well , I don't understand vectors at all.. |
| 03-19-2009, 04:21 PM | #13 |
Still you should take a look at anitarfs vector lib (vJass) testmap ..it got bouncing balls (press esc ingame) |
| 03-20-2009, 02:59 AM | #14 |
I am in grade 8 so I have never heard of vectors, took a look at them and they were too puzzling for me. @Vexorian Negate which angle? |
