• 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.

[JASS] Creep Respawn System

Status
Not open for further replies.
Level 7
Joined
Feb 26, 2005
Messages
210
I'm makeing a creep respawn system for my map, Lost Temple Advanced. It should work in two triggers, but the problem is the actual respawning. Look here:
  • Load Creep Locations
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set G = (Units owned by Neutral Hostile)
      • Unit Group - Pick every unit in G and do (Actions)
        • Loop - Actions
          • Set V = (V + 1)
          • Unit - Set the custom value of (Picked unit) to V
          • Set CreepLoc[(Custom value of (Picked unit))] = (Position of (Picked unit))
          • Set CreepAngle[(Custom value of (Picked unit))] = (Facing of (Picked unit))
          • Set CreepRange[(Custom value of (Picked unit))] = (Current acquisition range of (Picked unit))
What this dose is gives all units on the field custom value so that the Original Location, Faceing Angle, and Aquisitation Range can be stored in variables (no worries about clearing them to check for leaks- once set they don't need to be changed).

The second trigger is in JASS:
JASS:
function IsCreep takes nothing returns boolean
    if GetOwningPlayer(GetDyingUnit()) == Player(12) and IsUnitType(GetDyingUnit(), UNIT_TYPE_SUMMONED) == false then
        return true
    else
        return false
    endif
endfunction

function IsPest takes nothing returns boolean
    if GetOwningPlayer(GetFilterUnit()) != Player(12) and IsUnitAliveBJ(GetFilterUnit()) == true and GetUnitTypeId(GetFilterUnit()) != 'oeye' then
        return true
    else
        return false
    endif
endfunction

function RespawnCreep takes nothing returns nothing
    local integer i = GetUnitUserData(GetDyingUnit())
    local group g
    local boolexpr b = Condition(function IsPest)
    set udg_CreepDead[i] = true
    loop
        exitwhen (udg_CreepDead[i] == false)
        call TriggerSleepAction(5.00)
        set g = GetUnitsInRangeOfLocMatching(500.00, udg_CreepLoc[i], b)
        if CountUnitsInGroup(g) == 0 then
            call CreateNUnitsAtLoc(1, GetUnitTypeId(GetDyingUnit()), Player(12), udg_CreepLoc[i], udg_CreepAngle[i])
            call SetUnitAcquireRangeBJ(GetLastCreatedUnit(), udg_CreepRange[i])
            call SetUnitUserData(GetLastCreatedUnit(), GetUnitUserData(GetDyingUnit()))
            set udg_CreepDead[GetUnitUserData(GetDyingUnit())] = false
        endif
        set g = null
    endloop
endfunction

//===========================================================================
function InitTrig_Creep_Respawn takes nothing returns nothing
    set gg_trg_Creep_Respawn = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Creep_Respawn, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( gg_trg_Creep_Respawn, Condition( function IsCreep ) )
    call TriggerAddAction( gg_trg_Creep_Respawn, function RespawnCreep )
endfunction
What this is supposed to do is check every x seconds after a unit dies for any "Pests". "Pests" being units within a y-size radius which meet the following conditions:

a) Not controlled by Neutral Hostile.
b) Not dead.
c) Not a Sentry Ward (Sentry Wards are exluded since they can more-than-easily prevent creep respawning).

If the number of Pests is 0, then the creep respawns at its original location with the old unit's facing angle and aqusitation range.

However, this is not working when more than one unit is dead at the same time. How do I solve the problem?
 
You need a local variable for the dying unit.
JASS:
local unit dieunit = GetDyingUnit()
otherwise another dying unit will overwrite the current dying unit.

And clean up your booleans
JASS:
function IsCreep takes nothing returns boolean
    return GetOwningPlayer(GetDyingUnit()) == Player(12) and IsUnitType(GetDyingUnit(), UNIT_TYPE_SUMMONED) == false
endfunction
JASS:
function IsPest takes nothing returns boolean
    return GetOwningPlayer(GetFilterUnit()) != Player(12) and IsUnitAliveBJ(GetFilterUnit()) == true and GetUnitTypeId(GetFilterUnit()) != 'oeye'
endfunction
 
Level 11
Joined
Oct 13, 2005
Messages
233
I'd like to point out that you're using a lot of BJ functions in your JASS code. You should instead use natives. For instance, use CreateUnit or CreateUnitAtLoc instead of CreateNUnitsAtLoc. In that case, you'll have to use a local unit variable since CreateUnit/CreateUnitAtLoc don't set bj_lastCreatedUnit to the unit they create.

SetUnitAcquireRangeBJ can be changed to SetUnitAcquireRange (just remove the BJ). This particular BJ function is just pointless. You're also setting a local variable to GetUnitUserData(GetDyingUnit()), and for some parts of the trigger you're calling both those functions again instead of just using the local variable.

IsUnitAliveBJ(unit) == true can be changed to GetWidgetLife(unit) > 0. IsUnitAliveBJ returns 'not IsUnitDeadBJ' which returns 'GetUnitState(unit, UNIT_STATE_LIFE) > 0'. GetWidgetLife does the same thing, only it's shorter to type (and can be used on all widgets). A widget is a unit, destructable, or item.

For the group part, you're never destroying the group you assign to the variable 'g', so you have a memory leak for each time the loop is run. Also, let's take a look at what GetUnitsInRangeOfLoc does:
JASS:
function GetUnitsInRangeOfLocMatching takes real radius, location whichLocation, boolexpr filter returns group
    local group g = CreateGroup()
    call GroupEnumUnitsInRangeOfLoc(g, whichLocation, radius, filter)
    call DestroyBoolExpr(filter)
    return g
endfunction
There's another one of your problems, it's destroying your boolexpr! You would need to create a new boolexpr each time you used this function if you were to keep using this function. Instead, at each loop, set g = CreateGroup() then use the function GroupEnumUnitsInRangeOfLoc(g, location, radius, boolexpr) and then go about as normal (make sure to destroy the group after each loop).

Also, you don't need to null the variable g after each loop, you only need to null it once at the end of the trigger. You should also destroy your boolexpr at the end too and then null your local variable b as well.
 
Level 7
Joined
Feb 26, 2005
Messages
210
Okay, I changed the trigger around and it works now, but why do I need to use natives instead of BJ functions? And how do I refer to the last created unit with the CreateUnit/CreateUnitAtLoc function if bj_lastCreatedUnit dosn't work?
JASS:
function IsCreep takes nothing returns boolean
    return GetOwningPlayer(GetDyingUnit()) == Player(12) and IsUnitType(GetDyingUnit(), UNIT_TYPE_SUMMONED) == false
endfunction

function IsPest takes nothing returns boolean
    return GetOwningPlayer(GetFilterUnit()) != Player(12) and GetOwningPlayer(GetFilterUnit()) != Player(15) and GetWidgetLife(GetFilterUnit()) > 0 and GetUnitTypeId(GetFilterUnit()) != 'oeye'
endfunction

function RespawnCreep takes nothing returns nothing
    local integer i = GetUnitUserData(GetDyingUnit())
    local real interval = 0.
    local unit u = GetDyingUnit()
    local group g
    local boolexpr b = Condition(function IsPest)
    local boolean x
    set udg_CreepDead[i] = true

    loop
        exitwhen (udg_CreepDead[i] == false)
        call TriggerSleepAction(0.10)
        set g = CreateGroup()
        call GroupEnumUnitsInRangeOfLoc(g, udg_CreepLoc[i], 600.00, b)
        if CountUnitsInGroup(g) == 0 then
            set interval = interval + 0.1
            if interval >= 120.00 then
                call CreateNUnitsAtLoc(1, GetUnitTypeId(u), Player(12), udg_CreepLoc[i], udg_CreepAngle[i])
                call SetUnitAcquireRangeBJ(GetLastCreatedUnit(), udg_CreepRange[i])
                call SetUnitUserData(GetLastCreatedUnit(), i)
                set udg_CreepDead[i] = false
            endif
        endif
        call DestroyGroup(g)
    endloop

    set g = null
    set b = null
endfunction

//===========================================================================
function InitTrig_Creep_Respawn takes nothing returns nothing
    set gg_trg_Creep_Respawn = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Creep_Respawn, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddCondition( gg_trg_Creep_Respawn, Condition( function IsCreep ) )
    call TriggerAddAction( gg_trg_Creep_Respawn, function RespawnCreep )
endfunction
 
Level 11
Joined
Oct 13, 2005
Messages
233
set <unit> = CreateUnit(...) and so on. Using BJ functions is a bad habit, why call a function that calls another function when you can just call the second function? I'm not saying all BJ functions are bad, it's just that most tend to be. Like TriggerRegisterAnyUnitEventBJ is useful. Also, you should define 'u' before 'i' so you can just use the variable 'u' instead of the function GetDyingUnit() again.
 
Status
Not open for further replies.
Top