05/12/2006 @22:27:39 ^22:52:06

Regular/Extended CeilingRaiseToHighest is really screwy!

Note: I wrote this originally for Doomworld but decided to turn it into a site update. Of course nobody'll see it here but oh well!

Consider the four line specials that purportedly raise a ceiling to its highest surrounding ceiling. These are 40 (W1) 151 (WR, Boom) 166 (S1, Boom) and 186 (SR, Boom). The reason they're screwy is because they sometimes try to start undocumented floor effects as well, and do it in a highly inconsistent and frankly incomprehensible way that smacks of bugs and poor testing.

40 W1 Raise Ceiling to HEC

Let's start with the only one that works in Doom, the W1 type. The code, from linuxdoom-1.10/p_spec.c, is this:

case 40:
  // RaiseCeilingLowerFloor
  EV_DoCeiling( line, raiseToHighest );
  EV_DoFloor( line, lowerFloorToLowest );
  line->special = 0;

What happens is, the rising ceiling is started, then a lowering floor is attempted on the same sector. This fails because Doom doesn't allow two actions simultaneously on the same sector.

In Boom (and thus PrBoom, MBF, Eternity etc.) this was changed to the following:

case 40:
  // RaiseCeilingLowerFloor
  if (demo_compatibility)
    EV_DoCeiling( line, raiseToHighest );
    EV_DoFloor( line, lowerFloorToLowest ); //jff 02/12/98 doesn't work
    line->special = 0;
    if (EV_DoCeiling(line, raiseToHighest))
      line->special = 0;

"Aha," said the Boom developers. "We can make this thing work as it was originally intended, now that we can handle simultaneous floor/ceiling actions. But we must preserve the original behaviour for old demos."

The thing is, they didn't. Notice the test at the top of the block, for demo_compatibility. This should be* if (!demo_compatibility) ! The sense of the test is inverted. Why bother trying to start a floor action in a compatibility mode that doesn't support simultaneous floor and ceiling actions? This has to be a typo - you could miss out all the lines from the if to the else and the code would have the same effect.

* Of course this mistake was a blessing in disguise in a way - allowing a floor action on Doom's linedef type can cause problems in maps that weren't designed for it. Consider for example sectors 330/334 of MAP16 in Memento Mori II, in the self-building room beyond the blue door. The window ledges would lower down to the ground outside, and you could fall out and get stuck.

166/186 - S1/SR Raise Ceiling to HEC

On we go. These only exist in Boom, so there's no demo_compatibility to worry about. However, here's the code for the S1 type*:

case 166:
  // Raise ceiling, Lower floor
  // 166 S1 EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest)
  if (EV_DoCeiling(line, raiseToHighest) ||
      EV_DoFloor(line, lowerFloorToLowest))

The intention here again is that pressing the button will cause both the ceiling to rise and the floor to lower. However, notice the use of the double-bar shortcircuit operator. This means that if the ceiling raise succeeds - that is, there isn't already a ceiling action in progress on that sector - the floor lowering will not happen.

Therefore for the S1 type the floor will need to be lowered by another line somewhere else. The SR type can be made to lower the floor, by pressing it twice in quick succession! But - it gets worse - if you allow the ceiling to complete its rise, the button will lose its ability to make the floor lower!

I believe this bug is the result of another typo - the double-bar shortcircuit operator should be the single-bar, non-shortcircuiting version, so both functions are executed regardless of the success of the first one.

* The code for the SR type is identical except the second parameter to P_ChangeSwitchTexture is 1, indicating the line is repeatable. It's not relevant to this discussion.

151 WR Raise Ceiling to HEC

This is the only one that works as you'd expect it to! It just raises the ceiling and lowers the floor without any messing about. However having said that the floor behaviour - as for all of these four line specials - is undocumented in boomref.txt, so maybe in fact it is just as wrong as the rest of them...


Don't use these line specials if you can avoid them. I feel they poorly implemented, extremely inconsistent in their behaviour, and probably weren't tested very well. Use the generalised versions which don't mess around with the floor. Passuse lines can be used to make a switch that starts two actions at the same time. Or you could use Szymanski's Splitdoor for DOOM2.EXE, which works in vanilla.