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

[Trigger] Need help with a "Chance to attack target" trigger

Status
Not open for further replies.
Level 2
Joined
May 2, 2019
Messages
6
I need a targeting system where an enemy unit choose a random unit (with a unique percent value) from a unit group and attack that unit. This system must apply to groups of any size and the total percent value of all units combined must add up to 100%.

Example:

Lets assume there are 3 available targets, each with a unique percent chance of being attacked.

Unit 1: 50% to be hit
Unit 2: 30% to be hit
Unit 3: 20% to be hit

I want the enemy to have 50% chance of attacking Unit 1, 30% chance of attacking unit 2 and 20% chance to attack unit 3.

How can I do this?
 
I think this should do the trick

  • Attack Stuff
    • Events
    • Conditions
    • Actions
      • Set AttackChance = (Random real number between 0.00 and 100.00)
      • Unit Group - Pick every unit in AttackGroup and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • AttackChance Less than or equal to 50.00
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • ((Picked unit) is in AttackChanceGroup[1]) Equal to True
                • Then - Actions
                  • Unit - Order AttackUnit to Attack (Random unit from AttackChanceGroup[1])
                • Else - Actions
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • AttackChance Greater than 50.00
                  • AttackChance Less than or equal to 80.00
                • Then - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • ((Picked unit) is in AttackChanceGroup[2]) Equal to True
                    • Then - Actions
                      • Unit - Order AttackUnit to Attack (Random unit from AttackChanceGroup[2])
                    • Else - Actions
                • Else - Actions
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • AttackChance Greater than 80.00
                    • Then - Actions
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • ((Picked unit) is in AttackChanceGroup[3]) Equal to True
                        • Then - Actions
                          • Unit - Order AttackUnit to Attack (Random unit from AttackChanceGroup[3])
                        • Else - Actions
                    • Else - Actions

Basically you add all the units you want to have a chance of being attacked to the AttackGroup unit group, then separate them into the groups AttackChanceGroup[1], [2], and [3]. Group [1] has a 50% chance of being attacked, [2] has 30% chance of being attacked and [3] has 20% chance of being attacked.
 
Last edited:
Level 2
Joined
May 2, 2019
Messages
6
@FeelsGoodMan I came up with something similar, which works fine when all values and unit counts are predetermined and "known". The problem occur when there are a large amount of units to pick from, each with their unique % chance of being hit. I couldn't figure out a smart way to implement this (seemingly simple task) and ended up with a lot of loops and conditions. So I thought maybe there is a better way to do this, I can think of one possible solution but it ain't pretty! :)

I will try to build on your idea and see if i can come up with a creative solution. Thanks a lot for the reply! :D
 
Level 2
Joined
May 2, 2019
Messages
6
I came up with an easier way to accomplish what I wanted. Its not exactly the same, but I will mention it here in case someone else struggle with a similar problem in the future:

I assigned the "Chance to be hit" value of every unit in a Hashtable, added all the possible targets into a Unit Group and created a random real value (0-100) to simulate percent. Then I searched through all the potential targets and picked the one with a % value closest to the "chance value" of the dice, and set that unit as the "Target".
 
Level 39
Joined
Feb 27, 2007
Messages
5,023
I came up with something similar, which works fine when all values and unit counts are predetermined and "known". The problem occur when there are a large amount of units to pick from, each with their unique % chance of being hit. I couldn't figure out a smart way to implement this (seemingly simple task) and ended up with a lot of loops and conditions.
The problem is overspecified: you’re giving too much information and some of it is conflicting/it doesn’t all add up because you’re trying to tell it what too many numbers should be. Defining percentages for units won’t work because they won’t all always add up to 100 or they’ll be more than 100.

I think, ultimately, you as a developer have to make a call about how exactly this system should work. The simplest (conceptually) is to use a weighting system like unitpools and itempools use. In fact I think you can use a unitpool here to solve your problem. Here are the steps I would use to accomplish this:
  1. Save, in a hashtable, a weight value for each unit type. Using the unit type as a key in the table will make it trivial to retrieve when necessary (better than using and searching through parallel arrays).
  2. Add all targets you need to choose between to a unit group.
  3. Create a new unitpool or clear an existing one.
  4. Loop through the group and add an entry to the unitpool for each unit encountered. Add its unit type and use the weighting value stored in the hashtable in step 1. If pools could output a random integer without trying to create a unit it would be possible to use the units handle ids as the integers in the pool instead to get the unit directly but we can do it this way. (I, unfortunately, assume that if you input the same unit id into the pool it just overwrites the previous weight value instead of combining them. This requires a little extra work to get the total chance for multiple of the same unit type to work properly but it can be overcome.)
  5. Place a random unit from the pool in an unused corner of the map. Read its unit type, then immediately remove it.
  6. Search through the group until you find a unit whose type matches the type of the unit that was placed. This is your aggro target.
  7. Clear or destroy the pool and group.
 
Level 8
Joined
May 21, 2019
Messages
435
Save all units in a unit group. Make a hashtable and assign a value to each, corresponding to their chance of being hit.
With your example:
Unit 1: 5
Unit 2: 3
Unit 3: 2
When an attack needs to pick a target, you add up the sum of these (10) by looping through the unit group and loading the hashtable value, while doing that you save the units and their interval numbers in 2 arrays. The interval number is an integer that you use for counting during the loop. A units interval value is that counter + their hit value from the hashtable. You're also going to need a counter integer for the units position in the array.
With the above example, you'd have something akin to the following variables:
  • Unit[1] = Unit 1
  • Unit[2] = Unit 2
  • Unit[3] = Unit 3
  • Interval[1] = 5
  • Interval[2] = 8
  • Interval[3] = 10
  • IntervalSize = 10
Now you roll between 1 and IntervalSize(10). Lets say you roll 7.
Then you run through the intervals in a loop:
Is Interval[1](5) greater than or equal to Roll(7)? No....
Is Interval[2] greater than or equal to Roll? Yes... so Unit[2] is the target then. Break loop and execute the attack trigger.
 
Last edited:
Level 2
Joined
May 2, 2019
Messages
6
@Pyrogasm "The problem is overspecified: you’re giving too much information and some of it is conflicting/it doesn’t all add up because you’re trying to tell it what too many numbers should be. Defining percentages for units won’t work because they won’t all always add up to 100 or they’ll be more than 100."

I tried to give a simple and clear example to make it easier to understand what I wanted to do. I thought it would be bothersome to read through a text wall. And yes, my comments are indeed conflicting! This is but a fraction of a much larger system, but this is where I got stuck and wanted some tips :D

@Cespie That is a good idea, thank you!

Thanks for all the suggestions everyone! I now consider this problem as solved. :)
 
Level 39
Joined
Feb 27, 2007
Messages
5,023
I didn’t mean it was hard for me to understand what you want to do. “Overspecified” is a term in math that means you gave the problem too much information. Like defining the distance, velocity, and duration a knock back should take.

Cespie’s idea is almost exactly what the unitpools method does but it with more steps, fwiw.
 
Level 8
Joined
May 21, 2019
Messages
435
Cespie’s idea is almost exactly what the unitpools method does but it with more steps, fwiw.

Yeah, it's more or less the same general concept. I just personally think that my example was a bit more specific. I honestly don't really understand how it's supposed to have more steps though. Yours uses unit types and spawns dummy units on the map. Computation-wise, it seems heavier, and the unit-type thing seems like it would put a weird restriction on how this system would work. What if the 3 potential targets are all Footmen? How does that even work then?

Genuine question, I may have just misunderstood you, but I figured that since it wasn't entirely clear to me, it may have been a bit too abstract for OP as well, so I attempted to write up a more straightforward example of how you could go about doing this.

Also, Unitpools seem to be a JASS thing. For GUI users, that can be a bit of a dealbreaker.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,023
What if the 3 potential targets are all Footmen? How does that even work then?
Simply picking the first footman you encounter isn't any less random. The total weight would have to be modified if the pool type replaces rather than combines. The difference is these steps:
  • Compute the total weight value of all units.
  • Randomize a value between 1 and this number.
  • Search sequentially through the array comparing the total value at each step to determine where to break.
Are what the pool does for you. Instead this becomes:
  • Randomize a unit type (this places it).
  • Read its type.
  • Remove the unit.
  • Search in parallel through the group until a picked unit's type matches the randomized type.
So I was wrong about it being literally more steps, but you're letting the unitpool do the 'hard' part for you. They're just a fun type nobody uses, which is why I recommended it. Yes, reading/setting arrays is less computationally expensive than spawning a unit, but in the grand scheme of things neither option will have any noticeable impact.
 
Simply picking the first footman you encounter isn't any less random. The total weight would have to be modified if the pool type replaces rather than combines. The difference is these steps:
  • Compute the total weight value of all units.
  • Randomize a value between 1 and this number.
  • Search sequentially through the array comparing the total value at each step to determine where to break.
Are what the pool does for you. Instead this becomes:
  • Randomize a unit type (this places it).
  • Read its type.
  • Remove the unit.
  • Search in parallel through the group until a picked unit's type matches the randomized type.
So I was wrong about it being literally more steps, but you're letting the unitpool do the 'hard' part for you. They're just a fun type nobody uses, which is why I recommended it. Yes, reading/setting arrays is less computationally expensive than spawning a unit, but in the grand scheme of things neither option will have any noticeable impact.
The thing you describe somehow sounds like something from Blizzard.j (probably nobody uses it)
Lightly edited (added arguments).
JASS:
//***************************************************************************
//*
//*  Random distribution
//*
//*  Used to select a random object from a given distribution of chances
//*
//*  - RandomDistReset() clears the distribution list
//*
//*  - RandomDistAddItem(integer, weight) adds a new object to the distribution list
//*    with a given identifier and an integer chance to be chosen
//*
//*  - RandomDistChoose() -> integer will use the current distribution list to choose
//*    one of the objects randomly based on the chance distribution
//*
//*  Note that the chances are effectively normalized by their sum,
//*  so only the relative values of each chance are important
//*
//***************************************************************************
 
Level 8
Joined
May 21, 2019
Messages
435
Simply picking the first footman you encounter isn't any less random.
Assuming that all footmen have the same aggro chance? Sure. But if you have 2 footman, and one of them is supposed to be 30 times more likely to be hit than the other, then picking "the first" footman becomes a 1:1 rather than a 1:30 chance.
Unless of course I am misunderstanding how this whole thing is supposed to work. I admit not understanding the full extent of the solution you're proposing.

Yes, reading/setting arrays is less computationally expensive than spawning a unit, but in the grand scheme of things neither option will have any noticeable impact.
It's a small computation in both cases, yeah, but this system does have the potential to be run at a very rapid pace since it's a combat mechanic.
Say that you have 100 attacks firing per second. That's 100 units spawned, read, and removed, per second. I'm not sure how heavy that is, but it sounds intensive.
 
Level 39
Joined
Feb 27, 2007
Messages
5,023
That would be 100 units switching targets every second, not 100 attacks per second. Aggro probably doesn't switch that often, but yes it's dependent on how often it's used. You are correct unitpools can only work when each unit of a particular type (in the aggro group) has the same weight; that this was the case was an assumption of mine.

The best solution is to use the functions Tasyen pointed out in Blizzard.j
 
Level 8
Joined
May 21, 2019
Messages
435
That would be 100 units switching targets every second, not 100 attacks per second. Aggro probably doesn't switch that often, but yes it's dependent on how often it's used. You are correct unitpools can only work when each unit of a particular type (in the aggro group) has the same weight; that this was the case was an assumption of mine.
The best solution is to use the functions Tasyen pointed out in Blizzard.j
Yeah Tasyens solution is definitely the best, if OP can use a Jass solution.
OP hasn't made clear what the game mode is, and there's a types of gameplay that actually involves switching targets on every attack. It's not all that common in W3 maps, but it exists.
I recently helped beta-test a mobile game that introduced an event that involved an archer shooting at targets on a target range. Which targets she shot at depended on their aggro chance, and at high attack speeds, this would be upwards of 20-30 attacks per second, which then had bounce and spray attacks, which maybe peaked at something like 60-70 attack rolls per second if things got a bit crazy.
Then there's the possibility of a "spray and pray" type spell which flails around randomly on a group of targets at a very high speed.
Couple both of these mechanic examples with the possibility of multiple units doing it at once, and things start to get intense.

The first thing that came to mind when I read OPs post was a system where each individual attack is random, because aggro systems are usually based on threat, so that the tank can maintain aggro consistently.

Total assumption on my part of course, but I kept the possibility that this needed to be scalable in mind.
 
Status
Not open for further replies.
Top