- Joined
- May 31, 2019
- Messages
- 150
Hello. Lately I've been working on revamping the Zerg Campaign's system code so that it's all in a modified Blizzard.j file. So far it's been going good, but today I've hit a snag with my Lurkers.
In the current release of the campaign, you can see they work just fine. However, re-doing them totally in JASS presents an opportunity to make their code more efficient.
Unfortunately, they're now crashing!
After some debugging, I've determined the exact line that is causing the crash.
Some context:
The Lurker fires 6 spines, each one appears in front of the last one. The loop ran when the Lurker attacks only creates dummy timer units that upon death will spawn the spines and deal damage.
What's note-worthy is that the creation of the first spine's dummy timer works just fine, no crash. It is only once the loop reaches its second iteration that the crash happens at this line.
I thought maybe my math was off in the JASS version and it was creating an invalid location or something, but from what I can tell, this part is very much the same as in the GUI version
I also tried setting the point to a global instead of a local variable, but this did not help anything.
So I'm pretty stumped on what the issue might be. I'd be grateful if anyone were able to crack it.
Below, the full GUI (working) version and JASS (crashing) version.
I've also attached my current Blizzard.j, as well as a zip containing a stripped-down version of my mod and a test map, so you can try it in-game yourself. Like the Zerg Campaign, it requires version 1.28 of War3.
In the current release of the campaign, you can see they work just fine. However, re-doing them totally in JASS presents an opportunity to make their code more efficient.
Unfortunately, they're now crashing!
After some debugging, I've determined the exact line that is causing the crash.
JASS:
call CreateNUnitsAtLoc(1, sc_TIMER_LURKER_SPINE, lurkerOwner, spinePt, bj_UNIT_FACING)
Some context:
The Lurker fires 6 spines, each one appears in front of the last one. The loop ran when the Lurker attacks only creates dummy timer units that upon death will spawn the spines and deal damage.
What's note-worthy is that the creation of the first spine's dummy timer works just fine, no crash. It is only once the loop reaches its second iteration that the crash happens at this line.
I thought maybe my math was off in the JASS version and it was creating an invalid location or something, but from what I can tell, this part is very much the same as in the GUI version
-
-------- Prior to entering this loop, tmpPoint1 has been set to the position of the Attacking Unit (the Lurker) --------
-
Set tmpPtLurkerSpine = (tmpPoint1 offset by ((Real((Integer A))) x Lurker_SpineDistance) towards (Facing of (Attacking unit)) degrees)
-
Unit - Create 1 Lurker_SpineTimerUnitType for (Owner of (Attacking unit)) at tmpPtLurkerSpine facing Default building facing degrees
JASS:
set spinePt = PolarProjectionBJ(lurkerPt, ( I2R(bj_forLoopAIndex) * sc_REAL_LURKER_SPINE_DISTANCE ), GetUnitFacing(whichLurker))
call CreateNUnitsAtLoc(1, sc_TIMER_LURKER_SPINE, lurkerOwner, spinePt, bj_UNIT_FACING)
I also tried setting the point to a global instead of a local variable, but this did not help anything.
So I'm pretty stumped on what the issue might be. I'd be grateful if anyone were able to crack it.
Below, the full GUI (working) version and JASS (crashing) version.
-
Lurker Attack
-
Events
-
Unit - A unit Is attacked
-
-
Conditions
-
Or - Any (Conditions) are true
-
Conditions
-
(Unit-type of (Attacking unit)) Equal to Zerg Lurker (Burrowed)
-
(Unit-type of (Attacking unit)) Equal to Hunter Lurker (Burrowed)
-
-
-
-
Actions
-
-------- Set the attacking Lurker's index if it doesn't have one already. --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
Or - Any (Conditions) are true
-
Conditions
-
LurkerUnit[(Custom value of (Attacking unit))] Not equal to (Attacking unit)
-
Lurker_CurrentIndex Equal to 0
-
-
-
-
Then - Actions
-
Set Lurker_CurrentIndex = (Lurker_CurrentIndex + 1)
-
Unit - Set the custom value of (Attacking unit) to Lurker_CurrentIndex
-
Set LurkerUnit[Lurker_CurrentIndex] = (Attacking unit)
-
-
Else - Actions
-
-
-------- Get the relevant position data --------
-
Set tmpPoint1 = (Position of (Attacking unit))
-
Set tmpPoint2 = (Position of (Triggering unit))
-
-------- Setup spines --------
-
Unit - Make (Attacking unit) face (Triggering unit) over 0.00 seconds
-
For each (Integer A) from 1 to Lurker_MissileSpines, do (Actions)
-
Loop - Actions
-
Game - Display to (All players) the text: (Attempting to create spine timer + (((String((Integer A))) + of Lurker ) + (String((Custom value of (Attacking unit))))))
-
-------- Set the position of the spine --------
-
Set tmpPtLurkerSpine = (tmpPoint1 offset by ((Real((Integer A))) x Lurker_SpineDistance) towards (Facing of (Attacking unit)) degrees)
-
Game - Display to (All players) the text: (Distance of spine from Lurker: + (String(((Real((Integer A))) x Lurker_SpineDistance))))
-
-------- Set the expiration timer for creating the spine --------
-
Set tmpRealLurkerSpine = (((Real((Integer A))) - 0.99) x Lurker_SpineDelay)
-
-------- CREATE TIMER FOR ART DUMMY --------
-
Unit - Create 1 Lurker_SpineTimerUnitType for (Owner of (Attacking unit)) at tmpPtLurkerSpine facing Default building facing degrees
-
Animation - Change (Last created unit)'s vertex coloring to (100.00%, 100.00%, 100.00%) with 100.00% transparency
-
Unit - Set the custom value of (Last created unit) to (Custom value of (Attacking unit))
-
Unit - Set life of (Last created unit) to (Real((Integer A)))
-
Unit - Add a tmpRealLurkerSpine second Generic expiration timer to (Last created unit)
-
-------- PLAY THE FIRST TIMER UNIT'S BIRTH ANIMATION SO THE LURKER MISSILE SOUND PLAYS --------
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
(Integer A) Equal to 1
-
-
Then - Actions
-
Unit - Create 1 Lurker Fire (Sound Dummy) for (Owner of (Attacking unit)) at tmpPtLurkerSpine facing Default building facing degrees
-
Animation - Play (Last created unit)'s birth animation
-
Animation - Change (Last created unit)'s vertex coloring to (100.00%, 100.00%, 100.00%) with 100.00% transparency
-
Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
-
-
Else - Actions
-
-
-------- CREATE TIMER FOR DAMAGE DUMMY --------
-
Unit - Create 1 Lurker_SpineDmgUnitType for (Owner of (Attacking unit)) at tmpPtLurkerSpine facing Default building facing degrees
-
Animation - Change (Last created unit)'s vertex coloring to (100.00%, 100.00%, 100.00%) with 100.00% transparency
-
Unit - Set the custom value of (Last created unit) to (Custom value of (Attacking unit))
-
Unit - Set life of (Last created unit) to (Real((Integer A)))
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
(Unit-type of (Attacking unit)) Equal to Hunter Lurker (Burrowed)
-
-
Then - Actions
-
Unit - Add Hunter Lurker Spine (Passive Marker) to (Last created unit)
-
-
Else - Actions
-
-
Unit - Set level of Spine Number ID (for Lurkers) for (Last created unit) to (Integer A)
-
Unit - Add a (tmpRealLurkerSpine + Lurker_SpineDmgPoint) second Generic expiration timer to (Last created unit)
-
Custom script: call RemoveLocation(udg_tmpPtLurkerSpine)
-
-
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
LurkerUnit[(Custom value of (Attacking unit))] Equal to (Attacking unit)
-
-
Then - Actions
-
Game - Display to (All players) the text: Lurker successfully...
-
-
Else - Actions
-
-
-------- Clear leaks --------
-
Custom script: call RemoveLocation(udg_tmpPoint1)
-
Custom script: call RemoveLocation(udg_tmpPoint2)
-
-
JASS:
function SC_Lurker_Attack takes player lurkerOwner, unit whichLurker, unit attackedUnit, location lurkerPt returns nothing
local integer lurkerId = GetUnitUserData(whichLurker)
local real lurkerAcqRange = 0.00
local real lurkerSpinesReal = 0.00
local integer lurkerRange = 0
local integer spineCount = sc_array_lurker_spineCount[lurkerId]
local location spinePt = null
local real spineDelay = 0.00
local real spineDelayDamage = 0.00
local real baseDamageAmount = sc_array_lurker_baseDamage[lurkerId]
local integer damageUpgLvl = 0
local attacktype attackType = sc_array_lurker_attacktype[lurkerId]
local boolean debugThis = true
call SetUnitFacingToFaceUnitTimed(whichLurker, attackedUnit, 0)
// If the Lurker's spine amount has not been set, set it.
if spineCount == null then
set lurkerAcqRange = GetUnitAcquireRange(whichLurker)
//set lurkerRange = R2I(lurkerAcqRange)
set lurkerSpinesReal = lurkerAcqRange/100.00
//set lurkerSpinesReal = 6.00
//set spineCount = 6
set spineCount = R2I(Math_Floor(lurkerSpinesReal))
//set spineCount = ModuloInteger(lurkerRange, 100)
set sc_array_lurker_spineCount[lurkerId] = spineCount
endif
// Set the Lurker's attack type if it's not already set
if attackType == null then
set attackType = ConvertAttackType(GetUnitAbilityLevel(whichLurker, sc_ABIL_ATTACK_TYPE) - 1)
set sc_array_lurker_attacktype[lurkerId] = attackType
endif
// If the Lurker's base damage has not been set, set it.
if baseDamageAmount == null then
// Base damage not-yet set
set baseDamageAmount = I2R(GetUnitPointValue(whichLurker))
set damageUpgLvl = sc_array_lurker_damageUpgLvlPerPlayer[GetPlayerId(lurkerOwner)]
if damageUpgLvl != GetPlayerTechCount(lurkerOwner, sc_TECH_ZERG_MISSILE_ATTACKS, true) then
// Damage upgrade was pre-set to something higher than 0, not researched
set damageUpgLvl = GetPlayerTechCount(lurkerOwner, sc_TECH_ZERG_MISSILE_ATTACKS, true)
set sc_array_lurker_damageUpgLvlPerPlayer[GetPlayerId(lurkerOwner)] = damageUpgLvl
endif
set baseDamageAmount = baseDamageAmount + I2R(sc_INT_LURKER_DAMAGE_UPG_AMOUNT * damageUpgLvl)
set sc_array_lurker_baseDamage[lurkerId] = baseDamageAmount
endif
// Compute the actual damage the Lurker should do by checking for damage buffs
set sc_array_lurker_currentDamage[lurkerId] = SC_ApplyDamageBuff(whichLurker, baseDamageAmount)
if debugThis then
call DisplayTextToPlayer(lurkerOwner, 0, 0, "Lurker #" + I2S(lurkerId) + ":")
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- lurkerPt: (" + R2S(GetLocationX(lurkerPt)) + "," + R2S(GetLocationY(lurkerPt)) + ")")
//call DisplayTextToPlayer(lurkerOwner, 0, 0, "- Acq Range: " + R2S(lurkerAcqRange))
//call DisplayTextToPlayer(lurkerOwner, 0, 0, "- Lurker Spines (Real): " + R2S(lurkerSpinesReal))
//call DisplayTextToPlayer(lurkerOwner, 0, 0, "- Spine Count: " + I2S(spineCount))
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- Attack Type: " + I2S(GetUnitAbilityLevel(whichLurker, sc_ABIL_ATTACK_TYPE) - 1))
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- Pre-Buff Damage: " + R2S(baseDamageAmount))
endif
// Setup the spines
set sc_array_lurker_currentSpine[lurkerId] = 0
call DestroyGroup(sc_array_lurker_spineDamageGroup[lurkerId])
// End this code early for testing
//return
set bj_forLoopAIndex=1
set bj_forLoopAIndexEnd=spineCount
loop
exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
if debugThis then
//call DisplayTextToPlayer(lurkerOwner, 0, 0, "- ForLoop Index: " + I2S(bj_forLoopAIndex))
//exitwhen bj_forLoopAIndex == 3
endif
// Set the position of the spine
set spinePt = PolarProjectionBJ(lurkerPt, ( I2R(bj_forLoopAIndex) * sc_REAL_LURKER_SPINE_DISTANCE ), GetUnitFacing(whichLurker))
// Set the delay for spawning the spine
set spineDelay =( ( I2R(bj_forLoopAIndex) - 0.99 ) * sc_REAL_LURKER_SPINE_DELAY )
// Set the delay for the spine dealing damage
set spineDelayDamage = spineDelay + sc_REAL_LURKER_SPINE_DELAY + (sc_REAL_LURKER_SPINE_DUR/2.00)
// End this code early for testing
if debugThis and bj_forLoopAIndex == 2 then
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- spinePt: (" + R2S(GetLocationX(spinePt)) + "," + R2S(GetLocationY(spinePt)) + ")")
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- spineDelay: " + R2S(spineDelay))
call DisplayTextToPlayer(lurkerOwner, 0, 0, "- spineDelayDamage: " + R2S(spineDelayDamage))
endif
// Comment out the below function call to observe that it is responsible for a crash.
call CreateNUnitsAtLoc(1, sc_TIMER_LURKER_SPINE, lurkerOwner, spinePt, bj_UNIT_FACING)
// CREATE TIMER FOR SPINE ART DUMMY
if debugThis then
exitwhen bj_forLoopAIndex == 2
endif
call SetUnitExploded(bj_lastCreatedUnit, true)
call SetUnitUserData(bj_lastCreatedUnit, lurkerId)
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', spineDelay)
// Create a sound dummy for the 'Lurker Fire' sound
if bj_forLoopAIndex == 1 then
call CreateNUnitsAtLoc(1, sc_SOUNDDUMMY_LURKER_FIRE, lurkerOwner, spinePt, bj_UNIT_FACING)
call SetUnitExploded(bj_lastCreatedUnit, true)
call SetUnitAnimation(bj_lastCreatedUnit, "birth")
//call SetUnitVertexColorBJ(GetLastCreatedUnit(), 100, 100, 100, 100.00)
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1.00)
endif
// CREATE TIMER FOR DAMAGE DUMMY
call CreateNUnitsAtLoc(1, sc_TIMER_LURKER_SPINE_DAMAGE, lurkerOwner, spinePt, bj_UNIT_FACING)
call SetUnitExploded(bj_lastCreatedUnit, true)
//call SetUnitVertexColorBJ(GetLastCreatedUnit(), 100, 100, 100, 100.00)
call SetUnitUserData(bj_lastCreatedUnit, lurkerId)
//call SetUnitAbilityLevelSwapped('Szsn', GetLastCreatedUnit(), GetForLoopIndexA())
call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', spineDelayDamage )
set bj_forLoopAIndex=bj_forLoopAIndex + 1
// Clear leak
call RemoveLocation(spinePt)
endloop
// Clear leak
set attackType = null
endfunction
I've also attached my current Blizzard.j, as well as a zip containing a stripped-down version of my mod and a test map, so you can try it in-game yourself. Like the Zerg Campaign, it requires version 1.28 of War3.