I wrote a SugarCube version, taking some cues from Josh’s implementation and with a bit of help by @Hituro!
We’ll start with the initialization passage, StoryInit
. It runs once at the beginning of every SugarCube story and is used to set variables.
The first thing to note is that instead of using the normal variable syntax like $suspects
or $rooms
in these first two Objects, I’m using setup.suspects
and setup.rooms
. Any setup
variables do not appear in the story’s State variables or history, so they’re great for generic objects that have data that will never change (ie static data), like a person’s name and what they look like. They’re accessed by setup.name
as opposed to $name
.
The second thing to note is that I’m using Generic Objects, which are not really mentioned in the documentation, because they’re actually just JavaScript objects. Generic objects can be referenced by key (ex: “red”, “green”, “blue”) and then value. In this case, the value is another object, which can be referenced by its own key (ex: “name”, “question”) which will give that nested key’s value (“Mys. Crimson”, “wanders”). To access the name “Mys. Crimson”, you’d write setup.suspects["red"].name
.
This string can be fed in as a variable as well: <<set $murderer = "red">><<print setup.suspects[$murderer].name>>
This aspect of objects is very useful in loops and widgets.
Re: suspects— the key closeAccuse
is for the situation when you accuse the right person but in the wrong location.
:: StoryInit
<<set setup.suspects = {
"red": {
name: "Mys. Crimson",
description: "Mys. Crimson is a sly-looking person wearing a large, bright red hat with a wide brim.",
verbs: "wanders",
question: 'They squint at you, a smirk playing at their lips. "What sorta thing made you think somemthin’ silly like that?"',
closeAccuse: '"Here?" Mys. Crimson scoffs. "Now?" They start to cackle, and keep doing so for an uncomforably long time.',
rightAccuse: '"How’d you guess?" Mys. Crimson drawls out, barely hiding their smirk. Then their expression darkens, and they step toward you menacingly. "No, really. How’d you guess..."'
},
"green": {
name: "Mrs. Peacock",
description: "Mrs. Peacock is an elegant woman in an emerald green, slightly iridescent dress.",
verbs: "promenades",
question: 'She laughs imperiously. "You would suspect me of such an awful act? What a foolish little thing you are!"',
closeAccuse: 'Mrs. Peacock waves her hand dismissively. "Detective, what a foolish suggestion! Why would I do such a thing, and in such a place?" Her smile seems a little too tight.',
rightAccuse: '"How dare you!" Mrs. Peacock says as her nostrils flare. And yet...she doesn’t deny it! You stare back fiercely, and her anger drains from her, turning into a look of defeat.'
},
"blue": {
name: "Lord Bluebottle",
description: "Lord Bluebottle is a man of great age and stature, wearing a well-tailored suit of sky blue.",
verbs: "strolls",
question: 'He raises an eyebrow and clears his throat. "Quite an imagination you have there, detective."',
closeAccuse: 'Lord Bluebottle shakes his head slowly. "Good detective, I would never do such a thing...here..." Hmm, that pause!',
rightAccuse: 'Lord Bluebottle raises an eyebrow and says in a low voice, "Quite an accusation you’ve made there, detective." You fold your arms and stare at him, and he smiles gently. "How ever did you figure it out...?"'
}
}>>
Re: rooms— the key nextTo
determines the rooms that the current room is near, making a map of sorts for suspects to move around in. It also includes the current room in case someone just hangs around the same place, which doesn’t quite make sense with the name, but I couldn’t think of a better one…
<<set setup.rooms = {
"foyer": {
name: "Foyer",
crimeSceneDescription: "Under the stairs, you find an envelope...filled with thallium!",
nextTo: ["dining", "lawn", "foyer", "parlour"]
},
"gardens": {
name: "Gardens",
crimeSceneDescription: "Within a flower bed you find a glove...soaked with blood!",
nextTo: ["lawn", "gardens"]
},
"lawn": {
name: "Lawn",
crimeSceneDescription: "In the thick grass you find a stray lawn dart...coated with ricin!",
nextTo: ["gardens", "foyer", "lawn"]
},
"dining": {
name: "Dining Room",
crimeSceneDescription: "On the table you find a glass of brandy...laced with arsenic!",
nextTo: ["foyer", "dining"],
},
"parlour": {
name: "Parlour",
crimeSceneDescription: "Under the couch you find a half-eaten box of chocolates...stuffed with cyanide!",
nextTo: ["foyer", "parlour", "gardens"],
}
}>>
Next, I set some normal variables, which will appear in the history and can be changed/redefined as per usual. The murderer is randomly picked from an array of suspects, created using JavaScript’s Object.keys()
and then using the random()
SugarCube macro. Same with the crime scene and an array of rooms.
<<set $murderer = Object.keys(setup.suspects).random()>>
<<set $crimeScene = Object.keys(setup.rooms).random()>>
<<set $beingInvestigated = "">>
<<set $beingQuestioned = "">>
<<set $knowsMurderer = false>>
<<set $knowsCrimeScene = false>>
<<set $inCurrentRoom = []>>
I want the suspects to be able to change rooms, so unlike the static data in setup, I made a variable that contains an array of objects, listing which person is currently in which room, and which room they’re coming from.
<<set $placements = [{person: "green", away: "gardens", here: "lawn"}, {person: "blue", away: "lawn", here: "foyer"}, {person: "red", away: "parlour", here: "parlour"}]>>
Now onto the actual game…
The starting passage has a short intro, and some headers. Then it <<include>>
s the text of the foyer passage, as little as there is, and sets $beingInvestigated
to the foyer.
:: Start
Despite the lack of morals of most of the rich, you had never thought the elderly Duke's birthday celebration at his lavish mansion would turn into the scene of his murder. The top detective you are, you've cleared out the estate of everyone aside from your prime suspects, and now you stalk around the halls, trying to figure out which of them committed the crime, and where...
! MINI CLUEDO
!! Aster Fialla
<<include "foyer">><<set $beingInvestigated = "foyer">>
After this it calls a custom widget I wrote, <<listChoices>>
(we’ll get to it). The <<link>>
macro links to the same passage we’re in, effectively reloading (and most crucially, rerendering) the page, and calls another macro <<moveSuspects>>
when clicked.
<<listChoices>>
<<link "Wait." "foyer">><<moveSuspects>><</link>>
Basic passages for the various rooms. Note that each room has a tag of [room]
.
:: gardens [room]
! Gardens
The luxurious gardens appear almost like a patchwork quilt laid out before your eyes. They lead back into the mansion by way of a porch. Do these beds contain only flowers, or is someone perhaps pushing up daisies...?
:: lawn [room]
! Lawn
Implements of various lawn games lie strewn about the unmown grass. Could any more sinister games have been played here...?
:: foyer [room]
! Foyer
The entrance to the mansion is itself grand and imposing. A long staircase spirals upward toward the higher floors--but as no one was in that area during the murder, you've deemed them off-limits and covered them with rope. Below the stairs though, did someone spiral out for the last time...?
:: dining [room]
! Dining Room
Half-eaten dinners lie on scattered plates on the long table. What secrets were sprinkled within the food and drink...?
:: parlour [room]
! Parlour
Where guests come to be entertained and to relax. There's a porch entrance that leads outside. Perhaps someone was in the mood for much darker entertainment...?
A passage titled PassageFooter
will appear at the end of every passage. In this one, we check whether the current passage’s tags contain the tag “room”, and if so, we do similar code as was in the Start passage–only this time, instead of a string, we pass in passage()
, the name of the current passage.
If the current passage tags include “detail” instead, we put a link to go back to the previous page.
:: PassageFooter
<<if tags().includes("room")>>
<<set $beingInvestigated = passage()>>
<<listChoices>>
<<link "Wait." `$beingInvestigated`>><<moveSuspects>><</link>>
<<elseif tags().includes("detail")>>
<<return>>
<</if>>
challenges.twee (10.5 KB) (edited slightly…)