| 08-06-2008, 04:37 PM | #1 |
Darkwulfv again. It's always me who has the problems, isn't it? Anyways, I'm working on a new spell; ignition. It's a bunch of stuff, but long story short the caster is supposed to charge a target. Sounds easy, right? Weeelll.. Not for me. This is entirely new to me, so of course, it screws up. Now the caster moves, oh yeah. Looks great, loving it. Problem is, he moves in the wrong direction. I believe this has everything to do with my trigonometry, but I don't know how to do it properly for this situation. Here's the whole ugly code (it's unfinished. I'm more concerned with it working than it working well). I'll pick out parts that I believe are causing the issues, but you can pick through the whole code if you so wish. JASS:scope Ignition initializer Init //add constants for SFX units + comment others globals private constant integer ABILITY_ID = 'A000' private constant integer IMMOLATE_ID = 'A001' private constant integer TRAIL_DUMMY = 'e000' //How many timer ticks per created flame private constant integer FLAME_INTERVAL = 2 //How many flame bursts created private constant integer FLAME_BURSTS = 1 //How many gut explosions created private constant integer GUT_EXPLOSIONS = 5 private constant integer FLAME_BURST_ID = 'e002' private constant integer GUT_EXPLOSION_ID = 'e001' //Red value of text private constant integer TEXT_RED = 255 //Green value of text private constant integer TEXT_GREEN = 64 //Blue value of text private constant integer TEXT_BLUE = 0 //Size of text private constant integer TEXT_SIZE = 12 private constant real TIMER_INTERVAL = .035 //The speed at which the run animation plays. //It's in %, so 150 is 50% faster, and so on. private constant real ANIMATION_SPEED = 150. //Base damage, modified later in "Damage" function. //How far the caster moves per timer tick private constant real SPEED = 5. //Heh, it rhymes. Anyways, this is the precise duration of ATTACK_ANIMATION. private constant real ANIMATION_DURATION = 1.133 //How long the trail flames last private constant real TRAIL_DURATION = 3 //Duration of text private constant real TEXT_DURATION = 4. private constant string RUN_ANIMATION = "Walk" private constant string ATTACK_ANIMATION = "Attack Slam" //Fire attached to the caster private constant string MOVE_FIRE = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl" //Fire left behind the caster private constant string TRAIL_FIRE = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl" //Fire made when target explodes private constant string BURST_FIRE = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl" //Guts made when target explodes private constant string BURST_GUTS = "Objects\\Spawnmodels\\Orc\\OrcSmallDeathExplode\\OrcSmallDeathExplode.mdl" //Attachment points private constant string ATTACH_1 = "hand left" private constant string ATTACH_2 = "hand right" private constant string ATTACH_3 = "chest" private constant string ATTACH_4 = "weapon" private constant string ATTACH_5 = "origin" //These are the messages shown during the spell. Set them to "" if you don't want any messages shown. private constant string CAST_MESSAGE = "Ignite..." private constant string MIDDLE_MESSAGE = "And..." private constant string END_MESSAGE = "BURN!!" private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL private constant weapontype WEAPON_TYPE = null endglobals private constant function Damage takes integer level returns real return 200. * level endfunction private constant function AOEDamage takes integer level returns real return 50. + (level * 50) endfunction private constant function Radius takes integer level returns real return 200. + (level * 25) endfunction //No touching from here on out! globals private gamecache GCache = null endglobals struct IgnitionData unit caster = null unit target = null integer level = 0 integer flametick = 0 integer ticks = 0 integer maxticks = 0 real angle = 0 real movedistanceX = 0 real movedistanceY = 0 effect array SFX[8191] static method create takes unit caster, unit target, integer level, real angle, real distance returns IgnitionData local IgnitionData D = IgnitionData.allocate() set D.caster = caster set D.target = target set D.level = level set D.angle = angle set D.movedistanceX = SPEED * Cos(angle) set D.movedistanceY = SPEED * Sin(angle) set D.maxticks = R2I(distance / SPEED) return D endmethod endstruct private function Knockback takes unit target, real angle returns nothing call SetUnitPosition(target, GetUnitX(target) + 50 * Cos(angle), GetUnitY(target) + 50 * Sin(angle)) endfunction private function knockfilter takes nothing returns boolean local unit u = GetFilterUnit() local boolean b1 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false local boolean b2 = IsUnitType(u, UNIT_TYPE_FLYING) == false local boolean b3 = IsUnitType(u, UNIT_TYPE_DEAD) == false set u = null return b1 and b2 and b3 endfunction private function filter takes nothing returns boolean local unit u = GetFilterUnit() local boolean b1 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false local boolean b2 = IsUnitType(u, UNIT_TYPE_FLYING) == false local boolean b3 = IsUnitType(u, UNIT_TYPE_DEAD) == false local boolean b4 = IsUnitEnemy(u, GetOwningPlayer(GetTriggerUnit())) local boolean b5 = GetUnitAbilityLevel(u, 'Avul') < 1 set u = null return b1 and b2 and b3 and b4 and b5 endfunction private function DamageFunc takes nothing returns nothing local timer t = GetExpiredTimer() local IgnitionData Data = GetStoredInteger(GCache, "GCache", "IgnitionDat") local unit u = Data.caster local unit u2 = Data.target local integer lvl = Data.level local real x = GetUnitX(u2) local real y = GetUnitY(u2) local group g = CreateGroup() local integer i = 1 local unit f local player p = GetOwningPlayer(u) call SetUnitAnimation(u, "stand") call UnitDamageTarget(u, u2, Damage(lvl), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) if Damage(lvl) > GetWidgetLife(u2) then loop exitwhen i > FLAME_BURSTS call UnitApplyTimedLife(CreateUnit(p, FLAME_BURST_ID, x, y, 270.), 'BTLF', i * .3) set i = i + 1 endloop set i = 1 loop exitwhen i > GUT_EXPLOSIONS call UnitApplyTimedLife(CreateUnit(p, GUT_EXPLOSION_ID, x, y, 270.), 'BTLF', i * .3) set i = i + 1 endloop call GroupEnumUnitsInRange(g, x, y, Radius(lvl), Condition(function filter)) loop set f = FirstOfGroup(g) exitwhen f == null call UnitDamageTarget(u, f, AOEDamage(lvl), false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) call GroupRemoveUnit(g, f) endloop endif call PauseTimer(t) call DestroyTimer(t) call DestroyGroup(g) call Data.destroy() set t = null set u = null set u2 = null set g = null set f = null set p = null endfunction private function Final takes IgnitionData dat returns nothing local unit caster = dat.caster local integer i = 1 call SetUnitAnimation(caster, ATTACK_ANIMATION) call SetUnitTimeScale(caster, 100 * 0.01) call TimerStart(CreateTimer(), ANIMATION_DURATION, false, function DamageFunc) loop exitwhen i > 6 call DestroyEffect(dat.SFX[i]) set i = i + 1 endloop call PauseUnit(caster, false) call SetUnitInvulnerable(caster, false) if GetLocalPlayer() == GetOwningPlayer(caster) then call SelectUnit(caster, true) endif set caster = null endfunction private function Callback takes nothing returns nothing local timer t = GetExpiredTimer() local group g = CreateGroup() local unit f local IgnitionData Data = GetStoredInteger(GCache, "GCache", "IgnitionDat") local unit u = Data.caster local unit u2 = Data.target local real x = GetUnitX(u) local real y = GetUnitY(u) local real x2 = GetUnitX(u2) local real y2 = GetUnitY(u2) local real newx = 0 local real newy = 0 local real angle = Atan2(y2 - y, x2 - x) local integer lvl = Data.level //Make Flames if Data.flametick == FLAME_INTERVAL then call UnitApplyTimedLife(CreateUnit(GetOwningPlayer(u), TRAIL_DUMMY, x, y, 270.), 'BTLF', TRAIL_DURATION) set Data.flametick = 0 endif //Middle Text if Data.ticks == Data.maxticks / 2 then if MIDDLE_MESSAGE != "" then call CreateTextTagEX(MIDDLE_MESSAGE, TEXT_SIZE, u, TEXT_DURATION, TEXT_RED, TEXT_GREEN, TEXT_BLUE) endif endif //Checking if moving still possible if Data.ticks < Data.maxticks then set newx = x + (SPEED * Cos(angle)) set newy = y + (SPEED * Sin(angle)) call SetUnitPosition(u, newx, newy ) call GroupEnumUnitsInRange(g, newx, newy, 35., Condition(function knockfilter)) //Knockback units in way loop set f = FirstOfGroup(g) exitwhen f == null if Data.angle >= Atan2(GetUnitY(f) - newy, GetUnitX(f) - newx) then call Knockback(f, Data.angle - (bj_DEGTORAD * 90)) else call Knockback(f, Data.angle + (bj_DEGTORAD * 90)) endif call GroupRemoveUnit(g, f) endloop set Data.ticks = Data.ticks + 1 set Data.flametick = Data.flametick + 1 //End of spell else if END_MESSAGE != "" then call CreateTextTagEX(END_MESSAGE, TEXT_SIZE, u, TEXT_DURATION, TEXT_RED, TEXT_GREEN, TEXT_BLUE) endif call PauseTimer(t) call DestroyTimer(t) call Final(Data) endif call DestroyGroup(g) set t = null set u = null set g = null set f = null endfunction private function Conditions takes nothing returns boolean return ( GetSpellAbilityId() == ABILITY_ID ) endfunction private function Actions takes nothing returns nothing local unit u = GetTriggerUnit() local unit u2 = GetSpellTargetUnit() local integer lvl = GetUnitAbilityLevel(u, ABILITY_ID) local timer t = CreateTimer() local real x = GetUnitX(u) local real y = GetUnitY(u) local real x2 = GetUnitX(u2) local real y2 = GetUnitY(u2) local real dx = x2 - x local real dy = y2 - y local real distance = SquareRoot(dx * dx + dy * dy) local IgnitionData data = IgnitionData.create(u, u2, lvl, Atan2(y2 - y, x2 - x), distance) if CAST_MESSAGE != "" then call CreateTextTagEX(CAST_MESSAGE, TEXT_SIZE, u, TEXT_DURATION, TEXT_RED, TEXT_GREEN, TEXT_BLUE) endif set data.SFX[1] = AddSpecialEffectTarget(MOVE_FIRE, u, ATTACH_1) set data.SFX[2] = AddSpecialEffectTarget(MOVE_FIRE, u, ATTACH_2) set data.SFX[3] = AddSpecialEffectTarget(MOVE_FIRE, u, ATTACH_3) set data.SFX[4] = AddSpecialEffectTarget(MOVE_FIRE, u, ATTACH_4) set data.SFX[5] = AddSpecialEffectTarget(MOVE_FIRE, u, ATTACH_5) call UnitAddAbility(u, IMMOLATE_ID) call SetUnitAbilityLevel(u, IMMOLATE_ID, lvl) call PauseUnit(u, true) call SetUnitInvulnerable(u, true) if GetLocalPlayer() == GetOwningPlayer(u) then call ClearSelection() endif call PolledWait(.75) call SetUnitAnimation(u, RUN_ANIMATION) call SetUnitTimeScale(u, ANIMATION_SPEED * 0.01) call StoreInteger(GCache, "GCache", "IgnitionDat", data) call TimerStart(t, TIMER_INTERVAL, true, function Callback) set u = null set u2 = null set t = 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 ) call Preload(TRAIL_FIRE) call Preload(BURST_FIRE) call Preload(BURST_GUTS) call FlushGameCache(InitGameCache("GCache")) set GCache = InitGameCache("GCache") set t = null endfunction endscope And now the areas I deem problematic: JASS:local real x = GetUnitX(u) local real y = GetUnitY(u) local real x2 = GetUnitX(u2) local real y2 = GetUnitY(u2) local real angle = Atan2(y2 - y, x2 - x) set newx = x + (SPEED * Cos(angle)) set newy = y + (SPEED * Sin(angle)) local real distance = SquareRoot(dx * dx + dy * dy) local IgnitionData data = IgnitionData.create(u, u2, lvl, Atan2(y2 - y, x2 - x), distance) I hope you get what I mean. Now, in my test map, the caster targets a unit below him (not directly; about 45 degrees lower), and goes flying upwards at that angle instead of downwards. He also goes like, 15x longer than he should but I can probably figure that out on my own. I also have a question; What is the best way to determine that the caster is in the middle of the distance he is supposed to travel, and that he is at the point he is supposed to end at? Right now, I just divide the distance by the units-moved-per-interval and check it in "ticks" (eg: 100 distance / 5 speed = 20 ticks so 10 ticks would be middle, 20 would be end), but right now he moves like all over the map instead of just ~600 distance. He moves about 2000+. (My example probably doesn't work, which is probably why my actual situation doesn't either) I hope I've given enough information and that it's decently clear enough to work with. I know my code is disgusting, but I'm going to clean it and make it look pretty after I get the spell itself working. Note: I suck at trigonometry since I've been learning it crash-course style, so if this is something as simple as putting a - where a + is, let me know. Of course, let me know how to check that it should be a - instead of a + in accordance to the situation. Thanks for any help I get, and bear with me... this isn't my strong point. |
| 08-07-2008, 03:55 AM | #2 | ||
Quote:
A few other things:
Quote:
Dunno what's up with the angle. Posting the map would be a good idea. |
| 08-07-2008, 04:24 AM | #3 | |||||||
Wow, stuff to change. Uhh, yeah a few things are iffy (like the 100 * .01), but like i said I wanted this to work before it works well. Quote:
Sooo... That's exactly what I'm doing. I thought it was MUI though, guess not. Maybe I'll just stick to Handle Vars. Quote:
Quote:
Quote:
Quote:
Quote:
Quote:
|
| 08-07-2008, 04:38 AM | #4 | |
Quote:
|
