WoWDB Design

I was the only active developper on db.mmo-champion.com and since I was no longer working at Curse, they decided to restart a database project, WoWDB.com, on the shiny Cobalt platform that powers SWOTR, Aion and Rift databases.

The release of Mist of Pandaria beta being close (less than 24 hours away) and the website without any CSS, I've been asked to come up with a design. 3 hours later, here's the result :)

As you can see, I borrowed a lot of design elements and CSS from the original MMO-Champion website. I really like the end result. Often database websites are on black backgrounds, making it with a light one gives a fresh look.

Countdown

MMO-Champion uses countdown to make hype around certain events. I've had the pleasure to do two of them, one for each expansion.

Cataclysm

28Hours
56Minutes
43Seconds
23Hours
56Minutes
43Seconds

That's my first one. The hardest part was to find a good font that doesn't suck with a big font-size. (Note: the times here are placeholders!)

Mist of Pandarian


051648

As you can see, my Photoshop skills have improved a lot since the first one :) I've been able to steal design elements from Blizzard website to make the artwork look better.

You can notice that each digit of numbers is absolutely positioned, therefore it doens't constantly move when the number changes.

I also use a Brawler, a custom Google Web Font and text-stroke to help with anti-aliasing.

Here is a report of the Ray Tracer written by myself Christopher Chedeau. I've taken the file format and most of the examples from the Ray Tracer of our friends Maxime Mouial and Clément Bœsch. The source is available on Github.

It is powered by Open Source technologies: glMatrix, CodeMirror, CoffeeScript, Twitter Bootstrap, jQuery and Web Workers.

Check out the demo, or click on any of the images.

Objects

Our Ray Tracer supports 4 object types: Plane, Sphere, Cylinder and Cone.

The core idea of the Ray Tracer is to send rays that will be reflected on items. Given a ray (origin and direction), we need to know if it intersect an object on the scene, and if it does, how to get a ray' that will be reflected on the object.

Knowing that, we open up our high school math book and come up with all the following formulas.

Legend: Ray Origin \(O\), Ray Direction \(D\), Intersection Position \(O'\), Intersection Normal \(N\) and Item Radius \(r\).

Intersection Normal
Plane \[t = \frac{O_z}{D_z}\] \[
N = \left\{
\begin{array}{l}
x = 0 \\
y = 0 \\
z = -sign(D_z)
\end{array} \right.
\]
Sphere \[
\begin{array}{l l l}
& t^2 & (O \cdot O) \\
+ & 2t & (O \cdot D) \\
+ & & (O \cdot O) - r^2
\end{array}
= 0\]
\[
N = \left\{
\begin{array}{l}
x = O'_x \\
y = O'_y \\
z = O'_z
\end{array} \right.
\]
Cylinder \[
\begin{array}{l l l}
& t^2 & (D_x D_x + D_y D_y) \\
+ & 2t & (O_x D_x + O_y D_y) \\
+ & & (O_x O_x + O_y O_y - r^2)
\end{array}
= 0\]
\[
N = \left\{
\begin{array}{l}
x = O'_x \\
y = O'_y \\
z = 0
\end{array} \right.
\]
Cone \[
\begin{array}{l l l}
& t^2 & (D_x D_x + D_y D_y - r^2 D_z D_z) \\
+ & 2t & (O_x D_x + O_y D_y - r^2 O_z D_z) \\
+ & & (O_x O_x + O_y O_y - r^2 O_z O_z)
\end{array}
= 0\]
\[
N = \left\{
\begin{array}{l}
x = O'_x \\
y = O'_y \\
z = - O'_z * tan(r^2)
\end{array} \right.
\]

In order to solve the equation \(at^2 + bt + c = 0\), we use
\[\Delta = b^2 - 4ac \]\[
\begin{array}{c c c}
\Delta \geq 0 & t_1 = \frac{-b - \sqrt{\Delta}}{2a} & t_2 = \frac{-b + \sqrt{\Delta}}{2a}
\end{array}
\]

And here is the formula for the reflected ray:

\[
\left\{
\begin{array}{l}
O' = O + tD + \varepsilon D' \\
D' = D - 2 (D \cdot N) * N
\end{array}
\right.
\]

In order to fight numerical precision errors, we are going to move the origin of the reflected point a little bit in the direction of the reflected ray (\(\varepsilon D'\)). It will avoid to falsely detect a collision with the current object.

Coordinates, Groups and Rotations

We want to move and rotate objects. In order to do that, we compute a transformation matrix (and it's inverse) for each object in the scene using the following code:

\[
T = \begin{array}{l}
(Identity * Translate_g * RotateX_g * RotateY_g * RotateZ_g) * \\
(Identity * Translate_i * RotateX_i * RotateY_i * RotateZ_i)
\end{array}
\]\[ I = T^{-1} \]

\[Translate(x, y, z) = \left(\begin{array}{c c c c}
1 & 0 & 0 & x \\
0 & 1 & 0 & y \\
0 & 0 & 1 & z \\
0 & 0 & 0 & 1
\end{array}\right)\]
\[RotateX(\alpha) = \left(\begin{array}{c c c c}
1 & 0 & 0 & 0 \\
0 & cos(\alpha) & -sin(\alpha) & 0 \\
0 & sin(\alpha) & cos(\alpha) & 0 \\
0 & 0 & 0 & 1
\end{array}\right)\]
\[RotateY(\alpha) = \left(\begin{array}{c c c c}
cos(\alpha) & 0 & sin(\alpha) & 0 \\
0 & 1 & 0 & 0 \\
-sin(\alpha) & 0 & cos(\alpha) & 0 \\
0 & 0 & 0 & 1
\end{array}\right)\]
\[RotateZ(\alpha) = \left(\begin{array}{c c c c}
cos(\alpha) & -sin(\alpha) & 0 & 0 \\
sin(\alpha) & cos(\alpha) & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1
\end{array}\right)\]

We have written the intersection and normal calculations in the object's coordinate system instead of the world's coordinate system. It makes them easier to write. We use the transformation matrix to do object -> world and the inverse matrix to do world -> object.

\[
\left\{\begin{array}{l}
O_{world} = T * O_{object} \\
D_{world} = (T * D_{object}) - (T * 0_4)
\end{array}\right.
\]
\[
\left\{\begin{array}{l}
O_{object} = I * O_{world} \\
D_{object} = (I * D_{world}) - (I * 0_4)
\end{array}\right.
\]
\[0_4 = \left(\begin{array}{c} 0 \\
0 \\
0 \\
1
\end{array}\right)
\]


Bounding Box

The previous equations give us objects with infinite dimensions (except for the sphere) whereas objects in real life have finite dimensions. To simulate this, it is possible to provide two points that will form a bounding box around the object. On the intersection test, we are going to use the nearest point that is inside the bounding box.

This gives us the ability to do various objects such as mirrors, table surface and legs, light bubbles and even a Pokeball!


Light

An object is composed of an Intensity \(I_o\), a Color \(C_o\) and a Brightness \(B_o\). Each light has a Color \(C_l\) and there is an ambient color \(C_a\). Using all those properties, we can calculate the color of a point using the following formula:

\[
I_o * (C_o + B_o) * \left(C_a + \sum_{l}{(N \cdot D) * C_l}\right)
\]

Only the lights visible from the intersection point are used in the sum. In order to check this, we send a shadow ray from the intersection point to the light and see if it intersects any object.

The following images are examples to demonstrate the lights.


Textures

In order to put a texture on an object, we need to map a point \((x, y, z)\) in the object's coordinate system into a point \((x, y)\) in the texture's coordinate system. For planes, it is straightforward, we just the \(z\) coordinate (which is equal to zero anyway). For spheres, cylinders and cones it is a bit more involved. Here is the formula where \(w\) and \(h\) are the width and height of the texture.

\[
\begin{array}{c c}
\phi = acos(\frac{O'_y}{r}) & \theta = \frac{acos\left(\frac{O'_x}{r * sin(\phi)}\right)}{2\pi}
\end{array}
\]\[
\begin{array}{c c}
x = w * \left\{\begin{array}{l l} \theta & \text{if } O'_x < 0 \\
1 - \theta & \text{else}\end{array}\right. & y = h * \frac{\phi}{\pi}
\end{array}
\]

Once we have the texture coordinates, we can easily create a checkerboard or put a texture. We added options such as scaling and repeat in order to control how the texture is placed.


We also support the alpha mask in order to make a color from a texture transparent.

Progressive Rendering

Ray tracing is a slow technique. At first, I generated pixels line by line, but I found out that the first few lines do not hold much information.

Instead, what we want to do is to have a fast overview of the scene and then improve on the details. In order to do that, during the first iteration we are only generating 1 pixel for a 32x32 square. Then we generate 1 pixel for a 16x16 square and so on ... We generate the top-left pixel and fill all the unknown pixels with it.

In order not to regenerate pixels we already seen, I came up with a condition to know if a pixel has already been generated. \(size\) is the current square size (32, 16, ...).

\[\left\{\begin{array}{l}
x \equiv 0 \pmod{size * 2}\\
y \equiv 0 \pmod{size * 2}
\end{array}\right.
\]

Supersampling

Aliasing is a problem with Ray Tracing and we solve this issue using supersampling. Basically, we send more than one ray for each pixel. We have to chose representative points from a square. There are multiple strategies: in the middle, in a grid or random. Check the result of various combinations in the following image:

Perlin Noise

We can generate random textures using Perlin Noise. We can control several parameters such as \(octaves\), the number of basic noise, the initial scale \(f\) and the factor of contribution \(p\) of the high frequency noises.

\[ noise(x, y, z) = \sum_{i = 0}^{octaves}{p^i * PerlinNoise(\frac{2^i}{f}x, \frac{2^i}{f}y, \frac{2^i}{f}z)} \]


\[noise\] \[noise * 20 - \lfloor noise * 20 \rfloor\] \[\frac{cos(noise) + 1}{2}\]

As seen in the example, we can apply additional functions after the noise has been generated to make interesting effects.

Portal

Last but not least, Portals from the self-titled game. They are easy to reproduce in a Ray Tracer and yet, I haven't seen any done.

If a ray enters portal A, it will go out from portal B. It is trivial to implement it, it is just a coordinates system transformation. Like we did for world and object transformation, we do it between A and B using their transformation matrix.

\[
\left\{\begin{array}{l}
O_{a}' = T * O_{b} \\
D_{a}' = (T * D_{b}) - (T * 0_4)
\end{array}\right.
\]
\[
\left\{\begin{array}{l}
O_{b}' = T * O_{a} \\
D_{b}' = (T * D_{a}) - (T * 0_4)
\end{array}\right.
\]

Scene Editor

In order to create scenes more easily, we have defined a scene description language. We developed a basic CodeMirror syntax highlighting script. Just enter write your scene down and press Ray Trace :)

Diablofans.com needed a bit of love. It is really gratifying to be able to visually improve a website by an order of magnitude just by changing some colors and fixing broken layout :)

Here are some of the changes I made:

Post

Poll

Blizzquote

For a school project, I have to implement Simulated Annealing meta heuristic.

Thanks to many open source web tools, I've been able to quickly do the project and have a pretty display. CoffeeScript, Raphael, Highcharts, Three.js, Twitter Bootstrap, jQuery and Web Workers.

2D Demo

You have a shuffled grid and you have to find the original grid. The only operation available is to swap 2 random elements. The cost function is the distance of the point with their original neighbors.

3D Demo

On this one, we are given many functions and we have to find the global minimum. The challenge was to be able to display the evolution of the algorithm, as it traverses 200k points per second.

CoffeeScript Sexyness

I've written the project in CoffeeScript and I don't regret it. I find myself writing code a lot faster because I have a lot less to type. Most of the Javascript syntax is either shortened (function to ->) or optional (parenthesis, curly brackets ...) in CoffeeScript. There are also handy features such as splats, generators ...

Destructuring Assignment

worker.onmessage = (data: [id, rest...]) ->
	switch id
		when 'update'
			[cost, temperature, accepted, tried, data, force] = rest

Trimmed JSON

chart = new Highcharts.Chart
	chart:
		renderTo: 'container'
	yAxis: [
		title:
			text: 'Cost'
	]
	tooltip:
		formatter: ->
			this.series.name + ': ' + this.y

Report

Felix Abecassis wrote a report that explains everything :) It's in French, sorry!

Download PDF

MMO-Champion is a World of Warcraft news website. When a new patch is released, we want to show what has changed in the game (Post Example). An english summary of each spell change is hand written, but we want to show the exact tooltip changes.

Generate Tooltips

First, we use the database application to generate the tooltips in HTML form.

Epic
Binds when equipped
Chest
1669 Armor
+341 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.
Epic
Binds when equipped
Chest
1669 Armor
+321 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 221.

Tokenize

We are not going to do a full HTML diff which is really difficult. Instead, the trick is to parse the HTML and extract only text nodes. We split them by space to generate a token stream.

ChestguardofNature'sFuryBindswhenequippedChest1669Armor+341Intellect+512StaminaRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby241. ChestguardofNature'sFuryBindswhenequippedChest1669Armor+321Intellect+512StaminaRedSocketSocketBonus:+10HasteRatingRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby221.

Diff

Paul Butler SimpleDiff algorithm is used on the two token streams to label each token with old, new or both.

ChestguardofNature'sFuryBindswhenequippedChest1669Armor+341Intellect+512StaminaRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby241. ChestguardofNature'sFuryBindswhenequippedChest1669Armor+321Intellect+512StaminaRedSocketSocketBonus:+10HasteRatingRequiresLevel85Equip:Improvescriticalstrikeratingby205.Equip:Increasesyourmasteryratingby221.

Combine

We parse the HTML again. This time we pretty-print the HTML while traversing it. As we parse text nodes, we add <ins> tags where we need to. We just make sure to group contiguous insertions.

Epic
Binds when equipped
Chest
1669 Armor
+341 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.
Epic
Binds when equipped
Chest
1669 Armor
+321 Intellect
+512 Stamina
Requires Level 85
Item Level 359
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 221.

Text Diff

We would also like to get a text-based diff we can insert in a Patch Note. We combine both token streams into one.

Chestguard of Nature's Fury
Binds when equipped
Chest
1669 Armor
+341+321 Intellect
+512 Stamina
Red Socket
Socket Bonus: +10 Haste Rating
Requires Level 85
Equip: Improves critical strike rating by 205.
Equip: Increases your mastery rating by 241.221.

Chestguard of Nature's Fury Binds when equipped Chest 1669 Armor +341+321 Intellect +512 Stamina Red Socket Socket Bonus: +10 Haste Rating Requires Level 85 Equip: Improves critical strike rating by 205. Equip: Increases your mastery rating by 241.221.

However, the result isn't that readable. There is too much noise. We want to show only the information that has changed! In order to do that, we are going to keep only the lines that contain changes.

+341+321 Intellect
Red Socket
Socket Bonus: +10 Haste Rating
Equip: Increases your mastery rating by 241.221.

+341+321 Intellect Red Socket Socket Bonus: +10 Haste Rating Equip: Increases your mastery rating by 241.221.

And it works a lot better :)

Conclusion

This approach is working really well with the World of Warcraft tooltips. After many patches and hundreds of changes, I haven't seen any weird behavior. They were common before using this technique. The only downside: non textual changes such as icons or colors will be completly ignored.

During the course "Introduction to Model Checking" by Alexandre Duret-Lutz we've been assigned to create a Binary Decision Diagram library. Contrary to most people, I've been writing mine in Javascript instead of C++. Overall it is running slower but by an acceptable factor (around x5).

Display

I've written a BDD display using the graph library called Dracula which is built on-top of RaphaëlJS. The API of the lib is really neat but there are only 2 available node layout algorithms and none of them really fit my needs. I'd love to have the ones available in GraphViz.

In order to enter a formula, I've written a small parser that accepts most common binary operations. The little dice gives you some random formula. Also, on the left, you have all the evaluations that satisfy your formula.

Use Case

Binary Decision Diagram are used in Boolean Satisfiability Problem and Model Checking. As a benchmark example, we use the BDD to solve the 8-Queens problem.

A version using web-workers is available. However, using more than one worker will be slower as the cost of communication (through JSON serialization) is really high (hundreds of thousands nodes). You can also try to use a random ordering for the variables, but beware, the execution time varies a lot (up to x100 for the fastest/slowest)!

Conclusion

It was a really fun project to do. You can view the sources and probably use them as a base if you ever need to use a BDD in your life :P


Let's get back three months before the beginning of my internship. Blizzard just released the private beta of the long waited real-time strategy (RTS) game Starcraft 2, a remake of their first extremely-successful game. I wanted to run a website for this promising game and started SC2Mapster.com. It is focused around the Map Editor of the game.

Start

I spent countless hours working on custom maps for Warcraft 3, the other RTS franchise from Blizzard. Therefore I decided to run SC2Mapster. I did not start the website from nothing, we adapted a custom web framework called CurseForge that we use for the addon developers. It has built-in support for file hosting, forums, announcements, wiki, etc... Everything needed!

Great, now it's time to get people in. At the time of the launch there were three kinds of competitors: important Warcraft 3 websites about custom maps such as HiveWorkshop, WC3Campaigns or Nibbits, huge portals about Starcraft 2 with a mapping part like TeamLiquid and new sharks like us.

The strategy that I use was to behave like a user. The map editor was not yet released but we could analyze the already existing maps. During this period I started contacting the few users that were hacking the maps and told them to come on the IRC. At the same time I started an effort to document all the parts of the editor. In order to get traffic from outside, I created the first custom map that was not just a basic modification of existing maps: Mapster TD. It has been linked on various non-mapping related websites and the video was viewed 35 000 times.

News

In the early days of Starcraft 2, it was really difficult to know what happened in the modding community. Everyone was working on his own and did not communicate with each other. I spent a lot of time seeking for websites and people that were acting in the area. Since I found some cool projects, I started listing those in the front page of SC2Mapster.

Quickly, people started to visit the website as they wanted to know about the Starcraft 2 map making. At the beginning the majority of links were sending people outside of SC2Mapster. Interestingly, people sticked to our website. They started contributing on the forums thanks to the news I was writing.

As it shows great results, I decided to start writing news at a daily rate. This was quite a challenge as I never had any experience in journalism. At first this was hard. I did not really know where to find good source of information. There were many interesting projects going on but they rarely provided material that was good to show. I started contacting people and ask them to make content that I could news. In 6 months, not a single person did not answer me or did not wanted to do it, this was really impressive!

I found that the best way to show off maps was video. The other options are text or screenshots. Text is painful to read and does not represent well a map. Screenshots are better but it is really hard to show liveliness with it. It is also painful to browse more than 5 screenshots. I decided to use video as primary support, if a project does not have a video then it will not be newsed. About a month after the adoption of that guideline, a lot of project started to do videos. It substantially improved the communication quality of a lot of projects.

Contest

A contest is an effective way to stimulate a community. The Starcraft 2 map editor being powerful, it gives wide range of areas to explore. When a contest takes place, this is the opportunity to make everyone focus on the same objective for a given period of time. This is also a motivator since the entries will be rated and a winner elected.

Contests were organized at a rate of two per month. One week to do the entry, one week to vote. As you can see, all the thirteen contests were unique.

  • World of Warcraft Boss Contest September 21st, 2010
  • Epic Battle Scene Contest September 2nd, 2010
  • Blizzard Custom Map Contest August 13th, 2010
  • User Interface Contest August 4th, 2010
  • Minigame Contest July 21th, 2010
  • Creature Contest June 29th, 2010
  • Wallpaper Contest June 17th, 2010
  • Spells Contest June 9th, 2010
  • 3D Modeling Contest May 26th, 2010
  • Melee AI Contest May 25th, 2010
  • Cut Scene Contest May 14th, 2010
  • Clock Contest May 2nd, 2010
  • Inventory Contest April 11th 2010

How to make a good contest

This is hard to organize a good contest.

The most important thing is to define the objective: stimulate the community. The community as a whole needs to participate. The rules of the contest should welcome both the newcomer and the expert. The challenge lies in the huge difference in skill between both, we want to make sure that everyone has a chance to win.

The first step toward this goal is to limit the time of the contest. Giving a week is enough for people to make interesting things, but not enough for a polished product. Therefore all the entries will be prototypes. The concept submission will be remain important factor. A week is also short enough so that people will start working right away, and not be tempted to do it later on, and then forget.

Then the subject is important. It must be balanced between specificity and openness. Each contest should have a specific theme so people have the feeling that they are competing on the same field. But it should give the opportunity to innovate. You want to vary the themes of your contests and make them cover all the features of the domain.

Since not everyone will participate, we make people vote for the best entry. This voting phase will show all the entries to the community. The results will be biased as people who vote will probably judge only with the provided videos and not test the map. But this is not important as the goal is to get people involved!

Incentives. I found out that people would gladly enter the contest even if there was no real prize. Being highlighted in the front-page of sc2mapster is motivating enough. If we were to put cash prizes, people would be less likely to do it for fun but try instead to abuse the system to win.

Finally, listen to the community. A lot of people came to me with contest ideas. However take care, most of them will not be good contests. Make sure you adapt them to fit all the rules I highlighted and eventually they will be good to go.

Moderation

The site got bigger. At one message per minute at peek hours, it was no longer able to review all the messages that were posted. Therefore it became difficult to moderate the forums.

We made a call for moderators and we were really surprised that 40 people applied. It was a tough decision to chose them. In the end one moderator was assigned for each forum, and three for the most active one. Using the activity count and their preferences we picked 13 out of all the applicants.

I had great faith in the moderators we took and wanted to give them the ability to conduct a project of their choice. It would be advertised on the front-page and labeled as an official SC2Mapster project. However this failed. This was more perceived as a constraint instead of an opportunity. It also confused them as to what were their role.

Another mistake we made was a lack of communication. We did not force the moderators to go into our private IRC channel. Therefore we were not able to create a group feeling. This was amplified by the timezone differences: the Europeans rarely talked to the Americans.

Even if I wasn't a really good manager, they did an awesome job at moderating the forums.

Working on the World of Raids Guild Recruitment I needed to make sure the user was really member of the guild he claimed to represent. Since there is no official API and we don't want to ask for the user login and password, we had to find an other way.

The idea is to take advantage of the official Armory. When the user logs off the game, his character profile is updated on the website. To make sure the user is who he pretends to be, we are going to ask him to make a change on his character, wait for the armory to update and verify.

Since it's a tool that can be useful to anyone, we decided to make it a standalone service with an open API. If you have a World of Warcraft website and feel the need to verify that your users are genuine, this is your chance!

It has been in activity since the launch of the recruitment tool. So far 100 conflicts have been automatically resolved thanks to this tool.

Check out WoW Genuine.com.

World of Raids Guild Recruitment

Guild recruitment is a recurrent problem in World of Warcraft, many attempt have been made but none succedeed so far. After a brainstorming we decided that the following points were crucial.

  • The guild recruiter has to spend less time as possible to set-up a guild and maintain it.
  • The guild search must be easy and focus on what people expect from their guild.

With these points in mind, we had to find technical responses in order to make the World of Raids Guild Recruitment Tool.

  • What You See Is What You Get: The guild management interface is the final display, there is no intermediate step.
  • Tailored Widgets: About every widget has been heavily customized to fit the user need.
  • Automatic Save: Every time you make a change, it is automatically published and available to anyone!
  • Javascript Search: Having all the guilds fetched during the loading allows to have a complex filtering system instantly updated.

Guild Search

Looking for a guild should no more be a pain! You just have to tweak the filters and result appear sorted as you edit them! No more long page reload or even ajax requests. It is instant!

We focused hard on making filters one click away. We also took great care of the raid time filter, it is an important aspect of the guild choice that is too often avoided because of its complexity.

For more details on the optimizations made, see the post Guild Recruitement – Search Optimizations.

Guild Management

Our original goal was to be able to create a guild in less than a minute (yes, 60 seconds!) and we pretty much did it. The proof in the following video.

In order to achieve this, we opted for a Wysiwyg approach to remove the need of making two interfaces for both display and edition. Every widget has been tailored to fit the user needs. For example, as soon as you enter your name and realm, it automatically gathers your progression from the official website.