Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save cyrusfirheir/c8b47ca3af805964b5ebe1d51f5a7d2e to your computer and use it in GitHub Desktop.

Select an option

Save cyrusfirheir/c8b47ca3af805964b5ebe1d51f5a7d2e to your computer and use it in GitHub Desktop.
CTP macro-set for SugarCube 2

Overview

This set of macros/functions aim to provide an easy way to set up content which is revealed bit-by-bit via user interaction.

Using nested <<linkreplace>> and <<linkappend>> works, but gets tedious and is often prone to errors. The CTP (Click To Proceed: original-est name ever) macros make it a bit easier by turning them into blocks instead of nests.

Installation

If using the Twine desktop/web app, copy contents of CTP.js to Story JavaScript, and contents of CTP.css to Story Stylesheet.

If using a compiler like Tweego, drop CTP.js and CTP.css to your source folder.

Example Usage

<<ctp "testID">>
  This is the first string.
<<ctpNext clear>>
  Second! It cleared the first one out!
<<ctpNext nobr>>
  Third, but with nobr...
<<ctpNext t8n>>
  And the final one. With a transition!
<</ctp>>

<<link "Next">>
  <<ctpAdvance "testID">>
<</link>>

Macros

<<ctp "id">>

  • id: (string) Unique ID to be used to identify the chain of content. Naming rule follows the same as those of SugarCube variable names (learn more here).

Example:

<<ctp "ID_of_the_year">>
  Bare minimum...
<</ctp>>

<<ctpNext [keywords]>>

To be used inside <<ctp>> to separate the content into blocks.

  • keywords: (optional|string) The following keywords can be used to alter behavior of the macro:
    • clear: Clears content of previous block. Use for replacing.
    • nobr: Appends content in the same line as last block instead of going to a new line.
    • t8n or transition: Custom CSS animation based transition (250ms fade-in by default).

Example:

<<ctp "fancyCTP">>
  One.
<<ctpNext clear>>
  Two with clear.
<<ctpNext nobr>>
  Three on the same line.
<<ctpNext t8n>>
  Fading four.
<</ctp>>

<<ctpAdvance "id">>

The 'proceed' part of Click To Proceed... Used to move the train forward and show the next blocks.

  • id: (string) Unique ID which was set up in <<ctp>>.

NOTE: Use with user interaction (inside a <<link>> or <<button>>) or inside a <<timed>> macro to ensure the DOM is loaded and has the element on page for the macro to target.

Example:

<<ctp "ID_of_the_year_once_again">>
  <!-- stuff -->
<</ctp>>

<<link "Next">>
  <<ctpAdvance "ID_of_the_year_once_again">>
<</link>>

JavaScript usage - The CTP object

The CTP custom object is set up as follows:

id: (string) Unique ID.

selector: (string) CSS selector to target to output to. When used by the macro, this is the slugified form of id.

Example:

var ctpTest = new CTP({
  id: "ctpTest",
  selector: "#ctp-test-id"
});

Other properties which are used under the hood:

stack: (array) Contains the content of all blocks.

log: (object) Keeps track of blocks amd their behaviors:

  • index: (whole number) Current index of block (zero-based).
  • clear: (array) Indices of blocks at which to clear the output element before appending new content.
  • nobr: (array) Indices of blocks where no <br> is added.
  • transition: (array) Indices of blocks which have the CSS transition (Target the class .macro-ctp-entry-t8n to customize).

Object methods:

CTP.add(content [, keywords])

Adds content to the end of the stack and returns the CTP object for chaining.

  • content: (string) The actual content in the block.
  • keywords: (string) Space-separated list of keywords (clear, nobr, t8n, transition) to modify behavior of the blocks.

Example:

ctpTest
  .add("This is the first string.")
  .add("Second! It cleared the first one out!", "clear")
  .add("Third, but with nobr...", "nobr")
  .add("And the final one. With a transition!", "t8n");

CTP.advance()

Does the same as <<ctpAdvance>>, moving to the next block. Returns the CTP object for chaining.

Example:

ctpTest.advance();

CTP.ctpEntry(index)

Returns the html output for a single block at the index passed into it.

  • index: (whole number) Index of block to return.

Example:

ctpTest.ctpEntry(2);

// Assuming ctpTest is the same as in the previous examples, this returns:
// <span class="macro-ctp-entry macro-ctp-entry-index-2">Third, but with nobr...</span>

CTP.out()

Returns the html output for entire chain from the last 'clear' to the current index.

Example:

// Assuming current index is 3
ctpTest.out()

/* Returns:
 *
 * <span class="macro-ctp-entry macro-ctp-entry-index-1">Second! It cleared the first one out!</span>
 * <span class="macro-ctp-entry macro-ctp-entry-index-2">Third, but with nobr...</span>
 * <br>
 * <span class="macro-ctp-entry macro-ctp-entry-index-3">And the final one. With a transition!</span>
 */

Complete usage:

JavaScript:

State.variables.ctpTest = new CTP({
  id: "ctpTest",
  selector: "#ctp-test-id"
});

State.variables.ctpTest
  .add("This is the first string.")
  .add("Second! It cleared the first one out!", "clear")
  .add("Third, but with nobr...", "nobr")
  .add("And the final one. With a transition!", "t8n");

In Passage:

<div id="#ctp-test-id">
  <<= $ctpTest.out()>>
</div>

<<link "Advance">>
  <<run $ctpTest.advance()>>
  <!-- Because $ctpTest was created manually, using the <<ctpAdvance>> macro won't work. To be able to use <<ctpAdvance>>, the CTP object needs to be set as a property of State.variables["#macro-ctp-dump"] as that is what is used internally to store CTP objects created via the macros. -->
<</link>>

TODO

  • Add CSS time keyword to delay before next block appears.
.macro-ctp-entry-t8n {
opacity: 1;
animation: macro-ctp-fade-in 0.25s ease;
}
@keyframes macro-ctp-fade-in {
0% {
opacity: 0;
}
}
window.CTP = function (config) {
this.id = "";
this.selector = "";
this.stack = [];
this.log = {
index: 0,
clear: [0],
nobr: [],
transition: []
};
Object.keys(config).forEach(function (pn) {
this[pn] = clone(config[pn]);
}, this);
};
CTP.prototype.add = function (entry) {
var _this = this;
var mods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
this.stack.push(entry);
mods.replace("t8n", "transition").split(" ").forEach(function (el) {
if (el) _this.log[el].pushUnique(_this.stack.length - 1);
}, this);
return this;
};
CTP.prototype.ctpEntry = function (index) {
if (index < 0 || index >= this.stack.length) return "";
var t8n = this.log.transition.includes(index) ? "macro-ctp-entry-t8n" : "";
var br = index === 0 || this.log.clear.includes(index) ? " " : this.log.nobr.includes(index) ? " " : "<br>";
return br + '<span class="macro-ctp-entry macro-ctp-entry-index-' + index + ' ' + t8n + '">' + this.stack[index] + '</span>';
};
CTP.prototype.advance = function () {
if (this.log.index === this.stack.length - 1) return this;
var index = ++this.log.index;
var _el = $(this.selector);
if (this.log.clear.includes(index)) _el.empty();
_el.wiki(this.ctpEntry(index));
return this;
};
CTP.prototype.out = function () {
var _this2 = this;
var clear = this.log.clear;
var clearIndex = this.log.index > 0 ? clear.findIndex(function (el) {
return el >= _this2.log.index;
}, this) - 1 : 0;
var ret = this.stack.map(function (el, index) {
return index;
}).slice(clearIndex, this.log.index + 1).reduce(function (acc, cur) {
return acc + _this2.ctpEntry(cur);
}, "");
return ret;
};
CTP.prototype.clone = function () {
return new CTP(this);
};
CTP.prototype.toJSON = function () {
var ownData = {};
Object.keys(this).forEach(function (pn) {
ownData[pn] = clone(this[pn]);
}, this);
return JSON.reviveWrapper('new CTP($ReviveData$)', ownData);
};
Macro.add("ctp", {
tags: ["ctpNext"],
handler: function handler() {
var _id = this.args[0];
var _selector = "#" + Util.slugify(_id);
var ctp = new CTP({
id: _id,
selector: _selector
});
this.payload.forEach(function (el, index) {
var _args = el.args;
if (el.name === "ctp") _args.reverse().pop();
ctp.add(el.contents.trim(), _args.join(" "));
});
variables()["#macro-ctp-dump"] = variables()["#macro-ctp-dump"] || {};
variables()["#macro-ctp-dump"][_id] = ctp;
$(this.output).wiki('<span id="' + Util.slugify(_id) + '" class="macro-ctp-wrapper">' + ctp.out() + '</span>');
}
});
Macro.add("ctpAdvance", {
handler: function handler() {
var _id = this.args[0];
variables()["#macro-ctp-dump"] = variables()["#macro-ctp-dump"] || {};
var ctp = variables()["#macro-ctp-dump"][_id];
if (ctp) {
ctp.advance();
if (ctp.log.index === ctp.stack.length - 1) {
delete variables()["#macro-ctp-dump"][_id];
}
}
}
});
$(document).on(':passageinit', function () {
delete variables()["#macro-ctp-dump"];
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment