mirror of
https://github.com/Oreolek/gamebookformat.git
synced 2024-06-26 03:41:04 +03:00
Inventory and codewords now both generic collections.
Some new code to handle tags with arguments.
This commit is contained in:
parent
d10177efd6
commit
98b2583f68
|
@ -1,16 +1,17 @@
|
|||
* 1 start
|
||||
Demonstrating how codewords (AKA sightings) can be used.
|
||||
Demonstrating how [collect code]Codewords[/collect]
|
||||
(AKA sightings) can be used.
|
||||
Go to [[second]].
|
||||
|
||||
* 2 second
|
||||
[set]warrior[/set]
|
||||
Got codeword [add code]warrior[/add].
|
||||
Simple enough to set a codeword. Turn to [[choice]].
|
||||
|
||||
* choice
|
||||
If you have the codeword [has]warrior[/has] you may
|
||||
If you have the codeword [has code]warrior[/has] you may
|
||||
turn to [[the_end]]. If you do not have the codeword
|
||||
[hasnot]fun[/hasnot], you may turn to [[notsofun]].
|
||||
If you have the codeword [has]fun[/has], you may instead
|
||||
[hasnot code]fun[/hasnot], you may turn to [[notsofun]].
|
||||
If you have the codeword [has code]fun[/has], you may instead
|
||||
turn to [[cheater]].
|
||||
Otherwise see [[forced]].
|
||||
|
||||
|
@ -22,7 +23,7 @@ having a codeword. Now go on to [[forced]].
|
|||
Cheater! There is no way you can have codeword fun.
|
||||
|
||||
* forced
|
||||
OK, if you have the codeword [has]warrior[/has], turn to
|
||||
OK, if you have the codeword [has code]warrior[/has], turn to
|
||||
[[auto the_end]] otherwise go back to [[second]]. Although
|
||||
we both know you have that codeword.
|
||||
|
||||
|
|
|
@ -1,54 +1,58 @@
|
|||
* 1 start
|
||||
Demonstrating how to manage player inventory. You start
|
||||
the book carrying a [take]sword[/take] and a [take]shield[/take].
|
||||
Demonstrating how to manage player [collect item]Inventory[/collect].
|
||||
You start
|
||||
the book carrying a [add item]sword[/add]
|
||||
and a [add item]shield[/add].
|
||||
Turn to [[tjunction]].
|
||||
|
||||
* tjunction
|
||||
You have reached a t-junction.
|
||||
Here you find a [found]key[/found] and a [found]stick[/found].
|
||||
Here you find a [found item]key[/found]
|
||||
and a [found item]stick[/found].
|
||||
You can go west to [[door]], or
|
||||
east to [[curse]].
|
||||
|
||||
* door
|
||||
There is a locked door here.
|
||||
If you have a [carries]key[/carries] you can use that to
|
||||
If you have a [has item]key[/has] you can use that to
|
||||
open the door, see [[inside]]. Being right before the link
|
||||
should be enough for the formatter to figure out that the
|
||||
key is required to be allowed to follow the link.
|
||||
Else you can try to open with the [carries]sword[/carries],
|
||||
Else you can try to open with the [has item]sword[/has],
|
||||
if you have it, see [[attempt_break_door_with_sword]].
|
||||
Hopefully the magic is good enough to pair
|
||||
pre-conditions to links, or more markup must be added later.
|
||||
You could also try to go back to pick up the key, see [[tjunction]].
|
||||
|
||||
* attempt_break_door_with_sword
|
||||
OK. The door is broken, but so is the [drop]sword[/drop].
|
||||
OK. The door is broken, but so is the [drop item]sword[/drop].
|
||||
Turn to [[inside]].
|
||||
|
||||
* curse
|
||||
There is a [found]cursed bracelet[/found] here. You can go on to
|
||||
There is a [found item]cursed bracelet[/found] here. You can go on to
|
||||
[[portal]] or go back to [[tjunction]]. You can also drop the
|
||||
stick for no particular reason, see [[drop_stick]].
|
||||
[has item]stick[/has] for no particular reason if you have it,
|
||||
see [[drop_stick]].
|
||||
|
||||
* drop_stick
|
||||
OK [drop]stick[/drop] dropped. Turn back to [[tjunction]] to confirm
|
||||
OK [drop item]stick[/drop] dropped. Turn back to [[tjunction]] to confirm
|
||||
stick can not be picked up again even if the text says it is there
|
||||
(books work that way, although ideally this dynamic version should
|
||||
provide some hints that it is no longer there).
|
||||
|
||||
* portal
|
||||
A magic portal ahead will only allow you to pass if you did not pick up the
|
||||
[notcarries]cursed bracelet[/notcarries], leading you to [[treasure]].
|
||||
If you have the [carries]cursed bracelet[/carries]
|
||||
[hasnot item]cursed bracelet[/hasnot], leading you to [[treasure]].
|
||||
If you have the [has item]cursed bracelet[/has]
|
||||
you have to go back to [[tjunction]] instead.
|
||||
Actually feel free to head back to [[tjunction]] either way.
|
||||
|
||||
* treasure
|
||||
You found [found]something valuable[/found], but there is no way forward,
|
||||
You found [found item]something valuable[/found], but there is no way forward,
|
||||
so you head back to [[tjunction]].
|
||||
|
||||
* inside
|
||||
There is a rope here that can be cut using a [carries]sword[/carries].
|
||||
There is a rope here that can be cut using a [has item]sword[/has].
|
||||
If you have one and want to do that, see [[cut_rope]].
|
||||
Otherwise turn to [[won]].
|
||||
|
||||
|
|
|
@ -59,7 +59,8 @@ class OutputFormat (object):
|
|||
raise Exception('Mismatched tag start [ in section %s' %
|
||||
self.name)
|
||||
tag = section.text[tag_start+1:tag_end].strip()
|
||||
tagname = tag.split()[0].strip()
|
||||
tagparts = tag.split()
|
||||
tagname = tagparts[0]
|
||||
end_tag_start = section.text.find('[', tag_end)
|
||||
if (not end_tag_start > tag_end
|
||||
and section.text[end_tag_start].startswith('[/' + tagname
|
||||
|
@ -69,8 +70,10 @@ class OutputFormat (object):
|
|||
inner = section.text[tag_end+1:end_tag_start]
|
||||
# FIXME this pollutes the mutable references object
|
||||
references['inner'] = inner
|
||||
f = self.format_with_template(tag.replace(' ', '_'),
|
||||
references)
|
||||
for i, arg in enumerate(tagparts[1:]):
|
||||
references['arg%d' % (i+1)] = arg
|
||||
f = self.format_with_template(tagname,
|
||||
references)
|
||||
if len(f) > 0:
|
||||
res += f
|
||||
else:
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
[CARRIES]%(inner)s[/CARRIES]
|
|
@ -1 +0,0 @@
|
|||
SET CODEWORD %(inner)s.
|
2
templates/html/add.html
Normal file
2
templates/html/add.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<span class="add" data-type="%(arg1)s"
|
||||
data-what="%(inner)s">%(inner)s</span>
|
|
@ -9,38 +9,46 @@
|
|||
var gamebook = {
|
||||
'player' : {
|
||||
'currentSection' : null,
|
||||
'inventory' : [],
|
||||
'codewords' : {},
|
||||
'collections' : {},
|
||||
|
||||
'take' : function(item) {
|
||||
if (this.inventory.indexOf(item) === -1
|
||||
&& !(item in gamebook.droppedItems)) {
|
||||
this.inventory.push(item);
|
||||
this.inventory.sort();
|
||||
gamebook.updateInventory();
|
||||
}
|
||||
'collect' : function(type, name) {
|
||||
this.collections[type] = {
|
||||
'name' : name,
|
||||
'contents' : [],
|
||||
'add' : function(what) {
|
||||
if (this.contents.indexOf(what) === -1
|
||||
&& !(what in gamebook.dropped[type])) {
|
||||
this.contents.push(what);
|
||||
this.contents.sort();
|
||||
}
|
||||
},
|
||||
'drop' : function(what) {
|
||||
var i = this.contents.indexOf(what);
|
||||
if (i >= 0) {
|
||||
this.contents.splice(i, 1);
|
||||
gamebook.dropped[type][what] = true;
|
||||
}
|
||||
},
|
||||
'has' : function(what) {
|
||||
return this.contents.indexOf(what) >= 0;
|
||||
}
|
||||
};
|
||||
gamebook.dropped[type] = {};
|
||||
gamebook.addCollectionView(type, name);
|
||||
},
|
||||
|
||||
'drop' : function(item) {
|
||||
var i = this.inventory.indexOf(item);
|
||||
if (i >= 0) {
|
||||
this.inventory.splice(i, 1);
|
||||
gamebook.droppedItems[item] = true;
|
||||
gamebook.updateInventory();
|
||||
}
|
||||
'add' : function(type, what) {
|
||||
this.collections[type].add(what);
|
||||
gamebook.updateCollectionsView();
|
||||
},
|
||||
|
||||
'carries' : function(item) {
|
||||
return this.inventory.indexOf(item) >= 0;
|
||||
'drop' : function(type, what) {
|
||||
this.collections[type].drop(what);
|
||||
gamebook.updateCollectionsView();
|
||||
},
|
||||
|
||||
'set' : function(codeword) {
|
||||
this.codewords[codeword] = true;
|
||||
gamebook.updateCodewords();
|
||||
},
|
||||
|
||||
'has' : function(codeword) {
|
||||
return codeword in this.codewords;
|
||||
'has' : function(type, what) {
|
||||
return this.collections[type].has(what);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -48,49 +56,13 @@
|
|||
|
||||
'turnToFunctions' : {},
|
||||
|
||||
'autoRun' : {},
|
||||
|
||||
'droppedItems' : {},
|
||||
|
||||
'addAutoRun' : function(nr, f) {
|
||||
if (nr in this.autoRun) {
|
||||
this.autoRun[nr].push(f);
|
||||
} else {
|
||||
this.autoRun[nr] = [f];
|
||||
}
|
||||
},
|
||||
|
||||
'runAutoRuns' : function(nr) {
|
||||
if (nr in gamebook.autoRun) {
|
||||
gamebook.autoRun[nr].forEach(function (f) {
|
||||
f();
|
||||
});
|
||||
}
|
||||
},
|
||||
'dropped' : {},
|
||||
|
||||
'addSection' : function(nr, element) {
|
||||
var section = {'element' : element, 'nr' : nr};
|
||||
this.sections[nr] = section;
|
||||
},
|
||||
|
||||
'takeItem' : function(nr, item) {
|
||||
this.addAutoRun(nr, function() {
|
||||
gamebook.player.take(item);
|
||||
});
|
||||
},
|
||||
|
||||
'dropItem' : function(nr, item) {
|
||||
this.addAutoRun(nr, function() {
|
||||
gamebook.player.drop(item);
|
||||
});
|
||||
},
|
||||
|
||||
'setCodeword' : function(nr, codeword) {
|
||||
this.addAutoRun(nr, function() {
|
||||
gamebook.player.set(codeword);
|
||||
});
|
||||
},
|
||||
|
||||
'turnTo' : function(nr) {
|
||||
console.log("Turning to " + nr + ".");
|
||||
if (!nr in this.sections) {
|
||||
|
@ -105,14 +77,12 @@
|
|||
this.player.currentSection.element.style.display = 'none';
|
||||
}
|
||||
var e = this.sections[nr].element;
|
||||
this.enableDisableSectionLinks(
|
||||
e.getElementsByClassName('sectiontext')[0]);
|
||||
this.runActions(e.getElementsByClassName('sectiontext')[0]);
|
||||
e.style.display = 'block';
|
||||
this.player.currentSection = gamebook.sections[nr];
|
||||
gamebook.runAutoRuns(nr);
|
||||
},
|
||||
|
||||
'enableDisableSectionLinks' : function(e) {
|
||||
'runActions' : function(e) {
|
||||
// FIXME handle auto links that must be followed if possible
|
||||
var enableNextLink = true;
|
||||
Array.prototype.forEach.call(e.childNodes, function(c) {
|
||||
|
@ -123,18 +93,22 @@
|
|||
gamebook.disableLink(c);
|
||||
}
|
||||
enableNextLink = true;
|
||||
} else if (c.className === 'itemcarried') {
|
||||
var item = c.dataset.carried;
|
||||
enableNextLink = gamebook.player.carries(item);
|
||||
} else if (c.className === 'itemnotcarried') {
|
||||
var item = c.dataset.carried;
|
||||
enableNextLink = !gamebook.player.carries(item);
|
||||
} else if (c.className === 'hascodeword') {
|
||||
var codeword = c.dataset.has;
|
||||
enableNextLink = gamebook.player.has(codeword);
|
||||
} else if (c.className === 'hasnotcodeword') {
|
||||
var codeword = c.dataset.has;
|
||||
enableNextLink = !gamebook.player.has(codeword);
|
||||
} else if (c.className === 'collect') {
|
||||
gamebook.player.collect(c.dataset.type, c.dataset.name);
|
||||
} else if (c.className === 'add') {
|
||||
gamebook.player.add(c.dataset.type, c.dataset.what);
|
||||
} else if (c.className === 'drop') {
|
||||
gamebook.player.drop(c.dataset.type, c.dataset.what);
|
||||
} else if (c.className === 'has') {
|
||||
enableNextLink = gamebook.player.has(c.dataset.type,
|
||||
c.dataset.what);
|
||||
console.log("has " + c.dataset.type +
|
||||
" " + c.dataset.what + " " + enableNextLink);
|
||||
} else if (c.className === 'hasnot') {
|
||||
enableNextLink = !gamebook.player.has(c.dataset.type,
|
||||
c.dataset.what);
|
||||
console.log("has not " + c.dataset.type +
|
||||
" " + c.dataset.what + " " + enableNextLink);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -151,17 +125,26 @@
|
|||
e.className = "disabledsectionref";
|
||||
},
|
||||
|
||||
'updateInventory' : function() {
|
||||
// FIXME obviously, this should be better written and do more
|
||||
var e = document.getElementById('inventoryitems');
|
||||
e.innerHTML = gamebook.player.inventory.join(', ');
|
||||
'addCollectionView' : function(type, name) {
|
||||
var ce = document.getElementById('collections');
|
||||
var template = document.getElementById('collectionTemplate');
|
||||
var e = template.cloneNode(true);
|
||||
e.className = "collection";
|
||||
e.getElementsByClassName('collectionheading')[0].innerHTML = name;
|
||||
e.dataset.type = type;
|
||||
ce.appendChild(e);
|
||||
},
|
||||
|
||||
'updateCodewords' : function() {
|
||||
// FIXME obviously, this should be better written and do more
|
||||
var e = document.getElementById('codewordslist');
|
||||
e.innerHTML = Object.keys(gamebook.player.codewords
|
||||
).sort().join(', ');
|
||||
'updateCollectionsView' : function() {
|
||||
var ce = document.getElementById('collections');
|
||||
Array.prototype.forEach.call(ce.childNodes, function(c) {
|
||||
if (c.className === 'collection') {
|
||||
var type = c.dataset.type;
|
||||
var collection = gamebook.player.collections[type];
|
||||
var cc = c.getElementsByClassName('collectioncontents')[0];
|
||||
cc.innerHTML = collection.contents.join(', ');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'getTurnToFunction' : function(nr) {
|
||||
|
@ -182,21 +165,24 @@
|
|||
<style>
|
||||
.enabledsectionref {font-weight: bold; cursor: pointer;}
|
||||
.disabledsectionref {font-weight: bold; color: #aaa; cursor: not-allowed;}
|
||||
.itemfound {font-weight: bold; cursor: pointer;}
|
||||
.sectionnumber {font-weight: bolder;
|
||||
margin-left: 50%%;
|
||||
margin-right: 50%%;}
|
||||
.section {display: none; width: 90%%; margin-left: 5%%; margin-right: 5%%;}
|
||||
.sectiontext {margin-top: 0.5em;}
|
||||
.gamebook {max-width: 30em; padding: 1em; width: 100%%; font-size: 133%%;}
|
||||
.inventory {background: #cfc; margin-top: 5em;}
|
||||
.inventoryheading {}
|
||||
.inventoryitems {}
|
||||
.codewords {background: #fcc; margin-top: 0.5em;}
|
||||
.codewordsheading {}
|
||||
.codewordslist {}
|
||||
.setcodeword {color: #f55;}
|
||||
.hascodeword {color: #f55;}
|
||||
.collections {margin-top: 4em;}
|
||||
.collection {background: #ddd; margin-top: 1em;}
|
||||
.collectionheading {}
|
||||
.collectionheading::after {content: ": ";}
|
||||
.collectioncontents {}
|
||||
.collect {}
|
||||
.add {font-weight: bold;}
|
||||
.found {font-weight: bold; cursor: pointer;}
|
||||
.drop {font-style: italic;}
|
||||
.has {font-style: italic;}
|
||||
.hasnot {font-style: italic;}
|
||||
.collectionTemplate {display: none;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<span class="itemcarried" data-carried="%(inner)s">%(inner)s</span>
|
2
templates/html/collect.html
Normal file
2
templates/html/collect.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<span class="collect" data-type="%(arg1)s"
|
||||
data-name="%(inner)s">%(inner)s</span>
|
|
@ -1,6 +1,2 @@
|
|||
<span class="itemdrop">%(inner)s</span>
|
||||
<script>
|
||||
if (this.gamebook) {
|
||||
gamebook.dropItem(%(nr)d, "%(inner)s");
|
||||
}
|
||||
</script>
|
||||
<span class="drop" data-type="%(arg1)s"
|
||||
data-what="%(inner)s">%(inner)s</span>
|
|
@ -1,15 +1,12 @@
|
|||
<div class="inventory">
|
||||
<span class="inventoryheading">Carrying: </span>
|
||||
<span class="inventoryitems" id="inventoryitems"></span>
|
||||
<div id="collections" class="collections">
|
||||
</div>
|
||||
<div class="codewords">
|
||||
<span class="codewordsheading">Codewords: </span>
|
||||
<span class="codewordslist" id="codewordslist"></span>
|
||||
<div id="collectionTemplate" class="collectionTemplate">
|
||||
<span class="collectionheading"></span>
|
||||
<span class="collectioncontents"></span>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
if (this.gamebook) {
|
||||
gamebook.updateInventory();
|
||||
gamebook.turnTo(1);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<span class="itemfound"
|
||||
onclick="gamebook.player.take('%(inner)s')"
|
||||
<span class="found"
|
||||
onclick="gamebook.player.add('%(arg1)s', '%(inner)s')"
|
||||
>%(inner)s</span>
|
||||
|
|
|
@ -1 +1 @@
|
|||
<span class="hascodeword" data-has="%(inner)s">%(inner)s</span>
|
||||
<span class="has" data-type="%(arg1)s" data-what="%(inner)s">%(inner)s</span>
|
|
@ -1 +1,2 @@
|
|||
<span class="hasnotcodeword" data-has="%(inner)s">%(inner)s</span>
|
||||
<span class="hasnot" data-type="%(arg1)s"
|
||||
data-what="%(inner)s">%(inner)s</span>
|
|
@ -1 +0,0 @@
|
|||
<span class="itemnotcarried" data-carried="%(inner)s">%(inner)s</span>
|
|
@ -1,7 +0,0 @@
|
|||
<div class="setcodeword">codeword %(inner)s</div>
|
||||
<script>
|
||||
if (this.gamebook) {
|
||||
console.log("set %(nr)d %(inner)s");
|
||||
gamebook.setCodeword(%(nr)d, "%(inner)s");
|
||||
}
|
||||
</script>
|
1
templates/tex/add.tex
Normal file
1
templates/tex/add.tex
Normal file
|
@ -0,0 +1 @@
|
|||
\textbf{%(inner)s}
|
1
templates/tex/drop.tex
Normal file
1
templates/tex/drop.tex
Normal file
|
@ -0,0 +1 @@
|
|||
\textit{%(inner)s}
|
|
@ -1 +0,0 @@
|
|||
Codeword \textit{%(inner)s}.
|
13
todo.org
13
todo.org
|
@ -1,4 +1,4 @@
|
|||
* TODO [23/47] [48%]
|
||||
* TODO [24/48] [50%]
|
||||
- [X] Debug output
|
||||
- [X] DOT output
|
||||
- [X] LaTeX output
|
||||
|
@ -24,8 +24,12 @@
|
|||
- [X] Make sure dropped items can not be picked up again
|
||||
- [X] Fix problem with map file from other book sometimes crashing formatter
|
||||
- [X] Keys from formatted book should be added to map file, not replace it
|
||||
- [X] Make inventory and codewords generic
|
||||
They are the same, sort of, and it might be good to be able to
|
||||
add other similar entities as well, like skills or spells or whatever.
|
||||
- [ ] More formatting possibilities in sections
|
||||
Look at existing gamebooks to get ideas.
|
||||
- [ ] Otherwise/else handling for has (or has not).
|
||||
- [ ] Book option to set max section number to use
|
||||
- [ ] Book option to set pdf layout (page size and number of columns)
|
||||
- [ ] Book option to set title
|
||||
|
@ -33,6 +37,8 @@
|
|||
- [ ] Book option to set date
|
||||
- [ ] Quote strings to not break formatting.
|
||||
- [ ] 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.
|
||||
- [ ] Some way to insert character sheet in book introduction
|
||||
- [ ] Some way to insert dice at bottom of pages for LaTeX
|
||||
- [ ] Some way to insert optional random numbers table at end of book
|
||||
|
@ -43,11 +49,10 @@
|
|||
- [ ] Verify gamebook for debug and dot outputs
|
||||
- [ ] Dummy sections (handle properly when verifying)
|
||||
- [ ] Counters (life, money, whatever) create and set
|
||||
count tag to declare new counter, text in tag is display name
|
||||
optional argument sets the starting value of the tag
|
||||
- [ ] Counters increase/decrease
|
||||
- [ ] Counters check
|
||||
- [ ] Document Gamebook format
|
||||
- [ ] HTML CSS
|
||||
- [ ] Higher level text-language for Gamebooks
|
||||
- [ ] Make inventory and codewords generic
|
||||
They are the same, sort of, and it might be good to be able to
|
||||
add other similar entities as well, like skills or spells or whatever.
|
||||
|
|
Loading…
Reference in a new issue