0 votes
by (190 points)

Hi all need help today for this little problem code from https://twinery.org/questions/25979/getting-health-and-stats-to-change-dynamically?show=25979#q25979, the code is working fine but how to limit the value changes for statusbar so it's not below 0 and not over than maximum value ? anyhelp will be appreciated. sorry for my bad english. oh here's the code btw. thx before

/*
	<<statusbar "identifier" "$maximum" "$current" "$changer">>

	identifier - The UNIQUE identifier used as the HTML element ID of this bar.
	$maximum   - The story variable that contains the maximum value this bar can be.
	$current   - The story variable than contains the current value of this bar.
	$changer   - The story variable this bar monitors to determine how much the current value should change by.

	eg. <<statusbar "sanity-bar" "$totalSanity" "$sanity" "$abuse">>
*/
Macro.add('statusbar', {
	skipArgs : false,
	handler  : function () {
		/* Check if correct parameters were passed. */
		if (this.args.length < 4) {
			return this.error('not enough parameters specified');
		}
		/* TODO: Validate each of the four parameters being passed to the macro. */

		var identifier  = this.args[0],
			maximum     = State.getVar(this.args[1]),
			current     = State.getVar(this.args[2]),
			change      = State.getVar(this.args[3]),
			barWidth    = (current / maximum) * 100,
			changeWidth = 0,
			delayReset  = false;

		/* Generate the HTML structure. */
		const $parent = jQuery(document.createElement('div'));
		$parent
			.addClass("status-bar")
			.attr({
				'id': identifier,
				'data-maximum': maximum,
				'data-current': current
			});

		const $bar = jQuery(document.createElement('div'));
		$bar
			.addClass("bar")
			.css('width', barWidth + "%")
			.appendTo($parent);

		const $change = jQuery(document.createElement('div'));
		$change
			.addClass("change")
			.css('width', changeWidth + "%")
			.appendTo($bar);

		$parent.appendTo(this.output);

		/* Handle any required change to the current value. */
		if (change != 0) {
			changeWidth = (change / current) * 100;
			current     -= change;
			barWidth    = (current / maximum) * 100;

			State.setVar(this.args[2], current);
			State.setVar(this.args[3], 0);
			delayReset = true;
		}

		/* Apply the change visual effect if needed. */
		if (delayReset) {
			setTimeout(function(){
				$change.css({'width': '0'});
				$bar.css('width', barWidth + "%");
			}, 500);
		}
	}
});

 

2 Answers

0 votes
by (190 points)
 
Best answer

Thx for @HiEv for his great method even it's not works properly , it make me have an idea how to make it works. like code below  

change this line

current     = State.getVar(this.args[2]),

into this

xstat       = State.getVar(this.args[2]),
current     = Math.clamp(xstat, 0, maximum),

 

and change this line

current -= change;

into this

current 	= Math.clamp((current - change), 0, maximum);

 

0 votes
by (44.7k points)

Change this line:

change      = State.getVar(this.args[3]),

to this:

change      = State.variables[this.args[3]].clamp(0, maximum),

and this line:

current     -= change;

to this:

current     = (current - change).clamp(0, maximum);

That should make sure that the "current" value is always from 0 to "maximum".  See the .clamp() method for details.

Also, as shown above, you can use "State.variables[...]" any time you would use "State.getVar(...)".  It's slightly better because it gets the value directly, instead of through a function.  (see State.variables)

Hope that helps!  :-)

by (190 points)
Hi HiEv, thx for the quick answer. but i got this when add your code

Error: cannot execute macro <<statusbar>>: Cannot read property 'clamp' of undefined
by (44.7k points)

That should only happen if the variable before the .clamp() is not a number.

Speaking which, the first line I recommended to change above was the wrong one.  Instead of the "change" line it should have been this one:

current     = State.getVar(this.args[2]),

and change it to:

current     = State.variables[this.args[2]].clamp(0, maximum),

However, if "this.args[2]" isn't a valid story variable name, then you'll get that "undefined" error.

You can do something like this if you want to make sure that the variable exists:

if (hasOwnProperty.call(State.variables, this.args[2])) {
	current = State.variables[this.args[2]].clamp(0, maximum);
} else {
	throw new Error("Invalid 'current' variable name of '" + this.args[2] + "'.");
}

That will only set "current" variable if the variable name passed as a parameter is valid, otherwise it will throw an error.  You can use something like that to check the other parameters as well.

Hopefully that should do the trick.

by (159k points)

@HiEv

The names of the related variables are being passed into the <<statusbar>> so that the macro can modify the values of those variables.

<<statusbar "sanity-bar" "$totalSanity" "$sanity" "$abuse">>

This means that the values of this.args 1 to 3 will contain a $ (dollar sign) at the start of them.

this.args[1]	=> "$totalSanity"
this.args[2]	=> "$sanity"
this.args[3]	=> "$abuse"

Which in turn means that State.getVar(argument) and State.variables[arguement] don't equate to the same thing.

State.getVar(this.args[2])
=> State.getVar("$sanity")
=> Current value of $sanity

State.variables[this.args[2]]
=> State.variables["$sanity"]
=> Unknown key.

 

by (190 points)

@HiEv

it's still same error, but i managed to fix this using "correct" clamp method

change this line

current     = State.getVar(this.args[2]),

into this

xstat       = State.getVar(this.args[2]),
current     = Math.clamp(xstat, 0, maximum),

 

and change this line

current -= change;

into this

current 	= Math.clamp((current - change), 0, maximum);

 

it's works fine in my end, but i dont know it will cause another problem in the future if using this code because of bug ? tell me what you think ? thx before

by (44.7k points)
As long as the value that Math.clamp() is working on is a number then it should be fine.
...