The Dink Network

Warp-bug found and fixed

FreeDink-data Source Code

September 20th 2012, 06:44 PM
anon.gif
shevek
Ghost They/Them
 
As I mentioned about historical hero 2, I noticed a bug that sometimes when warping out of a room, instead the game would hang. This was very hard to reproduce, though. With Dink and the 4 towers, it was a lot easier: almost always when leaving the first tower after beating the splitbug, the game hangs. So I grabbed my debugger and went hunting.

I've traced the bug to src/freedink.c, line 3876 (all this is copied from regular dink, so while the file will have a different name, the same problem should be in it, but probably on a different line number). There, it uses find_sprite to get a sprite number from an editor number, I think (could be the other way around). It uses the result to set the seq member, which should make the touch animation play before warping.

If the sprite is not found (not sure how that is possible, since it has just been touched, but it appearantly happens when too many sprites have recently been created, like with the splitbug), find_sprite will return 0. So far so good. It will check if spr[0].seq == 0, which it should be. But somehow isn't always.

My suggested fix is to change
if (spr[sprite].seq == 0)

into
if (sprite == 0 || spr[sprite].seq == 0)


It's a dirty fix, and probably doesn't fix the actual problem, but that means that sometimes an touch animation may not play. That's a lot better than hanging the game with no way to continue. I've tested this, and it works at least in Dink and the 4 towers.
September 20th 2012, 07:41 PM
spike.gif
Interesting! I'm familiar with this bug. At least in regular Dink, it only happens with sprites that are type 0 or type 2 (and presumably anything else, except for type 1 (normal sprite)), which act more like background than real sprites. (E.g. a type zero sprite will disappear if it's placed on top of an animated tile)
September 21st 2012, 05:16 AM
anon.gif
shevek
Ghost They/Them
 
(E.g. a type zero sprite will disappear if it's placed on top of an animated tile)

That's a different thing. Type 0 sprites aren't really sprites. They are just a way of defining the background, namely as the tiles with the type 0 sprites on top. For performance, the background is drawn during draw_screen. After that, the type 0 sprites are forgotten. This means they cannot have hardness, have a script, do damage, have a warp connected, etc.

The proper way to do animated tiles would be to prepare two layers: one for the tiles, and one for the sprites, and draw both of them each time the screen is drawn (and all the type 1 sprites, of course), or at least create a new "real" background from them each time the tiles are animated. This also costs more time and memory, which may have made a significant difference when Dink was first released. So for performance, the type 0 sprites are part of the background.

Animated tiles draw their new image onto the background. They overwrite the existing background, which is assumed to be the old tile, but can contain type 0 sprites (including dead bodies). If it does, those sprites are simply overwritten and thus lost.

I think Seth must have known this, but he chose to implement it this way because of performance. So that's a feature, not a bug.

Type 2 sprites are identical to type 1, except that they aren't drawn to the screen. The problem here is a type 2 sprite (but I'm not sure if it can happen with type 1 as well) with a warp attached, which is somehow missing in the list of "currently active sprites" (this may actually be intended behaviour for type 2 sprites). In that case, find_sprite will return 0. That wouldn't be a problem, because it will simply look up spr[0].seq, which should be empty, and start warping only when it is 0 (meaning the animation is finished). spr[0].seq starts as 0, so all is well. But if it somehow becomes non-0, there is a problem. It is never used, so its animation doesn't actually run, and it will never become 0 if it isn't. Which means the game will hang.

The problem really is that spr[0].seq somehow becomes non-0. I think this happens when too many (non-background) sprites are simultaneously active. The bug should be fixed by not using spr[0] in that case, but this workaround has the same effect.
September 21st 2012, 10:21 AM
spike.gif
That's a different thing. Type 0 sprites aren't really sprites. They are just a way of defining the background, namely as the tiles with the type 0 sprites on top. For performance, the background is drawn during draw_screen. After that, the type 0 sprites are forgotten.

Yeah, I get that. I figured that the reason the engine check isn't able to find the sprite may be that they *aren't* a true sprite, thus enabling this bug. I can verify that the bug does NOT happen with type 1 sprites -- I actually encountered the same bug on my own entry for this very same contest. (and sidestepped it by changing all my type 0 warps to type 1 warps)

This means they cannot have hardness, have a script, do damage, have a warp connected, etc.

That's not true. Type 0 sprites can have hardness and warp properties. (A case in point: most stairs going down in Original Dink)

Type 2 sprites are identical to type 1, except that they aren't drawn to the screen.

Going to contest that too. You can't give a type 2 sprite a script, and if memory serves, you can't "pick up" the sprite via script either, the same as with type 0 sprites... Are you sure they're not essentially the same thing? (since they behave almost exactly the same way...)
September 21st 2012, 12:14 PM
anon.gif
shevek
Ghost They/Them
 
Type 0 sprites can have hardness and warp properties.

They can? That's just weird. Ok, the hardness I can understand; there's a hardness map created at draw_screen, so that can include information from background sprites. Making warps work sounds just crazy, but well, nothing should surprise me when it comes to Dink's source code.

You can't give a type 2 sprite a script

Oh right. I always forget, what I describe is what I want them to be, but it doesn't actually work that way. Instead, you need to set the disabled property, which can only be done from a script. Which makes invisible sprites (as opposed to disabled) pretty useless IMO.

Are you sure they're not essentially the same thing?

Could be. I'm always hoping they're the same as type 1, but they certainly aren't, indeed.