1
0
Fork 0
mirror of https://github.com/Oreolek/gamebookformat.git synced 2024-05-14 15:08:19 +03:00

More formatting options.

This commit is contained in:
Pelle Nilsson 2013-06-11 21:59:39 +02:00
parent 5a468bdd65
commit f60a650598
36 changed files with 229 additions and 43 deletions

1
.gitignore vendored
View file

@ -8,7 +8,6 @@ test.dot
*.toc
*.pdf
test.rtf
*.png
*.out
test.html
.lein*

4
examples/.gitignore vendored
View file

@ -3,4 +3,6 @@
*.html
*.dot
*.debug
*.txt
*.txt
*.png
!testimage.png

View file

@ -1,12 +1,27 @@
title = Format
starttext = Adventure begins in section 1.
hideintrotext = HIDE THE INTRO
showintrotext = SHOW THE INTRO
= Introduction
Adding an introduction
to the gamebook here. This will create
a section, but it will not be shuffled nor numbered
with the gamebook sections below.
= Another Heading
This starts another non-shuffled section.
* 1 start
This examples tests gamebook formatting, not so much game mechanics or
references. Currently there is nothing here really.
This section contains some tricky characters to quote,
like } and { and " and ' and \.
HTML will probably not like <div> or &boom;.
There should be an image below as well.
If something broke, turn to [[bad]],
otherwise turn to [[good]].
[img]testimage.png[/img]
* dum :dummy:
Sections tagged as dummy will not be

View file

@ -1,4 +1,11 @@
max = 400
= Introduction
This gamebook demonstrates simple references between sections.
Also notice that an intro section like this one can
reference sections, like the start at [[start]] or the next
section at [[next]].
* 1 start
This is where the adventure begins. You can go on to the
next section, see [[next]] or try the other instead, see [[other]].

BIN
examples/testimage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

View file

@ -28,6 +28,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import re
import os
import os.path
import sys
@ -41,6 +42,8 @@ from output import OutputFormat
USAGE = "usage: %prog [options] inputfile(s)... outputfile"
SECTION_NAME_RE = re.compile("^[a-z][a-z_0-9]*$")
def of(extension, name, quote):
return {'extension' : extension,
'name' : name,
@ -81,17 +84,15 @@ def parse_file_to_book(inputfile, book):
number = None
text = ""
tags = None
intro_section = False
for line in inputfile.readlines():
if before_first_section:
if '=' in line:
config = line.split('=')
book.configure(config[0].strip(), config[1].strip())
if line.startswith('*'):
before_first_section = False
if name:
add_section_to_book(book, name, text, number)
add_section_to_book(book, name, text, intro_section, number)
number = None
text = ""
intro_section = False
heading = [h.strip() for h in line[1:].strip().split()]
if len(heading) > 1 and heading[-1].startswith(':'):
if not heading[-1].endswith(':'):
@ -106,20 +107,34 @@ def parse_file_to_book(inputfile, book):
elif len(heading) == 2:
number = int(heading[0])
name = heading[1]
else:
raise Exception("bad section heading %s" % str(heading))
else:
if not name or not SECTION_NAME_RE.match(name):
raise Exception("bad section heading: %s" % str(heading))
elif line.startswith('='):
if name:
add_section_to_book(book, name, text, intro_section, number)
name = line[1:].strip()
intro_section = True
text = ""
elif before_first_section and '=' in line:
config = line.split('=')
book.configure(config[0].strip(), config[1].strip())
elif name:
text = text + " " + line.strip()
elif len(line.strip()):
raise Exception("unknown content before sections: %s" % line.strip())
if name:
add_section_to_book(book, name, text, number, tags)
add_section_to_book(book, name, text, intro_section, number, tags)
def add_section_to_book(book, name, text, number=None, tags=None):
def add_section_to_book(book, name, text, intro_section=False, number=None, tags=None):
section = sections.Section(name, text)
if tags:
section.add_tags(tags)
book.add(section)
if number:
book.force_section_nr(name, number)
if intro_section:
book.addintro(section)
else:
book.add(section)
if number:
book.force_section_nr(name, number)
def make_output(outputfilename, templatedirs):
for of in OUTPUT_FORMATS:
@ -134,6 +149,8 @@ def write_book(book, output_format, outputfilename):
shuffled_sections = book.shuffle()
output = open(outputfilename, 'w')
output_format.write_begin(book, output)
output_format.write_intro_sections(book, shuffled_sections, output)
output_format.write_sections_begin(book, output)
output_format.write_shuffled_sections(shuffled_sections, output)
output_format.write_end(book, output)
save_section_mapping(shuffled_sections, outputfilename)

View file

@ -12,6 +12,27 @@ class OutputFormat (object):
# FIXME make sure book config is properly quoted
print >> output, self.format_with_template("begin", book.config)
def write_intro_sections(self, book, shuffled_sections, output):
for s in book.introsections:
if not s.hastag('dummy'):
self.write_intro_section(s, shuffled_sections, output)
def write_intro_section(self, section, shuffled_sections, output):
# FIXME some serious code-duplication here
refs = []
refsdict = ReferenceFormatter(section, shuffled_sections,
self.format_with_template("section_ref"),
self.quote)
formatted_text = self.format_section(section, refsdict)
print >> output, self.format_with_template("introsection", {
'name' : section.name,
'text' : formatted_text,
'refs' : '\n'.join(refsdict.getfound()) # hack for DOT output
}),
def write_sections_begin(self, book, output):
print >> output, self.format_with_template("sections_begin", book.config);
def write_shuffled_sections(self, shuffled_sections, output):
for i, p in enumerate(shuffled_sections.as_list):
if p and not p.hastag('dummy'):
@ -26,7 +47,7 @@ class OutputFormat (object):
self.quote)
formatted_text = self.format_section(section, refsdict)
print >> output, self.format_with_template("section", {
'nr' : shuffled_sections.to_nr[section],
'nr' : section.nr,
'name' : section.name,
'text' : formatted_text,
'refs' : '\n'.join(refsdict.getfound()) # hack for DOT output
@ -92,7 +113,7 @@ class OutputFormat (object):
}),
def write_end(self, book, output):
print >> output, self.format_with_template("end"),
print >> output, self.format_with_template("end", book.config),
def format_with_template(self, name, values=None):
template = self.templates.get(name)
@ -108,7 +129,7 @@ class ReferenceFormatter (object):
self.shuffled_sections = shuffled_sections
self.found = set()
self.ref_template = ref_template
self.items = {'nr' : shuffled_sections.to_nr[section]}
self.items = {'nr' : section.nr}
self.quote = quote
def __getitem__(self, key):
@ -116,8 +137,8 @@ class ReferenceFormatter (object):
return self.quote(self.items[key])
to_section = self.shuffled_sections.from_name[key]
res = self.ref_template % {
'nr' : self.shuffled_sections.to_nr[to_section],
'from_nr' : self.shuffled_sections.to_nr[self.section]
'nr' : to_section.nr,
'from_nr' : self.section.nr
}
if key in self.shuffled_sections.name_to_nr:
self.found.add(res)

View file

@ -17,30 +17,57 @@ class Section:
return "Section(%s, %s, %s)" % (repr(self.name), repr(self.text),
repr(self.tags))
class ShuffledSection (Section):
def __init__(self, nr, section):
self.nr = nr
self.name = section.name
self.text = section.text
self.tags = section.tags.copy()
def __repr__(self):
return "ShuffledSection(%d, %s, %s, %s)" % (self.nr,
repr(self.name), repr(self.text),
repr(self.tags))
class IntroSection (Section):
def __init__(self, section):
self.nr = -1
self.name = section.name
self.text = section.text
self.tags = section.tags.copy()
def __repr__(self):
return "IntroSection(%d, %s, %s, %s)" % (repr(self.name), repr(self.text),
repr(self.tags))
class ShuffledSections:
def __init__(self, as_list, from_nr, to_nr, from_name, nr_sections):
def __init__(self, as_list, from_nr, from_name, nr_sections):
self.as_list = as_list
self.from_nr = from_nr
self.to_nr = to_nr
self.from_name = from_name
self.name_to_nr = {}
for n in from_name:
self.name_to_nr[n] = to_nr[from_name[n]]
self.name_to_nr[n] = from_name[n].nr
for nr in nr_sections:
self.name_to_nr[nr_sections[nr]] = nr
STR_BOOK_CONFIG = set(['title', 'author'])
STR_BOOK_CONFIG = set(['title', 'author', 'starttext', 'hideintrotext',
'showintrotext'])
INT_BOOK_CONFIG = set(['max'])
class Book:
def __init__(self):
self.sections = []
self.introsections = []
self.from_name = {}
self.nr_sections = {}
self.codewords = set()
self.config = {'max' : 0,
'title' : 'Gamebook',
'author' : ''}
'author' : '',
'starttext' : 'Turn to 1 to begin.',
'hideintrotext' : '(hide instructions)',
'showintrotext' : '(show instructions)'}
def configure(self, name, value):
if name in INT_BOOK_CONFIG:
@ -59,6 +86,9 @@ class Book:
if len(self.sections) > self.config['max']:
self.config['max'] = len(self.sections)
def addintro(self, section):
self.introsections.append(IntroSection(section))
def add_codeword(self, word):
self.codewords.add(word)
@ -70,8 +100,8 @@ class Book:
def shuffle(self):
as_list = [None]
from_nr = {}
to_nr = {}
shuffled = self.sections[:]
shuffled_from_name = {}
while len(shuffled) < self.config['max']:
dummy = Section('Dummy', '')
dummy.add_tags(['dummy'])
@ -83,16 +113,16 @@ class Book:
for nr in range(1, self.config['max'] + 1):
if (self.nr_sections.has_key(nr)
and self.nr_sections[nr] in self.from_name):
section = self.from_name[self.nr_sections[nr]]
section = ShuffledSection(nr, self.from_name[self.nr_sections[nr]])
elif len(shuffled):
section = shuffled.pop()
section = ShuffledSection(nr, shuffled.pop())
else:
section = None
as_list.append(section)
from_nr[nr] = section
if section:
to_nr[section] = nr
return ShuffledSections(as_list, from_nr, to_nr, self.from_name.copy(),
shuffled_from_name[section.name] = section
return ShuffledSections(as_list, from_nr, shuffled_from_name,
self.nr_sections)
class Item (object):

View file

View file

@ -0,0 +1,2 @@
#include "introsectionheading"
#include "sectionbody"

View file

@ -0,0 +1,2 @@
%(starttext)s

View file

@ -0,0 +1 @@
[IMG]%(inner)s[/IMG]

View file

@ -0,0 +1,2 @@
%(name)s
%(text)s

View file

View file

View file

@ -12,5 +12,5 @@
</style>
</head>
<body>
#include "intro"
#include "hideintro"
<div class="gamebook">

View file

@ -3,5 +3,6 @@
<script>
#include "endscript"
</script>
#include "showintro"
</body>
</html>

View file

@ -1,3 +1,2 @@
if (this.gamebook) {
gamebook.turnTo(1);
}

View file

@ -0,0 +1,2 @@
<div class="hideintrolink nodisplay"
onclick="gamebook.hideIntroSections()">%(hideintrotext)s</div>

1
templates/html/img.html Normal file
View file

@ -0,0 +1 @@
<img src="%(inner)s" class="sectionimage"></img>

View file

@ -0,0 +1,6 @@
<div class="introsection">
<span class="introsectionheading">%(name)s</span>
<div class="introsectionbody">
%(text)s
</div>
</div>

View file

@ -64,6 +64,12 @@
this.displaySection(nr);
},
'start' : function() {
this.hideIntroSections();
this.addClassToClass('startlink', 'nodisplay');
this.turnTo(1);
},
'displaySection' : function(nr) {
if (this.player.currentSection) {
this.player.currentSection.element.style.display = 'none';
@ -74,6 +80,35 @@
this.player.currentSection = gamebook.sections[nr];
},
'hideIntroSections' : function() {
this.addClassToClass('introsection', 'nodisplay');
this.removeClassFromClass('displayintrolink', 'nodisplay');
this.addClassToClass('hideintrolink', 'nodisplay');
},
'showIntroSections' : function() {
this.removeClassFromClass('introsection', 'nodisplay');
this.addClassToClass('displayintrolink', 'nodisplay');
this.removeClassFromClass('hideintrolink', 'nodisplay');
document.body.scrollIntoView();
},
'addClassToClass' : function(className, addClass) {
Array.prototype.forEach.call(
document.getElementsByClassName(className),
function(e) {
e.classList.add(addClass);
});
},
'removeClassFromClass' : function(className, removeClass) {
Array.prototype.forEach.call(
document.getElementsByClassName(className),
function(e) {
e.classList.remove(removeClass);
});
},
'runActions' : function(e) {
var enableNextLink = true;
var hasXorScope = false;

View file

@ -0,0 +1,2 @@
<div class="startlink"
onclick="gamebook.start()">%(starttext)s</div>

View file

@ -0,0 +1,2 @@
<div class="displayintrolink nodisplay"
onclick="gamebook.showIntroSections()">%(showintrotext)s</div>

View file

@ -0,0 +1,2 @@
<div class="startlink"
onclick="gamebook.start()">%(starttext)s</div>

View file

@ -18,3 +18,8 @@
.has {font-style: italic;}
.hasnot {font-style: italic;}
.collectionTemplate {display: none;}
.sectionimage {width: 100%%; padding: 1em;}
.nodisplay {display: none;}
.startlink {font-weight: bold; cursor: pointer;}
.displayintrolink {cursor: pointer;}
.hideintrolink {cursor: pointer;}

View file

@ -0,0 +1,2 @@
\b \qc %(name)s
\b0\

View file

@ -2,6 +2,7 @@
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[hidelinks]{hyperref}
\usepackage{graphicx}
#include "geometry"
\newif\ifpdf
@ -24,5 +25,8 @@
\begin{document}
#include "titlepage"
\pagestyle{empty}
\clearpage

View file

@ -1,2 +1,2 @@
\documentclass[A5,twocolumn]{book}
\documentclass[a5,onecolumn]{book}

4
templates/tex/img.tex Normal file
View file

@ -0,0 +1,4 @@
\begin{center}
\includegraphics[width=.9\textwidth]{%(inner)s}
\end{center}

View file

@ -0,0 +1,3 @@
\subsection*{\begin{center} \textbf{%(name)s} \end{center}}

View file

@ -1,5 +1,3 @@
\subsection*{\begin{center} \textbf{%(nr)d} \end{center}}
\noindent
%(text)s
\newline
\vspace{1em}

View file

@ -1,3 +1,5 @@
\phantomsection
\refstepcounter{sectionnr}
\label{section%(nr)d}
\subsection*{\begin{center} \textbf{%(nr)d} \end{center}}

View file

@ -0,0 +1 @@
%(name)s

View file

@ -1,2 +1 @@
%(nr)d

View file

@ -1,4 +1,4 @@
* TODO [33/59] [55%]
* TODO [37/65] [56%]
- [X] Debug output
- [X] DOT output
- [X] LaTeX output
@ -36,13 +36,12 @@
- [X] Book option to set max section number to use
- [X] Quote strings to not break formatting.
- [X] Include other templates from a template.
- [ ] Template for book introduction (including rules etc)
- [X] Template for book introduction (including rules etc)
Sections with some markup (has number 0?) are added as chapters
of introduction, otherwise formatted identical to other sections.
- [ ] Inserting images
- [ ] More formatting possibilities in sections
Look at existing gamebooks to get ideas.
- [ ] Only accept specific characters in identifiers (eg section names)
- [X] Inserting images
- [X] HTML hide intro sections with link to display again
- [X] Only accept specific characters in section names
eg [a-z][a-z_0-9]+
- [ ] Random pick of link to follow from a section.
- [ ] Possibility to make predictable random numbers and shuffling for testing
@ -73,6 +72,27 @@
- [ ] Some way to insert optional random numbers table at end of book
- [ ] Defensive removal of any weird unicode not handled by quoting.
- [ ] Somewhat user-friendly error messages
- [ ] More formatting possibilities in sections
Look at existing gamebooks to get ideas.
- [ ] Document Gamebook format
- [ ] HTML CSS
- [ ] Higher level text-language for Gamebooks
- [ ] BGG forum output (.bgg)
- [ ] Make sure HTML output works with javascript disabled and in inferior browsers