Sector type 17 is disabled after loading a saved game

Sector type 17 is a lighting effect that was first introduced in Doom II. The effect causes the lighting of the sector to flicker in random grades between its initial brightness and a value that depends on the neighboring sectors. Although the effect has been meant to work constantly, it becomes disabled if the player loads a saved game. When the restoration takes place, the sectors with the type will receive the light level they had at the moment the game was saved and flickering no longer occurs on them. The bug is still present in Ultimate Doom.

Cause
The reason is simply that the id developers forgot to add this sector type to the savegame code. A more detailed explanation follows.

Thinkers
The explanation is simple: when setting up a level, sector types are treated by the P_SpawnSpecials function (p_spec.c line 1241). For effects other than damaging floors, the sector type is cleared and instead a thinker is spawned that will be associated to the sector and perform the effect, be it closing a door in half a minute or modifying the light level. Sector type 17 is the only new sector type introduced by Doom II, so it was added after most of the thinker code had already been written and bug fixed.

Each of these sector types is associated to its own thinker type:

A typical P_SpawnFoobar(sector) function will look like this: void P_SpawnFoobar(sector_t* sector) {    // Declaring a struct corresponding to the thinker type foobar_t*	foo; // Sector type is reset to 0. sector->special = 0; foo = Z_Malloc ( sizeof(*foo), PU_LEVSPEC, 0); // A thinker is added to the struct P_AddThinker (&foo->thinker); // The thinker function is set to the thinker type foo->thinker.function.acp1 = (actionf_p1) T_FooBar; // The sector is attached to the struct so that the // thinker will know what to affect. foo->sector = sector; // Init rest of the struct's variables as needed. } You notice that in type 4, which is both strobing and damaging, the sector type is explicitly restored to 4 after calling the thinker spawn function, since P_SpawnStrobeFlash will have reset it to 0.

The important part of this explanation is that sector type 17 uses a new thinker function.

Savegames
Since these sectors do not keep their type, the savegame code has a function written to preserve their thinkers. This is P_ArchiveSpecials in p_saveg.c, line 356. The function's description above should sum up the nature of the problem:

// // Things to handle: // // T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list // T_VerticalDoor, (vldoor_t: sector_t * swizzle), // T_MoveFloor, (floormove_t: sector_t * swizzle), // T_LightFlash, (lightflash_t: sector_t * swizzle), // T_StrobeFlash, (strobe_t: sector_t *), // T_Glow, (glow_t: sector_t *), // T_PlatRaise, (plat_t: sector_t *), - active list // Indeed, you do not see T_FireFlicker in that list. When the new thinker was added for Doom II, the developers simply forgot to also add it to the savegame code. The bulk of the P_ArchiveSpecials function is a switch statement that will write relevant info if the thinker type is T_MoveCeiling, T_VerticalDoor, T_MoveFloor, T_PlatRaise, T_LightFlash, T_StrobeFlash, or T_Glow. Since a T_FireFlicker is not any of those, they will not be written to the savegame.

To represent these various thinker types in archived games, P_ArchiveSpecials uses values from this enumeration: enum {    tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_glow, tc_endspecials } specials_e; Notice the absence of a "tc_flicker" in the list. If it had been added, between tc_glow and tc_endspecial, it would have the value 7.

And similarly, the thinker is also forgotten about by the reverse function P_UnArchiveSpecials used to load a savegame. It handles the same thinker types as its counterpart, in the same order. If a port fixed P_ArchiveSpecials and its enum but forgot to also fix P_UnarchiveSpecials, when handling the saved T_FireFlicker thinker, the error message "P_UnarchiveSpecials:Unknown tclass 7 in savegame".

Fix
This was fixed in Boom. For comparison, the specials_e enumeration in Boom looks like this: enum { tc_ceiling, tc_door, tc_floor, tc_plat, tc_flash, tc_strobe, tc_flicker,    //jff 8/8/98 add missing fire flicker entry tc_glow, tc_elevator,   //jff 2/22/98 new elevator type thinker tc_scroll,     // killough 3/7/98: new scroll effect thinker tc_friction,   // phares 3/18/98:  new friction effect thinker tc_pusher,     // phares 3/22/98:  new push/pull effect thinker tc_endspecials } specials_e;

The problem was also fixed in a different ways in source ports where most of the code was rewritten in an approach, such as ZDoom or Eternity Engine. There, thinker objects contain their own serialization code, which avoids the problem of having related code in two different parts of the program and therefore makes it harder to forget about savegames when adding a new thinker type.