| 10-18-2008, 03:56 AM | #1 |
In my spell I am running a timer at an interval of .04 seconds. In the callback function, I want to detect every time that the current expired time is a whole number, so that I can change a stored number. Every time the timer function runs, I add .04 to the stored time, so that every 25 intervals the current time is a whole number, and I can do my little action. I've used I2R(R2I(current time)) to round off the number and check if the rounded value and the actual value are equivalent. However, in game, the condition is not recognizing when the current time is equivalent to an integer. Here is the timer function, I would appreciate some help. JASS:
static method align takes nothing returns nothing
local rage r=GetTimerData(GetExpiredTimer())
local real targx=GetUnitX(r.enraged)
local real targy=GetUnitY(r.enraged)
set r.current_time=r.current_time + ALIGN_INTERVAL
if r.current_time==I2R(R2I(r.current_time)) then
set r.rage_meter=r.rage_meter-1
endif
call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter)
call MoveLightning(r.light, false, r.castx, r.casty, targx, targy)
call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi))
if r.current_time==STABLE_THRESHOLD then
set r.finished=true
endif
if r.current_time==ENDTIME or r.rage_meter==0 then
call r.release()
endif
endmethod
|
| 10-18-2008, 05:37 AM | #2 |
Just use a counter integer variable and do a modulo check: JASS:struct rage //Whatever else you have integer count = 0 static method align takes nothing returns nothing local rage r=GetTimerData(GetExpiredTimer()) local real targx=GetUnitX(r.enraged) local real targy=GetUnitY(r.enraged) set r.current_time=r.current_time + ALIGN_INTERVAL set r.count = r.count+1 if ModuloInteger(r.count, 25) == 1 then set r.rage_meter=r.rage_meter-1 set r.count = 0 endif call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter) call MoveLightning(r.light, false, r.castx, r.casty, targx, targy) call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi)) if r.current_time==STABLE_THRESHOLD then set r.finished=true endif if r.current_time==ENDTIME or r.rage_meter==0 then call r.release() endif endmethod endstruct |
| 10-18-2008, 05:44 AM | #3 |
Reason why it probably isn't working is that floats are not 100% accurate and it may actually be slightly off and not exactly on. This is the nature of floating points. |
| 10-18-2008, 12:27 PM | #4 |
I've seen uses of Modulo for finding a remainder of zero, I thought that might be a good answer. Thank you Pyro and Ammorth, +rep . Eh Pyro, I can't give you more yet, I must "spread it around" a bit. You'll probably be helpful in the future, I don't doubt it :P. EDIT 2: Well apparently the modulus satisfies the condition no matter what, so after 10 intervals the rage meter is 0 and the spell cuts off... EDIT 3: I tried using BJDebugMsg and it turns out that because the dividend must be divided evenly by the divisor, it will always return 1 if r.count <= 25. Checking more... EDIT 4: Yep, the modulus has to be zero, I'm pretty sure it fully works now! thanks again. JASS:if ModuloInteger(r.count, 25) == 0 then set r.count=0 set r.rage_meter=r.rage_meter-1 endif |
| 10-18-2008, 05:35 PM | #5 |
Whoops. That's what I meant. |
| 10-20-2008, 07:42 PM | #6 |
I didnt want to start a new thread, so I'll just start this one up again. The spell works fine the first time it is cast, but any during subsequent times it is casted, the used struct instance is instantly destroyed twice for some reason. Here is the code, I will explain it a lil bit: JASS:// Uses TimerUtils and PUI scope InfernalRage globals private constant integer ABILITY_ID='A000' private constant integer BLOODLUST_ID='A001' private constant integer FLAME_EFFECTS_ID='A003' private constant integer CRIPPLE_ID='A002' private constant integer LUST_BUFF_ID='B000' private constant integer DUMMY_ID='e000' private constant string ON_KILL_EFFECTS="Objects\\Spawnmodels\\Orc\\OrcLargeDeathExplode\\OrcLargeDeathExplode.mdl" private constant string FLAME_EFFECTS="Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl" private constant string LUST_EFFECTS="Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl" private constant string CRIPPLE_EFFECTS="Abilities\\Spells\\Undead\\Cripple\\CrippleTarget.mdl" private constant string RELEASE_EFFECTS="Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl" private constant string LIGHTNING_MODEL="AFOD" private constant string LUST_ORDER="bloodlust" private constant string CRIPPLE_ORDER="cripple" private constant real TOTAL_DURATION=0 private constant real LIGHTNING_HEIGHT=75.00 private constant real LIGHTNING_HIDE_HEIGHT=2000.00 private constant real LUSTER_DURATION=2.00 private constant real ALIGN_INTERVAL=0.04 private constant real ENDTIME=20.00 private constant real STABLE_THRESHOLD=10.00 private constant real pi=bj_PI endglobals struct rage // a lightning stack - inspired by Vexorian's TimerUtils / CSSafety timer stack static lightning array light_array static integer light_index=0 static method newLight takes real x1, real y1, real x2, real y2 returns lightning if .light_index==0 then set .light_array[0]=AddLightningEx(LIGHTNING_MODEL, false, x1, y1, LIGHTNING_HEIGHT, x2, y2, LIGHTNING_HEIGHT) else set .light_index=.light_index-1 call MoveLightningEx(.light_array[.light_index], false, x1, y1, LIGHTNING_HEIGHT, x2, y2, LIGHTNING_HEIGHT) endif return .light_array[.light_index] endmethod static method releaseLight takes lightning l returns nothing call MoveLightningEx(l, false, 0, 0, LIGHTNING_HIDE_HEIGHT, 0, 0, LIGHTNING_HIDE_HEIGHT) set .light_array[.light_index]=l set .light_index=.light_index+1 endmethod //! runtextmacro PUI() unit caster unit enraged player enraged_owner lightning light integer rage_meter=10 real current_time=0 integer count=0 boolean finished=false boolean released=false real castx real casty timer align_timer static method align takes nothing returns nothing local rage r=GetTimerData(GetExpiredTimer()) local real targx=GetUnitX(r.enraged) local real targy=GetUnitY(r.enraged) set r.current_time=r.current_time + ALIGN_INTERVAL set r.count = r.count+1 if ModuloInteger(r.count, 25) == 0 then set r.count = 0 set r.rage_meter=r.rage_meter-1 endif call SetLightningColor(r.light, 1.,1.,1.,.1*r.rage_meter) call MoveLightning(r.light, false, r.castx, r.casty, targx, targy) call SetUnitFacing(r.caster, Atan2(targy-r.casty, targx-r.castx)*(180/pi)) if r.current_time==STABLE_THRESHOLD then set r.finished=true endif if r.current_time==ENDTIME or r.rage_meter==0 then set r.released=true call BJDebugMsg("Released either because of rage meter or time") call r.release() endif endmethod static method create takes unit cast, unit targ returns rage local rage r=rage.allocate() local player cast_owner=GetOwningPlayer(cast) local real targx=GetUnitX(targ) local real targy=GetUnitY(targ) local unit luster=CreateUnit(cast_owner, DUMMY_ID, targx, targy, 0) set r.caster=cast set r.castx=GetUnitX(cast) set r.casty=GetUnitY(cast) set r.enraged=targ set r.enraged_owner=GetOwningPlayer(targ) if GetLocalPlayer()==r.enraged_owner then call SelectUnit(targ, false) endif if r.enraged_owner!=cast_owner then call SetUnitOwner(targ, cast_owner, false) endif if GetLocalPlayer()==cast_owner then call SelectUnit(cast, false) call SelectUnit(targ, true) endif set r.light=.newLight(r.castx, r.casty, targx, targy) call UnitApplyTimedLife(luster, 'BTLF', LUSTER_DURATION) call UnitAddAbility(luster, BLOODLUST_ID) call IssueTargetOrder(luster, LUST_ORDER, targ) call UnitAddAbility(targ, FLAME_EFFECTS_ID) set r.align_timer=NewTimer() call TimerStart(r.align_timer, ALIGN_INTERVAL, true, function rage.align) set luster=null return r endmethod private method onDestroy takes nothing returns nothing local unit crippler local player cast_owner=GetOwningPlayer(this.caster) call BJDebugMsg(I2S(this)) call SetUnitPosition(this.caster, this.castx, this.casty) call SetUnitPosition(this.enraged, GetUnitX(this.enraged), GetUnitY(this.enraged)) call DestroyEffect(AddSpecialEffectTarget(RELEASE_EFFECTS, this.enraged, "origin")) call UnitRemoveAbility(this.enraged, LUST_BUFF_ID) call UnitRemoveAbility(this.enraged, FLAME_EFFECTS_ID) call ReleaseTimer(this.align_timer) call .releaseLight(this.light) if GetLocalPlayer()==cast_owner then call SelectUnit(this.caster, true) endif if this.enraged_owner!=cast_owner then if GetLocalPlayer()==cast_owner then call SelectUnit(this.enraged, false) endif call SetUnitOwner(this.enraged, this.enraged_owner, false) if GetLocalPlayer()==this.enraged_owner then call SelectUnit(this.enraged, true) endif endif if not this.finished then set crippler=CreateUnit(Player(15), DUMMY_ID, this.castx, this.casty, 0) call UnitAddAbility(crippler, CRIPPLE_ID) call UnitApplyTimedLife(crippler, 'BTLF', 2.0) call IssueTargetOrder(crippler, CRIPPLE_ORDER, this.caster) set crippler=CreateUnit(Player(15), DUMMY_ID, this.castx, this.casty, 0) call UnitAddAbility(crippler, CRIPPLE_ID) call UnitApplyTimedLife(crippler, 'BTLF', 2.0) call IssueTargetOrder(crippler, CRIPPLE_ORDER, this.enraged) endif set crippler=null endmethod private static method onKill takes nothing returns boolean local rage r if GetUnitAbilityLevel(GetKillingUnit(), LUST_BUFF_ID)>0 then set r=rage[GetKillingUnit()] call BJDebugMsg("Kill gotten by enraged unit") call DestroyEffect(AddSpecialEffect(ON_KILL_EFFECTS, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))) if r.rage_meter<10 then set r.rage_meter=r.rage_meter+1 endif endif return false endmethod private static method endCast takes nothing returns boolean local rage r if GetSpellAbilityId()==ABILITY_ID then set r=rage[GetTriggerUnit()] if not r.finished and not r.released then call BJDebugMsg("Released because of endcast") set r.released=true call r.release() endif endif return false endmethod private static method onDeath takes nothing returns boolean local rage r if GetUnitAbilityLevel(GetTriggerUnit(), FLAME_EFFECTS_ID)>0 then set r=rage[GetTriggerUnit()] call BJDebugMsg("Released because of death of enraged") set r.released=true call r.release() endif return false endmethod private static method spellExecute takes nothing returns boolean local rage r if GetSpellAbilityId()==ABILITY_ID then set r=rage.create(GetTriggerUnit(), GetSpellTargetUnit()) set rage[r.caster]=r set rage[r.enraged]=r call SetTimerData(r.align_timer, r) endif return false endmethod private static method onInit takes nothing returns nothing local unit u=CreateUnit(Player(15), DUMMY_ID, 0, 0, 0) local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(t, Condition(function rage.spellExecute)) set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH) call TriggerAddCondition(t, Condition(function rage.onKill)) set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH) call TriggerAddCondition(t, Condition(function rage.onDeath)) set t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST) call TriggerAddCondition(t, Condition(function rage.endCast)) call UnitAddAbility(u, ABILITY_ID) call UnitAddAbility(u, BLOODLUST_ID) call UnitAddAbility(u, FLAME_EFFECTS_ID) call UnitRemoveAbility(u, BLOODLUST_ID) call UnitRemoveAbility(u, ABILITY_ID) call UnitRemoveAbility(u, FLAME_EFFECTS_ID) call DestroyEffect(AddSpecialEffectTarget(RELEASE_EFFECTS, u, "origin")) call DestroyEffect(AddSpecialEffectTarget(ON_KILL_EFFECTS, u, "origin")) call DestroyEffect(AddSpecialEffectTarget(FLAME_EFFECTS, u, "origin")) call DestroyEffect(AddSpecialEffectTarget(LUST_EFFECTS, u, "origin")) call DestroyEffect(AddSpecialEffectTarget(CRIPPLE_EFFECTS, u, "origin")) set t=CreateTrigger() call RemoveUnit(u) set u=null endmethod endstruct endscope Basically, this spell will carry out an action and add bloodlust to a target unit. That unit is under the control of the caster for a certain amount of time. I have several conditions that will cut this link: If the enraged (targeted) unit dies, the spell is ended. If the caster stops casting (voluntary, dies, stunned, etc) then that also ends the spell and destroys the struct. If the "rage meter" equals zero, then the spell is also ended. So the first time the spell is cast, it works just like I need it. But the second time, the struct is destroyed instantly after casting for some reason. I tested it to try and find the source but all I know is that the first struct instance (1) is destroyed twice consecutively, causing double-free's abound and then directly causing the spell to end again because the caster's position is reset and the conditions are out of whack. Can anyone help me out with this? EDIT: It's coming along, I'm now starting the timer after attaching data, that seems to have helped greatly. But the struct is still getting destroyed, and the create method is looping every 10 seconds. EDIT 2: I fixed it. It had something to do with PUI's struct attachment, so I just reverted to using it's GetUnitIndex() and storing it in a hashtable. |
