| 01-11-2009, 09:24 AM | #1 |
Hey i got a strange problem in a Novaspell i made, i get an "unable to allocate id for struct"-error when i use following nova struct: JASS:private struct Nova unit caster unit array targets[UNIT_MAX] group g integer sizeofg real timerdata static method create takes unit u returns Nova local Nova n=.allocate() local integer i=0 set n.caster=u set n.g=CreateGroup() set n.sizeofg=0 set n.timerdata=0 loop exitwhen i>UNIT_MAX set n.targets[i]=null set i=i+1 endloop return n endmethod private method onDestroy takes nothing returns nothing local integer i=0 call DestroyGroup(.g) loop exitwhen i>.sizeofg set .targets[i]=null set i=i+1 endloop set .caster=null set .g=null endmethod endstruct Full Spell: JASS:scope Nova initializer Init //// Nova Spell by Akolyt0r /// Requirements: JassNewGen,TimerUtils globals private constant integer spellid = 'A000' //Nova (Hero) Ability Rawcode private constant integer dummyid = 'u000' //Dummy Unit Rawcode private constant integer fxspellid = 'A001' //Custom Ability Rawcode (some shockwave like ability only for eyecandy) private constant integer fxspellradius = 575 private constant integer fxstartdistbonus = 125 private constant integer fxmissilespeed = 200 private constant real TIME_OUT = 0.05 private constant integer UNIT_MAX = 8190 endglobals private function theDamage takes integer level returns real return I2R(level*50) endfunction //// private struct Nova unit caster unit array targets[UNIT_MAX] group g integer sizeofg real timerdata static method create takes unit u returns Nova local Nova n=.allocate() local integer i=0 set n.caster=u set n.g=CreateGroup() set n.sizeofg=0 set n.timerdata=0 loop exitwhen i>UNIT_MAX set n.targets[i]=null set i=i+1 endloop return n endmethod private method onDestroy takes nothing returns nothing local integer i=0 call DestroyGroup(.g) loop exitwhen i>.sizeofg set .targets[i]=null set i=i+1 endloop set .caster=null set .g=null endmethod endstruct globals private Nova TMP endglobals private function Conditions takes nothing returns boolean return GetSpellAbilityId()==spellid endfunction private function NovaTimerCallback takes nothing returns nothing local Nova n=GetTimerData(GetExpiredTimer()) local integer i=0 local real dx = 0 local real dy = 0 local real dist = 0 set n.timerdata=n.timerdata+TIME_OUT loop exitwhen i>n.sizeofg if(n.targets[i]!=null)then set dx = GetUnitX(n.targets[i]) - GetUnitX(n.caster) set dy = GetUnitY(n.targets[i]) - GetUnitY(n.caster) set dist = SquareRoot(dx * dx + dy * dy) if(dist<fxspellradius and dist<fxstartdistbonus+n.timerdata*fxmissilespeed)then call UnitDamageTarget(n.caster,n.targets[i],50*I2R(GetUnitAbilityLevel(n.caster,spellid)),true,true,ATTACK_TYPE_NORMAL ,DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS) set n.targets[i]=null endif endif set i=i+1 endloop endfunction private function EnumUnitsInArray takes nothing returns nothing local Nova n=TMP local unit u=GetEnumUnit() if (IsPlayerEnemy(GetOwningPlayer(u),GetOwningPlayer(n.caster))) then set n.targets[n.sizeofg]=u set n.sizeofg=n.sizeofg+1 endif set u=null endfunction private function Actions takes nothing returns nothing local Nova n=Nova.create(GetTriggerUnit()) local player p=GetOwningPlayer(n.caster) local real cx=GetUnitX(n.caster) local real cy=GetUnitY(n.caster) local integer i=0 local unit dummy local timer t=NewTimer() call GroupEnumUnitsInRange(n.g,cx,cy,fxspellradius,Condition(function True)) set TMP=n call ForGroup(n.g,function EnumUnitsInArray) loop exitwhen i>360 set dummy=CreateUnit(p,dummyid,cx,cy,0) call UnitApplyTimedLife(dummy,'BTLF',0.5) call IssuePointOrder(dummy,"breathoffire",cx+100*Cos(i*bj_DEGTORAD),cy+100*Sin(i*bj_DEGTORAD)) set i=i+30 endloop call SetTimerData(t, n) call TimerStart(t,TIME_OUT,true,function NovaTimerCallback) call PolledWait2(fxspellradius/fxmissilespeed+0.5) //cleanup call PauseTimer(t) call ReleaseTimer(t) call n.destroy() set t=null set dummy=null set p=null endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddAction(t,function Actions ) call TriggerAddCondition( t,Condition(function Conditions)) endfunction endscope The strange thing about is, the spell itself works as it should but i always get thos struct errors ..."error to allocate" and "attempt to destry a null struct" .. Another question regarding the spell, i use a global to get the struct data in the EnumUnitsInArray function (a ForGroup callback), ...should i use some attachment system to do that, or is that ok as is (which sure would be worse performance wise, but safer) ? And before you say something...yeah i will move the unitfilter (which is currently in the TimerCallback) to the groupenum ..some day |
| 01-11-2009, 09:49 AM | #2 |
It's Nova.allocate not .allocate. |
| 01-11-2009, 10:02 AM | #3 | |
Quote:
I didin't look over the system, but generaly it isin't a poor practice to do. The exceptions are things you know are going to be being called very rapidly. In almost all cases it's acceptable, unless a wait or timer is involved. If there's one of thoes, attach the variables to the timer or some other method. Reguarding the actual problem, Vestras is right. Static methods are outside the scope of the struct and it's variables. It's best to treat Static methods as functions, essentialy that's what they are. |
| 01-11-2009, 11:11 AM | #4 | ||
Quote:
No, it doesnt matter, both "would" work, but doesnt 0.o Quote:
Hmm, currently i have the problem, that units which are at cast time out of nova range, but step into nova range while missile is flying, and which are being hit by the missile arent effected by the Nova. To Fix this i would have to EnumTheUnits each TIME_OUT=0.05 seconds, and then start a ForGroup Call for them. Is it still arguable to use a global in a timer callback each 0.05 seconds to get the struct in the ForGroupCallback ? JASS://EXAMPLE private function DamageUnitsInArray takes nothing returns nothing local Nova n=TMP local unit u=GetEnumUnit() local real dist=GetUnitDistance(u,n.caster) set n.timerdata=n.timerdata+TIME_OUT if(dist<fxspellradius and dist<fxstartdistbonus+n.timerdata*fxmissilespeed)then //check if unit would be hit by missile call UnitDamageTarget(n.caster,u,50*I2R(GetUnitAbilityLevel(n.caster,spellid)),true,true,ATTACK_TYPE_NORMAL ,DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS) call GroupRemoveUnit(n.g,u) endif set u=null endfunction private function NovaTimerCallback takes nothing returns nothing local Nova n=GetTimerData(GetExpiredTimer()) call GroupEnumUnitsInRange(n.g,cx,cy,fxspellradius,Condition(function SomeFilter)) set TMP=n call ForGroup(n.g,function DamageUnitsInArray) endfunction private function NovaActions takes nothing returns nothing local Nova n=Nova.create(GetTriggerUnit()) local timer t=NewTimer() //do nova effects call SetTimerData(t, n) call TimerStart(t,0.05,true,function NovaTimerCallback) endfunction |
| 01-11-2009, 01:06 PM | #5 |
First off: What is your UNIT_MAX? Struct instance limits are severely hampered by the use of arrays. Instance limit becomes: 8100 / Highest array limit. Use a HandleTable if you are going to store units (or even a group if it refers to each instance). Otherwise, use a global group and damage the units inside your filter. EDIT: If you have a lot of struct members to reference in the callback, use a temporary instance. If you have two or three struct members to reference, use individual globals. |
| 01-11-2009, 01:33 PM | #6 |
yeah just noticed that it works when i reduce UNIT_MAX to a reasonable ammount like 200 :> Pretty unlikely that there are 8190 units in a area with a radius of 575 ^^ EDIT: Totally rewrote it ..no arrays anymore..now i have GroupEnums every TIME_OUT, but since TIME_OUT=0.15 still looks good thats ok. JASS:scope Nova initializer Init //// Nova Spell by Akolyt0r /// Requirements: JassNewGen,TimerUtils globals private constant integer spellid = 'A000' //Nova (Hero) Ability Rawcode private constant integer dummyid = 'u000' //Dummy Unit Rawcode private constant integer fxspellid = 'A001' //Custom Ability Rawcode private constant integer fxspellradius = 575 //AoE Range of Custom Wave Ability (maybe add some more) private constant integer fxstartdistbonus = 100 //blah debug for fine adjustment private constant integer fxmissilespeed = 225 //Missile Speed of Custom Wave Ability private constant real TIME_OUT = 0.15 // endglobals private function theDamage takes integer level returns real return I2R(level*50) endfunction private function NovaEffectFilter takes unit u, unit caster returns boolean return (IsPlayerEnemy(GetOwningPlayer(u),GetOwningPlayer(caster))and(GetUnitTypeId(u)!=dummyid) ) endfunction //// Do Not Edit Below //// //// Do Not Edit Below //// private struct Nova unit caster group g group alreadyHit real totalElapsed real cx real cy static method create takes unit u returns Nova local Nova n=Nova.allocate() set n.caster=u set n.cx=GetUnitX(u) set n.cy=GetUnitY(u) set n.g=CreateGroup() set n.alreadyHit=CreateGroup() set n.totalElapsed=0 return n endmethod private method onDestroy takes nothing returns nothing call DestroyGroup(.g) call DestroyGroup(.alreadyHit) set .caster=null set .g=null endmethod endstruct globals private Nova TMP endglobals private function Conditions takes nothing returns boolean return GetSpellAbilityId()==spellid endfunction private function NovaEffectCallback takes nothing returns nothing local Nova n=TMP local unit u=GetEnumUnit() local real dist = 0 set dist = GetUnitToLocXYDistance(u,n.cx,n.cy) if(NovaEffectFilter(u,n.caster)and dist<fxspellradius and dist<fxstartdistbonus+n.totalElapsed*fxmissilespeed)and(IsUnitInGroup(u,n.alreadyHit)==false)then call GroupAddUnit(n.alreadyHit,u) call UnitDamageTarget(n.caster,u,theDamage(GetUnitAbilityLevel(n.caster,spellid)),true,true,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,WEAPON_TYPE_WHOKNOWS) endif set u=null endfunction private function NovaTimerCallback takes nothing returns nothing local Nova n=GetTimerData(GetExpiredTimer()) set n.totalElapsed=n.totalElapsed+TIME_OUT call GroupEnumUnitsInRange(n.g,n.cx,n.cy,fxspellradius,Condition(function True)) set TMP=n // call ForGroup(n.g,function NovaEffectCallback) call GroupClear(n.g) endfunction private function Actions takes nothing returns nothing local Nova n=Nova.create(GetTriggerUnit()) local player p=GetOwningPlayer(n.caster) local integer i=0 local unit dummy local timer t=NewTimer() loop exitwhen i>360 set dummy=CreateUnit(p,dummyid,n.cx,n.cy,0) call UnitApplyTimedLife(dummy,'BTLF',0.3) call IssuePointOrder(dummy,"breathoffire",n.cx+100*Cos(i*bj_DEGTORAD),n.cy+100*Sin(i*bj_DEGTORAD)) set i=i+30 endloop // call SetTimerData(t, n) call TimerStart(t,TIME_OUT,true,function NovaTimerCallback) // call PolledWait2(fxspellradius/fxmissilespeed+2*TIME_OUT) //cleanup call ReleaseTimer(t) call n.destroy() set t=null set dummy=null set p=null endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddAction(t,function Actions ) call TriggerAddCondition( t,Condition(function Conditions)) endfunction endscope |
