1

My Intent: Making a CYOA Using Objects

I'm quite new to Javascript and have been trying to make a simple CYOA game, using this code from this Reddit comment as a template. However, I want to use objects (whose values are intended to be constant) to store the various string values for the messages and the object each choice points to, as opposed to having all the objects in arrays and having to point to them using using their index in the array. My reasoning is that it'd be (theoretically) simpler for me to organize using strings like "msg_001" or "story5_28" rather than having to change a bunch of numbers in the event I inserted some new set of messages in the middle of an array.

My Problem: The First Message Isn't Displayed Again

Basically, I want to loop back to the first message and its set of answers, but it won't.

The initial printCurrentMsg() works (changing the content of the "message" divs to the values of msgText, and looping through the object's choices array to set the buttons in the "choices" div, based on the object specified in currentMsg) and the buttons' respective onlick attributes seem to work, until they're set to show msg_000.

It appears to be that whatever the value of currentMsg is, printCurrentMsg won't show the object the string refers to, other than when it does initially. Additionally, after using console.log at various points in the script, I noticed that currentMsg isn't changed, and using console.log(typeof) for both currentMsg and window[currentMsg] shows that the former is a string and the latter is an object. Am I unintentionally creating two separate variables?

I've tried...

  • ...using parameters in printCurrentMessage.
  • ...using currentMsg in the functions instead of window[currentMsg].
  • ...using dot notation instead of bracket notation.
  • ...using this[] instead of window[].

I'm not sure whether this has to do with asynchronicity, I'm accessing object properties incorrectly, my comprehension of scope is flawed, or if I'm erroneously using global variables. Should I be using some sort of callback?

Using a "dummy" msg_000—making another object with a different name but the same properties—serves as a stopgap solution, but I still wouldn't understand what the problem is. Having all the msg_*** objects in an array and referring to them by index number instead of string would also work, but I'm hesitant to rely on that for both the aforementioned tediousness and the fact that I still don't understand why the value of currentMsg remains unchanged.

In order to better articulate my problem, here is a jsfiddle with my code, and I shall post it below as well.

//messages
var msg_000 = { //Starts with this one, I want to be able to go back to it
  msgName: "msg_000",
  msgText: "Sup! Choose an option!",
  choices: [
    ans_000 = {
      ansText: "Climb a hill!",
      ansGoto: "msg_001" //this works
    },
    ans_001 = {
      ansText: "Skin a cat!",
      ansGoto: "msg_002" //this works
    },
    ans_002 = {
      ansText: "Build a birdhouse!",
      ansGoto: "msg_003" //this works
    }
  ]
};
var msg_001 = {
  msgName: "msg_001",
  msgText: "You summit the great snowy peaks!",
  choices: [
    ans_000 = {
      ansText: "Talk to the Recursion Guru!",
      ansGoto: "msg_000" //this doesn't work
    }
  ]
};
var msg_002 = {
  msgName: "msg_002",
  msgText: "You suffer severe lacerations to the face!",
  choices: [
    ans_000 = {
      ansText: "Start Over",
      ansGoto: "msg_000" //this doesn't work
    }
  ]
};
var msg_003 = {
  msgText: "You build a pretty average looking birdhouse. Some grackles have moved in nonetheless, placing their various knicknacks, bedding materials, and chrono-gateways within their new abode.",
  choices: [
    ans_000 = {
      ansText: "Step through the chrono-gateway!",
      ansGoto: "msg_000" //this doesn't work
    },
    ans_001 = {
      ansText: "I think I wanna climb that mountain over there.",
      ansGoto: "msg_001" //this works
    }
  ]
}

var currentMsg = "msg_000"; //the first message is "msg_000"

printCurrentMsg = function() {
  document.getElementById("message").innerHTML =
    window[currentMsg].msgText;
  //sets the message (the div with the id of "message")
  //based on the "currentMsg" variable. "currentMsg.msgText" 
  //doesn't seem to work.
  var choices = "";
  for (var i = 0, l = window[currentMsg].choices.length; i < l; i++) {
    choices += "<p><button onclick='setMessage(" +
      window[currentMsg].choices[i].ansGoto + ")'>" +
      window[currentMsg].choices[i].ansText + "<br>Goto " +
      window[currentMsg].choices[i].ansGoto + "</button></p>";
    //make the buttons, sets the button's onclick 
    //"setMessage" function's parameter to the the value of 
    //the "ansGoto" property -> in the answers object at the 
    //i/th index of the choices property array -> in the 
    //"msg_(number)" object."
  };
  document.getElementById("choices").innerHTML = choices;
  //takes the value of the "choices" [local?] variable and puts 
  //it in the "choices" div.
};

setMessage = function(msg) {
  window[currentMsg] = msg; //I think this is the source of all 
  //my problems, it's supposed to set "currentMsg" to the value 
  //of the "msg" parameter, but when I check it with 
  //console.log(currentMsg) it hasn't been changed (i.e., still 
  //it's initial value of "msg_000") and when I use 
  //console.log(window[currentMsg]) it returns "[Object 
  //object]"; using typeof shows me that "currentMsg" is a 
  //string and "window[currentMsg]" is an object. I thought 
  //they both were the same object, am I unintentionally 
  //creating two different objects?
  printCurrentMsg(); //runs that function, seems to display the 
  //messages except the ones from object "msg_000".
};

printCurrentMsg(); //Displays the initial message and choices 
//from "msg_000", but after a new message is chosen it won't 
//display "msg_000" if it's pointed to from an "ansGoto" 
//property.
<!DOCTYPE html>
<html>

<body>

  <div id="message"></div>
  <!-- "msgText" goes here -->
  <div id="choices"></div>
  <!-- "choices" go here -->

</body>

</html>

Thank you for your time.

Barmar
  • 741,623
  • 53
  • 500
  • 612
innerHTML
  • 13
  • 3
  • I hope the title of my question isn't too vague; the fact that I'm not sure how to succinctly word my question is probably telling of the nature of my problem. – innerHTML Feb 07 '18 at 22:07
  • Don't use dynamic variable names, use an object to hold all the messages. – Barmar Feb 07 '18 at 22:13

1 Answers1

0

When setMessage() does window[currentMsg] = msg;, it replaces the value of the variable holding a message. E.g. if the current message is msg_000, and you do setMessage(msg_002), it's equivalent to writing msg_000 = msg_002;.

What you really want to do is change the value of currentMsg to be the name of the new message. So you should do: currentMsg = msg.msgName;.

You were also missing the msgName property in msg_003.

As a best practice, you shouldn't use global variables for all this. Create your own object messages, and use messages[currentMsg].

//messages
var msg_000 = { //Starts with this one, I want to be able to go back to it
  msgName: "msg_000",
  msgText: "Sup! Choose an option!",
  choices: [
    ans_000 = {
      ansText: "Climb a hill!",
      ansGoto: "msg_001" //this works
    },
    ans_001 = {
      ansText: "Skin a cat!",
      ansGoto: "msg_002" //this works
    },
    ans_002 = {
      ansText: "Build a birdhouse!",
      ansGoto: "msg_003" //this works
    }
  ]
};
var msg_001 = {
  msgName: "msg_001",
  msgText: "You summit the great snowy peaks!",
  choices: [
    ans_000 = {
      ansText: "Talk to the Recursion Guru!",
      ansGoto: "msg_000" //this doesn't work
    }
  ]
};
var msg_002 = {
  msgName: "msg_002",
  msgText: "You suffer severe lacerations to the face!",
  choices: [
    ans_000 = {
      ansText: "Start Over",
      ansGoto: "msg_000" //this doesn't work
    }
  ]
};
var msg_003 = {
  msgName: "msg_003",
  msgText: "You build a pretty average looking birdhouse. Some grackles have moved in nonetheless, placing their various knicknacks, bedding materials, and chrono-gateways within their new abode.",
  choices: [
    ans_000 = {
      ansText: "Step through the chrono-gateway!",
      ansGoto: "msg_000" //this doesn't work
    },
    ans_001 = {
      ansText: "I think I wanna climb that mountain over there.",
      ansGoto: "msg_001" //this works
    }
  ]
}

var currentMsg = "msg_000"; //the first message is "msg_000"

printCurrentMsg = function() {
  document.getElementById("message").innerHTML =
    window[currentMsg].msgText;
  //sets the message (the div with the id of "message")
  //based on the "currentMsg" variable. "currentMsg.msgText" 
  //doesn't seem to work.
  var choices = "";
  for (var i = 0, l = window[currentMsg].choices.length; i < l; i++) {
    choices += "<p><button onclick='setMessage(" +
      window[currentMsg].choices[i].ansGoto + ")'>" +
      window[currentMsg].choices[i].ansText + "<br>Goto " +
      window[currentMsg].choices[i].ansGoto + "</button></p>";
    //make the buttons, sets the button's onclick 
    //"setMessage" function's parameter to the the value of 
    //the "ansGoto" property -> in the answers object at the 
    //i/th index of the choices property array -> in the 
    //"msg_(number)" object."
  };
  document.getElementById("choices").innerHTML = choices;
  //takes the value of the "choices" [local?] variable and puts 
  //it in the "choices" div.
};

setMessage = function(msg) {
  currentMsg = msg.msgName;
  printCurrentMsg(); //runs that function, seems to display the 
  //messages except the ones from object "msg_000".
};

printCurrentMsg(); //Displays the initial message and choices 
//from "msg_000", but after a new message is chosen it won't 
//display "msg_000" if it's pointed to from an "ansGoto" 
//property.
<!DOCTYPE html>
<html>

<body>

  <div id="message"></div>
  <!-- "msgText" goes here -->
  <div id="choices"></div>
  <!-- "choices" go here -->

</body>

</html>
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • This is excellent, thank you. I thought my error was in using `window[currentMsg]`. The missing `msgName` stems from an earlier attempt to find a solution using properties (clearly I should have kept trying from that angle). I tried to purge all extraneous code for the sample, I guessed I missed those `msgName` properties in the first four objects. Good thing I did! – innerHTML Feb 07 '18 at 22:46