Bisquixe redelevopment: Feature list and reevaluation

I’m planning on reworking Bisquixe this summer to work with Parchment (which already incorporates a lot of Bisquixe features). This would help cut down Bisquixe’s technical debt.

Bisquixe doesn’t actually have that many features, so the more that Parchment or other extensions can do, the more that the actual elements of Bisquixe can get cut down. I’m going to list all of the features here with a description of how they’re implemented.

Bisquixe has two components: the Simple Multimedia Effects extension and the Bisquixe interpreter.

Simple Multimedia Effects

Hyperlinks

This is roughly 75% of the code, and essentially none of it is mine (I think I copied it from Angstmurf). Hyperlinks are already built in to glulx, they just have to be ‘turned on’. Turning them on makes an extra blank line appear when clicking on them, but you can turn that off by setting a variable called ‘echo off’. Doing that makes the player’s command not appear when typed in. Furthermore, you can’t switch that off and on during a single game.

So, almost all of the code in this section is completely replacing the Keyboard function specifically to echo the player’s command without the blank line.

I’ve reported this to Graham Nelson and other Inform maintainers but it’s really hard to describe if you haven’t tried making a hyperlink hybrid game, so I’m not sure I was able to describe the bug clearly enough for it to get fixed.

Future development and places for improvement

This is the only section that uses Glulx Entry Points, which has been a sticking point for a long time due to the multiple versions. I think @Zed has been working on his own hyperlink extension which, if it came to fruition, could either completely replace this section or be incorporated into Bisquixe.

Here’s the current implementation of how links are assigned during each turn (due to Angstsmurf):

The hyperlink list is a list of texts that vary..

To hyperlink (hyper-text - a text) as (hyper-command - a text):
	let hyperlink index be a number;
	if the hyper-command is listed in the hyperlink list:
		repeat with count running from 1 to the number of entries in the hyperlink list:
			if entry (count) of the hyperlink list is hyper-command:
				let hyperlink index be count;
	otherwise unless the hyper-command is "":
		add hyper-command to hyperlink list;
		let hyperlink index be the number of entries of hyperlink list;
	say "[set link (hyperlink index)][hyper-text][terminate link]";
		 
To say set link (N - a number):
	(-  if (glk_gestalt(gestalt_Hyperlinks, 0)) glk_set_hyperlink({N}); -)

To say terminate link:
	(-  if (glk_gestalt(gestalt_Hyperlinks, 0)) glk_set_hyperlink(0); -)

There have been some bugs found with this. Several them were unknown bugs hidden in Inform’s hyperlink code that wasn’t known because no one uses hyperlinks (for instance, the Windows Inform IDE had a bug, now fixed, that would mess up the 3rd hyperlink’s target every time). Another issue is with hyperlinks in tables.

Things that would be improvements:
-Moving hyperlink notation to base Inform
-Making a separate hyperlinks extension (or one to be included in Bisquixe, like Mitochondria)
-Adding new notation. The current notation I use is: hyperlink "bob" as "x bob". Zed had some ideas on this.

Adding audio and image files (game file-side)

Right now, without Bisquixe, to use audio and image files in Inform, you have to use external tools to edit the javascript to declare the files.

Bisquixe adds commands that give the interpreter instructions to add those images and files when gameplay starts. This can cause a slight flicker if an image is used on the first page.

The code is:

Inform 6:

	[ glk_add_audio _vararg_count;
	  @glk 5381 _vararg_count 0;
	  return 0;
	];

	[ glk_add_image _vararg_count;
	  @glk 5382 _vararg_count 0;
	  return 0;
	];

Inform 7:

To upload-audio (Temp - a text) with internal name (Temp1 - a sound name):
	let tempid be the Glulx resource ID of Temp1;
	let tempname be "[temp].mp3";
	add-audio tempname with id tempid;

To add-audio (Temp - a text) with id (Temp1 - a number) :
	(-
	if(glk_gestalt(5376, 0)){
	glk_add_audio(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}), {Temp1});
	}
	-)

To add-image (Temp - a text) with alttext (tempalt - a text) with id (Temp1 - a number) with width (Temp2 - a number) with height (Temp3 - a number) :
	(-
	if(glk_gestalt(5376, 0)){
	glk_add_image({Temp1},Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}),Glulx_ChangeAnyToCString(TEXT_TY_Say, {TempAlt}), {Temp2},{Temp3});
	}
	-)

Future development and places for improvement

I haven’t really heard any complaints about this. It would be nice for the Inform IDE to automatically do this to the javascript during release.

CSS setting and google font importing (game file-side)

These sections take in inform commands and send them to the interpreter.

This is the inform 6 code:

	[ glk_set_css_fast _vararg_count;
	  @glk 5377 _vararg_count 0;
	  return 0;
	];
	
	[ glk_set_css_slow _vararg_count;
	  @glk 5379 _vararg_count 0;
	  return 0;
	];

	[ glk_any_style _vararg_count;
	  @glk 5378 _vararg_count 0;
	  return 0;
	];

	[ glk_import_google_fonts 

This is the Inform 7 code:

To css-set-fast (Temp - a text):
	(-
	if(glk_gestalt(5376, 0)){
	glk_set_css_fast(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}));
	}
	-)

To css-set-slow (Temp - a text):
	(-
	if(glk_gestalt(5376, 0)){
	glk_set_css_slow(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}));
	}
	-)

To import-google-fonts (Temp - a text):
	(-
	if(glk_gestalt(5376, 0)){
	glk_import_google_fonts(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}));
	}
	-)
	
To upload-audio (Temp - a text) with internal name (Temp1 - a sound name):
	let tempid be the Glulx resource ID of Temp1;
	let tempname be "[temp].mp3";
	add-audio tempname with id tempid;

To add-audio (Temp - a text) with id (Temp1 - a number) :
	(-
	if(glk_gestalt(5376, 0)){
	glk_add_audio(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}), {Temp1});
	}
	-)

To add-image (Temp - a text) with alttext (tempalt - a text) with id (Temp1 - a number) with width (Temp2 - a number) with height (Temp3 - a number) :
	(-
	if(glk_gestalt(5376, 0)){
	glk_add_image({Temp1},Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}),Glulx_ChangeAnyToCString(TEXT_TY_Say, {TempAlt}), {Temp2},{Temp3});
	}
	-)
	
Part 2 - Arbitrary styling

Section 1 - Class naming
		 
To set-any-class (Temp - a text):
	(- 
	
	if(glk_gestalt(5376, 0)){
	glk_any_style(Glulx_ChangeAnyToCString(TEXT_TY_Say, {Temp}),style_counter);
	style_counter++;
	}	
	-)
	
Every turn (this is the style_counter reset rule):
	reset-style;
	
To reset-style:
	(- style_counter = 10; -)

Currently what happens on this end is that CSS-set-fast, cc-set-slow, and import-google-fonts take in a single text and send that to the interpreter. That’s entirely because I could figure out Inform 6 arrays, which would probably be more appropriate. I’ll discuss later how that might be improved.

The only even mildly complex piece of code above is the set-any-class. What that does is take in a string and send it to the interpreter, and it also sends a unique number to the interpreter that starts at 10 and goes up every time it’s called. This starts at 10 because there are 9 or so built-in glulx styles in Quixe, but I increased that cap to 100, and this uses that fact (more description given in the interpreter section).

Future development and places for improvement

Using Inform 6 arrays would give a lot more capabilities to Bisquixe. I could use help on figuring that out.

And that’s it! That’s the entirety of the Bisquixe extension: turning on hyperlinks, adding commands to tell the interpreter to add images and sound, and adding commands that pass text strings to the interpreter.

I’ll cover the interpreter in the next post.

5 Likes

The Bisquixe interpreter

This was created by taking a version of Quixe from the year I started working on Bisquixe and editing the files. I didn’t know what ‘min’ js files were so I edited those instead of the original ones (min files strip out comments and condense lines). Any rework would likely use the uniminimized files.

Here are the changes I made:

Adding styles to whole lines and hiding ‘hidden’ commands

divel.append(el);
/*Bisquixe start*/if(rtext.slice(0,5) == 'zzxvm'){
	el.parent().addClass('Hidden_Response');
}
el.parent().addClass('Buffer_'+rstyle);/*Bisquixe end*/
.Hidden_Response {
	display: none;
}

This is in the middle of the code that actually prints text on the screen. ‘zzxvm’ is a magic word for Bisquixe; any command printed that starts with zzxvm is not printed on the screen at all. This is to allow commands that affect things on the screen without being printed to the player. For instance, you can have a button on the screen that changes the background color when clicked. That can be achieved by having the button enter ‘zzxvm screen bright’ or something similar into Inform, which then takes effect like a normal inform command, just not printed to the screen.

In addition, this code adds the style ‘Buffer+ rstyle’ to the parent of the current text. That’s because Quixe prints texts in two wrappers (spans and divs) but styles are usually only applied to spans. By applying styles to the divs as well, the player can choose which one to change.

Future development and places for improvement

The buffer lines/div style line could be better documented; Max and Drew both wanted to do something involving the divs recently but had trouble figuring out what this code was doing (I also couldn’t remember and had to code dive).

Styles and adding styles to lines

Bisquixe changes the max number of styles to 100:

/*Bisquixe begin*/style_NUMSTYLES:100,/*Bisquixe end*/

and

*Bisquixe begin*/var StyleNameMap={0:'normal',1:'emphasized',2:'preformatted',3:'header',4:'subheader',5:'alert',6:'note',7:'blockquote',8:'input',9:'user1',10:'user2',11:'default',12:'default',13:'default',14:'default',15:'default',16:'default',17:'default',18:'default',19:'default',20:'default',21:'default',22:'default',23:'default',24:'default',25:'default',26:'default',27:'default',28:'default',29:'default',30:'default',31:'default',32:'default',33:'default',34:'default',35:'default',36:'default',37:'default',38:'default',39:'default',40:'default',41:'default',42:'default',43:'default',44:'default',45:'default',46:'default',47:'default',48:'default',49:'default',50:'default',51:'default',52:'default',53:'default',54:'default',55:'default',56:'default',57:'default',58:'default',59:'default',60:'default',61:'default',62:'default',63:'default',64:'default',65:'default',66:'default',67:'default',68:'default',69:'default',70:'default',71:'default',72:'default',73:'default',74:'default',75:'default',76:'default',77:'default',78:'default',79:'default',80:'default',81:'default',82:'default',83:'default',84:'default',85:'default',86:'default',87:'default',88:'default',89:'default',90:'default',91:'default',92:'default',93:'default',94:'default',95:'default',96:'default',97:'default',98:'default',99:'default',100:'default'}/*Bisquixe end*/

It also lets you add a style to a line:

function glk_any_style(val1,val2){
	StyleNameMap[val2]=val1;
	glk_set_style(val2);
	}

So, combined with the Inform code, here’s the process:
-Inform and glulx assign style numbers to each chunk of code. If the player uses set_any_style, it assigns the number 10 to that line along with a name. Once that reaches Quixe, it looks for things labelled ‘10’ and adds that name as a style. If a second custom style is used in the same turn, Inform sends 11 and a name, and so on. Each turn, it’s reset.

The number is needed because Glulx uses numbers to delineate when styles should start and stop.

Future development and places for improvement

This doesn’t get too much use and I haven’t heard any complaints. Open to ideas. I used it in Faery-Sitter to make the background slowly change chromatically over time, but it wasn’t that great.

Sound effects

A big chunk of glkote.min.js was rewritten for me to implement Quixe’s sound functions. They exist in Quixe but are empty, so I implemented them to make sure they work with Hanon Ondricek’s Transparent and Wade Clarke’s Six.

Future development and places for improvement

Parchment seems to have implemented these, so this section (which is the biggest chunk of custom code) could be eliminated.

Adding audio and images (interpreter-side)

This just does the same thing that Zarf’s tools do to let audio and images be uploaded, but it does it during runtime.

var image_keys={};

function glk_add_audio(val,num){
	var audioElement = document.createElement('audio');
    audioElement.setAttribute('src', 'Sounds/'+val);
    audioElement.id = 'Snd'+num;
	audioElement.class = 'playable';
	audioElement.type="audio/mp3";
	audioElement.preload = "none";
	audioElement.playing = false;
	audioElement.loop = false;
	audioElement.repeats=0;
	$(audioElement).bind("playing", function(){
		console.log(audioElement.id+' is playing');
		audioElement.playing = true;
		audioElement.repeats--;
	});
	$(audioElement).bind("ended",  function(){
		console.log(audioElement.id+' has ended');
	if(audioElement.playing == true){
		if(audioElement.repeats > 0){
		   audioElement.play();
		}
	   else if(audioElement.notify!=0){
			if(!gli_selectref)
			return;gli_selectref.set_field(0,Const.evtype_SoundNotify);gli_selectref.set_field(1,null);gli_selectref.set_field(2,num);gli_selectref.set_field(3,audioElement.notify);audioElement.notify=0;if(GiDispa)
			GiDispa.prepare_resume(gli_selectref);gli_selectref=null;VM.resume();
	   }
		audioElement.playing = false;     
	}});
	audioElement.notify=0;
	document.body.appendChild(audioElement);
}

function glk_add_image(num_key,val_url,val_alt,num_width,num_height){
	var tempkey = {
	'image': num_key,
	'url': 'Figures/'+val_url,
	'alttext': val_alt,
	'width': num_width,
	'height': num_height
	}
	image_keys[num_key]=tempkey;
	game_options['image_info_map'] = image_keys;
	Blorb.init(game_options.image_info_map,{format:'infomap'});
}

Future development and places for improvement

If an image is used on the first screen then there can be a flash while the image is quickly being uploaded through this code.

iOS Safari only plays mp3s but Inform doesn’t allow mp3s due to outdated info on copyrights, so the player has to use two formats when compiling (one to put in the blorb and one to include in the online upload).

Css setting

The first command was my first experiment with Bisquixe. It can be ignored (only including it in case someone reads the code).

After that, there are two ways to adjust CSS. One is ‘fast’ and one is ‘slow’.

‘Slow’ uses jquery to alter the css properties of the elements with the given style. It hasn’t been very popular; I think it only works on things that were printed in previous turns.

‘Fast’ appends new CSS to the head. I was worried this could cause performance problems, but it hasn’t been too bad except in one instance where someone was appending two things to the head every turn. Some browsers have a limit on this kind of thing, especially older ones.

Right now the way the code works is that Inform passes a single stream and then the code splits it along semicolons into three parts: the first part is the style, the second part is the css property we are going to change, and the third is the value of that property.

It’s essentially madlibs. It restricts the player to a relatively small set of possible css functions.

For instance, it doesn’t allow importing google fonts, so a separate function was written for that.

This section also handles internal links (which I forgot to mention in my original post as part of the hyperlink section). This allows links to open up webpages in the same directory.

/*Bisquixe start*/
5376:new FuncSpec(5376,"do_awesome_stuff",new Prototype([],null)),
5377:new FuncSpec(5377,"set_css_fast",new Prototype([new ArgString()],null)),
5378:new FuncSpec(5378,"any_style",new Prototype([new ArgString(),arg_int_signed],null)),
5379:new FuncSpec(5379,"set_css_slow",new Prototype([new ArgString()],null)),
5379:new FuncSpec(5379,"set_css_slow",new Prototype([new ArgString()],null)),
5380:new FuncSpec(5380,"import_google_fonts",new Prototype([new ArgString()],null)),
5381:new FuncSpec(5381,"add_audio",new Prototype([new ArgString(),arg_int_signed],null)),
5382:new FuncSpec(5382,"add_image",new Prototype([arg_int_signed, new ArgString(), new ArgString(), arg_int_signed, arg_int_signed],null)),
5383:new FuncSpec(5382,"internal_page",new Prototype([new ArgString(), arg_int_signed],null))
/*Bisquixe end*/


function glk_do_awesome_stuff(){
	
	var elements = document.getElementsByClassName("WindowFrame")
        for (var i = 0; i < elements.length; i++) {
            elements[i].style.background="black";
        }
}

function glk_set_css_fast(val){
	const vals = val.split(';');
//	alert (vals[0]+ " " + vals[1] + " " + vals[2]);
//		$(vals[0]).css(vals[1], vals[2]); 
	$('<style> '+vals[0]+' { '+vals[1]+': '+vals[2]+'; }</style>').appendTo('head');	
}

function glk_any_style(val1,val2){
	StyleNameMap[val2]=val1;
	glk_set_style(val2);
	}

function glk_set_css_slow(val){
	const vals = val.split(';');
//	alert (vals[0]+ " " + vals[1] + " " + vals[2]);
		$(vals[0]).css(vals[1], vals[2]); 
}

function fix_height(){
	document.getElementById("gameport").style.setProperty("height", "auto");
}
window.addEventListener('scroll', fix_height);

(short note, I have no idea what the above does or why I added up. Maybe Zarf fixed a bug in Quixe in a version after I forked bisquixe and I copied it over?)

function glk_import_google_fonts(val){
	$("<style> @import url('https://fonts.googleapis.com/css2?family="+val+"&display=swap'); </style>").appendTo('head');	
}

function glk_internal_page(val,newpage){
	if(newpage==0){
		window.location.href = '.'+val;
	}
	else {
		window.open('.'+val);
	}
}

Future development and places for improvement

This is by far the most-used part of the code, so improvements here would be great. Is there a good alternative to appending to the head that retains current functionality? Could it be employed in parallel? Is there a better divider or format for the CSS? Or should we allow appending arbitrary code to the header? It allows a lot more risky actions, but a person who could write risky bisquixe code could just write it in the js themselves so it’s not like we’d be stopping potential criminals through disallowing ‘bad’ js (just providing a moderate hindrance).

Should there be any more custom functions like the google fonts one?

Remainder

There’s also some extra code for bookkeeping purposes. These are ‘gestalts’ used when Inform asks ‘Can we do sound? Can we do hyperlinks?’ etc. All the 1’s mean that ‘yes, we can do this’. To know what the numbers mean, check out this resource: https://github.com/iftechfoundation/ifarchive-if-specs/blob/main/Glk-Spec.md

5376 means bisquixe capabilities.

/*Bisquixe start*/case 8:return 1;case 9:return 1;case 10:return 1;case 11:return 1;case 12:if(val==3||val==4)
return 1;else
return 0;case 13:return 1;case 14:return 1;case 15:return 1;case 16:return 1;case 17:return 1;case 18:return 1;case 19:if(val==Const.keycode_Escape)
return 1;if(val>=Const.keycode_Func12&&val<=Const.keycode_Func1)
return 1;return 0;case 20:return 1;case 21:return 1;case 22:return 1;case 23:return 0;case 5376:return 1;}/*Bisquixe End*/
1 Like

@Dannii has also been working on one that uses something akin to self-modifying code (“self-inspecting code”?) to allow a more elegant syntax. I’m looking forward to it, but it may not come out until Inform 11 (which will also be doing away with Glulx Entry Points once and for all).

I stopped working on my hyperlink notation once he announced his, since his will be a lot better, but if there’s interest I figured out a way to make it not conflict with Bisquixe styling in the interim.

Inform 11 thankfully will!

In this case, I’m not sure they would. You fundamentally need to pass a bunch of data to the @glk opcode, which means all that data needs to be in RAM at the same time. It’s not a big difference whether that’s one big buffer or a bunch of little buffers. And building a string to send is very easy in I7.

2 Likes

Even if it’s easy, if the string is composed of logically separate pieces (as appears to be the case for the CSS routines), it’s a very poor API – you’re requiring the user to learn a little mini-language instead of just expressing it in normal Inform syntax.

1 Like

This is true! I don’t think it should necessarily be user-facing; it could be assembled by the library. I just don’t think there’s much drawback for using strings to pass data from the library through Glk to the interpreter.

1 Like

Passing strings can be advantageous, as we can use substitutions and such.

Oh, you have a fair point. Something like this would be totally fine:

To set css (attribute - text) to (value - text) on (class - text):
    css-set-fast "[class]; [attribute]; [value]".
2 Likes

Current development Inform has a new hyperlink system by @Dannii. Before he finished it, I had been futzing with one, but there’s no reason for it now.

2 Likes

I finished the second post!

I am excited for future developments, but from past experience Inform life cycles can take years, and people can get into IF, make games, and leave in that time, so I’m going to keep or improve my current changes until that update happens (but make my code modular so I can just cut it out!)

4 Likes

That’s what I was thinking this morning, but…

In the past we’ve seen six and a half years between Inform releases, so this is a very real concern imo. There are 256 open issues on the tracker at the moment, which is quite a lot of work still to go!

2 Likes

Thanks for putting this together and for continuing to develop Bisquixe! Whatever the future might hold, game authorship is an ongoing matter of interest today.

I have identified some minor tweaks and suggestions that aren’t really relevant to what I see as a more high-level discussion; I’ll pass those on elsewhere.

As a general comment, it would be good to get extensions and prerequisites to a central location. For Inform 10, that’s the Friends of Inform repo. I share the concerns regarding GEP versioning in particular, since it seems hard to document/centralize.

Hyperlinks

An inline option is desirable. I’ve been using the one suggested elsewhere by Daniel, and it is working well so far. I’m not sure how it performs with styling. I initially thought it wouldn’t support it, but I have some new ideas. The solution requires Formatting Capture, which I believe is unreleased. If we could push toward an easily documented (or built-in) solution for inline links, that would be ideal.

The keyboard stuff is incompatible with my current version of Story Mode because it uses Daniel’s Command Preloading (which also does things with the keyboard). This isn’t a huge deal, I’m just mentioning it.

Audio and Image setting

This is more of an interpreter thing (I think), but I haven’t been able to get cross-fading/ introducing (from the music extension) working. It’s very possible that I’m just not using it right.

I wonder if we should discuss file formats for audio. I think we’re hard-coded for MP3 atm because of Safari, but Inform doesn’t support MP3. Perhaps the better move is dropping audio support for iOS? The current state of mobile play is already kind of a mixed bag.

CSS setting

Readability aside, I think it would be nice to be able to optionally send raw CSS (unformatted by the extension) for cases where the typical “triad” doesn’t apply. I’m thinking of things like keyframe animations, @font-face, and perhaps even media queries.

I’m interested in your comments regarding performance. Is there guidance or advice we should provide authors, or is this such a remote possibility that we don’t need to worry about it?

Anyway, thanks for maintaining this! Happy to test and/or discuss anything, as well as update the reference as needed.

2 Likes

When I have some spare time, I’ll try incorporating Dannii’s @catch trickery into that extension. It’s only a stopgap until Inform 11 comes out and we’ll have proper support built-in, but sometimes a stopgap is a good thing to have.

The upcoming version of Inform will change this, but, well…that might be a long way off.

I think the best long-term solution is to get MP3 support added to Glulx, by which I mean Glk, by which I mean Blorb. It doesn’t support MP3 currently because of “the patent concerns that encumber MP3”, but those expired in 2012 in Europe and 2017 in the United States. But changing the spec and waiting for support to percolate out to the various libraries won’t be a fast process.

(Still, no time like the present to start the wheels turning!)

2 Likes

Thanks for bringing this up.

(I’m sure somebody brought it up in 2018, and I handwaved it. But that was one whole I7 release cycle ago, so it’s probably time to think about it again…)

Let me kick it into a separate thread, though.

2 Likes

I made a new thread before seeing this post, oops! But yes, we should move discussion elsewhere.

1 Like

What I, personally, had been working on had been for current (as of its moment) development Inform, so it wouldn’t have helped 10.1 users anyway…

2 Likes

Inform 11 is comparatively very close to coming out…

Parchment now supports up to 255 styles. But it doesn’t support arbitrary CSS. Eventually I’ll also add that, but don’t wait for it.

1 Like

I remember when I implemented sound, there were 2 or 3 things that just didn’t work well. I know one of them was multiple copies of the same file.

I just checked Soundtest (an game designed by angstsmurf to test the full suite of sound capabilities). You can see the results here:
https://mathbrush.itch.io/soundtest

Sound fading in on a single track works, not sure about multiple tracks. But, this is definitely an area where I’m switching over to Parchment’s native features, so it might be worth testing those features early on Parchment games (like on Borogove) to see if they’ll need any adjustment.

This is great! With the Inform and parchment updates, Bisquixe might be rendered completely obsolete one day, which would be fantastic, because it would make all of these features more accessible and normalized.

1 Like