Let's Code: Mini-Cluedo

Now we need to be able to investigate our murder, which means we need to randomly generate a solution. We add these lines to the game block, which causes them to run on startup:

pick murderer these professor_boysenberry miss_cherry doctor_lime
pick crime_scene these foyer billiards_room library

Then allowing the player to search the current room is a simple matter of:

verb search_for_clues
eq $room $crime_scene
say There's no doubting it ... a murder took place in this very room!

verb search_for_clues
say Nothing seems out of the ordinary.

setverb search_for_clues intransitive

The empty setverb block means that the verb is always enabled (it can never contain a failing assertion), which is what we want. The first block will run to successful completion only if the current room is the crime scene; if it fails, we fall through to the second.

We also add a verb for questioning the suspects:

setverb question
has $this alive

verb question
eq $this $murderer
say {the $this} is unusually evasive in response to your questions. {Nom $this} must be the murderer!

verb question
eq $this professor_boysenberry
say "Please resolve this matter forthwith," says the professor. "I must return to my alembics."

verb question
eq $this miss_cherry
say "A murder!" says Miss Cherry. "How scandalous!"

verb question
eq $this doctor_lime
say "From the gruesome nature of the evidence, I concur that this was indeed a murder," says the doctor.

This time the setverb block says that we can question any thing which is alive, i.e. any NPC. As it happens, every thing in our game is an NPC anyway, but if we added some props, we wouldn’t want the player to be able to question those.

Gruescript allows you to write “specific verb blocks” with syntax like verb question doctor_lime. However, specific verb blocks always take precedence over general verb blocks, so if we used those here for the blocks which give the different NPCs their different response text, we’d never get as far as the general verb block which checks whether the NPC questioned is the murderer.

1 Like

Before we can allow the player to make an accusation, we need to define a randomised murder weapon, so we add to the initial game block:

run set_murder_weapon

Then we add a definition for it:

proc set_murder_weapon
random weapon_number 3
eq $weapon_number 1
write murder_weapon a large telephone directory

proc set_murder_weapon
eq $weapon_number 2
write murder_weapon a plate of putrefied sushi

proc set_murder_weapon
eq $weapon_number 3
write murder_weapon the spear of Longinus

I don’t know if there’s a more idiomatic way to do a switch statement than this. What’s happening is that the first block assigns a random value to the global variable weapon_number, then each of the blocks in turn checks the assertion that weapon_number is equal to a specific value and assigns an appropriate string to murder_weapon if it is. write is the same as assign except that it can assign a whole string, including evaluating substitutions if necessary (we don’t need that, but we do need a string with spaces, whereas assign would cut it off after the first space).

And by this point the code for allowing the player to accuse a suspect should all look pretty familiar:

setverb accuse
has $this alive

verb accuse
say "There is no doubt," you cry triumphantly, "that the murder was carried out by {the $this}, right here in {$room.dest_name}, with {$murder_weapon}!"
eq $room $crime_scene
eq $this $murderer
say The police investigation bears out your hypothesis, and you are lauded once again as the world's most brilliant detective.
die You have won

verb accuse
say But your hypothesis is swiftly disproven, and the murderer is never brought to justice.
die You have failed

The first block will always get as far as printing the text where you accuse the suspect; then, either both of the assertions will succeed, and we continue to winning the game, or one of them fails, in which case we move to the second block and trigger the losing ending instead.

1 Like

And that’s all. Here’s all the code in case anyone wants to try it out:

Full Gruescript listing
game Mini-Cluedo
id minicluedo
author Adam Biltcliffe
version 1.0.0
person 2
examine off
conversation off
wait on
say This should have been the perfect society gathering ... until MURDER reared its ugly head! Three suspects remain at large, but can you pinpoint which of them is the guilty party before time runs out?
pick murderer these professor_boysenberry miss_cherry doctor_lime
pick crime_scene these foyer billiards_room library
run set_murder_weapon

room foyer You're in the grand foyer.
prop dest_name the foyer

room billiards_room You're in the billiards room.
prop dest_name the billiards room

room library You're in the library.
prop dest_name the library

verb go_to_the_foyer
goto foyer

setverb go_to_the_foyer intransitive
!at foyer

verb go_to_the_billiards_room
goto billiards_room

setverb go_to_the_billiards_room intransitive
!at billiards_room

verb go_to_the_library
goto library

setverb go_to_the_library intransitive
!at library

thing professor_boysenberry Professor Boysenberry
name Professor Boysenberry
loc foyer
tags alive male proper_name

thing miss_cherry Miss Cherry
name Miss Cherry
loc foyer
tags alive female proper_name

thing doctor_lime Doctor Lime
name Doctor Lime
loc foyer
tags alive female proper_name

rule
all these professor_boysenberry miss_cherry doctor_lime
run wander $this

proc wander person
pick new_room these foyer billiards_room library
eq $person.loc $new_room
sayat $new_room {the $Person} paces back and forth.

proc wander person
sayat $person.loc {the $Person} decides to go to {$new_room.dest_name}.
assign old_room $person.loc
sayat $new_room {the $Person} arrives from {$old_room.dest_name}.
put $person $new_room

verb search_for_clues
eq $room $crime_scene
say There's no doubting it ... a murder took place in this very room!

verb search_for_clues
say Nothing seems out of the ordinary.

setverb search_for_clues intransitive

setverb question
has $this alive

setverb accuse
has $this alive

verb question
eq $this $murderer
say {the $this} is unusually evasive in response to your questions. {Nom $this} must be the murderer!

verb question
eq $this professor_boysenberry
say "Please resolve this matter forthwith," says the professor. "I must return to my alembics."

verb question
eq $this miss_cherry
say "A murder!" says Miss Cherry. "How scandalous!"

verb question
eq $this doctor_lime
say "From the gruesome nature of the evidence, I concur that this was indeed a murder," says the doctor.

proc set_murder_weapon
random weapon_number 3
eq $weapon_number 1
write murder_weapon a large telephone directory

proc set_murder_weapon
eq $weapon_number 2
write murder_weapon a plate of putrefied sushi

proc set_murder_weapon
eq $weapon_number 3
write murder_weapon the spear of Longinus

verb accuse
say "There is no doubt," you cry triumphantly, "that the murder was carried out by {the $this}, right here in {$room.dest_name}, with {$murder_weapon}!"
eq $room $crime_scene
eq $this $murderer
say The police investigation bears out your hypothesis, and you are lauded once again as the world's most brilliant detective.
die You have won

verb accuse
say But your hypothesis is swiftly disproven, and the murderer is never brought to justice.
die You have failed
4 Likes

Fantastic! It’s really cool seeing how closely the Gruescript structure can mirror the Dialog one.

1 Like

I’m planning to take a crack at this in twine, I’ll move the category to general authoring.

1 Like

At this rate I should write up a full proper spec!

Mini-Cluedo

A Loose Specification for a Tiny IF Demo

A murder has been committed, and it’s up to the player to solve it!

The game must involve:

  • Three or more rooms, which the player can move freely between
    • This can involve compass directions, GO TO, or whatever means of movement is most appropriate
  • Three or more suspects, who move independently between the rooms
    • They can move at random or on a fixed path, but must visit every room, and must move independently (i.e. not always staying together)
  • At the beginning of the game, one room is randomly chosen as the crime scene, and one suspect randomly chosen as the murderer
  • The player can search their current room to determine whether or not it is the crime scene
  • The player can question any suspect in their current room to determine whether or not they are the murderer
  • The player can accuse any suspect in their current room
    • If they accuse the murderer, at the crime scene, they win!
    • Otherwise, they lose
  • The player can wait or otherwise allow time to pass, to get the right suspects in the right rooms

That seem appropriate? Not too specific, not too vague?

4 Likes

I wrote a Twine/Harlowe implementation. I just took the text from Daniel’s Dialog version.

We’ll start off with a list of locations which are also Twine passage names, and one of suspects who each have a name, a movement verb, and a random current location (“here”). We set a random $crime_scene and $murderer name (and also a $weapon for flavor).

:: Initialize [startup]
{
(set: $locations to (a: "the hedge maze", "the fountain", "the rose garden"))
(set: $suspects to (a:
	(dm: "name", "Miss Peach", "verb", "flounces",
		"here", (either: ...$locations)),
	(dm: "name", "Reverend Brown", "verb", "shuffles",
		"here", (either: ...$locations)),
	(dm: "name", "Sergeant Gray", "verb", "strides",
		"here", (either: ...$locations))
))
(set: $crime_scene to (either: ...$locations))
(set: $murderer to (either: ...$suspects)'s name)
(set: $weapon to (either: "waffle iron", "poisoned croquemboche", "spell of ancient power"))
}

Each location passage has a header, a brief description, and then we’ll (display:) a “choices” passage to select the links we want to show.

:: the hedge maze
## The hedge maze
What gruesome secrets could be hidden amidst these tangled paths?

(display: "choices")

:: the fountain
## The fountain
The pleasant burble of the fountain could easily cover a victim's screams.

(display: "choices")

:: the rose garden
## The rose garden
The thorns are long and sharp, the petals red as blood.

(display: "choices")

Each suspect has “refutation” passage for when you question them but they’re innocent. I could have put these in the datamap with the other suspect data. And if I was working in the Twine editor, I probably would have. But building a twee file in a text editor, it’s so easy to add more passages that I just did that (and then I didn’t have to worry about having quotes within a quoted string).

:: Miss Peach's refutation
"I do declare! Bless your heart, you really thought I could do such a wicked thing?"

:: Reverend Brown's refutation
She shakes her head. "May the Lord forgive you for such an insulting question."

:: Sergeant Gray's refutation
"Harrumph! An officer of the law commit murder? Unthinkable! Absolutely absurd."

Then we start off with a message and a link to a random location (not necessarily the murder location, although when I was testing, they were the same six times in a row and I was starting to be pretty worried that I had a bug).

:: Start
Could it be? A murder most foul, at this pleasant garden party? Your crime senses are tingling—it must be!

You've dispersed all the guests except your three suspects. Now, can you (link-goto: "prove your suspicions", (either: ...$locations)) before it's too late? Or is this the end of your illustrious detecting career?

## MINI-CLUEDO
### An Interactive Demonstration

Now let’s look at the code of the thing. “choices” will randomly move the suspects around, and report their movement. Then it’ll figure out which choices to offer (I’ve broken it down into movement and suspect choices), and finally it has a “hook” to log responses to actions we perform while we’re in this location.

:: choices
(display: "move suspects")(display: "suspect movement report")
(display: "movement choices") (display: "suspect choices")
|log>[]

It was easier to move all the suspects first, recording their old location (“from”) and setting their new location (“here”), and then do the reporting afterwards. So moving the suspects is a simple (altered:) loop that alters each suspect’s data.

I named the reporting passage to read nicely with (display: "suspect movement report"). It’s also pretty much a simple loop that filters through the suspects and reports any that either end up here or leave here. Half of this code is just setting temporary abbreviations so I can write fun things like _name _verbs in from _there instead of the far more verbose and annoying print statements I’d need otherwise.

:: move suspects
{ (set: $suspects to (altered:
	via it + (dm: "from", its here, "here", (either: ...$locations)),
	...$suspects
)) }

:: suspect movement report
{ (set: _location to (passage:)'s name)
(for: each _suspect, ...$suspects)[
	(set: _name to _suspect's name)
	(set: _verbs to _suspect's verb)
	(set: _there to _suspect's from)
	(set: _here to _suspect's here)
	(if: _there is _location)[
		(if: _here is _location)[_name mills about.` `]
		(else:)[_name _verbs away to _there.` `]
	] (else-if: _here is _location)[_name _verbs in from _there.` `]
] }

Movement choices are Search, and then Go followed by a list of locations (with “wait” substituted for the current location). The Search link appends to the “log” hook and I’ve put the success and failure text in the code here.

:: movement choices
{
	<p>(link-reveal: "Search")[(append: ?log)[\
		(if: (passage:)'s name is $crime_scene)[\
			Your search uncovers some conclusive evidence: a murder took place in (print: (passage:)'s name)!
		](else:)[\
			No crimes seem to have happened here.
		]
	]]
	<p>Go (for: each _L, ...$locations)[\
		(if: _L is (passage:)'s name)[- (link-goto: "wait ", _L)]\
		(else:)[- (link-goto: _L)]\
	]
}

For the suspect choices, we start by filtering them to only the suspects who are here:

:: suspect choices
{ (set: _here to (passage:)'s name)
(set: _suspects to (find: where its here is _here, ...$suspects))

Then if there are any, we show Question (list of suspects) and Accuse (list of suspects).

Again, the Question links append to the log hook. This time they include the “You’ve found your murderer!” text, but the character’s responses are pulled from passages named “Reverend Brown’s refutation” etc.

For the accusations, we only want you to be able to do it once. So instead of appending to the log, we save your accusation (the suspect you chose, and the current location) and then we jump to the Accusation passage.

(if: _suspects's length > 0)[
	<p>Question (for: each _S, ..._suspects)[ -
		(link-reveal: _S's name)[(append: ?log)[\
			(if: _S's name is $murderer)[As you question (print: _S's name) you notice a slight nervous twitch. No doubt about it, you've found your murderer!]
			(else:)[(display: _S's name + "'s refutation")]
		]]
	]
	<p>Accuse (for: each _S, ..._suspects)[ -
		(link-reveal: _S's name)[\
			(set: $accused to _S's name)\
			(set: $accused_location to _here)\
			(goto: "Accusation")\
		]
	]
] }

The accusation says who you think it is (you say “I knew it!” whether you’re right or wrong) and then has an if-statement with further text about your success or failure, and finally a link to restart the game.

:: Accusation
"I knew it! It was $accused, in $accused_location, with the $weapon!"\
(if: $accused is $murderer and $accused_location is $crime_scene)[\
	 No one can fault your logic, and soon the murderer is carted away for trial.

	''*** (link: "Victory!")[(restart:)] ***''
](else:)[\
	But your logic was unsound. The murderer goes free.

	''*** (link: "Defeat...")[(restart:)] ***''
]

Here’s the built HTML and the Twee source – with the last bits of Twee boilerplate it’s 130 lines or so? For a bigger game this could turn into a serious wall of links (already it can be I think 10 in the worst case?) so we’d want to get smarter about what we present or how (verb/noun cycling links plus a “go” button?) but as a starting point it’s not bad.

harlowe-mini-cluedo.zip (137.0 KB)

5 Likes

Fantastic! I love seeing these different implementations, and knowing basically nothing about Twine before this, I love the 's syntax. Very readable code!

I’ve been trying to think of how to improve the spec slightly with the investigative parts. For example, currently it seems questioning a suspect will immediately determine if they are the murderer. Do they just admit it? Perhaps instead, you have to ask people about other people. or something. But therein in a combinatorial problem.

A while back i made a kind-of mini-cluedo. It also chose the villain randomly, and worked by questioning. However people didn’t like it much.

Murder at the Manor

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…)

1 Like

I’ll try to find time to finish my mini cluedo example. I’d like also to create some suitable visual scenes in context.

Here’s my WIP stab (good word) of the “Hall”. I wanted to give it a moody look with shadowy corners and dark walkways where anyone could be quietly “bumped off” without raising the alarm.

Also, are we assuming the victim is Dr. Black?

1 Like

OT: What ray-tracer render software you used ?

Best regards from Italy,
dott. Piergiorgio.

This is great stuff! I’m definitely seeing the differences between the Twine formats now.

Yeah, this definitely isn’t a very good game—but sort of like Cloak of Darkness, I wanted to keep it simple enough to implement without a standard library. The main point is: “in a choice-based system, can you have choices that are reusable between multiple scenes, and can you select the list of choices based on multiple independent pieces of state?” I know that’s decently hard in Ink and quite hard in ChoiceScript, but it seems to be much easier in Twine.

EDIT: So if you wanted each suspect to exculpate another suspect, for example, that would still be completely valid! I just didn’t want to put it in the spec, for fear of obfuscating the core point it was meant to make.

You can if you want! I’ve been enjoying watching different people put different spins on the suspects and the rooms as they reimplement it; the victim could be the same. Dr Black, Mr Boddy, the butler disguised as Mr Boddy…

2 Likes

Yeah, I’ve been trying to think of something simple that doesn’t involve a whole bunch of necessary interrogation;

Let’s you have suspects: A, B, C, D and locations: X, Y, Z, T.

And there is a single “verb” you can do, which is “question someone”. Whereby you ask, “where were you at the time of the murder?” Each of A-D answers X-T, but of course the villain is lying.

If we can assume each person was in a different location at the time of the murder, then the location not given in any answer is the murder room. Furthermore, this will be given twice. So you know the murder is one of two people. But which one is lying?

Let say A->X, B->Y, C->Z and D->Z. so T is the murder room and the murder is either C or D. But also you know A and B are innocent.

Knowing A and B are telling the truth, i wanted to come up with another question that you could then ask which would vindicate either C or D, whichever was not the murderer.

For example, maybe you could ask A and B who was in Z? But if they knew that then you could just ask who was in T?

Not quite right at the mo.

Sounds like building a knights-and-knaves puzzle, which would also be a fun coding challenge!

It’s rendered with Nvidia Iray which they give free but only works on their expensive graphics cards!

Here’s another picture i rendered for the Cluedo “Lounge”. I thought this classical scene might work well. Lots of entrances and exits. No blood here though, so maybe the rope or the lead pipe!

right-click, open to embiggen,

mah, I still prefer the good ol’ PoV (which allow realistic enough graphics for coders lacking visual arts prowess…) which is somewhat dated, but opensource…

(yes, I actually program 3D rendered graphics, albeit simple)

Best regards from Italy,
dott. Piergiorgio.

I’ve started working on my own (Ink) version, and I figured it might be nice to add an element of danger, wherein if you investigate the room while alone with the murderer they’ll stab you in the back while you’re distracted. Not sure it helps that much, though.

2 Likes

Huh. I was thinking it might be fun to do a Dendry version; show how storylet selection serves similar purposes as if-statements or rule matching in other languages. But Dendry is actually super minimal in a lot of ways so we’d have to resort to JavaScript for some of the data modelling, I think. But it still might be interesting for the presentation side.

3 Likes