My First Ludum Dare

After sitting on the sidelines and watching the past couple of Ludum Dare events, I am finally entering.

For those that do not know, Ludum Dare is a 48 hour game jam… more details on their site. I am looking forward to seeing what I can develop in 48 hours, and I do hope I can get enough completed so it can be played by others.

Right now, I am thinking of entering the Compo, and using Unity. This will also be my first attempt in getting a Unity game released, so add that additional stress to the mix.

For those that are interested, I plan on releasing whatever I develop on my page, and will tweet as much as I can throughout. I will not be live streaming, but I do plan on releasing a time-lapse video on my YouTube channel.

Ludum Dare 34 starts Friday, December 11 through Sunday, December 13.

Eating Your Own Dog Food

There’s a popular term in business called “Eating your own dog food”. In other words, “use your own products”. I had that opportunity during my recent upgrade to Tappy Holidays. I wanted to add a persistent high score and a game counter for achievements, and what better way to do that than with GBC Data Cabinet.

GBC Data Cabinet is a Corona SDK library I wrote to manage temporary and persistent data within your application. It’s on Corona’s plug-in store, and it’s free! Let me know what you think.

The following is the code I used to check for an existing persistent cabinet file. If it does not exist, then create one, and get it ready for upcoming data during game play. If persistent data does exist, then read the values.

CabExist = GBCDataCab.load("playerdata")

if CabExist == false then
    CabExist = GBCDataCab.createCabinet("playerdata")
if CabExist then
    highScore = GBCDataCab.get("playerdata", "highscore")
    if highScore == nil then highScore = 0 end
    intTotalGamesPlayed = GBCDataCab.get("playerdata", "gamesplayed")
    if intTotalGamesPlayed == nil then intTotalGamesPlayed = 0 end
    highScore = 0
    intTotalGamesPlayed = 0


Halloween Achievement in Pumpkin Patch Match

I guess Halloween is a perfect time for a bone-headed move. For those players of Pumpkin Patch Match, you may notice that there is an achievement that will be awarded if you play the game on October 31… today.

For some reason beyond my comprehension, the check for that achievement is coded incorrectly.  To get this achievement, you will need to do the following:

  • Play the game on October 31.
  • Get a score of 1.

Any other score will not reward the achievement at this time.

I am in the process of uploading an updated version of the game (version 1.1) to all the stores, but in the event that the game is not approved in time, please note that ending a game with a score of 1 will reward the achievement.

My apologies.

Announcing GBC Language Cabinet Plugin for CoronaSDK

GBC Language Cabinet is an easy to use way to display text in different languages. Simply specify which languages you are supporting, create or import tables of translated text, and use a basic function call to return the text in the correct language.

Several options exist to make your life even easier:

  • Text can be created with embedded keys. During the getText call, parameters can then be passed via a table. The returning text will be formatted to replace the keys with the parameters you passed. See below for examples.
  • You can import a comma-delimited file or a json file containing your text (including embedded keys!) instead of using multiple addText calls.

GBC Language Cabinet is available on the Corona Plugin Store.

Announcing GBC Data Cabinet for Corona SDK

GBC Data Cabinet, my free Corona SDK plugin, has been released on the Corona Plugin Store.

GBC Data Cabinet is used to create one or more data repositories, which is then used to store and recall data needed in your application. Data can be used throughout the application, without using global variables.

Data Cabinets can also be saved and recalled at a later time, which is ideal for keeping persistent data such as scores and other values that should not be reset between launches of the application.

For more information, check out the Corona Plugin Store, or view the plugin’s documentation.

Coding and Testing In-App Purchases in Corona – Part 1

Recently, I spent some time updating one of my apps, and spent a lot of time looking over my in-app purchase (IAP) code. Corona makes this very easy to implement, but there are some minor things to watch out for, especially if you are publishing to multiple stores. This page on the Corona site is a valuable resource, and hopefully this post will give you additional information.

Testing, on the other hand, is not as easy as you would think, since each store has their own way of handing this.

In this two part blog, I will outline my recent experiences in coding and testing IAP in Corona. Part one is all about coding IAP using Corona. We will cover testing in Part 2.

Coding for Various Stores
Three Stores, Three Libraries

Let’s configure the Apple, Google, and Amazon stores. First, we need to determine which library to use.

local store = nil
local platform = system.getInfo("targetAppStore")

if platform == "apple" then
    store = require ("store")
elseif platform == "google" then
    store = require("")
elseif platform == "amazon" then
    store = require ("")

Easy enough, find the target app store and load the library. Of course, make sure your build.settings file includes the Google and Amazon IAP plugins.

Next, we need to initialize the store library.  If using Composer, the scene:show or scene:create function is probably a good place for this.

if platform == "amazon" then
    store.init(platform, StoreListener)

Notice the difference?  The Amazon store does not require the store name.

That’s really all you need for set up. Next, let’s cover making purchases. This is also not too hard to code in Corona, but you need to be aware of what is returned to your listener for each store, since it is different. To get you started, we’ll build a simple store listener below.

When a user attempts to make a purchase, you need to let the store know, and you also need to listen for the store’s response.  The following code does this.

local function StoreListener(event)
    local transaction = event.transaction

    if transaction == "purchased" then
        -- successful purchase. Give user what they paid for

    elseif transaction == "cancelled" then
        -- User decided to cancel purchase

    elseif transaction == "failed" then
        -- D'oh, something went wrong. User could not purchase item.


-- Enter your purchase string here
local itemToPurchase = "com.mydomain.item"

-- make a purchase
if platform == "apple" then

Notice that the Apple store expects a table of items… even if there is only one. Amazon and Google do not support purchasing multiple items, so we pass in the actual string of the item we wish to purchase.

In the store listener, we are listening for a successful purchase, a cancel, or a failure. It’s up to you to code what you want to do when these events come in.

Notice that we must call finishTransaction to clean up, otherwise the store will not know that the transaction is complete. This is required for Apple, but no harm calling this for all other stores.

Restoring A Purchase

In the event a user had to reinstall your app, or in the event a person upgrades their phone, it’s a good idea to allow a user to restore their purchase. This is usually done when a user taps on a “Restore Purchase” button, and it’s as simple as calling store.restore() and listening for the “restored” event, with one exception. Google does not have a “restored” event. When restoring on the Google Play store, a “purchased” event is returned. So, how do you determine whether the “purchased” event is from a purchase or a restore? Let’s look at our modified store listener:

local function StoreListener(event)
    local transaction = event.transaction

    if transaction == "purchased" then
        if isGoogleRestore then
            -- Restore purchase code goes here
            -- successful purchase. Give user what they paid for

    elseif transaction = "restored" then
        -- Apple or Amazon restore code goes here

    elseif transaction == "cancelled" then
        -- User decided to cancel purchase

    elseif transaction == "failed" then
        -- D'oh, something went wrong. User could not purchase item.


-- define this variable 
local isGoogleRestore

-- place this in your restore button handler
if platform == "google then isGoogleRestore = true else isGoogleRestore = false end

Here it gets a bit tricky. For Apple and Amazon, you handle a restore by listening for a “restored” event. For Google, you can set a boolean value (isGoogleRestore) during your restore purchase code and listen for a “purchased” event. In the purchased event of your store listener, determine whether this is a Google restore or a normal Google/Apple/Amazon purchase by checking isGoogleRestore, and handle appropriately.

A Couple of Other Events

Amazon can send a “revoked” event, and Google can send a “refunded” event in cases where refunds were applied. If you listen for these, it’s again up to you to decide how to handle, but it basically involved removing the features that were previously purchased.


Consumables are items that can be purchased over and over again (coins, power-ups, etc). You should not and can not restore consumables via the store, but a user can buy these as many times as they want.

Google treats all items as non-consumable (Apple and Amazon allow you to create these items and identify them as consumable in their dashboards). Since non-consumable items can only be purchased once, how do we allow a consumable item to be purchased again?  Easy, with this piece of code:

if platform == "google" then
    -- call this for every consumable item you have in the store

This call may take some time, so I like to place it in my scene:show event (in Composer). You can listen for a “consumed” event in your store listener if you need to do some post-processing. If the user now decides to purchase a consumable item, Google will allow it.


This is the basis of coding IAP in Corona. I did not cover everything (such as loading products), but this should get you going. In a future blog post, we will discuss how to test IAP on each store.

Memory Lane – My first computer

I recently turned 50 and realized that I have been programming for over 35 years. There’s some interesting, funny, and stupid stories that pop into my head, so why not place some here. If there is some interest, I can post a few stories from time to time.

My First Computer

I was 15 years old in 1980. I visited a friend and he showed me a TRS-80 Model 1 that he had. We typed in a simple BASIC program from a book of games… it was something like “guess a number from 1 to 10”. We ran it, played a bit, and right then and there, I decided that I wanted to write software for a living.

It’s hard to describe how cool it actually was to “program” (ie: type code from a book) that computer for the first time. Of course, I had to get a computer for myself. I visited the local Radio Shack many times over the next few weeks, and decided I wanted the Model 3.

One weekend, my parents and I go over to the local Radio Shack and tell them we want to buy the Model 3 (looking back, that sucker was expensive.  It was $1599, that’s over $4500 today. Not sure how my parents were going to afford that, but they were very supportive and I guess they would “just find a way”). One of the store’s salespersons tells us that the computer salesman is not in today, but he will call us back on Monday. Bummer… guess I’ll wait until he calls.

He never does. I guess he never got my info, or didn’t really expect we would buy a computer, but in the meantime, I discovered the Atari 400, and we went out the next weekend and bought it. Thus begins my computer career. In hindsight, the Atari was a great choice over the Model 3. And a bit cheaper too, which I am sure my parents appreciated.