diff --git a/Readme.md b/Readme.md index 4f52ae7..e1d0884 100644 --- a/Readme.md +++ b/Readme.md @@ -6,7 +6,7 @@ You can use it in the Twine interactive editor, but at the moment it's probably ## How to use it -Add `storymanager.js` (and optionally `storymanager-widgets.tw`) to your Twine project. In JavaScript, add some story data if needed, and then add a storylet with a name, some tags (optionally), and a generator function that returns a list of instantiated storylet objects, like this: +Add `storymanager.js` (and optionally `storymanager-widgets.tw`) to your Twine project. In JavaScript, add some story data if needed, and then add a storylet with a name, some tags (optionally), and a `generate` generator function* that `yield`s one or more instantiated storylet objects, like this: ```javascript @@ -16,8 +16,7 @@ State.variables.currentLocation = "Deep space"; StoryManager.storylets["Go somewhere"] = { name: "Go somewhere", tags: ["in space"], - generate: function() { - let storylets = []; + generate: function*() { for (let loc of State.variables.locations) { if (loc == State.variables.currentLocation) continue; // Below is the instantiated potential storylet object: @@ -26,13 +25,14 @@ StoryManager.storylets["Go somewhere"] = { description: "Jump to " + loc, // Storylet link text planet: loc // Data associated with this storylet }; - storylets.push(storylet); + yield storylet; } - return storylets; } } ``` +(If you're unfamiliar with the `yield` keyword, think of it as a way a function can return multiple values without needing to create and return an array. Just remember that a function that uses `yield` needs to be defined as `function*`) + Then in Twine, write one or more passages associated with your storylet: ``` @@ -87,7 +87,7 @@ I have a few Twine hobby projects in various stage of completion, and I found my - [X] Widget for displaying storylet links - [ ] Make the widget into a macro - [ ] Weighted random choice -- [ ] Explore replacing storylet generators returning arrays with the `yield` keyword? **Pro:** produces cleaner code; **Con:** requires users to understand `yield` and remember to use the function * notation. +- [X] Explore replacing storylet generators returning arrays with the `yield` keyword? **Pro:** produces cleaner code; **Con:** requires users to understand `yield` and remember to use the function * notation. - [ ] Add storylet code to passages (as comments, a-la Tiny-QBN?) - [ ] CSS styling (probably to go with widgets/macros?) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 91d9a0e..c0f3465 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -36,14 +36,13 @@ State.variables.characters = [ ] ``` -Finally, we'll create the storylet generator. This is a function returning the list of potential storylets. In this case there should be three: one conversation per character. This also goes in the story-level JavaScript. +Finally, we'll create the storylet generator. This is a generator function yielding potential storylets. In this case there should be three: one conversation per character. This also goes in the story-level JavaScript. ```javascript StoryManager.storylets["Conversation"] = { name: "Conversation", tags: [], - generate: function() { - let storylets = []; + generate: function*() { for (let i in State.variables.characters) { let character = State.variables.characters[i]; let storylet = { @@ -52,9 +51,8 @@ StoryManager.storylets["Conversation"] = { character: character, priority: 0 } - storylets.push(storylet); + yield storylet; } - return storylets; } }; ``` @@ -160,15 +158,15 @@ Let's have a 20% chance of another random guest coming up and talking to the pla StoryManager.storylets["Buttonholed"] = { name: "Buttonholed", tags: [], - generate: function() { + generate: function*() { if (Math.random() < 0.2) { let char = randomChoice(State.variables.characters); - return [{ + yield { passage: "Being approached", description: "You see " + char.name + " approaching you.", interrupt: true, character: char - }] + }; } } } @@ -249,19 +247,17 @@ Finally, we can create the new storylet that checks if either the protagonist or StoryManager.storylets["Conversation topic"] = { name: "Conversation topic", tags: ["during conversation"], - generate: function() { - let storylets = []; + generate: function*() { for (let topic in State.variables.playerKnowledge) { if (State.variables.playerKnowledge[topic] > 0 | State.variables.talkingTo[topic] > 0) - storylets.push({ + yield { passage: "Conversation topic", description: "Talk about " + topic, priority: 0, topic: topic - }) + }; } - return storylets; } }; ``` @@ -368,13 +364,13 @@ Finally, we can add two new storylets: a failure condition for if your reputatio StoryManager.storylets["Asked to leave"] = { name: "Asked to leave", tags: ["circulating"], - generate: function() { + generate: function*() { if (State.variables.reputation < -1 && Math.random() < 0.5) { - return [{ + yield { passage: "Asked to leave", description: "You see a footman approaching you.", interrupt: true - }] + }; } } }; @@ -382,13 +378,13 @@ StoryManager.storylets["Asked to leave"] = { StoryManager.storylets["Seeing the duchess"] = { name: "Seeing the duchess", tags: ["circulating"], - generate: function() { + generate: function*() { if (State.variables.reputation > 6 && Math.random() < 0.5) { - return [{ + yield { passage: "Invited to see the Duchess", description: "You see a footman approaching you.", interrupt: true - }] + }; } } }; diff --git a/examples/duchess_party.html b/examples/duchess_party.html index e02b52f..e49c745 100644 --- a/examples/duchess_party.html +++ b/examples/duchess_party.html @@ -114,11 +114,11 @@ StoryManager.getAllStorylets = function(tag=null) { storylet = this.storylets[key]; if (tag === null || ("tags" in storylet && storylet.tags.includes(tag))) { // TODO: If using yield, this part will change - storylets = storylet.generate(); - for (let i in storylets) { - boundStorylet = storylets[i]; + //storylets = storylet.generate(); + for (let boundStorylet of storylet.generate()) { + //boundStorylet = storylets[i]; if (!("priority" in boundStorylet)) boundStorylet.priority = 0; - allStorylets.push(storylets[i]); + allStorylets.push(boundStorylet); } } } @@ -222,8 +222,7 @@ State.variables.conversationTopics = { StoryManager.storylets["Conversation"] = { name: "Conversation", tags: ["circulating"], - generate: function() { - let storylets = []; + generate: function*() { for (let i in State.variables.characters) { let character = State.variables.characters[i]; let storylet = { @@ -233,24 +232,23 @@ StoryManager.storylets["Conversation"] = { character: character } - storylets.push(storylet); + yield storylet; } - return storylets; } }; StoryManager.storylets["Buttonholed"] = { name: "Buttonholed", tags: ["circulating"], - generate: function() { + generate: function*() { if (Math.random() < 0.2) { let char = randomChoice(State.variables.characters); - return [{ + yield { passage: "Being approached", description: "You see " + char.name + " approaching you.", interrupt: true, character: char - }] + }; } } }; @@ -277,13 +275,14 @@ StoryManager.storylets["Conversation topic"] = { StoryManager.storylets["Asked to leave"] = { name: "Asked to leave", tags: ["circulating"], - generate: function() { + generate: function* + () { if (State.variables.reputation < -1 && Math.random() < 0.5) { - return [{ + yield { passage: "Asked to leave", description: "You see a footman approaching you.", interrupt: true - }] + }; } } }; @@ -291,13 +290,13 @@ StoryManager.storylets["Asked to leave"] = { StoryManager.storylets["Seeing the duchess"] = { name: "Seeing the duchess", tags: ["circulating"], - generate: function() { + generate: function*() { if (State.variables.reputation > 6 && Math.random() < 0.5) { - return [{ + yield { passage: "Invited to see the Duchess", description: "You see a footman approaching you.", interrupt: true - }] + } } } }; diff --git a/examples/duchess_party.js b/examples/duchess_party.js index d3cd364..38720dc 100644 --- a/examples/duchess_party.js +++ b/examples/duchess_party.js @@ -49,8 +49,7 @@ State.variables.conversationTopics = { StoryManager.storylets["Conversation"] = { name: "Conversation", tags: ["circulating"], - generate: function() { - let storylets = []; + generate: function*() { for (let i in State.variables.characters) { let character = State.variables.characters[i]; let storylet = { @@ -60,24 +59,23 @@ StoryManager.storylets["Conversation"] = { character: character } - storylets.push(storylet); + yield storylet; } - return storylets; } }; StoryManager.storylets["Buttonholed"] = { name: "Buttonholed", tags: ["circulating"], - generate: function() { + generate: function*() { if (Math.random() < 0.2) { let char = randomChoice(State.variables.characters); - return [{ + yield { passage: "Being approached", description: "You see " + char.name + " approaching you.", interrupt: true, character: char - }] + }; } } }; @@ -104,13 +102,14 @@ StoryManager.storylets["Conversation topic"] = { StoryManager.storylets["Asked to leave"] = { name: "Asked to leave", tags: ["circulating"], - generate: function() { + generate: function* + () { if (State.variables.reputation < -1 && Math.random() < 0.5) { - return [{ + yield { passage: "Asked to leave", description: "You see a footman approaching you.", interrupt: true - }] + }; } } }; @@ -118,13 +117,13 @@ StoryManager.storylets["Asked to leave"] = { StoryManager.storylets["Seeing the duchess"] = { name: "Seeing the duchess", tags: ["circulating"], - generate: function() { + generate: function*() { if (State.variables.reputation > 6 && Math.random() < 0.5) { - return [{ + yield { passage: "Invited to see the Duchess", description: "You see a footman approaching you.", interrupt: true - }] + } } } }; \ No newline at end of file diff --git a/examples/simple_space_example.html b/examples/simple_space_example.html index da6f704..8c858b7 100644 --- a/examples/simple_space_example.html +++ b/examples/simple_space_example.html @@ -114,11 +114,11 @@ StoryManager.getAllStorylets = function(tag=null) { storylet = this.storylets[key]; if (tag === null || ("tags" in storylet && storylet.tags.includes(tag))) { // TODO: If using yield, this part will change - storylets = storylet.generate(); - for (let i in storylets) { - boundStorylet = storylets[i]; + //storylets = storylet.generate(); + for (let boundStorylet of storylet.generate()) { + //boundStorylet = storylets[i]; if (!("priority" in boundStorylet)) boundStorylet.priority = 0; - allStorylets.push(storylets[i]); + allStorylets.push(boundStorylet); } } } @@ -171,14 +171,15 @@ StoryManager.getStorylets = function(n=null, tag=null, respect_interrupt=true) { window.SM = StoryManager; /* twine-user-script #2: "Story JavaScript" */ +Config.passages.nobr = true; // No unspecified linebreaks. + State.variables.locations = ["Earth", "Mars", "Ganymede"]; State.variables.currentLocation = "Deep space"; StoryManager.storylets["Go somewhere"] = { name: "Go somewhere", tags: ["in space"], - generate: function() { - let storylets = []; + generate: function*() { for (let loc of State.variables.locations) { if (loc == State.variables.currentLocation) continue; let storylet = { @@ -186,9 +187,8 @@ StoryManager.storylets["Go somewhere"] = { description: "Jump to " + loc, // Storylet link text planet: loc // Data associated with this storylet }; - storylets.push(storylet); + yield storylet; } - return storylets; } }<<widget ShowStoryletLinks>> <<for _storylet range $args[0]>> diff --git a/examples/simple_space_example.tw b/examples/simple_space_example.tw index 745dd09..fc23145 100644 --- a/examples/simple_space_example.tw +++ b/examples/simple_space_example.tw @@ -15,8 +15,7 @@ State.variables.currentLocation = "Deep space"; StoryManager.storylets["Go somewhere"] = { name: "Go somewhere", tags: ["in space"], - generate: function() { - let storylets = []; + generate: function*() { for (let loc of State.variables.locations) { if (loc == State.variables.currentLocation) continue; let storylet = { @@ -24,9 +23,8 @@ StoryManager.storylets["Go somewhere"] = { description: "Jump to " + loc, // Storylet link text planet: loc // Data associated with this storylet }; - storylets.push(storylet); + yield storylet; } - return storylets; } } diff --git a/examples/tutorial.html b/examples/tutorial.html index 0efd3dc..696203b 100644 --- a/examples/tutorial.html +++ b/examples/tutorial.html @@ -102,11 +102,6 @@ var saveAs=saveAs||navigator.msSaveBlob&&navigator.msSaveBlob.bind(navigator)||f