I enjoy story-telling games. I have various story telling games such as Gloom, a game called StorySoup I created which is loosely based on Nanofictionary, The Extraordinary Adventures of Baron Munchausen, and various heavily modified versions of Atlas Games’ Once Upon a Time. Of course the natural thing to do was to come up with my own game. This has a working title of “Story Soup” and about 600 cards … which means to shuffle the cards effectively you need pretty big hands. I have yet to finalise this, but in the meantime I was keen to use it to generate some plots for some stories.

Seeing as I had the card contents on a bunch of files, writing a python script to deal a hand was straight forward. One thing lead to another and I realised the I could use the same process to generate a hand of cards in a nice looking spread on a page in Scribus all ready for printing off. This script is based on the script outlined in this article: A Scribus Script to Distribute Images Across Page

So here are the scripts I developed. They are not all that user friendly but easy to modify to suit your own purposes. They are designed to run on a bunch of text files sitting in the same directory as the script.

Input

The scripts are automatic once they have been set going and so any files that need to be included need to be defined in the scripts themselves.

The files the scripts use to load content from are;

These are a text list of story elements. Each line is a separate story element. For instance here is a fragment from the Characters.txt.

Fragment of Characters.txt

So you can see from this example that serious stories are probable not going to be that common.

For the Scribus script there are some corresponding files for the background images. These were all strictly sized to fit the layout used in Scribus. Again, please refer to this article: A Scribus Script to Distribute Images Across Page

Standalone Python Script

The script that can be used with any python installation is listed below. You can download it from here: StoryCard_Deallerv2.py

# StoryCard_Deallerv2.py
# The following is a small utility to deal out a selection of cards from the
# Story Soup deck.

import os
import random

CardCollection = []  # List to contain all the story cards.

fnames = ["Challenges.txt","Characters.txt","Events.txt","Locations.txt","Objects.txt"] # List containing the source filenames for the different card types.

CardItems = len(fnames)


for CardType in range(0,CardItems):
    WorkingFile = open(fnames[CardType],"r")
    Filelines = WorkingFile.readlines()
    WorkingFile.close
    for line in Filelines:
        CardCollection.append([CardType,line])

# At this stage we should have a list with all of the cards and their type appended.  We need a seperate
# list for the ending cards.

EndingList = []

WorkingFile = open("Endings.txt","r")
Filelines = WorkingFile.readlines()
WorkingFile.close
for line in Filelines:
    EndingList.append(line)

StoryCardNo = 8 # Number of story cards to deal
EndingCardNo = 2 # Number of ending cards to deal.

StorySelection = random.sample(CardCollection,StoryCardNo)
EndingSelection = random.sample(EndingList,EndingCardNo)

print(StorySelection)
print(EndingSelection)

#Output this to a new text file.

DealtCardsFile = open("DealtCards.txt","w")
DealtCardsFile.writelines("Story Cards\n")
for line in StorySelection:
    CardCategory = fnames[line[0]]
    CardCat = CardCategory.split(".")
    CCateg = CardCat[0] + ": "
    Entry = CCateg + line[1]
    DealtCardsFile.writelines(Entry)
    
DealtCardsFile.writelines("\n\n")
DealtCardsFile.writelines("Endings\n")
DealtCardsFile.writelines(EndingSelection)
DealtCardsFile.close()

The way it works is that it loads the contents from each text file (except Endings.txt) into a single list together with a number that represents the type of card. It does the same with the ```Endings.txt`` but into a separate list.

From those lists it randomly selects a defined number of story element cards and ending cards and displays these in the terminal as well as writing them to a new text file. The two lines used to define how many cards are selected are;

StoryCardNo = 8 # Number of story cards to deal
EndingCardNo = 2 # Number of ending cards to deal.

Here are two examples of the output file content;

Story Cards
Challenges: They were stuck.
Characters: A golem made from something discarded.
Locations: a spaceship
Locations: An alternate plane / the afterlife etc
Events: A competition is won
Events: A summoning / summons
Events: a chase
Objects: Container


Endings
He saw the error in his ways and continued with even greater enjoyment.
When she put it on, everyone bowed their heads and submitted to her.

and another deal.

Story Cards
Objects: a bomb
Objects: A painting
Challenges: They were bored
Events: A treasure is discovered
Challenges: ... misled
Events: A project is completed
Challenges: ... disaster will occur if one of your characters gets hungry.
Objects: Underwear


Endings
They signed the deal then realised their mistake
The mysterious stranger rode off into the sunset, their final words hanging behind them "That was the wrong question."

Do they sound like starters for stories to you? Yep. I think so too. The first one looks like it would be a pretty daft story, but the second one has the potential to be a bit more edgy …with a bit of work.

The Scribus Script

The script above is the heart of this script. The difference is the use of the Scribus python functions to distribute the text around the page and place the appropriate background images for each card type. The script works in Scribus 1.4.3. I have yet to try it in the new Scribus 1.5. Depending on the fonts you have loaded on your machine you may need to alter line 121 scribus.setFont('Algerian Regular',frame) to one of your installed fonts - e.g. scribus.setFont('Arial Bold',frame).

Download the script from here: StoryCard_DeallerScribusv0.py

#!/usr/bin/env python
"""
StoryCard_DeallerScribusv0.py
The following is a small utility to deal out a selection of cards from the
Story Soup deck.  This variation uses an index rather than the Card type name
The script then pulls up the appropriate background image and drops them into
A sheet of 10 cards.

You will need to ensure there are enough pages in your document
to hold all of the cards you generate.

    @author: Hamish Trolove
    @website: www.techmonkeybusiness.com
    @copyright Creative Commons - by nc sa
  
"""


import os
import random
import sys


try:
    import scribus
    
except ImportError:
    print ("This script only runs from within Scribus.")
    sys.exit(1)
try:
    from PIL import Image
except ImportError:
    print ("Unable to import the Python Imaging Library module.")
    sys.exit(1)
    
    

CardCollection = []  # List to contain all the story cards.

fnames = ["Challenges.txt","Characters.txt","Events.txt","Locations.txt","Objects.txt"] # List containing the source filenames for the different card types.
Imgnames = ["Challenges.jpg","Characters.jpg","Events.jpg","Locations.jpg","Objects.jpg","Endings.jpg"] #List of the background image names for the corresponding card types.
CardItems = len(fnames)

multTx = 0 # Multiplier for the column position
multTy = 0 # Multiplier for the row position
pageInd = 1 # Page number
w = 55 #Width of Image frame
h = 90 #Height of Image frame
scribus.gotoPage(pageInd) # Make sure it always starts from first page

StoryCardNo = 8 # Number of story cards to deal
EndingCardNo = 2 # Number of ending cards to deal.
NumCards = StoryCardNo + EndingCardNo #Number of cards to insert.



for CardType in range(0,CardItems):
    WorkingFile = open(fnames[CardType],"r")
    Filelines = WorkingFile.readlines()
    WorkingFile.close
    for line in Filelines:
        CardCollection.append([CardType,line])

# At this stage we should have a list with all of the cards and their type appended.  We need a seperate
# list for the ending cards.

EndingList = []

WorkingFile = open("Endings.txt","r")
Filelines = WorkingFile.readlines()
WorkingFile.close
for line in Filelines:
    EndingList.append([5,line])


StorySelection = random.sample(CardCollection,StoryCardNo)
EndingSelection = random.sample(EndingList,EndingCardNo)

print(StorySelection)
print(EndingSelection)

StorySelection.extend(EndingSelection)

# Now we create the Scribus page(s).  This is done by reading StorySelection and EndingSelection
# and using the indexes in them to find the appropriate background image.

for EntryCd in StorySelection:  #Each entry will be another list with the index and card text.

    if multTy > 1:
        multTy = 0
        multTx = 0
        pageInd = pageInd + 1
        scribus.gotoPage(pageInd)
    
    x = 11 + w * multTx  #first corner is 11mm across.
    y = 15 + h * multTy  #first corner is 15mm down.
    
# create an image frame and put the image into it

#The business card dimensions are known and so we are looking to drop image into this size frame.
    
    ImageFrame = scribus.createImage(x, y, w, h)
    #BkgndImg = Image.open(Imgnames[EntryCd[0]])   #Pull out the index and use it to get the image file name.
    scribus.loadImage(Imgnames[EntryCd[0]], ImageFrame)
    scribus.setScaleImageToFrame(True, False,ImageFrame)
    scribus.setFillColor("None", ImageFrame)
    scribus.setLineColor("None", ImageFrame)
    scribus.selectObject(ImageFrame)
    scribus.moveSelectionToBack()
    scribus.deselectAll()
    #BkgndImg.close()
  
  #Now add the text over the top.
  
    frame = scribus.createText(x+7.5, y+35, w-15, h-30)
    text = unicode(EntryCd[1], 'iso-8859-2') #Pull out the text for the selected card.
    scribus.setText(text, frame)  
    scribus.setTextAlignment(1,frame) # ALIGN_CENTERED = 1
    scribus.setFont('Algerian Regular',frame)
    scribus.setFontSize(14,frame)
    scribus.setLineSpacing(12,frame)
    
    scribus.selectObject(frame)
    scribus.moveSelectionToFront()
    scribus.deselectAll()
  

# Control to index around the page.
  
    if multTx > 3:
        multTy = multTy + 1
        multTx = 0
        x = 11
      
    else:
        multTx = multTx + 1

The output is a Scribus document which can easily be turned into a pdf.

To use this script, start a new document with a A4 page in a landscape orientation. In this case the margin are 11mm on the left and right and 15mm on the top and bottom.

Run the script by going to Scripts in the top menu bar and Execute Script. Navigate to the StoryCard_DeallerScribusv0.py which will need to be in the same directory as your story element files, and hit “OK”.

Crash! Bang! Wollop! We have our spread of cards. The original card designs were not done with an automated system laying out the text in mind, and so there is a bit of realignment needed – or more to the point I should standardise the layout between card types. But whatever, the script does it’s job.

So here is an example output.

Scribus Story Card Dealer Script Output

I think I would struggle to get anything out of this spread.

Anyway, I hope this is useful at least as a basis for someone else’s card dealler, story shuffler.


Divider

Story Time

OK, so as an amusing aside here is a little story I wrote using the following spread of cards dealt by the Scribus version of the script.

Reality Story card spread

and the story created using these cards is available here or click on the image below to open the pdf version of the story;

Header for the Reality Story


Divider

Creative Commons by-nc-sa

The stuff on this page is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.