i have some questions about xecollider usage. i created a spell that summons missiles around a target location which are homing towards the center. enemy units hit by a missile take damage once and are added to a hittarget group. each unit within this group is pulled towards the center.
my questions now:
- what is the best/a good way to deal with this hittarget unitgroup that is shared through all missileinstances?
i created a "groupsemaphor" to count the references on the shared group and release it, when no more reference is acquiring the resource.
- is there a better way for the unit pulling?
i loop thourgh all units each xeanimationperiod, calculate the angle of the unit between the center and its position and move it then towards the center.
- is there anything else that i can do better/easier than i m doing it now?
Hidden information:
JASS:
scopeWaterImplosioninitializerinitglobalsprivateconstantintegerSPELL_ID = 'A006'privateconstantstringMISSILE_PATH = "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"privateconstantstringEFFECT_AOE = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"privateconstantstringEFFECT_TARGET = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"privateconstantrealMISSILE_SCALE = 1.5privateconstantrealMISSILE_SPEED = 550.0privateconstantintegerCOLL_SIZE = 50privateconstantintegerMAX_MISSILES = 12privateintegerarrayDAMAGEprivaterealarrayDAM_PER_INTprivateintegerarrayRADIUSprivatexedamageXEDAM = 0endglobalsprivatefunctionSetupValuestakesnothingreturnsnothingsetXEDAM = xedamage.create()
setXEDAM.damageNeutral = falsesetXEDAM.dtype = DAMAGE_TYPE_COLDsetXEDAM.exception = UNIT_TYPE_MAGIC_IMMUNEcallXEDAM.factor( UNIT_TYPE_STRUCTURE, 0 )
callXEDAM.useSpecialEffect( EFFECT_TARGET, "origin" )
setDAMAGE[0] = 30setDAMAGE[1] = 40setDAMAGE[2] = 50setDAMAGE[3] = 60setDAMAGE[4] = 70setDAM_PER_INT[0] = 0.75setDAM_PER_INT[1] = 0.75setDAM_PER_INT[2] = 0.80setDAM_PER_INT[3] = 0.80setDAM_PER_INT[4] = 0.80setRADIUS[0] = 350setRADIUS[1] = 350setRADIUS[2] = 375setRADIUS[3] = 375setRADIUS[4] = 400endfunction// end of user configuration// all missiles use the same group// to avoid multiple times damage on one target// each missile instance calls the acquire method to increase the reference counter by 1// each missile instance calls the release method on destruction to decrease the reference counter by 1// if the counter reaches 0 the group will be releasedprivatestructGroupSemaphorgroupgintegerreferencesmethodacquiretakesnothingreturnsnothingset.references = .references + 1endmethodmethodreleasetakesnothingreturnsnothingset.references = .references - 1if( .references <= 0 )thencall.destroy()
endifendmethodstaticmethodcreatetakesnothingreturnsGroupSemaphorlocalGroupSemaphorthis = GroupSemaphor.allocate()
set.g = NewGroup()
set.references = 0returnthisendmethodmethodonDestroytakesnothingreturnsnothingcallReleaseGroup( .g )
set.references = 0endmethodendstruct// each missile deals damage to enemy targets// the primary missile with the missileid zero creates the special effect at the end of the spell// and pulls all targets towards the center of the spellprivatestructMissileextendsxecolliderunitcasterintegerlvlintegermid// missile idGroupSemaphorpgrouprealrangerealtargetXrealtargetYstaticMissiletempthis// in the group callback method we pull all targets that are still// in range of the spell towards the center of the spellstaticmethodgroup_callbacktakesnothingreturnsnothinglocalunitu = GetEnumUnit()
localrealangle = 0localrealx = 0localrealy = 0localrealoffset = 0if( IsUnitInRangeXY( u, .tempthis.targetX, .tempthis.targetY, .tempthis.range ) )thensetx = GetUnitX( u )
sety = GetUnitY( u )
setoffset = .tempthis.speed * XE_ANIMATION_PERIODsetangle = Atan2( .tempthis.targetY - y, .tempthis.targetX - x )
callSetUnitX( u, GetUnitX( u ) + offset * Cos( angle ) )
callSetUnitY( u, GetUnitY( u ) + offset * Sin( angle ) )
endifsetu = nullendmethod// when an enemy unit is hit, we deal some damage to it// and at it to the hittargets groupmethodonUnitHittakesunithitTargetreturnsnothingif( .caster != nullandXEDAM.allowedTarget( .caster, hitTarget ) )thenif( notIsUnitInGroup( hitTarget, .pgroup.g ) )thencallXEDAM.damageTarget( .caster, hitTarget, DAMAGE[.lvl-1] + DAM_PER_INT[.lvl-1]*GetHeroInt( .caster, true ) )
callGroupAddUnit( .pgroup.g, hitTarget )
endifendifendmethod// we periodically update the remaining range of each missile// to destroy them, when they reach the target locationmethodloopControltakesnothingreturnsnothingset.range = .range - .speed*XE_ANIMATION_PERIODif( .range <= 0 )thencall.terminate()
elseif( .mid == 0 )thenset.tempthis = thiscallForGroup( .pgroup.g, functionMissile.group_callback )
endifendmethodstaticmethodcreatetakesunitc, realx, realy, realtx, realty, integerlevel, integermissileID, GroupSemaphorgreturnsMissilelocalrealangle = Atan2( ty-y, tx-x )
localMissilethis = Missile.allocate( x, y, angle )
set.caster = cset.lvl = levelset.mid = missileIDset.range = RADIUS[.lvl-1]
set.targetX = txset.targetY = tyset.pgroup = gcall.pgroup.acquire()
set.collisionSize = COLL_SIZEset.scale = MISSILE_SCALEset.fxpath = MISSILE_PATHset.speed = MISSILE_SPEEDset.angleSpeed = 2*bj_PIset.owner = GetOwningPlayer( c )
call.setTargetPoint( tx, ty )
returnthisendmethodmethodonDestroytakesnothingreturnsnothingif( .mid == 0 )thencallDestroyEffect( AddSpecialEffect( EFFECT_AOE, .x, .y ) )
endifcall.pgroup.release()
endmethodendstructprivatefunctionConditionstakesnothingreturnsbooleanlocalintegeri = 0localrealangle = 0localunitcaster = nulllocalrealtx = 0localrealty = 0localintegerlvl = 0localGroupSemaphorg = 0if( GetSpellAbilityId() == SPELL_ID ) thensetcaster = GetTriggerUnit()
settx = GetSpellTargetX()
setty = GetSpellTargetY()
setlvl = GetUnitAbilityLevel( caster, SPELL_ID )
setangle = 2*bj_PI/MAX_MISSILESsetg = GroupSemaphor.create()
// create the missiles in a circle around the target locationloopexitwheni >= MAX_MISSILEScallMissile.create( caster, tx + (RADIUS[lvl-1]-COLL_SIZE) * Cos( angle*i ), ty + (RADIUS[lvl-1]-COLL_SIZE) * Sin( angle*i ), tx, ty, lvl, i, g )
seti = i + 1endloopsetcaster = nullendifreturnfalseendfunctionprivatefunctioninittakesnothingreturnsnothinglocaltriggertrig = CreateTrigger()
callSetupValues()
callDestroyEffect( AddSpecialEffect( MISSILE_PATH, 0, 0 ) )
callDestroyEffect( AddSpecialEffect( EFFECT_AOE, 0, 0 ) )
callDestroyEffect( AddSpecialEffect( EFFECT_TARGET, 0, 0 ) )
callXE_PreloadAbility( SPELL_ID )
callTriggerAddCondition( trig, functionConditions )
callTriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
endfunctionendscope
It seems to me that it would make more sense in terms of code organization if your spell trigger created the "semaphor" object which would then itself create the missiles as well as move the units that the missiles hit towards the centre. The code wouldn't be significantly different from what you have now, just differently organised.
it can be done better only with native jass, all vJass extension compile into mess.
is this a serious comment? i don't get it at all
Quote:
It seems to me that it would make more sense in terms of code organization if your spell trigger created the "semaphor" object which would then itself create the missiles as well as move the units that the missiles hit towards the centre. The code wouldn't be significantly different from what you have now, just differently organised.
i coded it that way with some of my older spells. you then don't need the groupsemaphor since the spellstruct can contain the one group and you can save f.e. the spellinstance within the missilestruct to get accessto the group.
however this needs f.e. private keyword and makes the code (to me) more unreadable since there is no clean "code splitting" like you'd have it in java or c++.
another thing is, that xecollider misses some methods imo. first of all, when you take the code from the first post and set the missile speed to 600 and make a check like
the check will never come true. seems like a problem of the xecollider code when the missile has an anglespeed.
second, since you have no access to the xefxdummy, i tried the following:
Code:
if( x >= targetx-collsize and x <= targetx+collsize and
y >= targety-collsize and y <= targety+collsize )
same problem again.
the actuale working code looks like this. i used a lifetime now to destroy the missiles but imo thats not very accurate and nice:
Hidden information:
JASS:
scopeWaterImplosioninitializerinitglobalsprivateconstantintegerSPELL_ID = 'A006'privateconstantstringMISSILE_PATH = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveMissile.mdl"privateconstantstringEFFECT_AOE = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"privateconstantstringEFFECT_TARGET = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl"privateconstantrealMISSILE_SCALE = 0.35privateconstantrealMISSILE_TIME = 0.80// life time of each missileprivateconstantrealMISSILE_SPEED = 600.0privateconstantintegerCOLL_SIZE = 50privateconstantintegerMAX_MISSILES = 8privateintegerarrayDAMAGEprivaterealarrayDAM_PER_INTprivateintegerarrayRADIUSprivatexedamageXEDAM = 0endglobalsprivatefunctionSetupValuestakesnothingreturnsnothingsetXEDAM = xedamage.create()
setXEDAM.damageNeutral = falsesetXEDAM.dtype = DAMAGE_TYPE_COLDsetXEDAM.exception = UNIT_TYPE_MAGIC_IMMUNEcallXEDAM.factor( UNIT_TYPE_STRUCTURE, 0 )
callXEDAM.useSpecialEffect( EFFECT_TARGET, "origin" )
setDAMAGE[0] = 30setDAMAGE[1] = 40setDAMAGE[2] = 50setDAMAGE[3] = 60setDAMAGE[4] = 70setDAM_PER_INT[0] = 0.75setDAM_PER_INT[1] = 0.75setDAM_PER_INT[2] = 0.80setDAM_PER_INT[3] = 0.80setDAM_PER_INT[4] = 0.80setRADIUS[0] = 350setRADIUS[1] = 350setRADIUS[2] = 375setRADIUS[3] = 375setRADIUS[4] = 400endfunction// end of user configuration// each missile deals damage to enemy targets// the primary missile with the missileid zero creates the special effect at the end of the spell// and pulls all targets towards the center of the spellprivatestructMissileextendsxecolliderunitcasterintegerlvlintegermid// missile idGroupSemaphorpgrouprealrangerealtargetXrealtargetYstaticMissiletempthis// in the group callback method we pull all targets that are still// in range of the spell towards the center of the spellstaticmethodgroup_callbacktakesnothingreturnsnothinglocalunitu = GetEnumUnit()
localrealangle = 0localrealx = 0localrealy = 0localrealoffset = 0if( IsUnitInRangeXY( u, .tempthis.targetX, .tempthis.targetY, .tempthis.range ) )thensetx = GetUnitX( u )
sety = GetUnitY( u )
setoffset = .tempthis.speed * XE_ANIMATION_PERIODsetangle = Atan2( .tempthis.targetY - y, .tempthis.targetX - x )
callSetUnitX( u, GetUnitX( u ) + offset * Cos( angle ) )
callSetUnitY( u, GetUnitY( u ) + offset * Sin( angle ) )
endifsetu = nullendmethod// when an enemy unit is hit, we deal some damage to it// and add it to the hittargets groupmethodonUnitHittakesunithitTargetreturnsnothingif( .caster != nullandXEDAM.allowedTarget( .caster, hitTarget ) andnotIsUnitInGroup( hitTarget, .pgroup.g ) )thencallXEDAM.damageTarget( .caster, hitTarget, DAMAGE[.lvl-1] + DAM_PER_INT[.lvl-1]*GetHeroInt( .caster, true ) )
callGroupAddUnit( .pgroup.g, hitTarget )
endifendmethod// we periodically update the remaining range of each missile// to pull enemy units correctlymethodloopControltakesnothingreturnsnothinglocalrealdx = .x - .targetXlocalrealdy = .y - .targetYset.range = SquareRoot( dx*dx + dy*dy )
if( .mid == 0 )thenset.tempthis = thiscallForGroup( .pgroup.g, functionMissile.group_callback )
endifendmethodstaticmethodcreatetakesunitc, realx, realy, realtx, realty, integerlevel, integermissileID, GroupSemaphorgreturnsMissilelocalrealangle = Atan2( ty-y, tx-x )
localMissilethis = Missile.allocate( x, y, angle + bj_PI/2 )
set.caster = cset.lvl = levelset.mid = missileIDset.range = RADIUS[.lvl-1]
set.targetX = txset.targetY = tyset.pgroup = gcall.pgroup.acquire()
set.collisionSize = COLL_SIZEset.scale = MISSILE_SCALEset.fxpath = MISSILE_PATHset.speed = MISSILE_SPEEDset.expirationTime = MISSILE_TIMEset.angleSpeed = bj_PIset.owner = GetOwningPlayer( c )
call.setTargetPoint( tx, ty )
returnthisendmethodmethodonDestroytakesnothingreturnsnothingif( .mid == 0 )thencallDestroyEffect( AddSpecialEffect( EFFECT_AOE, .targetX, .targetY ) )
endifcall.pgroup.release()
endmethodendstructprivatefunctionConditionstakesnothingreturnsbooleanlocalintegeri = 0localrealangle = 0localunitcaster = nulllocalrealtx = 0localrealty = 0localintegerlvl = 0localGroupSemaphorg = 0if( GetSpellAbilityId() == SPELL_ID ) thensetcaster = GetTriggerUnit()
settx = GetSpellTargetX()
setty = GetSpellTargetY()
setlvl = GetUnitAbilityLevel( caster, SPELL_ID )
setangle = 2*bj_PI/MAX_MISSILESsetg = GroupSemaphor.create()
// create the missiles in a circle around the target locationloopexitwheni >= MAX_MISSILEScallMissile.create( caster, tx + (RADIUS[lvl-1]-COLL_SIZE) * Cos( angle*i ), ty + (RADIUS[lvl-1]-COLL_SIZE) * Sin( angle*i ), tx, ty, lvl, i, g )
seti = i + 1endloopsetcaster = nullendifreturnfalseendfunctionprivatefunctioninittakesnothingreturnsnothinglocaltriggertrig = CreateTrigger()
callSetupValues()
callDestroyEffect( AddSpecialEffect( EFFECT_AOE, 0, 0 ) )
callDestroyEffect( AddSpecialEffect( EFFECT_TARGET, 0, 0 ) )
callXE_PreloadAbility( SPELL_ID )
callTriggerAddCondition( trig, functionConditions )
callTriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
endfunctionendscope
another question:
is it possible to create a rotating missile with xecollider that rotates around unit u with an offset range of X and rotates around the unit 4 times a second?
i saw a rotating method within the xecollider library but there are no comments and when i checked out the periodicmovementmethod i found this
Code:
if (this.angleMode == ANGLE_ROTATING) then
//nothing (ns is already ns)
elseif( this.angleMode != ANGLE_NO_MOVEMENT) then