• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Boss Dependent Creep Spawn Points

Status
Not open for further replies.
Level 1
Joined
Nov 30, 2019
Messages
2
Hi guys,

I'm currently working on a small map where I have an area where 1 out of 2 bosses is randomly summoned into an area. Depending on which boss is summoned, it changes the group of creeps laid out throughout the map.

Now and I don't know if this is possible, but when a boss is killed and is replaced with another random boss, the entire creep group dependent on them is removed and replaced with the creep group classified under the new boss.

Is this possible, any advice on how to get started?

EDIT:

1) So I got the respawning boss randomiser down, however I can't seem to find a way to replace the units.
I'm using a trigger to respawn all existing creep units on the map to respawn in their initial location some time after the player kills them.

But I can't get a trigger to take those existing creeps on the map and replace them with bosses unit group when said boss spawns in. Hope that makes sense.

Any advice at all would be appreciated!
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,208
Is this possible, any advice on how to get started?
Yes it is very much possible.

At map initialization a typical creep respawn system will iterate all appropriate units on the map and for each one assign them a unique index in a set of arrays to hold their unit type and their initial location. This index is then stored to the unit as a custom value. When the unit dies the custom value for the unit is looked up and the unit is scheduled to respawn some time later.

What one can do is extend such a system so that it also tracks what the current unit is for that respawn index. One also creates a table (or hashtable) which maps a unit type to its alternative unit type for the boss (may need JASS or Lua directly). When the boss changes (new one spawns), iterate through all respawn indices and resolve the correct unit type (either the one specified, or the result from passing it through the alternative table for that boss). If the unit exists and is the correct unit type then do nothing. If the unit exists but it not the correct unit type then replace it and update the array to the new unit (replace removes the old unit and makes a new one breaking references). If the unit does not exist then nothing has to be done as they can respawn naturally (forcing a respawn would make code more complex). The respawn system itself is changed so that when it respawns the unit it also resolves the correct unit type as described above and creates that unit type and assigns it to the array.

Potentially more efficient systems exist if one enters each respawn as data rather than detecting based on units pre-placed on the map. However the efficiency does not matter as the system is trivial in any case.
There's an action that replaces a unit with another unit.
It works by removing the existing unit and making a new unit with some of the stats translated over. When using the action one must update all references to the old unit to refer to the new unit as that does not happen automatically. For example a unit variable pointing to the old unit will not automatically point to the new unit when one replaces the old unit, one must update the unit variable to point at the new unit.
 
Level 39
Joined
Feb 27, 2007
Messages
5,038
There's an action that replaces a unit with another unit.
It also has two reference leaks:
JASS:
function ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns unit
    local unit    oldUnit = whichUnit    // <------------
    local unit    newUnit     // <----------------
    local boolean wasHidden
    local integer index
    local item    indexItem
    local real    oldRatio

    // If we have bogus data, don't attempt the replace.
    if (oldUnit == null) then
        set bj_lastReplacedUnit = oldUnit
        return oldUnit
    endif

    // Hide the original unit.
    set wasHidden = IsUnitHidden(oldUnit)
    call ShowUnit(oldUnit, false)

    // Create the replacement unit.
    if (newUnitId == 'ugol') then
        set newUnit = CreateBlightedGoldmine(GetOwningPlayer(oldUnit), GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    else
        set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    endif

    // Set the unit's life and mana according to the requested method.
    if (unitStateMethod == bj_UNIT_STATE_METHOD_RELATIVE) then
        // Set the replacement's current/max life ratio to that of the old unit.
        // If both units have mana, do the same for mana.
        if (GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_LIFE) / GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE)
            call SetUnitState(newUnit, UNIT_STATE_LIFE, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        endif

        if (GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) > 0) and (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_MANA) / GetUnitState(oldUnit, UNIT_STATE_MAX_MANA)
            call SetUnitState(newUnit, UNIT_STATE_MANA, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_ABSOLUTE) then
        // Set the replacement's current life to that of the old unit.
        // If the new unit has mana, do the same for mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(oldUnit, UNIT_STATE_LIFE))
        if (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(oldUnit, UNIT_STATE_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_DEFAULTS) then
        // The newly created unit should already have default life and mana.
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_MAXIMUM) then
        // Use max life and mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
    else
        // Unrecognized unit state method - ignore the request.
    endif

    // Mirror properties of the old unit onto the new unit.
    //call PauseUnit(newUnit, IsUnitPaused(oldUnit))
    call SetResourceAmount(newUnit, GetResourceAmount(oldUnit))

    // If both the old and new units are heroes, handle their hero info.
    if (IsUnitType(oldUnit, UNIT_TYPE_HERO) and IsUnitType(newUnit, UNIT_TYPE_HERO)) then
        call SetHeroXP(newUnit, GetHeroXP(oldUnit), false)

        set index = 0
        loop
            set indexItem = UnitItemInSlot(oldUnit, index)
            if (indexItem != null) then
                call UnitRemoveItem(oldUnit, indexItem)
                call UnitAddItem(newUnit, indexItem)
            endif

            set index = index + 1
            exitwhen index >= bj_MAX_INVENTORY
        endloop
    endif

    // Remove or kill the original unit.  It is sometimes unsafe to remove
    // hidden units, so kill the original unit if it was previously hidden.
    if wasHidden then
        call KillUnit(oldUnit)
        call RemoveUnit(oldUnit)
    else
        call RemoveUnit(oldUnit)
    endif

    set bj_lastReplacedUnit = newUnit
    return newUnit
endfunction
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,898
It also has two reference leaks:
JASS:
function ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns unit
    local unit    oldUnit = whichUnit    // <------------
    local unit    newUnit     // <----------------
    local boolean wasHidden
    local integer index
    local item    indexItem
    local real    oldRatio

    // If we have bogus data, don't attempt the replace.
    if (oldUnit == null) then
        set bj_lastReplacedUnit = oldUnit
        return oldUnit
    endif

    // Hide the original unit.
    set wasHidden = IsUnitHidden(oldUnit)
    call ShowUnit(oldUnit, false)

    // Create the replacement unit.
    if (newUnitId == 'ugol') then
        set newUnit = CreateBlightedGoldmine(GetOwningPlayer(oldUnit), GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    else
        set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    endif

    // Set the unit's life and mana according to the requested method.
    if (unitStateMethod == bj_UNIT_STATE_METHOD_RELATIVE) then
        // Set the replacement's current/max life ratio to that of the old unit.
        // If both units have mana, do the same for mana.
        if (GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_LIFE) / GetUnitState(oldUnit, UNIT_STATE_MAX_LIFE)
            call SetUnitState(newUnit, UNIT_STATE_LIFE, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        endif

        if (GetUnitState(oldUnit, UNIT_STATE_MAX_MANA) > 0) and (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            set oldRatio = GetUnitState(oldUnit, UNIT_STATE_MANA) / GetUnitState(oldUnit, UNIT_STATE_MAX_MANA)
            call SetUnitState(newUnit, UNIT_STATE_MANA, oldRatio * GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_ABSOLUTE) then
        // Set the replacement's current life to that of the old unit.
        // If the new unit has mana, do the same for mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(oldUnit, UNIT_STATE_LIFE))
        if (GetUnitState(newUnit, UNIT_STATE_MAX_MANA) > 0) then
            call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(oldUnit, UNIT_STATE_MANA))
        endif
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_DEFAULTS) then
        // The newly created unit should already have default life and mana.
    elseif (unitStateMethod == bj_UNIT_STATE_METHOD_MAXIMUM) then
        // Use max life and mana.
        call SetUnitState(newUnit, UNIT_STATE_LIFE, GetUnitState(newUnit, UNIT_STATE_MAX_LIFE))
        call SetUnitState(newUnit, UNIT_STATE_MANA, GetUnitState(newUnit, UNIT_STATE_MAX_MANA))
    else
        // Unrecognized unit state method - ignore the request.
    endif

    // Mirror properties of the old unit onto the new unit.
    //call PauseUnit(newUnit, IsUnitPaused(oldUnit))
    call SetResourceAmount(newUnit, GetResourceAmount(oldUnit))

    // If both the old and new units are heroes, handle their hero info.
    if (IsUnitType(oldUnit, UNIT_TYPE_HERO) and IsUnitType(newUnit, UNIT_TYPE_HERO)) then
        call SetHeroXP(newUnit, GetHeroXP(oldUnit), false)

        set index = 0
        loop
            set indexItem = UnitItemInSlot(oldUnit, index)
            if (indexItem != null) then
                call UnitRemoveItem(oldUnit, indexItem)
                call UnitAddItem(newUnit, indexItem)
            endif

            set index = index + 1
            exitwhen index >= bj_MAX_INVENTORY
        endloop
    endif

    // Remove or kill the original unit.  It is sometimes unsafe to remove
    // hidden units, so kill the original unit if it was previously hidden.
    if wasHidden then
        call KillUnit(oldUnit)
        call RemoveUnit(oldUnit)
    else
        call RemoveUnit(oldUnit)
    endif

    set bj_lastReplacedUnit = newUnit
    return newUnit
endfunction
Wow, didn't know it also had that leak :/ Lame Blizzard.
 
Level 1
Joined
Nov 30, 2019
Messages
2
Code:
Map initialization

Set Respawn_Time = 30.00

Unit Group - Pick every unit in (Units owned by Neutral Hostile) and do (Actions)
    Loop - Actions
        Set Temp_Integer = (Temp_Integer + 1)
        Unit - Set the custom value of (Picked unit) to Temp_Integer
        Set Creep_Point[Temp_Integer] = (Position of (Picked unit))

Code:
Map initialization

(Owner of (Triggering unit)) Equal to Neutral Hostile
(Custom value of (Triggering unit)) Greater than 0

Actions
    Custom script:   local integer i = GetUnitTypeId(GetTriggerUnit())
    Custom script:   local integer ii = GetUnitUserData(GetTriggerUnit())
    Wait Respawn_Time game-time seconds
    Custom script:   call SetUnitUserData(CreateUnit(Player(12),i,GetLocationX(udg_Creep_Point[ii]),GetLocationY(udg_Creep_Point[ii]),270),ii)

So if I was using these as my respawners, I'd add to this to replace the unit?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,208
So if I was using these as my respawners, I'd add to this to replace the unit?
Both triggers need to store the units in an array as part of the same set of arrays as the position of the unit.

Another trigger is needed which iterates through all respawn indices and replaces existing units. This is run when the boss changes.

The unit type of the created unit or unit replacement is resolved from the unit type of the respawn put through a boss conversion table. This might not convert the type at all (map placed type is appropriate) or it might change it to an alternative type based on the boss currently deployed.

The boss conversion table might be implemented using a hashtable. The parent key is the boss ID (either a constant used in triggers or the boss unit type ID) while the child key is the respawn unit type ID. If no mapping exists then the respawn unit type ID is used for the unit. If a mapping exists then the returned unit type ID is used instead. The boss conversion tables should be populated with data at map initialization.
 
Status
Not open for further replies.
Top