intfiction.org

The Interactive Fiction Community Forum
It is currently Wed Sep 19, 2018 6:54 am

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Fri Dec 29, 2017 9:39 pm 
Offline

Joined: Fri Dec 29, 2017 9:31 pm
Posts: 4
I like the look and feel of Sugarcube, but I prefer the scripting environment of Snowman.

So I patched underscore templating into Sugarcube, just to see if it would work. Ended up being pretty easy, just one line of code changed, lol.

Now I am a happy internet-person. If this sounds interesting to you, too, I put the patched story format up at http://www.echohollow.net/sugarcube_underscore/ , along with instructions on usage and how to roll your own.

Currently only have State API exported, but it seems to be working. Will probably work on making it more feature complete as I need to.


Top
 Profile Send private message  
Reply with quote  
PostPosted: Sun Dec 31, 2017 11:13 am 
Offline

Joined: Fri Dec 29, 2017 9:31 pm
Posts: 4
FWIW, I have exported the rest of the Sugarcube APIs to the template processor, and added a config option and tag to compress multiple paragraph breaks into a single one (to remove blank lines resulting from code sections off by themselves and such). Same link as above, minimal documentation in README.php.

This is probably all I'll do with it for a while, as it seems to be adequate for my purposes and it's time to work on my game instead. But please let me know if you decide to use it for something or find any bugs or have any comments.


Top
 Profile Send private message  
Reply with quote  
PostPosted: Sat Jan 06, 2018 12:39 pm 
Offline

Joined: Fri Dec 29, 2017 9:31 pm
Posts: 4
Well, I think I figured out how to do this without patching the story format, so I will probably stop working on this patch and delete it from my repos.

This other way replaces the processText method on the passage before it is passed off to the wikifier. This seems like a really messy way to do it, but if there is some other way then I am too dumb to see it. :P

Even if it is a little messy, I think it's still less messy than trying to maintain a patch against upstream sugarcube and do releases.

In the story javascript:
Code:
//The underscore library will probably have to be pasted verbatim into 
//the story javascript for release, but this seems to work for testing
//and development.
$.getScript('file:///D:/path/to/underscore.js');

$(document).on(
      ':passagestart',
      function(ev) {
         // Don't try to process if underscore hasn't been loaded yet.
         if (_.template) {
            ev.passage.processText = function() {
               // Evaluate <% %> blocks.
               let processed = _.template(this.text)({
                  Dialog : Dialog,
                  Engine : Engine,
                  LoadScreen : LoadScreen,
                  Macro : Macro,
                  Save : Save,
                  Setting : Setting,
                  State : State,
                  Story : Story,
                  UI : UI,
                  UIBar : UIBar
               });
               // Strip leading and trailing newlines, and compress
               // multiple paragraph breaks into a single paragraph break.
               processed = processed.replace(/\n\n+/g, '\n\n').replace(
                     /^\n+|\n+$/g, '');
               
               // Everything past this was copied verbatim from the
               // original processText method.
               
               // Handle image passage transclusion.
               if (this.tags.includes('Twine.image')) {
                  processed = `[img[${processed}]]`;
               }
               return processed;
            };
         }
      });


You have to start the game on a "fake" passage, that does nothing but <<delay>> for a very short time, to give the underscore library time to load, and then <<goto>> the real start passage. A delay of even as little as 1ms seems to work (at least when using a local file with getScript; I haven't tried loading over http), but I've been using 100ms just in case. It's still pretty much instantaneous.


Top
 Profile Send private message  
Reply with quote  
PostPosted: Sat Jan 06, 2018 8:56 pm 
Offline
User avatar

Joined: Fri Aug 26, 2016 1:00 pm
Posts: 8
Location: Lafayette, Louisiana US
I don't buy the pitch—I think you're overselling things a bit—but it doesn't make me any difference what you use, so get your template on I guess.

I do, however, have some suggestions on how to monkey patch Underscore.js templates into the render pipeline. Both methods require some configuration—they're also untested.

Adding Underscore.js, via an external file (local or online), and patching in its template system: (goes in Story JavaScript or the equivalent)
Spoiler: show
Code:
/*
   This code is asynchronous, thus requires some special finagling.
   Underscore.js templates may be used anywhere except the `StoryInit`
   special passage.
*/
(function () {
   // Set the URL to Underscore.js here.  Local or remote files are acceptable.
   var libUrl = 'PASTE UNDERSCORE.JS URL HERE';

   // Grab a lock on the loading screen.
   var lockId = LoadScreen.lock();

   // Set up a utility function to release our lock on the loading
   // screen and reshow the the starting passage with Underscore.js
   // templates enabled.
   var unlockAndShow = function () {
      LoadScreen.unlock(lockId);
      Engine.show();
   };

   // Import Underscore.js and patch
   importScripts(libUrl).then(function () {
      // Monkey patch in our customized `<Passage>.processText()`.
      Passage.prototype.processText = function () {
         let processed = this.text;

         // Handle image passage transclusion.
         if (this.tags.includes('Twine.image')) {
            processed = '[img[' + processed + ']]';
         }

         // Normal passage handling.
         else {
            // Process <% %> blocks, remove all leading & trailing
            // newlines, and compact all internal sequences of
            // newlines into two newlines.
            processed = _.template(processed)({
               Dialog     : Dialog,
               Engine     : Engine,
               LoadScreen : LoadScreen,
               Macro      : Macro,
               Save       : Save,
               Setting    : Setting,
               State      : State,
               Story      : Story,
               UI         : UI,
               UIBar      : UIBar
            })
               .replace(/^\n+|\n+$/g, '')
               .replace(/\n\n+/g, '\n\n');

            // Handle `Config.passages.nobr` and the `nobr` tag.
            if (Config.passages.nobr || this.tags.includes('nobr')) {
               // Compact all internal sequences of newlines into single spaces.
               processed = processed.replace(/\n+/g, ' ');
            }
         }

         return processed;
      };

      // The starting passage has already been played.
      if (Engine.lastPlay !== null) {
         unlockAndShow();
      }

      // The starting passage is currently rendering.
      else if (Engine.isRendering()) {
         postdisplay['#monkey-patch-underscore.js'] = function (taskName) {
            delete postdisplay[taskName]; // single-use task
            unlockAndShow();
         };
      }

      // The starting passage's play state is unknown.
      else {
         setTimeout(unlockAndShow, 250);
      }
   }, function () {
      // Release our lock on the loading screen.
      LoadScreen.unlock(lockId);

      // The library failed to load, so we complain, though there's
      // likely already a error waiting for the player.
      throw new Error('Import of Underscore.js failed (url: "' + libUrl + '").');
   });
})();

Adding Underscore.js, directly via a wrapper, and patching in its template system: (goes in Story JavaScript or the equivalent)
Spoiler: show
Code:
/*
   This code is synchronous, thus doesn't require any special finagling.
   Underscore.js templates may be used anywhere.
*/
(function (define, exports) {
   /* PASTE UNDERSCORE.JS LIBRARY HERE */
}).call(window);
// Monkey patch in our customized `<Passage>.processText()`.
Passage.prototype.processText = function () {
   let processed = this.text;

   // Handle image passage transclusion.
   if (this.tags.includes('Twine.image')) {
      processed = '[img[' + processed + ']]';
   }

   // Normal passage handling.
   else {
      // Process <% %> blocks, remove all leading & trailing
      // newlines, and compact all internal sequences of
      // newlines into two newlines.
      processed = _.template(processed)({
         Dialog     : Dialog,
         Engine     : Engine,
         LoadScreen : LoadScreen,
         Macro      : Macro,
         Save       : Save,
         Setting    : Setting,
         State      : State,
         Story      : Story,
         UI         : UI,
         UIBar      : UIBar
      })
         .replace(/^\n+|\n+$/g, '')
         .replace(/\n\n+/g, '\n\n');

      // Handle `Config.passages.nobr` and the `nobr` tag.
      if (Config.passages.nobr || this.tags.includes('nobr')) {
         // Compact all internal sequences of newlines into single spaces.
         processed = processed.replace(/\n+/g, ' ');
      }
   }

   return processed;
};

_________________
Developer: SugarCube (a story format for Twine 1, Twine 2, Twee and compatibles)


Top
 Profile Send private message  
Reply with quote  
PostPosted: Sun Jan 07, 2018 11:34 am 
Offline

Joined: Fri Dec 29, 2017 9:31 pm
Posts: 4
Excellent. Thank you very much.


Top
 Profile Send private message  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group