diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a2f396c0..925f43b321 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added a config toggle (default false) for the effect that makes iota-holding items display their entire NBT data when Advanced Tooltips is enabled ([#1021](https://github.com/FallingColors/HexMod/pull/1021)) @Robotgiggle - Added a pattern display overlay for pattern-holding items (ie scrolls or slates) while holding shift in the inventory ([#879](https://github.com/FallingColors/HexMod/pull/879)) @SamsTheNerd - Added connected textures for Akashic Ligatures when using Continuity or Optifine ([#885](https://github.com/FallingColors/HexMod/pull/885)) @kineticneticat +- Added Interjection, Meditation, and Recollection for advanced pattern-list creation ([#1103](https://github.com/FallingColors/HexMod/pull/1103)) @Robotgiggle - Added Similarity Distillation and Dissimilarity Distillation for comparing iota types ([#1114](https://github.com/FallingColors/HexMod/pull/1114)) @IridescentVoid ### Changed 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 c30280a868..9f5257dd06 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 @@ -39,6 +39,8 @@ interface Action { * * The userdata tag is copied for you, so you don't need to worry about mutation messing up things * behind the scenes. + * + * Note that `image.parenCount` will always be 0 here - if it's greater, [operateInParens] is used instead. */ @Throws(Mishap::class) fun operate( @@ -51,8 +53,8 @@ interface Action { * The behavior of this action when inside parentheses (meaning `image.parenCount` will always be greater than 0). * By default, this just adds the pattern to the parenthesized list without updating the op count or performing any of its effects. * - * Note that behavior defined here can throw mishaps as usual. However, mishapping here will not affect the paren count, - * so the caster will still be in list-building mode after the mishap resolves. + * Note that behavior defined here can throw mishaps as usual. However, mishapping here while staffcasting will not + * affect the paren count, so the caster will still be in list-building mode after the mishap resolves. */ @Throws(Mishap::class) fun operateInParens( 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 8e92c35053..267c0a1bab 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 @@ -77,11 +77,13 @@ 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 parenthesized list. + * Returns a copy of this with the provided iota added to the parenthesized list. `escaped` is used by + * [OpUndo][at.petrak.hexcasting.common.casting.actions.escaping.OpUndo] to determine whether undoing the + * parenthesized iota should adjust the paren count (escaped parens do not affect the count). */ - fun withNewParenthesized(iota: Iota): CastingImage { + fun withNewParenthesized(iota: Iota, escaped: Boolean = false): CastingImage { val newParens = this.parenthesized.toMutableList() - newParens.add(ParenthesizedIota(iota, false)) + newParens.add(ParenthesizedIota(iota, escaped)) return this.copy(parenthesized = newParens) } 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 4daa133881..d243af7e21 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 @@ -117,11 +117,9 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { 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 - ) + escapeNext = false + ).withNewParenthesized(iota, escaped = true) } else { // if we're not in parentheses, just push the iota to the stack val newStack = this.image.stack.toMutableList() diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt index 141148dfdc..5de016d147 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/FrameEvaluate.kt @@ -17,7 +17,7 @@ import net.minecraft.server.level.ServerLevel /** * A list of patterns to be evaluated in sequence. * @property list the *remaining* list of patterns to be evaluated - * @property isMetacasting only for sound effects, if this is being cast from a hermes / iris + * @property isMetacasting if this is being cast from a meta-evaluation pattern */ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : ContinuationFrame { // Discard this frame and keep discarding frames. 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 c884b052ce..12716ff48f 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 @@ -78,7 +78,7 @@ protected Iota(@NotNull IotaType type, @NotNull Object payload) { return new CastResult( this, continuation, - vm.getImage().withNewParenthesized(this), + vm.getImage().withNewParenthesized(this, false), List.of(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE); 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 e24648df41..593e46a212 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 @@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.casting.eval.*; import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect; import at.petrak.hexcasting.api.casting.eval.vm.CastingVM; +import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate; 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; @@ -123,7 +124,7 @@ public boolean executable() { return new CastResult( this, continuation, - vm.getImage().withNewParenthesized(this), + vm.getImage().withNewParenthesized(this, false), List.of(), ResolvedPatternType.ESCAPED, HexEvalSounds.NORMAL_EXECUTE); @@ -167,10 +168,16 @@ public boolean executable() { result.getSound()); } catch (Mishap mishap) { + // If a mishap happens mid-parens at the top level (ie when staffcasting), keep any in-progress parens. + // If a mishap happens mid-parens while metacasting, wipe the parens - if we didn't do this, any open paren + // levels would remain open even though the metacast itself has mishapped and thus been aborted. + boolean wipeParens = continuation instanceof SpellContinuation.NotDone cnd + && cnd.getFrame() instanceof FrameEvaluate frameEval + && frameEval.isMetacasting(); return new CastResult( this, continuation, - null, + wipeParens ? vm.getImage().withResetEscape() : null, List.of(new OperatorSideEffect.DoMishap(mishap, new Mishap.Context(this.getPattern(), castedName.get()))), mishap.resolutionType(vm.getEnv()), HexEvalSounds.MISHAP); diff --git a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt index 4759c50a4b..5b1c370559 100644 --- a/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt +++ b/Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt @@ -72,11 +72,8 @@ class GuiSpellcasting constructor( // TODO this is the kinda hacky bit if (info.resolutionType == ResolvedPatternType.UNDONE) { - // find the last escaped pattern (or the opening paren if there's nothing else) and set it to UNDONE - this.patterns.reversed().drop(1).firstOrNull { - it.type == ResolvedPatternType.ESCAPED || - (it.type == ResolvedPatternType.EVALUATED && it.pattern.angles == HexActions.OPEN_PAREN.prototype.angles) - }?.let { it.type = ResolvedPatternType.UNDONE } + // find the last undo-able pattern and set its coloring to UNDONE + this.patterns.reversed().drop(1).firstOrNull { canBeUndone(it) }?.let { it.type = ResolvedPatternType.UNDONE } // use the normal EVALUATED coloring for the Evanition that was just drawn this.patterns.getOrNull(index)?.let { it.type = ResolvedPatternType.EVALUATED } } else this.patterns.getOrNull(index)?.let { @@ -88,6 +85,22 @@ class GuiSpellcasting constructor( this.calculateIotaDisplays() } + fun canBeUndone(resolvedPat: ResolvedPattern): Boolean { + return when (resolvedPat.type) { + // standard escaped patterns can always be undone + ResolvedPatternType.ESCAPED -> true + // everything else cannot be undone, with three exceptions: + // - unescaped Introspection and Meditation can be undone if there's nothing else left to undo + // - Interjection can always be undone (undoing its inserted iota) despite using the EVALUATED coloring + ResolvedPatternType.EVALUATED -> { + resolvedPat.pattern.angles == HexActions.OPEN_PAREN.prototype.angles || + resolvedPat.pattern.angles == HexActions.OPEN_N_PARENS.prototype.angles || + resolvedPat.pattern.angles == HexActions.READ_INTO_PARENS.prototype.angles + } + else -> false + } + } + fun calculateIotaDisplays() { val mc = Minecraft.getInstance() val width = (this.width * LHS_IOTAS_ALLOCATION).toInt() diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseAllParens.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseAllParens.kt new file mode 100644 index 0000000000..abddbaf468 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpCloseAllParens.kt @@ -0,0 +1,32 @@ +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.ParenthesizedOperationResult +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.DoubleIota +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.ListIota +import at.petrak.hexcasting.api.casting.mishaps.MishapNeedsParens +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpCloseAllParens : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + throw MishapNeedsParens() + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): ParenthesizedOperationResult { + val newStack = image.stack.toMutableList() + newStack.add(DoubleIota(image.parenCount.toDouble())) + newStack.add(ListIota(image.parenthesized.toList().map { it.iota })) + val image2 = image.withUsedOp().copy( + stack = newStack, + parenCount = 0, + parenthesized = listOf() + ) + return ParenthesizedOperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE, ResolvedPatternType.EVALUATED) + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenNParens.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenNParens.kt new file mode 100644 index 0000000000..3ad174eeef --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpOpenNParens.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.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation +import at.petrak.hexcasting.api.casting.getPositiveInt +import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds + +object OpOpenNParens : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + val newStack = image.stack.toMutableList() + val layers = newStack.getPositiveInt(newStack.lastIndex) + newStack.removeLast() + val image2 = image.withUsedOp().copy( + stack = newStack, + parenCount = layers + ) + return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) + } + + // Since there's no nice way to determine how many new layers it should open when drawn inside parens, we don't + // override operateInParens() at all. This pattern is just treated as any other pattern when parenthesized. +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpReadIntoParens.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpReadIntoParens.kt new file mode 100644 index 0000000000..d250d9ff9d --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/escaping/OpReadIntoParens.kt @@ -0,0 +1,46 @@ +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.ParenthesizedOperationResult +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.mishaps.MishapBadOffhandItem +import at.petrak.hexcasting.api.casting.mishaps.MishapNeedsParens +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds +import at.petrak.hexcasting.xplat.IXplatAbstractions + +object OpReadIntoParens : Action { + override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { + throw MishapNeedsParens() + } + + override fun operateInParens(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation, thisIota: Iota): ParenthesizedOperationResult { + val (handStack) = env.getHeldItemToOperateOn { + val dataHolder = IXplatAbstractions.INSTANCE.findDataHolder(it) + dataHolder != null && (dataHolder.readIota(env.world) != null || dataHolder.emptyIota() != null) + } ?: env.getHeldItemToOperateOn { + // If there are no data holders that are readable, find a data holder that isn't readable + // so that the error message is more helpful. + val dataHolder = IXplatAbstractions.INSTANCE.findDataHolder(it) + dataHolder != null + } ?: throw MishapBadOffhandItem.of(null, "iota.read") + + val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack) + ?: throw MishapBadOffhandItem.of(handStack, "iota.read") + + val datum = datumHolder.readIota(env.world) + ?: datumHolder.emptyIota() + ?: throw MishapBadOffhandItem.of(handStack, "iota.read") + + return ParenthesizedOperationResult( + image.withUsedOp().withNewParenthesized(datum, escaped = true), // inserted parens should not adjust paren count + listOf(), continuation, + HexEvalSounds.NORMAL_EXECUTE, + ResolvedPatternType.EVALUATED + ) + } +} \ 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 index c8f1b3aed3..fa9a236a0b 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 @@ -23,8 +23,9 @@ object OpUndo : Action { val last = newParens.removeLastOrNull() var newParenCount = image.parenCount if (last == null) { - // if there was nothing in the parenthesized list, undo the initial open paren - newParenCount-- + // If there was nothing in the parenthesized list, undo the initial open paren. + // This gets set to 0 rather than just decremented in case we started with open-n-parens. + newParenCount = 0 } else if (last.iota is PatternIota && !last.escaped) { // adjust paren count if undoing a non-escaped open or close paren when (last.iota.pattern.angles) { 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..b5ca773255 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 @@ -394,6 +394,12 @@ public class HexActions { 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 OPEN_N_PARENS = make("open_n_parens", + new ActionRegistryEntry(HexPattern.fromAngles("eqqqe", HexDir.SOUTH_WEST), OpOpenNParens.INSTANCE)); + public static final ActionRegistryEntry CLOSE_ALL_PARENS = make("close_all_parens", + new ActionRegistryEntry(HexPattern.fromAngles("qeeeq", HexDir.SOUTH_EAST), OpCloseAllParens.INSTANCE)); + public static final ActionRegistryEntry READ_INTO_PARENS = make("read_into_parens", + new ActionRegistryEntry(HexPattern.fromAngles("aqqqqqwded", HexDir.EAST), OpReadIntoParens.INSTANCE)); public static final ActionRegistryEntry UNDO = make("undo", new ActionRegistryEntry(HexPattern.fromAngles("eeedw", HexDir.EAST), OpUndo.INSTANCE)); 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 9e070e27c6..a6853dfc87 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 @@ -921,6 +921,9 @@ escape: "Consideration", open_paren: "Introspection", close_paren: "Retrospection", + open_n_parens: "Meditation", + close_all_parens: "Recollection", + read_into_parens: "Interjection", undo: "Evanition", eval: "Hermes' Gambit", @@ -1312,6 +1315,7 @@ entities: "Entities", lists: "List Manipulation", patterns_as_iotas: "Escaping Patterns", + advanced_escaping: "Advanced Escaping", readwrite: "Reading and Writing", meta: "Meta-Evaluation", circle_patterns: "Spell Circle Patterns", @@ -1510,7 +1514,7 @@ 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.", "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.", + needs_parens: "I attempted to draw a pattern that only works while $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)introspecting/$ 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.", @@ -1962,23 +1966,30 @@ patterns_as_iotas: { "1": "One of the many peculiarities of this art is that $(italic)patterns themselves/$ can act as iotas-- I can even put them onto my stack when casting.$(br2)This raises a fairly obvious question: how do I express them? If I simply drew a pattern, it would hardly tell Nature to add it to my stack-- rather, it would simply be matched to an action.", - "2": "Fortunately, Nature has provided me with a set of $(l:casting/influences)influences/$ that I can use to work with patterns directly.$(br2)In short, $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ lets me add one pattern to the stack, and $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ let me add a whole list.", + "2": "Fortunately, Nature has provided me with a set of $(l:casting/influences)influences/$ that I can use to work with patterns directly.$(br2)In short, $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ lets me add one pattern to the stack, $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ let me add a whole list, and $(l:patterns/patterns_as_iotas#hexcasting:undo)$(action)Evanition/$ lets me undo mistakes during the creation of such a list.", escape: { "1": "To use $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$, I draw it, then another arbitrary pattern. That second pattern is added to the stack.", "2": "One may find it helpful to think of this as \"escaping\" the pattern onto the stack, if they happen to be familiar with the science of computers.$(br2)The usual use for this is to copy the pattern to a $(l:items/scroll)$(item)Scroll/$ or $(l:items/slate)$(item)Slate/$ using $(l:patterns/readwrite#hexcasting:write)$(action)Scribe's Gambit/$, and then perhaps decorating with them.", }, parens: { - "1": "Drawing $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$ makes my drawing of patterns act differently, for a time. Until I draw $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Retrospection/$, the patterns I draw are saved. Then, when I draw $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$, they are added to the stack as a list iota.", + "1": "Drawing $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspection/$ makes my drawing of patterns act differently, for a time. Until I draw $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$, the patterns I draw are saved. Then, when I draw $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$, they are added to the stack as a list iota.", "2": "If I draw another $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Introspection/$, it'll still be saved to the list, but I'll then have to draw $(italic)two/$ $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospections/$ to get back to normal casting.", }, - undo: "Finally, if I make a mistake while drawing patterns inside $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Intro-/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ I can draw $(l:patterns/patterns_as_iotas#hexcasting:undo)$(action)Evanition/$ to remove the last pattern that I drew from the pattern list that is being constructed.", + undo: "Finally, if I make a mistake while drawing patterns inside $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Intro-/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$, I can draw $(l:patterns/patterns_as_iotas#hexcasting:undo)$(action)Evanition/$ to remove the last pattern that I drew from the pattern list that is being constructed.", further_notes: { title: "Further Notes", - "1": "I can escape the special behavior of $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Intro-/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ by drawing a $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ before them, which will simply add them to the list without affecting which the number of $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospections/$ I need to return to casting.$(br2)If I draw two $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Considerations/$ in a row while $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)introspecting/$, it will add a single $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ to the list.", + "1": "I can escape the special behavior of patterns like $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Intro-/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ by drawing a $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ before them, which will simply add them to the list without affecting which the number of $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospections/$ I need to return to casting.$(br2)If I draw two $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Considerations/$ in a row while $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)introspecting/$, it will add a single $(l:patterns/patterns_as_iotas#hexcasting:escape)$(action)Consideration/$ to the list.", "2": "If an iota other than a pattern is present in a list to be executed by $(l:patterns/meta#hexcasting:eval)$(action)Hermes' Gambit/$ or any other meta-evaluation pattern, it will normally result in a mishap. However, this can be avoided using the patterns described in this section.$(br2)Just as with pattern iotas, the patterns described here can be used to \"escape\" $(o)any other$() kind of iota, causing it to be pushed to the stack when something tries to evaluate it instead of causing a mishap.", - "3": "This technique may be useful if I want a _Hex to be able to reference a specific iota, such as a complicated vector or an entity reference, without having to construct or obtain it each time.$(br2)The process of getting such an iota into a list of patterns in the first place may be somewhat involved. The simplest method would be to draw a placeholder pattern when assembling the list, and then make use of $(l:patterns/lists#hexcasting:replace)$(action)Surgeon's Exaltation/$ to replace it with my desired iota.", + "3": "This technique may be useful if I want a _Hex to be able to reference a specific iota, such as a complicated vector or an entity reference, without having to construct or obtain it each time.$(br2)To get such an iota into a list of patterns in the first place, I can either use $(l:patterns/advanced_escaping#hexcasting:read_into_parens)$(action)Interjection/$ while creating the list, or draw a placeholder pattern and then use $(l:patterns/lists#hexcasting:replace)$(action)Surgeon's Exaltation/$ to replace it once the list has been created.", }, }, + + advanced_escaping: { + "1": "$(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Intro-/$ and $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$ are very useful, but when building complex _Hexes I may need more advanced techniques.$(br2)This section describes such techniques: $(l:patterns/advanced_escaping#hexcasting:read_into_parens)$(action)Interjection/$ lets me insert iotas directly into an in-progress list, while $(l:patterns/advanced_escaping#hexcasting:open_n_parens)$(action)Meditation/$ and $(l:patterns/advanced_escaping#hexcasting:close_all_parens)$(action)Recollection/$ let me start and finish the list creation process with arbitrary levels of nesting depth.", + read_into_parens: "Only works while $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)introspecting/$. Reads an iota from my offhand, and adds it to the in-progress list. For iotas other than patterns, I may need to $(l:patterns/patterns_as_iotas#iota_embedding)$(action)take steps/$ to avoid a mishap when executing the list.", + open_n_parens: "Removes a number from the stack, then acts as though I'd drawn that many $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspections/$. Treated as a normal pattern while $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)introspecting/$, so nesting this may require $(l:patterns/patterns_as_iotas#escaping_escapers)$(action)tricks/$ to draw extra $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospections/$.", + close_all_parens: "Works like $(l:patterns/patterns_as_iotas#hexcasting:close_paren)$(action)Retrospection/$, but closes $(italic)all/$ open $(l:patterns/patterns_as_iotas#hexcasting:open_paren)$(action)Introspections/$ at once. The number of layers closed, along with the list of saved patterns, are then added to the stack.", + }, readwrite: { "1": "This section deals with the storage of $(thing)Iotas/$ in a more permanent medium. Nearly any iota can be stored to a suitable item, such as a $(l:items/focus)$(item)Focus/$ or $(l:items/spellbook)$(item)Spellbook/$, and read back later. Certain items, such as an $(l:items/abacus)$(item)Abacus/$, can only be read from.$(br2)Iotas are usually read and written from the other hand, but it is also possible to read and write with an item when it is sitting on the ground as an item entity, or when in an item frame.", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/advanced_escaping.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/advanced_escaping.json new file mode 100644 index 0000000000..b971507b17 --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/advanced_escaping.json @@ -0,0 +1,34 @@ +{ + "name": "hexcasting.entry.advanced_escaping", + "category": "hexcasting:patterns", + "icon": "minecraft:emerald_block", + "sortnum": 9, + "advancement": "hexcasting:root", + "read_by_default": true, + "pages": [ + { + "type": "patchouli:text", + "text": "hexcasting.page.advanced_escaping.1" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:read_into_parens", + "anchor": "hexcasting:read_into_parens", + "text": "hexcasting.page.advanced_escaping.read_into_parens" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:open_n_parens", + "anchor": "hexcasting:open_n_parens", + "input": "num", + "text": "hexcasting.page.advanced_escaping.open_n_parens" + }, + { + "type": "hexcasting:pattern", + "op_id": "hexcasting:close_all_parens", + "anchor": "hexcasting:close_all_parens", + "output": "num, [pattern]", + "text": "hexcasting.page.advanced_escaping.close_all_parens" + } + ] +} \ No newline at end of file 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 0d7e834aeb..c5aafed351 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 @@ -44,11 +44,13 @@ }, { "type": "patchouli:text", + "anchor": "escaping_escapers", "title": "hexcasting.page.patterns_as_iotas.further_notes.title", "text": "hexcasting.page.patterns_as_iotas.further_notes.1" }, { "type": "patchouli:text", + "anchor": "iota_embedding", "text": "hexcasting.page.patterns_as_iotas.further_notes.2" }, { diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json index c661d10d73..495702887f 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json @@ -2,7 +2,7 @@ "name": "hexcasting.entry.readwrite", "category": "hexcasting:patterns", "icon": "minecraft:writable_book", - "sortnum": 9, + "sortnum": 10, "advancement": "hexcasting:root", "read_by_default": true, "pages": [