CTF Liveblog

Friday

It’s on Friday afternoon and time to start working on Capture the Flag game. After chatting with friends I have some vague title/theme ideas. In the sprites there’s this row of pieces that look like a broken crest:

crest pieces

I’m not really sure what they’re supposed to be, but they prompted some theme ideas that fit with teams playing CTF over and over on random maps. Maybe there was a great evil sealed away that has broken free. It was defeated, but its wild magic broke the world into ever-changing pieces. Which is not so bad as things go, but it would be nice to put things back together again. There’s a bit of a tragedy of the commons, though – whoever is the one to reassemble the seal to fix the world will have the power to shape it, so rival groups (families? clans? guilds?) are trying to take the pieces away from each other and remake the world to their design.

Yeah, kinda cheesy. Sue me, I played a lot of video games in the 90s, that does things to your brain. But it explains why people would be playing CTF, and points towards some kind of name about a shattered crest, or collecting the seal, or something.

Please do leave better suggestions in the comments. And leave this post open in a browser tab; I’ll be updating it and twitter over the next two and a half days as I make my game.

Set up a new Rails 4.0.0 project with Ruby 2, added pg, ActiveModel::Serializers, rspec, better-errors. Oh, and lunch. And tunes.

Stood up a Heroku app with backups, Mandrill, papertrail. Very slow initial deploy, hope that’s a one-off. Removed the secret_key_base from the initializer; why is that in source to get checked in by default?

Hm, a shame that the terrains in my tiles aren’t set up for Tiled. This map is going to be ugly. :)

Welcome to uglyworld. I’ve hand-created the first map.

Uglyworld

Map modeled. And I always forget I need the Heroku user env plugin because it is inexplicably not the default.

Game, player, and squad models. Maybe a little out of order here, and no serializers/controllers yet.

I’ve dropped in Crafty, it got a few recommendations and looks nice and small. Let’s see how painful the asset pipeline makes it to add this…

Added Haml and game creation. Going to be seeing uglyworld in a game shortly…

Hey look, it’s time to waste a half an hour debugging. Yes, it’s Asset Pipeline O’Clock.

Lots of plumbing – assets, CoffeeScript, passing json, initializing some game objects… but made the first render of the map with Crafty:

first render of map with crafty

First display of a unit on the map exposes some kind of scaling bug (which only took a few minutes to track down and fix; Tiled will export images that are the size of your current zoom, crazy as that sounds):

002 first render with unit

Spent the last two hours learning the ins and outs of Crafty’s click handling. The user clicks to select units and can click off onto nothing to deselect the current unit. In Crafty, this meant adding an invisible Entity the same size and shape as the Stage:

    @nothing = Crafty.e("2D, Canvas, Mouse")
      .attr({ z: 0, x: 0, y: 0, w: Crafty.stage.elem.offsetWidth, h: Crafty.stage.elem.offsetHeight, z: 0 })
      .bind 'Click', (e) ->
        console.log('nothing clicked', e, this)

And with that, it’s time to call it a night. Good sleep means good code. I’ve now touched all the major tools I’ll be using (which always means burning some time to learn some little particular) so I should be able to get a lot done tomorrow.

Saturday

I woke up and surfed a little, saw a Reddit thread for game design progress and posted in it. I was committing the sin of looking at the internet before I even really woke up, so I have a half-hour of morning chores, breakfast, etc. before I actually get started.

OK, took a lot longer than expected to get everything done, but I’m glad to keep up my daily routine. Time to add UI to selecting characters and doing A* to move them.

Speaking of taking longer than expected, I got movement cost calculating correctly. I meant to do this in more, smaller steps but there were just too many things tangled up together in terrain costs, looping over tiles, and calculating. But now I can click on a Unit to see its movement options and click off it to deselect the unit and hide the movement options.

movement costs correct

Decrementing movement points as a Unit moves around:

spending movement points

Oh right, the other major system I hadn’t touched was animation. But now I have Units taking the shortest path to where you click, walking around obstacles as needed. And then I can click again to move it some more. No screenshot for this one because I don’t want to figure out how to take a video of my desktop and then make an animated gif. But it works.

This went a lot slower than I’d hoped, and all to reinvent the basic movement I’ve seen in dozens of tactical games, which is kind of disheartening. But it’s a hell of a lot more progress than I’ve made on a tactical game, so I’m pretty happy. I’m bumping stances off the weekend to-do list (it’s fiddly). Tomorrow is fog of war (a variation on movement, actually), sending movement info up to the server to be synced between players (should be straightforward), playback of turn events (a little tricky), guard/tagging (uh… probably painful), and flag capturing (should be straightforward).

Which is to say, a lot of stuff. I’ll definitely have something where you can move characters around, but a game that one player can win or lose is unlikely. Hm. I wonder what’s on the calendar for next weekend…

Sunday

I’ve been up for an hour or so, converting the weird recursive code that animated movement (Crafty doesn’t have an animation queueing system, so I had to create callbacks that called tween() to walk to the next tile, and so on) into an AnimationQueue Crafty component that works by iteration (with per-step and completion callbacks). So now I’ve added that to my Unit entity and animation is just matter of converting the unit’s Route into an array of locations to move to, making it much clearer. This was optional for the moment but will be a huge help for playing back turns. Here’s that Queue, it is MIT/GPL dual-licensed like Crafty itself, if you’d like to use it:

# an interative animation queue for Crafty
Crafty.c "AnimationQueue",
  init: ->
    @requires("Tween")
    @step_done_callback = undefined

  # animations should be an array of hashes with the keys
  #   tween: a hash that gets passed to tween()
  #   frames: number of frames, passed to tween()
  #   callback: optional, a callback to run after this step completes
  # done is a callback to run when all steps are completed
  animate: (animations=[], done) ->
    @animation_queue ||= []
    Array::push.apply @animation_queue, animations

    # all done callbacks will fire, even if animate() is called multiple times
    # before finishing. Queueing a new animation from a callback results in
    # that callback executing immediately, because the queue is in the middle
    # of finishing. Use window.setInterval(function () {foo.animate(...)}, 0)
    # to queue after your callback is done.
    @done_callbacks ||= []
    @done_callbacks.push done if done

    # unbind any existing copies of our callback so that multiple calls to
    # animate() don't trigger multiple step endings
    @unbind('TweenEnd').bind 'TweenEnd', (property) =>
      # ignore TweenEnd for anything else
      return unless property == 'AnimationQueueStepDone'

      @step_done_callback() if @step_done_callback
      @step_done_callback = undefined

      if @animation_queue.length == 0
        done() for done in @done_callbacks
        @done_callbacks = []
        return

      # pop off next animation
      next = @animation_queue[0]
      @animation_queue = @animation_queue[1..]

      # get the next animation tweening
      next.tween['AnimationQueueStepDone'] = true
      @tween(next.tween, next.frames)
      @step_done_callback = next.callback

    # kick off TweenEnd callback loop
    @trigger('TweenEnd', 'AnimationQueueStepDone')

    this

It actually turned out to be really easy in Ubuntu to use gtk-recordmydesktop, ffmpeg, and gifsicle to make an animated gif of a guy moving. There’s terrible video compression artifacts (I’m not going to dick around with settings to fix that right now), but here it is:

animated movement

Oh right, FOV is not a variation on movement; it can’t turn corners. Thank you, Peter of 2009, for cleaning up and commenting the shadowcasting code in that roguelike you never finished. This is inverse shading (a little easier to debug) and boolean (see or don’t, no reduction over distance), but I saved myself a ton of time:

inverse fog with hard obstacles

Or not. I forgot this algorithm doesn’t walk tiles going out from the unit, so I can’t decrement terrain visibility costs. I’m going to eat and read raycasting algorithms. If there’s a simple algorithm that works I’ll implement it, but otherwise terrain visibility costs just got bumped off the weekend plans, and trees are going to be obscenely good for hiding in (because a unit can’t see past them, so in the trees visibility is the immediate ring of 8 trees).

Ouch. It took me a couple hours to evaluate other algorithms and then implement a raycasting one (and I lost 90m to roxterm deciding to eat any “i”s I typed, that was painful to track down). I knew this afternoon I should’ve punted on this, but I really, really wanted this mechanic and decided to go for quality over completion. Which partly defeats the point of trying to hack out a game in a weekend… I already knew I tend to doggedly pursue a problem until it’s dead rather than back off and re-evaluate. Tenacity helps make me a good debugger, but a worse developer when I’m failing to prioritize.

But still, I’m happy with this weekend. I made great strides on a game and exercised the dusty algorithms part of my brain. If I can get another free weekend, I can have a playable demo. :)

I’ll close with a couple pictures of how that fog turned out. Here it is with some artifacts: notice the tiles immediately southwest and southeast of the unit have a visibility score like the unit is looking through the tree rather than the grass. It’s arbitrary which their vision passes through, so I’d prefer to give them a little extra sight distance.

raycasting fog with artifacts

Here’s that artifact fixed:

correct vision costs

Here’s an experiment with fog shading. The unit can see into the trees they’re next to, but not into the farther trees, just as I wanted:

fog

If the unit is in the trees, they’ll have a little bit better vision out than someone looking in. A unit looking into the trees would pay the visibility cost for the trees this unit is standing on, but this unit doesn’t have to pay for the trees they’re standing on. Sneaky, sneaky:

fog hiding in trees

And I’ll end with a snap of the Trello board before I close it:

final trello board

Want more? I'm not as good at forgetting to update @pushcx on Twitter.