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

Making stealth mechanics.

Level 4
Joined
Oct 9, 2024
Messages
63
I want to create a stealth mechanic different from the Wind Walk that we have in the game, this stealth means hiding whenever you want even in front of the enemy doesn't work for me, I want situational stealth, like when you're inside a forest or the enemy's vision is blocked for you, I've been thinking about creating several areas in points on the map like large bushes and making a trigger to allow stealth to be used only in those locations, that's the initial idea that I want to learn how to do but I'm open to tips and suggestions.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
You can periodically check the position of your unit(s) with the Stealth ability and depending on where they are in the map you can Add/Remove or Disable/Enable their Stealth ability.
  • Events
    • Time - Every 0.10 seconds
  • Conditions
  • Actions
    • Set Variable StealthPoint = (Position of StealthUnit)
    • If (all conditions are true) then do (Actions)
      • If - Conditions
        • (Terrain-type at StealthPoint) Equal to Lordaeron - Dark Grass
      • Then - Actions
        • Unit - Add Stealth (Ability) to StealthUnit
      • Else - Actions
        • Unit - Remove Stealth (Ability) from StealthUnit
        • Unit - Remove Stealth (Buff) buff from StealthUnit
  • Custom script: call RemoveLocation( udg_StealthPoint )
^ This example checks to see if the Unit is standing on Dark Grass terrain, which you could exclusively use to represent "stealth areas". If it is standing on Dark Grass then Add Stealth, otherwise, Remove the Ability and the Buff (if any). This is assuming that you're using Wind Walk as your Stealth ability.
 
Last edited:
I've prepared a map for you that might fit the description of what you're trying to achieve. This map contains an aura that turns units under its influence invisible for as long as they have the aura. Attacking or casting spells will break the invisibility. You can apply the aura to any unit that you want to have the aura. In the map attached, I have towers bear this aura and you can test it.

Do note that in Warcraft 3, auras can linger for up to 5 seconds so leaving the area will still grant you the aura and thus your unit remains invisible. You can also change it so that you need to hold position while under the effects of the aura before your unit turns invisible by changing the bonus to shadow meld.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I've prepared a map for you that might fit the description of what you're trying to achieve. This map contains an aura that turns units under its influence invisible for as long as they have the aura. Attacking or casting spells will break the invisibility. You can apply the aura to any unit that you want to have the aura. In the map attached, I have towers bear this aura and you can test it.

Do note that in Warcraft 3, auras can linger for up to 5 seconds so leaving the area will still grant you the aura and thus your unit remains invisible. You can also change it so that you need to hold position while under the effects of the aura before your unit turns invisible by changing the bonus to shadow meld.
Just checking out your Auramancer solution, it looks nice.

One little nitpick (sorry I'm a bit bored atm) would be changing this:
vJASS:
        private static method disableAbility takes integer a returns nothing
            local integer i = 1
            loop
                exitwhen i > 24
                call SetPlayerAbilityAvailableBJ( false, a, ConvertedPlayer(i))
                set i = i + 1
            endloop
        endmethod
To this, for a MASSIVE performance gain :p
vJASS:
        private static method disableAbility takes integer a returns nothing
            local integer i = 0
            loop
                call SetPlayerAbilityAvailableBJ(false, a, Player(i))
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
        endmethod
Also, I'm still a bit confused about what OP wants.
making a trigger to allow stealth to be used only in those locations
I took this as "you can CAST stealth while in these locations". But I imagine it's more like the League of Legends brush mechanic which I think is what you did.
 
One little nitpick (sorry I'm a bit bored atm) would be changing this:
vJASS:
private static method disableAbility takes integer a returns nothing
local integer i = 1
loop
exitwhen i > 24
call SetPlayerAbilityAvailableBJ( false, a, ConvertedPlayer(i))
set i = i + 1
endloop
endmethod
To this, for a MASSIVE performance gain :p
vJASS:
private static method disableAbility takes integer a returns nothing
local integer i = 0
loop
call SetPlayerAbilityAvailableBJ(false, a, Player(i))
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
endmethod
I think its fine. It got approved yesterday and I'm looking for nails to pound with this hammer. :p Also, its only called during initialization anyway.

I took this as "you can CAST stealth while in these locations". But I imagine it's more like the League of Legends brush mechanic which I think is what you did.
Sounds like league to me too. Changing the bonus from permanent invisibility to shadow meld would fit the description. The downside is its not intuitive and right now shadow meld only works at night. The aura should also probably grant true sight and its going to the bush in league.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I think its fine. It got approved yesterday and I'm looking for nails to pound with this hammer. :p Also, its only called during initialization anyway.


Sounds like league to me too. Changing the bonus from permanent invisibility to shadow meld would fit the description. The downside is its not intuitive and right now shadow meld only works at night. The aura should also probably grant true sight and its going to the bush in league.
There's an option on Shadow Meld to use it at any time of day, at least on newer patches. Or you may have to use the new ability.
 
Level 4
Joined
Oct 9, 2024
Messages
63
I appreciate your help Blight Tower but I don't believe that your vision is what I'm looking for, the uncle gave me an explanation and since yesterday I've been working on creating it, an ability with 5 seconds duration and cooldown that can only be used while in bushes or other places like forests, maybe I should also allow this skill to always be usable at night, it is a stealth mechanic for an RPG map, not only heroes can stay stealthy, other enemies like bandits can also use this same skill, so cross a Dense forest can be dangerous for players especially at night, I have been a tabletop RPG master for over 10 years and I see great potential for mastering within a Warcraft 3 map.
 
Level 4
Joined
Oct 9, 2024
Messages
63
Uncle I could do this using a grass-type map block, but I couldn't do this with a previously selected region, I don't understand why not.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Uncle I could do this using a grass-type map block, but I couldn't do this with a previously selected region, I don't understand why not.
How were you doing it? In other words, please show us your triggers. It's easy to make mistakes along the way.
  • Events
    • Time - Every 0.10 seconds
  • Conditions
  • Actions
    • If (all conditions are true) then do (Actions)
      • If - Conditions
        • (MyRegion <gen> contains StealthUnit) Equal to True
      • Then - Actions
        • Unit - Add Stealth (Ability) to StealthUnit
      • Else - Actions
        • Unit - Remove Stealth (Ability) from StealthUnit
        • Unit - Remove Stealth (Buff) buff from StealthUnit
Anytime you have a question related to your trigger(s) you should post those triggers. It's the fastest way to get the correct answer.
 
Last edited:
Unsure if this is something of interest:
I have made a "almost finished" system that I've considered releasing here on Hive.
It's almost like League of Legends "Camouflage" (Invisible while no enemy units are within X range), but not really finished or polished.
I believe that you are still invisible while attacking and casting spells, so it might be somewhat broken in many situations.

It's adapted to a situation where many units have this ability and NOT to impact performance that much.
Note: There is no convenience method to specify range for one specific unit right now, but it's setup to make it possible. You can globally shut this system down with setSystemOn method and globally set invisibility status of all registered units with setInvisibleForAll (to for example make it only work during the night, or something.

This isn't really a finished system, but hey, might be useful?
JASS:
/*
* System for League of Legends-style camouflage. 
* https://leagueoflegends.fandom.com/wiki/Stealth#Camouflage
* Basically, you are invisible while X range away from enemies.
* UPDATES_PER_TICK constant is used to control how many units the system processes per tick, meaning some control over performance impact of system, if many units are registered.
* 
* setSystemOn is used to globally turn this system on/off. Turning it off reveals any unit that was invisible (removing the "Undead - Ghost" ability, if they had it). 
*/
library CamouflageSystem
    globals
        private constant integer CAMOUFLAGE_INVIS_ABILITY = 'Agho' //"Undead - Ghost"-based ability for being able to cast spells while invis
        private constant real BASE_RANGE = 500.0
        private constant integer UPDATES_PER_TICK = 8   //If less that this many units have this ability, it does not matter. If more than this many units have this ability, only so many will be updated per tick.
        private constant real TICK_RATE = 0.03125000
    endglobals
    struct Camouflage
        private static thistype array camouflagedUnits
        private static boolean turnedOn = true
        private static integer k = -1
        private static integer loopIndex = 0
        private static timer t = CreateTimer()
        private unit source
        private real range
        method makeVisible takes nothing returns nothing
            call UnitRemoveAbility(source, CAMOUFLAGE_INVIS_ABILITY)
        endmethod
        method makeInvis takes nothing returns nothing
            call UnitAddAbility(source, CAMOUFLAGE_INVIS_ABILITY)
        endmethod
        method isInvis takes nothing returns boolean
            return GetUnitAbilityLevel(source, CAMOUFLAGE_INVIS_ABILITY) > 0
        endmethod
        method toggleInvis takes nothing returns nothing
            if isInvis() then
                call makeVisible()
            else
                call makeInvis()
            endif
        endmethod
        public static method setInvisibleForAll takes boolean invis returns nothing
            local integer i = 0
            if invis then
                loop
                    exitwhen i > k
                    call camouflagedUnits[i].makeInvis()
                    set i = i + 1
                endloop
            else
                loop
                    exitwhen i > k
                    call camouflagedUnits[i].makeVisible()
                    set i = i + 1
                endloop
            endif
        endmethod
        public method getRange takes nothing returns real
            return range
        endmethod
        public method setRange takes real newRange returns nothing
            set range = newRange
        endmethod
        method update takes nothing returns nothing
            local group unitsInRange = CreateGroup()
            local player owner = GetOwningPlayer(source)
            local unit u
            local real x = GetUnitX(source)
            local real y = GetUnitY(source)
            local boolean hasEnemiesInRange = false
            local boolean isInvis = isInvis()
            call GroupEnumUnitsInRange(unitsInRange, x, y, range, null)
            loop
                set u = FirstOfGroup(unitsInRange)
                exitwhen u == null
                if UnitAlive(u) and not IsUnitAlly(u, owner) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
                    set hasEnemiesInRange = true
                    exitwhen true
                endif
                call GroupRemoveUnit(unitsInRange,u)
            endloop
            call DestroyGroup (unitsInRange)
            set u = null
            set owner = null
            set unitsInRange = null
            if hasEnemiesInRange and isInvis then
                //call BJDebugMsg("invis Unit turned visible")
                call makeVisible()
            elseif not isInvis and not hasEnemiesInRange then
                //call BJDebugMsg("Visible Unit turned invis")
                call makeInvis()
            endif
        endmethod
        static method onPeriod takes nothing returns nothing
            local thistype this
            local integer processed = 0
            if loopIndex > k then
                if k == -1 then
                    call PauseTimer(t) //Nothing to update!
                endif
                set loopIndex = 0
            else
                loop
                    exitwhen processed == UPDATES_PER_TICK or loopIndex > k
                    set processed = processed + 1
                    set this = camouflagedUnits[loopIndex]
                    if UnitAlive(source) then
                        call update()
                    else
                        set loopIndex = remove(loopIndex)
                    endif
                    set loopIndex = loopIndex + 1
                endloop
            endif
        endmethod
        public static method setSystemOn takes boolean newTurnedOn returns nothing
            if not (turnedOn == newTurnedOn) then
                if turnedOn and not newTurnedOn then
                    set turnedOn = false
                    call PauseTimer(t)
                    call setInvisibleForAll(false)
                else
                    set turnedOn = true
                    if k > -1 then
                        call TimerStart(t, TICK_RATE, true, function thistype.onPeriod)
                    endif
                endif
            endif
        endmethod
        static method register takes unit u returns nothing
            local thistype this = allocate()
            set .source = u
            set .range = BASE_RANGE
            set k = k + 1
            set camouflagedUnits[k] = this
            if turnedOn and k == 0 then
                call TimerStart(t, TICK_RATE, true, function thistype.onPeriod)
            endif
        endmethod
        static method remove takes integer i returns integer
            local thistype this = camouflagedUnits[i]
            set camouflagedUnits[i] = camouflagedUnits[k]
            set k = k - 1
            set source = null
            call .deallocate()
            if k == -1 then
                call PauseTimer(t)
            endif
            return i - 1
        endmethod
    endstruct
endlibrary

Testmap requiring WC3 version 1.31 or newer
(Testmap doesn't seem to have latest version of the system, but it's fairly minor differences).
 

Attachments

  • CamouflageSystem.w3x
    28.3 KB · Views: 6
anyway, i think you completely missed the point of what i have created. the point is you can have a locust unit emitting the aura (and even adjust the range of the aura) then apply it anywhere on your map so that you don't need to check for terrain. the only limitation of what i did was you cannot have an active component to it. well you can, if you omit the disable ability part of my solution and place the ability of your choice but that may yield an unexpected result for the cooldown of your abilities. (that goes the same for the proposed solution by uncle since you'll be adding and removing the abilities from the units).
 
Level 4
Joined
Oct 9, 2024
Messages
63
Uncle, I have time to work on this only now, I will pass you my trigger and ask you to look at my problem, I used the paladin only as the test unit.
Untitled Trigger 001
Events
Time - Every 0.10 seconds of game time
Conditions
Actions
Set VariableSet StealthPoint = (Terrain type at (Position of Paladin 0000 <gen>))
If (StealthPoint Equal to (Terrain type at (Center of Region 000 <gen>))) then do (Unit - Add Wind Walk to Paladin 0000 <gen>) else do (Unit - Remove Wind Walk from Paladin 0000 <gen>)
 
Level 4
Joined
Oct 9, 2024
Messages
63
  • Untitled Trigger 001
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • Set VariableSet StealthPoint = (Terrain type at (Position of Paladin 0000 <gen>))
      • If (StealthPoint Equal to (Terrain type at (Center of Region 000 <gen>))) then do (Unit - Add Wind Walk to Paladin 0000 <gen>) else do (Unit - Remove Wind Walk from Paladin 0000 <gen>)
 
Level 4
Joined
Oct 9, 2024
Messages
63
  • [IMG]https://www.hiveworkshop.com/styles/default/ratory/trigger_legacy/joinbottom.gif[/IMG][IMG]https://www.hiveworkshop.com/styles/default/ratory/trigger_legacy/if.gif[/IMG](MyRegion <gen> contains StealthUnit) Equal to True
I don't know how to make the action choices to get to the last one mentioned by you, uncle.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
  • Untitled Trigger 001
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • Set VariableSet StealthPoint = (Terrain type at (Position of Paladin 0000 <gen>))
      • If (StealthPoint Equal to (Terrain type at (Center of Region 000 <gen>))) then do (Unit - Add Wind Walk to Paladin 0000 <gen>) else do (Unit - Remove Wind Walk from Paladin 0000 <gen>)
Please stop spamming, I'll reply asap. Anyway, what are you trying to do?

I've shown you two methods, one using Terrain, the other using Regions. Which one of these do you want to use?

Terrain example:
  • Events
    • Time - Every 0.10 seconds
  • Conditions
  • Actions
    • Set Variable StealthPoint = (Position of StealthUnit)
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Terrain-type at StealthPoint) Equal to Lordaeron - Dark Grass
      • Then - Actions
        • Unit - Add Stealth (Ability) to StealthUnit
      • Else - Actions
        • Unit - Remove Stealth (Ability) from StealthUnit
        • Unit - Remove Stealth (Buff) buff from StealthUnit
  • Custom script: call RemoveLocation( udg_StealthPoint )
Region example:
  • Events
    • Time - Every 0.10 seconds
  • Conditions
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (MyRegion <gen> contains StealthUnit) Equal to True
      • Then - Actions
        • Unit - Add Stealth (Ability) to StealthUnit
      • Else - Actions
        • Unit - Remove Stealth (Ability) from StealthUnit
        • Unit - Remove Stealth (Buff) buff from StealthUnit
The TYPE of Variable you need to use is in the variable's name. StealthPoint = POINT. StealthUnit = UNIT.

Make sure to use the Multiple If Then Else:
1729263478950.png

^ Search For Text is very useful!

contains StealthUnit?
That Condition is a Boolean Comparison:
1729263781492.png
 
Level 4
Joined
Oct 9, 2024
Messages
63
I think I finally got it done.
  • Untitled Trigger 001
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • If ((Region 000 <gen> contains Paladin 0000 <gen>) Equal to True) then do (Unit - Add Wind Walk to Paladin 0000 <gen>) else do (Unit - Remove Wind Walk from Paladin 0000 <gen>)
 
Level 4
Joined
Oct 9, 2024
Messages
63
I'm close to completing this part of the Rio project, now I need to make it unusable instead of removing the ability, I want this to keep the ability's cooldown the same, I think of this mechanic as a way to emulate the time it takes to hide even when already inside an area like a bush, removing and adding it would not allow the cooldown to continue, I found an option called hide ability and I put the hide UI mark as false inside it, this made my ability unusable but stay there, I believe that's the way but I don't know how to complete the project.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I'm close to completing this part of the Rio project, now I need to make it unusable instead of removing the ability, I want this to keep the ability's cooldown the same, I think of this mechanic as a way to emulate the time it takes to hide even when already inside an area like a bush, removing and adding it would not allow the cooldown to continue, I found an option called hide ability and I put the hide UI mark as false inside it, this made my ability unusable but stay there, I believe that's the way but I don't know how to complete the project.
You want to Disable/Enable the ability:
  • Stealth Setup
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set VariableSet Stealth_Unit = Blademaster 0000 <gen>
      • Trigger - Turn on Stealth Check <gen>
      • Unit - For Stealth_Unit, Ability Wind Walk, Disable ability: True, Hide UI: False
  • Stealth Check
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Region 000 <gen> contains Stealth_Unit) Equal to True
        • Then - Actions
          • -------- If not already enabled, enable it: --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Is_Stealth_Enabled Equal to False
            • Then - Actions
              • Set VariableSet Is_Stealth_Enabled = True
              • Unit - For Stealth_Unit, Ability Wind Walk, Disable ability: False, Hide UI: False
            • Else - Actions
        • Else - Actions
          • -------- If not already disabled, disable it: --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Is_Stealth_Enabled Equal to True
            • Then - Actions
              • Set VariableSet Is_Stealth_Enabled = False
              • Unit - For Stealth_Unit, Ability Wind Walk, Disable ability: True, Hide UI: False
            • Else - Actions
You need the Is_Stealth_Enabled boolean variable, otherwise, the ability will not disable/enable properly.
 

Attachments

  • Stealth System 1.w3m
    18 KB · Views: 8
Level 4
Joined
Oct 9, 2024
Messages
63
That's right, you did exactly what I wanted to do, you managed to learn using your map, thank you very much.
 
Top