diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt index 49b895fbd..7d7c87502 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakInfo.kt @@ -44,7 +44,7 @@ data class BreakInfo( // Pre Processing var shouldProgress = false - var couldReBreak by OneSetPerTick(value = false, throwOnLimitBreach = true) + var couldReBreak by OneSetPerTick(value = RebreakManager.RebreakPotential.None, throwOnLimitBreach = true) var shouldSwap by OneSetPerTick(value = false, throwOnLimitBreach = true) var swapStack: ItemStack by OneSetPerTick(ItemStack.EMPTY, true) var minSwapTicks by OneSetPerTick(0, true) @@ -118,7 +118,7 @@ data class BreakInfo( val item = player.inventory.getStack(context.hotbarIndex) val breakDelta = context.cachedState.calcItemBlockBreakingDelta(player, world, context.blockPos, item) val breakProgress = breakDelta * (breakingTicks + 1) - return if (couldReBreak) + return if (couldReBreak == RebreakManager.RebreakPotential.Instant) breakConfig.swapMode.isEnabled() else when (breakConfig.swapMode) { BreakConfig.SwapMode.None -> false diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt index 32fc7d5b9..0c254aa5f 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/BreakManager.kt @@ -101,39 +101,49 @@ object BreakManager : RequestHandler( onOpen = { processRequest(activeRequest); simulateAbandoned() }, onClose = { checkForCancels() } ), PositionBlocking { + private val breakInfos = arrayOfNulls(2) + + private val activeInfos + get() = breakInfos + .filterNotNull() + .filter { it.type != RedundantSecondary } + private var primaryBreak: BreakInfo? get() = breakInfos[0] set(value) { breakInfos[0] = value } + private var secondaryBreak: BreakInfo? get() = breakInfos[1] set(value) { breakInfos[1] = value } - private val breakInfos = arrayOfNulls(2) + + private val abandonedBreak + get() = breakInfos[1].let { secondary -> + if (secondary?.abandoned == true && secondary.type != RedundantSecondary) secondary + else null + } + val currentStackSelection - get() = breakInfos + get() = activeInfos .lastOrNull { - it != null && it.type != RedundantSecondary && (it.breakConfig.doubleBreak || it.type == Secondary) + it.breakConfig.doubleBreak || it.type == Secondary }?.context?.itemSelection ?: StackSelection.EVERYTHING.select() - private val pendingBreakCount get() = breakInfos.count { it != null } + pendingActions.size + private val pendingBreakCount get() = activeInfos.count() + pendingActions.size override val blockedPositions - get() = breakInfos.mapNotNull { it?.context?.blockPos } + pendingActions.map { it.context.blockPos } + get() = activeInfos.map { it.context.blockPos } + pendingActions.map { it.context.blockPos } private var activeRequest: BreakRequest? = null private var rotationRequest: RotationRequest? = null private val rotated get() = rotationRequest?.done != false - private var swapped = false - set(value) { - field = value - if (!value) - breakInfos.forEach { it?.serverBreakTicks = 0 } - } + var swappedThisTick = false var swappedStack: ItemStack = ItemStack.EMPTY set(value) { if (value != field) breakInfos.forEach { it?.serverBreakTicks = 0 } + swappedThisTick = true field = value } private var breakCooldown = 0 @@ -161,6 +171,9 @@ object BreakManager : RequestHandler( super.load() listen(priority = Int.MIN_VALUE) { + if (!swappedThisTick) { + swappedStack = player.mainHandStack + } else swappedThisTick = false if (breakCooldown > 0) { breakCooldown-- } @@ -257,7 +270,7 @@ object BreakManager : RequestHandler( info.context.cachedState.getOutlineShape(world, info.context.blockPos).boundingBoxes.map { it.offset(info.context.blockPos) - }.forEach boxes@{ box -> + }.forEach boxes@ { box -> val interpolated = interpolateBox(box, interpolatedProgress, info.breakConfig) if (config.fill) event.renderer.buildFilled(interpolated, fillColor) if (config.outline) event.renderer.buildOutline(interpolated, outlineColor) @@ -266,7 +279,8 @@ object BreakManager : RequestHandler( } listenUnsafe(priority = Int.MIN_VALUE) { - breakInfos.forEach { it?.nullify() } + primaryBreak = null + secondaryBreak = null breakCooldown = 0 } @@ -309,9 +323,8 @@ object BreakManager : RequestHandler( // last break to be started run { if (!handlePreProcessing()) return@run - breakInfos - .filterNotNull() - .filter { it.type != RedundantSecondary && it.updatedThisTick } + activeInfos + .filter { it.updatedThisTick } .asReversed() .forEach { info -> if (info.shouldProgress) @@ -323,7 +336,7 @@ object BreakManager : RequestHandler( if (instantBreaks.isEmpty() && breaks.isEmpty()) { activeRequest = null } - if (breaksThisTick > 0 || breakInfos.any { it != null && it.type != RedundantSecondary }) { + if (breaksThisTick > 0 || activeInfos.isNotEmpty()) { activeThisTick = true } } @@ -379,7 +392,7 @@ object BreakManager : RequestHandler( * @return if the break context can be accepted. */ private fun SafeContext.canAccept(newCtx: BreakContext): Boolean { - if (breakInfos.none { it?.context?.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false + if (activeInfos.none { it.context.blockPos == newCtx.blockPos } && isPosBlocked(newCtx.blockPos)) return false if (!currentStackSelection.filterStack(player.inventory.getStack(newCtx.hotbarIndex))) return false @@ -391,9 +404,8 @@ object BreakManager : RequestHandler( } private fun SafeContext.handlePreProcessing(): Boolean { - breakInfos - .filterNotNull() - .filter { it.type != RedundantSecondary && it.updatedThisTick } + activeInfos + .filter { it.updatedThisTick } .let { infos -> rotationRequest = infos.firstOrNull { info -> info.breakConfig.rotateForBreak } ?.let { info -> @@ -401,8 +413,7 @@ object BreakManager : RequestHandler( rotation.submit(false) } - if (breakInfos.none { it != null && it.type != RedundantSecondary }) { - swapped = false + if (activeInfos.isEmpty()) { swappedStack = player.mainHandStack return true } @@ -412,14 +423,10 @@ object BreakManager : RequestHandler( } infos.firstOrNull()?.let { info -> infos.firstOrNull { it.shouldSwap && it.shouldProgress }?.let { last -> - if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks))) { - swapped = false + if (!info.context.requestSwap(info.request, max(info.minSwapTicks, last.minSwapTicks))) return false - } swappedStack = info.swapStack - swapped = true - info.serverBreakTicks++ - return true + if (info.minSwapTicks > 0) info.serverBreakTicks++ } } } @@ -510,25 +517,21 @@ object BreakManager : RequestHandler( private fun SafeContext.simulateAbandoned() { // Cancelled but double breaking so requires break manager to continue the simulation - breakInfos - .asSequence() - .filterNotNull() - .filter { it.abandoned && it.type != RedundantSecondary } - .forEach { info -> - with(info.request) { - info.context.blockPos - .toStructure(TargetState.Empty) - .toBlueprint() - .simulate(player.eyePos, interact, rotation, inventory, build) - .asSequence() - .filterIsInstance() - .filter { canAccept(it.context) } - .sorted() - .let { sim -> - info.updateInfo(sim.firstOrNull()?.context ?: return@forEach) - } - } + abandonedBreak?.let { abandonedInfo -> + with (abandonedInfo.request) { + abandonedInfo.context.blockPos + .toStructure(TargetState.Empty) + .toBlueprint() + .simulate(player.eyePos, interact, rotation, inventory, build) + .asSequence() + .filterIsInstance() + .filter { canAccept(it.context) } + .sorted() + .let { sim -> + abandonedInfo.updateInfo(sim.firstOrNull()?.context ?: return) + } } + } } private fun checkForCancels() { @@ -605,8 +608,14 @@ object BreakManager : RequestHandler( val cachedState = context.cachedState swapStack = player.inventory.getStack(context.hotbarIndex) - val breakAmount = - cachedState.calcBreakDelta(player, world, context.blockPos, breakConfig, swapStack) * (breakingTicks + 1) + val breakTicks = (breakingTicks + 1 - breakConfig.fudgeFactor).coerceAtLeast(1) + val breakAmount = cachedState.calcBreakDelta( + player, + world, + context.blockPos, + breakConfig, + swapStack + ) * breakTicks val breakAmountNoEfficiency = cachedState.calcBreakDelta( player, world, @@ -614,13 +623,11 @@ object BreakManager : RequestHandler( breakConfig, swapStack, ignoreEfficiency = true - ) * (breakingTicks + 1) + ) * breakTicks - minSwapTicks = if (breakAmount >= getBreakThreshold() || couldReBreak) { - val min = if (breakAmountNoEfficiency >= getBreakThreshold()) 0 else 1 - serverBreakTicks++ - min - } else 0 + minSwapTicks = if ((breakAmount >= getBreakThreshold() || couldReBreak == RebreakManager.RebreakPotential.Instant) && + (breakAmountNoEfficiency < getBreakThreshold() || type == Secondary)) 1 + else 0 } /** @@ -763,7 +770,7 @@ object BreakManager : RequestHandler( } val swing = config.swing - if (overBreakThreshold && (!swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor)) { + if (overBreakThreshold && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1)) { if (info.type == Primary) { onBlockBreak(info) info.stopBreakPacket(world, interaction) @@ -789,7 +796,7 @@ object BreakManager : RequestHandler( private fun SafeContext.startBreaking(info: BreakInfo): Boolean { val ctx = info.context - if (info.couldReBreak) { + if (info.couldReBreak.isPossible()) { when (val rebreakResult = RebreakManager.handleUpdate(info.context, info.request)) { is RebreakResult.StillBreaking -> { primaryBreak = rebreakResult.breakInfo.apply { @@ -837,8 +844,7 @@ object BreakManager : RequestHandler( val breakDelta = blockState.calcBreakDelta(player, world, ctx.blockPos, info.breakConfig) info.vanillaInstantBreakable = breakDelta >= 1 - val serverSwapped = !swapped || info.serverBreakTicks >= info.breakConfig.fudgeFactor - if (notEmpty && (breakDelta >= info.getBreakThreshold() && serverSwapped)) { + if (notEmpty && (breakDelta >= info.getBreakThreshold() && (info.serverBreakTicks >= info.breakConfig.fudgeFactor || info.minSwapTicks < 1))) { onBlockBreak(info) if (!info.vanillaInstantBreakable) breakCooldown = info.breakConfig.breakDelay } else { diff --git a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt index cf0e2099f..79be43605 100644 --- a/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt +++ b/src/main/kotlin/com/lambda/interaction/request/breaking/RebreakManager.kt @@ -35,7 +35,7 @@ object RebreakManager { var rebreak: BreakInfo? = null init { - listen(priority = Int.MIN_VALUE) { + listen(priority = Int.MIN_VALUE) { rebreak?.run { if (!progressedThisTick) { breakingTicks++ @@ -69,13 +69,17 @@ object RebreakManager { val stack = if (info.breakConfig.swapMode.isEnabled()) player.inventory.getStack(info.context.hotbarIndex) else player.mainHandStack - val breakDelta = - info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack) - reBreak.breakConfig.rebreak && + val breakDelta = info.context.cachedState.calcItemBlockBreakingDelta(player, world, info.context.blockPos, stack) + val possible = reBreak.breakConfig.rebreak && info.context.blockPos == reBreak.context.blockPos && - !reBreak.updatedThisTick && - ((reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold) - } == true + !reBreak.updatedThisTick + val instant = (reBreak.breakingTicks - info.breakConfig.fudgeFactor) * breakDelta >= info.breakConfig.breakThreshold + when { + possible && instant -> RebreakPotential.Instant + possible -> RebreakPotential.PartialProgress + else -> RebreakPotential.None + } + } ?: RebreakPotential.None fun handleUpdate(ctx: BreakContext, breakRequest: BreakRequest) = runSafe { @@ -99,4 +103,12 @@ object RebreakManager { RebreakResult.StillBreaking(reBreak) } } + + enum class RebreakPotential { + Instant, + PartialProgress, + None; + + fun isPossible() = this == Instant || this == PartialProgress + } } \ No newline at end of file