0 votes
asked by (190 points)

As the player moves through the game, I want the game to take a weighted number associated with each passage they click on and recalculate it. Below would be an example of the math used in JS.

function weightedAverage(v1, w1, v2, w2) {
  if (w1 === 0) return v2;
  if (w2 === 0) return v1;
  return ((v1 * w1) + (v2 * w2)) / (w1 + w2);
}

The problem I forsee is that with this code example I would have to define each passage with variables v1, v2, v3, etc. which could get quite unwieldy if I have hundreds of passages.

Is there a simpler method that would update and recalculate every time the player clicked on a passage? Would I use temporary variables? An array? 

Apologies in advance; I'm very new to coding and despite my best efforts I can't figure out where to start.

 

1 Answer

0 votes
answered ago by (30.7k points)
edited ago by

I tried combining the above information with the information you posted in the /r/twinegames Reddit, and also this information on calculating a weighted center, and I think I figured out what you're trying to do.

First, you need to initialize your values in your "StoryInit" passage like this:

<<set $xSums = 0>>
<<set $xWeights = 0>>
<<set $xMean = 0>>
<<set $ySums = 0>>
<<set $yWeights = 0>>
<<set $yMean = 0>>

Next you'd need to add a passage, give it "widget" and "nobr" tags, and put something like this code in it:

<<widget "wlink">>
/* <<wlink>> weighted link widget. */
/* Usage format: <<wlink "Link text" "Passage name" X XWeight Y YWeight>> */
/* Example: <<wlink "Go south" "Round room" 4 10 -3 8>> */
	<<set _linkText = $args[0]>>
	<<set _passageName = $args[1]>>
	<<set _xSums = $xSums + ($args[2] * $args[3])>>
	<<set _xWeights = $xWeights + $args[3]>>
	<<set _xMean = _xSums / _xWeights>>
	<<set _ySums = $ySums + ($args[4] * $args[5])>>
	<<set _yWeights = $yWeights + $args[5]>>
	<<set _yMean = _ySums / _yWeights>>

	<<if (_xMean < 0) || (_yMean < 0)>>
		/* This is an example where if either X or Y will be less than zero */
		/* if you click this link, then it takes you a different passage. */
		<<set _passageName = "Maze exit">>
	<</if>>

	<<capture _xSums, _xWeights, _xMean, _ySums, _yWeights, _yMean>>
		<<link _linkText _passageName>>
			<<set $xSums = _xSums>>
			<<set $xWeights = _xWeights>>
			<<set $xMean = _xMean>>
			<<set $ySums = _ySums>>
			<<set $yWeights = _yWeights>>
			<<set $yMean = _yMean>>
		<</link>>
	<</capture>>
<</widget>>

You'll need to modify that middle chunk of code to work the way you want, but this should make it so that any time you want to link to another passage, you would use the <<wlink>> widget to set the link text, passage name, X, X weight, Y, and Y weight which would be used to evaluate the new X and Y mean values and take you to a different passage, if needed, depending on their values.  See the SugarCube <<widget>> macro documentation for details on how widgets work.

If the values and weights for each passage you're going to will always be the same, regardless of where you came from, then you could create a setup object in the "StoryInit" passage which stores the values for each passage you'd go to like this:

<<set setup.passages = {}>> /* Initialize object */
<<set setup.passages["Round room"]   = {x:  4, xWeight: 10, y: -3, yWeight:  8}>>
<<set setup.passages["Machine room"] = {x:  7, xWeight:  5, y:  4, yWeight: 10}>>
etc...

And then you would modify the widget like this:

<<widget "wlink">>
/* <<wlink>> weighted link widget. */
/* Usage format: <<wlink "Link text" "Passage name">> */
/* Example: <<wlink "Go south" "Round room">> */
	<<set _linkText = $args[0]>>
	<<set _passageName = $args[1]>>
	<<set _passage = setup.passages[_passageName]>>
	<<set _xSums = $xSums + (_passage.x * _passage.xWeight)>>
	<<set _xWeights = $xWeights + _passage.xWeight>>
	<<set _xMean = _xSums / _xWeights>>
	<<set _ySums = $ySums + (_passage.y * _passage.yWeight)>>
	<<set _yWeights = $yWeights + _passage.yWeight>>
	<<set _yMean = _ySums / _yWeights>>

	<<if (_xMean < 0) || (_yMean < 0)>>
		/* This is an example where if either X or Y will be less than zero */
		/* if you click this link, then it takes you a different passage. */
		<<set _passageName = "Maze exit">>
	<</if>>

	<<capture _xSums, _xWeights, _xMean, _ySums, _yWeights, _yMean>>
		<<link _linkText _passageName>>
			<<set $xSums = _xSums>>
			<<set $xWeights = _xWeights>>
			<<set $xMean = _xMean>>
			<<set $ySums = _ySums>>
			<<set $yWeights = _yWeights>>
			<<set $yMean = _yMean>>
		<</link>>
	<</capture>>
<</widget>>

This way you only need to pass the link text and default passage name to the <<wlink>> widget, and it will do the rest.

NOTE: The above widgets do no validation of the parameters ($args[N]) passed to them.  So if you, for example, forget a parameter or use a string where it expects a number, then you may get errors or unexpected results with that widget.  See the usage and example comments within each widget's code.

Hopefully that answers your question.  If not, then you'll need to explain more clearly exactly what it is that you're trying to accomplish here.

commented ago by (190 points)
Wow. I really appreciate your feedback -- I was working on a solution on my own and I think I would have been stuck in code limbo for weeks.

I will try to mess around with this and let you know what I find. Thank you!
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.
...