• 🏆 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!

Move Units to 20 Random Points Without Having any Overlap?

Status
Not open for further replies.
Level 18
Joined
Mar 16, 2008
Messages
721
This is more of a cosmetic trigger but when player's hero gets defeated I want it to go to "heaven" zone. I put 20 points in this region and had it pick a random point to go to, but sometimes it does not work because that place is already occupied by another slain hero. Can anyone help me devise a way to prevent this? Here's my current trigger that only works sometimes.

  • ... Dies
    • Events
      • Unit - ... Dies
    • Conditions
    • Actions
      • ...
      • ...
      • ...
      • Set VariableSet Rezing_Hero_Hev_Var = (Dying unit)
      • Set VariableSet Ran_Num_Rez_Var = (Random integer number between 1 and 20)
      • Hero - Instantly revive Rezing_Hero_Hev_Var at Rez_Pt_Var[Ran_Num_Rez_Var], Show revival graphics
      • Unit - Make Rezing_Hero_Hev_Var Invulnerable
      • Unit - Change ownership of Rezing_Hero_Hev_Var to Neutral Passive and Change color
      • Unit - Change color of Rezing_Hero_Hev_Var to Snow
      • ...
      • ...
      • ...
 
Level 18
Joined
Oct 17, 2012
Messages
820
Use dynamic indexing to avoid repeats. The only thing is that you have to run Setup Trigger again once all points are taken.
Now, if you wanted to re-add a point that is no longer occupied before all points are taken. Increase Max by one and use the new Max as the array index for the new point.

  • Setup
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set Max = 10
      • For each (Integer A) from 1 to Max, do (Actions)
        • Loop - Actions
          • Set point[(Integer A)] = Some Location
  • PickRandomLoc
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Set integer = (Random integer number between 1 and Max)
      • Set randomPoint = point[integer]
      • Set point[integer] = point[Max]
      • Set Max = (Max - 1)
 
Last edited:
Level 18
Joined
Mar 16, 2008
Messages
721
@Cheshire just worried about the small change some other unit occupies that spot.

@SmitingDevil i'll go ahead with dynamic indexing. i'm still somewhat worried if some random player controlled unit happens to be on one of those points. can u think of a way to check if the region is occupied and then rerun the trigger?

wait how would I set these to twenty different locations?

  • Set Rez Hev Pts
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Max_Hev_Rez_Pts_Var = 20
      • For each (Integer A) from 1 to Max_Hev_Rez_Pts_Var, do (Actions)
        • Loop - Actions
          • Set VariableSet Rez_Pt_Var[(Integer A)] = (Center of Rez Zone 01 <gen>)
          • Set VariableSet Rez_Pt_Var[(Integer A)] = (Center of Rez Zone 02 <gen>)
          • Set VariableSet Rez_Pt_Var[(Integer A)] = (Center of Rez Zone 03 <gen>)
          • ...
hmmm? can you make regions arrays?
 
Last edited:
Level 21
Joined
Mar 29, 2020
Messages
1,237
@Cheshire just worried about the small change some other unit occupies that spot.
are you sure that is what is causing the issue? if so you can probably just use a different method of moving the unit to there. in general the game code knows how to place a unit next to a different unit and not on top of it even when it was given the same spawn point...
 
Level 18
Joined
Mar 16, 2008
Messages
721
so if they pick the same point using my simple method, they should just end up next to eachother? i suppose that's tolerable.

i'm double checking some other problem isn't lurking now.

edit, maybe i'll just do the random point, delete random point method.
 
Level 20
Joined
Feb 23, 2014
Messages
1,264
Not sure if this is the most efficient way of doing this, but it works:

1) First set these variables:

  • Relocation Start
    • Events
      • <use whatever event you want>
    • Conditions
    • Actions
      • Set SourceGroup = <Units that are to be teleported> -- unit group; if you want to, you can set this at map initialization instead
      • Set TargetRegion = <Region to which you want to teleport units> -- region; unit group; if you want to, you can set this at map initialization instead
      • Trigger - Run Relocation Trigger <gen> (checking conditions)

2) The relocation trigger:

  • Relocation Trigger
    • Events
    • Conditions
    • Actions
      • Set RandomPoint = (Random point in TargetRegion) -- point; alternatively you can skin the region variable from step 1 and choose a region here
      • Set TempGroup = (Units within 128.00 of RandomPoint) -- unit group; replace 128 with the spacing distance that works for you
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in TempGroup) Equal to 0
        • Then - Actions
          • Set TempUnit = (Random unit from SourceGroup)
          • Unit - Move TempUnit instantly to RandomPoint
          • Unit Group - Remove TempUnit from SourceGroup
        • Else - Actions
          • -------- do nothing here -------- -- you don't have to have this, I added it so it's more clear that next 2 actions are to be outside this block
      • Custom script: call RemoveLocation(udg_RandomPoint)
      • Custom script: call DestroyGroup(udg_TempGroup)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Number of units in SourceGroup) Greater than 0
        • Then - Actions
          • Trigger - Run Relocation Trigger <gen> (checking conditions)
        • Else - Actions
          • Custom script: call DestroyGroup(udg_SourceGroup) -- if you want to keep this group, skip this step

Obviously, this is a very simple example meant not as a complete solution (because it's 2 a.m. here and I can't force my brain to think), but more of an illustration how the effect could be achieved, so it doesn't account for unit pathing, could possibly be condensed and you might want to add some extra conditions to either TempGroup or SourceGroup to better match your needs (e.g. not count dead units near the arrival point).

Anyway, the way this works is that each time the relocation trigger is run, it will select a random point in the specified region and then check if there are no units in the 128 radius around that point. If not, then one randomly chosen unit from the source group will be moved there. Since a unit is removed from the unit group when it's teleported, the number of units remaining in the group serves as a counter for how many units still need to be allocated a valid point - the trigger will continue to run itself (i.e. select a random point and check if it's valid) until each unit is teleported.

A fair warning though, you want the spread / spacing distance to be small and/or the target region to be big, otherwise you might run into a situation where the trigger is unable to find enough valid points to accomodate all of your units, i.e. it will run itself forever causing a crash. If you don't want to risk it, you might also add some execution counter, which causes the trigger to stop or just move the units to random points if it can't finish properly.

So yeah, I don't guarantee it's 100% correct since I'm in zombie mode, but it should serve as a good enough inspiration. Good luck.
 
Last edited:
Level 20
Joined
Feb 23, 2014
Messages
1,264
So yeah, I completely misread what you wanted to do here - you didn't want to move units from a unit goup to 20 random points, but have 20 manually selected random points that you can later use to without repeats, so... Yeah, here's how you can do it:

a) Set your region array variable manually:

  • Actions
    • Set PointCount = 19 -- integer; value = n-1, where n is the number of points
    • Set Point[0] = (Center of <Region1>)
    • Set Point[1] = (Center of <Region2>)
    • Set Point[2] = (Center of <Region3>)
    • ...

b) Then once you move a unit, do this:

  • Actions
    • Set Number = (Random integer number between 0 and PointCount)
    • Unit - Move <your unit> instantly to Point[Number]
    • Custom script: call RemoveLocation(udg_Point[Number])
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Number Not equal to PointCount
      • Then - Actions
        • Set Point[Number] = Point[PointCount]
      • Else - Actions
    • Set PointCount = (PointCount - 1)

Let's explain how this works - we have 20 points (array indexes 0-19). The first thing that happens is that the trigger selects a random index number and by extension a random point from your pre-defined list. It then moves the unit and then removes the point. Let's consider 2 options:

a) The point selected had the maximum index number, i.e. 19 - in this case, Point[19] no longer has any value and we don't want to draw it again, so we decrease the maximum index number (PointCount) that can be selected by 1. The next time the trigger runs, it will pick between region indexes 0-18.

b) The point selected had an index number smaller than the maximum, e.g. 10 - now, the next time this runs, you want to draw any number other than 10 as that point was already used and removed, but how do you tell the game to exclude 10 from the next draw? Well, you can't, but what you can do is move the last point from the array to index 10, so that this index now contains a point that exists and wasn't used before.

However, while we removed an "empty index" from our array, we now have one value that's stored under 2 indexes - the point that was originally stored under index 19 is now also stored under index 10. That's unnecessary, so we reduce the maximum index by 1, thus going back to having a full "list" of unique points numbered 0-18 that we can use to draw from :)

---

Anyway, since my first answer missed the mark, I also played around with something similar - getting randomly selected points that are at least some distance from each other and storing them in an array for future use - basically the same my first idea, but with no units involved.

I know this isn't exactly what OP wanted, but since I've already done it, I'll post it anyway in case someone finds it useful:

1. Variables - I'm mentioning this separately, because there's one thing to keep an eye on. Overall, you need the following variables:
  • Integers - PointCounter, CompareLoop --> both have initial value 0 (default)
  • Points - TestPoint, ChosenPoint (array)
  • Boolean - IsPointValid --> when you create this variable, set the initial value to "True"

2. The trigger:

  • Point Selection
    • Events
    • Conditions
      • PointCounter Less than or equal to 19 -- compares to n-1 where n is the amount of points you want to select
    • Actions
      • Set TestPoint = (Random point in <Your Region>) -- choose your region here
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • PointCounter Greater than 0
        • Then - Actions
          • For each (Integer CompareLoop) from 0 to (PointCounter - 1), do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Distance between TestPoint and ChosenPoint[CompareLoop]) Less than or equal to 128.00 -- choose your spread distance here
                • Then - Actions
                  • Set IsPointValid = False
                • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • IsPointValid Equal to True
            • Then - Actions
              • Set ChosenPoint[PointCounter] = TestPoint
              • Set PointCounter = (PointCounter + 1)
            • Else - Actions
              • Custom script: call RemoveLocation(udg_TestPoint)
              • Set IsPointValid = True
        • Else - Actions
          • Set ChosenPoint[PointCounter] = TestPoint
          • Set PointCounter = (PointCounter + 1)
      • Trigger - Run (This trigger) (checking conditions)

The result is a region array with n randomly selected points more than <spread value> apart from each other, with indexes from 0 to n-1.
 
Last edited:
Level 18
Joined
Mar 16, 2008
Messages
721
Could you double check this for me if you have a minute? It's a bit confusing but I think we're pretty much just setting used points to unused points. I've put too much effort into such a minor thing lol.

  • Set Rez Hev Pts
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Rez_Pt_Count = 20
      • Set VariableSet Rez_Pt_Var[1] = (Center of Rez Zone 01 <gen>)
      • Set VariableSet Rez_Pt_Var[2] = (Center of Rez Zone 02 <gen>)
      • ...
  • Knight Rez in Heaven
    • Events
      • ...
    • Conditions
      • ...
    • Actions
      • Set VariableSet Ran_Num_Rez_Var = (Random integer number between 1 and Rez_Pt_Count)
      • Set VariableSet Rezing_Hero_Hev_Var = (Dying unit)
      • Hero - Instantly revive Rezing_Hero_Hev_Var at Rez_Pt_Var[Ran_Num_Rez_Var], Show revival graphics
      • Unit - Move Rezing_Hero_Hev_Var instantly to Rez_Pt_Var[Ran_Num_Rez_Var]
      • Custom script: call RemoveLocation(udg_Rez_Pt_Var[udg_Ran_Num_Rez_Var])
      • Unit - Make Rezing_Hero_Hev_Var Invulnerable
      • Unit - Change ownership of Rezing_Hero_Hev_Var to Neutral Passive and Retain color
      • Unit - Change color of Rezing_Hero_Hev_Var to Snow
      • Custom script: call SetUnitPropWindow(udg_Rezing_Hero_Hev_Var, 0.00)
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Ran_Num_Rez_Var Not equal to Rez_Pt_Count
        • Then - Actions
          • Set VariableSet Rez_Pt_Var[Ran_Num_Rez_Var] = Rez_Pt_Var[Rez_Pt_Count]
        • Else - Actions
      • Set VariableSet Rez_Pt_Count = (Rez_Pt_Count - 1)
 
Last edited:
Status
Not open for further replies.
Top