|
|
|
|
@ -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<Point> 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<Direction>(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<Direction>(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<Direction>(missile.var3); |
|
|
|
|
const Direction rightDirection = static_cast<Direction>(missile.var4); |
|
|
|
|
|
|
|
|
|
bool isStart = leftPosition == rightPosition; |
|
|
|
|
|
|
|
|
|
std::optional<Point> nextLeftPosition = std::nullopt; |
|
|
|
|
std::optional<Point> 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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|