project-image

Lancer Tactics

Created by Olive

Be gay // do giant robot crimes. A mecha tactics game adapted from the Lancer TTRPG (under its third-party license). The game is NOW AVAILABLE on itch.io!

Latest Updates from Our Project:

Portrait maker, dialogue bubbles, working towards a vertical slice
over 2 years ago – Fri, Nov 10, 2023 at 06:17:50 PM

Update day! Have some game dev vignettes!

Rewards update

But first a word for our sponsors aka you. Thanks to everyone who filled out their survey in time for our big shipping push. There's been a steady trickle of like a dozen packages per week to ship since then (for various reasons -- late survey, mail bounced, a new preorder) and the whole task of putting a batch together, verifying all these by-definition special cases, and getting them in the mail takes like half a day on my own. I'm looking at this pipeline of like 200 unresponded surveys in fear that this clerical work is going to continue forever.

So if you haven't filled it out, please do so now because I'm probably gonna start doing the batches way less frequently!

Related, the charge should have shown up as "WICK.WORKS" on your bank statement. A few folks (understandably) didn't recognize it and reported it to their banks; I'm telling you what to expect now because disputes set off a whole communication process Stripe wants me to go through and they're a gigantic pain.  😫

Vertical Slice

I've been getting antsy that we're, what, seven months out from the end of the campaign and we only have something that looks vaguely like a game. I think it was a good idea to start out by building out the underlying engine and tools, but it's time start to pulling it together into a form you can play. I spent the last week polishing the gameplay flow so there's a back-and-forth between you and your opponents:

Work we've done on that front:

  • Automatic camera movement and zooming as units use abilities. This doesn't sound like a lot but it's one of those little pinches of polish that goes a loooong way.
  • Giving all attacks/abilities default effects.
  • Being able to deploy lancers into start zones.
  • Giving sitreps enough triggers and hooks into the battlefield to know when their end condition is reached.
  • Adding a "mission complete" screen when the sitreps' end conditions are reached.
  • AI that can plan where it wants to be beyond its immediate vicinity. The berserker in the gif above runs after attacking off because it wants to occupy a nearby command zone.

FWIW, here's the current engine graph. It's grown a fair amount from last time, with the largest addition being missions and the editor thereof on the right side.

Portrait Maker!!

I've never made any like a character creator before, so it was fun puzzling this out! We ended up making a standalone portrait maker that directly pulls images from a specified folder and imports them as options in the appropriate category. We've only just started adding assets to it, but it's so exciting to see them all the layers come together into a character.

Here you can see me recoloring, adjusting, and adding eyes and it automatically updating when I switch back to the program:


We also had to figure out how to handle letting the player recolor certain parts of each layer. The answer for this kind of fancy graphic stuff is almost always 🌈~shader magic~🌈 and we found a recoloring-sprites one available for godot here. With that as our starting point, we adjusted it to be able to handle multiple channels of incoming color, allowing us to specify where to put up to three incoming colors to an asset.

Here you see the base channel (red) being applied to the entire area of hair. The yellow and magenta areas in the starting asset tell the shader to put the highlights and root colors there, if present, but fall back to red if they're not.

We experimented with blending (e.g. mixing the underlying red with a chosen blue highlight) but didn't find an immediate use for it that couldn't be achieved with just another color palette. We're going to keep that in our back pocket in case we want to do something fancy in the future.

In case you're curious, here's the engine graph for this system.

Dialogue

Visual novels deserve more respect. Managing that much text is not easy.

Background: it's good practice to keep your text content separate from your code in case you ever localize it to other languages, for the reasons listed here. But even if we don't end up translating LT, keeping the list of strings in a unified file makes reviewing and editing that text much easier than if those string were scattered around in a hundred different files.

So for LT, we've been putting all of our text — everything from menu options to ability descriptions — into a google sheet and have a utility that downloads and compiles it into a file that Godot can consume. Every string is associated with a key, so when we want to show some text we just ask Godot to give us the string for that key. The spreadsheet looks something like this:

However, this system works a lot better for static text that is true for one-off mission-based text. We don't want to have to write dialogue in a spreadsheet (much less assign each line its own unique key). We want to write it in-context in the mission editor here.

I'll spare you the gory details, but juggling where that text is saved, how unique keys are generated for each line, and compiling those chunks into the godot localization system took some figuring out and I came out with an appreciation for visual novels and other dialogue-heavy games to keep it together.

Animations

Previously, I said that we'd flesh out the animation system at a later point. That point was reached as part of the aforementioned vertical slice work. I put together a little preview utility to aid in making these effects.

Stuff that went into this:

  • Gave effects better control of their "wind up" periods and went to apply damage. You can see that above in the two melee attacks where the attacker pulls back before the hit effect is played.
  • Flashing enemies on being hit. This is usually easily done with a shader, but we are already using a shader for unit sprites (allowing the pixel art to resize without looking like garbage) so I had to actually make a second white copy of the sprite and fade it in and out on top of the original.
  • Being able to mix & match different attacks and hit effects.
  • Particle systems! The bullet hits above are defined by a particle emitter. I've only worked in custom engines before so having these ready to go in-engine is a very exciting area for me to explore.

Whew. I started this thinking there wasn't much to talk about, but the bullet points and gifs add up. As always, I hope you enjoyed getting this little look into the dev process. :)

Physical rewards are on their way!
over 2 years ago – Tue, Sep 26, 2023 at 09:14:22 PM

Headline says it all! Carpenter flew up last week & we gathered up some friends and had a packing party for the 2000-odd letters and parcels we had to ship out. I'm so happy that we were able deliver these on-schedule. We're doing great.

Notes:

Coin tiers ($96+): you should have gotten email with your tracking number.

Sticker tiers ($36 tier): I think tracking emails might have also been emailed out for Veterans  but with an invalid "n/a" as the number; we don't get tracking numbers for small postcard-sized envelopes, but they're on their way to you nonetheless.

Grunts ($10 tier): It ended up being easier for us to just send you all the stickers than individually parse out which one you chose. Enjoy the extras, merry christmas.

Late-comers: There's a handful of people who filled out the survey just too late for the lock-address cutoff. I'm going to start batching these and sending them out (hopefully) weekly until they stop trickling in.* If you filled out your survey just last week and didn't get a tracking email, this may be why. Message me if you're unsure.

Finally, if your package shows up missing anything or it's been like a month and you still don't have it, please message me so we can replace it! Although we did our best, percentage-wise we're bound to have missed a few & some are bound to not make it. I have a healthy buffer of replacements waiting, so just let me know!

Here's a picture of everything you should get, with/without a coin and with a variable number of stickers depending on your tier. As previously mentioned, I re-printed the IPS-N stickers because I picked the wrong material the first time around. Looking at them all in totality, the original glossy one would have been totally fine, but what's done is done and I need to get them off my hands so y'all get both.

* this is a joke. surveys will never stop trickling in. the record-holder from my last campaign filled out their survey six years after the campaign was over.

Progress gifs + rewards update
over 2 years ago – Mon, Sep 18, 2023 at 07:42:13 AM

Last call to fill out surveys!

We'll be packing up rewards this coming week and plan on shipping them by the end of the month! Addresses will be locked this week, so if you've moved or are reading this and somehow have still not filled out your backerkit survey (260 folks as of the time of writing) now is the time to fill it out. If you've lost or never saw your link, you can find it again here.

Generic progress report go!

OK I know I said this month would be slower because I'd be traveling and taking some time off and I swear I did (thought I had to cancel going to Ireland because of a Covid scare) but still found time to add... actually quite a lot of stuff. I mostly focused on adding features to the battle engine.

(it should go without saying: since we've been mostly focusing on the engine and UI design, all these graphics are still only a step past being placeholders)

Line of sight: I was able to re-use a lot of code from the demo, which itself borrowed this algorithm from the rougelike community. I spent a weekend trying to optimize it enough to use in gdscript instead of c#, but it ended up being too slow. To calculate for an entire map, the gdscript version took around ~25 seconds to run as opposed to the ~2 seconds in C#. This means that there's not going to be another web version of the game until Godot 4 is able to export C# code to html5 — which I've seen a few mentions that it's being worked on so a browser demo remains possibly possible eventually.

Pathfinding: Godot's built-in A* algo is not set up to have multiple possible movement costs to enter the same tile (such as the difference between climbing a cliff and stepping there from the same elevation) so I had to come up with a sorta-weird hack where I stacked a bunch of pathfinding nodes at the same tile; one for every possible movement cost that could enter that tile. It now is smart enough to use ramps and step up cliffs gradually using shorter climbable objects as stepping stones.

Attack animations: Although we haven't been focusing on the fancy graphical juice of the game, I still needed to set up the structures that would contain the animation data. Not much to say here besides we've done so and will probably flesh it out more once we start focusing on game feel.



Destructible terrain: terrain tiles now can transform into another specified tile when they are damaged (half health) or destroyed. I also added support for multi-tile terrain objects (like large rocks) that share a single health pool.

Enemy AI: I've added a draft of a controller that gets the enemy to run around and use their weapons. I've tried to make it a little more robust than the version in the demo where they just run at you until they're in range and then shoot, but of course this means there's now more room for strange behaviors. For instance, some of them are currently doing a weird little dance before and after they shoot.

Weapon profiles: it's a very strange experience playing Baldur's Gate 3 while working on a video game adaptation of a different TTRPG. I feel like I have a little peek behind the curtain having dealt with many of the same challenges inherent in doing so. One such challenge is how to wrangle the many, many actions available to a character and deal with those actions contextually becoming available and unavailable.

For example, in BG3, when you cast the Hex spell on someone, you become able to re-apply that Hex to a different enemy without spending another spell slot. One way to approach this would be to give some kind of discount on the Hex action so using it again doesn't cost a slot —  but what they've done instead is a far more flexible, modular, and stateless solution: they simply make an entirely separate second version of the action which does not cost a spell slot show up in your action bar. Having abilities be able to just in general hide and show various versions of  actions means you can use that same system for weapon profiles, charged and uncharged modes, and temporary bonus actions like  the re-application of hexes.

So we've copied this for LT. Here's the Drake's assault cannon, which  has two modes: regular and spinning. Using the "spin up" action hides  itself and the regular attack, then shows the spinning and "spin down"  actions. This is also a glimpse at the direction Mark has been going for  the player interface — we're taking some more pages out of BG3's book  and making an icon-and-tooltip-rich action bar instead of the demo's  nested radial menu system which was stylish but we think would end up  being too impractical.

Contextual actions: Sometimes you need to take an  action that comes from your environment instead of from your own  abilities. Since we've done a good job keeping the code for the actions stateless (they don't know anything about where they are or who they belong to  beyond what we tell them when they're activated) it was just a matter of  asking a unit's neighbors if they have any actions they'd like to  share. Here's an example of that in action: a restock drone giving an  adjacent ally an action to consume the drone.

Finally, I don't have a good gif for it, but I expanded the  Buff/ongoing status condition system to have the ability to run custom  snippets of code in reaction to events, just like legit Reactions do.  This will give us the ability to do strange one-off things like handle the Witch's Chain ability that monitors how far the unit it is applied  to moves and do nasty stuff when triggered.

Next month we're planning be shifting away from the battle engine  somewhat and working on more character sheet + pilot portrait maker  stuff.

Shipping in one month! Please update your address if necessary.
over 2 years ago – Wed, Aug 30, 2023 at 04:15:24 PM

This post is for backers only. Please visit Kickstarter.com and log in to read.

Event system, aka, how to handle nested "you've activated my trap card" triggers
over 2 years ago – Sun, Aug 06, 2023 at 05:43:04 PM

I'd like to take some time this month to talk about how we're  handling the extreme levels of actions / reactions / counter-reactions  in the Lancer system. To summarize the problem that we need to handle,  playing a round can sometimes go like this:

"OK, the Mirage Commander uses Quick March to let the Berserker boost—" The next 30 minutes: many reactions and triggered traps and objections.

Weapons and systems that respond to triggers and have a wide variety of effects, which can then themselves be reacted to, and so on. Most of the time these reactions can be resolved sometime after the thing they're reacting to, but sometimes they come first and can cancel the original event (e.g. Vlad's spike damage destroying its attacker before the attack resolves).

In tabletop play, there are some loose guidelines for how to handle the order of resolution, but a lot of the time they boil down to GM or player arbitration. In making a computer adaptation of the system, we gotta come up with a more mechanistic system that can handle the worst case scenarios. For reference, the comprehensive rules that dictate the similarly-reaction-nested game of Magic the Gathering weigh in just under 200 pages long. It's not an easy problem, is what I'm saying.

Let's start off with a simple case. Here's a Tokugawa and a Berserker duking it out.

The Toku uses a charged blade to hit the Berserker, which triggers its Aggression trait and it hits right back. Assuming everything hits, here's how the event tree would end up looking in Lancer Tactics:

Nice and linear. Easy peezy. We can resolve them all in a row, one after the other.

Let's throw something extra in — say that the damage on the Toku was enough to structure it, giving the player an opportunity to brace*. If they decide to brace, they'll lose most of their next turn. Now, before the damage event can resolve, we have to go and resolve this side-branch of deciding whether or not to brace.

The brace trigger happens because it's listening for damage events going off on the Toku and will check if those would be enough to structure it, so it inserts itself and pauses the damage event until the player makes a decision. If they decide that they don't feel like structuring today, it'll put temporary buff on the unit to get resistance to the next instance of damage. Once the damage event resumes, that buff will be factored into the calculation of how much damage actually gets through.

You might ask why we're breaking up the attack into so many steps (activate, attack, damage); the short answer is "to let various abilities detect and trigger from them individually", but there's some more insidious edge cases doing so hedges out. Let's now take a look at how the system handles multiple targets.

This Bombard is makeING an area-of-effect attack that'll catch multiple players. The attacks and damage resolves on both the Mourning Cloak and the Vlad before the MC's Expose Singularity kicks in and it teleports away.

Why is it important that we resolve all the attacks and damage before doing triggers like the teleport? Well, if the Bombard was closer to the players, like this:

The explosion would trigger the Vlad's Shrike Armor and do 1 damage to the Bombard before the attacks resolve. If that was enough to destroy the Bombard, the whole attack would be cancelled — including the one on the Mourning Cloak! We'd be in a bad spot if we'd already applied the damage and subsequent movement!

So instead, we queue up all the attacks to be resolved breadth-first and resolve any before-resolution triggers from any of them, like the Vlad's Shrike Armor.

In general, this system has events follow this pattern:

  • queue and then resolve all interrupting reactions that happen pre-block (Shrike Armor, Overwatch)
  • check to see if we're still valid, abort if not (did the Shrike Armor destroy us?)
  • do the thing (move/stabilize/boost)
  • queue any events that come from this event, to be resolved either depth-first (being Structured) or breadth-first (all the attacks within our AOE)
  • queue any followup reactions (Aggression, Expose Singularity)

* we're currently planning on changing the Brace trigger from any damage to only when you'd be structured — a slight modification to the rules, but one that'll prevent a "Brace?" prompt from showing up every time you get hit. That's 95% of the time when I've seen people brace, anyway.

Status Update

So how's the game looking? Since the last update, we've:

  • Worked our way about 70% of the way through the mission editor tasks, including trigger and dialogue editors. The biggest remaining tasks within it is the pilot editor and managing reinforcement groups. (not counting the actual tile painting, which is still being done in Tiled until things become more solid)
  • We've added the VERY basic skeleton of turn/round management and player movement; when we hit "playtest" in the editor, we can now actually tell our lil robots to move around.
  • And finally, we put in the full order for the coins! 1.5 inch diameter (to make the Lich more legible as a spooky guy) and now in silver. No more-detailed ETA on shipping yet, but we're still on track for before the end of this year.

The next month or two will be slow going development-wise as I'll be taking a vacation in Ireland to touch some rocks. I love working on the game itself, but the associated project management is taking a toll on my brain so this'll hopefully be well-timed. We built in plenty of buffer time for a reason.

Hope you had a good weekend!

 🌺 Olive