Zemeroth v0.6: Renown, Upgrades, Sprite Frames & Flips, Effect Icons, and Videos

Hi, folks! I'm happy to announce Zemeroth v0.6. Main features of this release are: renown and fighter upgrades, possessions, sprite frames and flips, and status effect icons.

Title image showing new fighter types, icons, explosions, etc

Zemeroth is my hobby turn-based hexagonal tactics game written in Rust. You can download precompiled v0.6 binaries for Windows, Linux, and macOS or play the online version on itch.io (should work on mobile browsers too).


After 0.5 release, I've experimented a little bit with smaller forum updates and short complimentary videos, but I've expectedly failed to make them regularly. Actually, I only managed to publish one such update: drafts for second and third updates were never finished. So, I decided to cancel my attempts at making weeklies and squashed all the "weekly" text drafts together into this larger announcement post.

Video drafts were also squashed into a video version of this post, check it out:

YouTube vide preview

So, what does this release add to the game?

Renown and Fighter Upgrades

The biggest additions of this release are the renown system and fighter upgrades.

"Renown" is the currency of the campaign mode that the player receives by winning battles and spends on recruiting and upgrading their fighters between battles. The term is obviously borrowed from Banner Saga.

Updated campaign menu looks like this:

a screenshot of campaign menu

Now it shows not only the last battle casualties and their current fighters but also their current renown and a list of possible actions with their costs (in renown).

The player is now free to choose more then one action if they have enough renown.

One upgrade option is chosen randomly for up to two upgradable fighters in the player's group.

If the player doesn't like provided upgrade options, they can skip straight to the next battle (that will be a little bit harder) and use their renown later.

Recruit candidates (and the amount of received renown after a battle) are still encoded in the award section of campaign's nodes (this is likely to become a little bit more random too in future versions). A sample from assets/campaign_0.ron:

initial_agents: ["swordsman", "spearman"],
nodes: [
    // . . .
    (
        scenario: (
            objects: [
                (owner: None, typename: "boulder", line: None, count: 1),
                (owner: Some((1)), typename: "imp", line: Some(Front), count: 3),
                (owner: Some((1)), typename: "imp_bomber", line: Some(Middle), count: 2),
            ],
        ),
        award: (
            recruits: ["spearman", "alchemist"],
            renown: 18,
        ),
    ),
    // . . .

(Note to myself: Employ an "implicit_some" RON extension.)

Fighter costs and upgrade options are described in a assets/agent_campaign_info.ron config, that looks like this:

{
    "swordsman": (
        cost: 10,
        upgrades: ["heavy_swordsman", "elite_swordsman"],
    ),
    "elite_swordsman": (cost: 15),
    "heavy_swordsman": (cost: 14),
    "spearman": (
        cost: 11,
        upgrades: ["heavy_spearman", "elite_spearman"],
    ),
    "elite_spearman": (cost: 15),
    // . . .

Recruitment cost consists of a basic type cost plus a group size penalty (the player's fighters count). The penalty is added because the intended size of the group is four to six fighters.

The upgrade cost is just a difference between original and upgraded type costs.

Now the campaign has some level of strategy: the player should think if it's better to recruit a new fighter or upgrade the existing ones. The player should never have enough renown to buy everything they want.


As for the new fighter types, most of the current upgrades can be split into two "kinds":

Basic fighter types were nerfed: fewer strength points, accuracy, abilities, etc, but are still useful.

The idea is that the player should have fighters from all three kinds in their group: slow heavies are supposed to be used as devastating "iron fist", quick and agile elites are used for vanguard and flanks, and basic fighters are used to fight weak enemies or finish off wounded ones.

Here are current "upgrade trees" (they have only one level for now, but I'm planning to add more nodes in future versions):

upgrade trees

(See objects.ron for exact details.)

As you can see, sometimes the upgraded versions lose some of their abilities. These upgrades are more like a specialization, not just an improvement: the fighter focuses on a smaller set of skills.

I also try to make fighter's stats match their visuals so the situation described in this itch.io review won't repeat:

... the most mobile unit in the game is the only one wearing heavy armor. Perhaps it'd be more fitting for the Hammerman to be in plate.

Agent's Info Screen

A basic fighter info screen was added:

agent info screen

It's opened by clicking on a small [i] button on the right from a fighter's type in the campaign menu:

"i" button in the campaign menu

This screen allows the player to look up stats and abilities before recruiting or upgrading a fighter.

Possession

Another gameplay change is possessions: imp summoners can now possess imps to give them more action points for a few turns.

The "Possessed" status is visualized with a yellow lightning status icon (read more about the status icons in the "Status Effect Icons" section below).

Possession demo 1: spearman is killed by a possessed imp

On the beginning of their turn, possessed imp gets three additional Joker points (reminder: Jokers can be used as attack or move points and aren't removed when the agent receives damage).

Possessed imps can run through the whole map, make a lot of attacks, and they won't stop on your reaction attacks until they're dead. So the player must look closely for potentially possessed imps and be ready to reposition fighters to form a lethal defense line:

Possession demo 1: possessed imp is killed with reaction attacks

The idea is that the player should never be in a situation like when two possessed imps run towards a lonely and badly positioned fighter.

Note: "Possession" looks like to be a bad name for one demon forcing a lesser demon to be more performing, so this ability and effect will likely be renamed in future versions.

Visual Improvements

There're many small visual improvements in this release.

Current Tile Highlighting

First, a tile under the cursor is highlighted now when using a mouse (it was requested by many playtesters). It makes no sense do this with touch inputs because the user will just constantly see the latest tile they touched, so the feature only works when input event's delta movement isn't zero.

demo of the tile highlighting

^ Switching between mouse input and touch emulation in the web version

Sprites Flipping

Next, agent sprites are now flipped horizontally to match their action's direction. Weapon flashes are also now flipped when needed.

scene with flipped agents

^ A screenshot with both imps and humans faced left and right

I've wanted to add this for a long time because previously humans were facing strictly to the right (imps - to the left) and sometimes they were attacking each other backward.

To implement this enum Facing { Left, Right }, a Sprite::set_facing method, and a corresponding action::SetFacing scene action were added to the zscene library.

The implementation of Sprite:set_facing is a little hacky atm. I was hoping to implement this method using only ggez's ggez::DrawParams' scale and offset fields, but because of this bug, it doesn't really work with custom screen coordinates that I'm using. So the method was implemented on zscene::Sprite's abstraction level using external offset.

Dodge Animations

It's hard to actually miss while attacking a static target with a melee weapon in real life. Most of the misses are caused by the targets dodging moves. So, simple target dodge animations when an attack misses were added to the game to display this.

Dodge animation demo

^ Dodge animations demo

Move Interruption Message

If any tile of a movement path is inside the attack range of an enemy agent with attack points, a reaction attack is triggered. If this attack succeeds the movement is interrupted.

A move point (or a joker) is spent even if the agent hasn't actually moved anywhere: the starting tile is also considered a part of the path. This prevents agents from exiting a melee too easily ("Jump" and "Dash" abilities exist to counter this).

I like this mechanic, but sometimes it wasn't clear to playtesters what just happened: they clicked on a tile, but were attacked and can't move anymore.

A helper message is now shown when an agent's move is interrupted.

Demo of the movement interruption message

^ A demo of a heavy swordsman's failed attempt to move away from an imp

Frames

From the beginning of the project I decided that I don't want to implement real smooth animations for agents: I don't like how 2D skeletal animations look in general and per frame animations are too hard to make. Quoting from the initial vision:

* Simple vector 2d graphics with just 3-5 sprites per unit;

So the plan was to have a few situational sprites ("attacking", "taking damage", etc) per unit and use simple procedural animation to make the image more alive. It's a compromise between having real animations and only having totally static pieces.

Some procedural animations (like "bumpy" walk or blood splatters and dust) were implemented in previous versions of the game, but all agents still used only one sprite.

With this release, agents finally get special sprite frames for (some) abilities!

frames demo

^ A demo of special ability frames ("Rage", "Heal", and "Summon")

For now, special frames are used only for the visualization of abilities. I've tried adding "attack frames", but they conflicted too much with weapon flashes and I decided that the game looks better without these frames.

Though, it's likely that spearman will get special directional attack frames in the next versions because he can attack enemies two tiles away from him and it looks weird with a completely static sprite sometimes because the spear is too far away from its target.


Sprite sets are configured now with a sprites.ron config that looks like this:

{
    // ...
    "alchemist": (
        paths: {
            "": "/alchemist.png",
            "throw": "/alchemist_throw.png",
            "heal": "/alchemist_heal.png",
        },
        offset_x: 0.05,
        offset_y: 0.1,
        shadow_size_coefficient: 1.0,
    ),
    "imp_summoner": (
        paths: {
            "": "/imp_summoner.png",
            "summon": "/imp_summoner_summon.png",
        },
        offset_x: 0.0,
        offset_y: 0.15,
        shadow_size_coefficient: 1.3,
    ),
    // ...
}

The "" (empty string) frame is considered the default frame.

As the event visualizers don't know anything about specific agent types, the code usually checks if the sprite has some event-specific frame and only then adds an action node that will switch the sprite to that frame during its execution:

let frame = "jump";
if sprite_object.has_frame(frame) {
    actions.push(action::SetFrame::new(&sprite_object, frame).boxed());
}

Frames are stored inside the zscene::Sprite struct like this:

drawable: Option<Box<dyn Drawable>>,
drawables: HashMap<String, Option<Box<dyn Drawable>>>,
current_frame_name: String,

I didn't want to index a HashMap with a string for every sprite on every frame, so the current frame lives in a special field drawable and everything is stored as options to simplify frame swapping.

Explosion Ground Marks

To make the battlefield look more interesting decorative explosion ground marks were added:

explosion ground marks demo

Same as blood, they're slowly disappearing into transparency in three turns to avoid making the battlefield too noisy and unreadable.

It looks boring when there're many similar big explosion mark sprites on the battlefield, so in future versions there should be something like 3-5 randomly chosen versions of this sprite (#531).

Status Effect Icons

Icons for lasting effects were added so the player can quickly see a more detailed game state info:

status effect icons demo

There're three lasting effects atm:

image files

If there's more than one lasting effect applied to one agent, icons are stacked vertically:

stacked icons demo

The icons are twice the size of brief info dots because they're more detailed.

External Contributions

There were a few PRs from external contributors:

Thanks, folks!

Other Changes

The game now has a (temporary?) text logo:

Text logo

The image was manually re-drawn (in a low-poly style similar to game's angular sprites) from a text written in the "Old London" font.

Not sure if it really fits the game - surprisingly many people say that it looks like something related to Death Metal and not just a generic medieval font - but it'll do for now.

Spreading the Word

I've got the ozkriff.games domain and moved my devlog to it. This is my first domain and the buying process turned out to be not nearly as scary as I was expecting.

I revived my Patreon page: patreon.com/ozkriff.

I also created new social pages (in addition to twitter):

... and a bunch forum threads and database pages (in addition to itch.io):

The initial plan was to post weekly updates to Patreon, forums, and catalog pages but as that experiment was canceled (see the preface) I most likely will just post links to new version announces there. Let's hope that this will motivate me to make smaller releases more often. :)

About Devlog Videos

As written in the preface, new devlog posts will be complimented with their video versions for folks who prefer consuming information in a more dynamic audio-visual form. I don't have any experience with making of such videos (I've been only making GIFs and short gameplay videos) so I'm learning this stuff as I go along. I see it as a part of game development, so it makes sense to add a few notes about my current routine to the devlog.

Ideally, I'd like to do record a live demo without a strict script, but speaking to the camera when you're not used to it is quite stressful and additionally I'm not quite comfortable with English. I've tried a few times to record the whole video "live" in one piece and it totally failed for me.

So, I prepare a script (by adapting a text post a little bit) and read it out (usually, one section at a time). I don't have any external mic and just use a simple headset for now (as clearly can be seen from the video). I use Audacity to do the recording and to filter out most noticeable background noises.

Then, I record short intro and outro clips using my phone's (its camera isn't perfect, but still much better than my laptop's webcam) to personalize the voiceover a little bit.

GIFs from the post can't be reused for the visualization because they're too small, so I go through the script and use SimpleScreenRecorder to record a lot of screen clips.

I'm using Kubuntu as my main OS, so the natural choice for a video editor is Kdenlive. Its UI feels a little bit clunky, but docs are fine and it seems to do all the basic stuff that I need: cut/match the video clips to the voiceover and add some background music.

Kdenlive screenshot

Finally, I prepare and add subtitles. There're at least three reasons to do this: accessibility, my terrible English pronunciation, and translation (to Russian). Again, KDE software seems to do the job fine: I just add the script to KDE Subtitle Composer line by line and tweak the timings a little bit.

How KDE Subtitle Composer looks

And that's it, the video is done and can be uploaded to YouTube.

I'm not completely happy with the quality of the 0.6 announcement video, but I guess it's only a question of practice.


just silly final gif

That's all for today, thanks for reading!

Here's Zemeroth's roadmap, if you want to know on what I'm going to work next.

If you're interested in Zemeroth project you can follow @ozkriff on Twitter for fresh news or subscribe to my YouTube channel. Also, if you're interested in Rust game development in general, you may want to check @rust_gamedev on Twitter.

Discussions of this post: /r/rust, twitter.