Sep 2, 2020

SIDE-TRACK: City Connection (NES) game mechanics

I don't know why, but I had an urge to dive into the code of Jaleco's NES port of City Connection. It's not a good game, but I had an idea for a competitive multiplayer version, so I took a crack at the code. For the unacquainted, City Connection is a gimmicky action game where you drive a car leaking paint across various cities, all the while dodging police, spike traps, and cats. The car behaves a bit like fast-driven cars in anime of the 70s and 80s, (imho) specifically Lupin III. Many reviewers complained about the handling of the car, but it has always held some bit of charm for me.

<insert screenshot>
^ It's a screenshot! ^

Each stage is comprised of 4 rows with 4 city blocks each, with each block being comprised of 32 pertinent tiles. In short, each stage is 128 tiles long, with 512 tiles per stage which may or may not be road tiles. The tiles are referenced simply by their PPU addresses, with an unpainted road using tile #3D, a painted road using #3E. Unpaved tiles use whatever value is determined by the stage's background tilemap. Each step of the game, one block is singled out to be checked and every tile is compared against #3D (unpainted road). If there are no tiles in the block with a value of #3D, a corresponding bit is set in the variable tracking painted roads. Thus, the stage is complete when the variable has a value of #FFFF. This mechanic would mean there could be a delay between the last road tile is painted and when the level is cleared, but we are talking about less than 1/4s. (Update: After multiple plays, I verified this delay does indeed exist.)

NES programmers were limited in how much tile data they could change at a time before graphical glitches started occurring. The safe limit was 4 rows of tile data. It's no coincidence that there are 4 rows of roads as well. All tiles along each of the 4 rows moves in increments of 8, whereas the background image itself moves according to the PPU scroll speed. Any tiles within the 4 rows that do not have a value of #3D or #3E are routinely replaced with the proper tile value to complete the background. This process would appear to happen every step, which seems redundantly slow to me -- just update them every 8px moved. These replacement tile values are stored in the block of RAM immediately after the road map. Since each stage is 64 tiles long, the total block of replacement tiles holds 256 tiles. All totaled, each row of tiles the player can potentially drive along take up 768 bytes just for tile data alone. 

Timing and randomization are handled by three variables. The game primarily runs on a 16-bit step counter incremented each step. Every 16 steps as long as the player is on the road, a barrier counter increases by 1. If the player changes rows or hops over a barrier, the counter is reset. There is also a randomizer variable, which each turn is multiplied by 3, then increased by 3, then increased once more if no buttons are pressed on the gamepad.

The player's car has a 16x8 bounding box, essentially. So if an oilcan is within 8 pixels vertically and 16 pixels horizontally of the player's origin, the player collects that oilcan.

Scoring is as follows: If the car is at max speed, then if the step counter is at a multiple of 4, add 1 mile. Each value of the score is artificially inflated by 100, so a score of 1 will appear as 100 to the player (very common practice in game design). Every 64 steps (i.e., every 16 miles) increases the score by 1. An extra life is awarded each time either player hits 300 points and 1000 points; this can only happen once per play-through. Popping a balloon ads 10 points. $$cop scoring needed$$

Byte $A0 is the stage design, which affects the background and police cars. There are 6 stages (in order): New York, USA; London, England; Paris, France; Schwangau, Germany; Agra, India; and Nara, Japan. Sprites for police are updated every step accordingly. Music is updated accordingly when returning from pausing. The background design is only updated after dying.

Byte $A1 is the road map. There are 16 possible maps. After map #07, the maps loop between #08 to #0F. Things get interesting here; perhaps this code was borrowed from other games. Each map starts with two tiles numbers to use. What's interesting is there are only two tiles that can be used: #3D and #40, so why they do things this way is a head-scratcher. What follows are alternating counts for each tile, ending with a value of #FF. Let's look at map 13 for an example: 

#[18 17 10 12 10 15 0A 6A 10 06 10 10 10 0D 27 1C 80]

This will spawn 24 road tiles, 23 empty tiles, 16 road tiles, 18 empty tiles, 16 road tiles 21 empty tiles, and 10 road tiles on the first row. If you add up the numbers in each color set, they add up to #80 (128). Ultimately it doesn't matter if the set contains four sequences each totaling 128, since anything over 128 will just carry down to the next row. If the entire set adds up to more than 512, though, I would expect some graphical glitches at best, full game corruption at worst. I didn't check to see if they included a fail-safe counter. 

Each player has their own corresponding variables, so Player 1 could be in New York while Player 2 is in Paris. For single variables such as stage, road map, and lives, a third variable is used to keep track of the active player's stats. So you would have P1 stats, P2 stats, and Active stats; again, this is seen in a lot of games. As for saving the road data between players, the entire road map for each player is compressed from 512 bytes to just 64 bytes by looping through each tile, comparing it to #3E, and rotating the carry bit into the arrays at $240 (for P1) and $280 (for P2) whenever a player dies. When a player spawns, including at the start of a game, the entire road map is redrawn and then it loops through the saved bits, setting each road tile to paved if the corresponding bit was saved. 

Cops will start spawning after 512 steps. The first cop spawns when the low bits of the step counter are #00 and will always be aggro after 5888 miles. The second cop spawns when the low bits of the step counter are #40 will always be aggro after 8960 miles. The third cop spawns when the low bits of the step counter are #80. After being on the same level for 1024 steps (when the barrier counter is #40) and the low bits of the step counter are #C1, an aggro cop will attempt to spawn.

Barriers also start popping up after 1024 steps on the same level. There are two unique barriers spawning when the step counter is between the ranges of [#00,#0F] or [#80,#8F] and [#70,#7F] or [#F0,#FF], attempting to trap the player. The barrier code is rookie-level inefficient, performing all the calculations before checking if a barrier can even be spawned; there's plenty of room for optimization. 

Cats will spawn when the low bits of the step counter are #10 or #90. 

Balloons spawn every #700 steps at a random row on a random side of the screen. Consequently, there is a chance the balloon will spawn and then immediately go off-screen because the player is driving the wrong direction. Balloons move 1 pixel every other step (as opposed to 1 or 2 pixels each step like other objects).

A UFO will spawn on stage 0 (New York), if the 10th oilcan of the current life is thrown when the screen position & #1FF is between #1E0 and #1FF (the Statue of Liberty in the middle of the screen). Collecting the UFO grants an extra life.

Collecting a balloon in London when you have exactly 11 oilcans in stock will cause another balloon to immediately spawn randomly.

Byte $2C keeps track of if you perform a special attack (pressing B when the absolute value of speed is #04) on specific stages: in Paris it gets set to #40, in Agra it gets set to #80.

Performing a double-attack with 0 oilcans while in Paris causes hearts to spawn instead of balloons.

Also in Paris, when you have exactly 18 oilcans in stock, the entire top row of roads is painted (blocks_painted & #0F==#0F), and perform a double-attack, a comet will fall from the sky. You don’t get any bonus for catching the comet and you can’t catch it anyway since it is coded to move ahead of the player.

In Agra, performing a double-attack when the screen position is between #13F and #154 (when the Taj Mahal is just a hair to the left of screen center) will turn the sky orange. I think that’s all it does.

In Nara, successfully jumping over a spike trap sets byte $2D. A bunny will fall from the moon and hop off the screen. Grabbing the bunny will increase the balloon count.

There is a limit of 100 oilcans. There can only be two tossed oilcans at a time, meaning you cannot perform a special attack if at least one oilcan is already on the screen.

The stage stops scrolling if speed is less than 8.


No comments:

Post a Comment

©TheouAegis Productions™. Powered by Blogger.