Need beta testers for Halloween-themed game

My first idea for an EctoComp entry has grown well outside the bounds of its three hour limit, so I’ll be finishing and releasing it “just because.” I thought I was aiming small enough, but was I wrong. That’s not to say it’s a large game, but I’m adding a bit more to it now that it’s not comp-eligible. Extra puzzles. More detail. Better polish.

It’s a browser-based Twine game. Hypertext IF. But it has more of a traditional text adventure feel. I’m aiming for a fun, fairly short Halloween diversion suitable for all ages. (Well, most ages. It would probably only scare small children too young to read it on their own.)

The game isn’t ready yet. And it might not be ready for a few more days. But it seems like finding beta testers can take about that long nowadays, so I’d just like to line up a couple of willing volunteers by then.

Thanks!

How about entering it in the Saugus.net Halloween comp? It would have to be ready by the 22nd.

And oh yeah, I’ll test. Just PM me.

I wasn’t aware of another competition, but I’ll go check that out. Either way, it’ll need some testing, so thanks!!

(Edit) It looks to be aimed at kids – or at least people still undergoing some kind of education. I see an option for an Adult category, but it doesn’t seem to be their main focus. Also, it says accepted formats are Zcode and Glulx, which this isn’t, so I’m not sure it’s eligible. But I’ll look some more.

I’d love to test it, I’m really interested to see what you’ve been up to in Twine. I’ve had some ambitious ideas myself, but seem to come up short on the technical aspect of things. Speaking of which, I’d also like to get a look at that Twine cheat sheet you mentioned elsewhere, if you don’t mind me… well, cheating.

Awesome. That’s two already. Thanks! Hopefully I’ll be done in the next couple of days. Depends on how work goes, though.

Here’s my cheat sheet, unfinished. I planned to do some formatting, make a PDF with two columns, etc. I still might, but one thing I’ve discovered is that there is a lot more to Twine than just the “basics” once you start trying to do anything very complicated. What’s needed most is probably a reference guide with samples and macros and lots of things. It seems like most answers are out there, but they come from various unindexed blog pages, tutorials, and group posts.

[spoiler][code]SIMPLE LINKS:

[[Page1]]
[[Go to Page 1|Page1]

[img[whatever.jpg]]

FORMATTING:

Italics: //text//
Boldface: ‘‘text’’
Underline: text
Subscript: H2O
Superscript: meters/second^^2^^
Monospace: {{{text}}}
Horizontal line: ----
Bulleted list:

  • one
  • two
    Numbered list:

one

two

Any HTML to embed can go here.

An image where the width auto-resizes to fit the passage content area.
Style could be moved to a CSS class, and other things (like a negative
offset to the margin-top) can be added). Best for banner titles, etc.

SPECIAL PASSAGE NAMES:

Start - The first passage when the game starts.
StoryTitle - Sets the title of the game.
StorySubtitle - Allows for a brief subhead under the game title (Jonah style only).
StoryAuthor - Sets the game’s byline.
StoryMenu - Additional links to appear on the sidebar (Sugercane style).

MACROS:

<<display “Page1”>> - Embeds all the contents of another passage.
<<print “[[Link Name|” + $passage + “]]”>> - Link to a variable page name.
<<choice “Page1”>> - Choice based on a passage title.

Internal link inside an HTML block:
Link text

Choice based on passage title and alternate display label.
Note that the order is opposite of a standard [[link]]. Here, the title is first.
Also note that this appears to give a warning icon AND not work in Sugarcane.
<<choice “Page1” “Return to Page 1”>>

Note: Choice displays options excluding any that were already seen.
However, there doesn’t appear to be a way to provide an alternate label.
<<actions “Page1” “Page2” “Page3”>>

<<set $meals = 1>>
<<set $meals = $meals - 1>>
<<set $weapon = “axe”>>
<<set $hascow = true>>

Saving variables to a cookie for persistence:
<<remember $meals>>
<<remember $escapes = $escapes + 1>>
<<<print $escapes>>

<<print Math.round(Math.random() * 6)>>

<<if $foundBerry>>
<<display “Victory”>>
<>
“That’s too bad,” he says. “I had such high hopes for you…”
<>

<<if $meals eq 0>>
No more meals…
<>

Note that IF’s can be nested multiple levels
However, there appears to be no “ELSE IF” that I’ve seen.
Example:

<<if $var1>>
<<if $var2>>
var1 and var2 are both true.
<>
only var1 is true.
<>
<>
<<if $var2>>
only var2 is true.
<>
neither var is true.
<>
<>

<>
Use to suppress any linebreaks and output while running macros.
Could also be used to embed coding comments in a passage.
<<set $meals = 1>>
<<set $weapon = “axe”>>
<>

To force a linebreak:
<<set $newLine = String.fromCharCode(13) + String.fromCharCode(10)>>
<<print $newLine>>
(The character codes could be inline, but making it global means it’s easily reusable.)

LOGICAL OPERATORS:
eq (Equals)
neq (Not Equals)
gt (Greater Than)
gte (Greater Than or Equal To)
lt (Less Than)
lte (Less Than or Equal To)
and (Both Conditions True)
or (Either Condition True)
not (Inverts True/False)

TAGS:
stylesheet - Marks the passage as containing CSS info.
script - Marks the passage as containing custom JavaScript.
bookmark - Causes a passage to be added to the “rewind” menu.

BASIC CSS LABELS:
body Affects everything displayed — a great place to change the typeface for the entire page.
#floater The story menu in the upper-right corner of the page (Jonah only).
h1 The story’s title (Jonah only).
h2 The story’s subtitle (Jonah only).
h3 The story’s author (Jonah only).
#footer The footer at the bottom of the page (Jonah only).
#sidebar A container for the sidebar menu (Sugarcane only).
#sidebar #storyTitle The story’s title (Sugarcane only).
#sidebar #storyAuthor The story’s author (Sugarcane only).
#sidebar #credits The non-link portion of the Twee/TiddlyWiki message (Sugarcane only).
#sidebar #credits:hover Style when hovering over the Twee/TiddlyWiki message (Sugarcane only).
#sidebar #credits a The links that appear in the Twee/TiddlyWiki message (Sugarcane only).
#sidebar #credits a:hover Style when overing over the links that appear in the Twee/TiddlyWiki message.
#sidebar #snapback The “Rewind” sidebar menu option (Sugarcane only).
#sidebar #restart The “Restart” sidebar menu option (Sugarcane only).
#sidebar #share The “Share” sidebar menu option (Sugarcane only).
#sidebar #storyMenu Additional text/links that appear in the StoryMenu passage (Sugarcane).
#passages A container for all passages displayed on the page.
.passage A single passage on the page.
.passage .title A passage’s title.
.passage .toolbar A container for the Bookmark and Rewind to Here links for each passage (Jonah).
.passage .body A passage’s body text.
.passage .body .internalLink A link to another passage in the story (Jonah).
.passage .body .brokenLink A link that points to a nonexistent passage in the story (Jonah).
.passage .body .externalLink A link that points to another Web page (Jonah).
.passage .body .choice A link created through the <> macro (Jonah).
.passage .body .disabled A link created through the <> macro that is no longer available to the reader.
a.internalLink A link to another passage in the story (Sugarcane).
a.internalLink:hover Style when hovering over an internal link (Sugarcane).
a.externalLink A link that points to another Web page (Sugarcane).
a.externalLink:hover Style when hovering over an external link (Sugarcane).
a.brokenLink A link that points to a nonexistent passage in the story (Sugarcane).

Ex: Hide passage titles in Jonah format:
.passage .title { display: none }

MODIFIED SHARE LINKS TO OPEN IN NEW WINDOW:
(Must be done as a post-edit on the resulting HTML. Find the existing four DIV’s and replace them.)

Delicious
Facebook
StumbleUpon
Twitter
[/code][/spoiler]

That cheat sheet is great - the main thing keeping me from trying twine is I hate the default font output.

Is there an example of how to apply the CSS styles to a game? (I’m just knowledgeable enough of HTML to be dangerous.)

Well, that is very helpful! Thanks. One thing: when I made Witch’s Girl the ‘remember’ function for cookied variables was documented but didn’t actually work (as a result you have to play the game in one sitting), have you tested it to check if it does now?

@mostly useless

No, I haven’t actually tried the remember macro. For my purposes, remembering just one or two variables wouldn’t be very useful. In JavaScript you can package variables together, and it might be possible to ‘remember’ the package as a whole. It would take some hacking. Since my first goal was to do the whole game in three hours, I didn’t bother getting into any kind of saving. So this one I’m working on also needs to be played in one sitting.

Even if the built-in one doesn’t work, it probably wouldn’t be too difficult to come up with a cookie loading/saving macro. I’ve done that in JavaScript before, so it would just be a matter of customizing it into a macro. You could even rewrite the “remember” macro that’s already there by taking it out of the HTML and adding it as a new, modified macro. Assuming I end up doing more than just this game, that’s probably something I’ll try to figure out.

@HanonO

You can add CSS by creating a new passage and giving it whatever title you want (Example: “Styles”). But in the “tags” you need to put the word stylesheet in all lower case. In CSS, you basically identify the style class name, then put various style information in brackets.

You can think of it as there being three kinds of class names:

Styles for built in elements, like “a” and “hr” and “body”.

Styles for named elements. For example, if you have something like this:

In The Div
you can style it using “#whatever”.

Styles for named classes. For example, you could create a style called “.test” and use it like

.

You can identify the style based on their object hierarchy. If you have a div with an ID of “test” with a nested div given the class name “blah” you could set up a style with #test .blah { }. You can also apply the style information to more than one element by separating them with commas (for instance, to add a certain font to several different classes). It cascades (hence the C in CSS), so you can put some style information in one definition, and more in another definition.

Here are some examples:

[code]/* This will hide the “restart” menu option in Sugarcane */
#sidebar #snapback
{
display: none;
}

/* This changes the font size and color for the “author” line on the sidebar */
#sidebar #storyAuthor,
#sidebar #storyAuthor a
{
color: #C0B0B0 !important;
font-size: 12px !important;
}

/* This sets the font and color for the game title on the sidebar */
#sidebar #storyTitle
{
color: #D04040;
font: bold 22px Verdana,sans-serif;
}[/code]

The cheat sheet has a bunch of class names, but not everything. Basically you’d add that to the CSS and then put whatever style you wanted to apply to that element. There is some really cool stuff you can do with CSS. Not just changing fonts and colors. You can force everything to upper or lower case. You can create rounded edges on boxes. You can put borders around stuff. You could even change the positioning of everything on the page.

For instance, in this game I’m working on, I’ve made the sidebar wider, styled stuff in various hues of red, increased the font sizes some, and added a static “title bar” that displays the current room and score at the top (by way of adding new HTML directly into the “StoryMenu” passage).

As far as I can tell, any valid CSS will work, so whatever you pick up from online tutorials and references in general is fair game. Twine doesn’t have to do anything to support it per se, because it’s all being fed into the DOM of the browser.

You can also put stuff directly into an element using inline CSS, but that’s only handy if you’re adding HTML directly to your game/story using the . For instance:

<html><span style="font-size:24px">This is very large text</span></html>

I sent an email to the organizer asking a couple questions about the rules. The applicable part of the response is that they’re only doing Z-Machine and Glulx games because most of the readership isn’t familiar with IF and they want to keep things simple by sticking to one VM.

It was a nice idea though. At least now I know that the competition exists!

It appears to still be broken. For example, I tried to save a variable “$TESTX” and it ends up in the cookie as “undefinedTESTX”. So there’s something going on in the built-in function. Those functions are fixable though. You’d just have to know what was wrong, and replace it in a “javascript”-tagged passage.

For this new game I’m working on, I’ve written my own load/save. It basically wraps up ALL variables and saves the entire game state to a cookie. I added macros that load, save, delete, and verify the cookies, so I can have a system menu where players can load/save in up to three slots. It even tells you the date/time that the save was made. I was worried for a while that this would have to be played in just one sitting, since the game has gotten bigger than I originally planned, but thankfully this code seems to do the trick.

I need to test it in Chrome and FireFox, but it should be fine. My cookie data is about 1.5k which fits well under the 4k standard max size for a cookie.

Nice! I’m guessing that was a lot of work, would you consider sharing it? I imagine there’s a ton of Twiners out there who’d be excited about it, and giving the player the choice of when to load and save is way better than just forcibly remembering everything. Roughly how many variables do you think would fit in the 4k allowance? Is it just based on the length of the variable names and the length of the stored numbers, or is it more complicated than that?

It would have been a lot harder if user-created variables weren’t already packaged into a single object. Since they are, it was just a matter of serializing it all as JavaScript and then saving to a cookie.

I have quite a few variables in my game. But one thing I’m doing is erasing the values of some of the bigger ones I don’t need to save, before saving. For instance, there’s a situation where I create a pretty big block of text stored in a variable. But it’s something I just do for formatting purposes, and it doesn’t need to be saved. So set it to blank like this, before calling the “save” macro:

<<set $whatever="">>

You could add a javascript alert in the “save” routine that displays the size of the resulting string. That’s how I figured out how big mine were. Uncomment the alert line in the code to do that. 4k is 4096 bytes. I’ve read that a cookie has something like 50 bytes of overhead too, so you’d probably want to start worrying if your cookies are more than 4000 characters long.

Somewhere at the beginning of the story/game, before giving players an option to save, you’d need to create a few variables that the save macros use:

<<set $tempFlag = false>>
<<set $tempText = “”>>
<<set $saveTime = “”>>

You can name your saves whatever you want, but I called mine “SG1” and “SG2” and so forth. I’m limiting people to 3 saves plus an autosave (which they can turn on or off) since there is also a limit on the number of cookies you can have per domain.

One thing I found out is that you can’t store cookies in Chrome locally. If you have a local web server (localhost or whatever) then sure, but if you just open the file in Chrome from a folder on your hard drive, the cookies “succeed” but don’t actually save. It does work online though, and it works locally in Internet Explorer and FireFox.

The macros are used like this:
<<deletegame “SG1”>>

<<testgame “SG1”>>
Afterwards, $tempText will contain either “Empty” or the date the save was made and the room name. For the room name, you might want to remove it from the code or change it to some other piece of information from your particular game, since this was specific to mine.

<<savegame “SG1”>>
Afterwards, $tempFlag will contain true if successful or false if not.

<<loadgame “SG1”>>
Afterwards, $tempFlag will contain true if successful or false if not. I use “testgame” first, just to make sure the cookie exists before attempting to load it.

Some limitations –

You need to keep the cookie sizes less than 4k, and no more than 20 cookies (saves) per domain. If you’ve got several games on the same domain, all allowing mutliple saves, that could become a problem. I don’t know if adding a “path” to the cookie would help with that, but I don’t think so. So there could be issues if a bunch of Twine games with big saves end up on the same website. In addition, if everybody uses the “SGx” naming I did, the cookies could potentially overwrite each other between games (so use some other unique name).

One work-around for the 4k limit would be to split a save in half and store it in two separate cookies, then load both and concat back together when loading. In theory, you could do multiple splits and allow a pretty huge set of variables for a game.

The cookie content is larger due to the Base64 encoding. You could actually remove the code for the Base64 object (which I found online) and the calls to “encode” and “decode” and end up with cookies that are something like 30% smaller, but then all your variables are human-readable in the cookie.

This completely replaces the current game’s user-created object list with the one loaded. So if you create a game, allow people to save, then release an update that adds some important new variables, you would need to do something to gracefully handle loading the prior save. For instance, checking for the existence of a “version” variable in the loaded data, and then adding the new stuff back in aftewards. Ex:

<><<if $version eq “” || $version < 2>>
<<set $newVar1 = “Something”>>
<<set $newVar2 = “Something Else”>>
Etc.
<><>

[spoiler][code]macros[‘deletegame’] =
{
handler: function (place, name, params)
{
try
{
var cookieName = params[0];
var exdate=new Date();
exdate.setDate(exdate.getDate() - 365);
var delCookie = [cookieName, ‘=; expires=’, exdate.toUTCString(), ‘;’].join(’’);
document.cookie = delCookie;
state.history[0].variables[“tempFlag”] = true;
}
catch(e)
{
state.history[0].variables[“tempFlag”] = false;
}
}
};

macros[‘savegame’] =
{
handler: function (place, name, params)
{
try
{
var cookieName = params[0];
var exdate=new Date();
exdate.setDate(exdate.getDate() + 365);
state.history[0].variables[“saveTime”] = GetTimeString();
var value = JSON.stringify(state.history[0].variables);
var encoded = escape(Base64.encode(value));
var cookie = [cookieName, ‘=’, encoded, ‘; expires=’, exdate.toUTCString(), ‘;’].join(’’);
// window.alert(cookie.length);
document.cookie = cookie;
state.history[0].variables[“tempFlag”] = true;
}
catch(e)
{
state.history[0].variables[“tempFlag”] = false;
}
}
};

macros[‘loadgame’] =
{
handler: function (place, name, params)
{
var cookieName = params[0];
var result = LoadGame(cookieName);
if (result == null)
{
state.history[0].variables[“tempFlag”] = false;
} else {
state.history[0].variables = result;
state.history[0].variables[“tempFlag”] = true;
}
}
};

macros[‘testgame’] =
{
handler: function (place, name, params)
{
var cookieName = params[0];
var result = LoadGame(cookieName);
state.history[0].variables[“tempText”] =
(result == null ? “Empty” : result[“saveTime”] + " - " + result[“roomName”]);
}
}

function LoadGame(cookieName)
{
try
{
var result = document.cookie.match(new RegExp(cookieName + ‘=([^;]+)’));
var decoded = Base64.decode(unescape(result[1]));
var valueObject = JSON.parse(decoded);
return (valueObject);
}
catch(e)
{
return null;
}
}

function GetTimeString()
{
var d = new Date();
var newDate =
(“00” + (d.getMonth() + 1)).slice(-2) + “/” +
(“00” + d.getDate()).slice(-2) + “/” +
d.getFullYear() + " " +
(“00” + d.getHours()).slice(-2) + “:” +
(“00” + d.getMinutes()).slice(-2); // + “:” +
//(“00” + d.getSeconds()).slice(-2);
return newDate;
}

// --------------------------------------------------------------------------------

/**
*

**/
var Base64 = {

// private property
_keyStr : “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”,

// public method for encoding
encode : function (input) {
var output = “”;
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;

input = Base64._utf8_encode(input);

while (i < input.length) {

    chr1 = input.charCodeAt(i++);
    chr2 = input.charCodeAt(i++);
    chr3 = input.charCodeAt(i++);

    enc1 = chr1 >> 2;
    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
    enc4 = chr3 & 63;

    if (isNaN(chr2)) {
        enc3 = enc4 = 64;
    } else if (isNaN(chr3)) {
        enc4 = 64;
    }

    output = output +
    this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
    this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

}

return output;

},

// public method for decoding
decode : function (input) {
var output = “”;
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;

input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

while (i < input.length) {

    enc1 = this._keyStr.indexOf(input.charAt(i++));
    enc2 = this._keyStr.indexOf(input.charAt(i++));
    enc3 = this._keyStr.indexOf(input.charAt(i++));
    enc4 = this._keyStr.indexOf(input.charAt(i++));

    chr1 = (enc1 << 2) | (enc2 >> 4);
    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
    chr3 = ((enc3 & 3) << 6) | enc4;

    output = output + String.fromCharCode(chr1);

    if (enc3 != 64) {
        output = output + String.fromCharCode(chr2);
    }
    if (enc4 != 64) {
        output = output + String.fromCharCode(chr3);
    }

}

output = Base64._utf8_decode(output);

return output;

},

// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = “”;

for (var n = 0; n < string.length; n++) {

    var c = string.charCodeAt(n);

    if (c < 128) {
        utftext += String.fromCharCode(c);
    }
    else if((c > 127) && (c < 2048)) {
        utftext += String.fromCharCode((c >> 6) | 192);
        utftext += String.fromCharCode((c & 63) | 128);
    }
    else {
        utftext += String.fromCharCode((c >> 12) | 224);
        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
        utftext += String.fromCharCode((c & 63) | 128);
    }

}

return utftext;

},

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = “”;
var i = 0;
var c = c1 = c2 = 0;

while ( i < utftext.length ) {

    c = utftext.charCodeAt(i);

    if (c < 128) {
        string += String.fromCharCode(c);
        i++;
    }
    else if((c > 191) && (c < 224)) {
        c2 = utftext.charCodeAt(i+1);
        string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
        i += 2;
    }
    else {
        c2 = utftext.charCodeAt(i+1);
        c3 = utftext.charCodeAt(i+2);
        string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
        i += 3;
    }

}

return string;

} }[/code][/spoiler]

If cookies start to become too limiting you could use localstorage instead. You get several megabytes of space with the drawback of incompatibility with some very old browsers.

I need to read up on HTML5. I used to do a lot more direct-to-browser stuff before working with ASP.net and Telerik Rad Controls. They tend to encapsulate those things.

Something else I’ve done in the past would probably work. You can call web services or post to a CGI from JavaScript, so you could have offline storage for a game. It’d probably need to be gated with a unique game password or user account or something.

Suddenly I find Twine intimidating.

It’s not so bad. You can do quite a bit without getting too involved with CSS and JavaScript. I personally found it kind of limiting for my purposes, though. I wanted to create more of a text-based point-and-click adventure, not a choose-your-own-adventure. It has been a little harder to achieve something like that, but hopefully the end result will be worthwhile.

The game is finished and ready for beta testing. I could probably use one or two more testers after an initial round is completed. I want to wrap up any stray issues and then have it checked out by a fresh pair of eyes. If anybody else is interested in testing and has the time to do it later today or possibly tomorrow, please let me know. I’m hoping to finish and get this released and announced so people have time to discover it before Halloween. :slight_smile:

It’s not a “long” game, but it’s not quite the super-short adventure it started out to be. It’s hard to say how long it will take to complete, or to beta-test. I can speed run it in a few minutes, but of course that’s without reading anything, and it bypasses a lot of the fun.

In case anybody is interested and missed it above. :slight_smile: So far, it has been tested by a grand total of zero people. Heh.

Testing in progress, Merk! Looking good so far, I’ll PM you with first impressions in a few hours.

Okay, sounds good. I’ve done minor changes, but they’re already live. I bumped up the main text size by 1px and fixed a small number of additional spelling errors. Thanks.