Dynamic Difficulty in Ecco II

If you're working on a project that involves hacking the games, or making a new Ecco game, post it here!
Post Reply
User avatar
Moduvator
Site Admin
Posts: 55
Joined: 16 Dec 2025, 22:10
Location: Atlantis
Contact:

Dynamic Difficulty in Ecco II

Post by Moduvator »

Tides of Time was quite a unique game in many different ways, one of which was its ability to automatically adjust the difficulty based on how well you were progressing through it which was quite a novel concept at a time. But how exactly does this system work and what does it actually take for the skill level so change? This is the problem that we are going to explore in this topic.

I would like to acknowledge the work that has already been done by Johnny on reverse engineering these sections of the game when he wrote his password generator currently live on our website as this has saved me a lot of time tracking everything down. His legacy lives on and hopefully will help inspire the others along the way!

What is Dynamic Difficulty exactly?
When starting a new game, the player is faced with a choice of three tunnels that correspond to difficulty level. The one directly above that has a couple of spiky shells blocking it will set game to Hard, the one next to it that goes diagonally up and to the right will choose Easy and finally going straight to the right will start a Normal game. In this mode the game will be tracking your progress and automatically switch between Easy and Difficult depending on how well you are progressing through the levels.

Current difficulty level can be immediately seen by the size of Ecco's life and air meters: as soon as you see that they've become somewhat squashed, the game is now in Hard mode!
image.png
How does this work?
And now finally we are starting to get deep into the code!
Everything that is required for this the game keeps at a memory location of 0xA7BC::D. It's two bytes that they are using the individual nibbles of as follows:

Code: Select all

0x0000
  ^^^^
  │││└--- Current difficulty (1: easy, 0: hard)
  ││└---- Force Easy Mode (0: disabled, 8: enabled)
  │└----- Deaths counter when easy/hard is forced (0::F), points when Normal (0::1F -- MSB carry)
  └------ Force Hard Mode (0: disabled, 8: enabled)
Easy and Hard difficulty
Things are fairly straightforward here. Starting the game on the selection screen preloads this memory location with 0x1001. Forcing Easy mode sets it to 0x0081 after which it stays that way and does not change throughout the rest of the game. Similarly, forcing Hard mode will fix it at 0x8000 -- note how Easy Mode flag mentioned above has been set to 0.
In both cases above the third (from the right) nibble is then used to keep track of player's deaths (0::F).
  • Dying once on Forced Easy will read 0x0181, twice 0x0281, etc until 0x0F81.
  • Dying once on Forced Hard will read 0x8100, twice 0x8200, etc until 0x8F00.
You can see how the maximum number of player deaths the game can track is 15 at most. This data is saved along the rest inside passwords generated by the game.

Normal difficulty
So far, so standard, but what we are really interested in is what happens when Normal difficulty is selected and this is where things start to get really exciting as essentially the same data set is now used in a completely different way!
  • As game begins, the location is preloaded with 0x0C01: easy mode flag is set and we are allocated 0xC or 12 in decimal points that are now stored in the location previously used to track player's deaths.
  • Winning a regular stage decrements that counter by a few points. How many depends on the initial value.
  • Losing at a regular stage increments that counter by a few points.
  • Losing at a 3D stage increments that counter by exactly one point. That makes them more forgiving.
Assuming for the sake of brevity a flawless run from the start, the points progression is going to be as follows:

Code: Select all

0x0C    Home Bay
0x09    Crystal Springs
0x07    Fault Zone
0x06    Two Tides
0x05    Trellia's Bay
0x04    Skyway
0x03    Sky Tides    -    Hard Mode starts
We can see here how we need to win five levels in a row. By the time we have reached Sky Tides, the entire memory location will now be reading 0x0300 -- note how the Easy Mode flag has now been reset. The game is now in Hard mode and the points counter will now remain at 3 and not go below that as long as you are not losing any of the levels. But let us be realistic now and assume we weren't ready for double the scrolling speed and extra sections of the map and have, rather predictably, lost the level. What our points are going to be in that case? As we restart the level we can see the familiar section is now reading 0x0B01: we have just been penalized by eight points and game is now back in easy mode. Assuming we have learned our lesson, how long is it going to take to climb back from this?

Code: Select all

0x0B	Sky Tides	- 	Back to Easy Mode
0x09	Tube of Medusa
0x07	Skylands
0x06	Fin to Feather
0x05	Eagle's Bay
0x04	Asterite's Cave
0x03	The Lost Orcas	-	Hard Mode returns
Points Calculation Algorithm
There are two main functions that are responsible for calculating the difficulty points. The one that decrements them every time you win is located at ROM offset 0x865D8 and its section that we are interested in will look like this when greatly simplified:

Code: Select all

uint16_t winNormalStage(uint16_t points) {
    uint16_t modifier = points >> 2;

    modifier = points - modifier;
    points |= modifier;
    
    return points;
}
In layman's terms, to calculate the new amount of points whenever player wins a level, we divide their current points by 4, discarding the remainder, and subtract that from the amount of points they have. Note how because this is integer division, the modifier will become zero as soon as points reach 3, which explains why they stop there. As you can see, not only the code for this is very short, but also very elegant!

Its sibling responsible for penalizing the player and living at ROM offset 0x868CC is a little more complicated as they now have to be careful not to clobber the flags adjacent to it:

Code: Select all

uint16_t loseNormalStage(uint16_t points) {
    uint16_t modifier = points;

    if(modifier < 20) modifier++;
    if(modifier >= 4) {
        if((modifier < 10) modifier = 10;
    }
    
    points |= modifier;

    return points;
}
The code will also make sure that number of points cannot go above 0x1F or 31 in decimal.

3D Stage Points System
You will have most likely noted how password generator on our website has that checkbox and setting it causes additional ticks to appear on the difficulty bar. This has to do with the fact mentioned above, where 3D stages will always penalize the player by one point only when dying. In normal mode the password generator assumes that every 3D stage has been passed flawlessly, but real life could be different and multiple losses on those could affect the player's progression towards harder difficulty levels. This could be considered a cosmetic addon, aimed at making generated passwords more like as if they were generated by the game that does not really affect anything else.

Conclusion
Tides of time was a very unique game for its time on Mega Drive and one of the many aspects that made it such was its dynamic difficulty. I hope that this short article has helped demystify the features of this system and overall made you better appreciate the elegant code that Novotrade have come up with for this!
User avatar
Moduvator
Site Admin
Posts: 55
Joined: 16 Dec 2025, 22:10
Location: Atlantis
Contact:

Re: Dynamic Difficulty in Ecco II

Post by Moduvator »

I have been profiling both functions responsible for bonus and penalty points and I have got an interesting update on this.

The function responsible for penalizing you when you lose, has got a slightly unpredictable behaviour due to the way it is written works. To me it almost looks like Molnar hand-picked the modifiers based on what the bonus function generates and did not account for the fact 3D stages only take one point away if you lose. Due to the fact the function ORs your current points and modifier it is possible to pick values that will not give you a maximum penalty.

Take a hypothetical situation:
  • We start with 3 points, having won many levels before this
  • We then die 2 times in a row. This sets our points to 15 (0x0F)
  • Level that we are at has a 3D stage at its exit. We die once on that. This bumps our points up to 16 (0x10)
  • We restart the main stage and this time we die somewhere during that.
  • Instead of bumping our points all the way up to 31 (0x1F) function sets our points to 17 (0x11)!
Moreover, subsequent loses will increment our points to 19, then to 23 after which they will stop increasing. Technically this means we are now going to need less flawless victories to get Hard mode to be triggered again. I have produced a chart to illustrate this:
image.png
Given that the player has no way of tracking their current points as well as that nothing really breaks in this system in these intermediate situations, it's likely that this has been considered good enough for the purpose and not worth optimizing further. If anything, this is also a good illustration how one should be careful when ORing values together as this may produce unpredictable results.
Post Reply