Choicescript-Like Stats Screen And Opposed Pair Stats In Twine

Adapting some of my old Choicescript projects to Twine. How would I go about setting opposed pair stats like in Choicescript, or a functional equivalent? As an example, one of my stat pairs is Limelight/Shadows, and I want to be able to have it measured and displayed as a percentage where one end falls when the other rises. Also, how would I set up a permanent sidebar link to a menu where stats are displayed?

You may want to consider using SugarCube for your new project, as it comes with both an extendable menu area (the StoryMenu special passage) in its sidebar, as well as a extendable Dialog system you could use to display the stats without causing the end-user to navigate away from the ‘current’ Passage.

SugarCube is also (in some ways) easier to extend. But you can do it in Harlowe, it’s just a little more clunky.

Story Stylesheet
.statBar {
  width: 20em;  /* Overall stat-bar width (in 'm' characters) */
  background: #fb5;  /* Right-side background color */
  color: #111;  /* Text color */
  font-weight: bold;
  margin: 0.5ex auto;
  position: relative;
}

.statBar div {
  position: relative;
  z-index: 1;
}

.statBar .statLeftBg {
  position: absolute;
  top: 0px;
  left: 0px;
  background: #1a5;  /* Left-side background color */
  z-index: 0;
}
Opposed Stat passage (displays the stat bar)
{
(set: _bar to '<div class="statBar">')
(set: _bar to it + '<div style="float:right">&nbsp;$right ' + (text: 100-$percent) + '%&nbsp;</div>')
(set: _bar to it + '<div class="statLeftBg" style="width:'+(text: $percent)+'%">&nbsp;</div>')
(set: _bar to it + '<div>&nbsp;$left $percent%</div>')
(set: _bar to it + '</div>')
_bar
}
%+ passage
{
	(set: _end to 50 + 50*(sign: $change))
	(set: $percent to it + (abs: $change)/100 * (_end - $percent))
}
%- passage (optional: unlike ChoiceScript, my %+ is ok with negative changes)
(set: $percent to -it)(display: "%+")
Using the above code
{
	<!-- Initialize a stat. -->
	(set: $Limelight to 50)

	<!-- Change a stat (note that it's ok to use negative values with %+) -->
	(set: $percent to $Limelight)(set: $change to -30)
	(display: "%+")
	(set: $Limelight to $percent)

	<!-- Display a stat bar -->
	(set: $left to "Limelight")(set: $right to "Shadows")(set: $percent to $Limelight)
	(display: "Opposed Stat")
}
1 Like

I wrote a Twine game in 2016 called “Stuff and Nonsense” which had a basic stat screen (more inventory than stats) which displays on the side (and players could also click on a link to get more info on what the stats meant). There was a huge amount of code making that work, and I reckon I’ll stick to ChoiceScript for that from now on—ChoiceScript is a little harder to learn but has lots of really excellent functionality, especially with stats, especially opposed stats.

May I ask why you want it in Twine?

I can send you the chunks of code from “Stuff and Nonsense” if that’s helpful for you.

https://philome.la/FBanksBooks/stuff-and-nonsense/play/index.html

2 Likes

The Twine Cookbook also includes two Harlowe based recipes for showing dynamic content within the left margin of the story area, the same area of the page that Harlowe’s own <tw-sidebar> (header) element is re-positioned to. note: both recipes support the 3.x series of Harlowe.
“Left Sidebar”: Harlowe (both v1.x and v2.x series)
“Left Sidebar”: Harlowe (only v2.1.0 or later)

note: My early post was not mean to imply that such functionality could not be achieve in Harlowe, just that it may not be the best story format to use to do such. Especially if you also plan to use Array or Data-Map for storage of information, because Harlowe implements its own variations of those objects and those implementations are quite inefficient when it comes to things like looping / contents manipulation / element accessing / etc…

Yeah, if you’re not already too attached to Harlowe…here’s the same code for SugarCube:

Story Stylesheet (same as Harlowe)
.statBar {
  width: 20em;  /* Overall stat-bar width (in 'm' characters) */
  background: #fb5;  /* Right-side background color */
  color: #111;  /* Text color */
  font-weight: bold;
  margin: 0.5ex auto;
  position: relative;
}

.statBar div {
  position: relative;
  z-index: 1;
}

.statBar .statLeftBg {
  position: absolute;
  top: 0px;
  left: 0px;
  background: #1a5;  /* Left-side background color */
  z-index: 0;
}
Story JavaScript (defines <<fair_plus>>)
// <<fair_plus "$stat" _change>>
Macro.add('fair_plus', {
	handler: function() {
		var percent = State.getVar(this.args[0]), change = this.args[1]
		var end = 50 + 50 * Math.sign(change)
		percent += Math.abs(change)/100 * (end - percent)
		State.setVar(this.args[0], percent)
	}
})
passage with widget tag (defines <<opposed_stat>>, <<fair_minus>>)
<<widget "fair_minus">><<fair_plus $args[0] -$args[1]>><</widget>>

<<widget "opposed_stat">><<nobr>>
<div class="statBar">
	<div style="float:right">&nbsp;$args[1] <<= 100-$args[2]>>%&nbsp;</div>
	<<= '<div class="statLeftBg" style="width:'+$args[2]+'%">&nbsp;</div>'>>
	<div>&nbsp;$args[0] $args[2]%</div>
</div>
<</nobr>><</widget>>
<!-- Initialize a stat. -->
<<set $Limelight to 50>>
​
<!-- Change a stat (note that negative values are ok) -->
<<fair_plus "$Limelight" -30>>
​
<!-- Display a stat bar -->
<<opposed_stat "Limelight" "Shadows" $Limelight>>
1 Like

This is over a year later, but your fairmath code is so elegant, @JoshGrams, I was so happy to have found it ! Can I ask if there’s any easy way to have the fairmath calculation round up to the nearest percent? Using something like <<fair_plus "$courage" +3>> results in funny percentages like 7.85% courage! No worries if not, though! :slight_smile:

Off the top of my head, I’d probably keep the full accuracy in the variable and round it only for display, so throw a Math.round() around the $args[2] and 100-$args[2]? Let me know if you can’t get that to work and I’ll look at it more closely…

Hm, I’m still getting percentages after adding Math.round around what you indicated, but no rush! Thanks for your time!

I believe Josh had something like the following in mind:

<<widget "opposed_stat">><<nobr>>
<div class="statBar"><<set _args2 to Math.round($args[2])>>
	<div style="float:right">&nbsp;$args[1] <<= 100 - _args2>>%&nbsp;</div>
	<div class="statLeftBg" @style="'width:' + _args2 + '%'">&nbsp;</div>
	<div>&nbsp;$args[0] _args2%</div>
</div>
<</nobr>><</widget>>

Yes, that’s pretty much what I tried–I’m still getting decimal percentages, but I just realized that this widget only applies to opposed stats, and not all fairmath used?

Oh. Never mind, it looks like ChoiceScript actually does convert it back to an integer. I was worried what that would do to small changes near the ends, but maybe they just have no effect in extreme cases. Yeah. Then you can round it off in fair_plus:

// <<fair_plus "$stat" _change>>
Macro.add('fair_plus', {
	handler: function() {
		var percent = State.getVar(this.args[0]), change = this.args[1]
		var end = 50 + 50 * Math.sign(change)
		percent += Math.abs(change)/100 * (end - percent)
		State.setVar(this.args[0], Math.round(percent))
	}
})
1 Like

Thank you, it’s working flawlessly now! I appreciate your help! :slight_smile:

Hey! I just started out in twine (Sugarcube 2) and got normal stat bars down thanks to Chapel’s stuff, but I tried to use this for opposed stats and when I do like <<fair_plus “$mischief” +5>> it makes the stat bar go like 500% Michieveous and -400% Rational (like I showed in the picture). Any idea what I could be doing wrong?

Oh, that’s very odd. I might need to see the code for that one: sent you a PM.