Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Conversation Dialogue for Twine

edited March 2014 in Workshop
I have previously used choicescript for writing, however, I found the lack of a node map really difficult and I had to write my own using other software to keep track of all of the important choices etc.  Then I came across Twine, and saw something that offered a node map (yay!), but didn't offer something similar to fakechoice, which is basically giving the user choices which wouldn't really result in different paths. 

I've been working on some javascript (which I have also posted about in the help forum here), and with the assistance of the kind folk here and on stackoverflow, I have managed to cobble something together which seems to do everything I want.  For example, if you would like to have a conversation with an NPC, but don't want to create multiple nodes to do this, then this is what the script is capable of.

Here's a demo of what it can do:  Dropbox

It's running in sugarcane, and all of the text for the demo with the exception of the check variables passage at the end is in one passage. 

This is NOT a custom macro for twine, as I'm certainly not at the level I need to be in understanding those to be able to make this into one, but I will probably begin to attempt to change it into a custom macro at some point.  Anyway, I thought I would share the script in case anyone was interested. 

Here's a link to the actual javascript: fakechoice1.js

For this to work, you have to use html which would look similar to this:
<div class="inlinechoice-0" id="0">
<a class="question" id="1">Question 1</a><br>
<a class="question" id="2">Question 2</a><br>
</div>
This presents the user with a list of options, which will disappear once one of the questions is clicked.  The next piece of html would look like this:
<div class="answer-1">
You asked the first question.
</div>
<div class="answer-2">
You asked the second question.
</div>
Note that <a class="question" id="1"> would return <div class="answer-1">, so the numbering here is important. 

Finally, you need this piece of html so that everything appears where it should.
<div id="dialogue_container"> </div>
<div id="append"><br>You've finished asking questions.<br>
The dialogue_container is where all of your chosen questions and answers will appear.  The append div will show once there are no more questions and I guess is optional.  In the example above I haven't nested any additional questions within the answers, but the demo link does do that.  Inlinechoices begin at 0 and increment by 1, so if you were to nest them in an answer div you would call them inlinechoice-1 with an id of 1, and inlinechoice-2 etc.  I'm not claiming that this is an elegant solution by any means, however, it's what I've managed to work out via a combination of google, stackoverflow and various Twine resources. 

Currently, there are a few issues, which I am hoping to iron out. 

Issue one is that I am having to place the javascript in an external .js to the twine .tws as I can't get it to work otherwise.  I believe that I should be able to place the contents of the javascript into a script passage and simply call it using <<display>> as advised here however, I couldn't get this to work.

Issue two is spacing and breaks.  If you check the demo, you'll find the spacing is a bit odd.  There also appears to be some slight indentations going on for reasons I don't understand, and <<nobr>> and <<silently>> don't really seem to help.

Issue three is twine variables.  The javascript currently passes all of the choices made in a passage to an array, which is updated once you leave the passage.  This is more of a work around than a desired solution though, it would be nice to update the array in passage.  In the mean time, to pick up if certain choices were made, I'll probably use the methods described on here, which should suffice my needs for the moment.  Of all of the issues, this is probably the one I can live with quite happily. 

Issue four is that I want to make this into a custom macro.  To get it to work currently, there's quite a lot of clumsy html required, and I'm sure I just need to do something like replace and custom macro tags with the required html rather than having it directly in the passage.

Anyway, I'm posting this here in case it's of any use to anyone, and I'll probably use this thread to record any updates and improvements.  I know this is useful certainly for me, and hopefully it will be for others. 

Comments

  • Dang, Bawpie, I hate to be the barer of bad news, but I clicked the game and it errored: "Error: (Syntax error, unrecognized expression: div[class*=inlinechoice)"

    So, I clicked the Dropbox link again. No error, but nothing happened when I clicked the links in the story.

    So, I downloaded it and used the latest version of Chrome, Firefox and IE to open it locally. Clicking the links did nothing at all, but again no error.

    I opened it up in Twine and took a look, but it seemed there were multiple different inlinechoice DIV's, so I didn't mess with it further.

    Windows 7 64-bit.

    Maybe something was wrong with the jQuery or being on Dropbox? Maybe my computer's just stupid.
  • Ah, well that was weird, because it was working on IE and Chrome, and also on my phone (Chrome) in Windows 8.  So I tested it on Firefox and got the same error!  Then I realised it was because I was missing a ']' in two of my attribute selectors (as someone already pointed out on stackoverflow so now I see why I needed to fix that)!  D'oh!  So I've fixed that now, so it'd be very cool if you could quickly check to see if that's resolved the error for you too? 

    I have noticed another weird issue though...if you accidentally click in the gap between one of the choices, everything disappears, even if you don't actually click a choice which I guess is because you're clicking within the inlinechoice div, even if you're not clicking on anything...so I need to fix that too.  Hah.  Oh well, I'm further along than I was two weeks ago anyway.  :)

    Thanks for trying so many options with this though, it was certainly my error, and nothing to do with you (or your PC)!

    Edit:  Managed to fix the clicking on the gap issue. 

  • Looks like you got it!

    Pretty cool, Bawpie! :)
  • bawpie wrote:

    This is NOT a custom macro for twine, as I'm certainly not at the level I need to be in understanding those to be able to make this into one, but I will probably begin to attempt to change it into a custom macro at some point.  Anyway, I thought I would share the script in case anyone was interested. 


    I hope you will be able to make this into a macro at some point down the track, it would be mighty handy for doing a lot of dialogue in a game and save time. That would be awesome. I am sure many Twine users will use it in their games.

    bawpie wrote:

    Issue one is that I am having to place the javascript in an external .js to the twine .tws as I can't get it to work otherwise.  I believe that I should be able to place the contents of the javascript into a script passage and simply call it using <<display>> as advised here however, I couldn't get this to work.


    I tried out the demo and it worked fine via the dropbox link, but when I downloaded the html and imported it into Twine and ran it, but it just outputted all the dialogue text at once. So I am guessing this is 'issue one' you are talking about? 
  • Well, having played a little with this in my own project, I know I need to make it into a custom macro just to make my own life easier :)  So I definitely intend to try to do that, so watch this space. 

    Issue one refers to this example:  Internal JS Demo

    And this is a link to the non-working tws:  here

    Basically, in that example you can click on the links, but nothing happens.  I think what you've encountered is possibly because importing the html into twine hasn't included the .css styling?  If you want to see how it looks in Twine, this is a link to the working tws file:  here

    Thanks though, it's good to know others would find this useful. 
  • Made a little more progress on this today, mainly thanks to TheMadExile:

    HTML Link
    TWS Link

    Basically, I've been able to cut down the amount of html required to get this to work.  So now, the syntax looks like this:
    <<choiceblock 1>>
    <<question 1 $test $test+1>><br>"So how are you feeling?" you enquire.<<endquestion>>
    <<endchoiceblock>>

    <<answer 1>> "Yeah, not too bad chummer."
    <<choiceblock 2>>
    <<question 2 $mood "pleasant">>"Good to know," you remark.<<endquestion>>
    <<question 3 $mood "unfriendly">><br>"Don't call me chummer," you warn.<<endquestion>>
    <<endchoiceblock>>

    <<answer 2>>He grins.<<endanswer>>
    <<answer 3>>He frowns.<<endanswer>>

    <<endanswer>>

    <div id="dialogue_container"> </div>

    <<append>>Before you continue, you hear a commotion at the window.
    <br>[[Check Choices|variables]]<<endappend>>
    However, I'm still having to use a script to externally load the javascript that does all of the work (the custom macro just allows me to use the custom tags), and I'm also still having to add the <div id="dialogue_container"> </div> into any passage that uses it, so still not ideal.  In all, I'd say it's about 75% there at the moment.  Currently I can only figure out how to assign one variable at a time inside the <<question>> tag, but hopefully someone over in the help forum can help me with that.

  • Wouldn't something like this work?
    <<nobr>>
    <<$set question = 1>>
    <<if $question is 1>>
    Question 1<br>
    [[Answer 1.1|self][$question to 2;$answer 1 to 1]]<br>
    [[Answer 1.2|self][$question to 3;$answer 1 to 2]]<br>
    <endif>>
    <<if $question is 2>>
    Question 2<br>
    [[Answer 2.1|self][$question to 3;$answer 2 to 1]]<br>
    [[Answer 2.2|self][$question to 4;$answer 2 to 2]]<br>
    <endif>>
    <<if $question is 3>>
    Question 3<br>
    [[Answer 3.1|self][$question to 2;$answer 3 to 1]]<br>
    [[Answer 3.2|next][$question to 3;$answer 3 to 2]]<br>
    <endif>>
    <<if $question is 4>>
    Question 4<br>
    [[Answer 4.1|next][$answer 4 to 1]]<br>
    [[Answer 4.2|next][$answer 4 to 2]]<br>
    <endif>>
    <<nobr>>
    ...when you arrive at next, you'd need to look at the value of $question to see which answers are valid.

    Should be possible to write one that'll work with a conversation in a javascript object, which means you could dump the whole thing into a macro...
    <<set $conv to conv1>>
    <<converse>>
  • Possibly?  I'm open to looking at alternative ways of doing this, but it's hard for me to tell from your example of how this would work.  I'm really, really clueless when it comes to javascript I'm afraid. 

    I've tried putting your example into Twine (with a couple of changes such as keeping the <<set $question = 1>> in a separate passage), but couldn't get it to work although I'm not sure why.  With this method, you'd also have to spread the choices over at least two passages if you had any nesting, and what the posted code achieves it keeping it all in one passage.   

    Here's what I tried (in a passage titled self):
    <<if $question eq 1>>
    Question 1<br>
    [[Answer 1.1|Self][$question = 2;$answer 1 = 1]]<br>
    [[Answer 1.2|Self][$question = 3;$answer 1 = 2]]<br>
    <<elseif $question eq 2>>
    Question 2<br>
    [[Answer 2.1|Self][$question = 3;$answer 2 = 1]]<br>
    [[Answer 2.2|Self][$question = 4;$answer 2 = 2]]<br>
    <<elseif $question eq 3>>
    Question 3<br>
    [[Answer 3.1|Self][$question = 2;$answer 3 = 1]]<br>
    [[Answer 3.2|next][$question = 3;$answer 3 = 2]]<br>
    <<elseif $question eq 4>>
    Question 4<br>
    [[Answer 4.1|next][$answer 4 = 1]]<br>
    [[Answer 4.2|next][$answer 4 = 2]]<br>
    <<endif>>
    But it doesn't seem to set the variable to change when clicking on answer 1.1 or 1.2. 
  • I haven't really had a crack at doing a dialogue system yet and taking the latest ideas discussed in this thread I think I have something that works well.

    What would of been nice would be if you could combine <<choice [[link syntax]]>> and [[Continue|Questions][$question = 5;$answer4 =0]] so you can tack on variables and have the questions only show once but it doesn't seem possible.

    I created a demo (sugarcane) showing what I have done, there are two types of question/answer sets one where there are three questions the player can ask a NPC and the other where the player is being asked a question and has two possible answers to choose from.

    I have placed comments that are fairly obvious in some of the passages for clarity for everyone (i hope they make sense). There may be a more refined method but at least this works. However this might get hard to manage in a big game. The only bit I need to polish is my counter for player asking npc questions can easily count if the player clicks the same question link three times before moving onto next question. The passages have been named as topic of question or answer as a placeholder, I suppose this would be the way to manage topics.

    Conversation Demo:
    https://www.mediafire.com/?mshytnb7hyz09yy
  • @Dazakiwi38: That's certainly one way of doing quite a robust dialogue system, I'll certainly have a play around with it in a bit.

    The aim with my system was to have something that wouldn't require multiple passages, so I guess it's less for a full on conversation, and more for incidental dialogues which perhaps don't change anything immediately and appear quite linear, but may silently adjust the characters opinion of you.  So, for example:

    You greet Joe with a smile.
    You nod at Joe.
    You scowl at Joe.

    3 options, but actually, I don't want to write a lot of if statements, I just want Joe to respond in one of three ways:

    Joe smiles back at you.
    Joe nods back at you.
    Joe scowls back.

    Then I want the passage to continue...

    "Hey, you two!" your boss yells.  "Get to work!"

    Using Twine's traditional methods, this would require extensive use of macros in ways they weren't quite intended, or multiple passages.  So I guess that's what I'm aiming to make a little easier. 

    Thanks to The Pixie and MadExile, I'm now able to set multiple variables when a particular question is asked. 

    This is achieved with this syntax:  <<question 1 variable "variablevalue" variable2 "variablevalue2">>

    So, I'd say I'm about 80% there.  Just need to get rid of the html dialogue_container and figure out how to integrate my custom .js. 

    It's definitely interesting to see how others would approach this though. 

    Edit:  Just added the dialogue container div into the choiceblock macro, so no need to declare it in the passage any more.  :)

    Demo here:  HTML Example | TWS Example
  • Just needs a little tweaking...
    <<if $question is 0>><<set $question to 1>><<endif>>
    <<if $question eq 1>>
    You answers <<$answer1>> <<$answer2>> <<$answer3>>
    Question 1<br>
    [[Answer 1.1|Self][$question = 2;$answer1 = 1]]<br>
    [[Answer 1.2|Self][$question = 3;$answer1 = 2]]<br>
    <<elseif $question eq 2>>
    You answers <<$answer1>> <<$answer2>> <<$answer3>>
    Question 2<br>
    [[Answer 2.1|Self][$question = 3;$answer2 = 1]]<br>
    [[Answer 2.2|Self][$question = 4;$answer2 = 2]]<br>
    <<elseif $question eq 3>>
    You answers <<$answer1>> <<$answer2>> <<$answer3>>
    Question 3<br>
    [[Answer 3.1|Self][$question = 2;$answer3 = 1]]<br>
    [[Answer 3.2|next][$question = 3;$answer3 = 2]]<br>
    <<elseif $question eq 4>>
    You answers <<$answer1>> <<$answer2>> <<$answer3>>
    Question 4<br>
    [[Answer 4.1|next][$answer4 = 1]]<br>
    [[Answer 4.2|next][$answer4 = 2]]<br>
    <<endif>>
    I'm currently working on something where you can put the conversation in a javascript object, which makes it somewhat easier to edit...
    <<set $convo to
    {
    start:{
    text:"You seem to be having a conversation with a disembodied voice. Friendly enough though.",
    options: [ {choice:"'So what's this about tricking the word counter?'", goto:"Chatter",next:"tricking"} ],
    },
    tricking:{
    text:"'Yes, tricking. It's pretty dumb and there are some simple workarounds that'll let you get a lot of words in that it thinks are only one.'",
    options: [ {choice:"'Could you tell me one of them?'",goto:"Chatter",next:"whichone"} ],
    },
    whichone:{
    text:"'Which one of them would you like to know about?'",
    There seem to be a couple of bugs in how twine 1.4.1 handles arrays in links and variable references in tails that mean I can't use a really short macro to do it, but I've got a version with a flattened loop running - although it's having difficulty updating the convo structure to remember where it's been...
  • Got the macro working now...

    Place in a Passage called converse:
    <<nobr>>
    <<if $oldlocation neq 0>><<set $convo[$oldlocation].options[$picked].visited to 1>><<endif>>

    <<print $convo[$location].text>><br><br>

    <<set $oldlocation to $location>>
    <<set $convo[$location].visited to 1>>

    <<set $options to $convo[$location].options>>

    <<if $options.length gt 0>><<set $opt0 to $options[0]>>
    <<set $show to 1>>
    <<set $check to $opt0.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt0.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link0 to $opt0.goto>><<if $link0 is 0>><<set $link0 to $self>><<endif>>
    [[0 - <<$opt0.choice>><br>|$link0][$picked to 0;$location to $opt0.next]]<<endif>><<endif>>

    <<if $options.length gt 1>><<set $opt1 to $options[1]>>
    <<set $show to 1>>
    <<set $check to $opt1.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt1.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link1 to $opt1.goto>><<if $link1 is 0>><<set $link1 to $self>><<endif>>
    [[1 - <<$opt1.choice>><br>|$link1][$picked to 1;$location to $opt1.next]]<<endif>><<endif>>

    <<if $options.length gt 2>><<set $opt2 to $options[2]>>
    <<set $show to 1>>
    <<set $check to $opt2.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt2.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link2 to $opt2.goto>><<if $link2 is 0>><<set $link2 to $self>><<endif>>
    [[2 - <<$opt2.choice>><br>|$link2][$picked to 2;$location to $opt2.next]]<<endif>><<endif>>

    <<if $options.length gt 3>><<set $opt3 to $options[3]>>
    <<set $show to 1>>
    <<set $check to $opt3.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt3.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link3 to $opt3.goto>><<if $link3 is 0>><<set $link3 to $self>><<endif>>
    [[3 - <<$opt3.choice>><br>|$link3][$picked to 3;$location to $opt3.next]]<<endif>><<endif>>

    <<if $options.length gt 4>><<set $opt4 to $options[4]>>
    <<set $show to 1>>
    <<set $check to $opt4.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt4.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link4 to $opt4.goto>><<if $link4 is 0>><<set $link4 to $self>><<endif>>
    [[4 - <<$opt4.choice>><br>|$link4][$picked to 4;$location to $opt4.next]]<<endif>><<endif>>

    <<if $options.length gt 5>><<set $opt5 to $options[5]>>
    <<set $show to 1>>
    <<set $check to $opt5.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt5.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link5 to $opt5.goto>><<if $link5 is 0>><<set $link5 to $self>><<endif>>
    [[5 - <<$opt5.choice>><br>|$link5][$picked to 5;$location to $opt5.next]]<<endif>><<endif>>

    <<if $options.length gt 6>><<set $opt6 to $options[6]>>
    <<set $show to 1>>
    <<set $check to $opt6.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt6.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link6 to $opt6.goto>><<if $link6 is 0>><<set $link6 to $self>><<endif>>
    [[6 - <<$opt6.choice>><br>|$link6][$picked to 6;$location to $opt6.next]]<<endif>><<endif>>

    <<if $options.length gt 7>><<set $opt7 to $options[7]>>
    <<set $show to 1>>
    <<set $check to $opt7.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt7.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link7 to $opt7.goto>><<if $link7 is 0>><<set $link7 to $self>><<endif>>
    [[7 - <<$opt7.choice>><br>|$link7][$picked to 7;$location to $opt7.next]]<<endif>><<endif>>

    <<if $options.length gt 8>><<set $opt8 to $options[8]>>
    <<set $show to 1>>
    <<set $check to $opt8.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt8.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link8 to $opt8.goto>><<if $link8 is 0>><<set $link8 to $self>><<endif>>
    [[8 - <<$opt8.choice>><br>|$link8][$picked to 8;$location to $opt8.next]]<<endif>><<endif>>

    <<if $options.length gt 9>><<set $opt9 to $options[9]>>
    <<set $show to 1>>
    <<set $check to $opt9.showif>><<if $check neq 0 and $convo[$check].visited is 0>><<set $show to 0>><<endif>>
    <<set $check to $opt9.hideif>><<if $check neq 0 and $convo[$check].visited is 1>><<set $show to 0>><<endif>>
    <<if $show is 1>><<set $link9 to $opt9.goto>><<if $link9 is 0>><<set $link9 to $self>><<endif>>
    [[9 - <<$opt9.choice>><br>|$link9][$picked to 9;$location to $opt9.next]]<<endif>><<endif>>

    <<endnobr>>
    In your setup, you need to define your conversation:
    <<nobr>>
    <<set $meetjoe to {
    start:{
    text: "You meet Joe. How do you greet him?",
    visited: 0,
    options: [ {choice: "With a smile", next:"smilejoe"},
    {choice: "With a nod", next:"nodjoe"},
    {choice: "With a scowl", next:"scowljoe"} ],
    },
    smilejoe:{
    text: "You smile at Joe. He smiles back.",
    visited: 0,
    options: [ {choice: "Uh-oh, the boss is coming!", next:"boss"} ],
    },
    nodjoe:{
    text: "You nod at Joe. He nods back.",
    visited: 0,
    options: [ {choice: "Uh-oh, the boss is coming!", next:"boss"} ],
    },
    scowljoe:{
    text: "You scowl at Joe, the skiving lecher. He scowls back at you.",
    visited: 0,
    options: [ {choice: "Uh-oh, the boss is coming!", next:"boss"} ],
    },
    boss:{
    text: "The boss does not look happy. 'Get to work you lazy good for nothings!'",
    visited: 0,
    options: [ {choice: "Do some work", goto:"Work"} ],
    },
    }>>
    <<endnobr>>
    Create a Passage to hold it - MeetJoe
    <<nobr>>
    <<if $location is 0>><<set $convo to $meetjoe>><<set $location to "start">><<set $self to passage()>><<endif>>
    <<converse>>
    <<endnobr>>
    You then need a passage called Work that the conversation will pass control to when it ends.  At the start of work you need to include:
    <<set $location to 0>>
    You can check which conversation nodes were visited with $meetjoe[node].visited it'll be 1 if that choice was picked.
  • Well, that looks pretty detailed.  Is the conversation bit added in a separate passage?

    I've tweaked my macro a little, so there is no need for any custom html directly in passage, as the macro will handle it.  I'm still having to call the fakechoice1.js via the LoadJS macro but rather than do it manually, it's integrated into the dialoguemacro code.  I think it's about as seamless as I can make it for now.  For this to work you need to have the following saved as a .js script saved in the same folder as your game (it should be called fakechoice1):
    var $arr = []

    $('div[class*=inlinechoice]').on('click','.question', function(e) {
    var $this = $(this),
    $id = $this.prop('id');

    //This selects the id of the inlinechoice div.
    $cid = $(this).closest('div[class*=inlinechoice]').attr("id")

    //This part will remove any choices once a selection has been made.
    $('.inlinechoice-' + $cid).hide();

    //This picks up the question selected ready to be passed to the dialogue_container.
    var $question = $this.text() + '<br><br>';

    //This passes the chosen answers into a variable, and then searches to see if
    //there are any nested choices (identified by any further questions in the branch)
    //and appends the contents of the append tag if not.
    $str = $('.answer-' + $id).html();
    $term = "question";
    $index = $str.indexOf($term);
    if ($index != -1) {
    //Do Nothing
    } else {
    $('#append').show();
    }

    //This is for Twine, to push the choices into an array.
    $arr.push($id);
    state.history[0].variables['choice_id'] = $arr.join(', ');

    //This pushes the chosen question and the corresponding answer branch into the dialogue_container.

    $("#dialogue_container").append("<span class = 'dark'>"+$question+'</span>');
    $("#dialogue_container").append($('.answer-' + $id).contents());

    });

    Then you need 2 script passages:

    My dialogue macro script:

    //USAGE:
    //<<choiceblock 1>>
    //<<question 1>>This is question 1<<endquestion>>
    //You can also assign a variable in a question tag like this:
    //<<question 2 variable "q2asked">>This is question 2<<endquestion>>
    //If you wish to assign multiple variables, then use this:
    //<<question 3 variable "q3asked" variable2 "foo">>This is question 3<<endquestion>>
    //<<endchoiceblock>>
    //
    //<<answer 1>>This is the response to question 1<<endanswer>>
    //
    //You may also nest a choiceblock inside an answer like this:
    //<<answer 2>> Some text <<choiceblock 2>><<question 3>>This is question 3<<endquestion>><<endchoiceblock>><<endanswer>>
    //
    //<<answer 3>>This is the response to question 3<<endanswer>>
    //
    //<<append>>This is shown after answer 1 or answer 2 is revealed<</append>>
    //
    //NB: Currently there is 1 additional element required:
    //You need to have fakechoice1.js saved in the same folder as your game file, and also Dan Cox's LoadJS custom macro.


    (function () {
    version.extensions['dialoguemacro'] = {
    major: 1,
    minor: 0,
    revision: 0
    };

    macros['append'] = macros['answer'] = macros['question'] = macros['choiceblock'] =
    {
    handler: function(place, macroName, params, parser)
    {

    // process the contents of the container macro
    var openTag = macroName
    , closeTag = "end" + macroName
    , start = parser.source.indexOf(">>", parser.matchStart) + 2
    , end = -1
    , tagBegin = start
    , tagEnd = start
    , opened = 1;
    while ((tagBegin = parser.source.indexOf("<<", tagEnd)) !== -1
    &amp;&amp; (tagEnd = parser.source.indexOf(">>", tagBegin)) !== -1)
    {
    var tagName = parser.source.slice(tagBegin + 2, tagEnd)
    , tagDelim = tagName.search(/\s/);
    if (tagDelim !== -1)
    {
    tagName = tagName.slice(0, tagDelim);
    }
    tagEnd += 2;
    switch (tagName)
    {
    case closeTag:
    opened--;
    break;
    case openTag:
    opened++;
    break;
    }
    if (opened === 0)
    {
    end = tagBegin;
    break;
    }
    }

    // if we successfully found an end tag for the macro
    if (end !== -1)
    {

    parser.nextMatch = tagEnd;

    id = params[0];
    contents = parser.source.slice(start, end);

    //based on which macro has been used, different actions are required.
    switch (macroName)
    {
    case "choiceblock":
    //The choiceblock is what is wrapped around the questions and will make them disappear when clicked.

    switch(id)
    {
    case "1":
    new Wikifier(place, '<<loadJS fakechoice1.js>><div class="inlinechoice-' + id + '" id="' + id + '"><br>' + contents + '</div>' + '<div id="dialogue_container"> </div>');
    break;
    default:
    new Wikifier(place, '<div class="inlinechoice-' + id + '" id="' + id + '"><br>' + contents + '</div>' + '<div id="dialogue_container"> </div>');
    }
    break;

    case "question":
    //Questions should be nested within a choiceblock and each assigned an id which will link to a corresponding
    //answer. A single variable may be set within a question tag.

    //This part checks to see if any variables are being set within the question tag.
    if (params.length - 1 % 2 == 1) {
    throwError(place, "<<" + macroName + ">>: odd number of parameters given");
    return;
    }

    el = document.createElement('a');
    el.className = 'internalLink';
    el.href = "javascript:void(0)";
    el.innerHTML = ('<a class="question" id="' + id + '">' + contents + '</a>');

    el.onclick = function(){
    for (var index = 0;index < params.length; index += 2) {
    var s;
    if (isNaN(params[index+2])) {
    s = "state.history[0].variables." + params[index+1] + " = '" + params[index+2] + "'";
    } else {
    s = "state.history[0].variables." + params[index+1] + " = " + params[index+2];
    }
    eval(s);
    }
    };
    place.appendChild(el);
    break;


    case "answer":
    //This creates an answer div with a corresponding id which will be shown when the corresponding question is clicked.
    new Wikifier(place, '<div class="answer-' + id + '">' + contents + '</div>');
    break;

    case "append":
    //This creates the append div which the css will render hidden until there are no further choices.
    new Wikifier(place, '<div id="' + macroName + '">' + contents + '</div>');
    break;
    }
    }
    else
    {
    throwError(place, "<<" + macroName + ">>: cannot find a matching close tag");
    }
    },
    };
    macros["endchoiceblock"] = { handler: function () {} };
    macros["endquestion"] = { handler: function () {} };
    macros["endanswer"] = { handler: function () {} };
    macros["endappend"] = { handler: function () {} };
    }());
    Dan Cox's script for loading external .js files.
    macros['loadJS'] =
    {
    handler: function(place, object, parameters)
    {
    var se = document.createElement("script");
    se.type = 'text/javascript';
    se.src = parameters[0];
    var hT = document.getElementsByTagName("HEAD")[0];
    hT.appendChild(se);
    if(se.innerText) {eval(se.innerText);}
    else {eval(se.textContent);}
    }
    }
    And finally, in a stylesheet passage you'd need this:
    div[class*='inlinechoice-'] { cursor: pointer; }

    div[class*='answer-'] {display:none;}

    #append {display:none;}

    .dark {
    transition: all 3s; -webkit-transition: all 3s;
    opacity: 0.4;
    }
    .dark:hover {
    transition: all 0.1s; -webkit-transition: all 0.1s;
    opacity:1;
    text-shadow: 0 0 1em white;
    }

    So, a passage would look like this:

    You approach Joe, your work colleague.

    <<choiceblock 1>>
    <<question 1 Joe "friendly">>"Hey Joe," you greet cordially. Joe is a pretty cool guy.<<endquestion>><<br>
    <<question 2 Joe "neutral">>You nod at Joe. You regard him as a colleague and nothing more.<<endquestion>><<br>>
    <<question 3 Joe "hatred">>You can't help but scowl when you see Joe, he's a complete waste of skin and oxygen.<<endquestion>>
    <<endchoiceblock>>

    <<answer 1>>Joe smiles and gives a little wave when he sees you.<<endanswer>>
    <<answer 2>>Joe nods back to you then goes back to what he was doing.<<endanswer>>
    <<answer 3>>Joe notes your frown and returns the sentiment. You two are never going to get on.<<endanswer>>

    <<append>>"Hey, you two! Get a move on," yells your boss from the other side of the yard.<<endappend>>

    Finally, you need to make sure Jquery is enabled in the storysettings passage.

    The append part is entirely optional and should only appear once all choices have been exhausted (one choice per choiceblock), but every passage that has a choiceblock should start with <<choiceblock 1>>.  Any additional choiceblocks in a passage should have a different id, (i.e. 2, 3, 4 etc.).

      You can set multiple variables in each question (which will only be set when that question is clicked), but you must name your variable without the $ sigil.  It'd be nice to fix that at some point, but it's purely superficial. 

    For me, this is a lot easier to understand and read then looking at lots of if statements or custom html, but I guess everyone is different.  It's certainly not elegant, but it does appear to work.  There isn't much error handling built into either the fakechoice1.js or the custom macros, so I guess it'd be fairly easy to mess it up, but all of the examples I've tried so far seem to work.  :)

    Example files:  TWS File | HTML File
  • I'm trying this right now, but in the case it doesn't work.. can this add to an existing variable value?
  • I'm getting this error now.

    There is a technical problem with this story (dialogue: unterminated regular expression literal).You may be able to continue playing, but some parts may not work properly.
Sign In or Register to comment.