diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a88d65fb9..e2d2a1e34e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110 - Added Simulate, which causes the next pattern drawn to be simulated (to check for mishaps) rather than executed ([#1194](https://github.com/FallingColors/HexMod/pull/1194)) @Robotgiggle - Added the `hex_unbreakable` tag for blocks that should be immune to Break Block regardless of the configured mining tier ([#1186](https://github.com/FallingColors/HexMod/pull/1186)) @Robotgiggle @slava110 +- Added a new Ancient Cypher hex that impulses nearby items towards the caster ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid + +### Changed + +- Reworked Thoth's Gambit in a variety of ways ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid + - The argument order now takes the data list first and then the loop body + - The loop body can now be either a list or a single executable iota + - The pattern itself is now a special handler with a variable tail length + - The tail length determines how many extra iotas from the original stack are included in each iteration's stack + - Iotas included via the above process are now popped from the original stack ### Fixed diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt index 3ef9c443f4..cbb030d350 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameForEach.kt @@ -10,32 +10,29 @@ import at.petrak.hexcasting.common.lib.hex.HexEvalSounds import com.mojang.serialization.MapCodec import com.mojang.serialization.codecs.RecordCodecBuilder import net.minecraft.network.RegistryFriendlyByteBuf -import net.minecraft.network.codec.ByteBufCodecs import net.minecraft.network.codec.StreamCodec import net.minecraft.server.level.ServerLevel -import java.util.* -import kotlin.jvm.optionals.getOrNull /** * A frame representing all the state for a Thoth evaluation. - * Pushed by an OpForEach. - * @property first whether the input stack state is the first one (since we don't want to save the base-stack before any changes are made) + * Pushed by SpecialHandlerForEach. * @property data list of *remaining* datums to ForEach over * @property code code to run per datum - * @property baseStack the stack state at Thoth entry - * @property acc concatenated list of final stack states after Thoth exit + * @property contextStack the stack state used for each iteration + * @property stashedStack the stack state to restore after all iterations finish + * @property acc concatenated list of final stack states after each iteration */ data class FrameForEach( val data: TreeList, val code: TreeList, - val baseStack: TreeList?, + val contextStack: TreeList, + val stashedStack: TreeList, val acc: TreeList ) : ContinuationFrame { - /** When halting, we add the stack state at halt to the stack accumulator, then return the original pre-Thoth stack, plus the accumulator. */ + /** When halting, we add the stack state at halt to the stack accumulator, then return the stashed stack, plus the accumulator. */ override fun breakDownwards(stack: TreeList): Pair> { - val newStack = baseStack ?: TreeList.empty() - return true to newStack.appended(ListIota(acc.appendedAll(stack))) + return true to stashedStack.appended(ListIota(acc.appendedAll(stack))) } /** Step the Thoth computation, enqueueing one code evaluation. */ @@ -44,41 +41,36 @@ data class FrameForEach( level: ServerLevel, harness: CastingVM ): CastResult { - // If this is the very first Thoth step (i.e. no Thoth computations run yet)... - val (stack, newAcc) = if (baseStack == null) { - // init stack to the harness stack... - harness.image.stack to acc - } else { - // else save the stack to the accumulator and reuse the saved base stack. - baseStack to acc.appendedAll(harness.image.stack) - } + // Save the stack to the accumulator. On the first iteration, the stack will be empty. + val newAcc = acc.appendedAll(harness.image.stack) // If we still have data to process... - val (stackTop, newImage, newCont) = if (!data.isEmpty()) { - // push the next datum to the top of the stack, + val (newStack, newImage, newCont) = if (!data.isEmpty()) { + // Create a stack composed of the context stack plus the next datum + val stack = contextStack.appended(data.head()) val cont2 = continuation // put the next Thoth object back on the stack for the next Thoth cycle, - .pushFrame(FrameForEach(data.tail(), code, stack, newAcc)) + .pushFrame(FrameForEach(data.tail(), code, contextStack, stashedStack, newAcc)) // and prep the Thoth'd code block for evaluation. .pushFrame(FrameEvaluate(code, true)) - Triple(data.head(), harness.image.withUsedOp(), cont2) + Triple(stack, harness.image.withUsedOp(), cont2) } else { - // Else, dump our final list onto the stack. - Triple(ListIota(newAcc), harness.image, continuation) + // Else, restore the stashed stack, and dump our final list on top. + val stack = stashedStack.appended(ListIota(newAcc)) + Triple(stack, harness.image, continuation) } - val tStack = stack.appended(stackTop) return CastResult( ListIota(code), newCont, // reset escapes so they don't carry over to other iterations or out of thoth - newImage.withResetEscape().copy(stack = tStack), + newImage.withResetEscape().copy(stack = newStack), listOf(), ResolvedPatternType.EVALUATED, HexEvalSounds.THOTH, ) } - override fun size() = data.size + code.size + acc.size + (baseStack?.size ?: 0) + override fun size() = data.size + code.size + acc.size + contextStack.size + stashedStack.size override val type: ContinuationFrame.Type<*> = TYPE @@ -89,21 +81,21 @@ data class FrameForEach( inst.group( TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("data").forGetter { it.data }, TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("code").forGetter { it.code }, - TreeList.codecOf(IotaType.TYPED_CODEC).optionalFieldOf("base").forGetter { Optional.ofNullable(it.baseStack) }, + TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("context").forGetter { it.contextStack }, + TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("stashed").forGetter { it.stashedStack }, TreeList.codecOf(IotaType.TYPED_CODEC).fieldOf("accumulator").forGetter { it.acc } - ).apply(inst) { a, b, c, d -> - FrameForEach(a, b, c.getOrNull(), d) + ).apply(inst) { a, b, c, d, e -> + FrameForEach(a, b, c, d, e) } } val STREAM_CODEC = StreamCodec.composite( IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::data, IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::code, - ByteBufCodecs.optional(IotaType.TYPED_STREAM_CODEC - .apply(TreeList.streamCodecOp())), { Optional.ofNullable(it.baseStack) }, - IotaType.TYPED_STREAM_CODEC - .apply(TreeList.streamCodecOp()), FrameForEach::acc - ) { a, b, c, d -> - FrameForEach(a, b, c.getOrNull(), d) + IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::contextStack, + IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::stashedStack, + IotaType.TYPED_STREAM_CODEC.apply(TreeList.streamCodecOp()), FrameForEach::acc + ) { a, b, c, d, e -> + FrameForEach(a, b, c, d, e) } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/OpForEach.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/OpForEach.kt deleted file mode 100644 index e3100fb1bf..0000000000 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/OpForEach.kt +++ /dev/null @@ -1,30 +0,0 @@ -package at.petrak.hexcasting.common.casting.actions.eval - -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.vm.CastingImage -import at.petrak.hexcasting.api.casting.eval.vm.FrameForEach -import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation -import at.petrak.hexcasting.api.casting.getList -import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs -import at.petrak.hexcasting.api.utils.TreeList -import at.petrak.hexcasting.common.lib.hex.HexEvalSounds - -object OpForEach : Action { - override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { - val stack = image.stack - - if (stack.size < 2) - throw MishapNotEnoughArgs(2, stack.size) - - val instrs = stack.getList(stack.lastIndex - 1, stack.size) - val datums = stack.getList(stack.lastIndex, stack.size) - val newStack = stack.dropRight(2) - - val frame = FrameForEach(datums, instrs, null, TreeList.empty()) - val image2 = image.withUsedOp().copy(stack = newStack) - - return OperationResult(image2, listOf(), continuation.pushFrame(frame), HexEvalSounds.THOTH) - } -} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/SpecialHandlerForEach.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/SpecialHandlerForEach.kt new file mode 100644 index 0000000000..d4f788acbb --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/SpecialHandlerForEach.kt @@ -0,0 +1,75 @@ +package at.petrak.hexcasting.common.casting.actions.eval + +import at.petrak.hexcasting.api.HexAPI +import at.petrak.hexcasting.api.casting.castables.Action +import at.petrak.hexcasting.api.casting.castables.SpecialHandler +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.OperationResult +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.FrameForEach +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.evaluatable +import at.petrak.hexcasting.api.casting.getList +import at.petrak.hexcasting.api.casting.math.HexPattern +import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs +import at.petrak.hexcasting.api.utils.TreeList +import at.petrak.hexcasting.api.utils.asTranslatedComponent +import at.petrak.hexcasting.api.utils.lightPurple +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds +import at.petrak.hexcasting.common.lib.hex.HexSpecialHandlers +import at.petrak.hexcasting.xplat.IXplatAbstractions +import net.minecraft.network.chat.Component + +class SpecialHandlerForEach(val n: Int) : SpecialHandler { + override fun act(): Action { + return InnerAction(n) + } + + override fun getName(): Component { + val key = IXplatAbstractions.INSTANCE.specialHandlerRegistry.getResourceKey(HexSpecialHandlers.FOR_EACH).get() + return HexAPI.instance().getSpecialHandlerI18nKey(key) + .asTranslatedComponent(n.toString()).lightPurple + } + + class InnerAction(val n: Int) : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + var stack = image.stack + + if (stack.size < 2 + n) + throw MishapNotEnoughArgs(2 + n, stack.size) + + val datums = stack.getList(stack.lastIndex - 1, stack.size) + val instrs = evaluatable(stack[stack.lastIndex], 0) + stack = stack.dropRight(2) + + val instrList = instrs.map({ TreeList.from(listOf(it)) }, { it }) + + val contextStack = stack.takeRight(n) + val stashedStack = stack.dropRight(n) + + val frame = FrameForEach(datums, instrList, contextStack, stashedStack, TreeList.empty()) + val image2 = image.withUsedOp().copy(stack = TreeList.empty()) + + return OperationResult(image2, listOf(), continuation.pushFrame(frame), HexEvalSounds.THOTH) + } + } + + class Factory : SpecialHandler.Factory { + override fun tryMatch(pat: HexPattern, env: CastingEnvironment): SpecialHandlerForEach? { + val sig = pat.anglesSignature() + if (!sig.startsWith("waaddw")) return null + + val tail = sig.substring(6) + if (tail.length % 2 != 0) return null + + for ((index, segment) in tail.chunked(2).withIndex()) { + when (index % 2) { + 0 -> if (segment != "da") return null + 1 -> if (segment != "ad") return null + } + } + + return SpecialHandlerForEach(tail.length / 2) + } + } +} 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 7feab39471..356fefe37c 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 @@ -546,8 +546,6 @@ public class HexActions { // new ActionRegistryEntry(HexPattern.fromAngles("qaeaq", HexDir.NORTH_WEST), OpConcat.INSTANCE)); public static final ActionRegistryEntry INDEX = make("index", new OperationAction(HexPattern.fromAngles("deeed", HexDir.NORTH_WEST))); - public static final ActionRegistryEntry FOR_EACH = make("for_each", - new ActionRegistryEntry(HexPattern.fromAngles("dadad", HexDir.NORTH_EAST), OpForEach.INSTANCE)); // public static final ActionRegistryEntry LIST_SIZE = make("list_size", // new ActionRegistryEntry(HexPattern.fromAngles("aqaeaq", HexDir.EAST), OpListSize.INSTANCE)); public static final ActionRegistryEntry SINGLETON = make("singleton", diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexSpecialHandlers.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexSpecialHandlers.java index c69fb9363c..150045cc46 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexSpecialHandlers.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/hex/HexSpecialHandlers.java @@ -1,6 +1,7 @@ package at.petrak.hexcasting.common.lib.hex; import at.petrak.hexcasting.api.casting.castables.SpecialHandler; +import at.petrak.hexcasting.common.casting.actions.eval.SpecialHandlerForEach; import at.petrak.hexcasting.common.casting.actions.math.SpecialHandlerNumberLiteral; import at.petrak.hexcasting.common.casting.actions.stack.SpecialHandlerMask; import net.minecraft.resources.ResourceLocation; @@ -18,6 +19,8 @@ public class HexSpecialHandlers { new SpecialHandlerNumberLiteral.Factory()); public static final SpecialHandler.Factory MASK = make("mask", new SpecialHandlerMask.Factory()); + public static final SpecialHandler.Factory FOR_EACH = make("for_each", + new SpecialHandlerForEach.Factory()); private static SpecialHandler.Factory make(String name, SpecialHandler.Factory handler) { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java index 82585e1a51..729b19aeca 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/loot/AddHexToAncientCypherFunc.java @@ -75,12 +75,13 @@ public LootItemFunctionType getType() { new Pair<>("hexcasting.loot_hex.ascend", new String[] {"NORTH_EAST qaq","SOUTH_EAST aqaae","WEST qqqqqawwawawd"}), new Pair<>("hexcasting.loot_hex.blink", new String[] {"NORTH_EAST qaq","EAST aadaa","EAST aa","NORTH_EAST qaq","NORTH_EAST wa","EAST wqaawdd","NORTH_EAST qaq","EAST aa","NORTH_WEST wddw","NORTH_EAST wqaqw","SOUTH_EAST aqaaw","NORTH_WEST wddw","SOUTH_WEST awqqqwaq"}), new Pair<>("hexcasting.loot_hex.blastoff", new String[] {"NORTH_EAST qaq","NORTH_WEST qqqqqew","SOUTH_EAST aqaawaa","SOUTH_EAST waqaw","SOUTH_WEST awqqqwaqw"}), - new Pair<>("hexcasting.loot_hex.radar", new String[] {"WEST qqq","EAST aadaa","EAST aa","SOUTH_EAST aqaawa","SOUTH_WEST ewdqdwe","NORTH_EAST de","EAST eee","NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaaeaqq","SOUTH_EAST qqqqqwdeddwd","NORTH_EAST dadad"}), + new Pair<>("hexcasting.loot_hex.radar", new String[] {"NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaaeaqq","SOUTH_EAST qqqqqwdeddwd","WEST qqq","EAST aadaa","EAST aa","SOUTH_EAST aqaawa","SOUTH_WEST ewdqdwe","NORTH_EAST de","EAST eee","EAST waaddw"}), new Pair<>("hexcasting.loot_hex.beckon", new String[] {"NORTH_EAST qaq","EAST aa","NORTH_EAST qaq","NORTH_EAST wa","EAST weaqa","EAST aadaa","EAST dd","NORTH_EAST qaq","EAST aa","EAST aawdd","NORTH_WEST wddw","EAST aadaa","NORTH_EAST wqaqw","NORTH_EAST wdedw","SOUTH_EAST aqaawa","SOUTH_EAST waqaw","SOUTH_WEST awqqqwaqw"}), new Pair<>("hexcasting.loot_hex.detonate", new String[] {"NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaaedwd","EAST ddwddwdd"}), new Pair<>("hexcasting.loot_hex.shockwave", new String[] {"NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaawaa","EAST aadaadaa","SOUTH_EAST aqawqadaq","SOUTH_EAST aqaaedwd","EAST aawaawaa","NORTH_EAST qqa","EAST qaqqqqq"}), - new Pair<>("hexcasting.loot_hex.heat_wave", new String[] {"WEST qqq","SOUTH_EAST aaqawawa","EAST eee","NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaae","SOUTH_EAST qqqqqwded","SOUTH_WEST aaqwqaa","SOUTH_EAST a","NORTH_EAST dadad"}), - new Pair<>("hexcasting.loot_hex.wither_wave", new String[] {"WEST qqq","SOUTH_EAST aqaae","SOUTH_EAST aqaaw","SOUTH_WEST qqqqqaewawawe","EAST eee","NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaae","SOUTH_EAST qqqqqwdeddwd","SOUTH_WEST aaqwqaa","SOUTH_EAST a","NORTH_EAST dadad"}), - new Pair<>("hexcasting.loot_hex.flight_zone", new String[] {"NORTH_EAST qaq","SOUTH_EAST aqaaq","SOUTH_WEST awawaawq"}) + new Pair<>("hexcasting.loot_hex.heat_wave", new String[] {"NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaae","SOUTH_EAST qqqqqwded","SOUTH_WEST aaqwqaa","SOUTH_EAST a","WEST qqq","SOUTH_EAST aaqawawa","EAST eee","EAST waaddw"}), + new Pair<>("hexcasting.loot_hex.wither_wave", new String[] {"NORTH_EAST qaq","EAST aa","SOUTH_EAST aqaae","SOUTH_EAST qqqqqwdeddwd","SOUTH_WEST aaqwqaa","SOUTH_EAST a","WEST qqq","SOUTH_EAST aqaae","SOUTH_EAST aqaaw","SOUTH_WEST qqqqqaewawawe","EAST eee","EAST waaddw"}), + new Pair<>("hexcasting.loot_hex.flight_zone", new String[] {"NORTH_EAST qaq","SOUTH_EAST aqaaq","SOUTH_WEST awawaawq"}), + new Pair<>("hexcasting.loot_hex.magnet", new String[] {"NORTH_EAST qaq","EAST aa","EAST aadaa","SOUTH_EAST aqaaeq","SOUTH_EAST qqqqqwdeddww","WEST qqq","EAST ddqaa","EAST aa","NORTH_WEST wddw","EAST aadaa","NORTH_EAST wqaqw","NORTH_EAST wdedw","SOUTH_WEST awqqqwaqw","EAST eee","EAST waaddwda"}) ); } diff --git a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/AbstractPatternComponent.java b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/AbstractPatternComponent.java index 531bb2a85e..e92959b624 100644 --- a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/AbstractPatternComponent.java +++ b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/AbstractPatternComponent.java @@ -62,14 +62,17 @@ public void render(GuiGraphics graphics, IComponentRenderContext context, float : PatternColors.READABLE_GRID_SCROLL_COLORS; } + int colsInLastRow = (patterns.size() - 1) % cols + 1; + double lastRowOffset = (cols - colsInLastRow) * cellW / 2; for(int p = 0; p < patterns.size(); p++){ int r = p / cols; int c = p % cols; + double offset = r < rows - 1 ? 0 : lastRowOffset; HexPattern pattern = patterns.get(p); ps.pushPose(); - ps.translate(cellW * c, cellH * r + 16, 100); + ps.translate(offset + cellW * c, cellH * r + 16, 100); PatternRenderer.renderPattern(pattern, graphics.pose(), patSets, patCols, 0, 4); ps.popPose(); 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 e70a5192d5..322e6c311d 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 @@ -570,6 +570,7 @@ "heat_wave": "Heat Wave", "wither_wave": "Wither Wave", "flight_zone": "Flight Zone", + "magnet": "Magnet", }, // TODO: post-eigengrau make these less anticlimactic @@ -986,6 +987,7 @@ "special.hexcasting:": { number: "Numerical Reflection: %s", mask: "Bookkeeper's Gambit: %s", + for_each: "Thoth's Gambit: %s", }, "iota.hexcasting:": { @@ -1999,10 +2001,11 @@ meta: { "eval.1": "Remove a pattern or list of patterns from the stack, then cast them as if I had drawn them myself with my $(l:items/staff)$(item)Staff/$ (until a $(l:patterns/meta#hexcasting:halt)$(action)Charon's Gambit/$ is encountered). If an iota is escaped with $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ or $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)its ilk/$, it will be pushed to the stack. Otherwise, non-patterns will fail.", "eval.2": "This can be very powerful in tandem with $(l:items/focus)$(item)Foci/$, and according to one esoteric scroll I found it also makes the bureaucracy of Nature a \"Turing-complete\" system.$(br2)However, it seems there's a limit to how many times a _Hex can cast itself-- Nature doesn't look kindly on runaway spells!$(br2)In addition, with the energies of the patterns occurring without me to guide them, any mishap will cause the remaining actions to immediately unravel.", - - "for_each.1": "Remove a list of patterns and a list from the stack, then cast the given pattern-list over each element of the second list.", - "for_each.2": "More specifically, for each element in the second list, it will:$(li)Create a new stack, with everything on the current stack plus that element$(li)Draw all the patterns in the first list$(li)Save all the iotas remaining on the stack to a list$(br)Then, after all is said and done, pushes the list of saved iotas onto the main stack.$(br2)No wonder all the practitioners of this art go mad.", - + + "for_each.1": "A family of patterns that pops an evaluatable iota, a data list, and $(o)n/$ iotas of context from the stack, determined by tail length. Then cast the evaluatable over each element of the first list.", + "for_each.2": "In the examples, the context size is 0, 1, and 2 iotas, going clockwise from the top left.$(br2)Specifically, for each data value, it will:$(li)create a sub-stack, populated with a copy of the context iotas$(li)push the data value$(li)cast the evaluatable iota$(li)store all iotas remaining on the sub-stack to a hidden list$(br2)Finally, it restores the original stack, and pushes the hidden list onto the stack.", + "for_each.3": "For example, if I had the number 5, a list of [1, 2, 3], and a $(l:patterns/patterns_as_iotas)$(thing)pattern iota/$ of $(l:patterns/math#hexcasting:add)$(action)Additive Distillation/$ on my stack, I can draw $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit: 1/$ to obtain a list of [6, 7, 8], with nothing else left on my stack.$(br2)Certainly difficult to wrap my head around, but it seems worth learning. I can already think of multiple applications for this pattern.", + "halt.1": "This pattern forcibly halts a _Hex. This is mostly useless on its own, as I could simply just stop writing patterns, or put down my staff.", "halt.2": "But when combined with $(l:patterns/meta#hexcasting:eval)$(action)Hermes'/$ or $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambits/$, it becomes $(italics)far/$ more interesting. Those patterns serve to 'contain' that halting, and rather than ending the entire _Hex, those gambits end instead. This can be used to cause $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit/$ not to operate on every iota it's given. An escape from the madness, as it were.", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/meta.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/meta.json index 63e7f68c4a..adbbdf65de 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/meta.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/meta.json @@ -10,7 +10,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:eval", "anchor": "hexcasting:eval", - "input": "[pattern] | pattern", + "input": "evaluatable", "output": "many", "text": "hexcasting.page.meta.eval.1" }, @@ -22,7 +22,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:eval/cc", "anchor": "hexcasting:eval/cc", - "input": "[pattern] | pattern", + "input": "evaluatable", "output": "many", "text": "hexcasting.page.meta.eval/cc.1" }, @@ -31,17 +31,35 @@ "text": "hexcasting.page.meta.eval/cc.2" }, { - "type": "hexcasting:pattern", + "type": "hexcasting:manual_pattern", "op_id": "hexcasting:for_each", "anchor": "hexcasting:for_each", - "input": "list of patterns, list", + "input": "many, list, evaluatable", "output": "list", - "text": "hexcasting.page.meta.for_each.1" + "text": "hexcasting.page.meta.for_each.1", + "patterns": [ + { + "startdir": "EAST", + "signature": "waaddw" + }, + { + "startdir": "EAST", + "signature": "waaddwda" + }, + { + "startdir": "EAST", + "signature": "waaddwdaad" + } + ] }, { "type": "patchouli:text", "text": "hexcasting.page.meta.for_each.2" }, + { + "type": "patchouli:text", + "text": "hexcasting.page.meta.for_each.3" + }, { "type": "hexcasting:pattern", "op_id": "hexcasting:halt",