From 81e9ad1312aafb74657c55ec4a48eb57d99086fd Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Thu, 14 May 2026 18:52:29 -0400 Subject: [PATCH 1/5] Make escape patterns into proper Actions --- .../api/casting/castables/Action.kt | 26 +++ .../api/casting/eval/SpecialPatterns.java | 1 + .../api/casting/eval/vm/CastingVM.kt | 171 +++--------------- .../api/casting/iota/PatternIota.java | 35 +++- .../casting/actions/escaping/OpCloseParen.kt | 45 +++++ .../casting/actions/escaping/OpEscape.kt | 26 +++ .../casting/actions/escaping/OpOpenParen.kt | 31 ++++ .../common/casting/actions/escaping/OpUndo.kt | 36 ++++ .../hexcasting/common/lib/hex/HexActions.java | 11 +- 9 files changed, 225 insertions(+), 157 deletions(-) create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpEscape.kt create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenParen.kt create mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt index a3963d7640..63ad950f35 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt @@ -1,11 +1,16 @@ package at.petrak.hexcasting.api.casting.castables +import at.petrak.hexcasting.api.casting.eval.CastResult import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.math.HexPattern import at.petrak.hexcasting.api.casting.mishaps.Mishap +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds import net.minecraft.world.phys.Vec3 import java.text.DecimalFormat @@ -41,6 +46,27 @@ interface Action { continuation: SpellContinuation ): OperationResult + /** + * The behavior of this action when inside parentheses. By default, this is just to add the pattern + * to the parenthesized list without updating the op count or performing any of its usual effects. + */ + @Throws(Mishap::class) + fun operateInParens( + env: CastingEnvironment, + image: CastingImage, + continuation: SpellContinuation, + thisIota: Iota, + ): Pair { + val newParens = image.parenthesized.toMutableList() + newParens.add(ParenthesizedIota(thisIota, false)) + return OperationResult( + image.copy(parenthesized = newParens), + listOf(), + continuation, + HexEvalSounds.NORMAL_EXECUTE + ) to ResolvedPatternType.ESCAPED + } + companion object { // I see why vazkii did this: you can't raycast out to infinity! const val RAYCAST_DISTANCE: Double = 32.0 diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/SpecialPatterns.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/SpecialPatterns.java index f87fcfdfae..b50a5552eb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/SpecialPatterns.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/SpecialPatterns.java @@ -3,6 +3,7 @@ import at.petrak.hexcasting.api.casting.math.HexDir; import at.petrak.hexcasting.api.casting.math.HexPattern; +@Deprecated(since = "0.11.4") public final class SpecialPatterns { public static final HexPattern INTROSPECTION = HexPattern.fromAngles("qqq", HexDir.WEST); public static final HexPattern RETROSPECTION = HexPattern.fromAngles("eee", HexDir.EAST); diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt index 3d03a9a843..52d712c50d 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt @@ -109,30 +109,32 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { try { // TODO we can have a special intro/retro sound // ALSO TODO need to add reader macro-style things - try { - this.handleParentheses(iota)?.let { (data, resolutionType) -> - return@executeInner CastResult(iota, continuation, data, listOf(), resolutionType, HexEvalSounds.NORMAL_EXECUTE) + + // Handle single-iota escaping (ie via Consideration) + // This is here rather than in Iota since this behavior should not be overriden. + if (this.image.escapeNext) { + val newImage: CastingImage + if (this.image.parenCount > 0) { + // if we're inside parentheses, add the iota to the list with escaped set to true + val newParens = this.image.parenthesized.toMutableList() + newParens.add(ParenthesizedIota(iota, true)) + newImage = this.image.copy( + escapeNext = false, + parenthesized = newParens + ) + } else { + // if we're not in parentheses, just push the iota to the stack + val newStack = this.image.stack.toMutableList() + newStack.add(iota) + newImage = this.image.copy( + stack = newStack, + escapeNext = false, + ) } - } catch (e: MishapTooManyCloseParens) { - // This is ridiculous and needs to be fixed - return CastResult( - iota, - continuation, - null, - listOf( - OperatorSideEffect.DoMishap( - e, - Mishap.Context( - (iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST), - HexAPI.instance().getRawHookI18n(HexAPI.modLoc("close_paren")) - ) - ) - ), - ResolvedPatternType.ERRORED, - HexEvalSounds.MISHAP - ) + return CastResult(iota, continuation, newImage, listOf(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE) } + // Handle normal behavior (including parens escaping) return iota.execute(this, world, continuation) } catch (exception: Exception) { // This means something very bad has happened @@ -173,133 +175,6 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { return Pair(stackDescs, ravenmind) } - /** - * Return a non-null value if we handled this in some sort of parenthesey way, - * either escaping it onto the stack or changing the parenthese-handling state. - */ - @Throws(MishapTooManyCloseParens::class) - private fun handleParentheses(iota: Iota): Pair? { - val sig = (iota as? PatternIota)?.pattern?.angles - - var displayDepth = this.image.parenCount - - val out = if (displayDepth > 0) { - if (this.image.escapeNext) { - val newParens = this.image.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(iota, true)) - this.image.copy( - escapeNext = false, - parenthesized = newParens - ) to ResolvedPatternType.ESCAPED - } else { - - when (sig) { - SpecialPatterns.CONSIDERATION.angles -> { - this.image.copy( - escapeNext = true, - ) to ResolvedPatternType.EVALUATED - } - - SpecialPatterns.EVANITION.angles -> { - val newParens = this.image.parenthesized.toMutableList() - val last = newParens.removeLastOrNull() - val newParenCount = this.image.parenCount + if (last == null || last.escaped || last.iota !is PatternIota) 0 else when (last.iota.pattern) { - SpecialPatterns.INTROSPECTION -> -1 - SpecialPatterns.RETROSPECTION -> 1 - else -> 0 - } - this.image.copy(parenthesized = newParens, parenCount = newParenCount) to if (last == null) ResolvedPatternType.ERRORED else ResolvedPatternType.UNDONE - } - - SpecialPatterns.INTROSPECTION.angles -> { - // we have escaped the parens onto the stack; we just also record our count. - val newParens = this.image.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(iota, false)) - this.image.copy( - parenthesized = newParens, - parenCount = this.image.parenCount + 1 - ) to if (this.image.parenCount == 0) ResolvedPatternType.EVALUATED else ResolvedPatternType.ESCAPED - } - - SpecialPatterns.RETROSPECTION.angles -> { - val newParenCount = this.image.parenCount - 1 - displayDepth-- - if (newParenCount == 0) { - val newStack = this.image.stack.toMutableList() - newStack.add(ListIota(this.image.parenthesized.toList().map { it.iota })) - this.image.copy( - stack = newStack, - parenCount = newParenCount, - parenthesized = listOf() - ) to ResolvedPatternType.EVALUATED - } else if (newParenCount < 0) { - throw MishapTooManyCloseParens() - } else { - // we have this situation: "(()" - // we need to add the close paren - val newParens = this.image.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(iota, false)) - this.image.copy( - parenCount = newParenCount, - parenthesized = newParens - ) to ResolvedPatternType.ESCAPED - } - } - - else -> { - val newParens = this.image.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(iota, false)) - this.image.copy( - parenthesized = newParens - ) to ResolvedPatternType.ESCAPED - } - } - } - } else if (this.image.escapeNext) { - val newStack = this.image.stack.toMutableList() - newStack.add(iota) - this.image.copy( - stack = newStack, - escapeNext = false, - ) to ResolvedPatternType.ESCAPED - } else { - when (sig) { - SpecialPatterns.CONSIDERATION.angles -> { - this.image.copy( - escapeNext = true - ) to ResolvedPatternType.EVALUATED - } - - SpecialPatterns.INTROSPECTION.angles -> { - this.image.copy( - parenCount = this.image.parenCount + 1 - ) to ResolvedPatternType.EVALUATED - } - - SpecialPatterns.RETROSPECTION.angles -> { - throw MishapTooManyCloseParens() - } - - else -> { - null - } - } - } - - // TODO: replace this once we can read things from the client - /* - if (out != null) { - val display = if (iota is PatternIota) { - PatternNameHelper.representationForPattern(iota.pattern) - .copy() - .withStyle(if (out.second == ResolvedPatternType.ESCAPED) ChatFormatting.YELLOW else ChatFormatting.AQUA) - } else iota.display() - displayPatternDebug(this.escapeNext, displayDepth, display) - } - */ - return out - } - data class TempControllerInfo( var earlyExit: Boolean, ) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java index e3aa1c76f0..f05723d092 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java @@ -5,8 +5,10 @@ import at.petrak.hexcasting.api.casting.PatternShapeMatch; import at.petrak.hexcasting.api.casting.castables.Action; import at.petrak.hexcasting.api.casting.eval.CastResult; +import at.petrak.hexcasting.api.casting.eval.OperationResult; import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType; import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect; +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage; import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation; import at.petrak.hexcasting.api.casting.math.HexPattern; @@ -27,10 +29,12 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -74,6 +78,8 @@ public boolean toleratesOther(Iota that) { var lookup = PatternRegistryManifest.matchPattern(this.getPattern(), vm.getEnv()); vm.getEnv().precheckAction(lookup); + var inParens = vm.getImage().getParenCount() > 0; + Action action; if (lookup instanceof PatternShapeMatch.Normal || lookup instanceof PatternShapeMatch.PerWorld) { ResourceKey key; @@ -101,12 +107,27 @@ public boolean toleratesOther(Iota that) { throw new MishapInvalidPattern(this.getPattern()); } else throw new IllegalStateException(); - // do the actual calculation!! - var result = action.operate( - vm.getEnv(), - vm.getImage(), - continuation - ); + OperationResult result; + ResolvedPatternType resolutionType; + if (inParens) { + // handle parenthetized behavior + var resultAndType = action.operateInParens( + vm.getEnv(), + vm.getImage(), + continuation, + this + ); + result = resultAndType.getFirst(); + resolutionType = resultAndType.getSecond(); + } else { + // do the actual calculation!! + result = action.operate( + vm.getEnv(), + vm.getImage(), + continuation + ); + resolutionType = ResolvedPatternType.EVALUATED; + } var cont2 = result.getNewContinuation(); // TODO parens also break prescience @@ -117,7 +138,7 @@ public boolean toleratesOther(Iota that) { cont2, result.getNewImage(), sideEffects, - ResolvedPatternType.EVALUATED, + resolutionType, result.getSound()); } catch (Mishap mishap) { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt new file mode 100644 index 0000000000..8ee1572866 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt @@ -0,0 +1,45 @@ +package at.petrak.hexcasting.common.casting.actions.escaping + +import at.petrak.hexcasting.api.casting.castables.Action +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.ListIota +import at.petrak.hexcasting.api.casting.mishaps.MishapTooManyCloseParens +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpCloseParen : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + throw MishapTooManyCloseParens() + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { + val newParenCount = image.parenCount - 1 + if (newParenCount == 0) { + val newStack = image.stack.toMutableList() + newStack.add(ListIota(image.parenthesized.toList().map { it.iota })) + val image2 = image.copy( + stack = newStack, + parenCount = newParenCount, + parenthesized = listOf() + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to ResolvedPatternType.EVALUATED + } else if (newParenCount < 0) { + throw MishapTooManyCloseParens() + } else { + // we have this situation: "(()" + // we need to add the close paren + val newParens = image.parenthesized.toMutableList() + newParens.add(ParenthesizedIota(thisIota, false)) + val image2 = image.copy( + parenCount = newParenCount, + parenthesized = newParens + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to ResolvedPatternType.ESCAPED + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpEscape.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpEscape.kt new file mode 100644 index 0000000000..2c3656fb93 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpEscape.kt @@ -0,0 +1,26 @@ +package at.petrak.hexcasting.common.casting.actions.escaping + +import at.petrak.hexcasting.api.casting.castables.Action +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpEscape : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + val image2 = image.copy( + escapeNext = true + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { + val image2 = image.copy( + escapeNext = true + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to ResolvedPatternType.EVALUATED + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenParen.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenParen.kt new file mode 100644 index 0000000000..a0c826087b --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenParen.kt @@ -0,0 +1,31 @@ +package at.petrak.hexcasting.common.casting.actions.escaping + +import at.petrak.hexcasting.api.casting.castables.Action +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpOpenParen : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + val image2 = image.copy( + parenCount = image.parenCount + 1 + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { + // we have escaped the parens onto the stack; we just also record our count. + val newParens = image.parenthesized.toMutableList() + newParens.add(CastingImage.ParenthesizedIota(thisIota, false)) + val image2 = image.copy( + parenthesized = newParens, + parenCount = image.parenCount + 1 + ) + val resolutionType = if (image.parenCount == 0) ResolvedPatternType.EVALUATED else ResolvedPatternType.ESCAPED + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to resolutionType + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt new file mode 100644 index 0000000000..0565f91f61 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt @@ -0,0 +1,36 @@ +package at.petrak.hexcasting.common.casting.actions.escaping + +import at.petrak.hexcasting.api.casting.castables.Action +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.PatternIota +import at.petrak.hexcasting.api.casting.mishaps.MishapTooManyCloseParens +import at.petrak.hexcasting.common.lib.hex.HexActions +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpUndo : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + // TODO: this should have its own mishap + throw MishapTooManyCloseParens() + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { + val newParens = image.parenthesized.toMutableList() + val last = newParens.removeLastOrNull() + val newParenCount = image.parenCount + if (last == null || last.escaped || last.iota !is PatternIota) 0 else when (last.iota.pattern.angles) { + HexActions.OPEN_PAREN.prototype.angles -> -1 + HexActions.CLOSE_PAREN.prototype.angles -> 1 + else -> 0 + } + val image2 = image.copy( + parenthesized = newParens, + parenCount = newParenCount + ) + val resolutionType = if (last == null) ResolvedPatternType.ERRORED else ResolvedPatternType.UNDONE + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to resolutionType + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java index 62dabec81f..95918ef011 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexActions.java @@ -16,6 +16,7 @@ import at.petrak.hexcasting.common.casting.actions.circles.OpCircleBounds; import at.petrak.hexcasting.common.casting.actions.circles.OpImpetusDir; import at.petrak.hexcasting.common.casting.actions.circles.OpImpetusPos; +import at.petrak.hexcasting.common.casting.actions.escaping.*; import at.petrak.hexcasting.common.casting.actions.eval.*; import at.petrak.hexcasting.common.casting.actions.lists.OpEmptyList; import at.petrak.hexcasting.common.casting.actions.lists.OpLastNToList; @@ -382,8 +383,14 @@ public class HexActions { // == Meta stuff == - // Intro/Retro/Consideration are now special-form-likes and aren't even ops. - // TODO should there be a registry for these too + public static final ActionRegistryEntry ESCAPE = make("escape", + new ActionRegistryEntry(HexPattern.fromAngles("qqqaw", HexDir.WEST), OpEscape.INSTANCE)); + public static final ActionRegistryEntry OPEN_PAREN = make("open_paren", + new ActionRegistryEntry(HexPattern.fromAngles("qqq", HexDir.WEST), OpOpenParen.INSTANCE)); + public static final ActionRegistryEntry CLOSE_PAREN = make("close_paren", + new ActionRegistryEntry(HexPattern.fromAngles("eee", HexDir.EAST), OpCloseParen.INSTANCE)); + public static final ActionRegistryEntry UNDO = make("undo", + new ActionRegistryEntry(HexPattern.fromAngles("eeedw", HexDir.EAST), OpUndo.INSTANCE)); // http://www.toroidalsnark.net/mkss3-pix/CalderheadJMM2014.pdf // eval being a space filling curve feels apt doesn't it From 8f2d934f9efc502bf31175249835ee79b4180df4 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Wed, 13 May 2026 21:44:38 -0400 Subject: [PATCH 2/5] Fix iota embedding and invalid patterns in intro/retro --- .../api/casting/castables/Action.kt | 4 +- .../api/casting/eval/vm/CastingImage.kt | 10 +++++ .../hexcasting/api/casting/iota/Iota.java | 40 ++++++++++++------- .../api/casting/iota/PatternIota.java | 14 ++++++- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt index 63ad950f35..631fdd1d06 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt @@ -57,10 +57,8 @@ interface Action { continuation: SpellContinuation, thisIota: Iota, ): Pair { - val newParens = image.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(thisIota, false)) return OperationResult( - image.copy(parenthesized = newParens), + image.withNewParenthesized(thisIota, false), listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt index cf887a117b..66688c8276 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt @@ -72,6 +72,16 @@ data class CastingImage private constructor( */ fun withResetEscape() = this.copy(parenCount = 0, parenthesized = listOf(), escapeNext = false) + /** + * Returns a copy of this with the provided iota added to the parenthetized list. + * [escaped] determines whether the iota should be considered escaped or not. + */ + fun withNewParenthesized(iota: Iota, escaped: Boolean): CastingImage { + val newParens = this.parenthesized.toMutableList() + newParens.add(ParenthesizedIota(iota, escaped)) + return this.copy(parenthesized = newParens) + } + fun serializeToNbt() = NBTBuilder { TAG_STACK %= stack.serializeToNBT() diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java index cad6baee82..154b5644d5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java @@ -49,22 +49,34 @@ protected Iota(@NotNull IotaType type, @NotNull Object payload) { abstract public @NotNull Tag serialize(); /** - * This method is called when this iota is executed (i.e. Hermes is run on a list containing it, unescaped). - * By default it will return a {@link CastResult} indicating an error has occurred. + * This method is called when this iota is executed (i.e. Hermes is run on a list containing it). + * By default, if the iota is not within parentheses, it will return a {@link CastResult} indicating an error has occurred. + * If the iota *is* within parentheses, it will instead be added to the parenthesized list. + * Iotas can also be escaped using Consideration, but that's handled in {@link CastingVM#executeInner}. */ public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) { - return new CastResult( - this, - continuation, - null, // Should never matter - List.of( - new OperatorSideEffect.DoMishap( - new MishapUnescapedValue(this), - new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null) - ) - ), - ResolvedPatternType.INVALID, - HexEvalSounds.MISHAP); + if (vm.getImage().getParenCount() > 0) { + return new CastResult( + this, + continuation, + vm.getImage().withNewParenthesized(this, false), + List.of(), + ResolvedPatternType.ESCAPED, + HexEvalSounds.NORMAL_EXECUTE); + } else { + return new CastResult( + this, + continuation, + null, // Should never matter + List.of( + new OperatorSideEffect.DoMishap( + new MishapUnescapedValue(this), + new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null) + ) + ), + ResolvedPatternType.INVALID, + HexEvalSounds.MISHAP); + } } /** diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java index f05723d092..98ca1114cc 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java @@ -104,7 +104,19 @@ public boolean toleratesOther(Iota that) { castedName = special.handler::getName; action = special.handler.act(); } else if (lookup instanceof PatternShapeMatch.Nothing) { - throw new MishapInvalidPattern(this.getPattern()); + if (inParens) { + // invalid patterns still get added to the list when in parens + return new CastResult( + this, + continuation, + vm.getImage().withNewParenthesized(this, false), + List.of(), + ResolvedPatternType.ESCAPED, + HexEvalSounds.NORMAL_EXECUTE); + } else { + // if you draw something invalid outside parens, it mishaps + throw new MishapInvalidPattern(this.getPattern()); + } } else throw new IllegalStateException(); OperationResult result; From fe0fe44d51da60da857ac6771b67ad62395ab999 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Thu, 14 May 2026 18:55:40 -0400 Subject: [PATCH 3/5] Update lang and mishap description --- .../java/at/petrak/hexcasting/api/HexAPI.java | 5 +-- ...anyCloseParens.kt => MishapNeedsParens.kt} | 4 +- .../casting/actions/escaping/OpCloseParen.kt | 6 +-- .../common/casting/actions/escaping/OpUndo.kt | 6 +-- .../hexcasting/lang/en_us.flatten.json5 | 18 ++++----- .../en_us/entries/casting/mishaps.json | 4 +- .../entries/patterns/patterns_as_iotas.json | 40 ++++++------------- 7 files changed, 32 insertions(+), 51 deletions(-) rename Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/{MishapTooManyCloseParens.kt => MishapNeedsParens.kt} (90%) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java index 45af9fee79..4009d12fd5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java @@ -62,9 +62,7 @@ default String getSpecialHandlerI18nKey(ResourceKey> a return "hexcasting.special.%s".formatted(action.location().toString()); } - /** - * Currently introspection/retrospection/consideration are hardcoded, but at least their names won't be - */ + @Deprecated(since = "0.11.4") default String getRawHookI18nKey(ResourceLocation name) { return "hexcasting.rawhook.%s".formatted(name); } @@ -79,6 +77,7 @@ default Component getSpecialHandlerI18n(ResourceKey> k .withStyle(ChatFormatting.LIGHT_PURPLE); } + @Deprecated(since = "0.11.4") default Component getRawHookI18n(ResourceLocation name) { return Component.translatable(getRawHookI18nKey(name)).withStyle(ChatFormatting.LIGHT_PURPLE); } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapTooManyCloseParens.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapNeedsParens.kt similarity index 90% rename from Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapTooManyCloseParens.kt rename to Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapNeedsParens.kt index dfa159bda7..e6ec2a5053 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapTooManyCloseParens.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapNeedsParens.kt @@ -6,7 +6,7 @@ import at.petrak.hexcasting.api.casting.iota.PatternIota import at.petrak.hexcasting.api.pigment.FrozenPigment import net.minecraft.world.item.DyeColor -class MishapTooManyCloseParens : Mishap() { +class MishapNeedsParens : Mishap() { override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment = dyeColor(DyeColor.ORANGE) @@ -17,5 +17,5 @@ class MishapTooManyCloseParens : Mishap() { } override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = - error("too_many_close_parens") + error("needs_parens") } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt index 8ee1572866..18ed2163df 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseParen.kt @@ -9,12 +9,12 @@ import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.ListIota -import at.petrak.hexcasting.api.casting.mishaps.MishapTooManyCloseParens +import at.petrak.hexcasting.api.casting.mishaps.MishapNeedsParens import at.petrak.hexcasting.common.lib.hex.HexEvalSounds object OpCloseParen : Action { override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { - throw MishapTooManyCloseParens() + throw MishapNeedsParens() } override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { @@ -29,7 +29,7 @@ object OpCloseParen : Action { ) return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to ResolvedPatternType.EVALUATED } else if (newParenCount < 0) { - throw MishapTooManyCloseParens() + throw MishapNeedsParens() } else { // we have this situation: "(()" // we need to add the close paren diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt index 0565f91f61..30f92efe08 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpUndo.kt @@ -8,14 +8,13 @@ import at.petrak.hexcasting.api.casting.eval.vm.CastingImage import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.PatternIota -import at.petrak.hexcasting.api.casting.mishaps.MishapTooManyCloseParens +import at.petrak.hexcasting.api.casting.mishaps.MishapNeedsParens import at.petrak.hexcasting.common.lib.hex.HexActions import at.petrak.hexcasting.common.lib.hex.HexEvalSounds object OpUndo : Action { override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { - // TODO: this should have its own mishap - throw MishapTooManyCloseParens() + throw MishapNeedsParens() } override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): Pair { @@ -30,6 +29,7 @@ object OpUndo : Action { parenthesized = newParens, parenCount = newParenCount ) + // TODO: this should properly mishap if there was nothing to remove val resolutionType = if (last == null) ResolvedPatternType.ERRORED else ResolvedPatternType.UNDONE return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) to resolutionType } diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index dd39d0e0c5..54c4042942 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -915,6 +915,11 @@ create_lava: "Create Lava", "teleport/great": "Greater Teleport", brainsweep: "Flay Mind", + + escape: "Consideration", + open_paren: "Introspection", + close_paren: "Retrospection", + undo: "Evanition", eval: "Hermes' Gambit", "eval/cc": "Iris' Gambit", @@ -986,13 +991,6 @@ number: "Numerical Reflection: %s", mask: "Bookkeeper's Gambit: %s", }, - - "rawhook.hexcasting:": { - open_paren: "Introspection", - close_paren: "Retrospection", - escape: "Consideration", - undo: "Evanition", - }, "iota.hexcasting:": { null: { @@ -1042,7 +1040,7 @@ not_enough_args: "expected %s or more arguments but the stack was only %s tall", no_args: "expected %s or more arguments but the stack was empty", - too_many_close_parens: "Did not first use Introspection", + needs_parens: "Did not first use Introspection", wrong_dimension: "cannot see %s from %s", entity_too_far: "%s is out of range", @@ -1509,8 +1507,8 @@ "incorrect_block.title": "Incorrect Block", incorrect_block: "The action requires some sort of block at a target location, but the block supplied was not suitable.$(br2)Causes bright green sparks, and causes an ephemeral explosion at the given location. The explosion doesn't seem to harm me, the world, or anything else though; it's just startling.", - "retrospection.title": "Hasty Retrospection", - retrospection: "I attempted to draw $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ without first drawing $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$.$(br2)Causes orange sparks, and pushes the pattern for $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ to the stack as a pattern iota.", + "needs_parens.title": "Absent Introspection", + needs_parens: "I attempted to draw $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ or $(l:patterns/patterns_as_iotas#hexcasting:undo)$(action)Evanition/$ without first drawing $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$.$(br2)Causes orange sparks, and pushes the pattern I tried to draw to the stack as an iota.", "too_many_patterns.title": "Lost in Thought", too_many_patterns: "I attempted to evaluate too many patterns in one _Hex. Often, this happens because I've accidentally created an infinite loop.$(br2)Causes dark blue sparks, and chokes all the air out of me.", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json index 5a01a51556..45fdc0aa70 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/casting/mishaps.json @@ -70,8 +70,8 @@ }, { "type": "patchouli:text", - "title": "hexcasting.page.mishaps.retrospection.title", - "text": "hexcasting.page.mishaps.retrospection" + "title": "hexcasting.page.mishaps.needs_parens.title", + "text": "hexcasting.page.mishaps.needs_parens" }, { "type": "patchouli:text", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/patterns_as_iotas.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/patterns_as_iotas.json index 1f8cc778af..0d7e834aeb 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/patterns_as_iotas.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/patterns_as_iotas.json @@ -15,48 +15,32 @@ "text": "hexcasting.page.patterns_as_iotas.2" }, { - "type": "hexcasting:manual_pattern", - "header": "hexcasting.rawhook.hexcasting:escape", + "type": "hexcasting:pattern", + "op_id": "hexcasting:escape", "anchor": "hexcasting:escape", - "text": "hexcasting.page.patterns_as_iotas.escape.1", - "patterns": { - "startdir": "WEST", - "signature": "qqqaw" - } + "text": "hexcasting.page.patterns_as_iotas.escape.1" }, { "type": "patchouli:text", "text": "hexcasting.page.patterns_as_iotas.escape.2" }, { - "type": "hexcasting:manual_pattern", - "header": "hexcasting.rawhook.hexcasting:open_paren", + "type": "hexcasting:pattern", + "op_id": "hexcasting:open_paren", "anchor": "hexcasting:open_paren", - "text": "hexcasting.page.patterns_as_iotas.parens.1", - "patterns": { - "startdir": "WEST", - "signature": "qqq" - } + "text": "hexcasting.page.patterns_as_iotas.parens.1" }, { - "type": "hexcasting:manual_pattern", - "header": "hexcasting.rawhook.hexcasting:close_paren", + "type": "hexcasting:pattern", + "op_id": "hexcasting:close_paren", "anchor": "hexcasting:close_paren", - "text": "hexcasting.page.patterns_as_iotas.parens.2", - "patterns": { - "startdir": "EAST", - "signature": "eee" - } + "text": "hexcasting.page.patterns_as_iotas.parens.2" }, { - "type": "hexcasting:manual_pattern", - "header": "hexcasting.rawhook.hexcasting:undo", + "type": "hexcasting:pattern", + "op_id": "hexcasting:undo", "anchor": "hexcasting:undo", - "text": "hexcasting.page.patterns_as_iotas.undo", - "patterns": { - "startdir": "EAST", - "signature": "eeedw" - } + "text": "hexcasting.page.patterns_as_iotas.undo" }, { "type": "patchouli:text", From 6b605c90fe1c908c6a6826f20b808b26002c406f Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Thu, 14 May 2026 00:13:14 -0400 Subject: [PATCH 4/5] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c834cd67..1efa67d444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Internal - The mod now uses Fabric Loom 1.9, Gradle 8.11, and Kotlin 2.0.20, by Robotgiggle in [#1043](https://github.com/FallingColors/HexMod/pull/1043). +- Added a method in `Action` to define behavior when inside parentheses, and de-hardcoded the iota-escaping patterns, by Robotgiggle in [#1047](https://github.com/FallingColors/HexMod/pull/1047). - Updated Inline dependency from 1.0.1 to 1.2.2, by Robotgiggle in [#1043](https://github.com/FallingColors/HexMod/pull/1043). ## `0.11.3` - 2025-11-22 From 4bfb4272568ab95f4f57dcbb0b108ba21f190788 Mon Sep 17 00:00:00 2001 From: Robotgiggle <88736742+Robotgiggle@users.noreply.github.com> Date: Thu, 14 May 2026 20:25:50 -0400 Subject: [PATCH 5/5] Add Iota.executeInParens to handle parens behavior --- .../api/casting/eval/vm/CastingVM.kt | 9 +++- .../hexcasting/api/casting/iota/Iota.java | 43 ++++++++-------- .../api/casting/iota/PatternIota.java | 49 ++++++++++++------- 3 files changed, 61 insertions(+), 40 deletions(-) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt index 52d712c50d..4daa133881 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt @@ -134,8 +134,13 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { return CastResult(iota, continuation, newImage, listOf(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE) } - // Handle normal behavior (including parens escaping) - return iota.execute(this, world, continuation) + if (this.image.parenCount > 0) { + // Handle parens escaping + return iota.executeInParens(this, world, continuation) + } else { + // Handle normal execution behavior + return iota.execute(this, world, continuation) + } } catch (exception: Exception) { // This means something very bad has happened exception.printStackTrace() diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java index 154b5644d5..659c4f0d47 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/Iota.java @@ -49,34 +49,37 @@ protected Iota(@NotNull IotaType type, @NotNull Object payload) { abstract public @NotNull Tag serialize(); /** - * This method is called when this iota is executed (i.e. Hermes is run on a list containing it). - * By default, if the iota is not within parentheses, it will return a {@link CastResult} indicating an error has occurred. - * If the iota *is* within parentheses, it will instead be added to the parenthesized list. - * Iotas can also be escaped using Consideration, but that's handled in {@link CastingVM#executeInner}. + * This method is called when this iota is directly executed (i.e. Hermes is run on a list containing it, unescaped). + * By default, it will return a {@link CastResult} indicating an error has occurred. */ public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) { - if (vm.getImage().getParenCount() > 0) { - return new CastResult( + return new CastResult( + this, + continuation, + null, // Should never matter + List.of( + new OperatorSideEffect.DoMishap( + new MishapUnescapedValue(this), + new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null) + ) + ), + ResolvedPatternType.INVALID, + HexEvalSounds.MISHAP); + } + + /** + * This method is called when this iota is executed inside parentheses (i.e. Hermes is run on a list containing Introspection, this, Retrospection). + * By default, the iota will be added to the in-progress parenthesized list instead of causing a mishap. + * This is specifically for parentheses-based escaping, Consideration escaping is handled in {@link CastingVM#executeInner}. + */ + public @NotNull CastResult executeInParens(CastingVM vm, ServerLevel world, SpellContinuation continuation) { + return new CastResult( this, continuation, vm.getImage().withNewParenthesized(this, false), List.of(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE); - } else { - return new CastResult( - this, - continuation, - null, // Should never matter - List.of( - new OperatorSideEffect.DoMishap( - new MishapUnescapedValue(this), - new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null) - ) - ), - ResolvedPatternType.INVALID, - HexEvalSounds.MISHAP); - } } /** diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java index 98ca1114cc..bfb2182234 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/iota/PatternIota.java @@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.casting.PatternShapeMatch; import at.petrak.hexcasting.api.casting.castables.Action; import at.petrak.hexcasting.api.casting.eval.CastResult; +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment; import at.petrak.hexcasting.api.casting.eval.OperationResult; import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType; import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect; @@ -13,7 +14,6 @@ import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation; import at.petrak.hexcasting.api.casting.math.HexPattern; import at.petrak.hexcasting.api.casting.mishaps.Mishap; -import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooMuch; import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidPattern; import at.petrak.hexcasting.api.casting.mishaps.MishapUnenlightened; import at.petrak.hexcasting.api.mod.HexTags; @@ -29,12 +29,10 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Supplier; @@ -73,13 +71,33 @@ public boolean toleratesOther(Iota that) { @Override public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) { + return lookupAndOperate(vm, world, continuation, false); + } + + @Override + public @NotNull CastResult executeInParens(CastingVM vm, ServerLevel world, SpellContinuation continuation) { + return lookupAndOperate(vm, world, continuation, true); + } + + @Override + public boolean executable() { + return true; + } + + /** + * Look up this iota's pattern in the action registry, and attempt to invoke the behavior of the resulting Action. + * If {@code inParens} is false, the {@link Action#operate} method is used, and patterns lacking a matching Action will mishap. + * If {@code inParens} is true, the {@link Action#operateInParens} method is used instead, and patterns lacking a matching Action + * will be added to the in-progress parenthesized list as usual. + * In either case, any mishaps thrown during the lookup or operation process will be caught, and an appropriate + * CastResult will be returned. + */ + private @NotNull CastResult lookupAndOperate(CastingVM vm, ServerLevel world, SpellContinuation continuation, boolean inParens) { Supplier<@Nullable Component> castedName = () -> null; try { var lookup = PatternRegistryManifest.matchPattern(this.getPattern(), vm.getEnv()); vm.getEnv().precheckAction(lookup); - var inParens = vm.getImage().getParenCount() > 0; - Action action; if (lookup instanceof PatternShapeMatch.Normal || lookup instanceof PatternShapeMatch.PerWorld) { ResourceKey key; @@ -91,7 +109,7 @@ public boolean toleratesOther(Iota that) { } var reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), key, - HexTags.Actions.REQUIRES_ENLIGHTENMENT); + HexTags.Actions.REQUIRES_ENLIGHTENMENT); castedName = () -> HexAPI.instance().getActionI18n(key, reqsEnlightenment); action = Objects.requireNonNull(IXplatAbstractions.INSTANCE.getActionRegistry().get(key)).action(); @@ -124,19 +142,19 @@ public boolean toleratesOther(Iota that) { if (inParens) { // handle parenthetized behavior var resultAndType = action.operateInParens( - vm.getEnv(), - vm.getImage(), - continuation, - this + vm.getEnv(), + vm.getImage(), + continuation, + this ); result = resultAndType.getFirst(); resolutionType = resultAndType.getSecond(); } else { // do the actual calculation!! result = action.operate( - vm.getEnv(), - vm.getImage(), - continuation + vm.getEnv(), + vm.getImage(), + continuation ); resolutionType = ResolvedPatternType.EVALUATED; } @@ -164,11 +182,6 @@ public boolean toleratesOther(Iota that) { } } - @Override - public boolean executable() { - return true; - } - public static IotaType TYPE = new IotaType<>() { @Override public PatternIota deserialize(Tag tag, ServerLevel world) throws IllegalArgumentException {