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

[General] Stash inventory

Level 12
Joined
Jul 5, 2014
Messages
551
I came to the realization that it is pretty unreasonable to have a RPG with 3 heroes and crafting system with only the 18 slots for everything. I've tried looking up a solution but it was either wonky or way too advanced. My first idea was summoning a stash box but involve issues (summoning them on top of each other or locations they shouldn't be), I'm starting to lean toward some backpack-like option which I guess leads to uncharted area. My intent:

1. Items can't be used from the stash, nor they can provide passive bonus.
2. They should work even after crossing to a different map.
3. Each of the 3 heroes should have that stash with 6 slots.
4. Constant access to stashed items.

Is there any solution that's not too complicated and won't bug out?
 
Level 30
Joined
Aug 29, 2012
Messages
1,383
1 is easy with a modified Inventory (hero) ability with the following tweaks

1721054363000.png


Then you can store any unit just like a hero in a game cache for your campaign so no particular problem for point 2.

I'd just create a stash unit with flying movement type but 0 speed so it doesn't block anything but can't be abused to fly around the map, and then one ability to summon/dismiss stash (based on Defend perhaps so you can toggle it). You'd use Hide unit when the stash is dismissed, and move it to the hero location when summoned
 
Level 12
Joined
Jul 5, 2014
Messages
551
1 is easy with a modified Inventory (hero) ability with the following tweaks

View attachment 480019

Then you can store any unit just like a hero in a game cache for your campaign so no particular problem for point 2.

I'd just create a stash unit with flying movement type but 0 speed so it doesn't block anything but can't be abused to fly around the map, and then one ability to summon/dismiss stash (based on Defend perhaps so you can toggle it). You'd use Hide unit when the stash is dismissed, and move it to the hero location when summoned
Yes, that was my current solution, only I removed collision rather than setting it to flying. However, I found it an ugly solution that 3 heroes would summon their stashes on top of each other's, inside walls, themselves, etc, which is why considered some backpack-like solution instead.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
If you want the Backpack units to teleport next to the Hero without clipping into things then you could try something like this:
  • Events
    • Time - Elapsed game time is 0.00 seconds
  • Conditions
  • Actions
    • Unit - Create 1 Footman for Neutral Passive at (Center of (playable map area))
    • Set Variable Backpack_Tester = (Last created unit)
    • Unit - Make Backpack_Tester Invulnerable
    • Unit - Hide Backpack_Tester
  • Events
    • A Hero summons their backpack
  • Conditions
  • Actions
    • Unit - Unhide Backpack_Tester
    • Set Variable Backpack_Point = (Position of Hero)
    • Unit - Move Backpack_Tester instantly to Backpack_Point
    • Custom script: call RemoveLocation( udg_Backpack_Point )
    • Set Variable Backpack_Point = (Position of Backpack_Tester)
    • Unit - Hide Backpack_Tester
    • Unit - Move The_Backpack instantly to Backpack_Point
    • Custom script: call RemoveLocation( udg_Backpack_Point )
Now when you summon a Backpack it will take into consideration Collision from all nearby sources. This is because our Footman (which you could change to something else) has been used to find us the nearest pathable point to our Hero. If done correctly the user should never be able to see or interact with this Footman. Of course this only solves the initial placement of the Backpack. It may also require that you add an extra step for rare cases where the Footman was moved far away from the Hero because no pathable point was found. A Distance check would suffice, if the Footman exceeds 512.00 range from the Hero then you can just force the Backpack to be directly on top of the Hero.

Edit: Might be even easier to just toggle the Backpack's Collision on/off as needed.
IE: Move backpack to pos of hero -> Turn backpack collision on -> Order backpack to Stop (forces a collision update) -> Turn backpack collision off.
 
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
If you want the Backpack units to teleport next to the Hero without clipping into things then you could try something like this:
  • Events
    • Time - Elapsed game time is 0.00 seconds
  • Conditions
  • Actions
    • Unit - Create 1 Footman for Neutral Passive at (Center of (playable map area))
    • Set Variable Backpack_Tester = (Last created unit)
    • Unit - Make Backpack_Tester Invulnerable
    • Unit - Hide Backpack_Tester
  • Events
    • A Hero summons their backpack
  • Conditions
  • Actions
    • Unit - Unhide Backpack_Tester
    • Set Variable Backpack_Point = (Position of Hero)
    • Unit - Move Backpack_Tester instantly to Backpack_Point
    • Custom script: call RemoveLocation( udg_Backpack_Point )
    • Set Variable Backpack_Point = (Position of Backpack_Tester)
    • Unit - Hide Backpack_Tester
    • Unit - Move The_Backpack instantly to Backpack_Point
    • Custom script: call RemoveLocation( udg_Backpack_Point )
Now when you summon a Backpack it will take into consideration Collision from all nearby sources. This is because our Footman (which you could change to something else) has been used to find us the nearest pathable point to our Hero. If done correctly the user should never be able to see or interact with this Footman. Of course this only solves the initial placement of the Backpack. It may also require that you add an extra step for rare cases where the Footman was moved far away from the Hero because no pathable point was found. A Distance check would suffice, if the Footman exceeds 512.00 range from the Hero then you can just force the Backpack to be directly on top of the Hero.

Edit: Might be even easier to just toggle the Backpack's Collision on/off as needed.
IE: Move backpack to pos of hero -> Turn backpack collision on -> Order backpack to Stop (forces a collision update) -> Turn backpack collision off.
Toggling the collision still drops the stash on the hero, so it doesn't seem to do anything. And I fear that the footman solution gives opportunity to abuse the skill against enemies to create a temporary block.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
Toggling the collision still drops the stash on the hero, so it doesn't seem to do anything.
The Backpack needs to actually have Collision for it to work, so your Backpack would basically have the same settings as say a Footman but you would control when it's Collision is on/off.

And I fear that the footman solution gives opportunity to abuse the skill against enemies to create a temporary block.
The Footman solution doesn't do that. When you execute Actions within the same game frame -> Unhide/Hide, the logic happens at the "same time". Meaning that no other code will run between those things happening, IE: Anything related to a temporary block. You will quite literally NEVER see this Footman, it will always be Hidden for all intents and purposes.
 
Level 12
Joined
Jul 5, 2014
Messages
551
The Backpack needs to actually have Collision for it to work, so your Backpack would basically have the same settings as say a Footman but you would control when it's Collision is on/off.


The Footman solution doesn't do that. When you execute Actions within the same game frame -> Unhide/Hide, the logic happens at the "same time". Meaning that no other code will run between those things happening, IE: Anything related to a temporary block. You will quite literally NEVER see this Footman, it will always be Hidden for all intents and purposes.
My stash has collision and it still ends up on the hero. This is the involved trigger based on your suggestion:

  • Backpack
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stash
    • Actions
      • Sound - Play FeralSpiritTarget1 <gen>
      • Unit - Move Stash 0533 <gen> instantly to (Position of (Casting unit))
      • Unit - Turn collision for Stash 0533 <gen> On
      • Unit - Order Stash 0533 <gen> to Stop
      • Unit - Turn collision for Stash 0533 <gen> Off
      • Special Effect - Create a special effect at (Position of Stash 0533 <gen>) using Objects\Spawnmodels\Other\ToonBoom\ToonBoom.mdl
      • Special Effect - Destroy (Last created special effect)
      • Unit - Unhide Stash 0533 <gen>
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
My stash has collision and it still ends up on the hero. This is the involved trigger based on your suggestion:

  • Backpack
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stash
    • Actions
      • Sound - Play FeralSpiritTarget1 <gen>
      • Unit - Move Stash 0533 <gen> instantly to (Position of (Casting unit))
      • Unit - Turn collision for Stash 0533 <gen> On
      • Unit - Order Stash 0533 <gen> to Stop
      • Unit - Turn collision for Stash 0533 <gen> Off
      • Special Effect - Create a special effect at (Position of Stash 0533 <gen>) using Objects\Spawnmodels\Other\ToonBoom\ToonBoom.mdl
      • Special Effect - Destroy (Last created special effect)
      • Unit - Unhide Stash 0533 <gen>
I think you'd want to turn collision on prior to moving it. There may also be issues because the Stash cannot move.

Anyway, I tested my initial suggestion and it works without issues:
  • Backpack Setup
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set VariableSet Backpack_Pathing_Point_A = (Center of (Playable map area))
      • Unit - Create 1 Pathing Tester (Backpack) for Neutral Passive at Backpack_Pathing_Point_A facing Default building facing degrees
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_A )
      • Set VariableSet Backpack_Pathing_Tester = (Last created unit)
      • Unit - Hide Backpack_Pathing_Tester
  • Backpack Get Backpack
    • Events
    • Conditions
    • Actions
      • -------- Position pathing tester on top of the hero: --------
      • Set VariableSet Backpack_Pathing_Point_A = (Position of Backpack_Hero)
      • Unit - Move Backpack_Pathing_Tester instantly to Backpack_Pathing_Point_A
      • -------- --------
      • -------- The pathing tester will have been forced to the nearest pathable point, get this point: --------
      • Set VariableSet Backpack_Pathing_Point_B = (Position of Backpack_Pathing_Tester)
      • -------- --------
      • -------- Do an optional distance check to ensure that the backpack is within reach of the hero: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between Backpack_Pathing_Point_A and Backpack_Pathing_Point_B) Less than 512.00
        • Then - Actions
          • -------- It's close enough: --------
          • Unit - Move Backpack_Unit instantly to Backpack_Pathing_Point_B
        • Else - Actions
          • -------- It was too far away, force it directly on top of the hero: --------
          • Unit - Move Backpack_Unit instantly to Backpack_Pathing_Point_A
      • -------- --------
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_A )
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_B )
To summon the backpack to your hero you simply do this (modify the Event/Event Responses as you see fit):
  • Backpack Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • -------- Summon the Backpack for the Paladin: --------
      • Set VariableSet Backpack_Hero = Paladin 0002 <gen>
      • Set VariableSet Backpack_Unit = Backpack 0000 <gen>
      • Trigger - Run Backpack Get Backpack <gen> (ignoring conditions)
^ So you set variables referencing the Hero and it's personal Backpack and then run the "Get Backpack" trigger. That trigger will handle the logic for repositioning the Backpack nearby OR directly on top of the hero in a worst case scenario. You can modify the distance (512.00) to control the logic for what's considered "too far away" or just get rid of that entirely if you don't like it.

Surprising enough I found that you don't even need to Unhide the Pathing Tester unit. Units still calculate their own collision while Hidden despite not interfering with other collidables.

Pathing Tester (Backpack) is a copy and pasted Footman with Collision Size set to 32.00, Attacks Enabled = None, and has the Invulnerable (Neutral) ability. This Unit should never interfere with anything.
 

Attachments

  • Backpack Test 1.w3m
    19.7 KB · Views: 5
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
I think you'd want to turn collision on prior to moving it. There may also be issues because the Stash cannot move.

Anyway, I tested my initial suggestion and it works without issues:
  • Backpack Setup
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set VariableSet Backpack_Pathing_Point_A = (Center of (Playable map area))
      • Unit - Create 1 Pathing Tester (Backpack) for Neutral Passive at Backpack_Pathing_Point_A facing Default building facing degrees
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_A )
      • Set VariableSet Backpack_Pathing_Tester = (Last created unit)
      • Unit - Hide Backpack_Pathing_Tester
  • Backpack Get Backpack
    • Events
    • Conditions
    • Actions
      • -------- Position pathing tester on top of the hero: --------
      • Set VariableSet Backpack_Pathing_Point_A = (Position of Backpack_Hero)
      • Unit - Move Backpack_Pathing_Tester instantly to Backpack_Pathing_Point_A
      • -------- --------
      • -------- The pathing tester will have been forced to the nearest pathable point, get this point: --------
      • Set VariableSet Backpack_Pathing_Point_B = (Position of Backpack_Pathing_Tester)
      • -------- --------
      • -------- Do an optional distance check to ensure that the backpack is within reach of the hero: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between Backpack_Pathing_Point_A and Backpack_Pathing_Point_B) Less than 512.00
        • Then - Actions
          • -------- It's close enough: --------
          • Unit - Move Backpack_Unit instantly to Backpack_Pathing_Point_B
        • Else - Actions
          • -------- It was too far away, force it directly on top of the hero: --------
          • Unit - Move Backpack_Unit instantly to Backpack_Pathing_Point_A
      • -------- --------
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_A )
      • Custom script: call RemoveLocation( udg_Backpack_Pathing_Point_B )
To summon the backpack to your hero you simply do this (modify the Event/Event Responses as you see fit):
  • Backpack Demo
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • -------- Summon the Backpack for the Paladin: --------
      • Set VariableSet Backpack_Hero = Paladin 0002 <gen>
      • Set VariableSet Backpack_Unit = Backpack 0000 <gen>
      • Trigger - Run Backpack Get Backpack <gen> (ignoring conditions)
^ So you set variables referencing the Hero and it's personal Backpack and then run the "Get Backpack" trigger. That trigger will handle the logic for repositioning the Backpack nearby OR directly on top of the hero in a worst case scenario. You can modify the distance (512.00) to control the logic for what's considered "too far away" or just get rid of that entirely if you don't like it.

Surprising enough I found that you don't even need to Unhide the Pathing Tester unit. Units still calculate their own collision while Hidden despite not interfering with other collidables.

Pathing Tester (Backpack) is a copy and pasted Footman with Collision Size set to 32.00, Attacks Enabled = None, and has the Invulnerable (Neutral) ability. This Unit should never interfere with anything.
Uhh, I can't open your map because it says some level info data missing.

Your triggers don't "unhide" the stash, so I first thought it didn't even summon. Then, there's a collision around the stash, which I'm guessing is the dummy, so it does interfere by creating a block point.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
Uhh, I can't open your map because it says some level info data missing.

Your triggers don't "unhide" the stash, so I first thought it didn't even summon. Then, there's a collision around the stash, which I'm guessing is the dummy, so it does interfere by creating a block point.
Yeah, I'm on the latest patch so my maps are saved with the latest features. You can't open my maps since you're on an older editor version that doesn't have those features.

Anyway, those triggers work, there's no interference or I wouldn't suggest it.

I mentioned Unhiding was unnecessary in my previous post. This is because Hidden units still collide with other obstacles but not vice versa. To put this into perspective, a Goblin Zeppelin (or any transport unit for that matter) Hides units that it loads. Those units aren't blocking pathing or interfering with anything else while in that state. That's the entire purpose behind Hide/Unhide, it's a useful feature for keeping a unit alive but out of the way.

Furthermore, this all happens within the same game frame, so even if Unhiding was necessary it STILL shouldn't interfere with anything.

It should only take 5 minutes to get this up and running for yourself.
 
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
Yeah, I'm on the latest patch so my maps are saved with the latest features. You can't open my maps since you're on an older editor version that doesn't have those features.

Anyway, those triggers work, there's no interference or I wouldn't suggest it.

I mentioned Unhiding was unnecessary in my previous post. This is because Hidden units still collide with other obstacles but not vice versa. To put this into perspective, a Goblin Zeppelin (or any transport unit for that matter) Hides units that it loads. Those units aren't blocking pathing or interfering with anything else. That's the entire purpose behind Hide/Unhide, it's a useful feature for keeping a unit alive but out of the way.

Furthermore, this all happens within the same game frame, so even if Unhiding was necessary it STILL shouldn't interfere with anything.
I just tested it and it doesn't work well. Unhiding is necessary because it's the stash that I need to see or I can't trade items with it. Also, summoning the stash keeps collide on because the dummy is never removed. You specifically said the footman solution doesn't block the path, yet it does. Did I overlook something? This is what the trigger looks like by copying yours:

  • Stash setup
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • Set HeroStashSummon = (Center of (Playable map area))
      • Unit - Create 1 StashPathDummy for Neutral Passive at HeroStashSummon facing Default building facing degrees
      • Custom script: call RemoveLocation(udg_HeroStashSummon)
      • Set StashPathingDummy = (Last created unit)
      • Unit - Hide StashPathingDummy
  • Stash Test
    • Events
    • Conditions
    • Actions
      • Set HeroStashSummon = (Position of Hero)
      • Unit - Move StashPathingDummy instantly to HeroStashSummon, facing Default building facing degrees
      • Set StashDummyPosition = (Position of StashPathingDummy)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between HeroStashSummon and StashDummyPosition) Less than 512.00
        • Then - Actions
          • Unit - Move Stash 0533 <gen> instantly to StashDummyPosition
          • Unit - Unhide Stash 0533 <gen>
        • Else - Actions
          • Unit - Move Stash 0533 <gen> instantly to HeroStashSummon
          • Unit - Unhide Stash 0533 <gen>
      • Custom script: call RemoveLocation(udg_HeroStashSummon)
      • Custom script: call RemoveLocation(udg_StashDummyPosition)
  • Summon stash
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stash
    • Actions
      • Sound - Play FeralSpiritTarget1 <gen>
      • Trigger - Run Stash Test <gen> (checking conditions)
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
If possible could you attach a demo map of what you have above?

But if I had to guess it's because:
1) You're using the wrong Move action when moving the StashPathingDummy.
2) You're Unhiding the Stash after Moving it. Try Unhiding before.
3) You're using the wrong settings in the Object Editor for your Unit(s).
4) I highly doubt this but maybe there's a version discrepancy.

My Object Editor settings:
1) My Stash has 0 Collision Size, Speed Base = 0, and Movement Type = None. This prevents it from colliding with other Units.
2) My StashPathingDummy has 32 Collision Size, Speed Base = 1, and Movement Type = Foot. This allows it to collide with other Units.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
Your speed base is 1? I had mine on 0. I set the movement speed in the test map to 1 and now the hero can walk past it. I don't get why this makes the difference?
Any value > 0 should enable Collision.

I suppose it has to do with the fact that a Speed Base > 0 implies that the Unit should be able to move around and somewhere in the source code that is an important distinguishing factor. Probably has to do with how Buildings function.

But I can't say for certain, Warcraft 3 has a lot of rules that you just need to follow because that's the way things have been designed to function. All you can really do is learn about and try to memorize these quirks. It's often a painful experience but at least Hive can help you get to the bottom of things.
 
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
Despite the inital collision's resolution, I still have the issue of two stashes can get into each other. Despite the stashes have no collision, there should be something that prevents them from getting summoned on top of each other. Keeping it disabled during combat and for a few seconds after that combat would be a nice bonus.

Here's the 2 people stash map if it helps seeing what I have:
 

Attachments

  • Stash demo.w3x
    20 KB · Views: 5

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
You don't need to create multiple copies of the Stash in the Object Editor, one should be fine.

With that in mind, here's a solution that's a bit more advanced since we're relying on Array Variables and For Loops.

At the start of the game we find all of your Heroes that have the Stash ability. Then we create a Stash for them as well as their own personal StashPathingDummy:
  • Stash setup
    • Events
      • Time - Elapsed game time is 0.01 seconds
    • Conditions
    • Actions
      • Set Stash_Dummy_Position = (Center of (Playable map area))
      • -------- --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Level of Stash for (Picked unit)) Greater than 0
            • Then - Actions
              • Set Stash_Total_Amount = (Stash_Total_Amount + 1)
              • -------- --------
              • Set Stash_Hero[Stash_Total_Amount] = (Picked unit)
              • -------- --------
              • Unit - Create 1 Stash for (Owner of (Picked unit)) at Stash_Dummy_Position facing Default building facing degrees
              • Set Stash[Stash_Total_Amount] = (Last created unit)
              • Unit - Hide Stash[Stash_Total_Amount]
              • -------- --------
              • Unit - Create 1 StashPathingDummy for Neutral Passive at Stash_Dummy_Position facing Default building facing degrees
              • Set Stash_Dummy[Stash_Total_Amount] = (Last created unit)
              • Unit - Hide Stash_Dummy[Stash_Total_Amount]
            • Else - Actions
      • -------- --------
      • Custom script: call RemoveLocation(udg_Stash_Dummy_Position)
So we track the Hero, Stash, and Dummy using Unit array variables.

Then whenever one of these Heroes casts the Stash ability we either Unhide and reposition their Stash or Hide it again:
  • Summon stash
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stash
    • Actions
      • For each (Integer Stash_Loop) from 1 to Stash_Total_Amount, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Triggering unit) Equal to Stash_Hero[Stash_Loop]
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Stash[Stash_Loop] is hidden) Equal to True
                • Then - Actions
                  • -------- SUMMON THE STASH: --------
                  • Set Stash_Hero_Position = (Position of Stash_Hero[Stash_Loop])
                  • Unit - Move Stash_Dummy[Stash_Loop] instantly to Stash_Hero_Position, facing Default building facing degrees
                  • -------- --------
                  • Set Stash_Dummy_Position = (Position of Stash_Dummy[Stash_Loop])
                  • -------- --------
                  • Unit - Unhide Stash[Stash_Loop]
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Distance between Stash_Hero_Position and Stash_Dummy_Position) Less than 512.00
                    • Then - Actions
                      • Unit - Move Stash[Stash_Loop] instantly to Stash_Dummy_Position
                    • Else - Actions
                      • Unit - Move Stash[Stash_Loop] instantly to Stash_Hero_Position
                  • -------- --------
                  • Custom script: call RemoveLocation(udg_Stash_Hero_Position)
                  • Custom script: call RemoveLocation(udg_Stash_Dummy_Position)
                • Else - Actions
                  • -------- HIDE THE STASH: --------
                  • Unit - Hide Stash[Stash_Loop]
            • Else - Actions
This allows a Hero to toggle their Stash on and off.

The Variables I'm using:
1721087763249.png


The final result is that you can place down any number of Heroes with the Stash ability and they will all "just work" from the start of the game and onwards. From my tests this seems to prevent the Stashes from stacking on top of one another since the StashPathingDummy is still positioned on top of it's respective Stash. This means that other StashPathingDummy's will collide with it and get pushed away. In some rare cases the Stashes got close to one another but there was still enough room to click between them.

Remember to edit your Stash and StashDummy units in the Object Editor so that they don't have any unwanted stats. I noticed the StashDummy still had it's Attack Enabled and they both had Food Costs. A Hidden unit shouldn't be able to attack but I always like to be on the safe side with these sorts of things.

Keeping it disabled during combat and for a few seconds after that combat would be a nice bonus.
This is a bit more complicated. You can rely on a version compatible Damage Engine to detect when Damage is taken OR you can use the less reliable "A unit is Attacked" Event. The idea would be to Remove the Stash ability in response to one of these Events firing then Add it back after a delay. You would use a unique One-shot Timer to act as the delay since these restart when started again. If you want it to be a global effect then one Timer would suffice and you can simply Disable the ability. If you want each Hero to have their own Timer then you'll need to enter more advanced territory with Arrays. You could also play around with Tech Requirements.

The basic idea for a single Hero:
A unit is Attacked -> It's the Hero -> Remove it's Stash ability -> Start CombatTimer as a One-shot timer that lasts 5 seconds.
CombatTimer expires -> Add Stash ability back to the Hero.
 

Attachments

  • Stash demo.w3x
    20.6 KB · Views: 5
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
You don't need to create multiple copies of the Stash in the Object Editor, one should be fine.

With that in mind, here's a solution that's a bit more advanced since we're relying on Array Variables and For Loops.

At the start of the game we find all of your Heroes that have the Stash ability. Then we create a Stash for them as well as their own personal StashPathingDummy:
  • Stash setup
    • Events
      • Time - Elapsed game time is 0.01 seconds
    • Conditions
    • Actions
      • Set Stash_Dummy_Position = (Center of (Playable map area))
      • -------- --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units in (Playable map area)) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Level of Stash for (Picked unit)) Greater than 0
            • Then - Actions
              • Set Stash_Total_Amount = (Stash_Total_Amount + 1)
              • -------- --------
              • Set Stash_Hero[Stash_Total_Amount] = (Picked unit)
              • -------- --------
              • Unit - Create 1 Stash for (Owner of (Picked unit)) at Stash_Dummy_Position facing Default building facing degrees
              • Set Stash[Stash_Total_Amount] = (Last created unit)
              • Unit - Hide Stash[Stash_Total_Amount]
              • -------- --------
              • Unit - Create 1 StashPathingDummy for Neutral Passive at Stash_Dummy_Position facing Default building facing degrees
              • Set Stash_Dummy[Stash_Total_Amount] = (Last created unit)
              • Unit - Hide Stash_Dummy[Stash_Total_Amount]
            • Else - Actions
      • -------- --------
      • Custom script: call RemoveLocation(udg_Stash_Dummy_Position)
So we track the Hero, Stash, and Dummy using Unit array variables.

Then whenever one of these Heroes casts the Stash ability we either Unhide and reposition their Stash or Hide it again:
  • Summon stash
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Stash
    • Actions
      • For each (Integer Stash_Loop) from 1 to Stash_Total_Amount, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Triggering unit) Equal to Stash_Hero[Stash_Loop]
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Stash[Stash_Loop] is hidden) Equal to True
                • Then - Actions
                  • -------- SUMMON THE STASH: --------
                  • Set Stash_Hero_Position = (Position of Stash_Hero[Stash_Loop])
                  • Unit - Move Stash_Dummy[Stash_Loop] instantly to Stash_Hero_Position, facing Default building facing degrees
                  • -------- --------
                  • Set Stash_Dummy_Position = (Position of Stash_Dummy[Stash_Loop])
                  • -------- --------
                  • Unit - Unhide Stash[Stash_Loop]
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (Distance between Stash_Hero_Position and Stash_Dummy_Position) Less than 512.00
                    • Then - Actions
                      • Unit - Move Stash[Stash_Loop] instantly to Stash_Dummy_Position
                    • Else - Actions
                      • Unit - Move Stash[Stash_Loop] instantly to Stash_Hero_Position
                  • -------- --------
                  • Custom script: call RemoveLocation(udg_Stash_Hero_Position)
                  • Custom script: call RemoveLocation(udg_Stash_Dummy_Position)
                • Else - Actions
                  • -------- HIDE THE STASH: --------
                  • Unit - Hide Stash[Stash_Loop]
            • Else - Actions
This allows a Hero to toggle their Stash on and off.

The Variables I'm using:
View attachment 480031

The final result is that you can place down any number of Heroes with the Stash ability and they will all "just work" from the start of the game and onwards. From my tests this seems to prevent the Stashes from stacking on top of one another since the StashPathingDummy is still positioned on top of it's respective Stash. This means that other StashPathingDummy's will collide with it and get pushed away. In some rare cases the Stashes got close to one another but there was still enough room to click between them.

Remember to edit your Stash and StashDummy units in the Object Editor so that they don't have any unwanted stats. I noticed the StashDummy still had it's Attack Enabled and they both had Food Costs. A Hidden unit shouldn't be able to attack but I always like to be on the safe side with these sorts of things.


This is a bit more complicated. You can rely on a version compatible Damage Engine to detect when Damage is taken OR you can use the less reliable "A unit is Attacked" Event. The idea would be to Remove the Stash ability in response to one of these Events firing then Add it back after a delay. You would use a unique One-shot Timer to act as the delay since these restart when started again. If you want it to be a global effect then one Timer would suffice and you can simply Disable the ability. If you want each Hero to have their own Timer then you'll need to enter more advanced territory with Arrays. You could also play around with Tech Requirements.

The basic idea for a single Hero:
A unit is Attacked -> It's the Hero -> Remove it's Stash ability -> Start CombatTimer as a One-shot timer that lasts 5 seconds.
CombatTimer expires -> Add Stash ability back to the Hero.
Man, that seems rather complex without the ability to open your attached map and see it things there. I don't think I've ever worked with arrays and relying on the info from the forum alone is so much harder. Isn't there some way to make this map compatible with 1.26?
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
Maybe someone else with multiple versions installed can open it and Save it on 1.26, unfortunately I cannot.

It's not too difficult to recreate if you just go step by step. Create the variables first. Then create the Setup trigger. Then create the Summon trigger.

If you have a 2nd monitor you should have it display my triggers and zoom in to make it easy to read. Read it top to bottom, slow and steady. Use the Search For Text feature when trying to find which Actions to create (it's finicky, don't be too specific in your search). Use logic to determine the Conditions -> if it mentions a Unit it's a Unit Comparison, if it mentions a whole number it's an Integer Comparison, if it mentions True/False it's a Boolean Comparison, etc.
 
Level 12
Joined
Jul 5, 2014
Messages
551
Maybe someone else with multiple versions installed can open it and Save it on 1.26, unfortunately I cannot.

It's not too difficult to recreate if you just go step by step. Create the variables first. Then create the Setup trigger. Then create the Summon trigger.

If you have a 2nd monitor you should have it display my triggers and zoom in to make it easy to read. Read it top to bottom, slow and steady. Use the Search For Text feature when trying to find which Actions to create (it's finicky, don't be too specific in your search). Use logic to determine the Conditions -> if it mentions a Unit it's a Unit Comparison, if it mentions a whole number it's an Integer Comparison, if it mentions True/False it's a Boolean Comparison, etc.
Okay, thanks, I'll see if I can make it work tomorrow. Toggling should be handy and maybe I'll settle for a single timer. I don't need the heroes to be damaged but when 10 ghouls running at ya, fiddling with the stash is kinda silly.

Edit: I'm examining the trigger more closely to identify what's what. Two things:

1. I do have to manually set which hero is which array, right? I doubt the game would somehow know on its own.

2. Does this work with initially disabled ability? I intend the stash to be an optional thing which acquired (enabled) after a quest.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
1) No, it's handled automatically in the Stash Setup trigger. Look at the Pick Every Unit action, one by one it finds each hero with the Stash ability and runs the logic needed to A) Store the Hero, B) Create and store their Stash, C) Create and store their StashPathingDummy. The 1st hero it finds uses index [1] in all of the Array variables. The 2nd hero it finds uses index [2] in all of the Array variables. This pattern continues for any number of heroes. If you then look at the Stash Summon trigger you can see that we don't need to know "which hero is which array", we can use the For Loop to make things generic.

HOWEVER, if there comes a time when you do need to get a specific Stash for a specific Hero then I can show you how to link these together. The basic idea would be to store the Array [index] number to the Hero itself which can then be used to reference both it's Stash[X] and it's StashDummy[X] variables.

2) It should still work with the Ability initially Disabled. But to guarantee no issues you should Disable the ability at the very bottom of the Stash Setup trigger after everything else has been setup, that way everything runs as planned. Also, it's perfectly fine to never utilize any of this, it's a very lightweight system and the Stash/Dummys will be Hidden and can remain that way throughout the entire game.
 
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
1) No, it's handled automatically in the Stash Setup trigger. Look at the Pick Every Unit action, one by one it finds each hero with the Stash ability and runs the logic needed to A) Store the Hero, B) Create and store their Stash, C) Create and store their StashPathingDummy. The 1st hero it finds uses index [1] in all of the Array variables. The 2nd hero it finds uses index [2] in all of the Array variables. This pattern continues for any number of heroes. If you then look at the Stash Summon trigger you can see that we don't need to know "which hero is which array", we can use the For Loop to make things generic.

HOWEVER, if there comes a time when you do need to get a specific Stash for a specific Hero then I can show you how to link these together. The basic idea would be to store the Array [index] number to the Hero itself which can then be used to reference both it's Stash[X] and it's StashDummy[X] variables.

2) It should still work with the Ability initially Disabled. But to guarantee no issues you should Disable the ability at the very bottom of the Stash Setup trigger after everything else has been setup, that way everything runs as planned. Also, it's perfectly fine to never utilize any of this, it's a very lightweight system and the Stash/Dummys will be Hidden and can remain that way throughout the entire game.
I've implemented the triggers and so far, it works like a charm (well, not counting the possibility of boxes getting summoned into each other. I think I get most of it but I'd like to better understand what's what here.

join.gif
set.gif
Set Stash_Total_Amount = (Stash_Total_Amount + 1)
1. What does that mean? It starts on 1 and every time it finds an appropriate hero, it adds 1 to the array?

line.gif
joinbottom.gif
if.gif
(Level of Stash for (Picked unit)) Greater than 0
2. Wouldn't that work with Equal to 1? The skill has no multiple levels.


3. What does the "stash_loop" checks? Whether the Stash is hidden or not?


4. I reduced the range and instead of summoning the stash on top of the hero if the range is too big, I gave an error message, saying there's no room for the stash. I also added a remove/add ability to restore the cooldown. Does that work as an alternative?

5. I realized that the best solution for the combat situation is to make the Stash vulnerable. That way, the players themselves will avoid summoning it into combat and this also gives a reason for hiding it. However if the stash is destroyed, I want to remove the Stash ability from the associated hero but I didn't find the way to identify them.

6. I made the Stash building in order to avoid drag selection. Does that interfere with the trigger?
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,871
1) Yes, but technically it starts at 0, then we increase it by 1 for each Hero.

On picking the first hero -> Increase Stash_Total_Amount from 0 to 1 -> Assign Arrays using [1] as the index.
On picking the second hero -> Increase Stash_Total_Amount from 1 to 2 -> Assign Arrays using [2] as the index.

2) Yes, it would work with anything that isn't <= 0. I went with Greater Than since it allows the trigger to still function properly when using multiple ability Levels. That's unlikely to happen, but why not choose the most flexible option?

3) Stash_Loop represents the "current loop cycle" and allows us to interact with all of our Array variables like Stash[1], Stash[2], etc. It's similar to the concept of using Pick Every Unit with a Unit Group, just that in this case Stash_Loop sort of represents our (Picked unit) if that makes any sense.

So a For Loop allows us to reference and associate any number of Variable Types by taking advantage of Arrays. Hive has many tutorials on them that go into greater detail.

4) That sounds fine to me.

5) You can use the For Loop again to determine which Stash died.
Once you know which Stash died you will also have access to it's associated Stash_Hero and Stash_Dummy:
  • Events
    • Unit - A unit Died
  • Conditions
    • (Unit-type of (Triggering unit)) Equal to Stash
  • Actions
    • For each (Integer Stash_Loop) from 1 to Stash_Total_Amount, do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Triggering unit) Equal to Stash[Stash_Loop]
          • Then - Actions
            • Unit - Remove Stash from Stash_Hero[Stash_Loop]
          • Else - Actions
6) That sounds fine to me assuming that it still Moves properly.
 
Last edited:
Level 12
Joined
Jul 5, 2014
Messages
551
1) Yes, but technically it starts at 0, then we increase it by 1 for each Hero.

On picking the first hero -> Increase Stash_Total_Amount from 0 to 1 -> Assign Arrays using [1] as the index.
On picking the second hero -> Increase Stash_Total_Amount from 1 to 2 -> Assign Arrays using [2] as the index.

2) Yes, it would work with anything that isn't <= 0. I went with Greater Than since it allows the trigger to still function properly when using multiple ability Levels. That's unlikely to happen, but why not choose the most flexible option?

3) Stash_Loop represents the "current loop cycle" and allows us to interact with all of our Array variables like Stash[1], Stash[2], etc. It's similar to the concept of using Pick Every Unit with a Unit Group, just that in this case Stash_Loop sort of represents our (Picked unit) if that makes any sense.

So a For Loop allows us to reference and associate any number of Variable Types by taking advantage of Arrays. You should look into how For Loops work, Hive has many tutorials on them that go into greater detail.

4) That sounds fine to me.

5) You can use the For Loop again to determine which Stash died.
Once you know which Stash died you will also have access to it's associated Stash_Hero and Stash_Dummy:
  • Events
    • Unit - A unit Died
  • Conditions
    • (Unit-type of (Triggering unit)) Equal to Stash
  • Actions
    • For each (Integer Stash_Loop) from 1 to Stash_Total_Amount, do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Triggering unit) Equal to Stash[Stash_Loop]
          • Then - Actions
            • Unit - Remove Stash from Stash_Hero[Stash_Loop]
          • Else - Actions
6) That sounds fine to me assuming that it still Moves properly.
Thanks, it seems to work well. I'm not sure I could make a trigger like that on my own but at least I understand it.
 
Top