0 votes
by (460 points)
retagged by


I'm making a Xp system, and it is rules by this widget :

<<if $Observant is 60 and $Xpobs is 5>><<set $Observant++>>
<<elseif $Observant is 61 and $Xpobs is 11>><<set $Observant++>>
<<elseif $Observant is 61 and $Xpobs is 18>><<set $Observant++>>
<<elseif $Observant is 62 and $Xpobs is 26>><<set $Observant++>>
<<elseif $Observant is 63 and $Xpobs is 35>><<set $Observant++>>
<<elseif $Observant is 64 and $Xpobs is 45>><<set $Observant++>>
<<elseif $Observant is 65 and $Xpobs is 56>><<set $Observant++>>
<<elseif $Observant is 66 and $Xpobs is 68>><<set $Observant++>>
<<elseif $Observant is 67 and $Xpobs is 81>><<set $Observant++>>
<<elseif $Observant is 68 and $Xpobs is 95>><<set $Observant++>>
<<elseif $Observant is 69 and $Xpobs is 110>><<set $Observant++>>
<<elseif $Observant is 70 and $Xpobs is 126>><<set $Observant++>><</if>>

I know that I can have only one $Observant++ with "or". Just do it to be readable.

My question is : is there a way to make it smoother? The increment is 5/6/7/8/9....

5 Answers

+1 vote
by (159k points)
selected by
Best answer

As a thought exercise I decided to solve the problem using a different process to @idling, and the resulting solution uses one less story variable (thus slightly reducing the overall History) at the cost of the added the need to call a custom Javascript function.

NOTES: I am going make a couple of assumptions:
1. That the test for $Observant is 61 appearing twice in your example is an error.
2. That $Xpobs equals 0 (zero) when $Observant equals 59 (fifty-nine).

The value for $Observant and the 'difference' of $Xpobs both increase in a linear fashion which means that you can use maths (like that shown in the 'A tougher sequence' section of this page) to generate an quadratic polynomial function which can be used to determine the associated value of $Xpobs for each $Observant.

Breaking down your values for $Observant and $Xpobs gives a table like the following:

n              59    60    61    62    63    64    65
f(n)           0     5	   11    18    26    35    45
diff              5     6     7     8     9     10
diff of diff         1     1     1     1     1

Which after a little consulting of the runes (and some simultaneous equation solving) we end up with the following algorithm:

$Xpobs = ($Observant² * 0.5) + ($Observant * -54.5) + 1475

The above can be turned into a usable function by adding Javascript code like the following to your project's Story Javascript area, it defines a setup.calcXpobs() function which you can use to calculate the Xpobs for a specific Observant.

setup.calcXpobs = function (observant) {
	return (Math.pow(observant, 2) * 0.5) + (observant * -54.5) + 1475;

... and you can test that it produces the correct values using TwineScript like the following within a Passage.

	<<for _i to 59; _i <= 70; _i++>>
	<tr><td>_i</td><td><<= setup.calcXpobs(_i)>></td></tr>

Using the new function to determine when to increase the value of $Observant is as simple as:

<<if $Xpobs is setup.calcXpobs($Observant + 1)>><<set $Observant++>><</if>>


+1 vote
by (23.6k points)

This part:

<<elseif $Observant is 61 and $Xpobs is 11>><<set $Observant++>>
<<elseif $Observant is 61 and $Xpobs is 18>><<set $Observant++>>

Seems to break the pattern. I'll just assume that's unintentional. If it is, then you can simplify this very easily by introducing a third variable:

<<set $observant to 60>>
<<set $xpObs to 0>>
<<set $nextLvl to 5>>

Now you could just say something like:

<<if $xpObs gte $nextLvl>><<set $observant++>><<set nextLvl += (5+$observant-60)>><</if>>


+1 vote
by (44.7k points)
edited by

This code should do the trick:

<<set _base = 0, _adder = 5, _level = 60>>
<<for $Xpobs > _base>>
	<<if _base + _adder > $Xpobs>>
		<<set _base += _adder++, _level++>>
<<set $Observant = _level>>

That code basically just runs in a loop, looking to see which level the $Xpobs fits in, and then setting $Observant to that level.

You can set $Xpobs to any number and that code will set $Observant to the correct level, so you can even skip levels or lose levels if you want to.  It should be fairly easy to modify as well, if later on you decide you want to change how leveling works.

If you meant to start $Observant at level 59, instead of level 60, just change "_level = 60" to "_level = 59".

If you want to keep the current discontinuity in leveling the same, then add the following code immediately after the "<</for>>" line:

<<if $Xpobs >= 18>>
	<<set _level-->>

Hope that helps! 

0 votes
by (460 points)


Thanks to you all.

First : Yes, the third line is an error. The base stat of "observant" is 60, so we can say that id $Observant is 59, $Xpobs is 0.

Second : I totally forget the algorithm, shame on me !!, that's cool to have three differents ways to do it.

I like the javascript version. I modify the twine fonction like this to work :

<<if $Xpobs is setup.calcXpobs($Observant)>><<set $Observant++>><</if>>


0 votes
by (1.1k points)

To generalize greyelf's answer, here's how to do it with arbitrary choices of firstLevel, firstCost, and costChange.  I usually store only the experience points and compute the current level when I need it, but I've also included the nextLevelAt function which tells you when to increment your level.

setup.firstLevel = 59
setup.firstCost = 5   // Your first level-up costs this much.
setup.costChange = 1  // The cost increases this much per level.

window.currentLevel = function(xp) {
	var x = setup.firstCost / setup.costChange
	x = x - 0.5 * setup.costChange
	var level = Math.sqrt(x * x + 2 * xp / setup.costChange) - x
	return setup.firstLevel + Math.floor(level)

window.nextLevelAt = function(level) {
	level = level - setup.firstLevel
	var x = setup.firstCost + 0.5 * setup.costChange * level
	return x * (level + 1)

You can do similar things with geometric series (where the next level cost x% more instead of x points more) but it gets more complicated.