• 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] Certain scripted units NOT returning to their spawn position after killing a unit

Level 4
Joined
Oct 8, 2023
Messages
21
Hey all, currently running my head into a wall trying to get this portion of my custom combat system working.

I have some scripted units on Neutral Hostile. They're set up with a base acquisition range of ~100, and they "social aggro" with a script (seen below). It's set up this way to work around the static gameplay constant aggro range so I can make more dynamic enemies. I also have a singular unit on Neutral Hostile who isn't scripted and has an acquisition range of 300. Whenever the little scripted guys kill a unit, they just stand around doing absolutely nothing. The big one doesn't have this issue; they return home just fine. I'm wanting to make the little guys go home after killing a unit as well. No idea what's causing them not to. Any help would be appreciated since I'm flat out of ideas.

Here are my triggers:

  • CrabTarget
    • Events
      • Unit - A unit owned by Neutral Hostile Is attacked
    • Conditions
      • ((Attacked unit) is in crabsalive) Equal to True
    • Actions
      • Unit Group - Add (Attacked unit) to crabsAttacking
      • Set crabTarget = (Attacking unit)
      • Unit - Order (Attacked unit) to Attack crabTarget
  • CrabSocialPullTrigger
    • Events
      • Time - Every 1.50 seconds of game time
    • Conditions
    • Actions
      • Unit Group - Pick every unit in crabsAttacking and do (Unit Group - Order (Units within 255.00 of (Position of (Picked unit)) matching (((Matching unit) is in crabsalive) Equal to True)) to Attack crabTarget)
      • Unit Group - Pick every unit in crabsAttacking and do (Unit Group - Order (Units within 10.00 of (Position of (Picked unit)) matching (((Matching unit) is in crabsLulled) Equal to True)) to Attack crabTarget)
  • CrabSocialAttackingTrigger
    • Events
      • Unit - A unit owned by Neutral Hostile Is issued an order targeting an object
    • Conditions
      • (Unit-type of (Ordered unit)) Equal to Lake Crab
    • Actions
      • Unit Group - Add (Ordered unit) to crabsAttacking
Big guy isn't scripted, little guy is. Big guy kills something and returns home, little guy kills something and stands around doing nothing. Both still attack when you get close as intended, but I want the little guys to go home too.

These are my gameplay constants. Fiddling around with them didn't seem to help.

I checked around on the forums & google and couldn't find anything that lead me to a solution. Any insight on what I'm doing wrong here is greatly appreciated!! Thanks 😅

Edit: I should note that they are set to "None", not creeps, if that makes a difference! The big crabs and little crabs all are so I don't think that has anything to do with it.
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,875
I don't fully understand the problem, nor the solution, but maybe this change will work.

The triggers seem like overkill to me and potentially a little buggy. How about trying just one trigger like this?
  • CrabTarget
    • Events
      • Unit - A unit owned by Neutral Hostile Is attacked
    • Conditions
      • ((Attacked unit) is in crabsAlive) Equal to True
    • Actions
      • Set crabTarget = (Attacking unit)
      • Set crabPoint = (Position of (Attacked unit))
      • Set nearbyCrabs = (Units within 500.00 range of crabPoint matching (Owner of (Matching unit)) Equal to Neutral Hostile
      • Unit Group - Pick every unit in nearbyCrabs and do (Actions)
        • Loop - Actions
          • Unit - Order (Picked unit) to Attack crabTarget
      • Custom script: call RemoveLocation( udg_crabPoint1 )
      • Custom script: call DestroyGroup( udg_nearbyCrabs )
Now whenever a crab is attacked (assuming that all crabs are in crabsAlive), all nearby Neutral Hostile units will attack the attacker. I also dealt with the memory leaks in the process (helps with map performance over time).

Also, I don't see the need for the other 2 triggers but maybe I'm missing something. 1) Units won't issue an order when they auto-acquire targets. 2) crabsLulled has a small AoE so it would probably only find the (Picked unit). At that point you should just order the (Picked unit) directly.

Some tips:

1) Use a hidden passive ability, for example one based on Storm Hammers, as a unique identifier for your units. You can add this to them in the Object Editor and check for it in your triggers:
  • Conditions
    • (Level of Crab (Hidden Classification) for (Attacked unit)) Greater than 0
Checking if a Unit is "in a Unit Group" requires that you loop over the unit group, checking each unit until you find your match. This grows in complexity as the number of units in the group grows and can be very inefficient.

2) Dead units will remain in Unit Groups. Remember to remove them if you aren't doing so already.

3) Always use the Multiple version of Actions when given the opportunity. You never know what you might want to add in the future.

4) The If Then Else action can be used inside of a Unit Group loop to easily filter for specific units. You can introduce a Unit variable as the (Picked unit) to optimize this process even further. It's faster to reference the same variable multiple times than it is to reference the same Event Response multiple times.
 
Last edited:
Level 4
Joined
Oct 8, 2023
Messages
21
Thank you for the reply; I'm sure my current setup of triggers would drive experienced folks to drink. Figured I'd iron all that out later once I'm sure things are actually working and I have a better understanding of the engine, haha. I didn't have anything set up to remove dead units from groups so I'll get on that, thank you! Future-proof code is something I'm working to understand better. At the moment everything's held together by duct tape and a dream, haha.

What's baffling me is that my setup is somehow, for some reason, preventing affected units from returning to their spawn points after killing a unit. For example, I have a guard you can bring mobs to. The guard will kill a crab I bring over, then return to where he was before attacking the crab. The crabs themselves, however, upon killing the guard (or the player character), just stand around instead of returning home. I'm assuming somewhere in my triggers is something I'm not turning off that's preventing them from doing that.

here's a video of the guard doing what i want the crabs to be doing. the guard has zero scripts and is simply an ally to the player in the forces menu.

I'll explain my triggers; the ones I have set up for the crabs are to ensure they work with additionally spawned crabs. Going for a kind of MMO type system there since the campaign features interconnected maps. It's like this:

1: first map initialization, it takes every crab and puts them in the crabsAlive unit group
  • InitGrabAllCrab
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Unit Group - Add all units of (Units of type Lake Crab) to crabsalive
      • Unit Group - Pick every unit in crabsalive and do (Trigger - Add to CrabFacepullAddToGroup <gen> the event (Unit - (Picked unit) Acquires a target))
the added trigger makes newly spawned crabs "social aggro" even if the first crab is pulled by their own acquisition range instead of being attacked.

2: when something in crabsAlive is attacked, it adds the attacker to crabTarget (this bit has gotten iffy with aggro bouncing back and forth with multiple attackers. i'll work it out later.)
3: every 1.5 seconds, crabs in crabsAttacking pulse aggro to crabsAlive in a radius by telling them to attack crabTarget. this essentially lets me create "factions" of units that help each other. (crabsLulled is a similar group with a heavily reduced aggro radius; it's meant to emulate EverQuest's line of spells that reduced enemy aggro range)
4: CrabSocialAttackingTrigger activates from the above, adding the newly aggro'd unit to crabsAttacking, which then pulse aggro as well. EverQuest "trains" were caused by a similar system and that's what i'm trying to recreate.

every 45s, a trigger checks if there are 6 or fewer crabs in the map. if so, it spawns one in. no issues there.
this then triggers:
  • CrabFacepullRespawnTargetAdd
    • Events
      • Unit - A unit enters (Playable map area)
    • Conditions
      • (Unit-type of (Triggering unit)) Equal to Lake Crab
    • Actions
      • Unit Group - Add (Triggering unit) to crabsalive
      • Trigger - Add to CrabFacepullAddToGroup <gen> the event (Unit - (Triggering unit) Acquires a target)
which then adds the crab to this so that face-pulled crabs, not just attacked ones, are a part of crabsAttacking too:
  • CrabFacepullAddToGroup
    • Events
    • Conditions
    • Actions
      • Unit Group - Add (Triggering unit) to crabsAttacking
      • Set crabTarget = (Targeted unit)
(i'm pretty sure this causes leaks over time; i couldn't find a better solution!)

all of that works as i'd like it to -- i'll work the leaks out once i'm more experienced.

...the issue is somewhere, throughout all of this, is a trigger, code, whatever, that prevents these crabs from returning to their spawn points after they kill something (like every other unit does by default, to my understanding). i cannot for the life of me figure out what part of the code that is 😅
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,875
It's probably because of the direct Attack order you're giving them. I realize you sort of need this due to your low acquisition range.

I would reset the Gameplay Constants back to their default values for starters. Then I would test to see if it's the direct Attack order that's causing the problem by disabling that Action and instead just manually aggro the crabs by getting close to them and drag them far enough away that they SHOULD return. You could also try switching the Attack order to Right Click instead. After that, try switching it to a Point Order -> Issue unit to Attack Move to (Position of crabTarget).

Do note that crabTarget can only be Set to one unit at a time, so it's not a great solution. For instance, if the crabs are fighting two separate targets at once then it will simply not work as intended since the variable will bounce between the two. A solution to that would be to introduce a Hashtable for tracking data on a per-unit basis, OR use my personal favorite -> Unit Indexer, which does a similar thing but in a much easier way. This would allow you to give each crab their own personal crabTarget.

Edit:
Also, you never remove units from crabsAttacking, which is a big issue. They should be removed from the group after their target dies or when they move too far away from their "camp". The single trigger solution I suggested before could help fix all of this.
 
Last edited:
Level 4
Joined
Oct 8, 2023
Messages
21
Order to right click & attack move had the same result, and I'm unsure how much resetting gameplay constants helped but removing all order commands in the crab scripts made the crabs begin to return home properly. Nail on the head with that one -- thank you. Happy to see it, but now I'm back to step 1 for social aggro. Any attempts at issuing orders causes units to essentially "forget their home" which I'm pretty sure would lead to softlocks aplenty with what I'm trying to make. Need to avoid that. I should note that one of my main concerns for this system is the ability to have custom "social" aggro ranges for individual creature types, so the gameplay constant creep camp radius value is being avoided.

I do have a computer player with zero AI set up to control the town guards, and they assist me after I get attacked, even by neutral creeps, so long as I'm in range. This is perfect. However, I want to be able to start a chain reaction, where when I pull one crab from a long distance or a guard comes to assist me, they "shout" repeatedly as they go, pulling any allies on their way to their destination. The previous system worked for that, but I cannot for the life of me find a way to replicate it without resorting to direct unit orders which, again, seem to break their ability to return home. Frustrating indeed.

Thank you again for the detailed write-ups on all of these engine quirks. I'm doing my best to understand and absorb things so the campaign isn't a laggy mess down the line, haha. I'll be checking that unit indexer out too. Never installed anything like an addon for WC3 before.

Btw, if I'm to continue requesting help on this extended issue, should I keep posting here in this thread or just make a new one? Thank you so much either way.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,875
A simple system could be thrown together using the Unit Indexer approach. A basic understanding of Array variables is needed.

How the Unit Indexer works:

The Unit Indexer automatically assigns each Unit a unique custom value as they come into existence. We can take advantage of this fact by using a Unit's custom value as the [index] in any Array variable of our choosing. This results in each Unit having it's own personal [index] in an Array.
In other words, each unit can have it's own value in an Array variable.

Creating the system:

First things first, store every single Neutral Hostile unit's "starting position" in a Point array variable. Whether they're preplaced or created via triggers, this should be Set the moment they come into existence:
  • Set Creep = (Your unit)
  • Set Creep_Start_Position[(Custom value of Creep)] = (Position of Creep)
^ Remember that each Creep now has it's own unique (Custom value) thanks to the Unit Indexer.


Then whenever a Neutral Hostile creep is attacked (preferably damaged) -> Add the creep to Aggro_Group and Set it's Creep_Target to the attacker:
  • Set Creep = (Attacked unit)
  • Set Creep_Target[(Custom value of Creep)] = (Attacking unit)
  • Unit Group - Add Creep to Creep_Aggro_Group
Aggro_Group contains all of our creeps that are chasing after a target.


Then every 1.50 seconds or so -> Pick every unit in Aggro_Group and Order them to Attack their own personal Creep_Target. ALSO, this is the big new change, do a Distance check between their current position and their starting position. If the distance is too far then Remove them from Aggro_Group and order them to Stop:
  • Unit Group - Pick every unit in Creep_Aggro_Group and do (Actions)
    • Loop - Actions
      • Set Creep = (Picked unit)
      • Set Creep_Current_Position = (Position of Creep)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Distance between Creep_Start_Position[(Custom value of Creep)] and Creep_Current_Position) Less than 900.00
        • Then - Actions
          • Unit - Order Creep to Attack Creep_Target[(Custom value of Creep)]
        • Else - Actions
          • Set Creep_Target[(Custom value of Creep)] = No unit
          • Unit Group - Remove Creep from Creep_Aggro_Group
          • Unit - Order Creep to Stop
This should achieve two things: 1) They stop attacking and return home when they're too far away from "home". 2) They no longer get periodic orders since they're out of the group. Additionally, you should Remove them from this Unit Group when they die.

I suggest looking into removing all of the memory leaks after you get it working.
 
Last edited:
Level 4
Joined
Oct 8, 2023
Messages
21
Note to anyone else struggling with this, if your brain is as small as mine and for some reason think (Your Unit) for "Creep" is a unit-type, save yourself the trouble. It's just a Unit variable. Don't overcomplicate it. It also has nothing to do with the variables created by the Unit Indexer. Spent far too long trying to reverse-engineer that whole deal, feeling like I was missing something the entire time and I feel incredibly dumb after figuring it out.

Anyways, in my case, creating a Murloc Tiderunner and setting "Murloc" to (Last Created Unit) worked perfectly!

  • PeriodicMurlocCreation
    • Events
      • Time - Every 4.00 seconds of game time
    • Conditions
    • Actions
      • Unit - Create 1 Murloc Tiderunner for Neutral Hostile at (Random point in spawnsgohereregion <gen>) facing Default building facing degrees
      • Set Murloc = (Last created unit)
      • Unit Group - Add Murloc to Murloc_Alive_Group
      • Set Murloc_Start_Position[(Custom value of Murloc)] = (Position of (Last created unit))
  • AMurlocIsAttacked
    • Events
      • Unit - A unit Is attacked
    • Conditions
      • (Owner of (Attacked unit)) Equal to Neutral Hostile
      • (Unit-type of (Attacked unit)) Equal to Murloc Tiderunner
    • Actions
      • Set Murloc = (Attacked unit)
      • Set MurlocTarget[(Custom value of Murloc)] = (Attacking unit)
      • Unit Group - Add Murloc to Murloc_Attacking_Group
      • Floating Text - Create floating text that reads if you see this, it... at Murloc_Start_Position[(Custom value of Murloc)] with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
Attacking a Murloc Tiderunner now has the intended effect of displaying text above their initial spawnpoint, meaning that the triggers worked and each individual murloc has a home!

war3_YplRpJbzts.png


I once again astonish myself with my ability to overcomplicate simple things. I feel like I should be paying for help as good as Uncle's. Thank you so much. 🫡
 
Top