Manifest is a competitive, two-player, turn-based strategy mod for Warcraft III developed by Finn Haverkamp.

Report RSS Tactical Information: Range

A ping range feature in Manifest allows players to see the attack range of their units.

Posted by on

In Manifest, it is vital that all tactical information is available to players readily. One important piece of information is the attack range of a given unit. If you play enough Warcraft III or play around with the World Editor enough, you eventually learn the measurement system of the game. For example, a statistic like "700 attack range" is a valuable piece of information to players, and with a glance, players will immediately recognize if their units are within attack range of the enemy or not. I make this basic information available to players with an individualized statistic ability for each unit. But I wanted to take it one step further. Nothing is more frustrating in games than when you think you can do something, but it turns out you can't, and it costs you a precious advantage as a result. So to curb this frustration, I've implemented a display range feature.

This is how it works. When players type the string "range" into the chat log, a circle of power will spawn beneath the currently selected unit, visually representing the attack range of that unit. Three seconds later, the circle of power disappears, but because of the capability, players now have a very intuitive understanding of the attack range of that unit and can make more informed tactical decisions as a result.

Here are a few screenshots of the display range feature in use. You'll notice that the Assassina in the final screenshot has a very small circle of power surrounding her; this is because she is a melee unit and has a range of only 100.

Range of the Longshot in stride stance.


Range of the Tempest.


Range of the Assassina:


Pretty sweet. However, the code for this feature was extremely buggy and far more difficult than it should have been to get to work properly.

Take a look at the final code for display range. It takes place over two triggers.

actionscript code:
Ping Range 1
Events
Player - Player 1 (Red) types a chat message containing range as An exact match
Player - Player 2 (Blue) types a chat message containing range as An exact match
Conditions
RangeAsked Equal to False
Actions
Set RangeAsked = True
Unit Group - Pick every unit in (Units owned by (Triggering player) matching (((Matching unit) is selected by (Triggering player)) Equal to False)) and do (Actions)
Loop - Actions
Unit Group - Remove (Picked unit) from (Units currently selected by (Triggering player))
Unit Group - Pick every unit in (Units currently selected by (Triggering player)) and do (Actions)
Loop - Actions
Set SelectedUnitOrigin = (Position of (Picked unit))
Set SelectedUnit = (Picked unit)
Unit - Create 1 Circle of Power [custom] for Neutral Passive at SelectedUnitOrigin facing Default building facing degrees
Set RangeCircle = (Last created unit)
Animation - Change RangeCircle's size to ((Current acquisition range of SelectedUnit)%, (Current acquisition range of SelectedUnit)%, (Current acquisition range of SelectedUnit)%) of its original size
Wait 3.00 seconds
Unit - Kill RangeCircle
Set RangeAsked = False
Trigger - Turn on Ping Range 2 <gen>
Trigger - Turn off (This trigger)

actionscript code:
Ping Range 2
Events
Player - Player 1 (Red) types a chat message containing range as An exact match
Player - Player 2 (Blue) types a chat message containing range as An exact match
Conditions
RangeAsked Equal to False
Actions
Set RangeAsked = True
Unit Group - Pick every unit in (Units owned by (Triggering player) matching (((Matching unit) is selected by (Triggering player)) Equal to False)) and do (Actions)
Loop - Actions
Unit Group - Remove (Picked unit) from (Units currently selected by (Triggering player))
Unit - Kill RangeCircle
Unit Group - Pick every unit in (Units currently selected by (Triggering player)) and do (Actions)
Loop - Actions
Set SelectedUnitOrigin = (Position of (Picked unit))
Set SelectedUnit = (Picked unit)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(RangeCircle is dead) Equal to True
Then - Actions
Unit - Create 1 Circle of Power [custom] for Neutral Passive at SelectedUnitOrigin facing Default building facing degrees
Set RangeCircle = (Last created unit)
Animation - Change RangeCircle's size to ((Current acquisition range of SelectedUnit)%, (Current acquisition range of SelectedUnit)%, (Current acquisition range of SelectedUnit)%) of its original size
Else - Actions
Wait 3.00 seconds
Unit - Kill RangeCircle
Set RangeAsked = False

A bit complex for such a simple request, right? This is for two reasons.

First, I need to make absolutely sure that the RangeCircle is dead before a new one is created. I didn't want to create an array of RangeCircles or a group, just one at a time, so if two exist simultaneously, then one will never disappear because the variable would have been overwritten. This is why the trigger is split into two parts. If you read carefully, you'll see why.

The second reason this was difficult is because of Warcraft III's management of unit selection. To prevent all sorts of horrendous, game-breaking bugs, I have disabled multiple unit selection and drag selection (almost. I have yet to figure out how to disable shit-selecting multiple units). Ordering multiple units simultaneously completely breaks my AP triggers, and therefore, I disallow players from doing so. That said, Warcraft III is a bit slow to recognize which unit you have selected, at least in reference to the clause "units currently selected." Therefore, I preempt Warcraft III I manually order the game to deselect all units as soon as the trigger is fired. When the action comes around to creating the circle of power for the currently selected unit, the game will recognize only one unit as presently selected.

You may notice that I also have a boolean variable that bookend each trigger and also is present as a condition. I want this trigger to run only once at any given moment, no duplicates. To prevent parallel firings of this trigger, I set a boolean to true when it begins, and to false when it ends. A condition for the trigger overall is that the boolean be false, hence, preventing multiple instances of the trigger at once.

I'm glad I could get this to work. Ease of access to tactical information is important for most any game, not to mention turn-based strategy games, and any way I can facilitate that then all the better.

Post comment Comments
Kerloc
Kerloc - - 34 comments

Hi,

dont use wait again - without wait you can merge the 2 triggers.
I suggest you to use a visual effect, the circle of power is very unprecise.

Use this:

For each (Integer A) from 1 to 360, do (Actions)
Loop - Actions

Unit - Create 1 range_marker for (Owner of (Picked unit)) at ((Position of (Picked unit)) offset by 'range' towards (Real((Integer A))) degrees) facing 0 degrees

Unit - Add a 3.00 second Standard expiration timer to (Last created unit)


-------------

The range marker is a unit with a visual effect as a model - it has locust, spell and damage immunity and no attack

Reply Good karma Bad karma+3 votes
Gryffin Author
Gryffin - - 7 comments

Kerloc, you're awesome. I've tried out your code, and it works perfectly, much better looking than before and more accurate to the unit's positioning. Right now, I've got Holy Light as the model, which does a good job of displaying the range, but is somewhat garish. I had never thought of the expiration timer, an excellent suggestion.

Reply Good karma+3 votes
Kerloc
Kerloc - - 34 comments

my code posted above is leaking location (memory leaks will slow down the computer if they aren't removed)

store the 'Position of picked unit' into a variable (range_Tmp_point for example) after that you can remove that leaking location with:

custom script: call RemoveLocation( udg_range_Tmp_point )

Reply Good karma Bad karma+2 votes
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: