Twine Integration Guide

This guide is for integrating DAD into a Twine game (with the SugarCube 2 story format). You should read this while going through the demo (download it and Import From File inside Twine). For more general tips, refer to the usage guide.

A caveat is that I'm not a SugarCube expert, so there are probably better ways to do some things. Take a look at Twine sample code and the SugarCube reference for help.

Requirements

Recommended setup

You can either work inside Twine 2 (the app), or compile the story from external files using Tweego. I recommend the second option for reasonably sized projects because the text editor inside Twine is not great… You might resort to doing the editing in something else (e.g. WebStorm, sublime) and copying it back to test, but this makes debugging slow.

However I will explain things assuming you are working inside the Twine app since it's the easiest way for you to get started, and there aren't that many differences.

Components

There are 2 major components of interactivity to a Twine game

Unless something is easier to do in TwineScript, I recommend implementing it in JS because it could be reusable outside of your story, or if you decide to switch story formats.

Import

The first thing is to import the da module. The simplest method is through the importScripts function near the start of your Story JS after including other external libraries like:

setup.lockID = LoadScreen.lock();
importScripts("http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/dist/da.js")
    .then(function () {
        setup.da = da;
        // extend the da module here

        da.load().then(function () {
            // da module is ready to draw here; you can create/manage drawing canvases here

            Engine.play("StoryInit", true);
            Engine.play(passage(), true);
            LoadScreen.unlock(setup.lockID);
        });
    }).catch(function (error) {
        alert(error);
    }
);

This has the advantage of always pointing to the latest version of DAD, but you may not want that. If you want a specific version, you can download dist/da.js from the repository by searching the history for the commit, like "1.29.1". You would then copy the content of that to the top of story javascript. Note that the da module is placed in the global frame so you can refer to it in TwineScript like <<run da.{...}>>.

Alternatively, you can clone the DAD repository (or as a submodule) and either import the latest dist/da.js, or directly from source as modules such as import {...} from "dynamic-avatar-drawer/src/player/player.js" and bundle everything together with webpack. This has the advantage of allowing an IDE (like WebStorm) to make coding a lot easier.

I recommend you create assets directly as modules inside src/clothes. This allows you to make pull requests to share your templates with other people.

Before Loading

Code extending the library should be placed before da.load(). As seen above, it should be placed at // extend the da module here. See the extension guide and the demo's code for what to do. Patterns can also be defined here as

    addPattern("red plaid",
        "http://www.creattor.com/files/37/1681/plaid-fabrics-textures-screenshots-1.jpg");

Which you can later specify as the color for fill, stroke, and similar properties like

            var belt = da.Clothes.create(da.SimpleBelt, {
                fill: da.getPattern("red plaid", 100),
            });
            State.variables.PC.wearClothing(belt);

Which would translate to TwineScript inside a passage like

<<set _belt = da.Clothes.create(da.SimpleBelt, {
                              fill: da.getPattern("red plaid", 100),
                          });>>
<<run $PC.wearClothing(_belt)>>

After Loading

We can now get canvas groups for drawing. In the demo, properties of drawing the PC is stored in settings, which is saved across sessions, but you can put it somehwere else. You might also want to look at focused windows for drawing high resolution portraits for dialogs.

Outside Loading

You can place methods here that don't depend on immediately having access to da properties. The most standard function would be something like drawToMenu(PC) in the demo. These functions have to be attached to window to be usable inside passages, so define them as

window.drawToMenu = function(avatar) {/* ... */};

Placing Canvas Group

If you want to relocate the canvas group, first you have to choose an existing DOM element as the parent. To help with that, you can open developer tools (F12 for chrome) and click on the arrow icon to the left:

developer tools

This lets you mouse over elements on the page

mouse over element

For example here we see that this DOM element has ID "menu" and is of type nav (we can ignore this). If we want to place the drawing canvas inside this group, we'll change the StoryJS around lines 107:

            var parent = document.getElementById("ui-bar-body");
            var menuPortrait = document.createElement("div");
            menuPortrait.id = "menu_portrait";
            parent.appendChild(menuPortrait);

To

            var parent = document.getElementById("menu");
            var menuPortrait = document.createElement("div");
            menuPortrait.id = "menu_portrait";
            parent.appendChild(menuPortrait);

If instead we want to place the canvas above the menu, we can do this: (the menu and the canvas are siblings under the same parent "ui-bar-body")

            var parent = document.getElementById("ui-bar-body");
            var menuPortrait = document.createElement("div");
            menuPortrait.id = "menu_portrait";
            parent.insertBefore(menuPortrait, document.getElementById("menu"));