04/19/20 - Donning the Purple Event Card App

Tags Android Development Ren'Py Tabletop Games

app screenshot

I’ve been playing Donning the Purple again and within a few games I began wishing for a more diverse event card deck. I’ve been thinking about getting the PNP files for the Votes & Virtue expansion but I realized I no longer had the finish I used to build the base game of Donning Purple. Since there are only 5 to 8 new event cards applicable to a solo game (cards were added via Kickstarter stretch goals, so I’m not sure exactly what the cards are now), I decided to build an assistant app to deal random event cards and included the base game cards as well as the cards from the expansion PNP prototype.

I still haven’t found a painless way to extract individual cards from a card sheet, but these are 9-card sheets without gutters so I was able to use Gimp to crop to the cards, Image > Transform > Guillotine to Layers to extract the individual cards for each sheet, and then you have to drag and drop each card from the newly created layered into a main image OR you can save each as a unique file and extract it. Otherwise Gimp overwrites the extracted card images. I could not find a way to mass-rename layers or combine layered images without manually dragging and dropping the layers. This is more time-consuming than I’d like but it’s still faster than writing a program from scratch so there’s that

I used PNGoo to optimize the card images and the app filesize was still kinda big for what it is, so I used TinyPNG’s web app to reduce it further. It has file limits which are a little annoying, but it is the best optimizer I have found so far and it helped whittle the apk size down from about 45mb to 25mb. You could make a much smaller app with something like Kivy but the Windows build process in Ren’Py is so easy and painless it makes cobbling together personal apps like this trivial.

The code is straight-forward, Ren’Py cycles through the card images whenever you click the card image. You can skip cards that aren’t relevant (e.g. advanced cards, which I couldn’t be bothered to filter out). There is one special case in this game. The Heir Assassination card is returned to the deck if you don’t have an heir. I added a special button for that.

The app shuffles and displays the event cards when clicked, and it tracks how many cards you’ve completed so far. This could be a simple template for future deck management apps, especially if you want to try adding your own cards on the fly.

Ren’Py’s list_images function returns 3 extra strings at the end. I slice the list to remove those 3 extra items and avoid throwing a display error after I shuffle the card images.

## card images have been dumped in /images
init python:
    import random
    def return_to_deck(index):        
        new_index = renpy.random.randint(index,len(card_list))
        card_list.insert(new_index, card_list[index])    

label main_menu:
    return    

label start:
    $ card_list = renpy.list_images()  
    $ card_list = card_list[:-3]
    $ random.shuffle(card_list)
    $ current_index = 0

label display_cards:
    show screen cards(card_list=card_list)
    pause
    return

screen cards(card_list):
    modal True    
    default current_card = 1
    frame:
        xalign 0.5 yalign 0.5 xfill True yfill True
        vbox:            
            xalign 0.5 yalign 0.5 spacing 20
            text ("Current Event Card: [current_card]")
            imagebutton:
                idle card_list[current_index]
                hover card_list[current_index]
                at shrinky
                action [SetVariable("current_index",current_index+1),SetScreenVariable("current_card",current_card+1)]       
            hbox:
                xalign 0.5 spacing 50
                textbutton "Skip" action SetVariable("current_index",current_index+1)
                textbutton "Return to Deck" action [Function(return_to_deck,index=current_index), SetVariable("current_index",current_index+1)]
                textbutton "Quit" action Quit(confirm=False) xalign 1.0

transform shrinky:
    zoom 0.85

style button_text:
    size 30

style text:
    size 50