diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 6eabf5b3f..b9507812f 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -731,20 +731,38 @@ bool GuardianTryFireAt(Missile &missile, Point target) return true; } -bool GrowWall(int id, Point position, Point target, MissileID type, int spellLevel, int damage, bool skipWall = false) +bool CanPlaceWall(Point position) { + if (!InDungeonBounds(position)) + return false; + [[maybe_unused]] const int dp = dPiece[position.x][position.y]; assert(dp <= MAXTILES && dp >= 0); - if (TileHasAny(position, TileProperties::BlockMissile) || !InDungeonBounds(target)) { - return false; - } + return !TileHasAny(position, TileProperties::BlockMissile); +} + +Missile *PlaceWall(int id, MissileID type, Point position, Direction direction, int spellLevel, int damage) +{ + return AddMissile(position, position + direction, direction, type, TARGET_BOTH, id, damage, spellLevel); +} - if (!skipWall) - AddMissile(position, position, Direction::South, type, TARGET_BOTH, id, damage, spellLevel); +bool TryGrowWall(int id, MissileID type, Point position, Direction direction, int spellLevel, int damage) +{ + if (!CanPlaceWall(position)) + return false; + PlaceWall(id, type, position, direction, spellLevel, damage); return true; } +std::optional MoveWallToNextPosition(Point position, Direction growDirection) +{ + Point nextPosition = position + growDirection; + if (!InDungeonBounds(nextPosition)) + return std::nullopt; + return nextPosition; +} + /** @brief Sync missile position with parent missile */ void SyncPositionWithParent(Missile &missile, const AddMissileParameter ¶meter) { @@ -3725,45 +3743,6 @@ void ProcessRhino(Missile &missile) PutMissile(missile); } -/* - * @brief Moves the control missile towards the right and grows walls. - * @param missile The control missile. - * @param type The missile ID of the wall missile. - * @param dmg The damage of the wall missile. - */ -static void ProcessWallControlLeft(Missile &missile, const MissileID type, const int dmg) -{ - const Point leftPosition = { missile.var1, missile.var2 }; - const Point target = leftPosition + static_cast(missile.var3); - - if (!missile.limitReached && GrowWall(missile._misource, leftPosition, target, type, missile._mispllvl, dmg)) { - missile.var1 = target.x; - missile.var2 = target.y; - } else { - missile.limitReached = true; - } -} - -/* - * @brief Moves the control missile towards the right and grows walls. - * @param missile The control missile. - * @param type The missile ID of the wall missile. - * @param dmg The damage of the wall missile. - * @param skipTile Should the tile be skipped. - */ -static void ProcessWallControlRight(Missile &missile, const MissileID type, const int dmg, const bool skipWall = false) -{ - const Point rightPosition = { missile.var5, missile.var6 }; - const Point target = rightPosition + static_cast(missile.var4); - - if (missile.var7 == 0 && GrowWall(missile._misource, rightPosition, target, type, missile._mispllvl, dmg, skipWall)) { - missile.var5 = target.x; - missile.var6 = target.y; - } else { - missile.var7 = 1; - } -} - void ProcessWallControl(Missile &missile) { missile.duration--; @@ -3792,9 +3771,43 @@ void ProcessWallControl(Missile &missile) const Point leftPosition = { missile.var1, missile.var2 }; const Point rightPosition = { missile.var5, missile.var6 }; + const Direction leftDirection = static_cast(missile.var3); + const Direction rightDirection = static_cast(missile.var4); + + bool isStart = leftPosition == rightPosition; + + std::optional nextLeftPosition = std::nullopt; + std::optional nextRightPosition = std::nullopt; + + if (isStart) { + if (!CanPlaceWall(leftPosition)) { + missile._miDelFlag = true; + return; + } + PlaceWall(missile._misource, type, leftPosition, leftDirection, missile._mispllvl, dmg); + nextLeftPosition = MoveWallToNextPosition(leftPosition, leftDirection); + nextRightPosition = MoveWallToNextPosition(rightPosition, rightDirection); + } else { + if (!missile.limitReached && TryGrowWall(missile._misource, type, leftPosition, Direction::South, missile._mispllvl, dmg)) { + nextLeftPosition = MoveWallToNextPosition(leftPosition, leftDirection); + } + if (missile.var7 == 0 && TryGrowWall(missile._misource, type, rightPosition, Direction::South, missile._mispllvl, dmg)) { + nextRightPosition = MoveWallToNextPosition(rightPosition, rightDirection); + } + } - ProcessWallControlLeft(missile, type, dmg); - ProcessWallControlRight(missile, type, dmg, leftPosition == rightPosition); + if (nextLeftPosition) { + missile.var1 = nextLeftPosition->x; + missile.var2 = nextLeftPosition->y; + } else { + missile.limitReached = true; + } + if (nextRightPosition) { + missile.var5 = nextRightPosition->x; + missile.var6 = nextRightPosition->y; + } else { + missile.var7 = 1; + } } void ProcessInfravision(Missile &missile) @@ -3835,39 +3848,27 @@ void ProcessApocalypse(Missile &missile) void ProcessFlameWaveControl(Missile &missile) { - bool f1 = false; - bool f2 = false; - const int id = missile._misource; + const Direction pdir = Players[id]._pdir; const Point src = missile.position.tile; const Direction sd = GetDirection(src, { missile.var1, missile.var2 }); - const Direction dira = Left(Left(sd)); - const Direction dirb = Right(Right(sd)); - Point na = src + sd; - [[maybe_unused]] int pn = dPiece[na.x][na.y]; - assert(pn >= 0 && pn <= MAXTILES); - if (!TileHasAny(na, TileProperties::BlockMissile)) { - const Direction pdir = Players[id]._pdir; - AddMissile(na, na + sd, pdir, MissileID::FlameWave, TARGET_MONSTERS, id, 0, missile._mispllvl); - na += dira; - Point nb = src + sd + dirb; - for (int j = 0; j < (missile._mispllvl / 2) + 2; j++) { - pn = dPiece[na.x][na.y]; // BUGFIX: dPiece is accessed before check against dungeon size and 0 - assert(pn >= 0 && pn <= MAXTILES); - if (TileHasAny(na, TileProperties::BlockMissile) || f1 || !InDungeonBounds(na)) { - f1 = true; - } else { - AddMissile(na, na + sd, pdir, MissileID::FlameWave, TARGET_MONSTERS, id, 0, missile._mispllvl); - na += dira; - } - pn = dPiece[nb.x][nb.y]; // BUGFIX: dPiece is accessed before check against dungeon size and 0 - assert(pn >= 0 && pn <= MAXTILES); - if (TileHasAny(nb, TileProperties::BlockMissile) || f2 || !InDungeonBounds(nb)) { - f2 = true; - } else { - AddMissile(nb, nb + sd, pdir, MissileID::FlameWave, TARGET_MONSTERS, id, 0, missile._mispllvl); - nb += dirb; - } + const Point start = src + sd; + if (CanPlaceWall(start)) { + PlaceWall(id, MissileID::FlameWave, start, pdir, missile._mispllvl, 0); + int segmentsToAdd = (missile._mispllvl / 2) + 2; + Point left = start; + const Direction dirLeft = Left(Left(sd)); + for (int j = 0; j < segmentsToAdd; j++) { + left += dirLeft; + if (!TryGrowWall(id, MissileID::FlameWave, left, pdir, missile._mispllvl, 0)) + break; + } + Point right = start; + const Direction dirRight = Right(Right(sd)); + for (int j = 0; j < segmentsToAdd; j++) { + right += dirRight; + if (!TryGrowWall(id, MissileID::FlameWave, right, pdir, missile._mispllvl, 0)) + break; } }