• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Trigger] Questions about the Detecting Corpse in target area.

Status
Not open for further replies.
Level 9
Joined
May 12, 2018
Messages
145
Hello there. I just entered Jass script.
I am using Warcraft Reforged latest patch and its editor, but I'm in trouble now.

This is the work I am making and want to make.

1. Replicate Devour Magic ability and change the appropriate target to Tree, etc. to remove the effectiveness of basic ability.
2. When using DevourMagicReplicated ability, Detect whether there is Corpses within the target area or whether AbilityCaster has 75 HP over.
2.1 If there is a Corpse within the target area, pick a Corpse closest to the center of target area. and set the TempPoint to that Corpse's location and remove the targeted Corpse.
2.2 If there is no Corpse and AbilityCaster has 75 HP over, remove AbilityCaster's HP 75 and It uses the initially specified target area as it is.
3. Returning true either of the two conditions, generate Blight in the target area(TempPoint) and exerts an Abolish Magic effect on all units within the 200 range of TempPoint.

This is the custom script I'm writing.
I'm writing scripts little by little, but I couldn't complete them due to problems on the way. I ask for your understanding on this.

  • Events
    • Unit - A unit Starts the effect of an ability
  • Conditions
    • (Ability being cast) Equal to Mad Ritual // (this is a DevourMagicDuplicated
  • Actions
    • Custom script: local group UGDeadSearch = CreateGroup()
    • Custom script: local group UGDead = CreateGroup()
    • Custom script: local unit UnitDead
    • Custom script: local unit UnitDead2
    • Custom script: local unit Ucr
    • Custom script: local unit UnitCast = GetSpellAbilityUnit()
    • Custom script: local player Pc = GetOwningPlayer(UnitCast)
    • Custom script: local real xT
    • Custom script: local real yT
    • Custom script: local effect Effect
    • -------- Initial --------
    • Custom script: set udg_TempPoint = GetSpellTargetLoc()
    • Custom script: call GroupEnumUnitsInRangeOfLoc(UGDeadSearch, udg_TempPoint, 200, null)
    • Custom script: loop
    • Custom script: set UnitDead = FirstOfGroup(UGDeadSearch)
    • Custom script: exitwhen UGDeadSearch == null
    • Custom script: if GetUnitTypeId(UnitDead) == 0 or IsUnitType(UnitDead, UNIT_TYPE_DEAD) then // Detect the Dead by Hiveworkshop, Zwiebelchen --------
    • Custom script: call GroupAddUnit ( UGDead, UnitDead )
    • Custom script: else
    • Custom script: endif
    • Custom script: call GroupRemoveUnit(UGDeadSearch, UnitDead)
    • Custom script: set UnitDead = null
    • Custom script: endloop
    • Custom script: if ( CountUnitsInGroup(UGDead) >= 1 ) then
    • Custom script: loop
    • Custom script: set UnitDead2 = FirstOfGroup(UGDead)
    • Custom script: exitwhen UGDead == null
    • Custom script: call GroupRemoveUnit(UGDead, UnitDead2)
    • Custom script: set UnitDead2 = null
    • Custom script: endloop
    • Custom script: set xT = GetLocationX(udg_TempPoint)
    • Custom script: set yT = GetLocationY(udg_TempPoint)
    • Custom script: set Ucr = CreateUnit((Pc), 'u003', xT,yT, 0) // u003 is Locust.
    • Custom script: call UnitAddAbility(Ucr, 'A06B') // A06B is Blight ( from 'Abgs')
    • Custom script: call UnitApplyTimedLife(Ucr, 'BTLF', 1.00)
    • Custom script: set Ucr = null
    • Custom script: set Effect = AddSpecialEffectLoc( "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl" , udg_TempPoint)
    • Custom script: call DestroyEffect(Effect)
    • Custom script: set Effect = null
    • Custom script: set Effect = AddSpecialEffectTarget( "Objects\\Spawnmodels\\NightElf\\NightElfLargeDeathExplode\\NightElfLargeDeathExplode.mdl", UnitCast, "origin")
    • Custom script: call DestroyEffect(Effect)
    • Custom script: set Effect = null
    • Custom script: endif
    • Custom script: set Effect = null
    • Custom script: set Ucr = null
    • Custom script: set UnitDead = null
    • Custom script: set UnitDead2 = null
    • Custom script: set UnitCast = null
    • Custom script: set Pc = null
    • Custom script: call DestroyGroup(UGDead)
    • Custom script: set UGDead = null
    • Custom script: call DestroyGroup(UGDeadSearch)
    • Custom script: set UGDeadSearch = null
    • Custom script: set udg_TempPoint = null

Whenever I use this ability, there is a lag(no lag when I turn off this trigger), and from the second loop, it does not work as desired.
I think the second loop will run after first loop exited, so I wrote the script like this, but this was not answer I guess.
Or do I have to learn struct and library and etc. ? I'm also reading JASS Tutorial, but this is not easy either.
 
Last edited:
Yeah, you should start working completely in Jass for better readability.

Anyway, it seems that the lag is caused by this part in the loops:
  • Custom script: loop
  • Custom script: set UnitDead = FirstOfGroup(UGDeadSearch)
  • Custom script: exitwhen UGDeadSearch == null <------- HERE
  • Custom script: loop
  • Custom script: set UnitDead2 = FirstOfGroup(UGDead)
  • Custom script: exitwhen UGDead == null <------- AND HERE

Those groups are not nulled at any moment, so the loops continue indefinitely. Instead of checking if the group is null, do it with the unit:
  • Custom script: loop
  • Custom script: set UnitDead = FirstOfGroup(UGDeadSearch)
  • Custom script: exitwhen UnitDead == null <------- FIXED
  • Custom script: loop
  • Custom script: set UnitDead2 = FirstOfGroup(UGDead)
  • Custom script: exitwhen UnitDead2 == null <------- FIXED

And null the groups after the loops are finished:
  • Custom script: endloop
  • Custom script: call DestroyGroup(UGDeadSearch)
  • Custom script: set UGDeadSearch == null
  • Custom script: endloop
  • Custom script: call DestroyGroup(UGDead)
  • Custom script: set UGDead == null
 
Level 9
Joined
May 12, 2018
Messages
145
I'd say either work entirely in Jass or go back to using mostly GUI. Triggering like this is impossible to read :p
Yeah, you should start working completely in Jass for better readability.

Anyway, it seems that the lag is caused by this part in the loops:
  • Custom script: loop
  • Custom script: set UnitDead = FirstOfGroup(UGDeadSearch)
  • Custom script: exitwhen UGDeadSearch == null <------- HERE
  • Custom script: loop
  • Custom script: set UnitDead2 = FirstOfGroup(UGDead)
  • Custom script: exitwhen UGDead == null <------- AND HERE

Those groups are not nulled at any moment, so the loops continue indefinitely. Instead of checking if the group is null, do it with the unit:
  • Custom script: loop
  • Custom script: set UnitDead = FirstOfGroup(UGDeadSearch)
  • Custom script: exitwhen UnitDead == null <------- FIXED
  • Custom script: loop
  • Custom script: set UnitDead2 = FirstOfGroup(UGDead)
  • Custom script: exitwhen UnitDead2 == null <------- FIXED

And null the groups after the loops are finished:
  • Custom script: endloop
  • Custom script: call DestroyGroup(UGDeadSearch)
  • Custom script: set UGDeadSearch == null
  • Custom script: endloop
  • Custom script: call DestroyGroup(UGDead)
  • Custom script: set UGDead == null

Thanks to reply! As you advised, I rewritten the testing script with Custom Text. There's no lag, loops seem to be going well sequentially.
Now I have to find a way to make Closest Corpse Detection...
Untitled-4.jpg


JASS:
function Trig_CultistMadRitual_Copy_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A00V' ) ) then
        return false
    endif
    return true
endfunction

function Trig_CultistMadRitual_Copy_Actions takes nothing returns nothing
    local group UGDeadSearch = CreateGroup()
    local group UGDead = CreateGroup()
    local unit RitualUnitDead
    local unit UnitCast = GetSpellAbilityUnit()
    local unit Ucr
    local player Pc = GetOwningPlayer(UnitCast)
    local effect Effect
    local real xT
    local real yT
    set udg_TempPoint = GetSpellTargetLoc()
    call GroupEnumUnitsInRangeOfLoc(UGDeadSearch, udg_TempPoint, 200, null)
    call BJDebugMsg("Detected ability used")
    loop
        set RitualUnitDead = FirstOfGroup(UGDeadSearch)
        exitwhen RitualUnitDead == null
        call GroupRemoveUnit(UGDeadSearch, RitualUnitDead)
        if GetUnitTypeId(RitualUnitDead) == 0 or IsUnitType(RitualUnitDead, UNIT_TYPE_DEAD) then // Detect the Dead by Hiveworkshop, Zwiebelchen
        call GroupAddUnit(UGDead, RitualUnitDead)
        call BJDebugMsg("Detect the Dead and moved to another group")
        else
        call BJDebugMsg("Couldnt detect the dead")
        endif
        set RitualUnitDead = null
    endloop
    call DestroyGroup(UGDeadSearch)
    set UGDeadSearch = null
    if ( CountUnitsInGroup(UGDead) >= 1 ) then
        loop
            set RitualUnitDead = FirstOfGroup(UGDead)
            exitwhen RitualUnitDead == null
            call GroupRemoveUnit(UGDead,RitualUnitDead)
            set RitualUnitDead = null
        endloop
        set xT = GetLocationX(udg_TempPoint)
        set yT = GetLocationY(udg_TempPoint)
        set Ucr = CreateUnit((Pc), 'u003', xT,yT, 0)
        call UnitAddAbility(Ucr, 'A06B') // Blight 200
        call UnitApplyTimedLife(Ucr, 'BTLF', 1.00)
        set Ucr = null
        set Effect = AddSpecialEffectLoc( "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl" , udg_TempPoint)
        call DestroyEffect(Effect)
        set Effect = null
        call BJDebugMsg("Detected UGDead >= 1")
    else
        call BJDebugMsg("Cant detect UGDead")
    endif
    call DestroyGroup(UGDead)
    set UGDead = null
    set RitualUnitDead = null
    set UnitCast = null
    set Pc = null
    set udg_TempPoint = null

endfunction

//===========================================================================
function InitTrig_CultistMadRitual_Copy takes nothing returns nothing
    set gg_trg_CultistMadRitual_Copy = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_CultistMadRitual_Copy, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_CultistMadRitual_Copy, Condition( function Trig_CultistMadRitual_Copy_Conditions ) )
    call TriggerAddAction( gg_trg_CultistMadRitual_Copy, function Trig_CultistMadRitual_Copy_Actions )
endfunction
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,868
Getting the closest of anything is actually pretty simple. Here's something already made that you can use:

Corpses are considered units so all you need to do is add all of the dead units in the targeted area to a group. Then from there enumerate over said group getting the distances of the units from the center of the area and comparing this value to one another. The unit with the lowest value distance is the closest.

Here's how it'd be done in GUI:
  • Actions
    • Set VariableSet Distance = 999999.00
    • Set VariableSet PointA = (Target point of ability being cast)
    • Set VariableSet Group = (Units within 512.00 of PointA matching (((Matching unit) is alive) Equal to False).)
    • Unit Group - Pick every unit in Group and do (Actions)
      • Loop - Actions
        • Set VariableSet PointB = (Position of (Picked unit))
        • Set VariableSet CurrentDistance = (Distance between PointA and PointB)
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • CurrentDistance Less than or equal to Distance
          • Then - Actions
            • Set VariableSet Distance = CurrentDistance
            • Set VariableSet ClosestCorpse = (Picked unit)
          • Else - Actions
        • Custom script: call RemoveLocation (udg_PointB)
    • Custom script: call RemoveLocation (udg_PointA)
    • Custom script: call DestroyGroup (udg_Group)
ClosestCorpse will be equal to the closest unit once the loop is finished.

Just make sure that ClosestCorpse isn't an old unit from a previous cast, this could happen if you fail to find any corpses in the area.
  • solution:
  • Set VariableSet ClosestCorpse = No unit
 
Last edited:
Level 9
Joined
May 12, 2018
Messages
145
Getting the closest of anything is actually pretty simple. Here's something already made that you can use:

Corpses are considered units so all you need to do is add all of the dead units in the targeted area to a group. Then from there enumerate over said group getting the distances of the units from the center of the area and comparing this value to one another. The unit with the lowest value distance is the closest.

Here's how it'd be done in GUI:
  • Actions
    • Set VariableSet Distance = 999999.00
    • Set VariableSet PointA = (Target point of ability being cast)
    • Set VariableSet Group = (Units within 512.00 of PointA matching (((Matching unit) is alive) Equal to False).)
    • Unit Group - Pick every unit in Group and do (Actions)
      • Loop - Actions
        • Set VariableSet PointB = (Position of (Picked unit))
        • Set VariableSet CurrentDistance = (Distance between PointA and PointB)
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • CurrentDistance Less than or equal to Distance
          • Then - Actions
            • Set VariableSet Distance = CurrentDistance
            • Set VariableSet ClosestCorpse = (Picked unit)
          • Else - Actions
        • Custom script: call RemoveLocation (udg_PointB)
    • Custom script: call RemoveLocation (udg_PointA)
    • Custom script: call DestroyGroup (udg_Group)
ClosestCorpse will be equal to the closest unit once the loop is finished.

Just make sure that ClosestCorpse isn't an old unit from a previous cast, this could happen if you fail to find any corpses in the area.
  • solution:
  • Set VariableSet ClosestCorpse = No unit

Thank you! But that GUI trigger is it safe from leaks? I am writing a script by referring to Blizzard.J on the Sourceforge site, I learned about the matching unit function and most of the GUI-BJ functions cannot avoid leakage because there is no script to destroy and set null the local group, and also leakage occurs if the existing variables are overwritten without emptying them.
I'm very afraid of memory loss due to leakage. So I'm studying JASS.


I'm reading the thread you showed me and the script written by Xonok now, but it's still too hard for me to fully understand. I am also considering about rewriting the script that I written, like Corpse Detection separately with a new library or function for later purposes to save file size and memory.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,868
I'm removing the Point/Group leaks. That's what the Custom script is for that says Remove//Destroy. It's not the end of the world to have a little bit of leaks, there's a plethora of GUI maps that run just fine and half of them don't even clean up their memory leaks like I'm doing. But of course, writing in Jass/Lua is superior to GUI and once mastered will make your life easier and open the door to more efficient code.

Anyway, the concept is pretty simple:
First we Set Distance to an extremely high value. It's important that this value is larger than the area of effect of the ability.
Next we Group the Dead units near PointA (target point of ability being cast).
Next we enumerate or loop over the Group, running the actions once for each (Picked unit).
Inside the Loop Actions:
  • A unit that hasn't been picked yet is set as the very first (picked unit).
  • CurrentDistance is set to the distance between the (target point of ability being) and the (picked unit) --> PointA and PointB. Since CurrentDistance is <= Distance (9999999), it's guaranteed that our If Then Else which checks if CurrentDistance <= Distance will return True and the Then - Actions will happen.
  • The Then - Action occur, setting our (picked unit) as the ClosestCorpse. This means that it's considered the closest unit as of right now. It also sets Distance to be equal to CurrentDistance, which is important because this means that the NEXT (picked unit) will have it's distance (CurrentDistance) compared to the ClosestCorpse's distance.
  • The Loop will then cycle onto the next (picked unit) and run the Actions again. So from this point on every (picked unit)'s distance will be compared to the ClosestCorpse's distance. If the (picked unit) is closer, it becomes the new ClosestCorpse and Distance shrinks down to this closer value. This guarantees that each unit will be compared to the ClosestCorpse and will either be ignored or become the new ClosestCorpse.
After it's all said and done you will be left with ClosestCorpse being equal to the winner, in other words the unit that had the smallest distance value. If there was a tie for closest then the last (picked unit) that was deemed closest will be chosen. You can adjust this tie behavior by changing the <= comparison to just <, which will make the system ignore units that have the same exact distance values (very unlikely).
 
Last edited:
Status
Not open for further replies.
Top