Overlapping NPC and player fix, diagonal dumb pathing dumbness fix

This commit is contained in:
dam 2026-06-18 23:34:26 +03:00
parent 6c01044bdb
commit 2caba4e6dd
No known key found for this signature in database
GPG key ID: 4AF4E722399663FB
2 changed files with 46 additions and 0 deletions

View file

@ -405,6 +405,16 @@ object CombatMovementIntents {
}
} else {
destinations.add(MovementDestination(targetLocation, pathfinder, allowPartialPath = true))
if (chebyshevToFootprint(attacker, target, targetLocation) <= target.size()) {
val border = CombatMovementPlanner.borderTiles(target, targetLocation, attacker.size())
val walkable = border.filter { RegionManager.isTeleportPermitted(it) }
val sorted = walkable.sortedWith(
compareBy<Location> { it.getDistance(attacker.location) }.thenBy { it.x }.thenBy { it.y }
)
for (location in sorted) {
destinations.add(MovementDestination(location, pathfinder, allowPartialPath = true))
}
}
}
trace.candidateCount += destinations.size
return destinations
@ -695,6 +705,12 @@ object CombatMovementIntents {
return dx * dx + dy * dy
}
private fun chebyshevToFootprint(attacker: Entity, target: Entity, targetLocation: Location): Int {
val closestX = attacker.location.x.coerceIn(targetLocation.x, targetLocation.x + target.size() - 1)
val closestY = attacker.location.y.coerceIn(targetLocation.y, targetLocation.y + target.size() - 1)
return maxOf(kotlin.math.abs(attacker.location.x - closestX), kotlin.math.abs(attacker.location.y - closestY))
}
private fun shouldUseTargetFootprintRoute(attacker: Entity, target: Entity, pathfinder: Pathfinder): Boolean {
if (attacker.properties.combatPulse.style != CombatStyle.MELEE || occupiedTilesOverlap(attacker, target)) {
return false

View file

@ -1187,6 +1187,36 @@ class CombatMovementTests {
}
}
@Test
fun dumbMeleeNpcShouldStepToAttackTileWhenDiagonallyAdjacentOnOpenTerrain() {
TestUtils.getMockPlayer("combat_open_diagonal_adjacent_target").use { player ->
val origin = Location.create(3200, 3600, 0)
place(player, origin)
val npc = NPC.create(100, origin.transform(1, 1, 0))
npc.init()
try {
configureMelee(npc)
assertFalse(meleeReach(npc, player), "Precondition: NPC should be diagonally adjacent, not in melee range.")
npc.attack(player)
CombatMovementIntents.clear()
CombatMovementIntents.request(npc, player)
CombatMovementIntents.resolve()
npc.walkingQueue.update()
assertTrue(
meleeReach(npc, player),
"A diagonally-adjacent dumb NPC on open terrain should step to an orthogonally-adjacent attack tile."
)
assertTrue(npc.properties.combatPulse.isAttacking)
} finally {
npc.clear()
CombatMovementIntents.clear()
}
}
}
@Test
fun dumbMeleeNpcShouldNotSidestepAroundImmediateSafespotBlocker() {
TestUtils.getMockPlayer("combat_cardinal_north_safespot_target").use { player ->