Zemeroth v0.0.3: Jokers, Rancor, Blood and more

Hi, comrades! Welcome to the second issue of Zemeroth's devlog.

Zemeroth is a turn-based hexagonal tactical game written in Rust.

It slowly grows into a middle-sized project: Zemeroth has 4.3k LoCs and 82🌟 now. Though I still can't find enough free time to work on it on daily basis and I have to take weeks-long breaks sometimes.

So, the progress is quite slow 🐌 but still, I've managed to do some amount of useful stuff during last three months. Here's a short video of how the game looks right now:

Ok, let's talk about what exactly have I done for the project during this autumn.

Pre-built binaries

The most important change for anyone who wants to try out Zemeroth and don't want to compile it from the source is auto-deployed pre-built binaries.

You can download them here: zemeroth/releases

They are compiled and deployed to Github's releases by CI servers (Travis, Appveyor and CircleCI) on every tag.

The latest release at the time of writing is v0.0.3:

v.0.0.3 release page

Linux/Windows and OSX versions are not that interesting - they are built exactly like it was done for ZoC.

Deploying Android apks is a more interesting story.

I've stolen basic apk-building script for CircleCI from @tomaka (thanks again!) - https://github.com/tomaka/glutin/pull/919 - and it worked straight out of the box.

Deploying apks is harder: circle-ci doesn't provide a built-in convenient way of deploying to Github, so you have to wield some curl/github_api magic.

All the magic is packed in .circleci/upload_apk_to_github_releases.sh script (which is based on this gist).

Also, some strange branch magic is required if you don't want to run both build and deploy targets on every commit.

Btw, old badges were not that clear of what do they mean:

build: passing, build: passing, passed - wtf?

Most programmers know that travis's badge usually means Linux/OSX builds and appveyor is for Windows builds. But what does the third one?

To fix this I've added custom labels to the badges through shields.io:


Looks a little bit better now:

uniform new badges

And there're two yet-to-be-solved issues:

Decorations: Grass and Blood Pools

The Grass is just a randomly-placed decoration, but Blood Pools are also an additional indicator of a successful attack:

two tiles of grass pool of blood

Jokers and Strength

Now, some actual gameplay changes!

To make Zemeroth a little bit more tactical Jokers were added. They can be used either as Attacks or Moves.

In the previous version, units were dying from a single hit. That doesn't work well with reaction attacks as they were dying too quickly: fighters should have some kind of basic hit points.

So Strength was added.

This name is stolen from Banner Saga: as I'm going to use this property also as a basic attack modifier. Right now every successful attack deals a damage of one strength but later a much more fine-grained battle math will be implemented.


To show the most important dynamic information about units on the map "Dots" were added.

thee agents with different stats showed by color dots

The current legend is:

@LPGhatguy pointed me out on twitter to a problem with colors:

With the amount of red/green in the game so far, you should try it with a Deuteranopia filter!

I've googled a simulator: etre.com/tools/colourblindsimulator. Here's an example of its output:

Example of output. Dots are hard to read now

And, yeah, that doesn't look readable at all. It's clear that I should change the colors and shapes in the near future.

Btw, http://gameaccessibilityguidelines.com is a very interesting resource. I've never thought about a lot of issues raised and explained there.

Info panel with selected unit's stats

Dots can show only a small portion of the dynamic information about units. Other less important dynamic or static information goes into the side panel.

info panel showing imp's stats using text

Btw, now you can select enemy units to see their move range and stats in the info panel.

NOTE: additionally, a deselection of any unit on the second click was added.


closeup of a spearman

Another important gameplay change is an addition of third fighter type: "Spearman".

He has an attack radius of two tiles and can control with reaction attacks a big area of 18 tiles around him.

spearman can attack distant enemies

On the picture above the spearman has only one Joker point: this is not an attack unit, he's almost useless during his own turn.

But if spearman hasn't used its Joker during his own turn, he has four reaction attacks during enemy's turn:

example of a spearman with 4 reactive attacks

And that's a lot considering that reaction attacks can interrupt enemy's movement.

Also, this unit has only three Strength points to accent his defence role.

Rancör - a Stupid Component System

Previously, Zemeroth's units were represented by a single struct holding all the stuff as its fields. Not a very adaptable solutions and it was impossible to create a non-unit type of objects.

It's the end of 2017 so the solution to this problem is obviously components.

With a component system, I should be able to implement: Boulders, Bombs, Fire, Poison stuff, Corpses, etc.

I don't think Zemeroth needs a full-featured ECS solution (like specs) as the game is turn-based. A bunch of HashMap<ObjId, ComponentType> will do the work fine.

So.. I've reinvented another ~~bicycle 🚲~~ square wheel! \o/

Meet Rancör - a simple macro-based component system. I'm not calling this an ECS because it has no systems, it's just a storage.

Nothing fancy, you just declare some usual structs for components and create a hidden HashMap-based monster-struct using a friendly macro:

rancor_storage!(Parts<ObjId>: {
    strength: component::Strength,
    pos: component::Pos,
    meta: component::Meta,
    belongs_to: component::BelongsTo,
    agent: component::Agent,

And then use individual fields:

parts.agent.insert(id, agent);
let agent = self.state.parts().agent.get(id);
if agent.moves == Moves(0) && agent.jokers == Jokers(0) { ... }

Or call parts.remove(id); to completely wipe-out the entity.

Rancor seems to work fine, but I see two issues:

As I do not expect anyone to use it, this carte lives in the Zemeroth's repo.

Btw, see https://gridbugs.org/programming-languages-make-terrible-game-engines and https://gridbugs.org/modifying-entity-component-system-for-turn-based-games articles about component systems and turn-based games in Rust.


Rust community kinda loves TOML, but I'm not a fan of this format. TOML is not that bad, but I find its tables too strange for anything but simple configs.

And I've decided to try to use RON format for Zemeroth.

(RON's readme has a pretty good list of reasons of why you may not want to use other formats)

Thanks to @kvark for starting this project originally and thanks to @torkleyy for resurrecting the project for Amethyst's needs.

Boulders and Rocks

To make tactics a little bit more interesting and to prototype non-agent objects using Rancör I've added some tile-blocking Boulders. This is a first non-agent object type in the game.

It's a little bit strange that I have different terrain types support for a long time, but not actually using them in the game at all. Meet randomly-placed tiles of TileType::Rocks type. You can move through these tiles, but it requires 3 move points and not 1.

a map with some boulders and rock tiles

Rock tiles and boulders work together with blood and grass to make the battlefield look a little bit less boring.

(Yes, I know that it'll be better to use a different texture for Rocks tiles, not just a darker color)

Logging: log & env_logger

I'm tired of adding a special-cased printlns to debug something small and removing them before the commit.

As I don't have much experience with slog, I decided to replace printlns with a classic env_logger. It seems to work, for now, the source code is filled with all kinds of info! and debug! calls. :)

Häte: Examples

Some news about my silly game engine.

It's important to decouple Häte from Zemeroth. First, Häte was extracted to a separate crate inside the repo. The next step is to separate example/test screens.

These screens do nothing game-specific so they belong to the engine.

There was a bunch of tasks needed to be solved before extracting the examples:

Häte: Cache Text Textures

ZoC was almost unusable in debug builds: it may take more than 10 seconds to start a game on some machines and now I'm trying to keep Zemeroth's debug builds fast enough.

One of the common problems is that Rusttype is very slow in debug builds, especially on Android. As a text in Häte is rendered by creating a separate texture for every string the simplest solution was to cache these textures.

Caching is a little bit sloppy solution, but we have no other choice until https://github.com/rust-lang/cargo/issues/1359 is implemented.

NOTE: Btw, there's a cool hack from @matklad, but you have to compile an outdated cargo from his dev-branch.

"Game Development in Rust"

me presenting a talk about zoc and zemeroth on spb meetup

There was a local meetup in Saint-Petersburg this September where

Everything is in Russian, but here are the links anyway, just in case:

overview of the slides

Thanks to JetBrains for hosting the event!

Short-Range Plans

RestructuredText -> Markdown

For a long time, I was trying to avoid the use of MD.

Mostly because MD has no built-in support for extensions and is MD is forever tied to HTML.

I was mostly hoping for RST to become popular enough. There was a chance, but doc team decided to stay with MD and now Rust community doesn't care about RST at all. :(

CommonMark solves the standardization problem to some degree. Though, it's still glued to HTML and is not easily extensible.

Anyway, I'm not going to use these documents anywhere except in the web browser, so... I'm going to migrate this blog to some rust blog generator from Pelican. This post is written in MD already.

Abilities and Lasting Effects

A skirmish game is unimaginable without special abilities and some interesting instant and lasting effects of the actions.

So, here are some previews of what I'm working right now:

Whew! That was the longest piece of text in English that I've ever written O.o.

That's all for today! :)

Discussions: /r/rust, twitter.