Shift on Solypsus 9

bannerYesterday our Solypsus 9 campaign continued. Tyranids settled down to munch on biomass, Orks brawled with anybody they could, and some Necrons decided to help out the Space Marines and Guard, while Chaos finally descended in force. Ten players made it through the snowy streets:

  • Forces of Order: Swords of Dorn+Legion of the Damned, fascist Guardsmen, White Scars, Space Marines, and Necrons
  • Spoiler Horde: The Flying Swarm, Ork Mob, Death Guard, Traitor Marines, Rotted Daemons

More photos are in the gallery. Full results and missions are on the event webpage. Core campaign mechanics are in the Solypsus 9 outline.

Chaos Marines ride to the assault on Solypsus 9!

Chaos Marines ride to the assault on Solypsus 9!

Mechanics

The core of the campaign is based around a map of the sole settlement on Solypsus 9. The alliances are working to control one of the major installations on the planet—the Laboratory, Hab Blocks, or Mine—as well as the supporting sites—the Starport, Generator, or Comms Tower. Each round the alliances simultaneously place secret orders on their active armies. Those orders are then revealed and the alliances alternate assigning a player and a target to a chosen order, with the other alliance(s) responding with a defending player. Cumulative mission results then determine the outcome and control of each territory. Full details are in the draft writeup. The system continues to seem to be working really well, especially as more players get a better handle on it.

commands

map

Drop on Solypsus 9

In last month’s fighting, a massive Tyranid horde descended on Solypsus 9 with new Orkoid hybrid morphs. Caught off guard, the Imperium was forced to drop assault their own planet to shore up defenses as fast as possible. Lead by the Swords of Dorn, they successfully established positions in the Laboratory, Mine, Comms Tower, and Starport. The invaders though occupied the Hab Blocks and began feasting.

Battle

This stage of the campaign started off focused on the mine, and then swung onto the Laboratory.

  • Round 1: The Tyranid settled down in the Hab Blocks to gorge on biomass after their long journey through the stars, while the Orks mob continued to roll throughout the settlement in their jalopy battlewagons. Finally awoken to the major conflict on Solypsus 9, Chaos Daemons and traitors descended from orbit against the Imperial forces. Death Guard and their cultists attacked the Astra Militarum guarding the Mine and won the tactical battle but were rebuffed by supporting forces. Traitor Marines dropped on the undefended Generator and were surprised but not deterred to find Necrons phasing onto Solypsus 9. Rotted daemons meanwhile were intercepted by a collection of Space Marines. Swords of Dorn engaged the Ork Mob at the center of the colony but could not overcome their sheer mass. Finally sated, a Tyranid Harridan led a sortie of flying beasts that cut through the White Scars defenders and entered the Laboratory complex.
  • Round 2: The Swords of Dorn rushed to retake the Laboratory, crushing a Chaos beast pack on the way. Orks, Death Guard, and Tyranid made a combined assault on the Mine, with Space Marines, Imperial Guard, and White Scars defenders suffering a major defeat. Necrons meanwhile successfully took the central pathway of the settlement from the rotted daemons.
  • Round 3: Death Guard could not repel an onslaught on the Generator from the Swords of Dorn. Traitor Marines again assaulted the Laboratory and almost lost to White Scars defenders, but the Ork Mob arrived hot on the heels of Necrons moving on the facility and rolled over the entire complex. Daemons guarding the Mine were dispelled by the exhortations of Astra Militarum Priests and Psykers even as their Space Marines outriders were mulched by the Tyranid swarm.
Swords of Dorn advance through the slum shacks surrounding the Hab Blocks.

Swords of Dorn advance through the slum shacks surrounding the Hab Blocks.

Orks and White Scars engage in a frenzy of speed and close combat.

Orks and White Scars engage in a frenzy of speed and close combat.

Imperial Guard roustabouts take up arms under the banner of psykers and priests.

Imperial Guard roustabouts take up arms under the banner of psykers and priests.

Outcome

Although the individual rounds were closely fought, Order took the day with 179 victory points to 140 largely due to successfully claiming a number of covert missions in the second round. Spoilers advanced their position slightly, maintaining control of the Hab Blocks and gaining the Laboratory. Order meanwhile entrenched their position on each of the other installations. Neither side though claimed their chosen primary objective for the day, leaving Order slightly ahead on campaign points earned, 5 to 4.

Again, more photos are in the gallery, and full results on the event webpage.

Final campaign positioning at the end of the second event.

Final campaign positioning at the end of the second event.

Design Notes

One of the challenges in the campaign structure is that we don’t have a fixed group size or composition. The underlying mechanics seem to support this well, but some narrative sleight-of-hand is required. In this case, a bunch of last month’s Spoilers couldn’t make it but some new Chaos players did. The number of players though came just short of warranting a third faction. Absent that, I erred toward simplicity and narrative continuity and kept the factions as Order and Spoilers.

The highly modified and fairly original Maelstrom mission seemed to work well. I did though notice a preference to use the standard cards to draw and track objective numbers, rather than marking our sheet, then consulting our sheet to read the actual text in play. That’s not a really problem, but clearly people prefer the physicality of cards over checkboxes. There are logistical hassles though to rolling our own cards that I’m not sure how to work around, and I can’t see just switching to using the standard deck (more on that its many flaws in a later post).

A rotted Plague Drone roams the battlefield.

A rotted Plague Drone roams the battlefield.

On a similar logistical note, almost every table played the last round decidedly slow. I would guess some part of it was the extra complexity of the Maelstrom bookkeeping, though everybody I polled seemed to think it was mostly just being tired. In general I continue to think 1850 points is just a touch high for 2.5 hours for a group mostly on the more casual end of the spectrum.

For this event I started introducing a few narrative aspects to the actual gameplay itself. One of the rounds included Apparations, little entities scattering around scaring everybody. I forgot though one of my cardinal rules of game design for 40k: If a narrative mechanic doesn’t clearly benefit somebody or directly determine the game outcome, most people will forget or purposefully ignore it. I do exactly the same thing with Mysterious Objectives, for example. To avoid unbalancing games I gave people the option to simply ignore this—Patrick, with his low-leadership Orks, in particular looked terrified. Several did, but the rest mostly forgot about them anyway. Reinforcing that design lemma though, people continue to be pretty excited about the covert missions giving to the trailing alliance. So, for next month the narrative aspects will be reworked into the covert missions, which will also provide some more inspiration and direction for the latter.

Stormtroopers search the burning crash site of their downed flyer for fellow survivors.

Stormtroopers search the burning crash site of their downed flyer for fellow survivors.

Onward!

Next month is going to be a doubles format, players competing in teams of two with each fielding 1000 points. Doubles are by far the best attended 40k events at Redcap’s, and attendance has been a bit higher for our events recently than the previous trend, so I’m hopeful for really good turnout. Should be a blast!

The Swords of Dorn stand firm at the center of Order's campaign to defend Solypsus 9.

The Swords of Dorn stand firm at the center of Order’s campaign to defend Solypsus 9.

Asteroids: Drawing Objects

Recently I’ve started mentoring a local high school student a bit on implementing a video game, and this is a technical note toward that.

Drawing basic shapes out of polygons to represent game objects is straightforward and requires just a bit of trigonometry, outlined here.

Shapes

The core idea is that the polygon is located around the object’s current position. So, a standard looking Asteroids ship might be defined as four points about the origin as in this diagram:

Four coordinates defining a fairly standard Asteroids player ship.

Four coordinates defining a fairly standard Asteroids player ship.

The polygon is captured by a list (array) of points in order around the shape. Caveat other restrictions in the game’s code, it doesn’t really matter if they’re clockwise or counter-clockwise. In this case the ship is defined as follows, proceeding counter-clockwise:

  1. (0, 24)
  2. (-18, -24)
  3. (0, -18)
  4. (18, -24)

One cute trick that’s often done in Asteroids is to randomly generate the polygons for the asteroids themselves. The points list needs to be in order though or else the lines will overlap and the shape look funny. There are several ways to do this, but one is to go around in a circle, picking a random angle within that arc of the circle, picking a random distance from the origin for that tip of the rock, and computing the x & y value for that polygon point using that angle and distance.

Drawing

In most polygon graphics APIs, drawing starts by starting a new shape if necessary (i.e., newpath) and moving the cursor to the first point in the list (i.e., moveto). It then loops over each of the other points and draws a line from the previous position to the current point (i.e., lineto). The path is then either closed by drawing a line to the first point in the list from the last, or using a specific function to close the path if the API has one (i.e., closepath).

Rotation and Placement

Of course, in the game the object typically has to rotate and move. Rotation is simple because we’re taking the object’s current position as the origin of the polygon representing it. Each point to draw just needs to be calculated through the basic rotation formula:

x' = (x * cos(angle)) - (y * sin(angle))
y' = (x * sine(angle)) + (y * sin(angle))

In these formulas, x and y are the current point in the polygon list while x' and y' are the actual points to draw. The direction the object is currently rotated to, i.e.,which way the player is facing, is in angle.

This will draw the polygon around the origin, but of course the object is actually somewhere else on screen. This is a simple translation, effectively moving the polygon’s origin to the object’s actual position. In other words, just adding the object’s x and y coordinates to the point to draw:

x' = x' + objectx
y' = y' + objecty

Minor Complications

Although the ship above has somewhat naturally been modeled facing up, angle 0 in trigonometry is actually facing to the right. So, the polygon should instead be modeled with its natural direction facing that way.

Adding just a small detail, essentially all modern computer displays and most software use a slightly different coordinate system from what’s typically used in mathematics: The origin is at the top left of the screen, and the y axis increases going down the screen, not up. Note that this means the 90 degree angle is actually facing down and 270 degrees points straight up.

A wide variety of ways to work with these facts can be applied, but the easiest is just to model the polygon facing to the right and to keep that coordinate scheme in mind. So, the example polygon above would actually be modeled as follows:

The ship actually facing angle 0.

The ship actually facing angle 0.

The counter-clockwise ordering of points would then be:

  1. (24, 0)
  2. (-24, -18)
  3. (-18, 0)
  4. (-24, 18)

Another small detail to keep in mind is that nearly all trigonometry functions in software libraries are based on radians rather than degrees, though most people work more easily in the latter. Converting between the two just requires a simple formula based on the identity relationship between them:

radians = pi * degrees / 180

Code

A simple demonstration of this is in the box below. Pressing the left and right arrow keys make the ship rotate, and it’s being drawn in the center of the screen rather than the origin (you may have to click on the box first to focus the keyboard on it):

The code snippets for this below are in Javascript, but should be easily applicable to most platforms.

The polygon for the ship has been defined as follows:

var playershape = [
  { x: 24,  y: 0},
  { x: -24, y: -18},
  { x: -18, y: 0},
  { x: -24, y: 18},
]

It’s just an array of points, each a Javascript object with an x and y value. There is also a player object that has its own x, y, and angle, representing the player’s position and orientation on screen.

A couple trigonometry helper functions are defined, to convert degrees to radians, and to rotate x and y values:

function degtorad(angle) {
  return 3.1415926 * angle / 180;
}

function rotx(x,y,angle) {
  return (x*Math.cos(angle)) - (y*Math.sin(angle));
}

function roty(x,y,angle) {
  return (x*Math.sin(angle)) + (y*Math.cos(angle));
}

Note that each of the x and y rotations take as input x, y, and angle, because the rotation formula requires each of those values.

As discussed above, the ship is drawn by starting a path at the first point of the polygon, looping through each other point, and then closing it off. At each step the point to draw is rotated about the ship’s position as the origin, and then translated to the ship’s actual position on screen. This function captures that, and is called by the main drawing routine each time the ship needs to be displayed:

function drawplayer() {
  var x, y;  // These will be the point to draw.
  var i = 0;  // i is the current polygon point we're using

  ctx.beginPath(); // ctx is the graphics drawing context in this Javascript program.

  // Calculate the actual draw point by rotating and then translating.
  x = rotx(playershape[i].x, playershape[i].y, player.angle) + player.x;
  y = roty(playershape[i].x, playershape[i].y, player.angle) + player.y;
  ctx.moveTo(x, y); // Start the polygon at this point.

  // Loop through the other points---note that this therefore begins at point 1, not 0!
  for (i = 1; i < playershape.length; i++) {
    x = rotx(playershape[i].x, playershape[i].y, player.angle) + player.x;
    y = roty(playershape[i].x, playershape[i].y, player.angle) + player.y;
    ctx.lineTo(x, y); // Extend the path from the previous point to this new one.
  }
  ctx.closePath(); // Close the path by adding a line back to the start.
  ctx.stroke(); // Draw the path.
}

To initialize the player, its position is set to the middle of the screen and its orientation set as facing straight up:

  player.x = canvas.width / 2;
  player.y = canvas.height / 2;
  player.angle = degtorad(270);

Each time the left or right key is pressed, the ship’s angle is updated like this.

    player.angle -= degtorad(10); // Decrease the angle by 10 degrees, making the
                                  // conversion to radians first before subtracting
                                  // from the current angle.
    while (player.angle < 0) {        // This actually isn't necessary, but just makes sure
      player.angle += 3.1415926 * 2;  // the player's angle is always between 0 and 2*pi.
    }                                 // The drawing routines and other logic will all
                                      // handle that fine, but it can make things easier for
                                      // the programmer in writing other parts of the code.

Conclusion

Those are the basic elements in drawing a simple game like Asteroids. The next complication is having an array or arrays of game objects. That’s necessary to capture all of them that might appear on screen, namely the rocks and bullets. A drawing function like that above then needs to be applied to each game object in the array(s), rather than being just hardcoded to a single game object instance like this example is to the player’s ship.

Drop on Solypsus 9

banner

Our Solypsus 9 campaign kicked off yesterday. A massive horde of Tyranid including some Orkoid biomorphs descended on the planet, with Space Marines and Imperial Guard arriving just in time to aid the colony.

Eight players braved a small snowstorm, one even coming up from Virginia:

  • Forces of Order: Swords of Dorn+Legion of the Damned, Kingbreakers, Blood Angels+Sentinels of Terra, and Armoured Company
  • Spoiler Horde: Living Artillery, Endless Swarm, Spore Cloud, and Ork Mob

More photos are in the day’s Flickr gallery. Results and missions are on the event webpage. Core campaign mechanics are in the Solypsus 9 draft.

Kingbreakers rush headfirst into the oncoming wave of Hive Tyrants.

Kingbreakers rush headfirst into the oncoming wave of Hive Tyrants.

Mechanics

The campaign mechanics seemed to work out really well in their debut. There are a lot of details, especially to accommodate varying group size and composition. The basic idea though is that each alliance has a set of armies abstractly representing the major thrusts and conflicts of the campaign. Both teams secretly simultaneously place commands on those armies to Attack, Support, or Defend. They then alternate picking one of their Attacks and nominating an attacking player and target territory, with the opposing side(s) responding with a defending player. Results and control of the targeted territories are based on victory points earned there that round. Support and Defend commands aid in those contests, respectively contributing +5 to all adjacent territories and +10 to the territory itself assuming they aren’t wiped out or blocked.

The missions themselves followed the basic pattern we’ve been using: Each is worth up to 20 victory points, with 9 points available for a given primary objective based on markers or kill points, 6 points for a secondary objective chosen by each player from a list with options such as assassinating characters or controlling terrain pieces, and up to 5 points for several tertiary objectives such as claiming first blood or tagging superheavy vehicles.

Battle commands going into round 3.

Battle commands going into round 3.

Battle

Action in the first stretch of the campaign was concentrated around the Laboratory and the open ground at the center of the colony.

Round 1: The Swords of Dorn dropped successfully on the Laboratory, beating out the Ork Mob harrying them. Blood Angels repulsed the Endless Swarm’s attack on the Generator. Both forces met on the colony’s central ground en masse with the Kingbreakers barely countering an advance by the Living Artillery but the Spore Cloud overwhelming the Armoured Company to take the territory.

Round 2: The Armoured Company was kept on the retreat by the Ork Mob, losing the Hab Blocks to the invaders. A counter-attack by the Living Artillery on the Laboratory was blocked by the Blood Angels. Kingbreakers tried to disrupt the swarm’s plans at the colony’s center but could not dislodge the Spore Cloud. At the Comms Tower the Swords of Dorn boltered away the Endless Swarm.

Round 3: Swords of Dorn and Blood Angels both assaulted the Mine, with the Legion of the Damned tipping the odds against the Spore Cloud and Ork Mob.

The Swords of Dorn make landing on Solypsus 9.

The Swords of Dorn make landing on Solypsus 9.

The Ork Mob surrounds the Blood Angels' landing site.

The Ork Mob surrounds the Blood Angels’ landing site.

While the Armoured Company as well is mobbed by the Orks.

While the Armoured Company as well is mobbed by the Orks.

But the Swords of Dorn stand strong throughout.

But the Swords of Dorn stand strong throughout.

Outcome

The Forces of Order squeaked out a tactical win, with 123 victory points to the Spoilers’ 110. They also claimed a lead in campaign points for controlling installations at the end of the session, 5 to 1. Most of the colony was successfully reinforced by the Imperium, with Order armies establishing positions at the Laboratory, Mine, Starport, and Communication Tower. The Spoilers though clawed their way into the Hab Blocks and gouged out a safe beachhead at a central position among the settlement’s installations.

Alex won the standings for Order and overall with three straight wins and 91 points. Patrick took the Spoilers’ lead with 70 points. Best Painted was also voted for Alex for his gorgeous Swords of Dorn and Legion of the Damned, with Michael P’s Tyranid big bugs in second then bumping up to take the prize. Michael also continued Jason’s tradition of his shirt matching his army, something we should all aspire to.

Status after the end of the first campaign session.

Status after the end of the first campaign session.

Kingbreakers

kingbreakers-iconWe had an odd number of players otherwise, so I wound up joining in the action. The Emperor’s greatest soldiers had a so-so day. As is typical, my all-around list didn’t have a clear best opponent so it was repeatedly put forward as an attacker for the defenders to react against with their preferred army.

In the first two rounds the Kingbreakers fought Justin and Michael’s big bugs. Unfortunately both had a strong force of wing-equipped Hive Tyrants. Typically I try to muddle through ignoring flyers and flying monstrous creatures. That’s been working out ok against Daemons and Imperial armies because the former have to land to assault and the latter typically don’t do enough damage. These flyrant flotillas though were well equipped to rip up my army, particularly the Knight. I’d taken the latter despite our Victory Through Attrition tertiary objective making it a substantial handicap, yielding a victory point for each hull point lost. I knew that’d be a problem but it’s just a cool model, and I wanted to see how much of a disadvantage it’d be. Turns out it’s a big one! Either way though these FMCs would have been a problem. Unfortunately I did not bring my usual bunker, which would have been helpful here to safely squat on a home objective.

In the final round two of the Spoilers had to leave so Lovell and I fought it out. This was an old school-feeling battle, much like facing 5th edition IG. His Armoured Company literally had no infantry models, and nothing but Leman Russ chassis except for a Sentinel and an Avenger flyer. This was probably the army I was best equipped to fight, due to lots of re-rolling melta. It was still an awful lot of tanks to go through though. Kingbreakers eventually won out in the kill points mission, but a lot of the killing was done by just a few units. Much of Lovell’s army that could really threaten the Knight was targeted and wrecked early, giving the Knight fairly free rein. Captain Angholan also did far more than his share, ruining a number of opponents with his Vorpal Blade (S6). Meanwhile many Tacticals and others stood around literally hoping Lovell would forget about them with his battle cannons.

Kingbreakers scour the ruins for Tyranid monsters, finding them all too often.

Kingbreakers scour the ruins for Tyranid monsters, finding them all too often.

Kingbreakers Ghosts snipe away at the brain bugs from the rooftops.

Kingbreakers Ghosts snipe away at the brain bugs from the rooftops.

A pitched battle on a critical objective.

A pitched battle on a critical objective.

The Battle Continues

This was definitely a busy day on my end, trying to play a fairly sizable army while also managing all the campaign and tournament logistics. It worked out to be another great day though. I was particularly pleased that the campaign mechanics seemed to work well, as they’re essentially an entire small boardgame unto themselves. The teams seemed to be making a number of real decisions about how to proceed on the map, and there was a definite evolution of the conflict across the board. I’m looking forward to how that progresses in the future, as well as other surprises to be thrown into the mix.

Again, photos are up in the day’s Flickr gallery, results and missions on the event webpage, and campaign mechanics in the Solypsus 9 outline. See y’all in February!

The Imperial Knight Greenheart walks among the horde.

The Imperial Knight Greenheart walks among the horde.