Browse Source

[Bugfix] Fix Missile obstruction checks (#8351)

pull/8453/head
Eric Robinson 2 months ago committed by GitHub
parent
commit
74170acb68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      Source/missiles.cpp
  2. 172
      Source/monster.cpp
  3. 6
      Source/monster.h

2
Source/missiles.cpp

@ -715,7 +715,7 @@ bool GuardianTryFireAt(Missile &missile, Point target)
{ {
const Point position = missile.position.tile; const Point position = missile.position.tile;
if (!LineClearMissile(position, target)) if (!LineClearMovingMissile(position, target))
return false; return false;
const int mid = dMonster[target.x][target.y] - 1; const int mid = dMonster[target.x][target.y] - 1;
if (mid < 0) if (mid < 0)

172
Source/monster.cpp

@ -1593,6 +1593,80 @@ Monster *AddSkeleton(Point position, Direction dir, bool inMap)
return AddMonster(position, dir, *typeIndex, inMap); return AddMonster(position, dir, *typeIndex, inMap);
} }
bool LineClear(tl::function_ref<bool(Point)> 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) bool IsLineNotSolid(Point startPoint, Point endPoint)
{ {
return LineClear(IsTileNotSolid, startPoint, endPoint); return LineClear(IsTileNotSolid, startPoint, endPoint);
@ -1918,7 +1992,7 @@ void AiRanged(Monster &monster)
RandomWalk(monster, Opposite(md)); RandomWalk(monster, Opposite(md));
} }
if (monster.mode == MonsterMode::Stand) { 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); const MissileID missileType = GetMissileType(monster.ai);
if (monster.ai == MonsterAIID::AcidUnique) if (monster.ai == MonsterAIID::AcidUnique)
StartRangedSpecialAttack(monster, missileType, 0); StartRangedSpecialAttack(monster, missileType, 0);
@ -1961,7 +2035,7 @@ void AiRangedAvoidance(Monster &monster)
if (monster.goalVar1++ >= static_cast<int>(2 * distanceToEnemy) && DirOK(monster, md)) { if (monster.goalVar1++ >= static_cast<int>(2 * distanceToEnemy) && DirOK(monster, md)) {
monster.goal = MonsterGoal::Normal; monster.goal = MonsterGoal::Normal;
} else if (v < (500 * (monster.intelligence + 1) >> lessmissiles) } else if (v < (500 * (monster.intelligence + 1) >> lessmissiles)
&& (LineClearMissile(monster.position.tile, monster.enemyPosition))) { && (LineClearMovingMissile(monster.position.tile, monster.enemyPosition))) {
StartRangedSpecialAttack(monster, missileType, dam); StartRangedSpecialAttack(monster, missileType, dam);
} else { } else {
RoundWalk(monster, md, &monster.goalVar2); RoundWalk(monster, md, &monster.goalVar2);
@ -1973,7 +2047,7 @@ void AiRangedAvoidance(Monster &monster)
if (monster.goal == MonsterGoal::Normal) { if (monster.goal == MonsterGoal::Normal) {
if (((distanceToEnemy >= 3 && v < ((500 * (monster.intelligence + 2)) >> lessmissiles)) if (((distanceToEnemy >= 3 && v < ((500 * (monster.intelligence + 2)) >> lessmissiles))
|| v < ((500 * (monster.intelligence + 1)) >> lessmissiles)) || v < ((500 * (monster.intelligence + 1)) >> lessmissiles))
&& LineClearMissile(monster.position.tile, monster.enemyPosition)) { && LineClearMovingMissile(monster.position.tile, monster.enemyPosition)) {
StartRangedSpecialAttack(monster, missileType, dam); StartRangedSpecialAttack(monster, missileType, dam);
} else if (distanceToEnemy >= 2) { } else if (distanceToEnemy >= 2) {
v = GenerateRnd(100); v = GenerateRnd(100);
@ -2096,7 +2170,7 @@ void SkeletonBowAi(Monster &monster)
if (!walking) { if (!walking) {
if (GenerateRnd(100) < 2 * monster.intelligence + 3) { 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); StartRangedAttack(monster, MissileID::Arrow, 4);
} }
} }
@ -2679,7 +2753,7 @@ void CounselorAi(Monster &monster)
} }
} else if (monster.goal == MonsterGoal::Normal) { } else if (monster.goal == MonsterGoal::Normal) {
if (distanceToEnemy >= 2) { 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 }; constexpr MissileID MissileTypes[4] = { MissileID::Firebolt, MissileID::ChargedBolt, MissileID::LightningControl, MissileID::Fireball };
StartRangedAttack(monster, MissileTypes[monster.intelligence], RandomIntBetween(monster.minDamage, monster.maxDamage)); StartRangedAttack(monster, MissileTypes[monster.intelligence], RandomIntBetween(monster.minDamage, monster.maxDamage));
} else if (GenerateRnd(100) < 30) { } else if (GenerateRnd(100) < 30) {
@ -3201,6 +3275,16 @@ void InitGolem(devilution::Monster &monster, uint8_t golemOwnerPlayerId, int16_t
UpdateEnemy(monster); UpdateEnemy(monster);
} }
bool PosOkMissile(Point position)
{
return !TileHasAny(position, TileProperties::BlockMissile);
}
bool PosOkMovingMissile(Point position)
{
return !IsMissileBlockedByTile(position);
}
} // namespace } // namespace
tl::expected<size_t, std::string> AddMonsterType(_monster_id type, placeflag placeflag) tl::expected<size_t, std::string> AddMonsterType(_monster_id type, placeflag placeflag)
@ -4254,88 +4338,14 @@ bool DirOK(const Monster &monster, Direction mdir)
return mcount == monster.packSize; return mcount == monster.packSize;
} }
bool PosOkMissile(Point position)
{
return !TileHasAny(position, TileProperties::BlockMissile);
}
bool LineClearMissile(Point startPoint, Point endPoint) bool LineClearMissile(Point startPoint, Point endPoint)
{ {
return LineClear(PosOkMissile, startPoint, endPoint); return LineClear(PosOkMissile, startPoint, endPoint);
} }
bool LineClear(tl::function_ref<bool(Point)> clear, Point startPoint, Point endPoint) bool LineClearMovingMissile(Point startPoint, Point endPoint)
{ {
Point position = startPoint; return LineClear(PosOkMovingMissile, startPoint, endPoint);
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;
} }
tl::expected<void, std::string> SyncMonsterAnim(Monster &monster) tl::expected<void, std::string> SyncMonsterAnim(Monster &monster)

6
Source/monster.h

@ -543,9 +543,11 @@ void RemoveEnemyReferences(const Player &player);
void ProcessMonsters(); void ProcessMonsters();
void FreeMonsters(); void FreeMonsters();
bool DirOK(const Monster &monster, Direction mdir); bool DirOK(const Monster &monster, Direction mdir);
bool PosOkMissile(Point position);
bool LineClearMissile(Point startPoint, Point endPoint); bool LineClearMissile(Point startPoint, Point endPoint);
bool LineClear(tl::function_ref<bool(Point)> 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<void, std::string> SyncMonsterAnim(Monster &monster); tl::expected<void, std::string> SyncMonsterAnim(Monster &monster);
void M_FallenFear(Point position); void M_FallenFear(Point position);
void PrintMonstHistory(int mt); void PrintMonstHistory(int mt);

Loading…
Cancel
Save