In a previous post I talked about the Crystal Castles source code. Based on this code I decided to figure out how the level data was stored and write a viewer for it.
Even though I called this a "level" viewer that term isn't used much in the source code, but there are two other terms that are important to understand, "wave" and "city". Each distinct layout for a level is called a city, and there are 16 different cities in the game. What some games call a "level" is referred to as a wave in Crystal Castles. There are 37 waves in the game. Since there are only 16 cities, most get repeated but there is a mechanism to give them some variety which I will explain later.
City data is stored in ROMS 136022-102.1h and 136022-101.1f with 1024 bytes allocated for each city.
The shape of a city is defined by a 22x22 grid of box each of which can have a different height. The first block of data for each city is 484 bytes and determines the height of each box. The height can range from 0 to 127. The first byte is the block in the back corner, it then proceeds along the back row to the right, and then continues left to right, back to front.
The next block are the 484 attribute bytes which are laid out just like the height data. The bits in each byte are as follows:
0-1: Region - Used to selectively turn off certain parts of the city in certain waves.
2: Accessibility (1 = Accessible/0 = not Accessible)
3: Unused
4: Gem (1 = Gem present)
5: Tunnel (1 = Block is part of a tunnel)
6-7: Priority - Used for accessible blocks that are behind other blocks. Changes the color of the sprites so they look like they are passing behind the block. I still need to do some research to better understand how this works.
The next byte is a count of how many elevators, maximum of five, are on the level, which is followed by the elevator's data, 11 bytes for each one:
0: Mode - 3 = Falling to the bottom/2 = Sitting at the top/1 = Rising to the top/0 = Siting at the bottom
1: Current Mode Time - Frames left in current mode
2: State - FF = Stopped/00 = Moving
3-4: Pointer to current mode time in RAM. This points to the current mode time, but the Mode and State bytes are also stored in RAM with it.
5: Top Height
6: Bottom Height
7: X position in pixels
8: Y position in pixels
9: Frames to wait at the top and bottom
10: Priority - Used to indicate that an elevator is behind another piece of the city
The first three bytes are moved to mode pointer address since they dynamically change during the level, which the remaining data stays static.
The waves are stored in a table in the ROM along with the game code. Each wave has one byte in the table. Bits 0-3 determine which of the 16 cities is used for the wave, and the rest of the bytes control the "regions". As shown above each block has an attribute that can assign the block to one of three regions, or no region. The remaining four bits in the wave table control the regions as follows:
bit 4 = 0 turn off region 1
bit 5 = 0 turn off region 2 (If both regions are set off, only one is randomly chosen to be off)
bits 6 - 7 : 0 = Turn off region 3
: 1,2,3= Turn on region 3
The regions allow for variations to cities that are used more then once. The region handling code does have a random determination for region three, but the way it is written the random state can never be reached.
I have used this information to build a Crystal Castles level viewer. The program is written in C# and can be compiled with Visual Studio Community Edition. The source code is available here:
https://github.com/danlb2000/CrystalCastlesLevelViewer
You can step though either by city of by wave, view the accessibility attribute and turn on or off each region. When in wave mode the regions will automatically be set based on the game rules.

No comments:
Post a Comment