Line break in typewritter text. [SOLVED]

<br> was giving me an error in type writter text, I wasn't quite sure what to do, and when I switched it to an expression rather than message, it switches back.

This is what fixed it. First enable advanced scripts, then go to the UI initialization scrip, add a javascript box and paste this into it:

eval("$(function(){function t(t){this.each(function(){$(this).data({typewritertext:$(this).html()}).attr({typewriterspeed:t}).html('')}),a||n()}var a,n=function(){var t=$('[typewriterspeed]'),e=0;if(t.length){var r=(t=t.first()).data('typewritertext');if(r)if(r.match(/^\\s*</)){var i=r.split('>')[0];if(t.append(i+'>'),i.match(/\\s*<(a|b|i|u|s|font|em|strong|span|div)(\\s.*)?(?<!\\/)$/i)){var s=t.children().last();s.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=s}else if(i.match(/\\s*<\\/(a|b|i|u|s|em|strong|span|div)(\\s*)$/i)){var p=t.parent();p.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=p}t.data('typewritertext',r.substr(i.length+1))}else t.append(r.substr(0,1)),e=t.attr('typewriterspeed'),t.data('typewritertext',r.substr(1));else t.removeData('typewritertext').removeAttr('typewriterspeed');e?a=setTimeout(n,e):n()}else a=0},e=$.fn.typewriter;enableRichTypewriter=function(){$.fn.typewriter=t},disableRichTypewriter=function(){$.fn.typewriter=e}});")

Hopefully this helps anyone who wants to add html elements such as color or <br>.
While doing this means you cannot run two type writter texts at the same time, you can switch between the different modes using the javascript commands JS.enableRichTypewriter() and JS.disableRichTypewriter().

Thanks to mrangel for the solution.


The typewriter effect uses .text() to get the text to process. That means it will discard any HTML inside the message, including the <br/>.

Fairly recently I posted a script that would modify the typewriter function so that you can call it multiple times and the lines appear one after the other, so possibly you could use that rather than putting a <br> in the text. Or I could modify it so that it'll catch line breaks.

EDIT: Went looking for my last post on the subject, but I can't find it. Really wish this forum had a decent search feature.


OK, a first attempt at this. A modified typewriter function which should work with <br> (and <img>). Just taking the function I'd previously written, and adding a nasty kludge to make it treat XML tags as a single character. Note that this will still break on tags with contents, so you can't use it with <b> or <em>.

$(function () {
  var typewriter_timer;
  var typewriter_iterator = function () {
    var $ele = $('span[typewriterspeed]');
    if ($ele.length) {
      $ele = $ele.first();
      var text = $ele.data('typewritertext');
      var speed = $ele.attr('typewriterspeed');
      if (text) {
        if (text.match(/^\s*</)) {
          var tag = text.split('>')[0];
          $ele.append(tag+'>');
          $ele.data('typewritertext', text.substr(tag.length+1));
        } else {
          $ele.append(text.substr(0, 1));
          $ele.data('typewritertext', text.substr(1));
        }
      } else {
        $ele.removeData('typewritertext').removeAttr('typewriterspeed');
      }
      typewriter_timer = setTimeout(typewriter_iterator, speed);
    } else {
      typewriter_timer = 0;
    }
  };
  $.fn.typewriter = function (speed) {
    this.each(function () {
      $(this).data({typewritertext: $(this).html()}).attr({typewriterspeed: speed}).html('');
    });
    if (!typewriter_timer) {
      typewriter_iterator();
    }
  };
});

Or a form that you can paste into your UI Initialisation script:

JS.eval("$(function(){var a,p=function(){var t=$('span[typewriterspeed]');if(t.length){var e=(t=t.first()).data('typewritertext'),r=t.attr('typewriterspeed');if(e)if(e.match(/^\\s*</)){var i=e.split('>')[0];t.append(i+'>'),t.data('typewritertext',e.substr(i.length+1))}else t.append(e.substr(0,1)),t.data('typewritertext',e.substr(1));else t.removeData('typewritertext').removeAttr('typewriterspeed');a=setTimeout(p,r)}else a=0};$.fn.typewriter=function(t){this.each(function(){$(this).data({typewritertext:$(this).html()}).attr({typewriterspeed:t}).html('')}),a||p()}});")

EDIT: Silly mistake. Hope there aren't any more, but not in a position to test it rn.


I am very new at functions, so I don't truely know where to put all this code. What do I name the function, what are the parameters? Where do I call it?


Lemme try testing it first though, maybe I'll figure it out. Everywhere I put it, it says there's an internal error.


That's Javascript code, not Quest code.

Like most Javascript modifications, you'll want to wrap it in a JS.eval block and put it in the UI initialisation script (on the "Advanced Scripts" tab of the game object). The second version has a little bit of Quest code wrapped around it, and is reduced to one line ready to paste into code view.

That is designed to replace the existing $.fn.typewriter function, which is called by Quest's TextFX_Typewriter core function. So once you put that line in the UI initialisation script, you can include <br/> with the built-in typewriter function.

(I've tested it now; there was a missing '' in the script I posted above, but I'm going to edit that to fix it right now)


It's bugging me that my script doesn't work properly. So here's a version which should also let you put bold, underline, colours, etc. in typewriter text.

$(function () {
  var typewriter_timer;
  var typewriter_iterator = function () {
    var $ele = $('[typewriterspeed]');
    if ($ele.length) {
      $ele = $ele.first();
      var text = $ele.data('typewritertext');
      var speed = $ele.attr('typewriterspeed');
      if (text) {
        if (text.match(/^\s*</)) {
          var tag = text.split('>')[0];
          $ele.append(tag+'>');
          if (tag.match(/\s*<(a|b|i|u|font|em|strong|span|div)(\s.*)?(?<!\/)$/i)) {
            var $new = $ele.children().last();
            $new.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $new;
          } else if (tag.match(/\s*<\/(a|b|i|em|strong|span|div)(\s*)$/i)) {
            var $old = $ele.parent();
            $old.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $old;
          }
          $ele.data('typewritertext', text.substr(tag.length+1));
        } else {
          $ele.append(text.substr(0, 1));
          $ele.data('typewritertext', text.substr(1));
        }
      } else {
        $ele.removeData('typewritertext').removeAttr('typewriterspeed');
      }
      typewriter_timer = setTimeout(typewriter_iterator, speed);
    } else {
      typewriter_timer = 0;
    }
  };
  $.fn.typewriter = function (speed) {
    this.each(function () {
      $(this).data({typewritertext: $(this).html()}).attr({typewriterspeed: speed}).html('');
    });
    if (!typewriter_timer) {
      typewriter_iterator();
    }
  };
});

Or compressed and wrapped in Quest code ready for insertion into the UI Initialisation script:

JS.eval("$(function(){var p,n=function(){var t=$('[typewriterspeed]');if(t.length){var e=(t=t.first()).data('typewritertext'),r=t.attr('typewriterspeed');if(e)if(e.match(/^\\s*</)){var i=e.split('>')[0];if(t.append(i+'>'),i.match(/\\s*<(a|b|i|u|font|em|strong|span|div)(\\s.*)?(?<!\\/)$/i)){var s=t.children().last();s.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=s}else if(i.match(/\\s*<\\/(a|b|i|em|strong|span|div)(\\s*)$/i)){var a=t.parent();a.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=a}t.data('typewritertext',e.substr(i.length+1))}else t.append(e.substr(0,1)),t.data('typewritertext',e.substr(1));else t.removeData('typewritertext').removeAttr('typewriterspeed');p=setTimeout(n,r)}else p=0};$.fn.typewriter=function(t){this.each(function(){$(this).data({typewritertext:$(this).html()}).attr({typewriterspeed:t}).html('')}),p||n()}});")

Error running script: Error evaluating expression 'GetAllChildObjects(room)': GetAllChildObjects function expected object parameter but was passed 'null'
Error running script: Error evaluating expression 'GetBoolean(room, "transparent")': GetBoolean function expected object parameter but was passed 'null'
Error running script: Error evaluating expression 'GetAllChildObjects(newParent)': GetAllChildObjects function expected ```

O.o What did I do wrong? I took the second one and wrapped it in quest code...

That's an odd set of errors. Looks like what I'd expect to see if you did RemoveObject (player) or similar.

Can you share the game so I can take a look, see if I can spot the problem?


I can get you this close...
But it needs a different "wait" command...

T1 = "This is a typewriter test..."
T2 = "with 2 lines??? "
T3 = "Maybe."
TextFX_Typewriter (T1, 100)
wait {
  TextFX_Typewriter (T2, 100)
  wait {
    TextFX_Typewriter (T3, 100)
    // msg ("waiting.")
  }
}

@CodeKrafter:

Just double checked. The errors you pasted are a result of the player object not being in a room when Quest attempts to get a list of visible objects (which it does every turn).

Hopefully that's a separate issue from the typewriter thing.

@DarkLizerd
That's one solution, but means that the player has to press a key.
The solution I posted in another thread (Beginners Problems) would allow you to reduce that to:

T1 = "This is a typewriter test..."
T2 = "with 2 lines??? "
T3 = "Maybe."
TextFX_Typewriter (T1, 100)
TextFX_Typewriter (T2, 100)
TextFX_Typewriter (T3, 100)

while with the script I posted above (which I've now tested to confirm that it works as designed) you can still do it like that, or you can do:

TextFX_Typewriter ("This is a typewriter test...<br>with <b>3</b> lines??? <br>Maybe.", 100)

Alrighty, now that I put the player in a room (I'm pretty dumb), it's working now. Now the only issue I'm having is that two type writer commands cannot work at the same time and it's slow overall. Is there a way to only activate the script when I need it and deactive it as soon as the command with the line break or color is over?


two type writer commands cannot work at the same time

That was what someone asked for originally. If you want to allow newlines within a typewriter block, but not run one at a time, I can probably adjust the script to work like that. I'll take a look at it when I get home.

it's slow overall

I can't replicate this issue. For me it runs at the same speed as the built-in typewriter function, and topjs isn't showing any heavy processing. Have you tried adjusting the 'speed' parameter?

Ah… the start and end of each line are counting as characters, so there's a little pause. That could be improved.

$(function () {
  var typewriter_timer;
  var typewriter_iterator = function () {
    var $ele = $('[typewriterspeed]');
    var speed = 0;
    if ($ele.length) {
      $ele = $ele.first();
      var text = $ele.data('typewritertext');
      if (text) {
        if (text.match(/^\s*</)) {
          var tag = text.split('>')[0];
          $ele.append(tag+'>');
          if (tag.match(/\s*<(a|b|i|u|s|font|em|strong|span|div)(\s.*)?(?<!\/)$/i)) {
            var $new = $ele.children().last();
            $new.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $new;
          } else if (tag.match(/\s*<\/(a|b|i|u|s|em|strong|span|div)(\s*)$/i)) {
            var $old = $ele.parent();
            $old.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $old;
          }
          $ele.data('typewritertext', text.substr(tag.length+1));
        } else {
          $ele.append(text.substr(0, 1));
          speed = $ele.attr('typewriterspeed');
          $ele.data('typewritertext', text.substr(1));
        }
      } else {
        $ele.removeData('typewritertext').removeAttr('typewriterspeed');
      }
      if (speed) {
        typewriter_timer = setTimeout(typewriter_iterator, speed);
      } else {
        typewriter_iterator();
      }
    } else {
      typewriter_timer = 0;
    }
  };
  $.fn.typewriter = function (speed) {
    this.each(function () {
      $(this).data({typewritertext: $(this).html()}).attr({typewriterspeed: speed}).html('');
    });
    if (!typewriter_timer) {
      typewriter_iterator();
    }
  };
});

or Questified:

JS.eval ("$(function(){var p,n=function(){var t=$('[typewriterspeed]'),e=0;if(t.length){var r=(t=t.first()).data('typewritertext');if(r)if(r.match(/^\\s*</)){var i=r.split('>')[0];if(t.append(i+'>'),i.match(/\\s*<(a|b|i|u|s|font|em|strong|span|div)(\\s.*)?(?<!\\/)$/i)){var s=t.children().last();s.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=s}else if(i.match(/\\s*<\\/(a|b|i|u|s|em|strong|span|div)(\\s*)$/i)){var a=t.parent();a.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=a}t.data('typewritertext',r.substr(i.length+1))}else t.append(r.substr(0,1)),e=t.attr('typewriterspeed'),t.data('typewritertext',r.substr(1));else t.removeData('typewritertext').removeAttr('typewriterspeed');e?p=setTimeout(n,e):n()}else p=0};$.fn.typewriter=function(t){this.each(function(){$(this).data({typewritertext:$(this).html()}).attr({typewriterspeed:t}).html('')}),p||n()}});")

Is there a way to only activate the script when I need it and deactive it as soon as the command with the line break or color is over?

Should be able to do that.

$(function () {
  var typewriter_timer;
  var typewriter_iterator = function () {
    var $ele = $('[typewriterspeed]');
    var speed = 0;
    if ($ele.length) {
      $ele = $ele.first();
      var text = $ele.data('typewritertext');
      if (text) {
        if (text.match(/^\s*</)) {
          var tag = text.split('>')[0];
          $ele.append(tag+'>');
          if (tag.match(/\s*<(a|b|i|u|s|font|em|strong|span|div)(\s.*)?(?<!\/)$/i)) {
            var $new = $ele.children().last();
            $new.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $new;
          } else if (tag.match(/\s*<\/(a|b|i|u|s|em|strong|span|div)(\s*)$/i)) {
            var $old = $ele.parent();
            $old.attr('typewriterspeed', $ele.attr('typewriterspeed'));
            $ele.removeAttr('typewriterspeed');
            $ele = $old;
          }
          $ele.data('typewritertext', text.substr(tag.length+1));
        } else {
          $ele.append(text.substr(0, 1));
          speed = $ele.attr('typewriterspeed');
          $ele.data('typewritertext', text.substr(1));
        }
      } else {
        $ele.removeData('typewritertext').removeAttr('typewriterspeed');
      }
      if (speed) {
        typewriter_timer = setTimeout(typewriter_iterator, speed);
      } else {
        typewriter_iterator();
      }
    } else {
      typewriter_timer = 0;
    }
  };
  var originaltypewriter = $.fn.typewriter;
  var richtypewriter = function (speed) {
    this.each(function () {
      $(this).data({typewritertext: $(this).html()}).attr({typewriterspeed: speed}).html('');
    });
    if (!typewriter_timer) {
      typewriter_iterator();
    }
  };
  enableRichTypewriter = function () { $.fn.typewriter = richtypewriter; };
  disableRichTypewriter = function () { $.fn.typewriter = originaltypewriter; };
});

Or in Quest form:

JS.eval("$(function(){function t(t){this.each(function(){$(this).data({typewritertext:$(this).html()}).attr({typewriterspeed:t}).html('')}),a||n()}var a,n=function(){var t=$('[typewriterspeed]'),e=0;if(t.length){var r=(t=t.first()).data('typewritertext');if(r)if(r.match(/^\\s*</)){var i=r.split('>')[0];if(t.append(i+'>'),i.match(/\\s*<(a|b|i|u|s|font|em|strong|span|div)(\\s.*)?(?<!\\/)$/i)){var s=t.children().last();s.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=s}else if(i.match(/\\s*<\\/(a|b|i|u|s|em|strong|span|div)(\\s*)$/i)){var p=t.parent();p.attr('typewriterspeed',t.attr('typewriterspeed')),t.removeAttr('typewriterspeed'),t=p}t.data('typewritertext',r.substr(i.length+1))}else t.append(r.substr(0,1)),e=t.attr('typewriterspeed'),t.data('typewritertext',r.substr(1));else t.removeData('typewritertext').removeAttr('typewriterspeed');e?a=setTimeout(n,e):n()}else a=0},e=$.fn.typewriter;enableRichTypewriter=function(){$.fn.typewriter=t},disableRichTypewriter=function(){$.fn.typewriter=e}});")

(note that I'm typing code on my phone again, so not able to test these. Sorry for any typos)

Then you should be able to use the instructions JS.enableRichTypewriter() and JS.disableRichTypewriter() to switch between my method and the original one as needed.


Thank you so much, hopefully this will also help out others who want to do the same.


Alright. Now I'm having a new issue. As soon as the player moves to a new room, the typewritter just doesn't show up at all. It doesn't matter if the rich type writter is enabled or not. I've tried eliminating different elements but nothing seems to be the cause. It happens when the game switched from the startup script to the first room's script.


I'm trying to work out what's the issue there, but I can't see it just by reading through.

Have you got an example game that I could take a look at?


https://www.dropbox.com/s/fbyr73o2fxehte4/game.zip?dl=0


Were you able to download it?


If this can't be fixed, I'll just go back to normal text.


suggestion
make it so that you can delete comments incase they duplicate.


I've been through the code again, but there's nothing I can see that should be causing such a problem.
I'll see if I can make a game with the same problem so I can test it out myself, but my internet is being really sluggish today.

Testing something like this is really hard without an example game to poke.
I've tried inserting the function into one of my own test games, and the typewriter function occasionally fails to display a line but I can't see any pattern to it. But the same seems to happen with the default typewriter; which leaves me completely confused.

Adding the function to another, new, project, it seems to work as intended.


OK, I've copy-pasted the code from your game into a new one so I can test it, and it seems to be working fine.

Is your problem when it comes to the line "testestest"? Because that's as far as I got testing it, and that seems to be working fine for me.

It's worth noting that if you press a key to advance to the next section, it clears the screen and hides the long typewriter intro. If you press a key before it's finished, those typewriters continue running in the background.

Two possible ways to fix this.

  1. The following line can be run to make the typewriter finish:
JS.eval("$('[typewriterspeed]').each(function () {$(this).removeAttr('typewriterspeed').append($(this).data('typewritertext'));});")
  1. In the big chunk of javascript, change $('[typewriterspeed]') to $('[typewriterspeed]:not(.clearedScreen)') so that when it's deciding which is the 'currently running' typewriter line, it ignores any that have been hidden by clearing the screen.

That's the issue, now that I know what it is, I can fix it. Thanks.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums