• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Replacing normal units with inventories trigger

Status
Not open for further replies.
Level 3
Joined
May 22, 2007
Messages
30
Ok I'm new to JASS so go easy on me. I want to create a function that will replace my unit with another when it uses an item, and when they drop the item it will turn them back to the original unit. Only problem is I need to make sure they give the unit's items to the unit replacing them.
I found ReplaceUnitBJ but I don't understand how to get it to work,
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
...and of course after that comes the nulling but I still need to know: how do you get this to work? I have tried the tutorials but I still can't understand.

What I do know is I need to set it to loop through the inventory even if it isn't a hero.

I just need to know how to make it get my replaced unit (ID of 'z000') from my original unit (ID of 'hpea', the peasant). This may seem blindingly obvious to some people but I have been stuck on this for awhile. If anyone could point me to anything that has all the handles, integers, reals, and all the other variables and explanations of each I'd appreciate it.

And if you know a better way to replace a unit and keep their items please tell me >.< although more than likely you'll yell at me about your way and how its better.
 
Level 11
Joined
Oct 13, 2005
Messages
233
ReplaceUnitBJ is a function Blizzard made that you can use to "replace" a unit. How it works is that you tell it the unit you want replaced, the unit id of the unit to replace it, and an integer that's used as what's called a flag. Based on the value of the flag, the unit will start out with a different amount of life. You can specify the unit to have the same ratio of current HP/full HP and current MP/full MP as the unit it replaces, the exact same current HP and MP as the unit it replaces, the new unit's default life/mana, or the new unit's max life/mana. This function will also go ahead and remove items from the old unit and give them to the new one if they're both heroes.

Now, I am going to assume that both of the units have an inventory size of 6, but you can easily change that if you wish. I will also assume that you want the new unit to start at its default hp/mana instead of at the old unit's. Again, you can feel free to adapt this function to your needs.

JASS:
function ReplaceUnit takes unit oldUnit, integer newUnitId returns unit
 local unit newUnit
 local integer slot = 0
 local item heldItem
 
    //Hide the old unit
    call ShowUnit(oldUnit, false)
    
    //Create the new unit at the same position as the old unit and make it face the same direction
    set newUnit = CreateUnit(GetOwningPlayer(oldUnit), newUnitId, GetUnitX(oldUnit), GetUnitY(oldUnit), GetUnitFacing(oldUnit))
    
    //Now to go through each inventory slot
    loop
        exitwhen slot >= 6    //Stop after slot #5 (0-5 = 6 slots)
        
        set heldItem = UnitItemInSlot(oldUnit, slot)
        
        if heldItem != null     //If the unit has an item in the slot
            
            //Make the old unit drop the item, give the item to the new unit
            call UnitRemoveItem(oldUnit, heldItem)
            call UnitAddItem(newUnit, heldItem)
            
        endif
        
        slot = slot + 1    //Go on to the next slot
    endloop
    
    call RemoveUnit(oldUnit)

    //Prevent possible memory leaks
    set bj_lastReplacedUnit = newUnit
    set heldItem = null
    set newUnit = null
    
    return bj_lastReplacedUnit    //Return the newly created unit
endfunction

Now, the function is fairly well explained with the comments except for perhaps the part at the end where I clean up possible memory leaks. Local variables that are handles (anything that's not an integer, real, string, boolean) should always be set to null before a function ends. Now, our function needs to return the newly created unit, but it's set to a local variable which must be nulled. So to get around that, I set a Blizzard variable to the new unit so I could set the newUnit variable to null.
If you didn't fully understand that, it's basically helping to prevent memory leaks in the code. If you wish to know anything else about that, let me know and I'll be glad to try and explain it.

Now, because you're fairly new to JASS, I'll explain how you would use this function. Suppose you've got a function myFunction that you're going to be calling this function from. This is how it would be done:
JASS:
function myFunction takes nothing returns nothing  //Could be anything
 local unit myOldUnit = GetTriggerUnit()    //Suppose these are going to be the actions from a trigger, and the unit being replaced is the triggering unit

 local unit myNewUnit = ReplaceUnit(myOldUnit, 'z000')
    // or
    set someRandomUnitVariable = ReplaceUnit(myOldUnit, 'z000')
endfunction

Again, if anything in my explanation isn't entirely clear, please tell me and I'll try and explain it.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
You basically need to make a custom version of that. As it it obvious the unit is not dead (as how else could it pick that item up), you can simplify it a lot by removing the comparision of HP checking for if the unit is dead. YOu then need to exchange all the items slot for slot between units.

wyrmlord's script does exactly that that you are after in a highly efficent way. Only problem with it is it will not keep the previous unit's HP or MANA in any way which may cause problems if you are not careful.

wyrmlord, I also notice you are using bj_lastReplacedUnit for GUI support, however since he knows basic JASS (or is learning) he techinaclly does not need that in the script as you quickly learn to eithor attach a value directly to a user difined global or to store it in a local if it is only used in one function.

Thus Bettaxx, feel free to remove the bj_lastReplacedUnit from that function if you use the function and do not use bj_lastReplacedUnit as it does completly nothing if you do not use it in GUI as, like stated eariler, you can get around it via the use of your own globals or a local (both of type unit ofcourse).
 
Level 11
Joined
Oct 13, 2005
Messages
233
Uh...

wyrmlord, I also notice you are using bj_lastReplacedUnit for GUI support, however since he knows basic JASS (or is learning) he techinaclly does not need that in the script as you quickly learn to eithor attach a value directly to a user difined global or to store it in a local if it is only used in one function.

Why define another global when you can use one that is built-in? I need to use a global no matter what to avoid leaks, so I used a Blizzard global that's already defined so that it would require less effort to implement into a map. It's just coincidence that I chose bj_lastReplacedUnit instead of another pre-defined global.

Thus Bettaxx, feel free to remove the bj_lastReplacedUnit from that function if you use the function and do not use bj_lastReplacedUnit as it does completly nothing if you do not use it in GUI as, like stated eariler, you can get around it via the use of your own globals or a local (both of type unit ofcourse).

Again, why create a global when you can use one already defined by Blizzard in every map? Also, if the local was returned, as I stated in my post, it would leak because you can't null a local variable and return it.

Bettaxx, I believe that my method is fine as it is now, barring any other unforeseen issues.
 
Level 3
Joined
May 22, 2007
Messages
30
Thanks. Whenever I try to put it in WE it crashes though... I blame World Editor Unlimited. :bored: But I'll figure something out.

EDIT: Got it! You were missing a "then" after the "if heldItem != null." Huh, now I have to make it check to see if a global effect isn't nulled, and if it isn't then destroy the effect so I can add a new one; that way it won't leak. And I have to make it work for every trigger that has this replacement system, so wish me luck. Thanks again!
 
Last edited:
Status
Not open for further replies.
Top