Hi, I'm new and it's my first time posting a thread here :] (Not sure if this is right forum to ask.)
So, um I started migrating from GUI to JASS recently. I tried browsing tutorials here and on Helper.net on making this spell. It's an MUI ability that uses hashtables as data bridge and timers for intervals. It is working as I planned but I'm not quite confident if I did it nicely or leakless as possible.
Idk if these vague details are needed but the spell basically does these every seconds:
So my question is: Can I ask if there's a (handle or reference) leak that I failed to null or destroy here? Especially the group that I stored on the hashtable.
I also gladly take suggestions on making the spell better.
Here's my trigger:
Uh sorry for my bad English btw. English is not my native language. I also attached the map if it is needed. Thanks!
So, um I started migrating from GUI to JASS recently. I tried browsing tutorials here and on Helper.net on making this spell. It's an MUI ability that uses hashtables as data bridge and timers for intervals. It is working as I planned but I'm not quite confident if I did it nicely or leakless as possible.
Idk if these vague details are needed but the spell basically does these every seconds:
- Pick all enemy units in an area nearby the caster as
targetGroup
- Every 0.2 seconds, randomly choose 1 unit from the
targetGroup
and damage it
So my question is: Can I ask if there's a (handle or reference) leak that I failed to null or destroy here? Especially the group that I stored on the hashtable.
I also gladly take suggestions on making the spell better.
Here's my trigger:
JASS:
function GetTheReckoning_SpellId takes nothing returns integer
return 'A000'
endfunction
function Trig_The_Reckoning_Conditions takes nothing returns boolean
return ( GetSpellAbilityId() == GetTheReckoning_SpellId() )
endfunction
function GetTheReckoning_SpellAoE takes nothing returns real
return 600.0
endfunction
function GetTheReckoning_Damage takes integer level returns real
return 60.0 + ( 40.0 * I2R( level ) )
endfunction
function TheReckoning_CreateExplosion takes location where returns nothing
call AddSpecialEffectLocBJ( where, "Abilities\\Weapons\\SteamTank\\SteamTankImpact.mdl" )
call DestroyEffectBJ( GetLastCreatedEffectBJ() )
endfunction
function TheReckoning_PrePostCastEffect takes location where returns nothing
call AddSpecialEffectLocBJ( where, "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl" )
call DestroyEffectBJ( GetLastCreatedEffectBJ() )
endfunction
// This function runs every 0.2 seconds.
function TheReckoning_HandlerFunc takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local unit u
local unit caster = LoadUnitHandle( udg_TR_Hashtable, id, StringHash("caster") )
local integer counter = LoadInteger( udg_TR_Hashtable, id, StringHash("counter") )
local group targetGroup = LoadGroupHandle( udg_TR_Hashtable, id, StringHash("targetGroup") )
local group targetGroupRaw
local group tempGroup
local location casterLoc = GetUnitLoc( caster )
local location uLoc
local boolean isUnitValid
local real damage
// Makes this block only run every second. This block sets up the `targetGroup` units to be damaged.
if ( ModuloInteger( counter, 5 ) == 0 ) then
call DestroyGroup( targetGroup )
set targetGroup = CreateGroup()
set targetGroupRaw = GetUnitsInRangeOfLocAll( GetTheReckoning_SpellAoE( ), casterLoc )
// Filters `targetGroupRaw` and puts filtered units on `targetGroup`
loop
set u = FirstOfGroup( targetGroupRaw )
exitwhen u == null
if ( IsUnitEnemy( u, GetOwningPlayer( caster ) ) and IsUnitAliveBJ( u ) ) then
call GroupAddUnit( targetGroup, u )
endif
call GroupRemoveUnit( targetGroupRaw, u )
endloop
call DestroyGroup( targetGroupRaw )
endif
// If no target, randomly create VFX at random point nearby. Else, damage the target and create VFX on target.
if ( FirstOfGroup( targetGroup ) == null ) then
set uLoc = PolarProjectionBJ(casterLoc, GetRandomReal(80.00, GetTheReckoning_SpellAoE() - 80.00), GetRandomReal(0, 360.00))
call TheReckoning_CreateExplosion( uLoc )
else
// Selects one random unit on `targetGroup`
set tempGroup = GetRandomSubGroup(1, targetGroup)
set u = FirstOfGroup( tempGroup )
set damage = GetTheReckoning_Damage( GetUnitAbilityLevel( caster, GetTheReckoning_SpellId( ) ) )
// Makes damage only half on `structure` and `mechanical` units.
if ( IsUnitType( u, UNIT_TYPE_STRUCTURE ) or IsUnitType( u, UNIT_TYPE_MECHANICAL ) ) then
call UnitDamageTargetBJ( caster, u, 0.5 * damage, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL )
else
call UnitDamageTargetBJ( caster, u, damage, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL )
endif
set uLoc = GetUnitLoc( u )
call TheReckoning_CreateExplosion( uLoc )
// Removes the unit from the `targetGroup` so it won't be called again for 1 second.
call GroupRemoveUnit( targetGroup, u )
call DestroyGroup( tempGroup )
endif
// After 12 seconds.
if ( counter >= 59 ) then
// Makes the caster now able to attack.
call UnitRemoveAbilityBJ( 'S000', caster )
call UnitAddAbilityBJ( 'S001', caster )
call TheReckoning_PrePostCastEffect( casterLoc )
call PauseTimer( t )
call DestroyTimer( t )
call DestroyGroup( targetGroup )
call FlushChildHashtable( udg_TR_Hashtable, id )
endif
call SaveInteger( udg_TR_Hashtable, id, StringHash("counter"), counter + 1 )
call SaveGroupHandle( udg_TR_Hashtable, id, StringHash("targetGroup"), targetGroup )
call RemoveLocation( uLoc )
call RemoveLocation( casterLoc )
set t = null
set u = null
set caster = null
set targetGroup = null
set targetGroupRaw = null
set tempGroup = null
endfunction
function Trig_The_Reckoning_New_Actions takes nothing returns nothing
local timer t = CreateTimer( )
local unit caster = GetTriggerUnit( )
local location casterLoc = GetUnitLoc( caster )
call TheReckoning_PrePostCastEffect( casterLoc )
call SaveUnitHandle( udg_TR_Hashtable, GetHandleId(t), StringHash("caster"), caster )
call SaveInteger( udg_TR_Hashtable, GetHandleId(t), StringHash("counter"), 0 )
call SaveGroupHandle( udg_TR_Hashtable, GetHandleId(t), StringHash("targetGroup"), null )
// This makes the caster unable to attack for the duration.
call UnitAddAbilityBJ( 'S000', caster )
call UnitRemoveAbilityBJ( 'S001', caster )
call TimerStart( t, 0.2, true, function TheReckoning_HandlerFunc )
call RemoveLocation( casterLoc )
set t = null
set caster = null
set casterLoc = null
endfunction
Uh sorry for my bad English btw. English is not my native language. I also attached the map if it is needed. Thanks!
Attachments
Last edited: