diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 896ce94d6..c5650f883 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -715,7 +715,7 @@ bool GuardianTryFireAt(Missile &missile, Point target) { const Point position = missile.position.tile; - if (!LineClearMissile(position, target)) + if (!LineClearMovingMissile(position, target)) return false; const int mid = dMonster[target.x][target.y] - 1; if (mid < 0) diff --git a/Source/monster.cpp b/Source/monster.cpp index f0f9e2ad6..f85334499 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -1593,6 +1593,80 @@ Monster *AddSkeleton(Point position, Direction dir, bool inMap) return AddMonster(position, dir, *typeIndex, inMap); } +bool LineClear(tl::function_ref clear, Point startPoint, Point endPoint) +{ + Point position = startPoint; + + int dx = endPoint.x - position.x; + int dy = endPoint.y - position.y; + if (std::abs(dx) > std::abs(dy)) { + if (dx < 0) { + std::swap(position, endPoint); + dx = -dx; + dy = -dy; + } + int d; + int yincD; + int dincD; + int dincH; + if (dy > 0) { + d = 2 * dy - dx; + dincD = 2 * dy; + dincH = 2 * (dy - dx); + yincD = 1; + } else { + d = 2 * dy + dx; + dincD = 2 * dy; + dincH = 2 * (dx + dy); + yincD = -1; + } + bool done = false; + while (!done && position != endPoint) { + if ((d <= 0) ^ (yincD < 0)) { + d += dincD; + } else { + d += dincH; + position.y += yincD; + } + position.x++; + done = position != startPoint && !clear(position); + } + } else { + if (dy < 0) { + std::swap(position, endPoint); + dy = -dy; + dx = -dx; + } + int d; + int xincD; + int dincD; + int dincH; + if (dx > 0) { + d = 2 * dx - dy; + dincD = 2 * dx; + dincH = 2 * (dx - dy); + xincD = 1; + } else { + d = 2 * dx + dy; + dincD = 2 * dx; + dincH = 2 * (dy + dx); + xincD = -1; + } + bool done = false; + while (!done && position != endPoint) { + if ((d <= 0) ^ (xincD < 0)) { + d += dincD; + } else { + d += dincH; + position.x += xincD; + } + position.y++; + done = position != startPoint && !clear(position); + } + } + return position == endPoint; +} + bool IsLineNotSolid(Point startPoint, Point endPoint) { return LineClear(IsTileNotSolid, startPoint, endPoint); @@ -1918,7 +1992,7 @@ void AiRanged(Monster &monster) RandomWalk(monster, Opposite(md)); } if (monster.mode == MonsterMode::Stand) { - if (LineClearMissile(monster.position.tile, monster.enemyPosition)) { + if (LineClearMovingMissile(monster.position.tile, monster.enemyPosition)) { const MissileID missileType = GetMissileType(monster.ai); if (monster.ai == MonsterAIID::AcidUnique) StartRangedSpecialAttack(monster, missileType, 0); @@ -1961,7 +2035,7 @@ void AiRangedAvoidance(Monster &monster) if (monster.goalVar1++ >= static_cast(2 * distanceToEnemy) && DirOK(monster, md)) { monster.goal = MonsterGoal::Normal; } else if (v < (500 * (monster.intelligence + 1) >> lessmissiles) - && (LineClearMissile(monster.position.tile, monster.enemyPosition))) { + && (LineClearMovingMissile(monster.position.tile, monster.enemyPosition))) { StartRangedSpecialAttack(monster, missileType, dam); } else { RoundWalk(monster, md, &monster.goalVar2); @@ -1973,7 +2047,7 @@ void AiRangedAvoidance(Monster &monster) if (monster.goal == MonsterGoal::Normal) { if (((distanceToEnemy >= 3 && v < ((500 * (monster.intelligence + 2)) >> lessmissiles)) || v < ((500 * (monster.intelligence + 1)) >> lessmissiles)) - && LineClearMissile(monster.position.tile, monster.enemyPosition)) { + && LineClearMovingMissile(monster.position.tile, monster.enemyPosition)) { StartRangedSpecialAttack(monster, missileType, dam); } else if (distanceToEnemy >= 2) { v = GenerateRnd(100); @@ -2096,7 +2170,7 @@ void SkeletonBowAi(Monster &monster) if (!walking) { if (GenerateRnd(100) < 2 * monster.intelligence + 3) { - if (LineClearMissile(monster.position.tile, monster.enemyPosition)) + if (LineClearMovingMissile(monster.position.tile, monster.enemyPosition)) StartRangedAttack(monster, MissileID::Arrow, 4); } } @@ -2679,7 +2753,7 @@ void CounselorAi(Monster &monster) } } else if (monster.goal == MonsterGoal::Normal) { if (distanceToEnemy >= 2) { - if (v < 5 * (monster.intelligence + 10) && LineClearMissile(monster.position.tile, monster.enemyPosition)) { + if (v < 5 * (monster.intelligence + 10) && LineClearMovingMissile(monster.position.tile, monster.enemyPosition)) { constexpr MissileID MissileTypes[4] = { MissileID::Firebolt, MissileID::ChargedBolt, MissileID::LightningControl, MissileID::Fireball }; StartRangedAttack(monster, MissileTypes[monster.intelligence], RandomIntBetween(monster.minDamage, monster.maxDamage)); } else if (GenerateRnd(100) < 30) { @@ -3201,6 +3275,16 @@ void InitGolem(devilution::Monster &monster, uint8_t golemOwnerPlayerId, int16_t UpdateEnemy(monster); } +bool PosOkMissile(Point position) +{ + return !TileHasAny(position, TileProperties::BlockMissile); +} + +bool PosOkMovingMissile(Point position) +{ + return !IsMissileBlockedByTile(position); +} + } // namespace tl::expected AddMonsterType(_monster_id type, placeflag placeflag) @@ -4254,88 +4338,14 @@ bool DirOK(const Monster &monster, Direction mdir) return mcount == monster.packSize; } -bool PosOkMissile(Point position) -{ - return !TileHasAny(position, TileProperties::BlockMissile); -} - bool LineClearMissile(Point startPoint, Point endPoint) { return LineClear(PosOkMissile, startPoint, endPoint); } -bool LineClear(tl::function_ref clear, Point startPoint, Point endPoint) +bool LineClearMovingMissile(Point startPoint, Point endPoint) { - Point position = startPoint; - - int dx = endPoint.x - position.x; - int dy = endPoint.y - position.y; - if (std::abs(dx) > std::abs(dy)) { - if (dx < 0) { - std::swap(position, endPoint); - dx = -dx; - dy = -dy; - } - int d; - int yincD; - int dincD; - int dincH; - if (dy > 0) { - d = 2 * dy - dx; - dincD = 2 * dy; - dincH = 2 * (dy - dx); - yincD = 1; - } else { - d = 2 * dy + dx; - dincD = 2 * dy; - dincH = 2 * (dx + dy); - yincD = -1; - } - bool done = false; - while (!done && position != endPoint) { - if ((d <= 0) ^ (yincD < 0)) { - d += dincD; - } else { - d += dincH; - position.y += yincD; - } - position.x++; - done = position != startPoint && !clear(position); - } - } else { - if (dy < 0) { - std::swap(position, endPoint); - dy = -dy; - dx = -dx; - } - int d; - int xincD; - int dincD; - int dincH; - if (dx > 0) { - d = 2 * dx - dy; - dincD = 2 * dx; - dincH = 2 * (dx - dy); - xincD = 1; - } else { - d = 2 * dx + dy; - dincD = 2 * dx; - dincH = 2 * (dy + dx); - xincD = -1; - } - bool done = false; - while (!done && position != endPoint) { - if ((d <= 0) ^ (xincD < 0)) { - d += dincD; - } else { - d += dincH; - position.x += xincD; - } - position.y++; - done = position != startPoint && !clear(position); - } - } - return position == endPoint; + return LineClear(PosOkMovingMissile, startPoint, endPoint); } tl::expected SyncMonsterAnim(Monster &monster) diff --git a/Source/monster.h b/Source/monster.h index a469ecd26..cf30a1fdf 100644 --- a/Source/monster.h +++ b/Source/monster.h @@ -543,9 +543,11 @@ void RemoveEnemyReferences(const Player &player); void ProcessMonsters(); void FreeMonsters(); bool DirOK(const Monster &monster, Direction mdir); -bool PosOkMissile(Point position); bool LineClearMissile(Point startPoint, Point endPoint); -bool LineClear(tl::function_ref clear, Point startPoint, Point endPoint); +/** + * @brief Checks for same missile obstructions as CheckMissileCol() for missiles that move along a path between two points + */ +bool LineClearMovingMissile(Point startPoint, Point endPoint); tl::expected SyncMonsterAnim(Monster &monster); void M_FallenFear(Point position); void PrintMonstHistory(int mt);