diff --git a/code/display.cpp b/code/display.cpp
index cc1fc98c5..46380c00a 100644
--- a/code/display.cpp
+++ b/code/display.cpp
@@ -2986,41 +2986,44 @@ void DisplayClass::Encroach_Fog(void)
/// The cell that the fog is to be regrown upon.
void DisplayClass::Fog_Cell(Cell const & cell)
{
- if (!In_Radar(cell)) return;
+ // An explicit queue avoids running the stack out on a cascade that spans most of the map.
+ std::vector| pending;
+ pending.push_back(cell);
- CellClass * cellptr = &(*this)[cell];
- bool fog = false;
+ while (!pending.empty()) {
+ Cell current = pending.back();
+ pending.pop_back();
- if (cellptr->IsFogMapped || cellptr->IsFogVisible) {
- fog = true;
- }
+ if (!In_Radar(current)) continue;
- cellptr->IsFogMapped = false;
- cellptr->IsFogVisible = false;
- cellptr->FogFrame = -2;
+ CellClass * cellptr = &(*this)[current];
+ bool fog = cellptr->IsFogMapped || cellptr->IsFogVisible;
- if (cellptr->IsMapped) {
- TacticalMap->Flag_Cell(*cellptr);
- }
+ cellptr->IsFogMapped = false;
+ cellptr->IsFogVisible = false;
+ cellptr->FogFrame = -2;
- for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) {
- Cell c = Adjacent_Cell(cell, dir);
- CellClass * cptr = &(*this)[c];
- int fog = TacticalMap->Cell_Shadow(cptr->Fetch_CellID(), true);
+ if (cellptr->IsMapped) {
+ TacticalMap->Flag_Cell(*cellptr);
+ }
- if (fog == -2 && cptr->FogFrame != -2) {
- Fog_Cell(c);
- } else {
- if ((cptr->IsFogVisible || fog != cptr->FogFrame) && fog >= 0 && cptr->FogFrame >= -1) {
- cptr->FogFrame = fog;
+ for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) {
+ Cell c = Adjacent_Cell(current, dir);
+ CellClass * cptr = &(*this)[c];
+ int fogshape = TacticalMap->Cell_Shadow(cptr->Fetch_CellID(), true);
+
+ if (fogshape == -2 && cptr->FogFrame != -2) {
+ pending.push_back(c);
+ } else if ((cptr->IsFogVisible || fogshape != cptr->FogFrame) && fogshape >= 0 && cptr->FogFrame >= -1) {
+ cptr->FogFrame = fogshape;
cptr->IsFogMapped = true;
cptr->IsFogVisible = false;
TacticalMap->Flag_Cell(*cptr);
}
}
- }
- if (fog) {
- cellptr->Fog_Cell();
+ if (fog) {
+ cellptr->Fog_Cell();
+ }
}
}
diff --git a/code/mainloop.cpp b/code/mainloop.cpp
index 16f292b44..28d086dec 100644
--- a/code/mainloop.cpp
+++ b/code/mainloop.cpp
@@ -40,6 +40,7 @@
#include "ipxmgr.h"
#include "language/language.h"
#include "logic.h"
+#include "map.h"
#include "misc.h"
#include "mpscore.h"
#include "msgbox.h"
@@ -540,7 +541,14 @@ void Keyboard_Process(KeyNumType & input)
if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) {
if (Session.Type == GAME_NORMAL) {
Debug_Unshroud = (Debug_Unshroud == false);
- Map.Flag_To_Redraw(GS_REDRAW_ALL);
+
+ // The same pair the reveal and blackout crates use, so turning this off
+ // leaves normal vision to re-explore the map rather than leaving it dark.
+ if (Debug_Unshroud) {
+ Map.Reveal_The_Map(true);
+ } else {
+ Map.Shroud_The_Map();
+ }
}
}
diff --git a/code/map.cpp b/code/map.cpp
index 9429804c6..4f8aa9ce7 100644
--- a/code/map.cpp
+++ b/code/map.cpp
@@ -11640,29 +11640,32 @@ bool MapClass::Is_Something_Nearby(Cell const & cell, int radius)
///
/// Determines if a coordinate is still under the shroud.
/// Which cell a coordinate appears over depends on how high it is, so the height is folded
-/// into the lookup before the shroud is consulted.
+/// into the lookup before the shroud is consulted. The map debugger's unshroud toggle sees
+/// through it, and nothing is reported as hidden while that is active.
///
/// bool; Is the coordinate still shrouded?
bool MapClass::Is_Shrouded(Coord const & coord)
{
- int level_height = coord.Z / LEVEL_LEPTON_H;
- if ((level_height & 1) != 0) {
- int offset = level_height / 2 + 1;
- Cell cell = coord.As_Cell();
- CellClass * cptr = &Map[Cell(cell.X - offset, cell.Y - offset)];
- if (cptr->IsMapped) {
- return(false);
- }
- cptr = &cptr->Adjacent_Cell(FACING_SE);
- if (!cptr->IsMapped) {
- return(true);
- }
- } else {
- int offset = level_height / 2;
- Cell cell = coord.As_Cell();
- CellClass * cptr = &Map[Cell(cell.X - offset, cell.Y - offset)];
- if (!cptr->IsMapped) {
- return(true);
+ if (!Debug_Unshroud) {
+ int level_height = coord.Z / LEVEL_LEPTON_H;
+ if ((level_height & 1) != 0) {
+ int offset = level_height / 2 + 1;
+ Cell cell = coord.As_Cell();
+ CellClass * cptr = &Map[Cell(cell.X - offset, cell.Y - offset)];
+ if (cptr->IsMapped) {
+ return(false);
+ }
+ cptr = &cptr->Adjacent_Cell(FACING_SE);
+ if (!cptr->IsMapped) {
+ return(true);
+ }
+ } else {
+ int offset = level_height / 2;
+ Cell cell = coord.As_Cell();
+ CellClass * cptr = &Map[Cell(cell.X - offset, cell.Y - offset)];
+ if (!cptr->IsMapped) {
+ return(true);
+ }
}
}
return(false);
diff --git a/manual/changes/f4-debug-unshroud-toggle.md b/manual/changes/f4-debug-unshroud-toggle.md
new file mode 100644
index 000000000..b4e25d634
--- /dev/null
+++ b/manual/changes/f4-debug-unshroud-toggle.md
@@ -0,0 +1,17 @@
+---
+title: Fix the F4 playtest shroud toggle doing nothing
+category: fix
+release: 0.2.0
+targets:
+- type: command
+ id: fixed:debug-unshroud
+ effect: changed
+credit:
+- torradmin
+---
+
+Pressing F4 flagged the whole tactical view for redraw but never touched the shroud data
+itself, so the map stayed exactly as shrouded as before. F4 now reveals the map when
+turning the toggle on and reshrouds it when turning the toggle off, the same pair the
+reveal-the-map and blackout crates use, and coordinate shroud checks now see through an
+active toggle instead of only the redraw seeing it.
diff --git a/manual/changes/stack-overflow-on-mass-shroud-unshroud.md b/manual/changes/stack-overflow-on-mass-shroud-unshroud.md
new file mode 100644
index 000000000..abc78dcd1
--- /dev/null
+++ b/manual/changes/stack-overflow-on-mass-shroud-unshroud.md
@@ -0,0 +1,17 @@
+---
+title: Fix crash when large fog changes unwound recursively
+category: fix
+release: 0.2.0
+targets:
+- type: system
+ id: map-visibility
+ effect: changed
+credit:
+- torradmin
+---
+
+Regrowing fog over a cell recursed into every neighbor that also needed to regrow, so
+shrouding or unshrouding a large connected area — a big map, a reveal-the-map crate, or the
+[map shroud toggle](/commands/fixed-debug-unshroud/) — could recurse deeply enough to
+overflow the stack and crash. Fog regrowth now walks an explicit queue instead, so the
+depth of a shroud change no longer bounds how much fog can change at once.
|