GithubHelp home page GithubHelp logo

tmedwards / sugarcube-2 Goto Github PK

View Code? Open in Web Editor NEW
167.0 13.0 38.0 2.79 MB

SugarCube is a free (gratis and libre) story format for Twine/Twee.

Home Page: https://www.motoslave.net/sugarcube/2/

License: BSD 2-Clause "Simplified" License

JavaScript 94.80% CSS 4.28% Smarty 0.65% Python 0.28%
twine twee story-format

sugarcube-2's People

Contributors

bawdyinkslinger avatar chapelr avatar cyrusfirheir avatar grausicht avatar karimechehbouni avatar marc-cornette avatar mrawi avatar theumnavigator avatar tmedwards avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sugarcube-2's Issues

Save slots export/import

Currently, there's no way to export or import the save slots via either the Save API or the Save dialog. It would be nice if this were possible.

Add a "disabled" parameter for <<button>> and similar macros

It would be nice to have a "disabled" parameter for <<button>> and similar macros which look different when disabled, especially if they have a transition time.

Currently, if you add a <<button>> to a passage which is already visible, there is no way to have it default to disabled, so you then have to disable it using jQuery, which causes it to "flash" due to the button's "transition" CSS, as it transitions from the non-disabled style to the disabled style.

It would be nice if this and any similar macros with noticeable transitions like that could be displayed in an initially disabled state by adding a "disabled" parameter to them. Such as:

<<button "Button Text" disabled>><</button>>

The documentation should probably also mention how to later enable such elements.

Terminating <<goto>>

A <<goto>> macro invocation does not currently terminate the current Wikifier call chain—specifically, the entire chain, not simply the current call. Terminating the call chain would be better, in general, and resolve a few issues.

Issues

There is the, minor, potential to break existing code.

Please document how to use the font tme-fa-icons in StoryMenu declarations

Please explicitly document how to use the font tme-fa-icons in StoryMenu declarations.

I spent most of today researching and getting help in how to add "fancy" icons to my own StoryMenu entries, like those used by SugarCube's default UI menu entries. I think it would be very helpful for other novices like myself, saving them a lot of time, if there were an explicit description of the procedure in the SugarCube documentation. It might be there by implication, but, if so, it was not at all obvious to me.

For example, it might be appropriate to

  1. Mention the name of the font that's used in SugarCube's default menu entries (tme-fa-icons)
  2. Mention how to find out what characters it provides and their codes.
  3. If it exists, mention the name of SugarCube's "i." (italics) class which is used to specify the font
  4. Otherwise, give an example of creating such a specification
  5. Perhaps mention the history why 'i." is used instead of some other HTML markup prefix.
  6. Show an example of code that one can use to prefix an icon to the menu entry.

FWIW, I finally managed to implement the icons in the story that I'm currently working on by using the following procedure:

I created this Story Stylesheet entry

i.fa 
{ font-family: 'tme-fa-icons';
  font-style: normal;
}

and invoked it using these StoryMenu entries

<<link '<i class="fa">&#xe807;&nbsp;</i>Navigation Help' "Navigation Help">><</link>>
<<link '<i class="fa">&#xe838;&nbsp;</i>Report a Problem'>> <<bugreport "Bug Report Instructions">><</link>>

JsDelivr CDN

Since you're on GitHub now, you could add the built Twine 2 format to the repo somewhere so users could add it to Twine 2 via the JSDelvr CDN: https://www.jsdelivr.com/

I serve my proofing format this way and I think it works pretty well, letting users grab specific versions (based on releases) or keep up to date by just getting the latest version. Not to mention you can better support the online version of the Twine 2 app.

Update timed macro's delay argument in docs

Today someone asked in the Discord server if it was possible to use a variable as the delay value for the timed macro. The docs (and the examples in it) only show using an actual value as a keyword, and say it has to be 'a valid CSS time value—e.g., 5s and 500ms'. Through testing it was discovered that using a variable that holds the correct value as a string in its place also works. It would be great if this possibility was documented, as well as illustrated in the examples for this macro, as this can be very useful in certain situations.

An equivalent of 'nobr' that strips leading and trailing spaces

Would it be possible to create an equivalent of the nobr tag that strips leading and trailing spaces from the output?

Currently you're required to either use line continuation markup, which is hard to deal with in long code, use Javascript, which is sometimes overkill for the things I want to do, or just put everything into one line, which is impractical with complicated code.

[Docs] Clarify that "current passage" means the current moment, not the passage the code is running from within.

For example, the tags() function features this line:

If omitted, will default to the current passage.

Some users have interpreted this to mean the passage the code is in rather than the passage associated with the current moment, e.g., they believe a tags() call in PassageHeader would return the tags of the PassageHeader passage.

I do not think the docs as written are necessarily unclear, but I think it wouldn't hurt to specify what is meant by the "current passage."

cycle macro: optionsfrom seems backwards

It seems that SugarCube recently added <<cycle>>. That's great! When I was using Harlowe, I had to simulate it with a lot of complex stuff. It's almost what I want, except that optionsfrom seems to handle arrays (and maps and generic objects) in the opposite way that I was expecting, and need. In particular, it makes sense to me to have the variable set to the index/key and that the label (visible to the reader) should be the value/element. Of course, people have presumably already been using <<cycle>>, so it seems the best thing for me to do is to define a <<cycle*>> custom macro that does it the way that seems to make more sense to me. But I wanted to check to see the reasoning behind the current macro.

Problem: quoted strings don't fully evaluate array indices

See the inline comments in the code below and the attached screengrab showing the problem.

:: Start
<<silently>> /* used just to make the screen readable */
/*
 * Problem:
 * When indirect indexing is used within a quoted string
 * the index doesn't get properly evaluated.
 * 
 * Trying to be more specific in my description:
 *
 * If one has an array of indices ( _pIndex below),
 * and uses another index ( _i below)
 * to obtain from that array the desired index into
 * another array ($pasageName below)
 * then quoted strings containing them get very confused.
 *
 * Instead of displaying the single desired element of the array,
 * the quoted string incorrectly contains the contents of the entire array
 * followed by the desired index in brackets.
 *
 * However, if one uses an intermediate variable ( _myIndex below)
 * the quoted string is OK.
 *
 * In other words, quoted strings work fine
 * when they are provided with an array indexed by just a single variable.
 *
 * I believe quoted strings should be able to work with an "indirect" index
 * just as link references do.
 *
 */

/* the code below demonstrates this problem with quoted strings
 * in both link and print statements.
 */
 
/* create an array containing 3 indices */

<<set _pIndex = [] >>
<<set _pIndex[0] = 1 >>
<<set _pIndex[1] = 3 >>
<<set _pIndex[2] = 5 >>

/* now attempt to link to and list the passageNames
 * corresponding to those 3 indices
 */

<</silently>>

/* indirect indexing in this "for" loop fails in quoted strings */
incorrect:
<<for _i = 0; _i<3; _i++>>
link:      <<link "passage # _pIndex[_i] = $passageName[_pIndex[_i]]" $passageName[_pIndex[_i]]>> <</link>>
print:     <<print "$passageName[_pIndex[_i]]">>
<</for>>

/* but using an intermediate variable works fine with quoted strings in both link and print */
correct:
<<for _i = 0; _i<3; _i++>>
      	  <<set _myIndex = _pIndex[_i]>>
link:  	  <<link "passage # _myIndex = $passageName[_myIndex]" $passageName[_pIndex[_i]]>> <</link>>
print: 	  <<print "$passageName[_myIndex]">>
<</for>>

----
''Story Compiler:'' <<= $('tw-storydata').attr('creator') + " v" + $('tw-storydata').attr('creator-version')>>
''Story Format:'' <<= $('tw-storydata').attr('format') + " v" + $('tw-storydata').attr('format-version')>>
''Browser:'' <<= navigator.userAgent>>


/* the corresponding passages */

:: upright0
!! Passage  upright0

:: upright1
!! Passage  upright1

:: upright2
!! Passage  upright2

:: reversed3
!! Passage  reversed3

:: upright4
!! Passage  upright4

:: upright5
!! Passage  upright5

:: upright6
!! Passage  upright6

:: StoryInit

/* create an array of passage names
 *
 * I'm aware I could do the initialization with a single set command
 * but using explicit index numbers helps me to understand
 * what I want to be happening.
 */

<<set $passageName = []>>
<<set $passageName[0] = "upright0">>
<<set $passageName[1] = "upright1">>
<<set $passageName[2] = "upright2">>
<<set $passageName[3] = "reversed3">>
<<set $passageName[4] = "upright4">>
<<set $passageName[5] = "upright5">>
<<set $passageName[6] = "upright6">>

:: StoryData
{
        "ifid": "C31A5464-60FC-4937-A550-C7D75F7692AF"
}

:: StoryTitle
Test Indexing

test_indexing

Support for variables in the custom style markup

Pretty much what it says on the tin. It would be nice if the custom style markup had some way of supporting variables, if not expressions—similar to what you can currently do with HTML tags and the evaluation attribute directive.

Vendor updates

Most vendor files appear to not be up-to-date:
FileSaver v??? -> v2.0.3
classList v??? -> v1.2.20180112 (probably?)
es5-shim v4.5.13 -> v4.5.14
es6-shim v0.35.4 -> v0.35.5
imagesLoaded v4.1.0 -> v4.1.4
jQuery v3.4.1 -> v3.5.1
lz-string v1.3.3 -> v1.4.4
normalize.css v3.0.3 -> v8.0.1
seedrandom.js v2.3.3 -> v3.0.5

Not sure how many of these you want to update, but the jQuery update included a security fix.

<<if>> check for "=" too strict: looks inside of strings

Consider the following text

<<set $test to "a=b">>
test: $test
cond: <<= $test.includes("=") >>
<<if $test.includes("=") >>
  Yes!
<</if>>

This generates

test: a=b
cond: true
Error: <<if>>: assignment operator found within <<if>> clause (perhaps you meant to use an equality operator: ==, ===, eq, is), invalid: $test.includes("=")

I ran into this inside of a <<link>> block where the error was not reported (apparently errors are swallowed by <<link>> ?) and so the <<if>> silently failed to do anything, which made the problem much harder to diagnose. (Should <<link>> be swallowing errors?). In any case, I see that there's a configuration option to turn off this behavior for <<if>> but I think it should be fixed anyway.

Config.cleanupWikifierOutput = true injects extra <p> blocks in delayed-output macros

The issue was discovered (not by me) in a simple text where part of it was added later using the <<timed>> macro. Boiling it down to the smallest possible code, the following code creates markedly different results depending on the Config.cleanupWikifierOutput setting:

<<nobr>>Text <<timed 1s>>more text<</timed>>.<</nobr>>

Result with Config.cleanupWikifierOutput = false

Text <span class="macro-timed">more text</span>.

Result with Config.cleanupWikifierOutput = true

Text <span class="macro-timed"><p>more text</p></span>.

Inside of the <<timed>> macro specifically, the Wikifier gets called without any options:

new Wikifier(frag, item.content);

Ref: https://github.com/tmedwards/sugarcube-2/blob/master/src/macros/macrolib.js#L3426

... but even if it was, there is currently no option to not surround the result with a <p>...</p> block that would be passed on to the internal convertBreaks() method. The issue is compounded by the fact that a construct like this:

A starting paragraph <<timed 1s>>that is being extended...

And also split apart<</timed>>. Isn't it //fun//?!?

... would require the macro to split the surrounding <p> DOM element into two ore more.


No generically applicable workaround aside of not using the setting exist yet, to my knowledge. For the use case of "inline" <<timed>> only, the following as a replacement of the above line of the <<timed>> macro works well enough:

let oldCleanup = Config.cleanupWikifierOutput;
Config.cleanupWikifierOutput = false;
new Wikifier(frag, item.content);
Config.cleanupWikifierOutput = oldCleanup;

An <<audioonend>> macro

Original issue thread (from the old Bitbucket repo):

1. David Blackman (blackmad @github):

I wanted functionality in sugarcube that would wait until an audio track finished playing and then reveal some markup in the current passage - like the <> macro.

I didn't want it to use <> because I wanted to remain in the same passage. Think of a passage with scene setting text, then autoplay audio saying "here's some background, etc etc ,etc do you want to go left or right? " and then when the audio is done, a left & right link come up on screen.

I wrote my own macro, patterned off <> (it only works against the development branch, I think Macro.get("cacheaudio").tracks isn't public in the current release?).

https://github.com/blackmad/sugarcube-audio-demo/blob/master/js/audioonend.js

I'm curious 1) if this is a reasonable implementation 2) if you'd ever consider adding this to core sugarcube functionality

Thanks, David

2. Me:

The basic idea is sound. That said, and your "style" notwithstanding, you have some issues:

Your initial comment—i.e., <<audioloopto>> macro—is incorrect.

You have only a single argument error, so you don't need the array. Just return the error.

You're mixing ES5 and ES6. You want ES5 if you want compatibility—SugarCube is transpiled into ES5. If you don't care about that, get rid of the ES5-isms—e.g., var.

You're using an old API—more on that below.

You're reusing the <<timed>> macro's classes, which I do not recommend.

Another potential issue is that you're only checking to see if the audio ended. If something else happens that causes the ended event not to fire, then that event handler will hang around indefinitely, possibly triggering later and causing issues. I'd suggest listening to other events—e.g., things like errors—and having your handler use a receiver parameter, so you know which triggered. At that point it would simply do either what it's supposed to, if the audio ended normally, or abort, if it was something else.

Using an old API

You were, obviously, looking at the wrong branch. I'm guessing you looked at the v3 development branch (default), which is both unreleased and significantly behind v2 at this point. You want one of the v2 branches, either the release (v2-release) or development (v2-devel) branch.

Of note here is that the SugarCube v2.28.0 release switched to a new audio API—eventually to be made public—and the audio macros were rewritten to use it. If you're attempting to target the current release version (v2.28.2), and you really should be, then your code will need to be updated for that.

3. Me:

I forgot to answer your other question—i.e., if I'd add it to SugarCube.

I can't say that I think this is needed within the core. I've never had anyone request or mention anything like this, before now—not that I recall, anyway.

There's nothing wrong with the idea, mind, so I could certainly write it up as an add-on for inclusion on SugarCube's website—there are already a few there available for download.

4. David Blackman (blackmad @github):

Apologies for the gross code, this was quickly thrown together for a deadline three days later. I'll clean it up but first I want to better understand 'SugarCube v2.28.0 release switched to a new audio API—eventually to be made public'

which part is eventually public? what is the correct way in v2.28.0+ to be able to get a reference to the audio object from a track name?

I really appreciate your detailed & timely review/comments/responses. I wish I'd written better code initially knowing you were going to be so thorough looking at it.

5. Me:

Apologies for the delay. Health issues have been problematic lately.

which part is eventually public?

The documentation of the API. There's currently no documentation on it—life has been getting in the way.

what is the correct way in v2.28.0+ to be able to get a reference to the audio object from a track name?

Replace the following from your code:

var tracks = Macro.get("cacheaudio").tracks,
loopId = this.args[0];

if (!tracks.hasOwnProperty(loopId))
	return this.error('loop track "' + loopId + '" does not exist');

const audio = tracks[loopId];

With either something like this:

var loopId = this.args[0];

if (!SimpleAudio.tracks.has(loopId)) {
	return this.error('loop track "' + loopId + '" does not exist');
}

var audio = SimpleAudio.tracks.get(loopId);

Or more simply and what I recommend:

var loopId = this.args[0];
var audio  = SimpleAudio.tracks.get(loopId);

if (!audio) {
	return this.error('loop track "' + loopId + '" does not exist');
}

6. David Blackman (blackmad @github):

Hey Thomas,

Thank you again for your detailed reply.

I think I’ve addressed all of your concerns, as well as separated out the macro into its own github repo, added some instructions and a demo - https://github.com/blackmad/sugarcube-audioonend

Let me know if there’s anything else you’d like to see changed, and if you’d be up to add it to the list of external macros in the sugarcube docs.

‌Best,
David

7. Me:

Sorry for the late reply. I’ll get to this soon.

The basic take away, however, is that something like this will likely get done as a 1st-party macro. Whether it’ll be included in the core or as an add-on, I’m unsure of yet. It will also likely be more generic, so you may still prefer your macro.

There’s some additional discussion about a general audio event macro on SugarCube’s Trello, if you’re interested.

8. Me:

PS: There are still some issues with the macro, both minor and glaring:

  1. Still have copy-paste silliness going on.
  2. You're using the onended IDL attribute rather than a modern event listener.
  3. You register an anonymous arrow function with the onended IDL attribute just to invoke another function.
  4. By using the onended IDL attribute the content will be rendered every time the event fires, which may not always be appropriate.
  5. You’re not clearing the event listener upon passage navigation, so the content will continue be rendered every time the event fires, even though its target has been navigated away from.

Something like the following (in ES5) might be more appropriate:

/*! <<audioonend>> macro for SugarCube 2.x */
!(function () {
	'use strict';

	if (Has.audio) {
		Macro.add('audioonend', {
			isAsync : true,
			tags    : null,
			handler : function () {
				if (this.args.length === 0) {
					return this.error('no track ID specified');
				}

				var id    = this.args[0];
				var track = SimpleAudio.tracks.get(id);

				if (!track) {
					return this.error('track "' + id + '" does not exist');
				}

				var content = this.payload[0].contents;

				if (content === '') {
					return;
				}

				// Custom debug view setup.
				if (Config.debug) {
					this.debugView.modes({ block : true });
				}

				var transition = this.args.length > 1 && /^(?:transition|t8n)$/.test(this.args[1]);
				var $wrapper   = jQuery(document.createElement('span'));

				$wrapper
					.addClass('macro-' + this.name)
					.appendTo(this.output);

				var handleEnded = function () {
					var frag = document.createDocumentFragment();
					new Wikifier(frag, content);

					var $output;

					if (transition) {
						$output = jQuery(document.createElement('span'));
						$output
							.addClass('macro-audioonend-insert macro-audioonend-in')
							.appendTo($wrapper);
					}
					else {
						$output = $wrapper;
					}

					$output.append(frag);

					if (transition) {
						setTimeout(function () {
							$output.removeClass('macro-audioonend-in')
						}, Engine.minDomActionDelay);
					}
				};

				jQuery(document).one(':passageinit', function () {
					track.off('ended', handleEnded);
				});

				track.on('ended.macros.macro-' + this.name, handleEnded);
			}
		});
	}
	else {
		Macro.add('audioonend', {
			skipArgs : true,
			handler  : function () {
				/* no-op */

				// Custom debug view setup.
				if (Config.debug) {
					this.debugView.modes({ hidden : true });
				}
			}
		});
	}
})();

Engine APIs to mark and return to moments within the history

Original request (from the old Bitbucket repo):

SugarCube contain special tag bookmark but mostly this good for player navigation. And I can’t find what can work like the same, but only when called from background.

Can you add two function in “Engine API” for realization this?

Engine.mark() - boolean. Setup mark on called moment in history to return for marked passage.

Engine.return() - boolean. Return game on last marked moment in past history. If moment was dropped from history (by max state limit) only will be return false.

Both function names is example.

This will be cool for returned game state if player die, for returned from ingame manual (because that passages only contains helpful description and not affected on game) and similar situations.


I'm thought little more about that and has concluded system must be a little more complexity. Need additional functions "Engine.hasMark() - boolean.", to check to exist mark or not, and "Engine.markStep() - int", to get number of step where mark set. These functions can be used in a lot of ways, but paramount is need to control mark in history borders (if mark can exit from max history we must have a way to check this).

First function used to check on exist of mark, Second function to calculate how far we go from here and how many steps we can do it before mark has died (be better if calculation be created by developers, I think).

Honestly, the fifth function will not be a hindrance and can be in engine. "Engine.markLeft() - int", return number of left steps before mark has died.

And example of how I can use this.

Player must resolve some puzzle and have 10 or fewer steps to resolve him.

I set mark on a step with rules if after a 10 step puzzle still not resolved will be showed dialog window with losing message and game returned on mark, after closing a dialog. That saves a lot of resources (because will allow rewrite wrong progress) and do it game more interactive.

[Docs] Guide regarding how the state works, particularly session state and `StoryInit`.

I've seen a lot of questions about how StoryInit works, what the difference between the session storage state and the autosave is, and generally about what happens when you click refresh on the browser vs restart in the UI.

I think a guide for this could be wise. I'm willing to write a rough version and PR it if it's something you think would be worth including.

bookmark vs. maxStates

If have set Config.history.maxStates = 3 because I want the player can rewind 3 passages at most.
Now some passages are tagged with "bookmark".

Sadly the tagged passages are only shown if they are on the maxStates range. If I set maxStates to 100 and go through my story, I can see the links on the bookmark dialog. But if I set maxStates to 3, I can see the last bookmark at most - because all other bookmarks are "older" than 3 passages, I think.

I would like to limit the redwind option to 3 (maxStates = 3) but also want to add particular passages to the bookmark dialog (irrespective of maxStates).

Would this be possible?

Blocking loadscreen

The loadscreen does not currently block loading of the starting passage, it simply hides it. Blocking would be better, in general, and resolve a few issues.

Issues

There is the, minor, potential to break existing code.

SugarCube parse errors in HTML comments have bad effect

I was trying to determine why an <<if>> macro was failing and was commenting out parts in the middle and it still failed. It turns out that a <</link>> was missing one of its closing greater-than signs and so it snarfed up the <<else>> token.

:: Start
<<set $step to 10>>
<<include "Step 06">>
<<include "Step 07">>

:: Step 06
<<if $step gte 6>>Before6
<<if $step is 6>>Then<!-- <<link "AddHi">>
<</link>--><<else>>Else<</if>> After6<</if>>

:: Step 07
<<if $step gte 7>>Before7
<<if $step is 7>>Then<!-- <<link "AddHi">>
<</link>>--><<else>>Else<</if>> After7<</if>>

The output is:

Before6
After6
Before7
Else After7

I think this is a parsing bug and a parsing mis-feature:

  • Parsing bug: The construct should comment out everything, including SugarCube. In particular, an HTML comment should not be legally ended with <</link>--><<else>>
  • Parsing misfeature: It would be nice if <</link><<else>> would generate an error rather than silently be treated as a legal synonym for <</link>>.

"Promise rejected error" on opening game

SugarCube v2.30.0 and tested in Opera.

Whenever you first open the HTML in a published story it throws a:

Promise rejected error:
DOMException: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD

This causes the game to be paused in the debugger if you have the inspection window open.

To fix this, the code should avoid trying to play anything if there's nothing to play.

Thanks.

StoryMenu can't access Config.debug

It seems that the passage StoryMenu can't access the Config API's variable debug.

When trying something like
<<if Config.debug>> /* some link */ <</if>>
inside StoryMenu.tw, the menu in the UI bar never displays the aforementioned link.

Compiled with tweego.

RFE: option to <<textbox>> for actions rather than a passage

I've found it limiting to have the only way to handle a CR on a textbox is to go to a new passage. I created my own version <<textboxlink>> that silently expands its payload when CR is pressed. I could do a pull-request, if you'd like. Otherwise, the only real difference is changing

// If Return/Enter is pressed, set the variable and, optionally, forward to another passage.
if (ev.which === 13) { // 13 is Return/Enter
	ev.preventDefault();
	State.setVar(varName, this.value);

	if (passage != null) { // lazy equality for null
		Engine.play(passage);
	}
}

to

// If Return/Enter is pressed, set the variable and, perform link
if (ev.which === 13) { // 13 is Return/Enter
	ev.preventDefault();
	State.setVar(varName, this.value); // redundant?
	Wikifier.wikifyEval(contents);
}

where contents is set to this.payload[0].contents.trim() in the outer scope.

Error triggers "Cannot read property 'stack' of null" error in SugarCube code

SugarCube v2.30.0 tested in Opera.

In lib/alert.js it has the following code:

		if (typeof error === 'object' && error.stack) {
			mesg += `\n\nStack Trace:\n${error.stack}`;
		}

However, "null" is an object, so if "error" is set to "null", then it will get past the "typeof error === 'object'" check, but when it tries to read the "stack" property of "error" it will throw the error:

Uncaught TypeError: Cannot read property 'stack' of null

To fix this you should do:

		if (!!error && typeof error === 'object' && error.stack) {
			mesg += `\n\nStack Trace:\n${error.stack}`;
		}

which will work since "null" has a falsy value, while any legitimate object will have a truthy value.

Thanks.

SugarCube UI passage StoryTitle does not support the include macro

It'd be helpful if the SugarCube UI passage StoryTitle supported the use of the include macro.
All of the other UI passages do support it. See the upper left corner of the attached screengrab.

I'm also including a Zip of the includes.tw file used to demonstrate this problem.
includes.zip

capture_005_13032020_000605

Background: I'm trying to simplify as much as possible the .tw file containing the story's text both so I'm not distracted by (and tempted to modify) the SugarCube code and to simplify the translation of the story's .tw file into other formats.

Line continuation with nobr only sometimes works

I'm using SugarCube 2.31.1 and Tweego 2.1.1

I've set nobr globally using Config.passages.nobr = true; and have a widget that I'm trying to use to display the current ingame time.

The widget is below:

<<widget "currenttime">>
    <<if $gameDate.getUTCHours() eq 0>>
        12\
    <<elseif $gameDate.getUTCHours() gt 12>>
        <<print $gameDate.getUTCHours() - 12>>\
    <<else>>
        <<print $gameDate.getUTCHours()>>\
    <</if>>:\
    <<if $gameDate.getUTCMinutes() lt 10>>0<</if>><<print $gameDate.getUTCMinutes()>>\
    <<if $gameDate.getUTCHours() gte 12>>PM<<else>>AM<</if>>\
<</widget>>

Given $gameDate is a JS Date object for 8 AM (The date is irrelevant), the output is 8:\ 00\ AM, meaning that the line continuation marker after the print macro for the hour was parsed properly, and the one after AM, but all others are ignored and output

Other times have no effect and the same line continuation characters are ignored,

Stateful audio subsystem

The audio subsystem—the SimpleAudio API and backing browser APIs—currently integrate neither the current audio configuration nor current playback state into play sessions—meaning neither page reloads nor save loads may restore the audio state.

This requires authors to either accept that audio may briefly stop playing after a page/save load or add extra code to ensure their audio is restored after such.

It would be better if the audio subsystem could handle this automatically for authors.

Issues

  • There is the potential to break existing code—likelihood in cases where authors have added their own restoration code.
  • It's trivial to use the SimpleAudio API to create state that cannot be serialized correctly. Unfortunately, nothing can be done about this save noting the instances within the documentation—that no one will read.

Suppress line-break to <br> conversion within select HTML element families

Suppress line-break to <br> conversion within select HTML element families—meaning markup that consists of an ancestor element that should only contain its own descendant elements; e.g., <table> and its various sub-elements.

Take the following example:

<table>
	<<for _name range $names>>
	<tr>
		<td>_name</td>
	</tr>
	<</for>>
</table>

Currently, for that to work as intended, you have to wrap it within some kind of line-break control feature—e.g., <<nobr>>—to suppress line-break to <br> conversion. Elsewise, you end up with <br> elements as children of the <table>, surrounding the <tr> elements. Not ideal.

National letters in name of variable

As far as I understand, $variable and _variable are translated to State.variables.variable/ State.temporary.variable (by markup/scripting.js). It may be better to translate into State.variables['variable']/ State.temporary['variable']. This will allow us to use the name with national letters. Of course, there is need to change RegExp Patterns.variable etc.

StoryInit passage caching?

When modifying passages, I can always just refresh the game in my browser (after recompiling) to see the changes immediately. However, any changes I make to the StoryInit passage seem to be completely ignored until I close the compiled game's tab and/or the browser, and reopen it again. Only then does it seem to register for example a different value in a set macro.

Tested this in Chrome, using both the normal and testmode of the story, as well as the web version of Twine and Tweego. The behavior is the same using all of those variants.

From my testing this seems to only affect the StoryInit passage. Normal passages, JS code and CSS all get changed instantly when I refresh the compiled game's page. It's not a huge issue for finished games, but it is getting annoying to have to close and reopen my game every time I need to test a change to the StoryInit passage.

Story splash/intro special passage

A special passage that is shown to players before the normal starting passage and does not become part of the history. Essentially, a splash or introduction screen.

My current idea on how it should work is that it would simply require any player interaction to move past—basically, the whole viewport would get the equivalent of an […].ariaClick() event handler.

Reasoning

  • To remove splash screens from the story history.
  • As a workaround for user activation requirements for media playback.

Others I'm likely forgetting at the moment.

Possible Names

  • StorySplash
  • StoryIntro

Other suggestions?

Suggestion: Please make software environment information available in-story

It'd be helpful for bug reporting if a description of the compile- and run-time software environment could be made available within a story, so it could be shown in a screengrab rather than the user perhaps providing the wrong information.

In particular, it'd be nice if SugarCube could provide any of the following items:

  1. The version of SugarCube used to produce the story
  2. The version of tweego or Twine used to compile the story
  3. The version of OS where the story was compiled
  4. The version of OS where the the story is being run
  5. The version of browser being used to run the story

I might have overlooked them, but I've been searching for a while and haven't found any of these possibilities mentioned in the docs.

Setting menu disappears on reload

The following code I have added to my start passage:

<<script>>
	var settingFontSize = function () {
		var emSize = (settings.fontSize/100).toString();
		document.getElementById("passages").style.fontSize = emSize+"em";
	};
	Setting.addHeader("");
	Setting.addRange("fontSize", {
		label    : "Font size",
		min      : 100,
		max      : 200,
		step     : 5,
		default  : 100,
		onInit   : settingFontSize,
		onChange : settingFontSize
	});	
	Setting.addToggle("playSound", {
		label    : "Play music?",
		default  : false
	});
	Setting.save();
<</script>>

Starting a new game the setting dialog works like expected. But after reloading the game (reload the window on browser or close and open the html file) or after resetting the values (with the default reset button on setting dialog) the setting button disappears from the menu.

If you incorrectly quote a variable in a link/button macro, the error message is confusing.

Here's a gif of the problem: https://cdn.discordapp.com/attachments/389868418855075840/722589854407590030/gif.gif

my mistake was quoting the variable in this button: <<button "Continue" "$exploration_back_passage">><</button>> When I clicked on the button, it looks for passage named $exploration_back_passage which didn't exist. The problem is, the error message says it's looking for a passage named Test Scene With No Links which does exist.

This error message was confusing. If it said it was looking for a passage named $exploration_back_passage , I would've immediately realized my mistake.

Castellano (Spanish from Spain) Translation

The current spanish translation you can find on https://www.motoslave.net/sugarcube/2/ is not from Spain, it's from somewhere in south america I think. I have no problem with that but some things look like they were translated wrongly as they don't make sense in spanish. I decided to translate them on my own and now they are readable by Españoles and any other spanish speaking people, not only by the specific community that made the original translation.

I have the file here but I don't know how to upload it as I've never worked with github as a team or as a contributor (never really used git other than branching for different versions).

SugarCube/twee special passage StoryData cannot be used in the include macro

It'd be helpful if the StoryData passage could be displayed within a story by using an include macro. Currently such an inclusion fails. See the attached screengrab. I'm also including a zip of the .tw used to display this problem.

includes.zip

capture_005_13032020_000605

Background: I'd like to be able to provide the story's IFID to the reader without having to manually copy the information contained in the StoryData passage. Such a manual inclusion might not be updated to show the correct information if the IFID has to be changed for some reason.

Save dialog disk button issues

Some people have been confused as to what the Save to Disk… and Load from Disk… buttons actually do—e.g., thinking they export the save slots to disk.

Add a teletype/typewriter macro

Add a teletype/typewriter macro that handles most content, rather than being limited to strings—e.g., as Typed.js is. Invocations within the same passage should coordinate—i.e., they should be processed in order of appearance, rather than all attempting to type at once.

The prototype would be something like:

<<type speed [start delay] [keep|none]>> … <</type>>

E.g.,

<<type 40ms>>
	Type characters from this content every 40 milliseconds.  Including [[links]] and ''other markup''!
<</type>>

<<type 40ms start 2s>>
	Type characters from this content every 40 milliseconds, starting after a 2 second delay.
<</type>>

<<type 40ms keep>>
	Type characters from this content every 40 milliseconds, keeping the cursor after typing is complete.
<</type>>

<<type 40ms none>>
	Type characters from this content every 40 milliseconds, not using a cursor at all.
<</type>>

EDIT: Updated prototype and examples:

Randomization is not consistent between sessions

Initializing the prng state with the same seed in different browser tabs yields different results.

// Using setTimeout to wait for all scripts to finish initializing.
setTimeout(() => {
  console.log(State.prng.isEnabled())
  console.log(State.prng.seed)
  console.log(State.prng.pull)
  console.log(State.random())
})

Tab 1:

true
wavyorangeneonrainbowfish
5350
0.6990063451940978

Tab 2:

true
wavyorangeneonrainbowfish
5336
0.20215290072679626

Refreshing the same tab multiple times seems to be consistent, but if I clear the session state and refresh I get a different pull/float again.

RFE: "auto" keyword on <<radiobutton>>

The <<radiobutton>> macro would be more useful if it added an auto keyword to set the radio button according the current value of the variable. This would permit the author to write <<radiobutton "$pie" "blueberry" auto>> which would select this button if and only if $pie evaluated to "blueberry".

The change is easy to accomplish. I could send a pull request. It consists of adding a few lines to the end of the macro. In place of:

	if (this.args.length > 2 && this.args[2] === 'checked') {
		el.checked = true;
		State.setVar(varName, checkValue);
	} 

we add an extra condition (leaving the first unchanged). This RFE is backward compatible:

	if (this.args.length > 2 && this.args[2] === 'checked') {
		el.checked = true;
		State.setVar(varName, checkValue);
	} else if (this.args.length > 2 && this.args[2] == 'auto') {
		if (State.getVar(varName) == checkValue) {
			el.checked = true;
		}
	}

I used == here but === may be the better approach.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.