diff --git a/CHANGELOG.md b/CHANGELOG.md index d401546f73..b4c4db1c3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ 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 +### Changed + +- Changed the argument order for Thoth's Gambit to take data first ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid +- Changed Thoth's Gambit to accept single iotas as the loop body ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid +- Changed Thoth's Gambit to be a special handler with a new shape ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid +- Changed Thoth's Gambit to use a context stack for iterations, and restore stashed iotas at the end ([#1106](https://github.com/FallingColors/HexMod/pull/1106)) @IridescentVoid + ### Fixed - Fixed Entity Iota comparison to use `equals` on entity IDs instead of reference equality ([#1101](https://github.com/FallingColors/HexMod/pull/1101)) @IridescentVoid 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 6133b2b627..d9b71638d3 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 @@ -14,28 +14,27 @@ 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) * @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: SpellList, val code: SpellList, - val baseStack: List?, + val contextStack: List, + val stashedStack: List, 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: List): Pair> { - val newStack = baseStack?.toMutableList() ?: mutableListOf() + val newStack = stashedStack.toMutableList() newStack.add(ListIota(acc.appendedAll(stack))) return true to newStack } @@ -46,42 +45,40 @@ 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.toList() 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.nonEmpty) { + val (newStack, newImage, newCont) = if (data.nonEmpty) { + // Restore the context stack, + val stack = contextStack.toMutableList() // push the next datum to the top of the stack, + stack.add(data.car) val cont2 = continuation // put the next Thoth object back on the stack for the next Thoth cycle, - .pushFrame(FrameForEach(data.cdr, code, stack, newAcc)) + .pushFrame(FrameForEach(data.cdr, code, contextStack, stashedStack, newAcc)) // and prep the Thoth'd code block for evaluation. .pushFrame(FrameEvaluate(code, true)) - Triple(data.car, 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, + val stack = stashedStack.toMutableList() + // and dump our final list onto the stack. + stack.add(ListIota(newAcc)) + Triple(stack, harness.image, continuation) } - val tStack = stack.toMutableList() - tStack.add(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 @@ -92,21 +89,24 @@ data class FrameForEach( inst.group( SpellList.CODEC.fieldOf("data").forGetter { it.data }, SpellList.CODEC.fieldOf("code").forGetter { it.code }, - IotaType.TYPED_CODEC.listOf().optionalFieldOf("base").forGetter { Optional.ofNullable(it.baseStack) }, + IotaType.TYPED_CODEC.listOf().fieldOf("context").forGetter { it.contextStack }, + IotaType.TYPED_CODEC.listOf().fieldOf("stashed").forGetter { it.stashedStack }, IotaType.TYPED_CODEC.listOf().fieldOf("accumulator").forGetter { it.acc } - ).apply(inst) { a, b, c, d -> - FrameForEach(a, b, c.getOrNull(), TreeList.from(d)) + ).apply(inst) { a, b, c, d, e -> + FrameForEach(a, b, c, d, TreeList.from(e)) } } val STREAM_CODEC = StreamCodec.composite( SpellList.STREAM_CODEC, FrameForEach::data, SpellList.STREAM_CODEC, FrameForEach::code, - ByteBufCodecs.optional(IotaType.TYPED_STREAM_CODEC - .apply(ByteBufCodecs.list())), { Optional.ofNullable(it.baseStack) }, + IotaType.TYPED_STREAM_CODEC + .apply(ByteBufCodecs.list()), FrameForEach::contextStack, + IotaType.TYPED_STREAM_CODEC + .apply(ByteBufCodecs.list()), FrameForEach::stashedStack, IotaType.TYPED_STREAM_CODEC .apply(ByteBufCodecs.list()), FrameForEach::acc - ) { a, b, c, d -> - FrameForEach(a, b, c.getOrNull(), TreeList.from(d)) + ) { a, b, c, d, e -> + FrameForEach(a, b, c, d, TreeList.from(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 926bce1d82..0000000000 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/OpForEach.kt +++ /dev/null @@ -1,31 +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.toMutableList() - - 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) - stack.removeLastOrNull() - stack.removeLastOrNull() - - val frame = FrameForEach(datums, instrs, null, TreeList.empty()) - val image2 = image.withUsedOp().copy(stack = stack) - - 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..9d4ab75696 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/eval/SpecialHandlerForEach.kt @@ -0,0 +1,77 @@ +package at.petrak.hexcasting.common.casting.actions.eval + +import at.petrak.hexcasting.api.HexAPI +import at.petrak.hexcasting.api.casting.SpellList +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 { + val stack = image.stack.toMutableList() + + 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.removeLastOrNull() + stack.removeLastOrNull() + + val instrList = instrs.map({ SpellList.LList(0, listOf(it)) }, { it }) + + val contextStack = stack.takeLast(n) + val stashedStack = stack.dropLast(n) + + val frame = FrameForEach(datums, instrList, contextStack, stashedStack, TreeList.empty()) + val image2 = image.withUsedOp().copy(stack = listOf()) + + 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 eb049a8e78..6bd371d5b7 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 @@ -551,8 +551,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 528f826464..584271a546 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 @@ -988,6 +989,7 @@ "special.hexcasting:": { number: "Numerical Reflection: %s", mask: "Bookkeeper's Gambit: %s", + for_each: "Thoth's Gambit: %s", }, "iota.hexcasting:": { @@ -2000,10 +2002,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, 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",