nanoMMO

MMORPG skills, cooldowns and a hotbar in C++

Four skills as content, used on the server with a level and a cooldown each, a hotbar on the number keys, animations from Mixamo, and a camera that tilts.

Watch on YouTube

We give our players skills, a thrust, a kick, a whirlwind and second wind, as content that the server decides, with a level to reach and a cooldown. The client gets a hotbar on the number keys with icons and countdowns, the characters get new animations from Mixamo, and the camera learns to tilt. This is episode 31 of a series about building an MMORPG from scratch in C++, with the focus on the engineering around the game.

The code

Each step on screen is a tag in the game's repository. The code at the end of the episode is ep031/end.

  1. 2:31ep031/step-1Tilting the cameraBrowseChanges
  2. 5:00ep031/step-2Tilting the cameraBrowseChanges
  3. 5:57ep031/step-3Your name in goldBrowseChanges
  4. 6:47ep031/step-4Skills as contentBrowseChanges
  5. 14:17ep031/step-5Skills on the serverBrowseChanges
  6. 23:53ep031/step-6A hotbarBrowseChanges
  7. 30:41ep031/step-7Where the cooldowns wentBrowseChanges
  8. 32:44ep031/step-8AnimationsBrowseChanges

Transcript

Intro

0:00 Hello and welcome back to episode thirty-one, where we're going to give our players skills, on a hotbar along the bottom of the screen, and let them tilt the camera too. Let's begin with a quick look at where we're going to be by the end of the episode.

0:14 Alice is level four, so she can use each of our four skills on her hotbar. A kick knocks a skeleton back, a whirlwind hits each one around her, second wind heals her, and a thrust lunges with the sword. And the bar counts down until each one is ready again.

0:31 On the website of the series you can find more information about the project. The code for the project is open source on GitHub, tagged at each step of each episode so that you can follow along.

Last time

0:43 Now, before we get to today's work, let's quickly go over what we did last time. Prometheus got two alerting rules, for what players would notice: a world that falls behind, and a world that's down. Each alert goes pending, and fires once its rule has held for a minute. Up at zero let go of a crashed world once Consul dropped it, so the rule for a world that's down looks back over a day instead. Alertmanager emails each alert as it fires and as it resolves, to Mailpit, a mail server in Docker, which we read with curl and jq. And Prometheus's promtool tests both rules in CI, from a file of test cases.

Today

1:22 I played the game for a while, to see what it needs next, and a few things stood out. Dragging with the right mouse button turns the camera around us, but it can't tilt it up or down, so we're stuck at the angle the zoom gives us. A white cube floats over our own player, so that we can find ourselves, which looks out of place next to the characters. And fighting is the space bar and nothing else, so today, our players get skills. They're going to be written down as content, like our items, so that adding one is an edit to a file. The server decides each one, with a level to reach before you can use it, and a cooldown before you can use it again. There's no skill tree: a player gets each skill once they reach its level. The client gets a hotbar along the bottom of the screen, on the number keys, the way a lot of MMOs still do it. Guild Wars two, for example, has a handful of skills on the keys one to five, where the left hand sits, just above W, A, S and D. And the skills get animations from Mixamo, which I've downloaded for us. All right, let's get started, with the camera.

Tilting the camera

2:32 The camera's turn comes from input dot h, which reads the keys and the mouse, so the tilt goes there too. Camera tilt is how far to tilt the camera this frame, in radians, and its comment says what the drag does: up over the player or down towards the ground, with the mouse, dragged with the right button down. In input dot c c, the tilt is zero while the mouse is over one of ImGui's windows, which capture the mouse, or while the right button is up. Otherwise it's how far the mouse moved up or down this frame, from raylib's get mouse delta, times the turn for each pixel that the camera's turn uses. So dragging down raises the camera, and it looks down on us more steeply, like in most games with a camera behind the player. In camera dot h, update takes the tilt after the turn. Its comment says so: it turns and tilts by the angles, in radians, and zooms in by the share of the distance, then looks at the player from there. The follow camera keeps its pitch next to its yaw, starting at thirty degrees, about where the camera sat before, at its default distance. Its comment says what the pitch is: how far above the player it looks down from, in radians, which is why thirty degrees is times raylib's DEG2RAD. In camera dot c c, raymath's include goes first, as it was there for the lerp between two pitches, which goes too. Until now, the camera worked the pitch out from the zoom, looking down more steeply the further it was, between twenty and fifty-five degrees. That would undo the player's tilt each time they zoom, so the zoom sets the distance alone. Update adds the tilt to the pitch, like it adds the turn to the yaw, and the camera's position comes from the pitch we keep. In main dot c c, the client passes the tilt from the mouse to the camera each frame, and the client's benchmark, which turns the camera around the island, passes a tilt of zero. And the README's controls say that dragging up and down tilts the camera.

4:30 With alice in the world, let's drag the mouse down with the right button held. The camera rises over alice, looking down on her more and more steeply, and then it goes over the top of her, and the view turns around. Now W walks alice towards us, down the screen. Nothing stops the pitch at straight down, so the drag took it past ninety degrees, where the cosine in the camera's position turns negative and puts the camera on the far side of the player. And W walks away from where the yaw says the camera is, which is behind her.

5:01 Update adds the tilt to the pitch with nothing to stop it, and the camera has two places it shouldn't go. So in camera dot c c, the pitch gets a lowest and a highest. The lowest is five degrees, just above level. The comment says why we stop at either end: past straight down, the camera would go over the top of the player and look back the other way. And near level, it looks along the ground, and any rise in between hides the player. Straight down is ninety degrees, so the highest is eighty, which looks down on the player from nearly overhead. Then update clamps the pitch, with the tilt added, between the two, with std clamp, like the zoom's distance.

5:41 Now a longer drag down stops with the camera nearly overhead, however far the mouse goes, and alice is right below us. Dragging up stops just above the ground, where the camera looks along the Meadows, past alice.

Your name in gold

5:57 While we're on what the camera shows, the white cube over our own player goes. The renderer drew it after our own figure, so that we could tell which of the players we were, and its height in scene dot h goes with it. To find ourselves instead, in nameplates dot c c, our name over our player is in gold, where the other players' names are white, and the monsters' are red. Name color picks the colour for a figure's name, and its comment says why: yours stands out from the other players', so that you can find yourself in a crowd. A monster's name stays red, ours is gold, and any other player's is white, as before. The nameplates now take the colour of each name from name color. And the README says that your name is the gold one, where it used to say that ours had a white marker over it.

Skills as content

6:48 Our skills are going to be content, like the items, monsters and NPCs from episode twenty-seven, so they get a schema of their own, skills dot proto, in the content folder. It's proto three, in a package of its own, nanommo dot skills, like the other schemas. Like an item has a weapon and a look, a skill has a part for each side. The effect comes first, and its fields are what the server reads. Cast milliseconds is how long the skill holds the player, who can't move or swing until it's done, like the sword's swing. Hit milliseconds is how far into it the skill lands, timed to when the blade or the foot gets there in the animation. Then the reach, in metres, and the damage. All says whether it hits each monster in reach, or the nearest. Push is how far it knocks a monster back, and heal is how much health it gives the player. Over the effect, its comment says what it is: what a skill does, and when, which is the server's part. The look follows, with what the players see of a skill. The icon is one of Font Awesome's, which we've had in ImGui since episode two, by its code point, the number Font Awesome gives it, which the hotbar turns into text. The clip names the characters' animation that the player moves with, and without one, they stand. And a glow is a colour around the player while they use the skill. It's optional, like a monster's tint, so that has glow can tell a skill without one from a glow whose colour is zero. Over the look, its comment says what it is: how a skill looks, the client's part. A skill has an id and a name, and like an item, it keeps its id for good, as clients and servers send skills by id. Then the level a player can use it from, and its cooldown, how long until they can use it again, with the effect and the look. And the file's message, skills, holds a list of them. Then skills dot t x t p b has our four skills, with its first two lines naming the schema, like the other files. Thrust is from level one, so that a new player has a skill to use, with a cooldown of eight seconds. It holds the player for three seconds, the length of its animation, and lands at nought point nine seconds, as the blade gets furthest. Its reach is two point four metres, as the thrust lunges forward. Its thirty-five damage is about two of the sword's swings, and its icon is Font Awesome's bullseye. Kick is from level two, with a cooldown of six seconds and a little damage, and it knocks the monster three metres back, which buys a second or two before it runs back at you. The kick lands at one point two seconds, as the foot reaches out, and its icon is a pair of shoe prints. Second wind is from level three, and heals forty, with a cooldown of thirty seconds, so that it can't keep a player alive on its own. It holds the player for a second, with a heartbeat for its icon. It has no clip, and glows green instead, half see-through, as the last two hex digits of its colour are its alpha. And whirlwind, from level four, hits each monster within two and a half metres, as the spin goes all the way around. Hitting several at once makes it the strongest, so its cooldown is the longest of the three that fight, at twelve seconds, and its icon is a tornado. Skills dot h starts with its header guard, and the headers that items dot h has: c std int and map for the table, string view for the text, and absl's status or for what goes wrong. Skills dot p b dot h is what protoc generates from our schema. The skill table holds the skills by id, in a map, so that the hotbar and the server can find a skill from the id that comes over the network. Parse skills reads the text, or says what's wrong with it. Its comment lists what that can be: a field that skills dot proto doesn't have, a skill with no id or with another's, one with no level, or one that lands after it's over. In skills dot c c, the includes are for the map we build, str cat for our errors, and our own checks dot h and text dot h from episode twenty-seven. Parse skills parses the text format into the file's message, like the other files, and gives back the error from parse text, with its line, if that fails. Then by id refuses a skill with no id, or one with another's, as it does for items. Then we go through the skills, and check what the types can't, starting with the level. The comment says why: zero is what protobuf gives a level we leave out, so a skill whose level we forgot would be everyone's, with no error. So a level of zero is an error, naming the skill. And a skill whose hit time is after its cast time is over would hold the player and do nothing, so that's an error too. Otherwise, we give back the table, with the skills moved into it. CMake builds the new schema and its file into our content library, with the generated code's warnings off, like the others. The content gets the skill table, next to the items, monsters and NPCs, and read content fills it from skills dot t x t p b, after the NPCs. That way, everything that reads the content folder has the skills, and none of it starts without good ones. Client hash covers what the clients read, so that a client and a world agree on it, and the skills join it after the NPCs. The loop's comment says what the clients read of a skill: each skill's level and cooldown, besides its look, while the effect is the server's alone. So for each skill, we copy it, clear the effect, and add the copy to the hash, which keeps the id, the name, the level, the cooldown and the look. That way, changing a skill's damage needs no new client, while a new cooldown does, as the bar would show the old one. In the content tests, the test that reads the game's content checks that there's a skill from level one, as a new player has to have one, and a skill's damage leaves the hash alone, like a weapon's. A new test checks the refusals that we've just written. A kick with a level, which lands inside its cast, is accepted, which shows that the text we build on is good. That kick without its level is refused, with the error that names it. And one that lands after its cast, at nine hundred milliseconds into a cast of three hundred, is refused with its own error, which names the skill too. The last case swaps the cast and the hit, and keeps the level, so that it's refused for its timing alone. And the README's section on content has the skills file in it, with what each side reads of a skill, and what the checks refuse. It also says that the skills' levels and cooldowns go into the client hash, while their effects stay out, like the fights.

13:25 The content tests read the game's content folder and pass, along with the new one that refuses skills that don't add up.

Skills on the server

13:34 On the server, a skill works like a swing, with the client saying which skill to use, and the server deciding the rest. The client sends the skill's id once, as the key goes down. A skill asked for while the player is busy, in a swing or another skill, waits until they're free, so that a key pressed a moment early isn't lost. Then the world checks that the player has reached the skill's level, and that its cooldown is over, and if not, it lets the skill go. A skill that starts holds the player, turns them to the nearest monster in reach, and starts its cooldown. Partway through, it lands, and each tick, the cooldowns that are left go to the player's client, so that the hotbar can show them.

14:17 In nanommo dot proto, the input gets a skill, the id of a skill to use. Its comment says that the client sends it once, as its key goes down. That's unlike attack, which stays true for as long as the space bar is held, while the server swings again and again. Motion gets a value for using a skill, and a player gets the skill they're using, while their motion is a skill's, so that everyone's clients can play its animation. And the world gets the cooldowns, a map from each skill that isn't ready to the milliseconds left, which go to that player alone. On the wire, a protobuf map is a list of key and value pairs, so it costs a few bytes for each skill that's cooling down, and nothing for the rest. In world dot h, the skills' header comes in for the skill table, and the motions get skill too. A player keeps the skill they're using, by its id, and how far into it they are, in cast, like swing does for the sword. Cooldowns holds how long until each skill they've used is ready again, by its id, as a chrono duration, like the respawn's. And input skill is a skill they asked for, which waits until they're free, next to the other inputs. A monster gets an attacker, and its comment says what it is: the player who hurt it last, whom it goes for, seen or not, while they're within its leash. A kick knocks a skeleton three metres back, past its sight of three metres, and without this, it would forget whoever kicked it, and wander off. The world takes the skill table with the items, and its comment now says that players fight with the items' weapons and the skills. Use skill is how the server hands the world a player's skill. Its comment says what happens next: the player uses the skill as soon as they're free, if they've reached its level and it's ready. Act takes the player's id now, like hit, land and hurt further down, so that hurt can tell the monster who hurt it. Then come the private functions we're about to write, aim, for what a swing and a skill share. Usable finds a skill that a player can use now, start and land are a skill's beginning and its blow, push is for the kick, and hurt is what a swing and a skill both do to a monster. And the world keeps the skill table next to the items. In world dot c c, iterator comes in, for std next in the cooldowns' loop. The constructor keeps the skills, moved in like the items, with each of its members on a line of its own now, as they no longer fit on one. Use skill finds the player, like set input does, and stores the skill in their input skill, for their next tick, while a player who isn't in the world is left alone. That way, the skill starts in the tick, in step with everyone's moves and swings, and not halfway through one, whenever a message happens to arrive. Step hands act each player's id along with the player. At the top of act, a loop goes over the player's cooldowns, and its comment says when they run down: whatever the player's doing. Each one runs down by the tick's time, and one that's over comes out of the map, with erase, which gives back the next entry, so that the loop carries on from there. So the map holds the skills that aren't ready. A dead player's skill goes, so that a key pressed while dead doesn't fire once they're back. A player using a skill counts up their cast time, like a swing, and lands the skill in the tick where the cast time goes past the hit time. Until the cast time is over, they stay put, and then they're idle again. Once they're free, std exchange takes the skill they asked for, and leaves zero in its place, so that each request goes once. Usable gives back the skill if they can use it now, and then it starts, before the swing and the walk. The comment says it: a skill asked for while the player was busy waits until now, and one they can't use yet goes. Aim takes a reach: the sword's is one point six metres, the thrust's two point four, and second wind has none, so it aims at no one. Aim is the old swing's search for a target, moved out so that skills can use it: it picks the nearest monster in reach, and turns the player towards it, as its comment says. Swing now sets the motion and the start of the swing, and aims with the weapon's reach. Hit hurts the monster by the weapon's damage and the level's extra, and the rest of what it did, a kill and its experience, moves into hurt, further down, so that skills can share it. Usable finds the skill, and gives back nothing for one that's unknown, cooling down, or above the player's level. A modified client can send any id it likes, so each of these is checked here, on the server. Start sets the motion and the skill, starts the cast at zero and the cooldown at the skill's, and aims with the skill's reach, like a swing. Land takes the skill's effect, and the player's level, which the damage needs. It heals first, with std min, so that the health goes no higher than their maximum, and a skill with no heal adds nothing. A skill with no damage stops there, as second wind does. Then the damage gets the level's extra, like a weapon's, and the loop goes over the land's monsters. Each living one in reach is hit, for a skill that hits them all, or the one it aimed at, if that one is still in reach. The loop calls the monster's id monster id, as id is the player's. Each one in reach is pushed by the skill's push, and then hurt. Push moves the monster by the skill's push, and its comment says where: away from the player, like a walk: where the sea or a tree is in the way, it slides along it, or stays put. A push of zero does nothing, and so does a monster that stands on the player, as there's no direction away from them. The step along x and z is the line between them, scaled to the push, kept inside the island's edge, like a player's walk. And walk keeps the monster out of the sea, so it slides along a coast. And hurt is what hit used to do, with the attacker. It tells the monster who hurt it, and takes the damage from its health, without going below zero. On a kill, the monster dies, the player gets its experience, and full health on a new level. In monsters dot h, spot gets a comment, as it does more now. The player the monster goes for is the one who hurt it last, seen or not, or else the nearest it can see, either way within its leash of its home. In monsters dot c c, spot looks for the attacker among the players first, and goes for them while they're alive and within the leash of its home, before it looks for the nearest player in sight. In hunt, once its attacker dies, leaves, or gets past its leash, spot gives back someone else or no one, and the comment says what happens then: it forgets them. And a monster that comes back to life starts with no attacker.

20:52 In server dot c c, a skill's motion goes on the wire as the protocol's, in the switch that turns the world's motions into the protocol's. The player's skill goes with it, while they use one. It's sent each tick, unlike the name and the level, as it's the part of the motion that says which animation to play, and a client that missed it would play the wrong one. Each land's world gets the skills from the content, next to the items. An input with a skill hands it to the player's land, after their keys, and a zero, which is no skill, doesn't go to the world at all. Where the server sends each player their own fields, it keeps a reference to the player now, as it needs their cooldowns as well as their experience. After the experience go their cooldowns: a duration cast turns each one from nanoseconds into milliseconds, which is plenty for a countdown, and it goes into the map under the skill's id. The tests' own content includes the skills' header, and gets a kick, from level two, with a cooldown of six seconds and a cast of a second. It hits at half a second, for ten damage, with a reach of one point four metres and a push of three. It's the tests' own, so that the game's skills can change without breaking them. In the world tests, a new test has two players next to a skeleton, in a world with the sword and the kick. One is at level one, and the other has a hundred experience, which is level two, and the skeleton stands a metre away from them. Both ask to kick it, and as the comment says, the kick is from level two, which the second player has reached. So after a tick, the player at level one is idle, and the one at level two is kicking, which is the skill's motion. Then a loop runs the world for ten more ticks, which takes us half a second into the kick, where it lands, and we look at the skeleton again, by its id. It has lost ten, and five for the level, and it stands three metres further away, at four. That's out of its sight, and the comment says what it does a second later: it goes back for the player who kicked it, and not for the other one, who stands just as close. Then, as the comment says, the player can't kick again until the cooldown's over: a kick a second after the first is refused, and one after the six seconds starts. The fuzzer from episode twenty-eight plays with skills too, and its players join with up to nine hundred and ninety-nine experience, the most at level four, one short of the thousand for level five. Its world gets the game's skills, and its players join at up to level four, where the last skill is, as the comment says. They use any skill from zero to five, with two unknown ones among them. And the fuzzer already checks that health stays between nothing and the player's maximum, which a heal could break now. And the README gets a section on skills, with a table of the four. It also says that cooldowns aren't saved, so a player who leaves and comes back finds their skills ready.

23:44 The world's tests pass, with the new one for skills among them, so the levels and the cooldowns hold on the server.

A hotbar

23:53 Input dot h gets the hotbar's keys, next to the space bar's, with optional, for a key that may not have gone down. Hotbar key says which of the number keys, from one to nine, went down this frame, as a slot from zero, with std optional for none. In input dot c c, like the other keys, it's none while the player types. raylib's key one to key nine follow each other, as in ASCII, which the comment says, so we check them in a loop. Is key pressed is true for the frame the key went down, so holding one down uses the skill once. The hotbar gets a header of its own, hotbar dot h, with the animator's figures for our level, the skill table, and the protocol's world for the cooldowns. Its comment says what it is: the skills along the bottom of the screen, in the order of their levels, each on a number key, one for the first, and so on, up to nine. Nine is as many number keys as there are, past zero. The constructor takes the skill table, and it's explicit, as it takes a single argument. Build draws the bar, from our figure and the world the server sent, and its comment says what it draws: each skill with its icon and its key, and how long until it's ready, or the level it needs. It gives back the skill the player used, with its key or a click, as the bar has no connection, and main sends it, like the chat window's line. And the bar keeps the skills in the order of their slots. Hotbar dot c c includes ImGui, and from the standard library, algorithm for the sort, c float for the largest float, c math for rounding up, and numbers for pi. Then come optional, string and vector, and our input dot h for the keys. A slot is fifty-six points square, with the icon at twenty-six. Since version one point nine two, ImGui draws a font at any size, so the icon stays sharp. The bar sits twelve points above the bottom, and the shade is black, at two thirds. The key and the seconds are a light grey, and the seconds get a black shadow, at twenty-two points, so that they read over the icon. Icon turns a code point into the UTF-8 that ImGui takes, as the skills file has the icon as a number. As the comment says, Font Awesome's icons are in Unicode's private use area, between U plus E zero zero zero and U plus F eight F F, which UTF-8 writes in three bytes. The first byte holds the top four bits after its marker, and the other two hold six bits each. Draw centered centres a line of text on a spot, at a size we give it. It measures the text with the font, at that size, so that the middle of the text lands on the spot. Then it draws the text a point down and to the right in black, and in grey over that, with the font and size given to add text each time. The comment says why: with a shadow, so that it reads over the icon. Draw cooldown takes the slot's corners, the share that's left, and the milliseconds, and shades the share of the slot that's still cooling down. The top is minus a quarter turn, as ImGui's angles grow clockwise from the right, with y growing down the screen. The slice is a path from the middle of the slot, around an arc from where the share ends to the top. The arc's radius is the slot's width, so that it reaches past the corners, and the clip rectangle cuts it to the square. The path is concave once the slice is more than half, so it's filled as a concave shape, and the seconds left are rounded up, so that the count shows one until the skill is ready. Above it, the comment says what it draws: a slice that shrinks clockwise from the top, with the seconds left in the middle. A skill with no cooldown would divide by zero, so it gets no slice. The constructor copies the skills into the slots, and sorts them by their level, where stable sort keeps skills of one level in the order of their ids. Any past nine are dropped, as they'd have no key. Build looks for our figure among the ones the animator made, and waits for our level, which comes with our name, once a second, as the comment says, like the player frame. A world with no skills has no bar either. The bar is a window in the middle of the bottom of the screen, with its pivot on its bottom edge, so that it grows upwards. It has no decoration, and fits its size to the slots, and it doesn't move, take the keyboard's navigation, or take the focus when it appears, so that the game keeps its keys. Then we read the key once for the frame, and go through the slots. We've reached a skill when our level is at least its level. The slots go side by side, with same line between them. Each gets an ID of its own from push ID, the skill's, so that ImGui can tell its buttons apart, as two skills could share an icon, which is all the text a button has. A skill we haven't reached is a disabled button, between begin disabled and end disabled, which ImGui greys out, and which can't be clicked. The button's label is the icon, at its size, which push font sets for the button and pop font puts back. With the mouse over it, a tooltip has the skill's name on its first line, and its level and its cooldown, in seconds, on the second. The hover check allows a disabled button, so that a greyed out skill says what it is too. Then we draw over the button, from its corners, with the window's draw list, so that what we draw goes on top of it. The skill's cooldown, if it has one, comes from the world that the server sent. A skill we haven't reached shows the level it needs, and one that's cooling down gets the slice. Its share is the milliseconds left that the server sends, over the cooldown in the skills file, and at most one. The key goes in the top left corner, and a click or the slot's key uses the skill. A greyed out skill still goes out with its key, as the server decides whether we can use it. Then pop ID, and once the slots are done, end the window, and give back the skill, if any. CMake builds hotbar dot c c into the client's library, and in main dot c c, the client includes the hotbar, and makes it from the content's skills. After the player frame, a skill from the bar goes to the server in an input, with what we sent last, so that we keep moving as we were, like a line of chat. And the README has the hotbar in its controls, and in its section on skills.

30:10 Alice is new, so she's level one, and her name over her is gold, with no cube, as she walks across the Meadows. Along the bottom, the hotbar has the thrust on one, ready, and the other three are greyed out, with the levels they need. Pressing one uses the thrust, and alice stops for three seconds, standing, as the characters have no animation for it yet. Pressing one again doesn't stop her, as the server has the thrust cooling down, but the hotbar shows no cooldown at all, and the thrust looks ready.

Where the cooldowns went

30:42 The server sends the cooldowns after the world, with the player's experience. But the client doesn't use that message as it arrives. Since episode nine, the world comes in parts, and Parts puts it back together, keeping the player's own fields one at a time: the id, the experience, the next level, the seed and the land. The cooldowns aren't among them, so they're dropped there. So in parts dot h, Parts keeps the cooldowns too, in a map of its own, from the skill's id to the milliseconds left, next to the land, like the protocol's map. In parts dot c c, the room at the top comes first. The cooldowns come after the players and monsters in each datagram, like the rest of the player's own fields, so the room that each part leaves for those has to grow. Otherwise a part could go past twelve hundred bytes, and IP could split it into fragments, which are all lost with any one of them. A cooldown takes up to ten bytes on the wire: a tag and a length, and a tag and a value each for the skill's id and the milliseconds. So the room grows by ten for each of the nine skills a hotbar holds, as the comment says. Further down, add takes the cooldowns from each part, like the experience. Each part carries all of the player's cooldowns, so add replaces the map with the part's, and a cooldown that's over drops out of it. And world puts them back into the world it gives the client. In the tests, the datagram test fills the player's own fields with the biggest values they can have, and now it adds nine cooldowns too, of four billion milliseconds each. That's ten bytes for each cooldown on the wire, which took the parts past twelve hundred bytes before this change. And keeps your cooldowns adds a world with a kick that has one and a half seconds left to Parts, and finds it in the world that comes back, at fifteen hundred milliseconds. Where the README says how the world gets there over UDP, it now says that each part ends with the player's own fields, cooldowns among them, with room for nine.

32:35 The parts tests pass, so the cooldowns come through, and the parts stay under twelve hundred bytes with nine of them.

Animations

32:45 The skills' animations come from Mixamo, like the characters' others since episode seven. I picked three that fit: thrust slash, kicking, and a spinning slash called standing melee attack three sixty low. Mixamo's terms keep its files out of the repo, so the README in assets slash mixamo lists them. Each comes with the settings to download it with: FBX binary, without skin, at thirty frames a second, with no keyframe reduction, like the others. In build dot sh, FBX2glTF converts each one into glTF, under the name of its skill's clip, thrust, kick and whirlwind. Then merge puts the three into each character, after the death. Merge takes the hips' movement out of the walk and the run, as our server moves the players. These three end where they start, so like the slash, they keep their motion, and the thrust lunges forward and comes back. In animator dot h, a figure keeps the skill that each of its motions is for, from and to, like the two motions it blends between, and the comment says when it's set: while a motion is a skill's. In animator dot c c, follow takes the skill, which for a monster is zero, as the comment says. A new figure starts with it in both. A new skill starts a new clip too, with the motion still a skill's, so that one skill straight after another blends into the next. Further down, players pass their skill from the protocol, and monsters pass zero. In characters dot h, the skills' generated header comes in for their looks. Learn finds a skill's clip in each character, by the name in its look, and a character keeps each skill's clip, by the skill's id. A pose is a clip and the frame in it, and pose for works it out for a motion, a skill and a time. It's static, as it reads nothing but the character it's given. In characters dot c c, learn does nothing for a look without a clip, like second wind's. For each character, it looks through the clips for one with the clip's name in it, with has word, like the motions' clips, checks that raylib can play it on the model, and keeps it. Has word is the motions' check, which lowers the name for the skeleton's clips, and if two clips match, the last one wins, as it does for the motions. Pose for starts with a skill's motion, and finds the skill's clip for this character, in the map that learn filled in, and when it's there, takes the clip from the character's clips by its index. It plays the clip once, from the start, and holds its last frame, like the death, and without one, it falls through to the idle. The other motions are as before, which the code from draw moves in here: the motion picks its clip, a motion that this client doesn't know plays the idle, and each plays at its speed, looping, apart from the death. Above it, the comment says what pose for does for skills: a skill plays its clip once, and one without a clip, or with one this character doesn't have, stands. Draw now takes both poses from pose for, the one it blends from and the one it blends to, and hands them to raylib's update model animation, which blends the two. The renderer keeps the glows' colours, by the skill each goes with, in a map from the skill's id to raylib's colour, filled once from the looks, so that drawing doesn't parse a colour each frame. In renderer dot c c, two numbers come first, in metres. A glow's middle is halfway up a player, nought point nine metres up, and it reaches a little over a metre each way, past their head and their feet, as the comment says. The renderer has the characters learn each skill's clip, and keeps the colour of each skill with a glow. After the sea, which is see-through too, each player using a skill with a glow gets a ball of its colour, raylib's sphere from the NPCs, with its middle at the chest. Its radius grows over the first quarter of a second, from nothing, as the blend between clips does. raylib blends colours with their alpha by default, so second wind's, at half, shows the player through it. And the README lists the new clips with the characters' others, and says what second wind looks like.

Using the skills

36:47 This time, alice starts at level four, which I've set in the database before she logs in, so that we don't have to kill twelve skeletons first. As she walks towards the skeletons near the middle, they come for her. On two, she kicks the one next to her, and it flies three metres back, before it runs back at her. On four, she spins with the sword, and hits each skeleton around her, and on three, second wind heals her, in a green glow, with no clip of its own, so she stands. And on one, she lunges forward with the thrust, at the skeleton in front of her. On the hotbar, the kick and the whirlwind are ready again, while second wind is still shaded, with the seconds left in the middle, and its slice shrinks as the cooldown runs down.

Wrap-up

37:34 So, today the camera got a tilt, with the right mouse button, which stops between five and eighty degrees, after we sent it over the top. The cube over our player went, and our name is in gold instead. Four skills went into the content, with a schema of their own, and the clients read their levels, cooldowns and looks. The server decides each skill: it waits while the player is busy, checks the level and the cooldown, holds the player, and lands partway through. The client got a hotbar on the number keys, with the cooldowns counting down, once Parts kept them. And the characters play three new clips from Mixamo, with a green glow for second wind.

Playing it

39:05 Let me know in the comments what you'd like to see next. And if you want to follow along with the series, subscribe to the channel. I hope you find this useful, and thanks for watching.