Things SimCity 2000 Never Intended You to Do

This post is meant as a showcase of some of the proof-of-concept things I’ve done with SimCity 2000 that it had never intended you to do.

The way the game stores an in progress disaster is by setting the city to disaster mode and setting the disaster type in the MISC segment of the .sc2 file and then adding actual disaster tiles to the XTXT segment/layer. But what happens if we don’t go into disaster mode and place disaster tiles? Well, we get a city floating in clouds, and hell, a city that’s eternally on fire but never consumed.

And once you can change anything at will, there are other interesting things that can be done. Make mixed bridges? Easy. Edit the terrain after the city has been built? Yep, that’s a single value changed. Make tunnels cross? Well, sort of. The game doesn’t seem to know what to do with them. Avenues? Yep, just remove the intersections.

There’s a lot more possible, but I’ve been focused on getting more of the game reverse engineered over building better editing tools. I have considered building a standalone editor that would also function as a game frontend, but haven’t had time to do much with it.

I’ve also used the editing ability to make cities that have features some cities in real-life have. Rivers in many places flood, and often have levees along them, so I made a city that used the completely raised terrain tile as levees along a river instead of making a 3-tile wide strip of raised terrain.

levees.PNG
Levees around a city to protect a city from flooding. Normally the game wouldn’t allow you to do this, but it adds some realism, especially with the non-ramp access to the bridge.
Advertisement

SimCity 2000

SimCity 2000 is the second of Maxis’ well received city builders and is my favourite in the series, in addition to being one of my favourite games of all time. I’m not sure exactly why, but it’s the best blend of building and management and the art.

I found that SimCity 4 was all about micromanagement at a scale that wasn’t fun. The game was too hard, at least without mods to fix some of the more serious issues. SimCity 2000’s art style is very well done 8-bit graphics which I knew weren’t supposed to be hyper-realistic and never looked janky, unlike some of the later games. Maybe there’s also some nostalgia too.

Despite being released around a quarter-century ago as of this writing, I still play the game occasionally. I play the Windows 95 version, which is the definitive version. Unfortunately, it doesn’t run on modern versions of Windows without a patch to the executable, so the inferior DOS version is the only one commercially available presently.

Even when I was a child, playing the game, I’d often get to the (admittedly large for the time) limits of the 128×128 tile map and wish my city could somehow continue past it, but actually doing so was beyond my abilities then.

I’d built one city out of 25 individual tiles over several years during high-school and early University, painstakingly reconciling the edges as I went. It was slow going, and I eventually got tired of it as it was getting to be more and more work as it grew.

Thomnar 5x5
A city I called Thomnar, which doesn’t mean anything, that I built using 5×5 city tiles.

And then in 2014, I was finishing up my last semester of University and working, and I felt the draw to the game again. I still really liked that large, multi-tile city and wanted to work on it more, but reconciling the edges was annoying. Except this time, I had the tools and technical ability to start reverse engineering the game and working on a re-implementation.

My initial goal was simple: figure out the city binary file format and write something to automatically reconcile the edges.

As I figured out more and more of the format, my focus shifted from writing a tool to reconcile edges to a complete re-implementation of the game. This also involved figuring out the complete sprite file spec, as well as working on the text data files.

floating cloud city
Having the (nearly) complete city file spec also means that I can make cities that are impossible to make in the game, such as this city that is floating in the clouds.

I have gotten most of the .sc2 file spec figured out, all of the game sprite file spec as well as the .mif sprite spec and some of the text file specs done, which are kept up-to-date on my GitHub.

I also have written a Python library and related tools to open, edit and save out the modified .sc2 file, available under my reverse engineering project on GitHub: OpenCity2k.

SimCity 2000 Posts:

Things SimCity 2000 Never Intended You to Do
Thomnar – Multiple Tile City
Reverse Engineering Using Memory Inspection and Numerical Analysis

Useful SimCity 2000 Links:

Run the Windows 95 version on modern Windows
OpenSC2K – Open Source re-implementation
Pat Coston’s ClubOpolis
My documentation on the game

Not NaNo Retrospective

Things went well at the start of the month, and I got a lot of work done on the game. In the second half of the month, things started taking longer and started requiring more knowledge of how cocos2d works. I never felt like I understood how exactly cocos2d’s event model worked, because I never did figure out how to have multiple things moving in the game at the same time. There seems like there should have been a way to set up a “do-x-per-tick” way to do events, because it’s common in games, but I never figured out how to do it.

And while I was struggling to get this figured out, I also got sick, which dramatically curtailed my ability to work on the game after the 22nd or so.

I enjoyed working on the game, and I think that doing it sort of like NaNoWriMo also worked well.  One of the big differences I realized is that it’s not really possible to get stuck in NaNo and not know how to proceed. Coming up with a story doesn’t require the tools to be properly understood (assuming you’re not doing it in a text editor with obscure command such as vim or emacs as well as writing), so there’s not really a way to get stuck. Even if the story isn’t going anywhere, you can always move on to the next story.

I think cocos2d is significantly better suited to doing 2D work than Panda3D, which is reasonable, given their focus. I’m glad I didn’t end up using Panda3D. However, cocos2d doesn’t seem to have many examples, the docs are very barebones, and it hasn’t been updated (as of when I wrote this) in 14 months, which is starting to look like an abandoned project.

Would I do it again? Probably. We’ll see next year.

Not NaNo Day 22

I fixed a small bug that kept enemy units from getting targets to attack. It needs some more work, because now the paths generated will go into a core’s safe area, which is something I’d like to avoid. I’ll probably have the enemy choose a different target if this happens, or wander aimlessly/despawn if there aren’t any valid targets.

But I ran into another problem, one which I already sort of knew existed. Two things don’t seem to be able to move at once. They technically can, but there are some weird issues around it. I don’t know if it’s because it’s happening in a subprocess (I’m not sure how cocos2d works this way) or what, but I’ve been unable to figure out exactly how to fix it. Ideally, I’d like some global tick rate, where things are updated every tick. Slower things might be updated some number of ticks, but the concept is the same.

I’m not sure how to do this with cocos2d, or even if it’s possible to do with cocos2d. I spent quite a bit of time trying to get something working, and not coming up with a good solution, so today’s update is a bit light.

cocos2d enemy attack.gif
Enemy appearing to attack a unit. It’s a fluke, not the intended behaviour. In the game it also doesn’t cycle, it goes the one spot and stops.

Not NaNo Day 21

I decided to tackle some of the performance issues today. First, I fixed the A* implementation to match the fixed pathfinding implementation. This didn’t resolve my performance issues, and I wasn’t expecting it to, but I wanted to make sure that it wasn’t my issue.

What I thought was my issue was the code that checked network connectivity. I timed that segment of code, and to my surprise, it was running in under 1ms, so that certainly isn’t why the game is crawling along when lots of energy network segments are being checked as powered or not.

That leaves the actual drawing function. With one network segment, it takes ~1ms again do draw, but by the time even ~15 are on the screen, it’s taking ~10ms, and when 85 are on the screen, ~75ms. Still not the smoking gun I expected, but maybe the issue was elsewhere in drawing above the layer where I am technically setting the sprites up to draw.

And then I noticed that I’d missed clearing the children of the network layer. I’m not sure why I need to do this, but I’d already figured out that it seriously impacted performance elsewhere and had to do it. What I’d missed doing for the network and safe layers, leads to huge amounts of sprites that are being drawn, just not visibly (I think, cocos2d/pyglet can be a little opaque at times). Each time a batch is added, it stays there unless it is explicitly gotten rid of, even things that are updated many times. I still don’t know the proper way to do this, but making the change made the game run about 5-10x faster.

cocos2d fps boost.PNG
8 FPS isn’t terrific, but it’s sure better than 0 FPS.

Not NaNo Day 20

Not much to update today. I started work on enemy movement, but was not able to get it working. I’m starting to duplicate large amounts of code, leading me to wonder if I’m doing things correctly around program structure and event handling.

cocos2d progress 1.PNG
Current progress.

Also, I seem to be doing something else wrong, because the game runs very poorly. Look at the bottom left of the above image. Yes, it is running at 0 FPS (my estimate is more like 0.5 FPS, or worse), which is telling me something is very wrong. Unfortunately, I’m not sure how to profile cocos2d to see if it’s a bottleneck there or in my code, but even for testing, things are unplayably slow, even without enemies attacking and friendly weapons firing on the attacking creeps. I’m not sure how I’ll address this.

Not NaNo Day 19

Including some code cleanup, I fixed the fog-of-war so that it moves with a unit as it moves. It’s not very important, but it was bugging me. I realized that movement of multiple units at the same time is also broken, which I haven’t looked at yet.

cocos2d unit movement fog
Fog of war properly following a unit.

The next thing was starting to implement enemies. The spawning is basic now: enemies spawn close to an enemy controlled core, and they don’t do anything. Making them move, attack and be attackable is the next step (or next few steps).

In future, I want them to spawn more intelligently. Not under safe areas, but near the edges. I’m not sure how to handle making them spawn if large segments of the map are safe, but probably still at the edges.

cocos2d enemy spawn.PNG
Enemies are the red circles. The red hex is an enemy controlled city core. The orange circles are non-functional weapons. 

I’m not sure what the mechanic to take over enemy controlled cores will be. I suspect it will a control network connection and some sort of countdown timer when many enemies rush the core to try to stop the takeover.

Not NaNo Day 18

Today’s task was moving the noise based terrain generation code from my testing branch into the main branch. Once I did this, and fixed a few bugs that cropped up, I also changes the sprites to be normal sized (64×64) versions of the much smaller (8×8) sprites that I’d used in my test. This should mean that, with some tweaks, chunk generation should work in full screen mode as well.

cocos2d terrain noise.PNG
Current state of the game, more or less.

 

There are still some things to fix, one of the main things being that the start city core can spawn on water. I’m thinking of either making sure it can’t spawn on water, or making it be able to be connected wirelessly and give a free wireless energy (and control whenever I implement that) tower to the player.

Not NaNo Day 17

I spent some more time working on terrain generation, preparing to bring it into the main branch of the game so I can start spawning enemy units. I added a second noise layer to use for not-directly-terrain related sources of randomness, in the game’s case, enemy city-core spawning in chunks.

terrain.png
Enemy city cores randomly distributed and not on water. The closeness they can get to each other is configurable and set to 42 units.

Not NaNo Day 16

Another relatively slow day, programming wise. I did get a first pass of unit movement working. It’s janky and needs more work, but a unit now follows the path set out for it. I think it’d be nice if it followed the path somewhat smoothly rather than teleporting between hexes, but that’s certainly not needed to get a game working.

There weren’t many examples for how to do move a sprite along a path in cocos2d, so I resorted to looking at the source code for how things are done elsewhere. I ended up subclassing the Action class, and implementing the step() function. I’m still not sure how to make the action stop running when I’ve exhausted the path, but it’s a good start. I definitely don’t want to leave the action running forever in the background.

cocos2d unit movement custom action.gif
Basic movement of a unit following the A* shortest path.