diff --git a/formatgamebook.py b/formatgamebook.py index cdcaa8d..421c151 100755 --- a/formatgamebook.py +++ b/formatgamebook.py @@ -50,6 +50,7 @@ OUTPUT_FORMATS = [LatexFormat(), HtmlFormat(), DebugFormat()] + def make_supported_formats_list_string(): return "Supported Output Formats:\n" + "\n".join( [str(f) for f in OUTPUT_FORMATS]) @@ -62,21 +63,34 @@ def format_gamebook(inputfilenames, outputfilename): output_format.write(book, open(outputfilename, 'w')) def parse_file_to_book(inputfile, book): - for name,contents in json.load(inputfile).iteritems(): - paragraph = paragraphs.Paragraph(name) - if 'text' in contents: - paragraph.addtext(contents['text']) - if 'number' in contents: - book.add(paragraph, contents['number']) + name = None + number = None + text = "" + for line in inputfile.readlines(): + if line.startswith('*'): + 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: - book.add(paragraph) + text = text + " " + line.strip() + if name: + book.add(paragraphs.Paragraph(name, text), number) def find_output_format(outputfilename): for of in OUTPUT_FORMATS: if of.supports(outputfilename): return of raise Exception("Unsupported or unknown output format for %s." - % outputfile) + % outputfilename) if __name__ == '__main__': import argparse diff --git a/output.py b/output.py index 7d2f07c..865fe33 100644 --- a/output.py +++ b/output.py @@ -2,8 +2,6 @@ import os import os.path import sys -from paragraphs import paragraph_refs_format - class OutputFormat (object): def __init__(self, extension, name): self.extension = extension @@ -29,19 +27,13 @@ class OutputFormat (object): def write_paragraph(self, paragraph, shuffled_paragraphs, output): refs = [] - def paragraph_link_render(to_paragraph, shuffled_paragraphs): - s = self.load_template("paragraph_ref") % { - 'nr' : shuffled_paragraphs.to_nr[to_paragraph], - 'from_nr' : shuffled_paragraphs.to_nr[paragraph] - } - refs.append(s) - return s - formatted_text = paragraph.format(shuffled_paragraphs, - paragraph_link_render) + refsdict = ReferenceFormatter(paragraph, shuffled_paragraphs, + self.load_template("paragraph_ref")) + formatted_text = paragraph.format(refsdict) print >> output, self.load_template("paragraph") % { 'nr' : shuffled_paragraphs.to_nr[paragraph], '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): @@ -64,3 +56,23 @@ class OutputFormat (object): self.cached_templates[name] = 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) diff --git a/paragraphs.py b/paragraphs.py index 8acd8fa..0512932 100644 --- a/paragraphs.py +++ b/paragraphs.py @@ -1,116 +1,16 @@ -#!/usr/bin/env python2.5 - import random 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: - def __init__(self, name, text=None, refs=None, items=None): + def __init__(self, name, text): self.name = name - if text: - self.text = text - else: - self.text = '' - if items: - self.items = items - else: - self.items = [] - if refs: - self.refs = refs - else: - self.refs = [] + self.text = text def __repr__(self): - return "Paragraph(%s, %s, %s, %s)" % (self.name.__repr__(), - self.text.__repr__(), - self.refs.__repr__(), - self.items.__repr__()) - def addtext(self, moretext): - self.text = self.text + moretext + return "Paragraph(%s, %s)" % (repr(self.name), repr(self.text)) - def format(self, shuffled=None, render_callback=None, bad_callback=None): - return paragraph_refs_format(self.text, self.refs, shuffled, render_callback, - bad_callback) + def format(self, references): + return self.text % references class ShuffledParagraphs: def __init__(self, as_list, from_nr, to_nr, from_name): diff --git a/readme.org b/readme.org index a397f89..e4c6733 100644 --- a/readme.org +++ b/readme.org @@ -3,6 +3,27 @@ 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). +** 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 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 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. - - diff --git a/test.gamebook b/test.gamebook new file mode 100644 index 0000000..0a116f6 --- /dev/null +++ b/test.gamebook @@ -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. + diff --git a/test.json b/test.json deleted file mode 100644 index 95ba847..0000000 --- a/test.json +++ /dev/null @@ -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." - } -} diff --git a/test.sh b/test.sh index e0fc40d..c38e141 100755 --- a/test.sh +++ b/test.sh @@ -1,18 +1,18 @@ #!/bin/sh -./formatgamebook.py test.json test.debug +./formatgamebook.py test.gamebook 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 -./formatgamebook.py test.json test.tex +./formatgamebook.py test.gamebook test.tex pdflatex test.tex && open test.pdf -./formatgamebook.py test.json test.html +./formatgamebook.py test.gamebook test.html open test.html -./formatgamebook.py test.json test.rtf +./formatgamebook.py test.gamebook test.rtf open test.rtf