| 08-16-2008, 08:04 AM | #1 |
Flaming Arrow Launches an arrow enchanted with fire, in an arc towards target point. Upon impact detonates damaging units in an AoE. == Anitarf helped me alot with this spell, now just trying to finish it. I'm having 2 problems:
JASS:globals flamingarrowdata array FlamingArrowArray integer FlamingArrowTotal = 0 integer ABILITY_RAWCODE = 'A002' real TIMER_INTERVAL = 0.03 real MISSILE_SPEED = 900 real MISSILE_GRAVITY = 300 real MISSILE_MINDIST = 400 real MISSILE_DAMAGE = 100 endglobals struct flamingarrowdata unit missile = null effect sfx = null integer level = 0 real sx = 0 real sy = 0 real sz = 0 real vx = 0 real vy = 0 real vz = 0 real time = 0 real duration = 0 endstruct function Conditions takes nothing returns boolean return GetSpellAbilityId() == ABILITY_RAWCODE endfunction function End takes unit u, integer level returns nothing local real x = GetUnitX(u) local real y = GetUnitY(u) call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl", x, y)) endfunction function Timer takes nothing returns nothing local flamingarrowdata dat local integer i = 0 local integer aoa = 0 local real z = 0 loop exitwhen i >= FlamingArrowTotal set dat = FlamingArrowArray[i] set dat.time = dat.time + TIMER_INTERVAL set z = GetZ(GetUnitX(dat.missile), GetUnitY(dat.missile)) //Move missile call SetUnitX(dat.missile, dat.sx + dat.vx * dat.time) call SetUnitY(dat.missile, dat.sy + dat.vy * dat.time) //Adjust height set z = dat.sz + dat.vz * dat.time - MISSILE_GRAVITY * dat.time * dat.time / 2 - z call BJDebugMsg(R2S(z)) //Z is coming out in negative figures?? call SetUnitFlyHeight(dat.missile, z, 0) //Adjust angle/facing set aoa = R2I(Atan2BJ(dat.vz -MISSILE_GRAVITY * dat.time, SquareRoot(dat.vx * dat.vx + dat.vy * dat.vy))) + 90 call SetUnitAnimationByIndex(dat.missile, aoa) //Has missile has reached its destination? if dat.time >= dat.duration then //Add an explosion call End(dat.missile, dat.level) //Destroy everything call DestroyEffect(dat.sfx) call KillUnit(dat.missile) set FlamingArrowTotal = FlamingArrowTotal - 1 set FlamingArrowArray[i] = FlamingArrowArray[FlamingArrowTotal] call dat.destroy() set i = i - 1 endif set i = i + 1 endloop if FlamingArrowTotal==0 then call ReleaseTimer(GetExpiredTimer()) endif endfunction function Actions takes nothing returns nothing local flamingarrowdata dat = flamingarrowdata.create() local unit u = GetTriggerUnit() local location l = GetSpellTargetLoc() local real dx = GetLocationX(l) - GetUnitX(u) local real dy = GetLocationY(l) - GetUnitY(u) local real dz = GetLocationZ(l) - (GetUnitZ(u) + 100) local real dist = SquareRoot(dx * dx + dy + dy) //Minimum distance if dist < MISSILE_MINDIST then set dx = dx * MISSILE_MINDIST / dist set dy = dy * MISSILE_MINDIST / dist set dist = MISSILE_MINDIST endi //Setup structs set dat.missile = CreateUnit(GetOwningPlayer(u), 'hmil', GetUnitX(u), GetUnitY(u), Atan2BJ(dy, dx)) set dat.sfx = AddSpecialEffectTarget(GetAbilityEffectById(ABILITY_RAWCODE, EFFECT_TYPE_MISSILE, 1), dat.missile, "origin") set dat.level = GetUnitAbilityLevel(u, ABILITY_RAWCODE) set dat.duration = dist / MISSILE_SPEED set dat.sx = GetUnitX(u) set dat.sy = GetUnitY(u) set dat.sz = GetUnitZ(u) set dat.vx = dx / dat.duration set dat.vy = dy / dat.duration set dat.vz = dz / dat.duration + MISSILE_GRAVITY * dat.duration / 2 //The Missile is shot/launched from 100 above the caster call SetUnitFlyHeight(dat.missile, 100, 0) if FlamingArrowTotal == 0 then call TimerStart(NewTimer(), TIMER_INTERVAL, true, function Timer) endif set FlamingArrowArray[FlamingArrowTotal] = dat set FlamingArrowTotal = FlamingArrowTotal + 1 call RemoveLocation(l) set u = null endfunction //=========================================================================== function InitTrig_Flaming_Arrow takes nothing returns nothing local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST) call TriggerAddCondition(t, Condition(function Conditions)) call TriggerAddAction(t, function Actions) endfunction |
| 08-16-2008, 08:21 AM | #2 |
Well one thing I'd suggest trying is put JASS://Adjust height set z = dat.sz + dat.vz * dat.time - MISSILE_GRAVITY * dat.time * dat.time / 2 - z in brackets, so: JASS://Adjust height set z = (dat.sz + dat.vz * dat.time - MISSILE_GRAVITY * dat.time * dat.time) / (2 - z) Not sure if it still follows those stupid BODMAS rules though, (Brackets, Of, Division, Mulitplication etc) |
| 08-16-2008, 08:35 AM | #3 |
Ok, I tried that but still getting negative Z's. |
| 08-16-2008, 08:52 AM | #4 |
set z = GetZ(GetUnitX(dat.missile), GetUnitY(dat.missile))
By any chance are you working on the cliff height 2? That's got a Z of 128 as far as I know. |
| 08-16-2008, 09:16 AM | #5 |
Oh thats a function I use, perhaps I should have included it: JASS:function GetZ takes real x, real y returns real local location l = Location(x, y) local real z = GetLocationZ(l) call RemoveLocation(l) return z endfunction function GetUnitZ takes unit u returns real local real x = GetUnitX(u) local real y = GetUnitY(u) local real z = GetZ(x, y) + GetUnitFlyHeight(u) return z endfunction I just did a test incase, when casting an ability: JASS:call BJDebugMsg(R2S(GetZ(GetUnitX(u),GetUnitY(u)))) call BJDebugMsg(R2S(GetUnitZ(u))) Both return 0, so seems to be ok. |
| 08-16-2008, 10:10 AM | #6 | |
Quote:
That may leak. JASS:function GetZ takes real x, real y returns real local location l = Location(x, y) local real z = GetLocationZ(l) call RemoveLocation(l) set l = null return z endfunction function GetUnitZ takes unit u returns real local real x = GetUnitX(u) local real y = GetUnitY(u) local real z = GetZ(x, y) + GetUnitFlyHeight(u) return z endfunction More optimized version. vJass:library zlib initializer init // not a compression library! globals private location loc = null endglobals private function init takes nothing returns nothing set loc = Location(0,0) endfunction function GetZ takes real x, real y returns real call MoveLocation(loc, x, y) return GetLocationZ(loc) endfunction function GetUnitZ takes unit u returns real return GetZ(GetUnitX(u),GetUnitY(u)) + GetUnitFlyHeight(u) endfunction endlibrary Well, I suggest you to avoid locals. |
| 08-16-2008, 11:18 AM | #7 |
Why dont you use this? JASS:globals private location l = Location(0.0,0.0) endglobals function GetZ takes real x, real y returns real call MoveLocation(l,x,y) return GetLocationZ(l) endfunction EDiT. ey, sorry for useless post, I just read previous post. |
| 08-16-2008, 11:19 AM | #8 | |
Quote:
Fulla: first thing I noticed, you should fix this: set dat.sz = GetUnitZ(u)+100 Fix it and report back. Also, you should finally change that 100 into a PROJECTILE_LAUNCH_Z constant, it's not just so the spell is more calibratable, it also makes it easier to understand wtf is going on in the code for someone first reading it. Also, you really need to put it all in a scope and make the functions properly private. |
| 08-16-2008, 11:25 AM | #9 | |
Quote:
My apologies to both you and Fulla, but I misread it. Also, while you're angered at me... does BODMAS still apply in Jass? |
| 08-16-2008, 01:22 PM | #10 |
Ok, fixed all the reported bugs. Still same problems but a little better. The arc works partially, but the projectile never goes up, just immediately descends (I even tried setting gravity to 10 down from 400, little difference). I've tried making it a scope but then the spell completely stops working, I'll need to readup more in this. Then I can also get rid of the data arrays/totals. === Also I've been thinking: - Since I'm now moving the projectile via velocity, couldn't time / duration be removed completely? - Instead simply do a projectile Z to ground Z check, stopping projectile when ground is higher. Would make things easier? New Code: JASS:globals flamingarrowdata array FlamingArrowArray integer FlamingArrowTotal = 0 integer ABILITY_RAWCODE = 'A002' real TIMER_INTERVAL = 0.03 real MISSILE_LAUNCHZ = 100 real MISSILE_SPEED = 900 real MISSILE_GRAVITY = 10 real MISSILE_MINDIST = 400 real MISSILE_DAMAGE = 100 endglobals struct flamingarrowdata unit missile = null effect sfx = null integer level = 0 real sx = 0 real sy = 0 real sz = 0 real vx = 0 real vy = 0 real vz = 0 real time = 0 real duration = 0 endstruct function Conditions takes nothing returns boolean return GetSpellAbilityId() == ABILITY_RAWCODE endfunction function End takes unit u, integer level returns nothing local real x = GetUnitX(u) local real y = GetUnitY(u) call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl", x, y)) endfunction function Timer takes nothing returns nothing local flamingarrowdata dat local integer i = 0 local integer aoa = 0 local real z = 0 loop exitwhen i >= FlamingArrowTotal set dat = FlamingArrowArray[i] set dat.time = dat.time + TIMER_INTERVAL set z = GetZ(GetUnitX(dat.missile), GetUnitY(dat.missile)) //Move missile call SetUnitX(dat.missile, dat.sx + dat.vx * dat.time) call SetUnitY(dat.missile, dat.sy + dat.vy * dat.time) //Adjust height set z = dat.sz + dat.vz * dat.time - MISSILE_GRAVITY * dat.time * dat.time / 2 - z call BJDebugMsg(R2S(z)) //Z is coming out in negative figures?? call SetUnitFlyHeight(dat.missile, z, 0) //Adjust angle/facing set aoa = R2I(Atan2BJ(dat.vz -MISSILE_GRAVITY * dat.time, SquareRoot(dat.vx * dat.vx + dat.vy * dat.vy))) + 90 call SetUnitAnimationByIndex(dat.missile, aoa) //Has missile has reached its destination? if dat.time >= dat.duration then //Add an explosion call End(dat.missile, dat.level) //Destroy everything call DestroyEffect(dat.sfx) call KillUnit(dat.missile) set FlamingArrowTotal = FlamingArrowTotal - 1 set FlamingArrowArray[i] = FlamingArrowArray[FlamingArrowTotal] call dat.destroy() set i = i - 1 endif set i = i + 1 endloop if FlamingArrowTotal==0 then call ReleaseTimer(GetExpiredTimer()) endif endfunction function Actions takes nothing returns nothing local flamingarrowdata dat = flamingarrowdata.create() local unit u = GetTriggerUnit() local location l = GetSpellTargetLoc() local real dx = GetLocationX(l) - GetUnitX(u) local real dy = GetLocationY(l) - GetUnitY(u) local real dz = GetLocationZ(l) - (GetUnitZ(u) + MISSILE_LAUNCHZ) local real dist = SquareRoot(dx * dx + dy + dy) //Minimum distance if dist < MISSILE_MINDIST then set dx = dx * MISSILE_MINDIST / dist set dy = dy * MISSILE_MINDIST / dist set dist = MISSILE_MINDIST endif //Setup structs set dat.missile = CreateUnit(GetOwningPlayer(u), 'hmil', GetUnitX(u), GetUnitY(u), Atan2BJ(dy, dx)) set dat.sfx = AddSpecialEffectTarget(GetAbilityEffectById(ABILITY_RAWCODE, EFFECT_TYPE_MISSILE, 1), dat.missile, "origin") set dat.level = GetUnitAbilityLevel(u, ABILITY_RAWCODE) set dat.duration = dist / MISSILE_SPEED set dat.sx = GetUnitX(u) set dat.sy = GetUnitY(u) set dat.sz = GetUnitZ(u) + MISSILE_LAUNCHZ set dat.vx = dx / dat.duration set dat.vy = dy / dat.duration set dat.vz = dz / dat.duration + MISSILE_GRAVITY * dat.duration / 2 //The Missile is shot/launched from 100 above the caster call SetUnitFlyHeight(dat.missile, 100, 0) if FlamingArrowTotal == 0 then call TimerStart(NewTimer(), TIMER_INTERVAL, true, function Timer) endif set FlamingArrowArray[FlamingArrowTotal] = dat set FlamingArrowTotal = FlamingArrowTotal + 1 call RemoveLocation(l) set u = null endfunction //=========================================================================== function InitTrig_FlamingArrow takes nothing returns nothing set Trig = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(Trig, EVENT_PLAYER_UNIT_SPELL_CAST) call TriggerAddCondition(Trig, Condition(function Conditions)) call TriggerAddAction(Trig, function Actions) endfunction EDIT: Uploaded map just incase. |
| 08-16-2008, 01:34 PM | #11 | |
Just a suggestion, but maybe you could use a create method in your struct to clean up the code in actions? A large chunk of it seems to be just setting struct members. It won't solve your problem, but at least your code will look a little neater :D Quote:
scope Blah initializer Init Where Init is a function that creates your trigger (basically just make the "InitTrig_TriggerName" function into a private and rename it). OR, make the InitTrig_TriggerName function (the one Wc3 makes by default) a public function and name it "InitTrig". Finally, make sure your scope name and the name of the trigger match exactly and that there's no spaces. And don't forget to make all your globals/functions in that scope private. EXAMPLE: JASS:scope Hax initializer Init globals private real blah = 2.4 private constant integer TEST_INTEGER = 3 private constant integer ABILITY_ID = 'A001' endglobals private function Conditions takes nothing returns boolean return GetSpellAbilityId() == 'A001' endfunction private function Actions takes nothing returns nothing local unit u = GetTriggerUnit() call KillUnit(u) set u = null endfunction private function Init takes nothing returns nothing local trigger t = CreateTrigger call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction( t, function Actions ) endfunction endscope Tada! |
| 08-16-2008, 02:10 PM | #12 | |||
Quote:
Quote:
Quote:
|
| 08-16-2008, 03:02 PM | #14 |
Change your gravity to something between 1000 and 2000 and fix this line: local real dist = SquareRoot(dx*dx + dy*dy) |
| 08-16-2008, 03:18 PM | #15 |
Works perfectly now :) Haha, what a silly mistake. Thx alot for your help. Now I'll attempt to fully vjass/scope it. I edited the post above, probably as you replied but just wanted to ask: If we periodically get the current projectile x/y/z AND the stored Velocity x/y/z AND the constant Gravity, would that not be sufficient to move a projectile & halt upon wall/ground collision? |
