Skills, and reading the client to fake its effects

Combat works, so the next thing is skills. Not the numbers, the server already does those. I mean what a skill looks like: the glow while you charge it, the bolt that flies out, the burst when it lands. This turned out to be one of the hardest things so far, and most of it is still rough.

Power Strike hitting the stone golem in the skill test bench

The effects come from the client, and the client hides how they work

Every L2 skill effect is stored in the original client, in a big file of particle “recipes”. Each recipe is a list of emitters with numbers on them: how many particles, what texture, what colour, how they fade, how they move. I can read those numbers out. The problem is the numbers on their own do not tell you what they mean. What does a colour of (20, 20, 1) actually draw? How is a texture with 16 little tiles in it played back? What does “translucent” mean exactly?

The values live in the data, but the maths that turns them into pixels is native code inside the client’s engine. So to get it right I had to go into that engine code and read it. It is a 30 MB binary from 2007, and luckily it still has its function names, so I could find the particle functions and read what they really do, instruction by instruction.

That paid off, because almost everything I had guessed was wrong. Two examples:

  • “Translucent”, which two thirds of all emitters use, is not additive blending like I assumed. It is a screen blend. Drawing it as additive is exactly why my first effects looked like blown out white blobs instead of soft L2 glows.
  • The colour maths is old 8 bit integer maths, and a multiplier like (20, 20, 1) does not make a bright yellow. It overflows and wraps around, and the real result is a near white. I had a bug where hits flashed an ugly orange yellow, and this was why.

You do not get these from the data. You only get them by reading the client.

Godot’s particle system could not do it

My plan was to use Godot’s built in GPU particles and just feed them the L2 numbers. That did not work. The L2 model and Godot’s model are different in enough small ways, the blend modes, the way sprites are turned into little animated flipbooks, the way a mesh is drawn unlit and two sided, that I kept fighting the engine instead of using it.

In the end I gave up on the built in system and wrote my own. The whole particle simulation now runs in my own C# code, one emitter at a time, exactly the way the client does it, and draws through a couple of custom shaders I wrote for the blend modes. It is more work but it is the only way it matches. Basically the entire skill effect layer is hand written C#.

A room to build them in, one by one

There are 1265 skills I can show, and so far only 49 of them have their effect ported over. So I built a small offline room to work through them. No server, no login, just my character, a stone golem to hit, and a dropdown of every skill. I pick one, press a key, and it plays the animation, the sound and whatever effect exists, so I can fix it and try again in a second instead of logging a whole client in.

The picture at the top is that room. It is where the rest of this happens.

Wind Strike, which is very wrong

To be honest about where this is: I tried Wind Strike, the basic mage attack, and it is a good example of how early this all is. The skill itself runs. The recipe names four effect classes, one for the cast, one dropped in front of the caster, one for the flying bolt, and one for the hit:

"1177": {
  "ca": "m_u000_a",
  "cs": "m_u000_b",
  "fl": "m_u000_c",
  "ta": "m_u000_d"
}

The timing is right too. The client casts it over four seconds, the shot leaves at 3.6s, and the bolt flies the last stretch to the target in 0.4s. My bench plays exactly that sequence, the mage does the cast animation, the bolt is launched on the right frame.

The trouble is you can barely see any of it. The glow, the bolt, the impact are all either missing or so wrong they do not read as anything. So the shape is correct and almost nothing about the look is. That is the state of most of these right now, and getting them from “the timing is right” to “it looks like Lineage 2” is going to take a long time and a lot of passes.

Where this leaves me

So skills animate, they are timed from the real client, and the effect system is my own code built to match the client’s maths. A handful look right, one of them is in the picture. Most, like Wind Strike, are still a long way off. The next while is just grinding through them in that room, one skill at a time.