mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2026-08-28 05:45:10 -06:00
Some code reformatting with ktfmt (Kotlinlang)
This commit is contained in:
parent
2caba4e6dd
commit
5857cba851
7 changed files with 1160 additions and 523 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -9,19 +9,19 @@ import core.game.world.map.Point
|
|||
import core.game.world.map.RegionManager
|
||||
import core.game.world.map.path.Pathfinder
|
||||
|
||||
/**
|
||||
* Plans combat-specific chase targets without mutating walking queues.
|
||||
*/
|
||||
/** Plans combat-specific chase targets without mutating walking queues. */
|
||||
object CombatMovementPlanner {
|
||||
data class Plan(
|
||||
val targetLocation: Location, val attackTile: Location?
|
||||
val targetLocation: Location,
|
||||
val attackTile: Location?,
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun plan(attacker: Entity, target: Entity): Plan {
|
||||
val targetLocation = predictedTargetLocation(target)
|
||||
return Plan(
|
||||
targetLocation = targetLocation, attackTile = chooseTargetBorderTile(attacker, target, targetLocation)
|
||||
targetLocation = targetLocation,
|
||||
attackTile = chooseTargetBorderTile(attacker, target, targetLocation),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +31,8 @@ object CombatMovementPlanner {
|
|||
return true
|
||||
}
|
||||
val targetTile = target.getClosestOccupiedTile(attacker.location)
|
||||
return attacker.location.getDistance(targetTile) > ServerConstants.MAX_PATHFIND_DISTANCE * 2.0
|
||||
return attacker.location.getDistance(targetTile) >
|
||||
ServerConstants.MAX_PATHFIND_DISTANCE * 2.0
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -114,28 +115,46 @@ object CombatMovementPlanner {
|
|||
}
|
||||
|
||||
@JvmStatic
|
||||
fun chooseTargetBorderTile(attacker: Entity, target: Entity, targetLocation: Location): Location? {
|
||||
fun chooseTargetBorderTile(
|
||||
attacker: Entity,
|
||||
target: Entity,
|
||||
targetLocation: Location,
|
||||
): Location? {
|
||||
return candidateAttackTiles(attacker, target, targetLocation).firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun candidateAttackTiles(attacker: Entity, target: Entity, targetLocation: Location): List<Location> {
|
||||
fun candidateAttackTiles(
|
||||
attacker: Entity,
|
||||
target: Entity,
|
||||
targetLocation: Location,
|
||||
): List<Location> {
|
||||
val candidates = borderTiles(target, targetLocation, attacker.size())
|
||||
val walkable = candidates.filter { RegionManager.isTeleportPermitted(it) }
|
||||
val attackable = walkable.filter { canInteractFrom(attacker, it, target, targetLocation) }
|
||||
return (attackable.ifEmpty { walkable.ifEmpty { candidates } }).sortedWith(compareBy<Location> {
|
||||
it.getDistance(
|
||||
attacker.location
|
||||
)
|
||||
}.thenBy { it.x }.thenBy { it.y })
|
||||
return (attackable.ifEmpty { walkable.ifEmpty { candidates } }).sortedWith(
|
||||
compareBy<Location> {
|
||||
it.getDistance(attacker.location)
|
||||
}
|
||||
.thenBy { it.x }
|
||||
.thenBy { it.y }
|
||||
)
|
||||
}
|
||||
|
||||
private fun canInteractFrom(
|
||||
attacker: Entity, location: Location, target: Entity, targetLocation: Location
|
||||
attacker: Entity,
|
||||
location: Location,
|
||||
target: Entity,
|
||||
targetLocation: Location,
|
||||
): Boolean {
|
||||
if (attacker.size() == 1 && target.size() == 1) {
|
||||
val direction = Direction.getDirection(location, targetLocation) ?: return false
|
||||
return direction.canMoveFrom(location.z, location.x, location.y, RegionManager::getClippingFlag)
|
||||
return direction.canMoveFrom(
|
||||
location.z,
|
||||
location.x,
|
||||
location.y,
|
||||
RegionManager::getClippingFlag,
|
||||
)
|
||||
}
|
||||
return Pathfinder.canInteract(
|
||||
location.x,
|
||||
|
|
@ -146,8 +165,10 @@ object CombatMovementPlanner {
|
|||
target.size(),
|
||||
target.size(),
|
||||
0,
|
||||
targetLocation.z
|
||||
) { z, x, y -> RegionManager.getClippingFlag(z, x, y) }
|
||||
targetLocation.z,
|
||||
) { z, x, y ->
|
||||
RegionManager.getClippingFlag(z, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -159,10 +180,18 @@ object CombatMovementPlanner {
|
|||
val maxOffset = targetSize - 1
|
||||
|
||||
for (offset in minOffset..maxOffset) {
|
||||
border.add(Location.create(targetLocation.x - attackerSize, targetLocation.y + offset, plane))
|
||||
border.add(Location.create(targetLocation.x + targetSize, targetLocation.y + offset, plane))
|
||||
border.add(Location.create(targetLocation.x + offset, targetLocation.y - attackerSize, plane))
|
||||
border.add(Location.create(targetLocation.x + offset, targetLocation.y + targetSize, plane))
|
||||
border.add(
|
||||
Location.create(targetLocation.x - attackerSize, targetLocation.y + offset, plane)
|
||||
)
|
||||
border.add(
|
||||
Location.create(targetLocation.x + targetSize, targetLocation.y + offset, plane)
|
||||
)
|
||||
border.add(
|
||||
Location.create(targetLocation.x + offset, targetLocation.y - attackerSize, plane)
|
||||
)
|
||||
border.add(
|
||||
Location.create(targetLocation.x + offset, targetLocation.y + targetSize, plane)
|
||||
)
|
||||
}
|
||||
|
||||
return border.toList()
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import core.game.world.map.RegionManager.getClippingFlag
|
|||
import core.game.world.map.path.Pathfinder
|
||||
import core.game.world.map.path.Pathfinder.*
|
||||
|
||||
/**
|
||||
* Shared combat reach calculations.
|
||||
*/
|
||||
/** Shared combat reach calculations. */
|
||||
object CombatReach {
|
||||
@JvmStatic
|
||||
fun isUsingHalberd(entity: Entity): Boolean {
|
||||
|
|
@ -49,26 +47,74 @@ object CombatReach {
|
|||
val size = entity.size()
|
||||
if (distance == 1) {
|
||||
for (i in 0 until size) {
|
||||
if (Pathfinder.isStandingIn(e.x - 1, e.y + i, 1, 1, x, y, victim.size(), victim.size())) {
|
||||
if (
|
||||
Pathfinder.isStandingIn(
|
||||
e.x - 1,
|
||||
e.y + i,
|
||||
1,
|
||||
1,
|
||||
x,
|
||||
y,
|
||||
victim.size(),
|
||||
victim.size(),
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (Pathfinder.isStandingIn(e.x + size, e.y + i, 1, 1, x, y, victim.size(), victim.size())) {
|
||||
if (
|
||||
Pathfinder.isStandingIn(
|
||||
e.x + size,
|
||||
e.y + i,
|
||||
1,
|
||||
1,
|
||||
x,
|
||||
y,
|
||||
victim.size(),
|
||||
victim.size(),
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (Pathfinder.isStandingIn(e.x + i, e.y - 1, 1, 1, x, y, victim.size(), victim.size())) {
|
||||
if (
|
||||
Pathfinder.isStandingIn(
|
||||
e.x + i,
|
||||
e.y - 1,
|
||||
1,
|
||||
1,
|
||||
x,
|
||||
y,
|
||||
victim.size(),
|
||||
victim.size(),
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (Pathfinder.isStandingIn(e.x + i, e.y + size, 1, 1, x, y, victim.size(), victim.size())) {
|
||||
if (
|
||||
Pathfinder.isStandingIn(
|
||||
e.x + i,
|
||||
e.y + size,
|
||||
1,
|
||||
1,
|
||||
x,
|
||||
y,
|
||||
victim.size(),
|
||||
victim.size(),
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return victim.getSwingHandler(false).type == CombatStyle.MELEE && e.withinDistance(
|
||||
victim.location,
|
||||
1
|
||||
) && victim.properties.combatPulse.getVictim() === entity && entity.index < victim.index
|
||||
return victim.getSwingHandler(false).type == CombatStyle.MELEE &&
|
||||
e.withinDistance(
|
||||
victim.location,
|
||||
1,
|
||||
) &&
|
||||
victim.properties.combatPulse.getVictim() === entity &&
|
||||
entity.index < victim.index
|
||||
}
|
||||
return entity.centerLocation.withinDistance(
|
||||
victim.centerLocation, distance + (size shr 1) + (victim.size() shr 1)
|
||||
victim.centerLocation,
|
||||
distance + (size shr 1) + (victim.size() shr 1),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +127,7 @@ object CombatReach {
|
|||
second.location.x,
|
||||
second.location.y,
|
||||
second.size(),
|
||||
second.size()
|
||||
second.size(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +149,9 @@ object CombatReach {
|
|||
fun canStepTowards(entity: Entity, victim: Entity): InteractionType {
|
||||
val closestVictimTile = victim.getClosestOccupiedTile(entity.location)
|
||||
val closestEntityTile = entity.getClosestOccupiedTile(closestVictimTile)
|
||||
val dir = closestEntityTile.deriveDirection(closestVictimTile) ?: return InteractionType.STILL_INTERACT
|
||||
val dir =
|
||||
closestEntityTile.deriveDirection(closestVictimTile)
|
||||
?: return InteractionType.STILL_INTERACT
|
||||
var next = closestEntityTile
|
||||
|
||||
// A fixed-direction walk can pass beside an oblique target without converging, so
|
||||
|
|
@ -136,37 +184,49 @@ object CombatReach {
|
|||
val components = next.getStepComponents(dir)
|
||||
|
||||
when (dir) {
|
||||
Direction.NORTH -> if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT
|
||||
Direction.EAST -> if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT
|
||||
Direction.SOUTH -> if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT
|
||||
Direction.WEST -> if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT
|
||||
Direction.NORTH ->
|
||||
if (getClippingFlag(next) and PREVENT_NORTH != 0) return InteractionType.NO_INTERACT
|
||||
Direction.EAST ->
|
||||
if (getClippingFlag(next) and PREVENT_EAST != 0) return InteractionType.NO_INTERACT
|
||||
Direction.SOUTH ->
|
||||
if (getClippingFlag(next) and PREVENT_SOUTH != 0) return InteractionType.NO_INTERACT
|
||||
Direction.WEST ->
|
||||
if (getClippingFlag(next) and PREVENT_WEST != 0) return InteractionType.NO_INTERACT
|
||||
|
||||
Direction.NORTH_EAST -> {
|
||||
if (getClippingFlag(components[0]) and PREVENT_EAST != 0 || getClippingFlag(components[1]) and PREVENT_NORTH != 0 || getClippingFlag(
|
||||
next
|
||||
) and PREVENT_NORTHEAST != 0
|
||||
) return InteractionType.NO_INTERACT
|
||||
if (
|
||||
getClippingFlag(components[0]) and PREVENT_EAST != 0 ||
|
||||
getClippingFlag(components[1]) and PREVENT_NORTH != 0 ||
|
||||
getClippingFlag(next) and PREVENT_NORTHEAST != 0
|
||||
)
|
||||
return InteractionType.NO_INTERACT
|
||||
}
|
||||
|
||||
Direction.NORTH_WEST -> {
|
||||
if (getClippingFlag(components[0]) and PREVENT_WEST != 0 || getClippingFlag(components[1]) and PREVENT_NORTH != 0 || getClippingFlag(
|
||||
next
|
||||
) and PREVENT_NORTHWEST != 0
|
||||
) return InteractionType.NO_INTERACT
|
||||
if (
|
||||
getClippingFlag(components[0]) and PREVENT_WEST != 0 ||
|
||||
getClippingFlag(components[1]) and PREVENT_NORTH != 0 ||
|
||||
getClippingFlag(next) and PREVENT_NORTHWEST != 0
|
||||
)
|
||||
return InteractionType.NO_INTERACT
|
||||
}
|
||||
|
||||
Direction.SOUTH_EAST -> {
|
||||
if (getClippingFlag(components[0]) and PREVENT_EAST != 0 || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 || getClippingFlag(
|
||||
next
|
||||
) and PREVENT_SOUTHEAST != 0
|
||||
) return InteractionType.NO_INTERACT
|
||||
if (
|
||||
getClippingFlag(components[0]) and PREVENT_EAST != 0 ||
|
||||
getClippingFlag(components[1]) and PREVENT_SOUTH != 0 ||
|
||||
getClippingFlag(next) and PREVENT_SOUTHEAST != 0
|
||||
)
|
||||
return InteractionType.NO_INTERACT
|
||||
}
|
||||
|
||||
Direction.SOUTH_WEST -> {
|
||||
if (getClippingFlag(components[0]) and PREVENT_WEST != 0 || getClippingFlag(components[1]) and PREVENT_SOUTH != 0 || getClippingFlag(
|
||||
next
|
||||
) and PREVENT_SOUTHWEST != 0
|
||||
) return InteractionType.NO_INTERACT
|
||||
if (
|
||||
getClippingFlag(components[0]) and PREVENT_WEST != 0 ||
|
||||
getClippingFlag(components[1]) and PREVENT_SOUTH != 0 ||
|
||||
getClippingFlag(next) and PREVENT_SOUTHWEST != 0
|
||||
)
|
||||
return InteractionType.NO_INTERACT
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@ import kotlin.math.floor
|
|||
private const val SEARCH_MAP_SIZE = 128
|
||||
private const val RING_BUFFER_SIZE = 4096
|
||||
|
||||
class RsmodPathfinder(
|
||||
private val maxWaypoints: Int = 25
|
||||
) : Pathfinder() {
|
||||
class RsmodPathfinder(private val maxWaypoints: Int = 25) : Pathfinder() {
|
||||
|
||||
private val defaultFinder = ThreadLocal.withInitial {
|
||||
PathFinder(RegionManager.RSMOD_CLIPPING_FLAGS, SEARCH_MAP_SIZE, RING_BUFFER_SIZE)
|
||||
|
|
@ -35,7 +33,7 @@ class RsmodPathfinder(
|
|||
type: Int,
|
||||
walkingFlag: Int,
|
||||
near: Boolean,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
clipMaskSupplier: ClipMaskSupplier?,
|
||||
): Path {
|
||||
val source = requireNotNull(start)
|
||||
val destination = requireNotNull(dest)
|
||||
|
|
@ -46,7 +44,10 @@ class RsmodPathfinder(
|
|||
|
||||
if (magnitude > ServerConstants.MAX_PATHFIND_DISTANCE) {
|
||||
if (canAttempt(source, destination)) {
|
||||
end = source.transform(vector.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1))
|
||||
end =
|
||||
source.transform(
|
||||
vector.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1)
|
||||
)
|
||||
} else {
|
||||
path.isMoveNear = true
|
||||
return path
|
||||
|
|
@ -54,29 +55,31 @@ class RsmodPathfinder(
|
|||
}
|
||||
|
||||
val shape = routeShape(type, sizeX, sizeY)
|
||||
val finder = if (clipMaskSupplier == null) {
|
||||
RegionManager.loadClippingWindow(source, SEARCH_MAP_SIZE)
|
||||
defaultFinder.get()
|
||||
} else {
|
||||
val state = suppliedFinder.get()
|
||||
state.loadCollisionWindow(source, clipMaskSupplier)
|
||||
state.finder
|
||||
}
|
||||
val route = finder.findPath(
|
||||
level = source.z,
|
||||
srcX = source.x,
|
||||
srcZ = source.y,
|
||||
destX = end.x,
|
||||
destZ = end.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = if (sizeX == 0) 1 else sizeX,
|
||||
destHeight = if (sizeY == 0) 1 else sizeY,
|
||||
objRot = rotation,
|
||||
objShape = shape,
|
||||
moveNear = near,
|
||||
blockAccessFlags = walkingFlag,
|
||||
maxWaypoints = maxWaypoints
|
||||
)
|
||||
val finder =
|
||||
if (clipMaskSupplier == null) {
|
||||
RegionManager.loadClippingWindow(source, SEARCH_MAP_SIZE)
|
||||
defaultFinder.get()
|
||||
} else {
|
||||
val state = suppliedFinder.get()
|
||||
state.loadCollisionWindow(source, clipMaskSupplier)
|
||||
state.finder
|
||||
}
|
||||
val route =
|
||||
finder.findPath(
|
||||
level = source.z,
|
||||
srcX = source.x,
|
||||
srcZ = source.y,
|
||||
destX = end.x,
|
||||
destZ = end.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = if (sizeX == 0) 1 else sizeX,
|
||||
destHeight = if (sizeY == 0) 1 else sizeY,
|
||||
objRot = rotation,
|
||||
objShape = shape,
|
||||
moveNear = near,
|
||||
blockAccessFlags = walkingFlag,
|
||||
maxWaypoints = maxWaypoints,
|
||||
)
|
||||
|
||||
if (route.failed) {
|
||||
return path
|
||||
|
|
@ -117,7 +120,7 @@ class RsmodPathfinder(
|
|||
type: Int,
|
||||
walkingFlag: Int,
|
||||
z: Int,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
clipMaskSupplier: ClipMaskSupplier?,
|
||||
): Boolean {
|
||||
if (clipMaskSupplier == null) {
|
||||
RegionManager.loadClippingWindow(Location.create(srcX, srcY, z), SEARCH_MAP_SIZE)
|
||||
|
|
@ -133,14 +136,15 @@ class RsmodPathfinder(
|
|||
srcSize = moverSize,
|
||||
objRot = rotation,
|
||||
objShape = routeShape(type, destWidth, destHeight),
|
||||
blockAccessFlags = walkingFlag
|
||||
blockAccessFlags = walkingFlag,
|
||||
)
|
||||
}
|
||||
// Reach checks only read tiles within the source/destination rectangles and
|
||||
// their cross-axis combinations, so loading just their padded bounding box
|
||||
// into a reused map produces the same reads as a freshly allocated full map.
|
||||
val flags = suppliedReachFlags.get()
|
||||
val destSize = maxOf(if (destWidth == 0) 1 else destWidth, if (destHeight == 0) 1 else destHeight)
|
||||
val destSize =
|
||||
maxOf(if (destWidth == 0) 1 else destWidth, if (destHeight == 0) 1 else destHeight)
|
||||
val minX = maxOf(0, minOf(srcX, destX) - 1)
|
||||
val minY = maxOf(0, minOf(srcY, destY) - 1)
|
||||
val maxX = maxOf(srcX + moverSize, destX + destSize) + 1
|
||||
|
|
@ -163,7 +167,7 @@ class RsmodPathfinder(
|
|||
srcSize = moverSize,
|
||||
objRot = rotation,
|
||||
objShape = routeShape(type, destWidth, destHeight),
|
||||
blockAccessFlags = walkingFlag
|
||||
blockAccessFlags = walkingFlag,
|
||||
)
|
||||
} finally {
|
||||
for (zoneX in (minX shr 3)..(maxX shr 3)) {
|
||||
|
|
@ -176,25 +180,35 @@ class RsmodPathfinder(
|
|||
|
||||
@JvmStatic
|
||||
fun lineOfSight(
|
||||
start: Location, dest: Location, moverSize: Int, destWidth: Int, destHeight: Int
|
||||
start: Location,
|
||||
dest: Location,
|
||||
moverSize: Int,
|
||||
destWidth: Int,
|
||||
destHeight: Int,
|
||||
): RayCast {
|
||||
RegionManager.loadClippingWindow(start, SEARCH_MAP_SIZE)
|
||||
return lineOfSightLoaded(start, dest, moverSize, destWidth, destHeight)
|
||||
}
|
||||
|
||||
private fun lineOfSightLoaded(
|
||||
start: Location, dest: Location, moverSize: Int, destWidth: Int, destHeight: Int
|
||||
start: Location,
|
||||
dest: Location,
|
||||
moverSize: Int,
|
||||
destWidth: Int,
|
||||
destHeight: Int,
|
||||
): RayCast {
|
||||
return projectileLineFinder.get().lineOfSight(
|
||||
level = start.z,
|
||||
srcX = start.x,
|
||||
srcZ = start.y,
|
||||
destX = dest.x,
|
||||
destZ = dest.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = destWidth,
|
||||
destHeight = destHeight
|
||||
)
|
||||
return projectileLineFinder
|
||||
.get()
|
||||
.lineOfSight(
|
||||
level = start.z,
|
||||
srcX = start.x,
|
||||
srcZ = start.y,
|
||||
destX = dest.x,
|
||||
destZ = dest.y,
|
||||
srcSize = moverSize,
|
||||
destWidth = destWidth,
|
||||
destHeight = destHeight,
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -204,7 +218,7 @@ class RsmodPathfinder(
|
|||
moverSize: Int,
|
||||
destWidth: Int,
|
||||
destHeight: Int,
|
||||
maxRaySteps: Int = Int.MAX_VALUE
|
||||
maxRaySteps: Int = Int.MAX_VALUE,
|
||||
): Boolean {
|
||||
val rayCast = lineOfSight(start, dest, moverSize, destWidth, destHeight)
|
||||
return rayCast.success && rayCast.coordinates.size <= maxRaySteps
|
||||
|
|
@ -216,10 +230,16 @@ class RsmodPathfinder(
|
|||
sourceSize: Int,
|
||||
targetLocation: Location,
|
||||
targetSize: Int,
|
||||
maxRaySteps: Int = Int.MAX_VALUE
|
||||
maxRaySteps: Int = Int.MAX_VALUE,
|
||||
): Boolean {
|
||||
RegionManager.loadClippingWindow(sourceLocation, SEARCH_MAP_SIZE)
|
||||
return hasLineOfSightBetweenLoaded(sourceLocation, sourceSize, targetLocation, targetSize, maxRaySteps)
|
||||
return hasLineOfSightBetweenLoaded(
|
||||
sourceLocation,
|
||||
sourceSize,
|
||||
targetLocation,
|
||||
targetSize,
|
||||
maxRaySteps,
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -233,7 +253,7 @@ class RsmodPathfinder(
|
|||
sourceSize: Int,
|
||||
targetLocation: Location,
|
||||
targetSize: Int,
|
||||
maxRaySteps: Int = Int.MAX_VALUE
|
||||
maxRaySteps: Int = Int.MAX_VALUE,
|
||||
): Boolean {
|
||||
if (sourceSize == 1 && targetSize == 1) {
|
||||
if (maxRaySteps == Int.MAX_VALUE) {
|
||||
|
|
@ -256,7 +276,8 @@ class RsmodPathfinder(
|
|||
// step, so tiles further than one orthogonal step apart can
|
||||
// never satisfy a single-step ray.
|
||||
val manhattan =
|
||||
kotlin.math.abs(source.x - (targetLocation.x + targetX)) + kotlin.math.abs(source.y - (targetLocation.y + targetY))
|
||||
kotlin.math.abs(source.x - (targetLocation.x + targetX)) +
|
||||
kotlin.math.abs(source.y - (targetLocation.y + targetY))
|
||||
if (manhattan > 1) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -273,17 +294,22 @@ class RsmodPathfinder(
|
|||
return false
|
||||
}
|
||||
|
||||
private fun hasSingleTileLineOfSight(sourceLocation: Location, targetLocation: Location): Boolean {
|
||||
return projectileLineValidator.get().hasLineOfSight(
|
||||
level = sourceLocation.z,
|
||||
srcX = sourceLocation.x,
|
||||
srcZ = sourceLocation.y,
|
||||
destX = targetLocation.x,
|
||||
destZ = targetLocation.y,
|
||||
srcSize = 1,
|
||||
destWidth = 1,
|
||||
destHeight = 1
|
||||
)
|
||||
private fun hasSingleTileLineOfSight(
|
||||
sourceLocation: Location,
|
||||
targetLocation: Location,
|
||||
): Boolean {
|
||||
return projectileLineValidator
|
||||
.get()
|
||||
.hasLineOfSight(
|
||||
level = sourceLocation.z,
|
||||
srcX = sourceLocation.x,
|
||||
srcZ = sourceLocation.y,
|
||||
destX = targetLocation.x,
|
||||
destZ = targetLocation.y,
|
||||
srcSize = 1,
|
||||
destWidth = 1,
|
||||
destHeight = 1,
|
||||
)
|
||||
}
|
||||
|
||||
private fun manhattanDistance(first: Location, second: Location): Int {
|
||||
|
|
@ -292,11 +318,12 @@ class RsmodPathfinder(
|
|||
return dx + dy
|
||||
}
|
||||
|
||||
private fun routeShape(type: Int, sizeX: Int, sizeY: Int): Int = when {
|
||||
type >= 0 -> type
|
||||
sizeX != 0 && sizeY != 0 -> 10
|
||||
else -> -1
|
||||
}
|
||||
private fun routeShape(type: Int, sizeX: Int, sizeY: Int): Int =
|
||||
when {
|
||||
type >= 0 -> type
|
||||
sizeX != 0 && sizeY != 0 -> 10
|
||||
else -> -1
|
||||
}
|
||||
|
||||
private val suppliedReachFlags = ThreadLocal.withInitial { CollisionFlagMap() }
|
||||
|
||||
|
|
@ -309,7 +336,9 @@ class RsmodPathfinder(
|
|||
}
|
||||
|
||||
private fun loadCollisionWindow(
|
||||
flags: CollisionFlagMap, start: Location, supplier: ClipMaskSupplier
|
||||
flags: CollisionFlagMap,
|
||||
start: Location,
|
||||
supplier: ClipMaskSupplier,
|
||||
) {
|
||||
val baseX = start.x - (SEARCH_MAP_SIZE / 2)
|
||||
val baseY = start.y - (SEARCH_MAP_SIZE / 2)
|
||||
|
|
|
|||
|
|
@ -14,13 +14,18 @@ class RsmodProjectilePathfinder : Pathfinder() {
|
|||
type: Int,
|
||||
walkingFlag: Int,
|
||||
near: Boolean,
|
||||
clipMaskSupplier: ClipMaskSupplier?
|
||||
clipMaskSupplier: ClipMaskSupplier?,
|
||||
): Path {
|
||||
val source = requireNotNull(start)
|
||||
val destination = requireNotNull(end)
|
||||
val rayCast = RsmodPathfinder.lineOfSight(
|
||||
start = source, dest = destination, moverSize = size, destWidth = sizeX, destHeight = sizeY
|
||||
)
|
||||
val rayCast =
|
||||
RsmodPathfinder.lineOfSight(
|
||||
start = source,
|
||||
dest = destination,
|
||||
moverSize = size,
|
||||
destWidth = sizeX,
|
||||
destHeight = sizeY,
|
||||
)
|
||||
val path = Path()
|
||||
if (!rayCast.success) {
|
||||
path.isMoveNear = rayCast.alternative
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,9 +37,10 @@ class CombatPerformanceTests {
|
|||
measureCombatTick(load, it)
|
||||
}
|
||||
|
||||
val durations = LongArray(MEASURED_TICKS) { tick ->
|
||||
measureCombatTick(load, tick + WARMUP_TICKS)
|
||||
}
|
||||
val durations =
|
||||
LongArray(MEASURED_TICKS) { tick ->
|
||||
measureCombatTick(load, tick + WARMUP_TICKS)
|
||||
}
|
||||
val sorted = durations.sorted()
|
||||
val p90Index = ((sorted.size * 9 + 9) / 10 - 1).coerceIn(0, sorted.lastIndex)
|
||||
val p90 = sorted[p90Index]
|
||||
|
|
@ -50,12 +51,12 @@ class CombatPerformanceTests {
|
|||
p90 <= HEADROOM_TICK_BUDGET_MILLIS,
|
||||
"650-player combat p90 tick time should leave room for slower live hardware. " +
|
||||
"durations=${durationText}ms, p90=${p90}ms, " +
|
||||
"budget=${HEADROOM_TICK_BUDGET_MILLIS}ms"
|
||||
"budget=${HEADROOM_TICK_BUDGET_MILLIS}ms",
|
||||
)
|
||||
assertTrue(
|
||||
max <= LIVE_TICK_BUDGET_MILLIS,
|
||||
"650-player combat tick should remain under the live 600ms server tick budget. " +
|
||||
"durations=${durationText}ms, max=${max}ms"
|
||||
"durations=${durationText}ms, max=${max}ms",
|
||||
)
|
||||
} finally {
|
||||
load?.close()
|
||||
|
|
@ -83,9 +84,10 @@ class CombatPerformanceTests {
|
|||
configureMeleePlayer(player)
|
||||
}
|
||||
|
||||
val pairs = players.chunked(2).mapIndexed { index, pair ->
|
||||
CombatPair(pair[0], pair[1], pairOrigin(index))
|
||||
}
|
||||
val pairs =
|
||||
players.chunked(2).mapIndexed { index, pair ->
|
||||
CombatPair(pair[0], pair[1], pairOrigin(index))
|
||||
}
|
||||
val load = CombatLoad(players, pairs, previousWildPvp)
|
||||
|
||||
GameWorld.settings!!.wild_pvp_enabled = true
|
||||
|
|
@ -105,7 +107,7 @@ class CombatPerformanceTests {
|
|||
assertEquals(
|
||||
TOTAL_PLAYER_COUNT,
|
||||
CombatMovementIntents.pendingCount(),
|
||||
"The performance fixture should exercise one combat movement intent per loaded player."
|
||||
"The performance fixture should exercise one combat movement intent per loaded player.",
|
||||
)
|
||||
|
||||
val start = System.nanoTime()
|
||||
|
|
@ -114,10 +116,11 @@ class CombatPerformanceTests {
|
|||
}
|
||||
|
||||
private fun configureMeleePlayer(player: Player) {
|
||||
player.properties.attackStyle = WeaponInterface.AttackStyle(
|
||||
WeaponInterface.STYLE_AGGRESSIVE,
|
||||
WeaponInterface.BONUS_CRUSH
|
||||
)
|
||||
player.properties.attackStyle =
|
||||
WeaponInterface.AttackStyle(
|
||||
WeaponInterface.STYLE_AGGRESSIVE,
|
||||
WeaponInterface.BONUS_CRUSH,
|
||||
)
|
||||
player.properties.combatPulse.updateStyle()
|
||||
player.properties.combatLevel = 126
|
||||
player.skills.setStaticLevel(Skills.HITPOINTS, 10_000)
|
||||
|
|
@ -131,13 +134,13 @@ class CombatPerformanceTests {
|
|||
private data class CombatPair(
|
||||
val first: Player,
|
||||
val second: Player,
|
||||
val origin: Location
|
||||
val origin: Location,
|
||||
)
|
||||
|
||||
private class CombatLoad(
|
||||
val players: List<Player>,
|
||||
private val pairs: List<CombatPair>,
|
||||
private val previousWildPvp: Boolean
|
||||
private val previousWildPvp: Boolean,
|
||||
) : AutoCloseable {
|
||||
|
||||
fun resetPairPositionsAndMovement(tick: Int) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue