The Vampyre - Targeting Trouble

After weeks of putting it off, I finally put my nose to the grindstone and finished my targeting system (for now) in The Vampyre.

This post is about my upcoming roguelike The Vampyre. Any images are of unfinished gameplay systems using art that isn't final and probably stolen from free sources. These posts are updates on the progress of programming a roguelike in Python using Pygame and libtcod. These posts never include code examples since I'm usually still figuring out whether I like how things work or not.

Important Gameplay Features

Any playable roguelike is going to handle having the player select targets in some way. The easiest thing to do, is simply convert the mouse location into some input by the player which is usable by the game (like selecting a target of a spell). The real problem arises when determining more than simple rules for target selection and communicating those rules to the player.

On a side note, an interesting 7DRL could include not allowing the player to control targeting. Either base the targeting off of rules, or make it random.

For my purposes, any (good) targeting system needs to handle complex rules and situations, communicate to the player what is (and isn’t) possible, and be relatively easy to implement. The rest of this post is basically going to cover how I attempt to meet those requirements.

Complex Rules and Situations

Roguelikes are complex, and I mean, extraordinarily complex. Often they incorporate any number of interactable game systems, and a targeting system needs to reflect that. I’ve broken down targeting into 3 crucial questions.

  1. Is the ability blocked by creatures?
  2. Is the ability blocked by walls?
  3. Does the ability have a range?

Some abilities may be affected by all of these or none, for example a ‘telepathic’ power may or may not have infinite range, or may be restricted to what the player sees. High level telepathic abilities could grant the player the ability to target any creature on the map, for example. Gameplay balance issues aside, it’s probably best to make a system that incorporates these possibilities sooner rather than later.

To accomplish this, I broke the process of tile selection into several pieces, the first piece is a function I’ve called map_find_line, which sounds stupid, but is literally what the function does. It takes in a starting position and an ending position, the player’s position and where the mouse pointer is, for example. libtcod’s line drawing tool kit is actually really well suited to this.

map_find_line uses the tool kit to interate though each address for the line and then stops when told to by any number of ‘rules’. For example, if the only rule is that the ability has a maximum range, map_find_line stops when the line reaches that length. It works for other rules too.

If the ability is blocked by creatures, but not walls, will stop interating when it hits a creature, but not when it hits a wall.

Whenever map_find_line stops, it returns a list of all the map addresses it went through, this list can later be used to draw out the result, or simply calculate the result of the ability. So far, I’ve found this type of system to be very flexible in letting me figure out where a shot should land if it can or can’t penetrate objects, etc.

Communicate Rules to the Player

This is a big one for me. Personally, I can’t stand it when I don’t know the targeting rules for abilities I’m using while I play. A tiles-based roguelike is a pretty unspecific abstraction of reality and things like physics, so not knowing if something is in the flight path of your arrow (for example) is one of those things that I think really breaks the experience for players.

On the arrow thing specifically, some games get around this quandry by simply letting most abilties pass through objects on their way to the intended target, but this cheapens it for me a little. If you play roguelikes because you like the tactical decision making involved, I think that removing of the elements of a battlefield (its layout) is problematic. Obviously, whether or not this is immersion breaking depends on the roguelike, and not every game needs to care about being 'tactical'. But, I digress.

There are several ways you can communicate with the player, the first way is by simply being explicit about targeting rules. “This ability pierces walls” tells the player they can do a certain thing, so hopefully they know they can do it. I’ve never really liked this method since it requires the player to remember off the top of their head what each ability does ( does fireball explode, or was that firebolt) when making a target selection.

The second method, and my personal go to, is to render out for the player what will happen when they use the ability during the target selection phase. Since map_find_line already calculates the abilities passage through the map, it’s pretty trivial to have a separate function that takes the list of addresses and draws the target line based on the same rules that map_find_line follows.

The following will show what I mean in action.

resultimage

By default the line is rendered out, and is blocked by walls and creatures. But I included an option to turn off the line (for things like a simple ‘look’ command), and can even change the line’s color on the fly. You can also pass in an ability radius, which will tell the function to draw the ‘explosion zone’ which defaults to red.

resultimage

You can see that this line is also blocked by creatures. By simply drawing out the results of the map_find_line function, I’m able to communicate to the player that targeting the monster directly will also put him into the blast radius. Something which may not be immediately obvious when dealing with various ranges and blast radii.

Be Easy to Use

This is one of those things you might have to just take my word for it, but by breaking the whole problem into smaller functions that work together, every instance of target selection is pretty trivial to call. The master function takes input from the player (along with the rule definitions like radius and max distance), gets the line calculation from map_find_line, and then passes the list of addresses to the drawing function which renders the result.

Thankfully, this means the single calling function can be used by just about any process that requires the player to select something on the map.

That’s all for today, thanks for reading!

Join My Newsletter

Sign up to receive weekly updates.

x