Devblog:

edit: see further discussion here

In my previous post I talked a little bit about skills and skillgain. Today I'm going to take a more in-depth look.

Games typically start out rewarding quite liberally at the outset, and then when you're nice and hooked they start to wean you off those rewards bit by bit. A perfect example would be any game based on the d20 system that Dungeons & Dragons uses: Your first level requires 300 xp, the second requires 900 xp, the 3rd level requires 2700, and so on and so forth. At the beginning, you get lots of shiny new feats and skill points to spend quite frequently, and then the pace at which you advance slows down progressively over the course of the game. The theory is that by the time you're nearing the 19th level and needing more than 300 000 experience points to level up, your character is not only more capable and can tackle tougher enemies and quests worth more experience, but you the player are more invested in your character. It feeds into the reward-seeking psychology that most of us have. Just one more level! You can do it!

We have the same in UO, too. In OSI's system the sum total of your skill points is weighted against your pace of advancement to moderately slow you down. Exponential skillgain looks something like this:

Exponential Skillgain

That's the graph produced using gnuplot of y = -log( x/1400 ) * 0.2 where y represents the character's chance to gain a skillpoint as a percentage, x represents that character's current skill in tenths of a point (because this is how RunUO represents skills internally), and where log() is the natural logarithm. The factors 1400 and 0.2 are just empirically-determined factors to make the curve look the way I want. You can see from the curve that at low skill levels, your chance to gain is very very high, and that it decreases exponentially from there. For example, at 60 skill (600 on the chart), your chance to gain a skillpoint is about 20%.

It's pretty straightforward, and it allows us to be flexible. Say we want the system to be able to scale a player's gain rate based on in-game factors automatically. We might want faster skill gains in a dungeon (as I've talked about previously), and we might want slower skill gains elsewhere for whatever reason. Well, it's easy to just apply a linear shift, like so:

Linear curve shifts

Three curves, all the same function except the red one has been shifted upwards by 0.5 and the green one has been shifted downwards by having 0.5 subtracted from it. With a little imagination we can see how effective this is for situations where you want to automatically retard or accelerate skillgain rates. Power Hour? Add a few percent. Slow-gain zone of some kind? Subtract a few percent. This ties in really well with the ideas presented in a previous post about having very fast gains in dungeons and slow gains in houses to discourage everyone from just afk botting.

Some of you might have already thought of the fact that this code needs to run every time anyone on the server swings a weapon, shoots a bow, steals something, hides, casts a spell, or crafts an item. In other words, it needs to be fast. Lightning-fast. Now, the natural logarithm isn't the speediest of calculations, but it's likely architecture-dependent. I'm not an expert in low-level CPU architecture, and I haven't done any benchmarking to see if log(x) is faster or slower than other functions, but I do know that modern CPUs are designed to do multiplication and other basic arithmetic very very fast. So let's see if we can find a function that's shaped like log(x) but is a little cheaper computationally-speaking. What might we try?

I can't imagine C# has a native erfc() function though, so we can try a polynomial approximation to it also.

Those curves are overlaid here:

Approximations

The blue curve is erfc(), the Complementary Error Function. The red curve is the Cosine function, the purple curve is the natural logarithm, and the green curve is the polynomial approximation to erfc(). None of them are exactly equal to log(x) but all are pretty close. The closest two are obviously the blue and green curves, which again are erfc() and the polynomial that approximates it is probably the best choice. After all, it's not necessary that the curve actually be exponential, only that it displays roughly exponentially-decreasing behaviour. I furthermore like that the polynomial curve is higher and isn't quite as steep in the low-skill areas which will make things more enticing for newbies as they'll see dramatic gains early on.

Finally, we come to the idea that not every action is the same as another. Using Anatomy or EvalInt on yourself is not the same as using it on someone else. Maybe we want using Eval on yourself to be quite easy, since you already know roughly how smart you are. Maybe we want mages to be very hard to Eval since Mind Blast means they have a very real desire to keep their Intelligence scores to themselves. Well to do that we can also introduce a variable that represents the chance to succeed, which will act as a mitigating factor to the skillgain calculation.

It looks something like this:

Varying chances to succeed a task

Each coloured curve represents a different difficulty. The lowest curve represents a 90% chance to succeed, which is to say the task is very easy. An example might be a grandmaster shieldfighter parrying blows from a newbie macefighter. Just as in real life, the veteran should not expect to gain much valuable practice from fighting a newbie. As the newbie's skill grows, the veteran's chance to parry goes down. We might be on the 50% curve now, and you can see that the veteran has an increased chance to gain skill as he is fighting a more difficult opponent.

This adds a bit of strategy to things as you will obviously see the most rapid gains from more difficult tasks, but at the same time you will succeed less if your task is difficult. Keeps us on our toes. It's incumbent on the player to find the optimal balance.

More to come, thanks for reading!