Burger Party 0.13

Burger Party 0.13 is out! In this version I finally gave the Pirate world an “end boss”. The US world had a wrestler, the Pirate world has a pirate chief, complete with a parrot on his shoulder:

Pirate Boss

This version also comes with some new achievements:

  • Three “All stars” achievements: finish all levels of world 1, 2, 3 with three stars.
  • Three “Perfect” achievements: get a perfect in all levels of world 1, 2, 3.

It’s also been a long time since I last added an ingredient. Starting with 0.13, you can now spice your burgers with mustard!

Mustard!

A few other minor changes have been done as well: If you speak French, then you’ll be happy to know the game is now fully translated. Also, a short sound is now played in the game over screen.

The game is getting solid now. I don’t want to claim I am 90% done because everybody knows the last 10% take 90% of the time :), but my list of things which must be done before a first release is shrinking. I hope you like this this new version.

Introducing Linguaj

As I mentioned in my post about Burger Party 0.12, I put together a gettext-based system to handle translations. This post explains how it works and how you can use it in your own projects.

Super Quick Introduction to Gettext

In case you are not familiar with it, here is how gettext works. You start by wrapping all strings you want to make translatable in a gettext() function call, like this:

printf(gettext("Hello, %s\n"), name);

Since this is quite verbose, it is very common in C to create a macro to replace the gettext() calls:

#define _(x) gettext(x)

Which allows to shorten the code to:

printf(_("Hello, %s\n"), name);

Then you run a tool called xgettext on your code. This tool looks through your source for calls to the functions used to mark translatable code and lists all the collected strings in a file named a PO Template (usually with the “.pot” extension). This is a text file with a bit of meta data at the beginning and then a series of lines, one for the original text and another for the translation. It looks like this:

msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-03-27 22:46+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#: src/hello.c:12
msgid "Hello, %s\n"
msgstr ""

#: src/utils.c:24
msgid "Setting up engine\n"
msgstr ""
...

You can turn this template into a PO for your language (either by copying it and filling the blanks, but preferably with the msginit tool). For example to translate our helloworld program to French we would create “helloworld-fr.po” from “helloworld.pot” and translate its content. Translation can be done by hand or using specialized tools like Poedit.

Now that you have your “helloworld-fr.po”, you run it through the msgfmt tool to turn your text-based “helloworld-fr.po” file into an optimized, binary “helloworld-fr.mo” file. Once this file is installed at the right place, you need to add another call at the beginning of your program to load it. Now all calls to _() return translated strings if they are available.

Why Gettext Rather than the Java ResourceBundle System?

For two reasons:

The first one is I find it more pleasant to work with real strings in my code than with constants. Especially when the strings contain place holders such as the “Hello, %s\n” string from my example.

The second and more important reason is that the ResourceBundle system does not support plural forms as well as gettext. Plural forms can be tricky: some languages have different plural forms depending on the number of elements to represent, for example Polish uses one plural form when the number is between 2 and 4 and another form when the number is between 5 and 21. Gettext handles this by providing special functions for plural forms.

Again, an example is probably the best way to explain this. Let’s borrow it from gettext documentation. Imagine you want to show a number of files. A crude implementation would be something like this:

printf(_("%d file(s)\n"), nb);

Not very nice, we can change it like this:

if (nb == 1) {
    printf(_("1 file\n"))
} else {
    printf(_("%d files\n"), nb);
}

Works fine in English, but won’t work for Polish as I explained. It won’t work for French either, because French does not use an ‘s’ if nb is 0. Instead we can do this:

printf(ngettext("%d file\n", "%d files\n", nb), nb);

The ngettext() function takes nb into account to return the correct string. Then printf() can format the string, replacing the %d with the value of nb. If we generate a .pot file from such code, we get this:

msgid "%d file\n"
msgid_plural "%d files\n"
msgstr[0] ""
msgstr[1] ""

And if we create a Polish .po file for it, we get this:

msgid "%d file\n"
msgid_plural "%d files\n"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""

One just have to provide the proper plural forms. Tools like Poedit are aware of these and adapt their user interface for plural forms, depending on the language.

Using Gettext in Java

As I said before, msgfmt turns .po files into binary .mo files, which can be used from C code. msgfmt also provides a Java mode when called with the --java2 option. In this mode it generates a temporary .java file, compiles it with the default javac and outputs the resulting .class file. The class in this file inherits from ResourceBundle, so if you don’t care about plurals, you can use it like a plain ResourceBundle, except constants are replaced with real English strings.

gettext provides libintl.jar for proper plural support. This .jar provides an API similar to gettext C API. It basically exposes the JAVA equivalent of gettext() and ngettext() (see https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/javadoc2/).

What About Android?

This sounds simple enough, but then comes Android-specific problems.

My first problem was the .class file created by msgfmt. It won’t load because it uses the JVM bytecode, not Android Dalvik .dex files. I made a patch for the tool to let it save the .java file instead, it has been applied recently, but is not yet available in the current release version.

Then comes a trickier problem: gettext is released under the LGPL 2.1 or later license, Burger Party is a proprietary game. The LGPL allows one to dynamically link a LGPL library to a proprietary program. It forbids static linking to ensure the user is able to replace the LGPL code with another version. Using a .jar in a .apk is not static linking, but it’s not really dynamic linking either: the .jar is turned into a .dex and packed inside the .apk. I believe the spirit of the LGPL license would allow it to be used in Android .apk files but it’s a gray area.

Searching the Internet for this topic brings many discussions pondering whether it is legal or not, where everybody says “but I am not a lawyer”. Since I wanted a more authoritative answer, I reached out to the gettext maintainers, who pointed me to the FSF. Following the instructions from their licensing page, I sent them an email.

Alas, questions asked by proprietary software developers are answered by paid support. They want $150 to answer my question. I understand they do not want to provide law advice for free to proprietary companies, but I find it quite sad that such a generic question does not get answered. $150 is not much, but I am not even sure I am going to make that much money with Burger Party, so I’d rather avoid it for now.

Finally, Introducing Linguaj

I already had a wrapper around gettext to provide a nicer API, so I decided to extend it to be able to use .po files. After all, the licensing problem is only with libintl.jar, not with the other gettext tools. Thus came Linguaj, an Apache 2.0 licensed Java API as well as tools to use .po files in your Java application or Android game.

The API is similar to gettext, yet a bit more convenient. Using it looks like this:

import static com.greenyetilab.linguaj.Translator.tr;
...
System.out.println(tr("Hello %s", name));

As you can see, it provides a static tr() method and supports message formatting so you don’t have to wrap the resulting string in a call to String.format().

It also supports plural as well:

import static com.greenyetilab.linguaj.Translator.trn;
...
System.out.println(trn("%# file", "%# files", nb));

Here we use trn() instead of tr() to get plural support. The third argument is the number we need to take into account to decide which plural form to return, and the special %# placeholder gets replaced with the value of this number. This is more convenient than ngettext() which requires you to pass the number twice: one time to ngettext(), another time to the string formatting code (as can be seen in the ngettext() example above).

You can use the xgettext tool to extract the strings. The invocation I use is:

xgettext --from-code=utf-8 --keyword=tr --keyword=trn:1,2 -o po/messages.pot *.java

What’s important here is the use of --keyword=tr and --keyword=trn:1,2 to tell xgettext which method names to look for.

Once you have your .po file, you must use po-compile to create a .java file usable by Linguaj. This Python-based tool is available in the scripts directory of the repository.

Then, just include the file in your project, together with Linguaj Java code (either generate a .jar using the Ant script or simply copy the two Java files) and you should be good to go.

One final note: Linguaj initializes itself when Translator.tr() or Translator.trn() is called for the first time, but you can call Translator.init() explicitly. This is handy if you want to force the use of a different language. You can call this method several times, making it possible to change languages while your code is running (useful for games). The example provided in the repository demonstrates this.

That’s it, I hope you find Linguaj useful for your translation needs. You can get it from its GitHub page.

Burger Party 0.12: Good Bye Mini-Games, Hello Achievements!

Time for a new release of Burger Party. In this version I made the difficult decision to remove the mini-games. The reason behind this is I was worried it would take way too long to get them to a level of quality I consider good enough for a public release.

I initially decided to add the mini-games as an incentive for players to play the game again, instead of introducing more classical incentives like achievements. My reasoning was that getting a small game was a more satisfying reward than random emblems. Retrospectively I think it was a mistake. Last month I spent quite some time with the family playing Zombie Tsunami. I can attest achievements are powerful motivators :-). This was the last push which made me decide to replace mini-games with achievements.

Achievements

I originally planned to have Burger Party in English only, using as little text as possible to keep it usable even if the player does not speak English. I still want to keep the amount of text to the minimum, but one can’t avoid text to explain achievements, so I had to dive into translations. I like the gettext way of translating better than the Java, constants everywhere, way. Therefore I came up with a custom gettext-based system to handle translations. I plan to release it later under a permissive license. So far only the achievements are translated and they are available in English and in French. The final game will be fully translated, and I hope I can get at least a Spanish translation ready in time.

The game currently features a little dozen achievements, but I already have a few others to implement for the next version. If you have cool ideas for achievements, I would love to hear about them!

That’s it for this version, I hope you like it!

PS: If you liked the Burger Vaders mini-game, you might be less sad to learn it is likely going to serve as the base for my next game… but I refrain myself from starting working on it for now: I want to finish this one first!

Burger Party 0.11

Meh, ads

This new version is a bit special. It is the first one which adds something I was not really looking forward to add: ads. I thought about this for a long time. I don’t like ads much so my initial plan was to sell Burger Party for a small fee. I changed my mind after I went through the “Top Grossing Android Apps” category on Google Play Store: the first non-free app there is #38! And this app is Minecraft, which is massively well known. Clearly, Android users do not like to pay for applications. It might be different on other markets: for example Blackberry users seem to be more willing to pay for games. If/when I publish Burger Party on Blackberry World, I might try to sell it there, instead of relying on ads.

Still, I want this game to be kid-friendly, and kids should not be exposed to advertisement when it can be avoided. Therefore my plan is to add an In-App-Purchase to let you remove the ad. This is not done yet though, and I may postpone this until after the first public release. Right now my focus is on getting Burger Party out as soon as possible.

Right now there is only one ad. It shows up under certain circumstances when you start the game. The current settings are: it won’t show up for the first 8 plays, and then it won’t show up more often than once every 12 minutes. I plan to adjust those settings based on your feedback: I don’t want ads to be too intrusive. Actually, the main reason for introducing ads early is to have enough time to tweak them before the public release.

I made a bit of research on ad networks and decided to use Heyzap for now. This ensures you get only game-oriented ads, which I find less annoying than ads trying to sell you clothes, travels, cars…

Cut scenes

This new version does not bring a lot of exciting changes but ads is not the only new “feature”: I also finally started to work on cut scenes. I added a short cut scene which appears when you finish a world, showing an airplane travelling to the new game area.

Travelling to Japan

I am not entirely happy with the map for now, so I may redo it later to give it a more cartoonish appearance. I also need to put together a longer cut scene for the end of the game.

New permissions

When you install this new version, you will notice Burger Party now requests a few permissions. It needs the permission to reach the network to download ads. It also ask for the permission to write to the sdcard. This is because of a new logging facility: a log is now created in the /burgerparty folder on your sdcard. I hope this helps track down difficult-to-reproduce issues.

That’s it for this new version, hope you enjoy it!

Burger Party 0.10

Here is a new version of Burger Party.

The main change in this new version is the new way to gain stars. You used to get stars depending on your score, this has changed: in this new version, customers will now pay you between 1 and 3 coins, depending on how satisfied they are. When you have enough coins, you get a star. Stars cost more and more as you progress through levels.

Collecting coins

I was fast enough to get 3 coins from my first customer, but it looks like this one is only going to give me 1 or 2…

Additionally, if you manage to satisfy all customers of a level, you now get a “Perfect” ribbon to highlight your outstanding performance.

Perfect ribbon

Another important change is the size and variety of burgers: in advanced levels, customers will now order a mix of small and large burgers, and some burgers may have a middle bun.

I also worked a bit on the Japan world. It no longer uses a place-holder character for its “unlocked item” screen. Instead a Japanese cook will tell you about unlocked items:

Japan unlocked item character

Some work has been done on mini-games as well.

Air Burger received a new set of tiles, and the “bad cheese” on the ground has been replaced by proper spikes.

Air Burger

A new enemy type has been added to Burger Vaders: a burger which looses one ingredient each time you hit it. Sounds have been improved, and extra guns are now temporary, in an effort to make the game a bit more difficult.

Burger Crush now has sounds when you destroy items. It is also a bit more forgiving: destroying 3 items now gives you a one second bonus: it used to only give you a time bonus if you managed to destroy 4 items or more at a time.

Finally some minor changes made it in as well:

  • A “mute” button to turn off music and sound effects has been added to the start screen and to the pause overlay
  • Overlay screens no longer have blurry borders
  • The yeti on the loading screen is now correctly scaled

Get it now!

PS: The sharpest eyes among you might have noticed I went from 0.8 to 0.10. 0.9 was expected to hit the Internet yesterday, but I found an annoying regression in it. Since it had already been uploaded on Google Play, I had to bump the version number.

Burger Party 0.8

Happy new year!

To celebrate the arrival of 2014, here is a new version of Burger Party.

The main change in this new version is the introduction of mini-games. Each world now comes with one mini-game, which you can unlock by collecting stars.

Mini games

Some user testing revealed the sandbox mode was confusing for first-time players: they often selected the sandbox mode instead of the “US World” button. To avoid this, the sandbox mode now requires 3 stars to unlock, removing the question of which button to press when presented with the world screen for the first time.

World Screen, first start

I was also annoyed by the overall appearance of the US world, it looked too grey. I fixed this by adjusting the counter and the ceiling to be more colorful.

More colorful counter

The Japan world received some work as well: the ninja has been redrawn to use the same flat drawing style as the other customers, and a new customer has been introduced: a Japanese girl in traditional costume, which I wrote about earlier.

Japanese girl

Finally some smaller changes made it in as well:

  • An about screen has been added.
  • The background of the new item screen is now animated.
  • Star count is now visible in the world list screen.
  • libgdx, the library used to develop the game, has been updated to version 0.9.9.
  • Images are now more compact, the game should use a bit less space.

Get it now!

Japanese Girl

This weekend I drew a new customer for the Japan world of Burger Party: a Japanese girl in traditional costume.

As usual, I started with a sketch:

Japanese girl sketch

Then redrew it with Inkscape. I eventually decided not to draw the sunshade as I was worried it would hide the other customers too much. Here is the first version from within the game:

Japanese girl in game

Hope you like it! This new customer will be part of Burger Party 0.8.

Burger Party 0.7

Here comes a new version of Burger Party! This new version comes with a few significant changes.

First, Burger Party now has a new world: Japan. You might remember I wrote earlier about drawing its background, it is finally in. This new world is far from finished, though: it is missing customers, a boss, a “unlocked item” image and some more world-specific ingredients, but then version number is only 0.7, so I think it’s OK…

Japan World, showing customers waiting in line and new rice toast

The second change is something I have been willing to implement for a long time: world-specific ingredients. Some ingredients can now have world-specific versions. For example the coconut juice is the Pirate-world version of the soda, the rice-toast is the Japan-world version of the toast.

The third significant change is the way customers appear. In previous versions, waiting customers were all staying next to each others, which was a) not very realistic b) prevented you from knowning how many customers you still had to serve to complete the level. In version 0.7, customers are now waiting in line, solving both problems.

Finally, I received a new version of the music from my musician brother, the melody in 0.6 was nice but very short, this new version is a much longer piece, which I am sure you are going to enjoy!

As usual, a few minor changes have made it in as well:

  • A new burger item has been added: fish
  • The difficulty in the levels is a bit more progressive. Still some work to do, though
  • When the game is restarted, it now correctly unlocks the level after the last completed one (Thanks to Mathieu for reporting this bug)
  • The gradients which appear in the customer bubble when the order is too tall has been fixed to always stretch to fill the bubble width

Get it from the usual place!

Japan world background done

This weekend I redrew the background for the Japan world, based on my previous sketch.

Here it is, I think it turned out reasonably well. Hope you like it!

Japan background

Japan world background

I started working on a third world for Burger Party: Japan. I don’t have much for now, except for the ninja I drew a long time ago, which I need to redo using flat shading.

Yesterday I did a bit of sketching and came up with this drawing for the background:

Sketch for Japan background

Next step: redrawing it with Inkscape.