Howdy, Stranger!

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

Reviving old macro for Sugarcube?

edited April 2016 in Help! with 2.0
---Using Twine 2 local/downloaded version, Sugarcube 2.2 (I think), Firefox 45.0.2,---

I am using Twine 2.0, Sugarcube 2.2 (I think), and Firefox 45.0.2

I am trying to use this old Twine 1 screen-shaking macro in Twine 2 with Sugarcube 2.2. I suspect the issue lies in how v2.0 defines things, and thus probably requires far more knowledge of Javascript that I have (next to none) to fix.

What's worse, when I copy/pasted the macro's javascript into the story's javascript section (don't pity me), it actually broke the Sugarcube Cyclinglink package, so that things such as <<timedinsert>> no longer worked until I removed the old code.

I suspect this macro was a little too obscure to get updated for the new versions of twine/sugarcube.

Any help in resuscitating this poor old macro would be much appreciated!

-Liyamu

(Original macro source: https://gist.github.com/dariusk/4651698)

Here's the screenShake Javascript:
function screenShake(time) {
  console.log(document);
  var el = document.getElementsByClassName('content');
  baz = el;
  console.log(baz[0]);
  el[0].className = el[0].className + ' shake';
  if (time > 0) {
    setTimeout(function () {
      el[0].className = 'content';
    }, time);
  }
};

// the screenShake macro. Adapted from Emmanuel Turner's article on creating Twine macros. http://eturnerx.blogspot.com/2012/12/how-to-create-custom-macros-in-twine.html
try {
  version.extensions['screenShakeMacro'] = {
    major: 1,
    minor: 0,
    revision: 0
  };
  macros['screenShake'] = {
    handler: function (place, macroName, params, parser) {
      var time = parseInt(params[0]);
      if (typeof time !== 'number') {
        time = 1000;
      }
      
      // we're overriding the fade function. It behaves as usual except it runs screenShake() if time >= 0.
      fade = function (el, options) {
        var current;
        var proxy = el.cloneNode(true);
        var direction = (options.fade == 'in') ? 1 : -1;

        el.parentNode.replaceChild(proxy, el);

        if (options.fade == 'in') {
          current = 0;
          proxy.style.visibility = 'visible';
        } else current = 1;

        setOpacity(proxy, current);
        var interval = window.setInterval(tick, 25);

        function tick() {
          current += 0.05 * direction;

          setOpacity(proxy, Math.easeInOut(current));

          if (((direction == 1) && (current >= 1)) || ((direction == -1) && (current <= 0))) {
            console.log('swapping fader proxy out');
            el.style.visibility = (options.fade == 'in') ? 'visible' : 'hidden';
            proxy.parentNode.replaceChild(el, proxy);
            delete proxy;
            window.clearInterval(interval);

            if (options.onComplete) options.onComplete();

            if (time >= 0) {
              screenShake(time);
              time = -1;
            }

          }
        };

        function setOpacity(el, opacity) {
          var percent = Math.floor(opacity * 100);

          // IE
          el.style.zoom = 1;
          el.style.filter = 'alpha(opacity=' + percent + ')';

          // CSS 3
          el.style.opacity = opacity;
        };
      };
    },
    init: function () {},
  };
} catch (e) {
  throwError(place, "screenShake Setup Error: " + e.message);
}

And here's the screenShake default CSS:
@keyframes shakeit {
  0% { transform: translate(2px, 1px) rotate(0deg); }
	10% { transform: translate(-1px, -2px) rotate(-1deg); }
	20% { transform: translate(-3px, 0px) rotate(1deg); }
	30% { transform: translate(0px, 2px) rotate(0deg); }
	40% { transform: translate(1px, -1px) rotate(1deg); }
	50% { transform: translate(-1px, 2px) rotate(-1deg); }
	60% { transform: translate(-3px, 1px) rotate(0deg); }
	70% { transform: translate(2px, 1px) rotate(-1deg); }
	80% { transform: translate(-1px, -1px) rotate(1deg); }
	90% { transform: translate(2px, 2px) rotate(0deg); }
	100% { transform: translate(1px, -2px) rotate(-1deg); }
}

@-o-keyframes shakeit {
	0% { -o-transform: translate(2px, 1px) rotate(0deg); }
	10% { -o-transform: translate(-1px, -2px) rotate(-1deg); }
	20% { -o-transform: translate(-3px, 0px) rotate(1deg); }
	30% { -o-transform: translate(0px, 2px) rotate(0deg); }
	40% { -o-transform: translate(1px, -1px) rotate(1deg); }
	50% { -o-transform: translate(-1px, 2px) rotate(-1deg); }
	60% { -o-transform: translate(-3px, 1px) rotate(0deg); }
	70% { -o-transform: translate(2px, 1px) rotate(-1deg); }
	80% { -o-transform: translate(-1px, -1px) rotate(1deg); }
	90% { -o-transform: translate(2px, 2px) rotate(0deg); }
	100% { -o-transform: translate(1px, -2px) rotate(-1deg); }
}

@-webkit-keyframes shakeit {
	0% { -webkit-transform: translate(2px, 1px) rotate(0deg); }
	10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); }
	20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); }
	30% { -webkit-transform: translate(0px, 2px) rotate(0deg); }
	40% { -webkit-transform: translate(1px, -1px) rotate(1deg); }
	50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); }
	60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); }
	70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); }
	80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); }
	90% { -webkit-transform: translate(2px, 2px) rotate(0deg); }
	100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); }
}

@-moz-keyframes shakeit {
	0% { -moz-transform: translate(2px, 1px) rotate(0deg); }
	10% { -moz-transform: translate(-1px, -2px) rotate(-1deg); }
	20% { -moz-transform: translate(-3px, 0px) rotate(1deg); }
	30% { -moz-transform: translate(0px, 2px) rotate(0deg); }
	40% { -moz-transform: translate(1px, -1px) rotate(1deg); }
	50% { -moz-transform: translate(-1px, 2px) rotate(-1deg); }
	60% { -moz-transform: translate(-3px, 1px) rotate(0deg); }
	70% { -moz-transform: translate(2px, 1px) rotate(-1deg); }
	80% { -moz-transform: translate(-1px, -1px) rotate(1deg); }
	90% { -moz-transform: translate(2px, 2px) rotate(0deg); }
	100% { -moz-transform: translate(1px, -2px) rotate(-1deg); }
}

.shake {
	-webkit-animation-name: shakeit;
	-webkit-animation-duration: 0.8s;
	-webkit-transform-origin:50% 50%;
	-webkit-animation-iteration-count: infinite;
	-webkit-animation-timing-function: linear;
  -moz-animation-name: shakeit;
	-moz-animation-duration: 0.8s;
	-moz-transform-origin:50% 50%;
	-moz-animation-iteration-count: infinite;
	-moz-animation-timing-function: linear;
  -o-animation-name: shakeit;
	-o-animation-duration: 0.8s;
	-o-transform-origin:50% 50%;
	-o-animation-iteration-count: infinite;
	-o-animation-timing-function: linear;
  animation-name: shakeit;
	animation-duration: 0.8s;
	transform-origin:50% 50%;
	animation-iteration-count: infinite;
	animation-timing-function: linear;
}
.shake{
	display:inline-block
}

Comments

  • That macro is an abomination in more ways that one. It's also pointless. Since it's accomplishing the shake via keyframe animations, there's no point in overriding the fade() function—and doing it badly. The CSS also needs some cleanup.

    Beyond being terrible, the reason it doesn't work in SugarCube is that SugarCube hasn't used the fade() function internally in, literally, ages—even v1 doesn't use it.

    The following macro requires SugarCube v2.5.0 or later. If you're using an older release of v2, a simple upgrade should get you on track.

    Script: (goes in Story JavaScript)
    /*
    	<<shake>> macro (requires SugarCube v2.5.0 or later)
    
    	Shakes the currently displayed passage.
    
    	Usage: <<shake [duration]>>
    		duration: (optional) The amount of time to shake the current
    		          passage (as a valid CSS time value; e.g. 5s and 500ms).
    		          If omitted, will default to 1 second (`1s`).
    
    	Examples:
    		<<shake>>     → Shakes the current passage for the default (1s)
    		<<shake 5s>>  → Shakes the current passage for 5s
    		<<shake 0s>>  → Shakes the current passage forever
    */
    Macro.add('shake', {
    	handler : function () {
    		var duration;
    
    		if (this.args.length === 0) {
    			duration = 1000;
    		}
    		else {
    			try {
    				duration = Util.fromCssTime(this.args[0]);
    			}
    			catch (e) {
    				return this.error(e.message);
    			}
    		}
    
    		var $passage = jQuery(
    			this.output.nodeType === Node.ELEMENT_NODE
    				? this.output
    				: '.passage'
    		);
    
    		$passage.addClass('shake');
    
    		if (duration > 0) {
    			setTimeout(function () {
    				$passage.removeClass('shake');
    			}, Engine.minDomActionDelay + duration);
    		}
    	}
    });
    
    Styles: (goes in Story Stylesheet)
    @-webkit-keyframes shakeit {
    	0% { -webkit-transform: translate(2px, 1px) rotate(0deg); }
    	10% { -webkit-transform: translate(-1px, -2px) rotate(-1deg); }
    	20% { -webkit-transform: translate(-3px, 0px) rotate(1deg); }
    	30% { -webkit-transform: translate(0px, 2px) rotate(0deg); }
    	40% { -webkit-transform: translate(1px, -1px) rotate(1deg); }
    	50% { -webkit-transform: translate(-1px, 2px) rotate(-1deg); }
    	60% { -webkit-transform: translate(-3px, 1px) rotate(0deg); }
    	70% { -webkit-transform: translate(2px, 1px) rotate(-1deg); }
    	80% { -webkit-transform: translate(-1px, -1px) rotate(1deg); }
    	90% { -webkit-transform: translate(2px, 2px) rotate(0deg); }
    	100% { -webkit-transform: translate(1px, -2px) rotate(-1deg); }
    }
    @-o-keyframes shakeit {
    	0% { -o-transform: translate(2px, 1px) rotate(0deg); }
    	10% { -o-transform: translate(-1px, -2px) rotate(-1deg); }
    	20% { -o-transform: translate(-3px, 0px) rotate(1deg); }
    	30% { -o-transform: translate(0px, 2px) rotate(0deg); }
    	40% { -o-transform: translate(1px, -1px) rotate(1deg); }
    	50% { -o-transform: translate(-1px, 2px) rotate(-1deg); }
    	60% { -o-transform: translate(-3px, 1px) rotate(0deg); }
    	70% { -o-transform: translate(2px, 1px) rotate(-1deg); }
    	80% { -o-transform: translate(-1px, -1px) rotate(1deg); }
    	90% { -o-transform: translate(2px, 2px) rotate(0deg); }
    	100% { -o-transform: translate(1px, -2px) rotate(-1deg); }
    }
    @keyframes shakeit {
    	0% { transform: translate(2px, 1px) rotate(0deg); }
    	10% { transform: translate(-1px, -2px) rotate(-1deg); }
    	20% { transform: translate(-3px, 0px) rotate(1deg); }
    	30% { transform: translate(0px, 2px) rotate(0deg); }
    	40% { transform: translate(1px, -1px) rotate(1deg); }
    	50% { transform: translate(-1px, 2px) rotate(-1deg); }
    	60% { transform: translate(-3px, 1px) rotate(0deg); }
    	70% { transform: translate(2px, 1px) rotate(-1deg); }
    	80% { transform: translate(-1px, -1px) rotate(1deg); }
    	90% { transform: translate(2px, 2px) rotate(0deg); }
    	100% { transform: translate(1px, -2px) rotate(-1deg); }
    }
    
    .shake {
    	display: inline-block;
    	-webkit-animation-duration: 0.8s;
    	     -o-animation-duration: 0.8s;
    	        animation-duration: 0.8s;
    	-webkit-animation-iteration-count: infinite;
    	     -o-animation-iteration-count: infinite;
    	        animation-iteration-count: infinite;
    	-webkit-animation-name: shakeit;
    	     -o-animation-name: shakeit;
    	        animation-name: shakeit;
    	-webkit-animation-timing-function: linear;
    	     -o-animation-timing-function: linear;
    	        animation-timing-function: linear;
    	-webkit-transform-origin: 50% 50%;
    	    -ms-transform-origin: 50% 50%;
    	     -o-transform-origin: 50% 50%;
    	        transform-origin: 50% 50%;
    }
    
  • Haha, wow. I'm glad I didn't write it, then! :) (not that I understand javascript at all)

    I had no idea it was that old either, although come to think of it, I was using Sugarcane back when I used it...

    Thank you so much for making a clean version of it! However, I can't seem to get it to work. :( I just now upgraded to the most recent SugarCube 2.5.0, and made sure the story itself was set to the newest format, and added the code into the story stylesheet and javascript sections. Neither <<shake>> nor <<shake 3s>> seemed to do anything. Is there something I'm missing?

    Thanks again!
  • I cut-n-pasted the Javascript and CSS into the relevant areas of a new SC 2.5.0 story, added some text and a call to the <<shake>> macro, and it works for me.

    I have attached an Archive file containing a demo story, use the Import From File option added it to your Story List.
  • edited April 2016
    I decided that I didn't like the default value and way of specifying an infinite shake, which I simply copied from the original macro, so I reworked the macro's arguments a bit. I think it makes a bit more sense now.

    The updated version of the <<shake>> macro, including documentation, is available on SugarCube 2's site (under: Downloads > Add-ons).
  • Awesome, you are guys are amazing.

    So I just figured out why it wasn't working: debug mode. In debug mode, the only thing that shakes is the actual macro <<shake>> if 'debug view' is clicked. Not sure why? But if I actually "play" the story, then it works as intended. But the "Test Story From Here" little bug icon does not. Weird.

    I'm just getting back into Twine after a 3-year hiatus, and boy has a lot changed! While I was a wizard back then, I feel I'm back to being a newbie in many ways.

    Last thing, while I still have your godly Twine-weaving attention: Is there a way to apply <<shake>> to a single element, ie. an internal link or word/sentence/picture/etc, instead of the whole passage? Or would that require another batch of fancy JS?
  • I cut-n-pasted the Javascript and CSS into the relevant areas of a new SC 2.5.0 story, added some text and a call to the <<shake>> macro, and it works for me.

    I have attached an Archive file containing a demo story, use the Import From File option added it to your Story List.

    I couldn't get this to download, or open in another page. I suspect I'm doing something dumb. I tried downloading/opening it with IE because sometimes Firefox gives me grief, but that didn't work either. Thanks anyway, though!
    I decided that I didn't like the default value and way of specifying an infinite shake, which I simply copied from the original macro, so I reworked the macro's arguments a bit. I think it makes a bit more sense now.

    The updated version of the <<shake>> macro, including documentation, is available on SugarCube 2's site (under: Downloads > Add-ons).

    Thanks for doing that. One major problem I saw during Twine 1 days was how fractured the documentation and availability of macros were. I know it's still early days in some ways, but I really appreciate having those macros all in one place. Especially now that there are so many branches of Twine forking all over.

    Is there a place on the forum or main website with a collection of stylesheets? I have an old stockpile of a bunch of custom stylesheets from before.
  • Liyamu wrote: »
    So I just figured out why it wasn't working: debug mode. In debug mode, the only thing that shakes is the actual macro <<shake>> if 'debug view' is clicked. Not sure why? But if I actually "play" the story, then it works as intended. But the "Test Story From Here" little bug icon does not. Weird.
    Ah. I wanted to allow <<shake>> to work if used directly within the passage or via player interaction (i.e. via <<click>>, <<button>>, etc). I got a bit clever there and tripped over the debug views. Shouldn't be hard to fix—maybe later today.

    That said, SugarCube's Test Mode is just what it says on the tin. It's a testing mode meant for debugging problems. It can, and probably will, cause layout issues—the problem here. It is really not meant for normal play-testing. Use it when something isn't working as you think it should, to get a visual representation of what's going on.

    Liyamu wrote: »
    Last thing, while I still have your godly Twine-weaving attention: Is there a way to apply <<shake>> to a single element, ie. an internal link or word/sentence/picture/etc, instead of the whole passage? Or would that require another batch of fancy JS?
    The shaking animation comes completely from the CSS, so all that would be needed to set any element shaking, normally, is to apply the "shake" class to it. The only real issue is timing. If you only want it to shake for a finite period, then you'll need to remove the class after your duration has expired.

    While I'm fixing the base <<shake>> macro, I'll look into adding an additional macro to make that easy.

    That said, it's possible to do so right now with a little kitbashing of some of SugarCube's core macros (see: DOM (Classes) macros, <<timed>> macro). For example:
    Some non-shaking text.
    
    <!--
    	Text which is initially shaking, shakes for 3s,
    	then stops shaking.
    -->\
    <span id="shaker-1" class="shake">Some shaking text.</span>\
    <<silently>>
    <<timed 3s>>
    	<<removeclass "#shaker-1" "shake">>
    <</timed>>
    <</silently>>
    
    <!--
    	Text which is initially calm, starts shaking after 3s,
    	shakes for 3s, then stops shaking.
    -->\
    <span id="shaker-2">More shaking text.</span>\
    <<silently>>
    <<timed 3s>>
    	<<addclass "#shaker-2" "shake">>
    <<next>>
    	<<removeclass "#shaker-2" "shake">>
    <</timed>>
    <</silently>>
    
    <!--
    	An image which shakes forever as there's no timed removal
    	of the "shake" class.
    -->\
    <img src="images/earthquake.png" class="shake">
    
    More non-shaking text.
    
    I used HMTL markup there, but SugarCube's wiki-style markup could be used instead—it's just a matter of preference.
  • Liyamu wrote: »
    I couldn't get this to download, or open in another page. I suspect I'm doing something dumb. I tried downloading/opening it with IE because sometimes Firefox gives me grief, but that didn't work either. Thanks anyway, though!
    For future reference:

    If using Windows then right mouse button click on the download icon and select then "Save Link As.." (or words to that effect) menu item. Use the equivalent option if using a different operating system.
    As the related comment stated, once you have downloaded that Archive HTML file you use the Import From File option to add the story contained within it to your Story List.
  • I fixed <<shake>> to work in Test Mode. Beyond that, it was renamed <<shakescreen>>, because there are two new macros in the, now, set. The new <<shake>> macro, which is a container macro, and the <<shaketarget>> macro, which allows you to easily target elements. The CSS was also updated, so be sure to replace that too.

    The new package, including documentation, is available on SugarCube 2's site (under: Downloads > Add-ons).

    I should probably replace this with a generic animation package at some point.
Sign In or Register to comment.