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"> $right ' + (text: 100-$percent) + '% </div>')
(set: _bar to it + '<div class="statLeftBg" style="width:'+(text: $percent)+'%"> </div>')
(set: _bar to it + '<div> $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")
}
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
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"> $args[1] <<= 100-$args[2]>>% </div>
<<= '<div class="statLeftBg" style="width:'+$args[2]+'%"> </div>'>>
<div> $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>>
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!
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"> $args[1] <<= 100 - _args2>>% </div>
<div class="statLeftBg" @style="'width:' + _args2 + '%'"> </div>
<div> $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))
}
})
Thank you, itâs working flawlessly now! I appreciate your help!
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.
Hi,
First off, I just wanna say thank you SO much for posting this. Iâve just started coding my own game recently, and could find little to nothing about opposing stats. Then, when I did none of it worked, your post was the only thing that helped me create what I wanted.
Iâve coded everything that youâve written and double checked it. But, for some reason my stats just wonât move. I donât know what Iâm doing wrong, and would GREATLY appreciate it if you could help me
Without seeing the code, my first guess would be that youâre still on the same passage? Stuff in Twine doesnât auto-update like a spreadsheet, so they wonât change until you go to a new page to re-display them.
As explained by @JoshGrams, we need an example of how your using the previous described âfair_plusâ technique, and more importantly knowing âwhereâ youâre displaying the âstatsâ.
However you donât necessarily need to do a Passage Transition to update a section of the page, you can use one of the DOM Macros (like <<replace>>
) to do that to any area youâve previously identified using markup or a HTML element. However you may need to delay the updating of the current page if the identified area youâre dynamically updating hasnât been added to the pageâs DOM yet.
The following is a very basic example of how to use the <<replace>>
macro to dynamically update an area of the page that was assigned an ID of number using Custom Style markup.
<<set $number to 5>>
number: @@#number;$number@@
<<link "Increase Number by 5">>
<<set $number to $number + 5>>
<<replace "#number">>$number<</replace>>
<</link>>
note: In the above example a <<link>>
macro is used to delay the execution of the < macro until after the identified area has been added to the pageâs DOM, however there are other techniques that can also be used depending on the exact requirements.
Hello!! Thank you so much for sharing this with us, when I came upon your post. I immedietly tried to copy paste everything.
Unfortunately, I got a <<opposite_stat>> macro doesânt exist error.
Iâm fairly new twine so I guess Iâm missing something, maybe something related to the widgets?
Update: Itâs alright. I got it : )