| 01-26-2010, 12:18 AM | #1 |
Ok I have two spells with different effects but with the same method on picking units and checking if a unit group is not empty. Basically I did this for picking the first unit in a unit group: Zinc:unit u,tmp = null; GroupEnumUnitsInRange(g,x,y,135.0,BOOLEXPR_TRUE); for(u = FirstOfGroup(g) ; u != null ; u = FirstOfGroup(g)){ if(IsUnitEnemy(u,GetOwningPlayer(d.c)) && GetWidgetLife(u) > 0.405 && ! IsUnitType(u,UNIT_TYPE_STRUCTURE) && GetUnitAbilityLevel(u, 'mark') == 0 && tmp == null){ tmp = u; UnitDamageTarget(d.c,tmp,100*d.l,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null); } GroupRemoveUnit(g,u); } So when I used either of the two spells inside my map, I get a crash, not always but sometimes, like the first try, when the first spell hits an enemy and dies, crashes the game, and the second try when the second spell also hits an enemy and dies, it crashes the game, that is why I doubt about the two spells because of the method I used to check units in a group. So these are the spells: Crippling Arrow Shoots a magical arrow towards the target point that deals damage and cripples the first enemy unit it hits, slowly cripples the unit until it may not able to move. Lasts 4 seconds. Zinc://! zinc library CripplingArrow requires SpellEvent, xepreload, TimerUtils, MyFunctions, GroupUtils, BoolexprUtils{ constant integer spell = 'A01W'; constant integer dspell= 'A01X'; constant integer arrow = 'e005'; constant integer bap = 'B00J'; constant real speed = 850.0; constant real distance = 2100.0; struct data{ unit c,d; real a,xpt,ctr; integer l; } struct crippleData{ unit u; real ctr; method onDestroy(){ UnitRemoveAbility(u,bap); UnitRemoveAbility(u,dspell); } } function cripple(unit u){ crippleData d = crippleData.create(); timer t = NewTimer(); d.u = u; d.ctr = 0.0; SetTimerData(t,d); UnitAddAbility(u,dspell); TimerStart(t,1.0,true,function(){ timer t = GetExpiredTimer(); crippleData d = GetTimerData(t); SetUnitAbilityLevel(d.u,dspell,GetUnitAbilityLevel(d.u,dspell)+1); if(d.ctr >= 3.0 || GetWidgetLife(d.u) <= 0.405){ d.ctr = 0.0; ReleaseTimer(t); d.destroy(); } d.ctr += 1.0; t = null; }); t = null; } function act(){ unit c = SpellEvent.CastingUnit; real x = GetUnitX(c); real y = GetUnitY(c); real tx= SpellEvent.TargetX; real ty= SpellEvent.TargetY; real a = Atan3(x,y,tx,ty); integer l = GetUnitAbilityLevel(c,spell); timer t = NewTimer(); data d = data.create(); d.c = c; d.d = CreateUnit(GetOwningPlayer(c),arrow,x,y,a*57.29582); d.a = a; d.l = l; d.xpt = distance / speed; d.ctr = 0.0; SetTimerData(t,d); TimerStart(t,0.035,true,function(){ timer t = GetExpiredTimer(); data d = GetTimerData(t); real x = GetUnitX(d.d); real y = GetUnitY(d.d); real px = x + (speed*0.035) * Cos(d.a); real py = y + (speed*0.035) * Sin(d.a); group g = NewGroup(); unit u,tmp = null; GroupEnumUnitsInRange(g,x,y,135.0,BOOLEXPR_TRUE); for(u = FirstOfGroup(g) ; u != null ; u = FirstOfGroup(g)){ if(IsUnitEnemy(u,GetOwningPlayer(d.c)) && GetWidgetLife(u) > 0.405 && ! IsUnitType(u,UNIT_TYPE_STRUCTURE) && GetUnitAbilityLevel(u, 'mark') == 0 && tmp == null){ tmp = u; UnitDamageTarget(d.c,tmp,100*d.l,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,null); cripple(tmp); KillUnit(d.d); } GroupRemoveUnit(g,u); } ReleaseGroup(g); if(d.ctr >= d.xpt || GetWidgetLife(d.d) <= 0.405){ d.ctr = 0.0; KillUnit(d.d); ReleaseTimer(t); d.destroy(); } SetUnitX(d.d,px); SetUnitY(d.d,py); d.ctr += 0.035; t = null; u = null; tmp = null; g = null; }); c = null; t = null; } function onInit(){ RegisterSpellEffectResponse(spell,act); XE_PreloadAbility(dspell); } } //! endzinc Scatter Shot (uses xecollider) Fires armor-piercing arrows to all nearby enemy units around Raven, each arrow homes to each desired target. Zinc://! zinc library ScatterShot requires MyFunctions, SpellEvent, TimerUtils, GroupUtils, BoolexprUtils, xecollider{ constant integer spell = 'A01Z'; constant string mdl = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl"; constant string sfx = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"; group f; struct data{ unit c; integer l; method onDestroy(){ PauseUnit(c,false); SetUnitTimeScale(c,1.0); SetUnitAnimation(c,"stand ready"); GroupClear(f); } } struct aCollider extends xecollider{ unit c,h; integer l; method onUnitHit(unit t){ if(IsUnitEnemy(t,GetOwningPlayer(c)) && GetWidgetLife(t) > 0.405 && ! IsUnitType(t,UNIT_TYPE_STRUCTURE) && GetUnitAbilityLevel(t,'mark') == 0){ UnitDamageTarget(c,t,75*l,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_UNIVERSAL,null); DestroyEffect(AddSpecialEffectTarget(sfx,t,"chest")); if(t == h) terminate(); } } method loopControl(){ if(GetWidgetLife(h) <= 0.405) terminate(); } } function act(){ timer t = NewTimer(); data d = data.create(); unit c = SpellEvent.CastingUnit; integer l = GetUnitAbilityLevel(c,spell); d.c = c; d.l = l; SetTimerData(t,d); TimerStart(t,0.3,true,function(){ timer t = GetExpiredTimer(); data d = GetTimerData(t); group g = NewGroup(); unit u,tmp = null; real x = GetUnitX(d.c),y=GetUnitY(d.c),tx,ty,a; aCollider xe; SetUnitTimeScale(d.c,4.0); PauseUnit(d.c,true); GroupEnumUnitsInRange(g,GetUnitX(d.c),GetUnitY(d.c),1300.0,BOOLEXPR_TRUE); for(u = FirstOfGroup(g) ; u != null ; u = FirstOfGroup(g)){ if(IsUnitEnemy(u,GetOwningPlayer(d.c)) && GetWidgetLife(u) > 0.405 && ! IsUnitType(u,UNIT_TYPE_STRUCTURE) && GetUnitAbilityLevel(u,'mark') == 0 && tmp == null && ! IsUnitInGroup(u,f)){ tmp = u; tx = GetUnitX(u); ty = GetUnitY(u); a = Atan3(x,y,tx,ty); SetUnitFacingTimed(d.c,a*57.29582,0.0); SetUnitAnimation(d.c,"attack"); GroupAddUnit(f,u); xe = aCollider.create(x,y,a); xe.c = d.c; xe.l = d.l; xe.h = tmp; xe.fxpath = mdl; xe.z = 50.0; xe.scale = 1.3; xe.collisionSize = 100.0; xe.speed = 1200; xe.angleSpeed = bj_PI * 4.0; xe.targetUnit = tmp; } GroupRemoveUnit(g,u); } ReleaseGroup(g); if(tmp == null || GetWidgetLife(d.c) <= 0.405){ ReleaseTimer(t); d.destroy(); } t = null; u = null; tmp = null; g = null; }); t = null; c = null; } function onInit(){ RegisterSpellEffectResponse(spell,act); f = CreateGroup(); } } //! endzinc |
| 01-26-2010, 03:41 AM | #2 |
compile code and see how it actually looks. |
| 01-26-2010, 08:32 AM | #3 |
I would really like to help you, but I don't like ZINC. |
| 01-26-2010, 09:56 AM | #4 |
so how do I compile it and get the output? |
| 01-26-2010, 10:10 AM | #5 |
Zinc:for(u = FirstOfGroup(g) ; u != null ; u = FirstOfGroup(g)){ |
| 01-26-2010, 11:49 AM | #6 |
tried u = null, increases the chance to crash the map u = u, I know, I'm stupid, causes a very heavy lag while the code is picking units, lol while looking at vex's spell coded using zinc, here's what he does to pick one unit Zinc:method onUnitHit(unit hitUnit) { if( (hitUnit!=owner) && ! IsUnitInGroup(hitUnit, targetLog ) ) { //when a new unit is hit: // Try doing damage, if damage gets done, the target is a valid // one and we can contnue and show the effect and get a new target if(! xdam[level].damageTarget(owner, hitUnit, missileDamage(level) ) ) return; flash("Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"); GroupAddUnit(targetLog, hitUnit) ; targetCount = targetCount + 1; if( targetCount == maxTargets(level) ) { //too many targets, terminate this missile. terminate(); return; } if( ( targetUnit != hitUnit ) && (targetUnit!=null) ) { //The assigned targetUnit was not hit yet, so continue // moving against it. return; } instance = this; targetUnit = null; //Pick a new target, start by setting target to null. GroupEnumUnitsInRange(enumgroup, x,y , detectDistance(level), function()->boolean{ thistype this = instance; //adopt-a-instance unit picked = GetFilterUnit(); real dx = x - GetUnitX(picked), dy = y - GetUnitY(picked); real dis = SquareRoot( dx*dx + dy*dy ); //Pick the best new target. if ( !IsUnitInGroup(picked,targetLog) && (picked!=owner) && ( (targetUnit == null) || (dis < targetdist) ) && xdam[level].allowedTarget(owner, picked) ) { targetdist = dis; targetUnit = picked; } picked = null; return false; }); if( targetUnit == null) { angleSpeed = 0; forgetTarget(); } } } and the full code Zinc://! zinc library ChainInferno requires xecollider, xedamage { // config stuff: // The triggerer spell's rawcode: constant integer SPELL_ID = 'A01N'; // A random off-set for the creation position of each missile. constant real NOISE = 50.0; function missileCount(integer level)->integer { return 3+level; } function missileMaxSpeed(integer level)->real { return 700.0 + level * 0.0; } function missileMinSpeed(integer level)->real { return 100.0 + level * 0.0; } function maxTargets(integer level) -> integer { return 3 + level; } /* The following functions are used when setting up the missiles As a matter of fact, it is not necessary to use functions like this you could easily just assign the stuff in the missiles, but these functions are a common way to have a configuration header in a spell... */ function missileDamage(integer level) -> real { return 15.0 + level * 10.0; } function detectDistance(integer level) -> real { return 1000.0 + 0.0 * level; } function configMissile(xecollider m, integer level) { //asorted settings for the missiles. // notice a xecollider object is used, and we can mess with these fields // in the config section like this thanks to OOPness. // In the case of this spell, maxSpeed and minSpeed are treated in a // special way, so they do not get assigned by this function. // The movement height of each missile: m.z = 60.0; //the duration of each missile m.expirationTime = 5.0 + 0.0 * level ; // The model used by the missile: m.fxpath = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"; m.collisionSize = 50.0 + level * 0.0; m.acceleration = 1200.0 + level * 0.0; m.angleSpeed = bj_PI * 4; } //likewise, we can configure the xedamage: function setupDamage(xedamage xd, integer level ) { xd.damageAllies =false; // xd.damageEnemies =true; // Hit enemies exclusively xd.damageNeutral = false; // xd.exception = UNIT_TYPE_STRUCTURE; //do not hit buildings. xd.abilityFactor('mark',0.0); xd.dtype = DAMAGE_TYPE_FIRE; // fire (magical) normal damage xd.atype = ATTACK_TYPE_NORMAL; } // ------------------------------------------------------------------------- // code stuff: // xedamage xdam[]; struct missile extends xecollider { integer level; integer targetCount = 0; unit owner; real time = 0; group targetLog; //Follows an example on how to have a custom create on // a xecollider struct... // static method create(real x, real y, real dir)->thistype { missile this = allocate(x,y,dir); /* allocate of xecollider children has that argument list */ if ( targetLog == null) { targetLog = CreateGroup(); } GroupClear(targetLog); return this; } static real targetdist = 0.0; //current target's distance static missile instance; /* to pass the current instance to the enum function */ method onUnitHit(unit hitUnit) { if( (hitUnit!=owner) && ! IsUnitInGroup(hitUnit, targetLog ) ) { //when a new unit is hit: // Try doing damage, if damage gets done, the target is a valid // one and we can contnue and show the effect and get a new target if(! xdam[level].damageTarget(owner, hitUnit, missileDamage(level) ) ) return; flash("Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"); GroupAddUnit(targetLog, hitUnit) ; targetCount = targetCount + 1; if( targetCount == maxTargets(level) ) { //too many targets, terminate this missile. terminate(); return; } if( ( targetUnit != hitUnit ) && (targetUnit!=null) ) { //The assigned targetUnit was not hit yet, so continue // moving against it. return; } instance = this; targetUnit = null; //Pick a new target, start by setting target to null. GroupEnumUnitsInRange(enumgroup, x,y , detectDistance(level), function()->boolean{ thistype this = instance; //adopt-a-instance unit picked = GetFilterUnit(); real dx = x - GetUnitX(picked), dy = y - GetUnitY(picked); real dis = SquareRoot( dx*dx + dy*dy ); //Pick the best new target. if ( !IsUnitInGroup(picked,targetLog) && (picked!=owner) && ( (targetUnit == null) || (dis < targetdist) ) && xdam[level].allowedTarget(owner, picked) ) { targetdist = dis; targetUnit = picked; } picked = null; return false; }); if( targetUnit == null) { angleSpeed = 0; forgetTarget(); } } } } group enumgroup; function onSpell() { unit u = GetTriggerUnit(); integer level = GetUnitAbilityLevel(u, SPELL_ID ); integer i; real tx = GetSpellTargetX(), ty=GetSpellTargetY(); real x = GetUnitX(u), y=GetUnitY(u); real f = Atan2( ty-y, tx-x ); missile m; if(xdam[level]==0) { //all array elements start at 0 //the trick here is to init the xedamage instance for each level only when necessary. xdam[level] = xedamage.create(); setupDamage(xdam[level], level); } for ( 0 <= i < missileCount(level) ) { //create a new missile: m = missile.create(x + GetRandomReal(-NOISE, NOISE), y + GetRandomReal(-NOISE, NOISE), f); // NOISE works just as a random off-set for the missile position, it makes the spell look better. m.owner = u; m.targetUnit = GetSpellTargetUnit(); m.level = level; // call configMissile : configMissile(m,level); //This one is important, the secret to the looks of the spell is with doing these tricks, each // missile starts at a speed slower than the previous one, then the movement is accelerated // by missileAcceleration(level) until all the missiles have the same speed. m.maxSpeed =missileMaxSpeed(level); m.speed = m.maxSpeed - (m.maxSpeed - missileMinSpeed(level) )* (i+1) / missileCount(level); } u=null; } function onInit() { //let's init the trigger : // "When any unit casts SPELL_ID, call onSpelll" trigger t = CreateTrigger(); TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT); TriggerAddAction(t, function onSpell); TriggerAddCondition(t, function()->boolean{ return GetSpellAbilityId() == SPELL_ID; }); //let's init the enumgroup variable: enumgroup = CreateGroup(); } } //! endzinc |
| 01-26-2010, 11:56 AM | #7 |
Argh, I hate these unknown functions. Btw, there is no loop at Vexs code. |
| 01-27-2010, 12:11 AM | #8 |
yeah, I need to know on how did he do that, I'm currently reading his code, I need to learn more stuffs. |
| 01-27-2010, 12:24 AM | #9 |
GroupEnumUnitsInRange is the loop. Here's a loop which kills all units in range from (x,y): JASS:globals private group dummyGroup=null endglobals private function Loop takes nothing returns boolean call KillUnit(GetFilterUnit()) // By using return false dummyGroup will never actually be filled. // We just use the group because GroupEnum wants a group. return false endfunction function KillUnitsInRange takes real x, real y, real range returns nothing if dummyGroup==null then set dummyGroup=CreateGroup() endif call GroupEnumUnitsInRange(dummyGroup, x, y, range, Condition(function Loop)) endfunction |
| 01-30-2010, 01:48 AM | #10 |
so, that means, function Loop can be used instead of looping through the filled group? but one thing I hate about that is, I don't know how to attach a struct to something to be used inside function Loop, the only thing I can think of is GetFilterUnit() and GetTriggerUnit(). I am also unsure if GetTriggerUnit will return the right unit (i.e. the unit that cast the spell), but when GroupEnum is inside a timer then how will I retrieve the unit that casts the spell? I know this will be possible by using structs to retrieve the data, but where should I attach the struct? Edit: I recoded Scatter Shot from Zinc to Normal Jass and still causes crash JASS:scope ScatterShot initializer onInit globals private constant integer spell = 'A01Z' private constant string mdl = "Abilities\\Weapons\\Arrow\\ArrowMissile.mdl" private constant string sfx = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" private group f endglobals private struct data unit c integer l private method onDestroy takes nothing returns nothing call PauseUnit(c,false) call SetUnitTimeScale(c,1.0) call SetUnitAnimation(c,"stand ready") call GroupClear(f) endmethod endstruct private struct aCollider extends xecollider unit c unit h integer l private method onUnitHit takes unit t returns nothing if IsUnitEnemy(t,GetOwningPlayer(c)) and GetWidgetLife(t) > 0.405 and not IsUnitType(t,UNIT_TYPE_STRUCTURE) and GetUnitAbilityLevel(t,'mark') == 0 then call UnitDamageTarget(c,t,75*l,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_UNIVERSAL,null) call DestroyEffect(AddSpecialEffectTarget(sfx,t,"chest")) if t == h then call terminate() endif endif endmethod private method loopControl takes nothing returns nothing if GetWidgetLife(h) <= 0.405 then call terminate() endif endmethod endstruct private function filt takes nothing returns boolean return GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) and GetUnitAbilityLevel(GetFilterUnit(),'mark') == 0 and not IsUnitInGroup(GetFilterUnit(),f) endfunction private function callBack takes nothing returns nothing local timer t = GetExpiredTimer() local data d = GetTimerData(t) local group g = NewGroup() local unit u local unit tmp = null local real x = GetUnitX(d.c) local real y=GetUnitY(d.c) local real tx local real ty local real a local aCollider xe call SetUnitTimeScale(d.c,4.0) call PauseUnit(d.c,true) call GroupEnumUnitsInRange(g,GetUnitX(d.c),GetUnitY(d.c),1300.0,Condition(function filt)) loop set u = FirstOfGroup(g) exitwhen u == null if IsUnitEnemy(u,GetOwningPlayer(d.c)) and tmp == null then set tmp = u set tx = GetUnitX(u) set ty = GetUnitY(u) set a = Atan3(x,y,tx,ty) call SetUnitFacingTimed(d.c,a*57.29582,0.0) call SetUnitAnimation(d.c,"attack") call GroupAddUnit(f,u) set xe = aCollider.create(x,y,a) set xe.c = d.c set xe.l = d.l set xe.h = tmp set xe.fxpath = mdl set xe.z = 50.0 set xe.scale = 1.3 set xe.collisionSize = 100.0 set xe.speed = 1200 set xe.angleSpeed = bj_PI * 4.0 set xe.targetUnit = tmp endif call GroupRemoveUnit(g,u) endloop call ReleaseGroup(g) if tmp == null or GetWidgetLife(d.c) <= 0.405 then call ReleaseTimer(t) call d.destroy() endif set t = null set u = null set tmp = null set g = null endfunction private function act takes nothing returns nothing local timer t = NewTimer() local data d = data.create() local unit c = SpellEvent.CastingUnit local integer l = GetUnitAbilityLevel(c,spell) set d.c = c set d.l = l call SetTimerData(t,d) call TimerStart(t,0.3,true,function callBack) set t = null set c = null endfunction private function onInit takes nothing returns nothing call RegisterSpellEffectResponse(spell,act) set f = CreateGroup() endfunction endscope |
