Making of DICED – Tournament

Author: Michael Stellmann (mi-chi), videos by Daniel Simon (DefDanny) – 2022/Oct/22 (Last update: Oct 30)

Note:

This text contains spoilers about game contents and features. As some things are essential or linked, there was no way to write this text without revealing certain things and refer to them in following paragraphs. If you didn’t play the game yet until league 2 or 3 and want to experience that moment of surprise, it is recommended to not read on.

For all the others who are still reading, welcome to an interesting ride about gaming development, design decisions.

I. Setting

There are many games already that allow lovers of dice games play on a digital gambling table. We wanted to make a game that’s fun to play, easy to pick up, hard to master, and bring it into a believable setting. As all table games are often regarded as kind of old-fashioned nowadays, we took the idea and emphasize this element. If it is considered old-fashioned anyway, we can as well setup a real old environment and pull the player into a different age.

One major goal was to make an entertaining experience, and a Western setting offered plenty of ways to do that: Rag time music, looks (fonts and graphics), stereotypes of characters, alcohol, hustlers and cheating. And to bring all of this to live, the game would have to offer a lot of interaction – besides playing the actual dice game.

Having learned that the actual base game we chose – a Yahtzee-style game – was very well known in many parts of the world – in some, it’s quite a tradition – we also found that there are regions where the game was known by its name, but no one ever played it. This also meant that we needed to teach the game. The obvious choice, writing all in the manual, was no option, as nobody wants to read manuals. Writing all the text in digital form inside the game and offering the player to read the manual there is not only quite as boring (it’s still “reading a manual”), but worse, during the game, you can’t look it up when you need it. And a manual is still a reference, an encyclopedia, dry information chained together. Most humans are not made to swallow a ton of information and recall that when it’s needed. They learn better when they are taught bit-by-bit in the moment when it’s necessary. The gaming industry has recognized this since long and took care of the demand by creating in-game tutorials. This still requires reading, but not so much at one time, and – being a virtual platform – it can be done interactively, which is more fun and resembles much more the real-life experience of having someone to explain something to you while you can try it at the same time. We wanted to try if that is possible on an 8-bit system.

II. Graphics

To create a believable environment, the audiovisual experience is the major key element, so the graphics not only needed to be top-notch, but also presented in smooth and fluid ways that we all got used to in the past decade.

The first tests showed that a lot can be made in screen 2 with the help of today’s graphic tools, and we decided to keep the drawing of the graphics and the designing of the screens independent from what a coder would consider as “doable” or impose any hard limiting factors on the graphics – other than what the hardware implies. The main problem with screen 2 is that it quickly runs out of graphic space – it can hold exactly one screen of unique patterns, and it is bad at hiding glitches when it comes to updating the graphics of the tile set at runtime. Nevertheless, we tried to stick with the motto:

“If it can be drawn, there must be a way to present it!”

Fonts and rendering

As good as the pattern mode is in the MSX-1 video processor, it totally fails when it comes to printing texts on the screen.

SCREEN 2 – the most usable graphic mode for video games on an MSX-1 – can not only display only 768 patterns on the screen at once, but has only space for 768 different patterns. Adding to that, displaying the same pattern in all areas of the screen requires max. 3 identical copies of that pattern – in that set of 768.

Specifically, the screen is divided into 3 areas with each 8 rows of patterns. Each of these areas have their own pattern-set of max. 256 characters (thus, 256 * 3 = 768), so if a pattern is defined to hold the bitmap of the letter ‘A’, and if that letter is to be displayed in texts in any of the 3 areas on the screen, that letter needs to be defined as a pattern in each area-set.

On top, patterns used for letters and numbers need to share the precious space with the game graphic patterns (for a whole set of text patterns: 26 upper case + 26 lower case + 10 numbers + some special characters = too many characters wasted). And as if that wouldn’t be enough, due to the size of the patterns (8×8 pixels), the text also looks ugly. All letters have the same width, height and spacing. Some games strip the lower case letters to save some space for more graphics, but these games

Oh, and that line above is all you can fit in one line at the screen. Yes, the whole screen width.

We were planning to not only have a lot of text in the game, but also display it in only a small window on the screen while having a lot of patterns used for the graphics. Oh, and it should be pleasing to read as well, or at least not hard-to-read – which makes upper and lower letters mandatory – so that pattern barrier needed to be broken up – almost 40 years of service is enough, time for something new.

Next to the small font, we also created one that is nicely matching the western theme of the game – with those typical bars in the center. The problem here was that in order to have these bars, the minimum height of capital letters (cap height) was 9 pixels, and with descenders (like in the letter Q or g), it’s 11.

This made one thing clear: The font render engine needed to be able to draw on a bitmap surface, at pixel granularity.

From here on, it became necessary that the pattern sets were managed dynamically within the game engine. Creating texts required patterns to be reserved dynamically, based on the position of the screen box of the text.

Also, due to the amount of text in the tutorials, we were expecting to do a lot of corrections and modifications over time. This brought up the need for auto-wrapping, and while we were at it – being at pixel precision – we also implemented

– styles (bold, underline) – on character level to not make the bold text appear condensed

– alignment (left, center, right)

– coloring

– umlauts and accents (as the game is not localized, that feature isn’t used yet)

– kerning (unconditional and conditional, according to previous character, and both, left and right).

Did I forget something? Ah, yes: spanning across area-pattern-sets – it’s screen 2 after all.

That flexibility also required a full blown new font format, and it should be save-spacing on top – not all fonts hold all characters, not all characters have kerning. And a conversion tool, so the fonts can be designed and updated easily with modern graphic tools in PNG format.

Small font (1024 bytes in MSX format)
Western font (1614 bytes in MSX format)

To create the kerning table, rendering the final texts on the screen will quickly reveal which letter pairs need to be closer together, but initially, rendering the following pairs turned out to be helpful for English texts:

‘s7. -O .” aj Ak am AV bc bv Co cv e, f’ f? fe ff fl fr gy he il Ku ky la lp LY my ot r, r: re ro Ru ry So Sp st Te To tt us Va vo w. wb xc y, Y. yo Yo

It should be noted that while it is tempting to mis-use the kerning to move all letters together until they are roughly 1 px apart just because you can, letters have a weight appearance, and the spacing needs to match the letters, i.e. large glyphs with lots of spacing inside letters (such as the Western font) require a certain distance between them to not appear condensed or unnaturally squeezed.

To save space, the MSX font data contains a used character bitmap (not “bitmap” as in graphics, but as in 10010010, where 1=indicates a present character, 0=absent) and an offset table to point to the render data of each used character. It also contains its width and height so the required space of a text on the screen can be computed faster. The offset table has a variable size and holds only entries for the characters which are actually used – a numbers-only (0-9) font would have a table with 10 offset entries. The renderer determines the index of the entry in the offset table from the used character bitmap. As this needs to be done for each letter during the rendering phas, one way to optimize the access to the table entries would have been to convert the ASCII-codes of the text to the exact indexes in the offset table. However, having to pre-process texts before writing them into the code turned out to be inconvenient during the development and when adding or removing letters from the font later. There are algorithmic ways to reduce the required overhead for the bit-counting to almost zero.

The render data of each character contains the start and size of the letter’s graphic data (variable size) and the kerning table for that letter, with a variable list of characters following that letter (this the term “kerning pairs”) and their left / right kerning value.

The graphic layout of screen 2 holds eight pixels in a single byte. To draw an object at pixel-granularity in the horizontal direction requires shifting the bits into the correct position before drawing. One technique to speed up drawing is to avoid that shifting by pre-rendering the object in all 8 horizontal positions – at the cost of 8 times more space. However, when doing tests with rendering the font graphics, we found that shifting the bitmap data at runtime was fast enough to render text at a reasonable speed, so we could save that extra space in the font data.

III. Tournaments

The game has 3 levels, called “leagues”, and the player would remain in a league until all tournaments were won at least once, and the boss was defeated. Within a league, there are up to 4 “series” of 1-3 tournaments.

Similar to other sports, the 3rd league is considered the beginner’s league, and thus, will be held locally, while the 2nd league is more on a regional level, whereas the 1st – permier – league is nation-wide. We wanted to express this fact also in the thumbnails at the tournament selection screen:

For the local matches (3rd league), the camera zoom is at a close range to a very small town. At regional level (2nd league), the zoom is further away, showing a city. And the first league shows various parts of the nation. There are East-West and North-South Border tournaments. The thumbnails in the tournament selection screen were created to show exactly these borders.

For the series within a league, we chose a lighting to represent a “day” – the first series is always in the morning / noon, the second series is in the afternoon, the third series is at night, and the fourth (2nd and 1st league only) shows the dawn after a boozy night.

Locations

As one tournament is always a varying number of matches, we wanted to give the player a “base” to return after each successful game, where he can rest between the matches and stock-up. As a tournament is considered a public event, there will be an audience, so running these in a saloon was a natural choice.

With 17 tournaments being held in 3 leagues, there’s a lot of repetition. To bring a bit of variation, we decided to create the saloons with a lot of detail and give each tournament its very own unique saloon. So yes, there are 17 saloons, with 4 base themes (incl. the tutorial level) and some minor and major changes in the layout – and a lot of details to find.

Technically, each league has a certain “base” theme for the saloons (most visible in the panels):

Each league has between 4 and 7 tournaments – and thus, as many saloon variations.

Due to the variations, no single league’s set of saloons does fit into a common set of 768 patterns. Therefore, each league’s saloon variations patterns were scanned for its common and varying (= delta) set of patterns. The whole set is then reduced that no single saloon variation uses more than 768 unique patterns (or more than 256 unique patterns in each of the three screen 2 areas) when the common set is combined with each tournament’s delta set.

And – due to the tutorials and the wallet in the lower corner of the saloon – the bottom area (= lower area pattern-set of screen 2) needed some additional patterns reserved to render the text surfaces.

The video below shows a time-lapse of the creation of the saloon:

Shops

Each saloon has three shops – the bar, the store and the piano player. The initial concept was to make all of these “Menu only”, like the title screen, but following the “If it can be drawn…” directive, we went for the fully visual approach.

For the presentation, we also tried something new. Instead of making the shop a new screen or displaying the shop items in an area of the saloon that was reserved for it, we wanted to blend it with the saloon, as an overlay. This, however, brought up another problem: the pattern-set size in screen 2 is very limited. As the saloons were drawn with many details, almost all of the 768 patterns were occupied, not a lot of patterns were left for the shops, which were also drawn as if there are no limits other than the color clash.

Due to the tightness in pattern space from the saloons, things were getting really complex. The solution required various steps now.

As the shops are being shown overlaid inside the saloon, there are some patterns in the saloon set that need to be visible at all times, and some that can be replaced – the latter patterns are inside the screen box that the overlaid shop is occupying.

So, from the common and the delta set of the shops, we needed to separate the patterns that are always visible from the patterns inside the shop box and marked them as “replaceable”.

When any shop is opened, the game blanks out the screen area of the shop first, so the replacement of the “inner” pattern-sets is not visible as glitches on the screen. Then, the pattern-set of the corresponding shop (there are 3) are loaded into the pattern-set, replacing the inner saloon patterns. In the case of the piano player, his clothes, hat and skin tone need to be color-patched additionally to match the colors as seen in the saloon. And then, the blank area is filled with the newly loaded patterns.

And when the shop is closed, the same procedure is done backwards. Thus, the saloon assets consist of an “outer” common set, an “inner” common set, and one “outer” delta set and one “inner” delta set for each tournament.

Due to the amount of details, some patterns that would be considered “common”, but which are not used in all variations of a league, needed to be stored redundantly in the delta sets of some variations to avoid an overflow of the 256 patterns per screen-area because of too many patterns in the common set.

IV. Controls

Here, we wanted to try a couple of new things:

1. Supporting major controllers

Would it be possible to support mouse, keyboard and joystick as if the game was designed around each single controller?

This means that if the game was designed around mouse control, using a mouse pointer on the screen is a no-brainer. However, if joystick support is added as an alternative control, having to move the mouse pointer around with the directional axis would be a major obstacle in the gaming experience and become boring quickly.

On the other hand, if the game was designed around joystick control, using focus rectangles or arrows that quickly jump to the action on each movement would be the way to go.

Adding keyboard, the movement could be similar to joystick, but as the player has another 70+ keys at hand, wouldn’t it be great to have some additional key mapped as shortcuts for certain actions?

Specifically in a dice match, the navigation with keyboard and joystick should be as direct as possible. Having to navigate a cursor over unnecessary options to reach the intended area would slow down the gaming experience unnecessarily, so we chose to not only have a focus rectangle to indicate the currently selected element (dice, scoreboard, roll button, bell), but mapping the most often used actions directly. One axis (left / right) is mapped exclusively to the dice selection, the other axis (up / down) to the scoreboard category selection. That way, even when the focus is currently at a dice, moving up or down would put the focus immediately to the scoreboard, and vice-versa (when moving left and right). One often-used element, the Roll button, was was mapped to a specific key (‘R’ / Enter) on the keyboard and to the second button on the joystick. And another rarely used element – the bell for the drinks is in the lower left bottom of the screen – was finally linked to the up / down-cycle, when it is present. There is no direct button for it, but the frequency of how often that feature can be used (max. once per match) will not interfere with the regular dice game workflow.

One insight was that it is difficult – or impossible – to combine mouse and keyboard / joystick controls with only one cursor, unless it is really obvious what is clickable on the screen or not. For keyboard and joystick, you always need a cursor, or a frame, or caret, to indicate where the focus is, and what is being activated with the action button. And for mouse, an additional cursor is needed to indicate where the mouse pointer is. Keyboard/joy does not need that mouse cursor, but the mouse – in most cases – does need the focus cursor to indicate whether the screen area under the current position of the pointer is clickable (focus rectangle shown) or not (focus rect hidden).

2. Changing controllers on-the-fly

Is it really necessary to decide the type of input – along with the port – at the beginning of the game? Keyboard, joystick or mouse? If the handling of all controllers has already been built into the game engine, why wouldn’t it be possible to not only detect, but also switch it at runtime?

It is clearly defined how the presence of a mouse can be detected, as well as a joystick movement and keyboard input.

Checking mouse presence:

The game is handling two states here, as they require different checks: When the mouse is present, it checks for absence, and when the mouse is absent, it checks for presence.

When a mouse is disconnected, the port permanently reads a delta of “+1” for both, x and y directions. As it is quite impossible for a human to keep the mouse movement consistently at that exact speed in both directions for a longer time, a mouse can be safely assumed as disconnected when that state persists for a few frames.

When a mouse is reconnected, the delta immediately changes to something other than “+1”. If that happens, it can be safely be assumed as reconnected.

Oh, and that way, it can also easily be detected at which port the mouse is connected. No need for the player to choose or set-up a specific one.

Reading the mouse is expensive, in terms of cycles, but the key here is that the presence (or absence) does not need to be checked at every frame. Only when it is actually connected. And only when the game requires an interaction.

3. How-swapping controls

In the game, there are sections where the player might want to use the mouse, and others where the keyboard is preferred, due to the additional hotkeys. We considered this a valid use-case scenario, so having to unplug the mouse to use the keyboard would not offer a good experience.

We introduced another layer that would keep the currently active mode, mouse or keyboard. To switch between them is easy: Just use the keyboard to switch to keyboard mode. This will hide the mouse pointer, indicating that the mouse if no longer active. And switching back to mouse mode is done by simply shaking the mouse. This will reveal the mouse pointer again, and the player would know that the mouse is active.

Caveat:

We also found an issue with one scenario, when a mouse in one port is used in combination with a joystick in the other port: This is because the joystick input interferes with the mouse query. Reading the mouse delta while the joystick is being moved does read valid but nonsensical values, so the auto-detection would be fooled. There is no good way to auto-detect joystick presence if it is not moving. And – unlike the combo mouse & keyboard – we didn’t see an advantage of having a mouse and joystick plugged at the same time and switching between them.

That said, unplugging the mouse and plugging-in a joystick works perfectly.

4. Keyboard repeat

The MSX BIOS has one feature that we always considered as outstanding compared to other systems of that time: It’s the keyboard repeat. Most other systems had a really quirky keyboard driver. The repeats were either too slow, laggy, or incomplete – MSX was the only system that could handle pressing the up and right cursor key at the same time, and moving the cursor up and right. And anyone who ever did something in MSX BASIC knows what we mean.

Many games are missing the concept of a key-repeat, or – that’s our opinion – execute it badly, compared to the experience in BASIC. For no reason. Thus, we tried how it would feel when navigating the menus, dice and categories, and it felt like home, so we kept it and added the feature to joystick controls as well.

We faced some issues with the repeating cursor, as some screens update other data and texts when the cursor is moved to an item or region, e.g. inside the shops, the item description and costs are updated, or in a match, selecting a category would render the category name under the scoreboard with the large western font. Or in the tournament selection, the where the specs are updated at the right side of the screen. As these updates are not fast enough to be performed withing a single frame (the text engine is rendering pixel-by-pixel after all), repeated movements got slowed down, blocked for a few frames by the screen update. This, combined with an audible selection effect had a huge impact on the smooth and fluid experience of the presentation and the look-and-feel. The solution was to defer the screen update for just a few frames. When the cursor wouldn’t be moved for at least one frame longer than the key repeat time (this varies in the BIOS of the MSX generations, but it’s never more than 3 frames on the systems we tested), we could safely assume that the screen can now be updated without interfering the repeat-action. A glitch in the animation effect is visible in some screens where the focus rectangle is moving smoothly towards the new selection – the movement gets stuck for a fraction of a second when texts are updated a few frames after moving the focus change. However, it appeared as less worse to us than having the keys repeat at a slower speed.

Lessons learned – Bad mouse? Or bad luck?

We also had our bad experiences, first and foremost, a glitchy mouse:

When navigating the scoreboard to put the current roll to a category, we accidentally faced a really nasty problem. “Nasty” because the accident is happening to the player, and for his disadvantage. But read on.

In a dice match, the game indicates the free categories with a cell cursor (and a pulsating focus frame). This cell cursor was originally designed for use with keyboard and joystick controls, and navigating up or down would simply skip the cell cursor over already filled cells and select the next free one. With mouse input, there is no concept of “skipping over” something with the pointer, so we decided to keep that cell cursor also with mouse input to indicate that a cell is available (in which case the cell cursor was shown) or not (=hidden).

But there was a problem: The mouse cursor is occupying 2 sprites per line (it’s actually composed of 3 sprites, but they are aligned in a way to have not more than 2 per line). The scoreboard is using up to 2 sprites itself for the white dots in the small dice in the left column of each category. That’s 4 already – the max that the video processor can handle. The cell cursor and the pulsating frame use also max. 2 sprites per frame (it’s composed of 5 sprites, but also aligned to max. 2 per line), making a total of 6. So either 2 sprites became invisible, or they started to flicker. We don’t like flickering and thus decided to hide the mouse pointer, and let the cell cursor take its function instead. As the cell cursor is moving at cell-granularity (=not pixel-wise), the player wouldn’t see the mouse pointer. It is still there, virtually, but not shown. When moving the mouse away from the cell column, or over an already used cell, the mouse pointer would reappear and the cell cursor would be hidden again.

While this was intuitive, we faced a problem that once in a while, when clicking the mouse, the physical action of clicking moved the mouse up just a bit on the mousepad, and when the virtual (hidden) mouse pointer was just around the upper edge of a cell, it might move in the cell above just a moment before the click was performed, and put the score in the wrong cell – in worst case, scoring 0 points in an otherwise easy-to-score cell and ditching the hard earned roll.

Thus, we tried to swap it (show the mouse pointer and hide the cell cursor), but the same happened from time to time when the mouse pointer was close enough to the upper end of a cell. As there is no more confirmation, and as the cell grid is very tight, we needed to come up with a way to avoid this.

First attempt: Cell Magnet

Whenever the mouse pointer was over a cell and staying there for a couple of frames, the mouse was centered vertically, to get it away from the edges of the cell, before the user would click. That “jump”, however was clearly visible and felt weird. So we switched again, hiding the mouse pointer when over a free cell and showing the cell cursor with the focus rect instead. Now the “magnet” feature wasn’t annoying because the pointer was no longer visible when doing its jump, but depending on the sensitivity of the mouse (or maybe the clumsiness of the player, we couldn’t say for sure), this happened with the test players even when the virtual mouse was centered. We were wondering how players were able to move the mouse 4 pixels away when clicking, but it happened. So we were back at step 1.

Second attempt: Brute Force

In the next attempt, we brought up data mining (or the OpenMSX equivalent: the debugdevice): logging the time passed between selecting categories and clicking the mouse. We found that, even for fast mouse players, no one ever made it under 17 frames. 17 frames between selecting the category, having the brain acknowledge that the cell cursor is in the right category and giving the order to click the mouse to put the score. But we also found – by a large margin – that there were entries in the log with 3 and less frames. It was hard to imagine that anyone would select a category and within 3 frames do all of the above and put the score. So we added a safe margin and defined 6 frames as the minimum time for the cell cursor to reside on a category before the score is being put in that category on mouse-click. And here comes the heavy gun: If not, the score is put in the previous category instead. No questions asked. (With mouse input only!).

And from there on, we had no more angry test-players. Well, at least not angry about that issue. (They were still angry at the bad rolls that they rolled, but that’s different story)

Lessons learned – How the brain works… not

For a faster playing experience on the keyboard, we added shortcut keys for the dice. One key to toggle the “holding” state of each die. Pressing the key “1” would hold the leftmost dice, key “2” the one to the right and so on, up to 5.

Sounds intuitive, does it? Yes, but no. Bonus points for guessing why! What could possibly go wrong with assigning dice positions to position keys?

The answer is: When the brain is thinking in numbers, it is not good at switching to a different number system.

An example – or should I say “an experiment”:

Let’s take the following roll:

Roll1 3 4 5 4
Position1 2 3 4 5

Think about which dice to hold.

Two 4s: Now think about the keys to press: 3 and 5

or

The 3, 4 and 5: Keys to press: 2, 3 and 4, or 2, 4 and 5

See the problem: It is all but intuitive having to press the keys 3 and 5 to keep the dice with value 4. Or pressing the keys 2, 3 and 4 to keep the dice with values 3, 4 and 5.

We had to come up with something radically different. The solution turned out to sound really complex when trying to explain it, but very easy to catch up: If the brain thinks in dice values during the evaluation phase, the keys should work accordingly. So instead of mapping the keys 1-5 to the dice positions, how about mapping the keys 1-6 to the dice values? Depending on the dice values in the roll, there may be keys that remain unassigned in a turn, if that value is not in the roll. Or keys that are assigned to two or more dice. In the latter case, any matching dice in the currently rolled (= non-held) set will be moved to the held dice area first, one per press, when continuing to press the same key again. If none are left, continuing to press the same key will move each die of that value back to the roll area, one per press.

But as mentioned earlier, it’s easier to play than to explain.

V. Roll and turn pace

In early versions of the game, playing against opponents was a simple turn-by-turn experience, one player after the other, i.e. the player did his 3 rolls, then the next opponent continues, and so on, until all opponents – max. 3 – did their turns.

This worked well with only the human and one CPU opponent but increased the downtime of the player dramatically with 2 or 3 opponents. As each player’s turn is independent of the others (every player uses his own set of dice), the idea was to let all opponents play their turn in parallel, while the player is thinking the strategy for his own 3 rolls.

Doing so, we found two issues:

1.

The CPU players might be faster than the human player. If the opponents are all finished, the player would no longer have the impression that the gambling table is busy, as nothing else would be moving or animating.

2.

The human player would have an advantage over the other players: He could always wait until they end their turn and act accordingly, already knowing what they scored.

The final solution was a mix of both: Playing each roll in parallel but wait between the single rolls until all players have finished their decisions. So each turn would be a sequence of (up to) 3 rolls, and every player would start the roll simultaneously. That reduced the downtime of the human player to zero, while keeping the impression of a match going on between multiple players.

VI. The dice rolling animation

Being a dice game, a player will spend most of the time in the game with – rolling dice. And even though all players are rolling the dice at the same time, the human player is only interested in one set: His own. As a consequence, animating the CPU players’ dice would – for most of the time – be a waste of effort. And that makes animating the human player’s dice an even more essential factor of the fun in the game (or lack of it, when it’s not done right). So the rolling animation should not only look visually appealing, but it should also make the player WANT to roll these dice… We needed to achieve several effects:

For one, the visuals should give the impression that the dice are actually rolling, like real dice. Due to the space restrictions implied by always displaying the scoreboard and all opponents on the table at the same time during a match, throwing dice on a playfield (as you would in real life) and letting them roll along and bounce off the edges was not an option. Things needed to stay in place. Lining the dice up on a virtual string and limiting the roll to a single dimension would be the way to go. And, while with real dice, you would lose the possibility to roll the two faces punched by the string, a video game gives the freedom to make 6 different faces appear on 4 sides of a dice. Virtual wonders of bit-magic, he he :o)

Next, we wanted to make the rolling animation an experience. Rolling real dice on a table takes only a fraction of a second and is not really exciting. Frequent role players often use dice-towers, where they throw in the dice at the top, then listen to clicking noises until the dice make their way through the obstacles in the tower on the way down, and after 1-2 seconds, the tower spits out the dice to a small field at the bottom of the tower. We like two things here: First, it is more random. More random than rolling dice directly by hand, which has room for cheating (or, as some call it, “creative ways of rolling dice”). Second, it raises the tension. 1-2 seconds of hope, enough time to send a small prayer, but fast enough to not stall the game’s workflow.

Unlike in a dice-tower, the dice would be visible at all times during the roll. As the dice roll would last for 1-2 seconds every time, the animation needed to look good enough that it does not get boring after seeing it the 100th time. The human brain tends to recognize simple patterns and become used to it – but also eventually bored. A real dice roll never becomes repetitive, as it never rolls in the same way (or – in the dice-tower – with the same noises and length). Thus, we wanted to make the dice look “real” while rolling (i.e. as 3-dimensional as possible in a 1-dimension animation axis) and have a natural motion path.

For the “real” look, we started with hand-drawing the animation phases of the dice. This quickly turned out to be unmanageable: Hundreds of frames would be needed to represent all of the 6 faces, eating up a lot of space.

Also, pre-drawn dice would always be in a specific order- it could be 1,2,3,4,5,6 – which wouldn’t look very random while rolling. Even with a seemingly random sequence like 3,5,1,6,2,4 it would always start over and not look so random anymore after some rolls. It became clear very quickly that the rolling dice faces needed to appear in a truly random sequence (…as random as a pseudo random number generator can be).

For the “natural motion path”, looking at that jerky animation that the hand-drawn dice would do, many more animation frames would be needed to make the roll appear smooth and believable. There was no other way than to actually render the dice faces in real-time, from a single texture bitmap per face, to any arbitrary roll angle. 2 Textures per dice. Up to 6 dice (normally 5, but using the Ancillary Aqua drink brings up the extra dice. Oops, spoiler alert!). With 60 fps. Adding to that, the linear movement path looked boring. To make it more lifelike, it needs to imitate something that players recognize intuitively from real life. In the case of rolling dice: inertia and friction. And – recalling the dice-tower – trigger an emotion: tension.

Time to bring out the heavy equipment:

1. Real looks

What’s better to create a 3-dimensional animation than… a 3-dimensional animation tool: In that case, we took Blender (as we couldn’t afford hundreds of bucks for a commercial application just to create a dice-animation for a game that get us no money in return). The first goal was to find a way to export data that the MSX can use to quickly draw and stretch the dice faces in real-time with perspective (i.e. a sense of depth). As the dice would only scale in one dimension (vertically), the impression of depth would be limited in a way that texture parts closer to the camera (=in the vertical center) would be less squeezed vertically than the distant parts (at the upper and lower vertical edges). Tests with also doing a horizontal stretch per line brought more problems with the color clash and didn’t make a real visual difference.

The pixel resolution of the dice faces (16×16) and color restrictions (or “color clash”: 2 colors per 8-pixel block) are small enough that it wouldn’t matter so much. In the end, using a gradient texture to measure the actual lines and distances, and a bit of hand-tweaking turned out to look good enough to be believable. But flat. We liked the lighting from the hand-drawn version, which was making a difference, so we re-added that, adjusted to the new, increased number of sequences.

Luckily, the lighting data is independent of the texture scaling and doesn’t need the heavy math, so we could squeeze out a few more cycles to also apply the color-fill over the rendered textures of each dice and keep up the 60 fps.

While testing, we found another unwanted glitch with the visuals: Random number generators often emit sequences of the same numbers, especially when the range is as small as in a dice game (1-6). Visually, it is not pleasing: For the opponents’ small dice, the “rolling” effect is purely based on the number of dots changing on the dice faces. If the face remains the same when the next value is rolled, it gives the impression that the dice is just static and has not changed. And for the player’s dice, it looks weird if the same face appears twice in a row. Thus, the rolling mechanism avoids during the animation phase (that little fact is important) to show the same dice value twice in a row. We should note that this does not affect the actual dice roll (more on that in “Random numbers…” later). Because of that, it may be happening that in the final phase of the roll, the resulting face of a die can be identical with the previous-last face shown during the roll animation. This may be seen as a glitch at the end of a roll, but we decided to favor strong (random) results over visuals.

2. Natural motion path

Now, that the dice looked good enough visually, the motion would have to become par.

The linear movement looked boring. Just boring. More dynamics were needed.

One of the first algorithms that comes to mind when imitating natural motions are splines. Two points connected with two weighted control points, creating a smooth curve. Mapping this curve to the dice’s roll axis turned out to be very promising. As most human brains are better at visualizing than at fractional math, we created a motion path editor so actually see the curves and the resulting animation.

Only more variation was needed. Variation of the path that a single die would roll. But also variation of how the whole set of dice would be “linked”.

We already had a solid idea for the latter: with 3 leagues (=3 levels) in the game, using the same roll sequence for thousands of rolls will eventually become boring. Offering different sets of dice that behave (=roll) differently are valid items in the game that a player would want to achieve. As there are three bosses, we chose to give two bosses boss a specific dice set that a player would win after defeating each of them. Oops, spoiler again. And this gave us a new idea on raising the tension in boss fights: Let them roll with their own large dice set during the battle. And at the same time, giving the player some motivation: To reach for that new set of dice.

3. Tension

The dice-tower effect of hoping for a good roll was mostly achieved by letting that roll last for 1-2 seconds (a bit longer for the blue Chaos dice set). As we were testing sequences with the newly created motion path editor, there was showing up a new opportunity to raise tension: an effect known from a “Wheel of Fortune” (yes, yes, yes, yes, noooooo). Some rolling sequences slow down at the end, come almost to a halt at the edge of the die, until finally landing on that wrong face. Not the one that was already showing up.

And for the Chaos dice, there’s an extra nasty sequence: Some dice flip one more time after they already came to a rest (yes, phew, noooooo).

VII. Opponents

A good dice game can be fun, but playing solo becomes boring quickly. It is much more fun with some competition.

It was recognized decades ago, when chess masters started to challenge the first world-class AIs, that winning against a piece of silicon is not as satisfying as playing against a player that you can see and feel.

To stay in line with the western theme, some typical stereotypes of that time were created. With 17 tournaments in total, there are lots of opponents to beat, and we wanted to avoid playing against the same characters in all tournaments and leagues again. Thus, a list of opponents was created with each having a specific look and a strength range – and for the theme, a name that… well… matches their origins (Clerics, Native Americans, Military). As can be seen in the tournament map screen.

The total resulted in over 190 individuals, and most of them have unique looks – a mix of the base stereotype and several color features in hairs, skin, clothes, and other characteristic features.

In a match, when all are at the gambling table, the opponent’s viewing direction is always facing towards the center of the table. to make them appear as they are sitting around that table.

Playing against real humans is always an experience of many emotions, joy and anger. In a dice game, strategy can only add to luck, but not replace it. And while every player will suffer from bad rolls, for a real gambler, it’s also very satisfying to see other players suffer. Thus, each character has alternative visuals, and depending on the outcome of a turn and the score, they will show their emotions accordingly, looking happy or angry. And even one more… but more on that later.

The videos below show a time-lapse of the creation of the character stereotypes “Calamity Cate” and “Sgt. Pepper”:

Bosses

The boss battles should be a special experience. Technically, the player plays 3 or more matches in a row against the same opponent, and at the end, the one with the highest score wins. But there should be a difference to the regular matches: This is a 1:1 duel after all, and the winner takes all the loser’s money. The tension should be much more dense, and we wanted to raise the pressure on the human player.

To achieve that, a small story scene with a different music style will introduce the boss battle. Here, the player has the chance to opt out like a wimp, e.g. to do some unfinished business, like buying this league’s musical scoresheets, which will no longer be available after advancing to the next league right after the battle. Also, no other music track is selectable. The boss has taken over control.

In the dice game, the layout is changed: The league’s final opponent is sitting on the opposite end of the table, looking straight into the player’s eyes, and the scoreboard is smaller, with columns for only two players. The new space is used to let the boss play with dice of the same size as the player, and with enough space to let him talk what he thinks. And talking he will. A lot. All good and bad actions of the player, as well as all good and bad actions of himself will be commented. Trying to win the game with all tricks, humiliating and demoralizing the player according to each bosses’ own style.

The final step to intensify the immersion of a duel was to let the boss use the same input mechanics as the player: seemingly act as if there is another human player by taking over control of the mouse pointer and use it to select the dice, press the roll button and put the score in the table.

And as a last detail, for the first two bosses who use their own new set of dice, the animations of the rolling dice should have a “signature”, a specific set of movements that makes these dice uniquely distinguishable from the other sets. Visually. And audibly. On top, one of the sets even has a practical advantage – for pro players: The roll sequence is shorter = faster play.

Blue dice (Chaos cubes, default)

Green dice (Zen cubes)

Red dice (Speed cubes)

VIII. Computer AI

As the competition is lined up, it needs an AI that is forgiving enough to let new players get into the game with ease and raise the strength incrementally with each new tournament until even experienced players find their ultimate challenge.

With only 8K of RAM and a lot of things already running at the same time, there’s not much room for heuristic methods that build, search and traverse probability trees to find the best strategy.

Thus, the algorithm was split in two parts:

For the first two rolls, a strategy is computed for the best dice to hold before doing the next roll, depending on which options are still available. Or exit early, if there is something suitable in the roll already.

And for the 3rd roll, to determine the best category on where to put the score.

This alone would – in the long run – enable a human player to adapt to the strategy of the CPU player and play more or less risky, depending on how the opponents are progressing in a match. To avoid that, we implemented 3 and a half strategies for the opponents, selected at random at the beginning of a tournament for each opponent. Two more conservative strategies, and one aggressive strategy with (that’s the half strategy) some specific sub-options to make it even more aggressive. The latter is a high-risk strategy: Win high or lose high.

The core algorithm can simply be described as computing a decision score of the current roll for each free category. In the last roll, the category with the highest decision score is chosen. This could also be in roll 1 or 2 already, when the decision score of a category is high enough and any more rolls are not promising any better score, for example if the roll is 5-of-a-kind already.

For rolls 1 and 2, however, there is no direct category to choose in most cases, so the decision score needs to include the probability to score the maximum points in a specific category when re-rolling a certain number of dice for 1 or 2 more times. At this point, due to the limited CPU and memory resources, the algorithm doesn’t compute the optimal outcome of all possible variations, but only computes the decision score for the same dice being re-rolled in both rolls to reach the maximum possible score in a specific category with the current roll. Then, the corresponding dice for the category with the highest decision score will be selected for a re-roll – and the others are held.

The decision score is based on the points that the roll would currently score in each category – some categories are a sum of dice, and some are a fixed score – weighted by the probability of getting the highest possible score when re-rolling 1, 2, 3 or 4 dice for 1 or 2 more times, or 0 times if it’s the 3rd roll. For many categories, the fixed score will be 0 in rolls 1 and 2.

Example:

A roll of 4, 4, 5, 6, 6 would score 0 pts in the category “Full House” (which has a fixed score of 25 pts). But as this roll could become a Full House when re-rolling the 5 into a 4 or 6, it is very close to it. The decision score is then weighted by the number of missing dice to reach the maximum score with that roll in that category, with how many variations that score can be reached and the reachable maximum score in that category. For some categories, there could be multiple solutions, e.g. with the same roll, the 3-of-a-kind category could score 4+4+4+6+6 = 24 pts if the 5 was re-rolled to a 4, or even 4+4+6+6+6 = 26 pts with another 6.

If this is roll 1 or 2, there are rolls left, so the category is not strictly relevant here. The main point is that the algorithm would – when we simplify the example and regard the above as the only decision cases (the only free categories) – always select the dice with the value “5” for the re-roll, as there are many potentially high score outcomes.

The result could be four 6es when re-rolling a 4 and a 5 to match the 4-of-a-kind category (which would score 4+6+6+6+6=28 pts), or five 6es when re-rolling both 4s and the 5 to match the 5-of-a-kind category (= 50 pts). Or five 4s when re-rolling the 5 and the two 6es.

On top of that, the decision score is then evaluated against the strategy. Each strategy has an extra ruleset to favor specific categories over others, even when the decision score would be higher on another category. Using the example above, having rolled a 4+4+6+6+6 (=26 pts in the category 3-of-a-kind), the CPU might choose to put the score in the category 6es in the upper section instead, where it would only score 18 pts, as this could count towards reaching the bonus of +35 pts in the upper section.

In another strategy, the CPU could also play aggressively in rolls 1 and 2 and re-roll three dice of the roll 4, 4, 5, 6, 6 in an attempt to achieve a 5-of-a-kind, but score conservatively in the third roll (e.g. trying to get the upper section bonus), when that goal is not reached.

To create the parameters for the strategies and to generate data for enough opponents for all tournaments, and adding a buffer so each new game would mix a handful of new opponents into a league, a test tool computed 100,000 matches using the engine with different values for the different parameters for each of the 4 strategies, computed the average score from the 50% around median and fed a database with these values.

The opponent list is generated from a tournament description file that defines the tournament types, the number of matches, the player line-up and an allowed strength range for the CPU opponents per tournament. A tool computes the number of opponents required at minimum to populate all tournaments in all leagues, takes care of range-overlapping and defines their strength range. Minimizing because in the end, the number of opponents defines the number of required stereotypes and color variations.

When a new league is entered, an opponent list for each tournament is generated (and stays the same while playing in that league), based on the opponent’s strength range. From that list, the opponents in the game then are assigned a strategy from the strategy database.

Players should be prepared to experience opponents that play a very different style than they would ever play, but especially in the higher levels, there’s a deadly strategy behind the decisions. Would you use a Full House to score 25 pts or use the roll to score three same dice to fill the upper categories to get that +35 pts bonus at the end of the match? Would you keep going for 5-of-a-kind if you could go for something else? How much risk would you take? It all depends on the actual values, number of rolls left in a turn, number of turns left in the match!

IX. Unlockables, Cheats and Pacing

To keep the game interesting for players for a bit longer than “just playing a dice game”, some collectibles have been added, in the form of music scores – the game features 8 songs being played in the matches in total – and upgrades to the scoreboard.

Becoming a better player is also a question of having more control over the match. Not directly – the dice are rolled randomly, nevertheless. Knowing when to go risky and when not is a major key to succeed in the higher tournaments. There are different tournament types, e.g. in “Worst-out” tournaments, players don’t need to finish every match with the highest score. If an opponent already has a much higher score in a single match, the player should let him go, stay safe, and simply make sure to not be the one with the lowest score in that match. No need to go for a high risk, play it safe, wait for a better situation. To have that control, it is mandatory to know the standing in a match – the own, and the others.

Thus, the scoreboard has been made upgradeable. At the beginning, these options are still locked, but they are also not required to win matches, as the opponents are weak enough. As the game progresses, the player will have the resources required to unlock these features when it becomes more and more important to do have them.

And for the Monks among us, we also decided to initially let the dice faces that are not 90 degree symmetrical to show both faces, the left and right-rotated versions of 2, 3 and 6, as seen below, and provide a unlockable item to have them always align equally when the roll is complete. It also makes it a bit harder to recognize same faces in a roll. Just like playing it with real dice.

As the tournaments late in the game can be very challenging, we needed to give the players ways to give their luck a helping hand. The drinks are a one-shot per match item to make the opponents drunk enough to pull a trick. These need to be bought in advance to each match and invalidate after a match if they were not used. There are 5 drinks in the game with totally differing effects, some of them even varying in their usefulness depending on the time (i.e. how many rolls there are left in the turn). The manual (PDF and the barkeeper in-game) describes them and their use accurately, and it’s up to the player to know and learn when and where it’s best to pull that advantage.

We got feedback that the drinks have too high costs (price and required reputation) at the beginning, but they were intentionally priced like that. The game’s difficulty is increased smooth but steadily towards the end of the game. At the beginning (3rd league), most players should not need any additional help to win matches. Over time, the Abacus / scoreboard upgrades gives them ways to become better with their own strategies in the game (2nd league). This is also where most of the first drinks will become affordable to be able to push the luck just a bit more. The first league will be tough, especially the KO type of matches, and here, the drinks will become a helpful way to win some of the matches. Knowing that, if it is a question of a lack of money, there is always a Worst-out type of tournament available where a little grinding can be done. And in these tournaments, even the 2nd placed player will get a share of the prize money.

X. Random numbers

What would a dice game be if the outcome does not appear random? Thus, we were looking for a strong random number generator that would still compute quickly on a Z80.

Good pseudo random number generators (PRNGs) – in the meaning of “cryptographically strong” – are mostly complex. Algorithmically complex and complex to implement.

Looking for a suitable method led us to the family of Xorshift generators, discovered by Marsaglia. The essence is shifting a base number for a certain amount of bits, while xoring that base number during the operation with the shifted number. For the dice rolls, the game uses a 32-bit value as the base number, as described in Wikipedia with:

x = old_seed;
x ^= x << 13;

x ^= x >> 17;
x ^= x << 5;
new_seed = x;

The game uses shift values of 8, 9 and 23, which are more friendly to compute with the instruction set of the Z80.

Starting a random sequence always requires an initial seed. The easiest way to do that is to use the current date and time, if that is available, or some frequently changing values, like the R register on the Z80. On a minimal MSX-1 system, there’s no battery-backed real time clock. There is a system counter that is incremented in each frame by the interrupt routine. That counter may differ on different systems but will almost always be the same on one system when the ROM starts, and – unfortunately – the same is true for the R register.

To simplest way to resolve that is to require some user-input, wait for it to complete, and then shuffle the seed with those timers and counters. No user will be able to perform the same steps in the exact same time (and “exact” means a granularity of at least 1/50th of a second). And even better is to re-shuffle the random seed at various places locations in the game whose locations and timings are bound to user-input.

As the game engine requires lots of random numbers during the roll sequence and the “thinking” sequence of the opponents, a faster, but much weaker 16-bit random algorithm is also implemented.

The strong and slow algorithm is used only when it matters, which is to compute the final dice values after a roll.

The weak and fast algorithm is used only for visuals and animations.

Random number ranges and biases

One notable thing about computing random numbers is commonly known as “biasing”. While PRNGs are usually designed to emit numbers that are distributed evenly over the whole number range (= each number has the same probability as all the other numbers), the ranges are usually power-of-two bit widths, starting at byte-size: 8, 16, 32, … bits. There are only a few applications in games that require exactly that number range. The simplest way to deal with that is to use the modulo operation, so to generate numbers in the range of 1 – 6 from a range of random values in a range of 0 – 255, offset the required range to zero (so the number range would become 0-5), compute the modulo of the generated number using the desired range size (in that case 6) as the divider, and add the offset back again. In (pseudocode):

dice_value = ( rnd8_value mod 6 ) + 1

All good? Even on a perfect PRNG, with that formula, some dice values will appear more often than others. To understand the phenomenon, let’s use different numbers:

Assume that random numbers are needed in the range of 0 – 191. The range size is 192 values. With a PRNG emitting values from 0 – 255, the numbers are mapped as follows:

0 → 0
1 → 1
2 → 2

190 → 190
191 → 191
192 → 0
193 → 1
194 → 2

255 → 63

Or visualized:

The top numbers are the output range of the PRNG. The bottom numbers are the results after applying the modulo – let’s call it the output range from now on.

Assuming that all PRNG numbers have the same probability (= 1 / 256), the probability of the resulting numbers differ: The dark areas show which numbers from the full PRNG range emit the output numbers 0 – 63, and the bright areas show the same for 64 – 191. It is clearly visible that the numbers in the range of 0 – 63 have two areas, so the probability of each number in that range is not 1 / 256 but 2 / 256. That’s twice as much, so that formula results in a bias towards the values 0 – 63.

Depending on the PRNG and output ranges, how much they overlap, and how many times the output range fits into the PRNG range, the bias range can be smaller or larger, but unless the PRNG range is an exact multiple of the desired output range, there is a bias.

In many cases, the bias may be neglectable, e.g. when computing dice values in an output range of 0 – 5 (=6 values) from a 32-bit PRNG value using the modulo method, there is a bias only in the last 4 values of a range of 4 billion values, meaning that the first 4 values of the desired output range have a probability of

0-3: 715,827,883 / 4,294,967,296 = 0.1666…

whereas the last 2 values (4 – 5) only have a probability of

4-5: 715,827,882 / 4,294,967,296 = 0.1666…

That’s an incredibly small number. Probably neglectible.

But here’s the caveat:

The modulo operation invokes a division, so to compute the modulo from a 32-bit PRNG requires a division of a 32-bit number by the modulo value. As the Z80 is not a modern CPU, dealing with 32-bit values is not a native strength. The same is true for divisions.

The game thus does not do that heavy math. Instead, it makes some shortcuts: As the Z80 is an 8-bit CPU, the native operation range it 8-bits. As the output values are in the range of 0-5, it fits well within the 8-bit range. So the first task is to bring the 32-bit PRNG into an equally suitable 8-bit range. Here, suitable means: without losing the cryptographical strength. Assuming that all values in the 32-bit PRNG range are distributed equally, then the same is true for any group of numbers in that range, or in other words, we can simply take any of the 4 bytes of the 32-bit number and treat the 8-bits of that byte as a random number in the range of 0 – 255.

With that, we can redo the bias math, which results in the following probabilities

0-3: 43 / 256 = 0.16796875 = ~16.8 %

4-5: 42 / 256 = 0.1640625 = ~16.4 %

Still small, and probably irrelevant for a dice game like this, but anyway…

There are two ways to deal with that:

1. Treat all PRNG numbers as floating-point numbers in the range of 0..1, then multiply the result to be in the range of the desired output.

2. Treat PRNG numbers in the biasing range as invalid. Assuming that the PRNG always emits values with the same probability, simply let it generate another value and repeat until it generates a number that falls within the valid range.

Using option 2 for dice, this would affect values greater than 251. Not many. For the first example (values from 0-191), this would affect all values > 191. And for a range of 129 values, this would affect all values > 128, or roughly every second value.

XI. Music

A lot of atmosphere can be generated by music, and for a western style game with saloons, there’s probably nothing better than some good old Rag Time with classics like The Entertainer or Maple Leaf by Scott Joplin.

All in all, there are 8 tracks available for the gambling table. Each league has 2 tracks to unlock, where the tutorial level also has 2, which you will need to buy in that level. When starting a regular game, these 2 songs are already owned by the player.

The game will select a track randomly at the beginning of each match at no cost, but as soon as the player owns 3 music sheets (e.g. after buying the first song in the 3rd league), a playlist can be created just before entering a match with up to 3 songs. Hence the price list of the piano player. And by the way: This will trigger a micro tutorial in the song screen, right before the next match is started.

As none of us is a musician, we needed to find a creative way to convert those songs efficiently.

The score sheets can be found on several web sites that preserve the Rag Time heritage. Most of the music from that time (around 1890-1910) is even royalty free. The compositions are primarily for piano, that is 2 voices, and each voice has – in some cases – up to 4 notes.

Transcribing notes – take 1

Our first music editor of choice was VortexTracker 2 (VT2, using the PT3-format), as it’s PSG only, and easy to catch up. Or that’s what we thought. Now one thing to note is that it only knows of major notes (#), not minors (b).

(Heck, even the PLAY command in BASIC could handle both, e.g. “c#” and “d-“).

So to be able to actually read the myriad of notes correctly in both clefs with all those keys, a helper tool was created where all could be set-up easily, and then, display the note value in the VortexTracker format (using only major notes) was displayed when using the cursor keys up and down to move thr virtual note.

Having done that, there was another obstacle raising its head: VT2 only supports that virtual keyboard input, where a key on the keyboard is mapped to the white and black keys of a piano.

This could be solved quickly by adding a translation table to the note display, but the next bigger problem was already there: Finding the correct note and the corresponding key is one thing, but entering that into the editor is another. Some attempts were made to feed the VT2 editor window with virtual keys, but due to some functions being mapped only to mouse in VT2, that lead to nowhere. Consequentially, we added a “transcript output window” to the note reader app where the keys would be tracked, along with some special keys for marking measures and an auto-counter for the measure number to be able to recognize when the transcription gets out of sync with the original score sheet when a measure had been skipped during the VT2 input. The output window would also keep track of octave changes and insert the corresponding keys that need to be pressed in VT2 to adjust that.

Entering one whole channel from beginning to the end now was quite efficient: Reading the sheet, moving the note up and down, pressing the correct keys and continue until the end. The transcription output window kept filling up, then switch to VT2 and just repeat the keys that were in that window, and eventually switching to the next pattern after every measure.

However, the faster we progressed, the faster were the next problems stacking up:

The note lengths needed to be synchronized manually, which required reading the score sheet a second time when entering the notes. The patterns had to be navigated manually. Repetition loops would have to be entered manually in the pattern list. Duplicate patterns were not identified automatically – and ignoring it would only increase the size of a song unnecessarily. This needed to be repeated for each channel, and how the heck can one choose the right one of up to 8 notes and reduce them to 3. What if – at the end – the wrong note was selected for a channel. How would we even know if a different note would sound better or worse for the reduction? How could this be tested, when VT2 only allows 3 channels to be present? Could we do a 6-channel version later or would that duplicate the work? And what about ultimately converting the music to a wonderful 9 channel FM piano music later?

Transcribing notes – take 2

Problems over problems, questions over questions. Another approach needed to be done. A radically more efficient approach.

The desired goal was to be able to enter all notes somewhere and create the VT2 song data from there. With options to retry with different channels, and options to output to different formats later. Also, entering notes should be quick and efficient, also for a non-musician.

From all the tools – commercial and free – that were evaluated, one tool stood out by a huge margin in all requirements: Denemo – Free and Open Music Notation Editor. Not going into praising mode here, and there’s a learning curve and some shortcuts that need to be configured, but once that is done, a ~80 measure piano piece can be hacked in with keyboard and mouse in under 3 hours. By a musical noob! And no, I’m not affiliated with the product.

Above: “Buzzer Rag” in Denemo

One of the major concerns was the output, we needed a format that is either well documented or so easy to parse that it requires no documentation. It turned out that the Lilypond format (extension .ly) is the latter and perfectly suited for further processing, and Denemo natively supports it. It is a plain text format that looks very similar to the Music Macro Language (MML) used by MSX-BASIC.

MvmntIVoiceI = {
         <c '' c'''>16 ees'' aes'' <des '' aes '' des'''>~  <des '' aes '' des'''> aes'' <d '' d'''> aes''\AutoBarline
         <ees '' aes '' ees'''>8. <d '' aes '' d'''>16 <des '' aes '' des'''>8 <c '' aes '' c'''>\AutoBarline
         <d '' c'''>16 aes'' bes'' <d '' aes '' c'''>~  <d '' aes '' c'''> aes'' <d '' aes '' c'''>8\AutoBarline
         <des '' g '' bes''> r <ees '' g '' bes '' ees'''> r\AutoBarline
%5

Above: Denemo export (Lilypond format)

So we wrote a tool that can read and parse the .ly files, along with a processor that squeezes the multi channel notes to max. 3 PSG channels and has some selection logic to priorize some voices over others, and an exporter that writes a VT2 text file.

[Pattern0]
....|..|C-6 .... ....|D#3 .... ....|C-5 .... ....
....|..|D#5 .... ....|--- .... ....|R-- .... ....
....|..|G#5 .... ....|--- .... ....|--- .... ....
....|..|C#6 .... ....|E-3 .... ....|C#5 .... ....
....|..|--- .... ....|--- .... ....|--- .... ....
....|..|G#5 .... ....|--- .... ....|R-- .... ....
....|..|D-6 .... ....|F-3 .... ....|D-5 .... ....
....|..|G#5 .... ....|--- .... ....|R-- .... ....

[Pattern1]
....|..|D#6 .... ....|F#3 .... ....|D#5 .... ....
....|..|--- .... ....|--- .... ....|--- .... ....
....|..|--- .... ....|F-3 .... ....|--- .... ....
....|..|D-6 .... ....|--- .... ....|D-5 .... ....
....|..|C#6 .... ....|E-3 .... ....|C#5 .... ....
....|..|--- .... ....|--- .... ....|--- .... ....
....|..|C-6 .... ....|D#3 .... ....|C-5 .... ....
....|..|--- .... ....|--- .... ....|--- .... ....

Above: Converter export (PT3-TXT format)

We are not sure if the many weeks spent in researching and developing this workflow saved any minute over, let’s say, analyzing, hand-selecting, entering and listening every single note one-by-one in VT2 from the beginning. But then, that’s the spirit of a developer: If you cannot resolve a task, create tools that resolve the task for you 🙂

XII. Tutorials

The tutorials are categorized in a whole tutorial level, where the tutor will guide the player through the basics of the controls and the game features, as well as teach the rules of the dice game, and micro tutorials that are triggered during the regular game whenever something new is available or a specific event was happening.

The tutorial level is purely optional and can be played over and over. The in-game tutorials are shown only once. Their “shown” state is also stored in the save game (code), so they won’t get triggered again.

As some of the tutorials are interactive, the underlying engine has been designed to always run in parallel to the game engine, so it can be issued in all screens and interact with the player, e.g. waiting for triggers or hint at areas of required action. The dynamic pattern management introduced with the font system helped to mix the tutorial assets (tutor’s head, speech bubble and the reserved patterns for the actual texts) dynamically at runtime in the screens where they are triggered.

XIII. Trivia

Winnable dice-sets order

The intended sequence of the dice sets was different at the beginning – it should have matched the base colors and characteristics of their original owners (from whom the player gets the dice from):

1. The green Zen cubes as the default dice (which the player gets from the dojo owner, Dolus Ictis)

2. The blue Chaos cubes from the boss at the end of the 3rd league (Ghostrider)

3. The red Speed cubes from the boss at the end of the 2nd league (Mexican)

The green Zen cubes roll in a perfect organized and satisfying order – almost predictable (although it ain’t), and would fit perfectly to a tutor. The blue Chaos cubes are the total opposite, they roll and stop totally out of order and sequence. They fit better to a Criminal.

Problem 1: As the palette of the bright green colors on the MSX-1 are very similar, almost indistinguishable, the lighting effect on the dice during the roll was almost invisible to the eye, whereas it was really shining with the blue Chaos dice.

Problem 2: If the player already gets the Zen dice, which are rolling beautifully in total harmony, as his initial set, what motivation would he have to win a chaotic rolling set of dice? Or in other words: Would any Fighter class character in a Role Playing Game get the most beautiful helmet at the very beginning for free, just to loot a rusty bucket from the first boss?

The Speed cubes were perfect as the trophy from the 2nd league boss. They will be likely the pro player’s favorites, as the duration of the rolling sequence is cut in half, and thus, the remain the dice of choice to stick with for most of the time. Players will play a lot of matches in the first league, so they should have a way to make the rolls fast by the time they get there.

In the end, and against our pursuit to make things matching, we swapped the order of the Zen and Chaos cubes into a sequence that is favorable to the progression of the game, at the cost of sacrificing the thematic.

The 6th dice

As the “legal” cheats in the game are bound to the opponents being drunk, it was a natural choice to do something with the well-known effect of “double vision”: allowing to roll with a 6th dice, while the opponents think they only see the additional dice because of their drunken state.

While this was very tempting, it required an overhaul of the logic how the game would score a player’s roll in the dice board. There are now 6 dice in the roll, but scoring is only allowed with 5.

Example:

With a roll of 1,1,1,6,6,6, if the player chooses the category 3-of-a-kind, the score could be 1+1+1+6+6 = 15 pts, or 1+1+6+6+6 = 20 pts. And it cannot be 1+1+1+6+6+6 = 21 pts.

Computing the score for 5 dice is straightforward. For fixed score categories, there are strict rules. For sum score categories, it’s the sum of the matching dice, and optionally a strict rule (e.g. 3-of-a-kind will only score the sum if there are at least 3 equal dice). With 6 dice, the rules cannot be applied so strict anymore, and in some cases, there are multiple ways to compute the score, with different results, as in the example above. And it’s without question that the player expects the game to give him the 20 pts, not the 15.

Easter eggs

You can drive Dolus Ictis patience to its limits. Persist to refuse buying the second music sheet when you are requested to do so.

The “Southern Borders” tournament in the tournament selection screen shows the big wall to Mexico. We don’t like walls. But it was tempting to give it a cameo appearance in a scene over a hundred years before it was built.

Leave a Reply