DefaultCaptureBallPolicy.kt
package io.github.lishangbu.avalon.game.calculator.capture
/**
* 默认捕捉球策略。
*
* 这里保持与当前项目 battle capture 设计一致:
*
* - 常见球使用倍率修正
* - `heavy-ball` 使用平坦捕获率修正
* - `master-ball` 直接成功
*
* 这使得 battle 模块后续接入时可以直接复用这一层,而无需再次硬编码球规则。
*/
class DefaultCaptureBallPolicy : CaptureBallPolicy {
override fun resolve(input: CaptureRateInput): BallResolution =
when (input.ballItemInternalName.trim().lowercase()) {
"master-ball" -> {
BallResolution(directSuccess = true, note = "master-ball")
}
"ultra-ball" -> {
BallResolution(multiplier = 2.0)
}
"great-ball" -> {
BallResolution(multiplier = 1.5)
}
"poke-ball" -> {
BallResolution(multiplier = 1.0)
}
"repeat-ball" -> {
BallResolution(multiplier = if (input.captureContext.alreadyCaught) 3.5 else 1.0)
}
"timer-ball" -> {
BallResolution(multiplier = (1.0 + 0.3 * (input.turn - 1)).coerceAtMost(4.0))
}
"beast-ball" -> {
BallResolution(multiplier = if (input.captureContext.isUltraBeast) 5.0 else 0.1)
}
"quick-ball" -> {
BallResolution(multiplier = if (input.turn == 1) 5.0 else 1.0)
}
"dusk-ball" -> {
BallResolution(multiplier = if (input.captureContext.isNight || input.captureContext.isCave) 3.0 else 1.0)
}
"net-ball" -> {
BallResolution(multiplier = if (hasType(input.captureContext, "water") || hasType(input.captureContext, "bug")) 3.5 else 1.0)
}
"dive-ball" -> {
BallResolution(
multiplier =
if (input.captureContext.isFishingEncounter || input.captureContext.isSurfEncounter) {
3.5
} else {
1.0
},
)
}
"nest-ball" -> {
BallResolution(multiplier = nestBallMultiplier(input.captureContext.targetLevel))
}
"heavy-ball" -> {
BallResolution(flatCaptureRateBonus = heavyBallFlatBonus(input.captureContext.targetWeight))
}
else -> {
throw IllegalArgumentException("Unsupported capture ball '${input.ballItemInternalName}'.")
}
}
/**
* `nest-ball` 在目标等级低于 30 时有更高倍率。
*
* 这里直接沿用项目现有策略:
* `max(10, 41 - level) / 10`
*/
private fun nestBallMultiplier(targetLevel: Int?): Double {
if (targetLevel == null || targetLevel >= 30) {
return 1.0
}
return ((41 - targetLevel).coerceAtLeast(10)) / 10.0
}
/**
* `heavy-ball` 使用平坦捕获率修正,而不是倍率修正。
*
* 体重区间逻辑保持与 battle capture 文档一致。
*/
private fun heavyBallFlatBonus(targetWeight: Int?): Int =
when {
targetWeight == null || targetWeight <= 0 -> 0
targetWeight < 1024 -> -20
targetWeight < 2048 -> 0
targetWeight < 3072 -> 20
else -> 30
}
private fun hasType(
context: CaptureContext,
typeInternalName: String,
): Boolean = context.targetTypes.any { type -> type.equals(typeInternalName, ignoreCase = true) }
}