mirror of
https://github.com/Oreolek/gamebookformat.git
synced 2024-06-26 03:41:04 +03:00
Switched from JSON to simpler format.
Code is a bit of a mess at the moment. Might have to generate some JSON later anyway to make it easier to make playable game in browser.
This commit is contained in:
parent
bdc101a6db
commit
707d5692cb
|
@ -50,6 +50,7 @@ OUTPUT_FORMATS = [LatexFormat(),
|
||||||
HtmlFormat(),
|
HtmlFormat(),
|
||||||
DebugFormat()]
|
DebugFormat()]
|
||||||
|
|
||||||
|
|
||||||
def make_supported_formats_list_string():
|
def make_supported_formats_list_string():
|
||||||
return "Supported Output Formats:\n" + "\n".join(
|
return "Supported Output Formats:\n" + "\n".join(
|
||||||
[str(f) for f in OUTPUT_FORMATS])
|
[str(f) for f in OUTPUT_FORMATS])
|
||||||
|
@ -62,21 +63,34 @@ def format_gamebook(inputfilenames, outputfilename):
|
||||||
output_format.write(book, open(outputfilename, 'w'))
|
output_format.write(book, open(outputfilename, 'w'))
|
||||||
|
|
||||||
def parse_file_to_book(inputfile, book):
|
def parse_file_to_book(inputfile, book):
|
||||||
for name,contents in json.load(inputfile).iteritems():
|
name = None
|
||||||
paragraph = paragraphs.Paragraph(name)
|
number = None
|
||||||
if 'text' in contents:
|
text = ""
|
||||||
paragraph.addtext(contents['text'])
|
for line in inputfile.readlines():
|
||||||
if 'number' in contents:
|
if line.startswith('*'):
|
||||||
book.add(paragraph, contents['number'])
|
if name:
|
||||||
|
book.add(paragraphs.Paragraph(name, text), number)
|
||||||
|
number = None
|
||||||
|
text = ""
|
||||||
|
heading = line[1:].strip().split(' ')
|
||||||
|
if len(heading) == 1:
|
||||||
|
name = heading[0]
|
||||||
|
elif len(heading) == 2:
|
||||||
|
number = int(heading[0])
|
||||||
|
name = heading[1]
|
||||||
|
else:
|
||||||
|
raise Exception("bad paragraph heading %s" % str(heading))
|
||||||
else:
|
else:
|
||||||
book.add(paragraph)
|
text = text + " " + line.strip()
|
||||||
|
if name:
|
||||||
|
book.add(paragraphs.Paragraph(name, text), number)
|
||||||
|
|
||||||
def find_output_format(outputfilename):
|
def find_output_format(outputfilename):
|
||||||
for of in OUTPUT_FORMATS:
|
for of in OUTPUT_FORMATS:
|
||||||
if of.supports(outputfilename):
|
if of.supports(outputfilename):
|
||||||
return of
|
return of
|
||||||
raise Exception("Unsupported or unknown output format for %s."
|
raise Exception("Unsupported or unknown output format for %s."
|
||||||
% outputfile)
|
% outputfilename)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import argparse
|
import argparse
|
||||||
|
|
36
output.py
36
output.py
|
@ -2,8 +2,6 @@ import os
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from paragraphs import paragraph_refs_format
|
|
||||||
|
|
||||||
class OutputFormat (object):
|
class OutputFormat (object):
|
||||||
def __init__(self, extension, name):
|
def __init__(self, extension, name):
|
||||||
self.extension = extension
|
self.extension = extension
|
||||||
|
@ -29,19 +27,13 @@ class OutputFormat (object):
|
||||||
|
|
||||||
def write_paragraph(self, paragraph, shuffled_paragraphs, output):
|
def write_paragraph(self, paragraph, shuffled_paragraphs, output):
|
||||||
refs = []
|
refs = []
|
||||||
def paragraph_link_render(to_paragraph, shuffled_paragraphs):
|
refsdict = ReferenceFormatter(paragraph, shuffled_paragraphs,
|
||||||
s = self.load_template("paragraph_ref") % {
|
self.load_template("paragraph_ref"))
|
||||||
'nr' : shuffled_paragraphs.to_nr[to_paragraph],
|
formatted_text = paragraph.format(refsdict)
|
||||||
'from_nr' : shuffled_paragraphs.to_nr[paragraph]
|
|
||||||
}
|
|
||||||
refs.append(s)
|
|
||||||
return s
|
|
||||||
formatted_text = paragraph.format(shuffled_paragraphs,
|
|
||||||
paragraph_link_render)
|
|
||||||
print >> output, self.load_template("paragraph") % {
|
print >> output, self.load_template("paragraph") % {
|
||||||
'nr' : shuffled_paragraphs.to_nr[paragraph],
|
'nr' : shuffled_paragraphs.to_nr[paragraph],
|
||||||
'text' : formatted_text,
|
'text' : formatted_text,
|
||||||
'refs' : '\n'.join(refs) # hack for DOT output reallyn
|
'refs' : '\n'.join(refsdict.getfound()) # hack for DOT output
|
||||||
},
|
},
|
||||||
|
|
||||||
def write_end(self, book, output):
|
def write_end(self, book, output):
|
||||||
|
@ -64,3 +56,23 @@ class OutputFormat (object):
|
||||||
self.cached_templates[name] = template
|
self.cached_templates[name] = template
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
class ReferenceFormatter (object):
|
||||||
|
"There is probably a better way, but this hack seems to work."
|
||||||
|
def __init__(self, paragraph, shuffled_paragraphs, ref_template):
|
||||||
|
self.paragraph = paragraph
|
||||||
|
self.shuffled_paragraphs = shuffled_paragraphs
|
||||||
|
self.found = set()
|
||||||
|
self.ref_template = ref_template
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
to_paragraph = self.shuffled_paragraphs.from_name[key]
|
||||||
|
res = self.ref_template % {
|
||||||
|
'nr' : self.shuffled_paragraphs.to_nr[to_paragraph],
|
||||||
|
'from_nr' : self.shuffled_paragraphs.to_nr[self.paragraph]
|
||||||
|
}
|
||||||
|
if key in self.shuffled_paragraphs.name_to_nr:
|
||||||
|
self.found.add(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getfound(self):
|
||||||
|
return list(self.found)
|
||||||
|
|
110
paragraphs.py
110
paragraphs.py
|
@ -1,116 +1,16 @@
|
||||||
#!/usr/bin/env python2.5
|
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def paragraph_refs_format(s,
|
|
||||||
refs,
|
|
||||||
shuffled=None,
|
|
||||||
render_callback=None,
|
|
||||||
bad_callback=None):
|
|
||||||
"""
|
|
||||||
render_callback, if set, must be a function accepting a paragraph name
|
|
||||||
and a ShuffledParagraphs object. It must return a string formatting
|
|
||||||
of a link to that reference. Its use is for instance if you render
|
|
||||||
the paragraphs to HTML you can return a HTML link. It will only be
|
|
||||||
used if format %c is used.
|
|
||||||
|
|
||||||
bad_callback, if set, must be a function accepting a paragraph
|
|
||||||
name and a ShuffledParagraphs object. It may attempt to construct
|
|
||||||
a new Paragraph object standing in for a missing paragraph, if there
|
|
||||||
is a way to automatically construct some paragraphs in the game.
|
|
||||||
If it can not construct a paragraph it should return None or False
|
|
||||||
which will cause the formatting to fail and raise an exception.
|
|
||||||
"""
|
|
||||||
oi = 0
|
|
||||||
next_ref = 0
|
|
||||||
res = ''
|
|
||||||
i = s.find('%')
|
|
||||||
while i >= 0:
|
|
||||||
res = res + s[oi:i]
|
|
||||||
skip = 0
|
|
||||||
code = s[i+1]
|
|
||||||
if code == '%':
|
|
||||||
res += '%'
|
|
||||||
else:
|
|
||||||
if code == '(':
|
|
||||||
ei = s.find(')', i+1)
|
|
||||||
if ei < 0:
|
|
||||||
raise "bad format, missing ) after %("
|
|
||||||
ref = s[i+2:ei]
|
|
||||||
skip = len(ref) + 2
|
|
||||||
code = s[ei+1]
|
|
||||||
else:
|
|
||||||
ref = refs[next_ref]
|
|
||||||
if type(ref) in set([str, unicode]):
|
|
||||||
if ref in shuffled.from_name:
|
|
||||||
ref = shuffled.from_name[ref]
|
|
||||||
elif bad_callback:
|
|
||||||
created_ref = bad_callback(ref, shuffled)
|
|
||||||
if not created_ref:
|
|
||||||
raise "bad reference " + ref
|
|
||||||
ref = created_ref
|
|
||||||
if code == 'n':
|
|
||||||
res += str(shuffled.to_nr[ref])
|
|
||||||
elif code == 'c':
|
|
||||||
if render_callback:
|
|
||||||
res += render_callback(ref, shuffled)
|
|
||||||
else:
|
|
||||||
res += str(ref)
|
|
||||||
elif code == 's':
|
|
||||||
res += str(ref.name)
|
|
||||||
elif code == 'r':
|
|
||||||
res += repr(ref.name)
|
|
||||||
next_ref = next_ref + 1
|
|
||||||
oi = i + 2 + skip
|
|
||||||
i = s.find('%', oi)
|
|
||||||
return res + s[oi:]
|
|
||||||
|
|
||||||
class ParagraphItem:
|
|
||||||
def __init__(self, text, refs=None, title=None):
|
|
||||||
self.text = text
|
|
||||||
self.title = title
|
|
||||||
if refs:
|
|
||||||
self.refs = refs
|
|
||||||
else:
|
|
||||||
self.refs = []
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "ParagraphItem(%s, %s, %s)" % (self.text.__repr__(),
|
|
||||||
self.refs.__repr__(),
|
|
||||||
self.title.__repr__())
|
|
||||||
|
|
||||||
def format(self, shuffled=None, render_callback=None, bad_callback=None):
|
|
||||||
return paragraph_refs_format(self.text, self.refs, shuffled, render_callback,
|
|
||||||
bad_callback)
|
|
||||||
|
|
||||||
class Paragraph:
|
class Paragraph:
|
||||||
def __init__(self, name, text=None, refs=None, items=None):
|
def __init__(self, name, text):
|
||||||
self.name = name
|
self.name = name
|
||||||
if text:
|
self.text = text
|
||||||
self.text = text
|
|
||||||
else:
|
|
||||||
self.text = ''
|
|
||||||
if items:
|
|
||||||
self.items = items
|
|
||||||
else:
|
|
||||||
self.items = []
|
|
||||||
if refs:
|
|
||||||
self.refs = refs
|
|
||||||
else:
|
|
||||||
self.refs = []
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Paragraph(%s, %s, %s, %s)" % (self.name.__repr__(),
|
return "Paragraph(%s, %s)" % (repr(self.name), repr(self.text))
|
||||||
self.text.__repr__(),
|
|
||||||
self.refs.__repr__(),
|
|
||||||
self.items.__repr__())
|
|
||||||
def addtext(self, moretext):
|
|
||||||
self.text = self.text + moretext
|
|
||||||
|
|
||||||
def format(self, shuffled=None, render_callback=None, bad_callback=None):
|
def format(self, references):
|
||||||
return paragraph_refs_format(self.text, self.refs, shuffled, render_callback,
|
return self.text % references
|
||||||
bad_callback)
|
|
||||||
|
|
||||||
class ShuffledParagraphs:
|
class ShuffledParagraphs:
|
||||||
def __init__(self, as_list, from_nr, to_nr, from_name):
|
def __init__(self, as_list, from_nr, to_nr, from_name):
|
||||||
|
|
43
readme.org
43
readme.org
|
@ -3,6 +3,27 @@
|
||||||
A tool to format [[http://www.gamebooks.org/][gamebooks]] into various formats useful for playing the
|
A tool to format [[http://www.gamebooks.org/][gamebooks]] into various formats useful for playing the
|
||||||
gamebook on paper or a screen (or for debugging it).
|
gamebook on paper or a screen (or for debugging it).
|
||||||
|
|
||||||
|
** Supported Output Formats
|
||||||
|
|
||||||
|
| Name | Extension | Description |
|
||||||
|
|------------------+-----------+-------------------------------------------------------------------------------------------------|
|
||||||
|
| LaTeX | .tex | Useful to generate PDFs using pdflatex or whatever LaTeX tools you prefer. |
|
||||||
|
| Rich Text Format | .rtf | Supported because the Windhammer Prize requires it. |
|
||||||
|
| Graphviz DOT | .dot | Use with the Graphviz dot tool to generate a flowchart graph of all paragraphs in the gamebook. |
|
||||||
|
| HTML | .html | Play gamebook in browser. |
|
||||||
|
| Debug Plain Text | .debug | Plain text debug output of gamebook contents. |
|
||||||
|
|
||||||
|
More to be added.
|
||||||
|
|
||||||
|
** Gamebook Format
|
||||||
|
|
||||||
|
The input file expected by the formatgamebook.py script must be in a
|
||||||
|
format containing information about all paragraphs in the book
|
||||||
|
plus some optional metadata. The format should (when TBD) be documented
|
||||||
|
here, but it is not expected that most users would ever have to look
|
||||||
|
at the gamebook file, but rather use a higher level format or a
|
||||||
|
GUI tool.
|
||||||
|
|
||||||
** License
|
** License
|
||||||
|
|
||||||
Copyright (c) 2013, Pelle Nilsson
|
Copyright (c) 2013, Pelle Nilsson
|
||||||
|
@ -31,25 +52,3 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
** Supported Output Formats
|
|
||||||
|
|
||||||
| Name | Extension | Description |
|
|
||||||
|------------------+-----------+-------------------------------------------------------------------------------------------------|
|
|
||||||
| LaTeX | .tex | Useful to generate PDFs using pdflatex or whatever LaTeX tools you prefer. |
|
|
||||||
| Rich Text Format | .rtf | Supported because the Windhammer Prize requires it. |
|
|
||||||
| Graphviz DOT | .dot | Use with the Graphviz dot tool to generate a flowchart graph of all paragraphs in the gamebook. |
|
|
||||||
| HTML | .html | Play gamebook in browser. javascript to make fully playable not yet implemented. |
|
|
||||||
| Debug Plain Text | .debug | Plain text debug output of gamebook contents. |
|
|
||||||
|
|
||||||
More to be added.
|
|
||||||
|
|
||||||
** JSON Gamebook Format
|
|
||||||
|
|
||||||
The input file expected by the formatgamebook.py script must be in a
|
|
||||||
JSON format containing information about all paragraphs in the book
|
|
||||||
plus some optional metadata. The format should (TBD) be documented
|
|
||||||
here, but it is not expected that most users would ever have to look
|
|
||||||
at the gamebook JSON file, but rather use a higher level format or a
|
|
||||||
GUI tool.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
14
test.gamebook
Normal file
14
test.gamebook
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
*1 start
|
||||||
|
This is where the adventure begins. You can go on to the
|
||||||
|
next paragraph, see %(next)s or try the other instead, see %(other)s.
|
||||||
|
|
||||||
|
*next
|
||||||
|
This is the next paragraph. Go on to the end at %(end)s.
|
||||||
|
|
||||||
|
*other
|
||||||
|
This is another paragraph. You can try the next paragraph now,
|
||||||
|
see %(next)s, or go on to the end, see %(end)s.
|
||||||
|
|
||||||
|
*end
|
||||||
|
The end.
|
||||||
|
|
18
test.json
18
test.json
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"start" : {
|
|
||||||
"text" : "This is where the adventure begins. You can go on to the next paragraph, see %(next)c or try the other instead, see %(other)c.",
|
|
||||||
"number" : 1
|
|
||||||
},
|
|
||||||
|
|
||||||
"next" : {
|
|
||||||
"text" : "This is the next paragraph. Go on to the end at %(end)c."
|
|
||||||
},
|
|
||||||
|
|
||||||
"other" : {
|
|
||||||
"text" : "This is another paragraph. You can try the next paragraph now, see %(next)c, or go on to the end, see %(end)c."
|
|
||||||
},
|
|
||||||
|
|
||||||
"end" : {
|
|
||||||
"text" : "The end."
|
|
||||||
}
|
|
||||||
}
|
|
10
test.sh
10
test.sh
|
@ -1,18 +1,18 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
./formatgamebook.py test.json test.debug
|
./formatgamebook.py test.gamebook test.debug
|
||||||
cat test.debug
|
cat test.debug
|
||||||
|
|
||||||
./formatgamebook.py test.json test.dot
|
./formatgamebook.py test.gamebook test.dot
|
||||||
dot -Tpng test.dot > test.png && open test.png
|
dot -Tpng test.dot > test.png && open test.png
|
||||||
|
|
||||||
./formatgamebook.py test.json test.tex
|
./formatgamebook.py test.gamebook test.tex
|
||||||
pdflatex test.tex && open test.pdf
|
pdflatex test.tex && open test.pdf
|
||||||
|
|
||||||
./formatgamebook.py test.json test.html
|
./formatgamebook.py test.gamebook test.html
|
||||||
open test.html
|
open test.html
|
||||||
|
|
||||||
./formatgamebook.py test.json test.rtf
|
./formatgamebook.py test.gamebook test.rtf
|
||||||
open test.rtf
|
open test.rtf
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue