0 votes
by (210 points)

So this should hopefully be a fairly simple question with a fairly simple answer. I am working on a character customization section of my game and I have a textbox for height in inches. I have something immediately after this which converts this to feet/inches. I want to be able to hit enter after putting something in the textbox and have it refresh either the page, or that section of code. The latter would be much preferred as it won't reposition the player's view. Here is a piece of the code I am talking about.

height - <<textbox "$pc.height" $pc.height charCreation4>> inches or <<print Math.trunc($pc.height / 12)>>&#x2032;<<print $pc.height % 12>>&#x2033;

I appologize if this is hard to read as I am still working on making good whitespace and I find it difficult to incorporate it in sections which appear as only one line to the player. Thank you in advance.

1 Answer

0 votes
by (159k points)
selected by
 
Best answer

Unfortunately the effect you're asking for isn't as simple as it sounds.

You will need to use JavaScript to both monitor the text-box's internal value and to update the displaying of your "feet & inches" output.

1. Place the following JavaScript within your project's Story Javascript area.

It defines a new setup.feetAndInches(value) function which you can use to generate a "feet & inches" related String based on the String value supplied to it, and it checks if the supplied value is a valid numerical String.

setup.feetAndInches = function (value) {

	var total = value;

	/* Try to convert the String value supplied into a number. */
	if (typeof total === 'string') {
		total = new Number(total.trim()).valueOf();
	}

	/* If that total isn't a number or equals zero then display zeros. */
	if (Number.isNaN(total) || total === 0) {
		return '0&#x2032;0&#x2033;';
	}

	/* Otherwise calculate the feet and inches then generate the output to display. */
	var feet = Math.trunc(total / 12);
	var inches = total % 12;
	return feet + '&#x2032;' + inches + '&#x2033;';
};


2. Define the area in your Passage you want the "feet and inches" to be displayed.

The following uses Custom Styles to define an HTML element with an ID of feet into which the String value generated in point 1 will be shown.

height - <<textbox "$pc.height" $pc.height>> inches or @@#feet;<<= setup.feetAndInches($pc.height)>>@@


3. Monitor the text-box and update the feet area as required.

The following locates the HTML element that represent the text-box (1) and monitors for both the event of the end-user pressing the ENTER key as well as the event of them moving focus away from the text-box. When either event occurs the code finds the feet area and updates it. This code is added to the same Passage as point 2.

<<script>>
	/* Handle validation of the PC's height. */
	$(document).one(':passagerender', function (ev) {
		$(ev.content)
			.find('#textbox-pcheight')
			.on('change.feet-and-inches', function () {
				$('#feet').html(setup.feetAndInches(this.value));
			})
			.on('keypress.feet-and-inches', function (ev) {
				// If Return/Enter is pressed then update the ddisplayed feet & inches.
				if (ev.which === 13) { // 13 is Return/Enter
					$('#feet').html(setup.feetAndInches(this.value));
				}
			});
	});
<</script>>

(1) the ID of the HTML element is textbox-pcheight which is the word 'textbox' appended to the start of the story variable name after it has been compressed.

by (210 points)

Thank you very much for the answer, everything works perfectly, but there is one small issue that is entirely my fault. I forgot about a segment of code I have that is related to the height variable in that passage.

<<if $pc.height < 56>>
	<<set $pc.height to 56>>
<<elseif $pc.height > 84>>
	<<set $pc.height to 84>>
<</if>>

This is just a simple limiter i made for the height so that people dont make 20' character or 1" characters. I notice that now with the new code it does not work. How can I change the program for it to work?

by (159k points)

Your new example code would never of changed the value entered by the user because it would of been executed before they have the opportunity to enter a value.

1. Replace the JavaScript in my previous point 1 with the following.

It defines an extra setup.toNumber(value, min, max) function which can be used to convert the value entered by the end-user into a number within a min / max range. It also changes the existing setup.feetAndInches(height) function to assume that the value passed to it has already been converted to a number.

setup.toNumber = function (value, min, max) {

	var result = new Number(value.trim()).valueOf();
	if (Number.isNaN(result)) {
		result = 0;
	}
	return Math.max(Math.min(result,max), min);
};

setup.feetAndInches = function (height) {

	/* If the height equals zero then display zeros. */
	if (height === 0) {
		return "0&#x2032;0&#x2033;";
	}

	/* Otherwise calculate the feet and inches then generate the String to be displayed. */
	var feet = Math.trunc(height / 12);
	var inches = height % 12;
	return feet + "&#x2032;" + inches + "&#x2033;";
};


2. My previous point 2 stays the same, but make sure that the $pc.height property has previously been initialised to a value somewhere between 56 & 84 (inclusive) before it is passed as the defaultValue to the text-box 

3. Replace the TwineScript / JavaScript in my previous point 3 with the following.

It converts the text-box into one that supports numbers (on supporting web-browsers) and assigns min & max values which will limit the scrolling up/down feature of the numeric input (1). It has also been change to use the new function from the new point 1 and importantly it now updates the related story variable which is necessary due to the fact that the setup.toNumber(value, min, max) function can alter the value that the end-user entered.

<<script>>
	/* Handle validation of the PC's height. */
	$(document).one(':passagerender', function (ev) {
		$(ev.content)
			.find('#textbox-pcheight')
			.attr({
				'type' : 'number',
				'min'  : 56,
				'max'  : 84
			})
			.on('change.feet-and-inches', function () {
				var height = setup.toNumber(this.value, 56, 84);
				this.value = height;
				State.setVar("$pc.height", height);
				$('#feet').html(setup.feetAndInches(height));
			})
			.on('keypress.feet-and-inches', function (ev) {
				// If Return/Enter is pressed then update the ddisplayed feet & inches.
				if (ev.which === 13) { // 13 is Return/Enter
					var height = setup.toNumber(this.value, 56, 84);
					this.value = height;
					State.setVar("$pc.height", height);
					$('#feet').html(setup.feetAndInches(height));
				}
			});
	});
<</script>>

 

by (210 points)
The Code is not working due to this error.

Error: Uncaught TypeError: State.setVar is not a function.

Any idea as to why this would be the case?
by (210 points)
Sorry, i realized the issue. The solution you gave me would not work in Sugarcube 2.21 as the State.setVar function did not exist in that version. I updated to the latest Sugarcube and now it works perfectly. Thank you once again.
...