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.

Walking With A Ghost

Sometime recently a new trailer went up for the Space Hulk: Deathwing video game. It’s pretty good:

Some of the guys have been discussing whether or not the unexpected musical selection, Kadebostany’s Walking With A Ghost, works. Juxtaposing action games with slower or offbeat music has definitely been a thing since at least the very successful, memorable, and beautiful “Mad World” trailer for Gears of War:

Given that Space Hulk: Deathwing is a Games Workshop licensee, it’s tempting to quickly dismiss the latest trailer as simple mimicry given GW’s general ineptness and the low quality of many of the offerings from both it and its licensees. However, I think the game studio developers quite possibly put a lot of thought into that selection, and that it comes close to inspired.

Synchronization

First, note how the whole video is well keyed to the music. That’s not coincidental, and took more than just slapping the song over the video. As a counter-example, note how easy it would have been to just throw Mad World over the Gears trailer. Although I don’t actually believe little effort went into tailoring that video to that music, it could have. That song doesn’t have a ton of really distinctive audio shifts, peaks, or valleys, and the action on the video is pretty subdued. They would basically work together no matter what, with no effort to sync them up. It takes a fair bit of close watching to actually pick up a few subtle touch points:

  • When the lyrics go “Worn out places, worn out faces” as the clip pans from the rubble to the face of first the demolished angel statue and then has its first closeup of the face of the soldier (starting at ~9s);
  • The lyrics go “No tomorrow, no tomorrow” as the soldier runs into the dying light, the video fades out, and then comes back in on yet more endless rubble and running (starting at ~22s).
  • Of course the closing, with the “Mad world” chorus as the hero leaps from a minor potential confrontation with a vaguely humanoid enemy into a hopeless situation in a demonically lit setting against truly otherworldly aliens (~40s).

In contrast, Walking With A Ghost has a couple pace changes and definite valleys, all of which the Space Hulk trailer video is keyed to. Several are pretty overt:

  • ~40 seconds in when the music picks up and the video goes from a setting pan and ultra slow motion to live combat;
  • ~70 seconds in, the music slows down again as the video slows to show the Tyranids bringing in reinforcements;
  • ~75 seconds in, the music picks back up into a more flowing style with horns giving a feeling of a Spanish bullfight or Mexican swashbuckling scene while the action picks back up, having switched to swirling swords and close combat until seguing to end with a last stand on steps vaguely reminiscent of a cathedral.

All in all, the video seems to have been certainly choreographed to the music and at a minimum the latter not just cynically slapped on in a cheap ploy to stand out or evoke some gravitas and intellectual veneer.

Lyrics

A few connections though are more subtle and tied to the lyrics, e.g.:

  • ~55 seconds in, “Round and round and round” as flames and the camera swirl around a Terminator;
  • ~64 seconds in, “I’m not creatin’ my flow with my ego” as the camera pans over a pretty solid symbol of ego, a powersword inscribed with “I am wrath. I am steel. I am the mercy of angels.”

With that, it’s worth looking at the full lyrics of the song:

I’m just walking with a ghost
And he’s walking by my side
My soul is dancing on my cheek
I don’t know where the exit is

Every day is still the same
And I don’t know what to do
I’m carrying my tears in a plastic bag
And it’s the only thing I got from you

I have short hair
And I’m faced with a few complications
So, so if you care
Try to analyse the situation
You know, man
As the leaves fall on the ground
My soul is goin’
Round and round and round

So please, do it well
Just break the spell
Why don’t you do it right?
I don’t want another fight
I’m not creatin’
My flow with my ego
I’m taking off my hood
And I’m entering deeply in the wood
You know, man

Bugs are my only food
And it puts me in a strange mood
I ain’t giving you my heart
On a silver plate
Why couldn’t we be just mates?
Oh no, never come back to me
Oh no, never come back to me

I wish i could be a child, write me another dance, another chance, another romance
we could just be friends

I wish i could be a child, write me another dance, another chance, another romance
it could be the end

Accepting a fair dose of lyricism, that actually captures and references an awful lot about a Space Marine’s life, especially those fighting in a space hulk.

It starts with the opening lines:

I’m just walking with a ghost
And he’s walking by my side

What does a Space Marine do but trudge along with the Emperor’s will aiding his every move and his spirit foremost in the Marine’s mind?

Bouncing around a bit, there’s a middle stanza about the Marines’ monastic lifestyle and how their dedication to the Emperor and setting aside of themselves is what gives them their strength as they wade into combat against tremendous odds in dark, unknown enemy territory:

I’m not creatin’
My flow with my ego
I’m taking off my hood
And I’m entering deeply in the wood

Toward the end, it turns out we’re specifically hearing from a Space Marine fighting, probably until he dies, with Tyranids—the bugs of this particular franchise:

Bugs are my only food
And it puts me in a strange mood
I ain’t giving you my heart
On a silver plate
Why couldn’t we be just mates?
Oh no, never come back to me
Oh no, never come back to me

Most dramatically though, most of the song captures what I take as an overarching theme of the Space Marines:

I don’t know where the exit is

Every day is still the same
And I don’t know what to do

…

My soul is goin’
Round and round and round

So please, do it well
Just break the spell
Why don’t you do it right?
I don’t want another fight

…

I wish i could be a child, write me another dance, another chance, another romance
we could just be friends

These lines are all about one of the central pathos of the Space Marines. They’re in many ways the pinnacle of humanity. It’s not generally readily apparent from the actual games, but these guys are all ostensibly artists and scholars without par. They’re brilliant, talented, dedicated, create great works while traveling between battles, and have built and maintain the greatest, most human, most advanced worlds and societies remaining in the Imperium of Man. But by and large, they spend all their time locked in a deathgrip with the universe, dying easily and frequently in endless combat in order to defend humanity, often times from itself.

That’s a key part of the appeal of the faction to me: Those Marines that haven’t become completely inured and numbed to that role must realize the deep tragedy of their lives. The brightest and most foresighted must futilely long for a way out of all the fighting, an impossible cessation of endless, all-consuming war. Though their personal specifics are lost to them through their conditioning (presumably—the fluff’s a bit contradictory), some of them must yearn to go back before they were inducted, when they were still children leading simple lives, unconcerned about the fate of humanity and not facing constant pain and death.

So, I would argue that even besides the choreography, the lyrics of the song actually make it an appropriate choice as well.

Conclusion

I figure there’s half a chance the producers of the trailer really did just throw on a cool song they heard that would maybe help their trailer stand out. That said, there’s little denying the whole video has been crafted around that selection. The choreography is too tightly synced, to music that decidedly requires it. At a minimum they weren’t just crassly thrown together with no effort.

At a maximum, the piece was actually chosen with some thought as to the lyrics and what’s going on in this fictional universe. Obviously all of the textual reading above is bullshit and nonsense. But it’s at least as valid as any literary interpretation out there, and I could believe it occurred to a developer if they happened to be actual fans and devotees of the 40k universe.

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.