Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Added

- Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110
- Added Extirpating Gambit, Reflecting Gambit, Bubbling Gambit, Dioscuri Gambit II, Tutu's Gambit, Janus' Gambit, and Atalanta's Gambit ([#1177](https://github.com/FallingColors/HexMod/pull/1177)) @IridescentVoid
- API: Added `breakSideways` method to `ContinuationFrame` to control behavior when used with Atalanta's Gambit ([#1177](https://github.com/FallingColors/HexMod/pull/1177)) @IridescentVoid

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ interface ContinuationFrame {
*/
fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>>

/**
* The OpContinue instruction wants us to "jump to" the end of the current ForEach or loop iteration.
* It leaves the frame intact on the stack to be executed next.
* @return whether the break should stop here
*/
fun breakSideways(): Boolean = false

/**
* Return the number of iotas contained inside this frame, used for determining whether it is valid to serialise.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ data class FrameForEach(
return true to newStack
}

/** When casting OpContinue, we should stop popping frames here. */
override fun breakSideways(): Boolean = true

/** Step the Thoth computation, enqueueing one code evaluation. */
override fun evaluate(
continuation: SpellContinuation,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package at.petrak.hexcasting.api.casting.mishaps

import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.pigment.FrozenPigment
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor

class MishapNeedsLoopContext : Mishap() {
// TODO: need to add to notebook?
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment =
dyeColor(DyeColor.BLUE)

override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component =
error("needs_loop_context")

override fun execute(env: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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.SpellContinuation
import at.petrak.hexcasting.api.casting.mishaps.MishapNeedsLoopContext
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds

object OpContinue : Action {
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
var newCont = continuation
while (newCont is SpellContinuation.NotDone && !newCont.frame.breakSideways())
newCont = newCont.next

if (newCont !is SpellContinuation.NotDone) {
// failed to find a loop frame
throw MishapNeedsLoopContext()
}

return OperationResult(image.withUsedOp(), listOf(), newCont, HexEvalSounds.SPELL)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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.SpellContinuation
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds

object OpNop : Action {
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult =
OperationResult(image, listOf(), continuation, HexEvalSounds.NOTHING)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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.SpellContinuation
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds

object OpTrulyHalt : Action {
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult =
OperationResult(image.withUsedOp(), listOf(), SpellContinuation.Done, HexEvalSounds.SPELL)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package at.petrak.hexcasting.common.casting.actions.lists

import at.petrak.hexcasting.api.casting.castables.ConstMediaAction
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.getList
import at.petrak.hexcasting.api.casting.getPositiveIntUnder
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota

object OpSnapList : ConstMediaAction {
override val argc = 2
override fun execute(args: List<Iota>, env: CastingEnvironment): List<Iota> {
val list = args.getList(0, argc).toList()
val breakPosition = args.getPositiveIntUnder(1, list.size)
return listOf(
ListIota(list.subList(0, breakPosition)),
list[breakPosition],
ListIota(list.subList(breakPosition + 1, list.size))
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package at.petrak.hexcasting.common.casting.actions.stack

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.getPositiveIntUnderInclusive
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds

object OpDuplicateMany : Action {
override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult {
val stack = image.stack.toMutableList()
if (stack.isEmpty())
throw MishapNotEnoughArgs(1, 0)
val count = stack.takeLast(1).getPositiveIntUnderInclusive(0, stack.size - 1)
stack.removeAt(stack.lastIndex)
for (iota in stack.takeLast(count))
stack.add(iota)
return OperationResult(image.withUsedOp().copy(stack = stack), listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@
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;
import at.petrak.hexcasting.common.casting.actions.lists.OpSingleton;
import at.petrak.hexcasting.common.casting.actions.lists.OpSplat;
import at.petrak.hexcasting.common.casting.actions.lists.*;
import at.petrak.hexcasting.common.casting.actions.local.OpPeekLocal;
import at.petrak.hexcasting.common.casting.actions.local.OpPushLocal;
import at.petrak.hexcasting.common.casting.actions.math.OpCoerceToAxial;
Expand Down Expand Up @@ -115,8 +112,11 @@ public class HexActions {
public static final ActionRegistryEntry ROTATE = make("rotate",
new ActionRegistryEntry(HexPattern.fromAngles("aaeaa", HexDir.EAST), new OpTwiddling(3, new int[]{1, 2, 0})));
public static final ActionRegistryEntry ROTATE_REVERSE = make("rotate_reverse",
new ActionRegistryEntry(HexPattern.fromAngles("ddqdd",
HexDir.NORTH_EAST), new OpTwiddling(3, new int[]{2, 0, 1})));
new ActionRegistryEntry(HexPattern.fromAngles("ddqdd", HexDir.NORTH_EAST), new OpTwiddling(3, new int[]{2, 0, 1})));
public static final ActionRegistryEntry SWAP_ONE_THREE = make("swap_one_three",
new ActionRegistryEntry(HexPattern.fromAngles("ddwqaq", HexDir.NORTH_EAST), new OpTwiddling(3, new int[]{2, 1, 0})));
public static final ActionRegistryEntry SWAP_TWO_THREE = make("swap_two_three",
new ActionRegistryEntry(HexPattern.fromAngles("aawede", HexDir.EAST), new OpTwiddling(3, new int[]{1, 0, 2})));
public static final ActionRegistryEntry DUPLICATE = make("duplicate",
new ActionRegistryEntry(HexPattern.fromAngles("aadaa", HexDir.EAST), new OpTwiddling(1, new int[]{0, 0})));
public static final ActionRegistryEntry OVER = make("over",
Expand All @@ -126,6 +126,8 @@ public class HexActions {
public static final ActionRegistryEntry TWO_DUP = make("2dup",
new ActionRegistryEntry(HexPattern.fromAngles("aadadaaw",
HexDir.EAST), new OpTwiddling(2, new int[]{0, 1, 0, 1})));
public static final ActionRegistryEntry DUPLICATE_MANY = make("duplicate_many",
new ActionRegistryEntry(HexPattern.fromAngles("waadadaa", HexDir.EAST), OpDuplicateMany.INSTANCE));

public static final ActionRegistryEntry STACK_LEN = make("stack_len",
new ActionRegistryEntry(HexPattern.fromAngles("qwaeawqaeaqa", HexDir.NORTH_WEST), OpStackSize.INSTANCE));
Expand Down Expand Up @@ -405,6 +407,12 @@ public class HexActions {
new ActionRegistryEntry(HexPattern.fromAngles("qwaqde", HexDir.NORTH_WEST), OpEvalBreakable.INSTANCE));
public static final ActionRegistryEntry HALT = make("halt",
new ActionRegistryEntry(HexPattern.fromAngles("aqdee", HexDir.SOUTH_WEST), OpHalt.INSTANCE));
public static final ActionRegistryEntry TRULY_HALT = make("truly_halt",
new ActionRegistryEntry(HexPattern.fromAngles("aadee", HexDir.SOUTH_WEST), OpTrulyHalt.INSTANCE));
public static final ActionRegistryEntry CONTINUE = make("continue",
new ActionRegistryEntry(HexPattern.fromAngles("aqdea", HexDir.SOUTH_WEST), OpContinue.INSTANCE));
public static final ActionRegistryEntry NOP = make("nop",
new ActionRegistryEntry(HexPattern.fromAngles("eedqa", HexDir.WEST), OpNop.INSTANCE));

public static final ActionRegistryEntry READ = make("read",
new ActionRegistryEntry(HexPattern.fromAngles("aqqqqq", HexDir.EAST), OpRead.INSTANCE));
Expand Down Expand Up @@ -577,6 +585,8 @@ public class HexActions {
new OperationAction(HexPattern.fromAngles("ddewedd", HexDir.SOUTH_EAST)));
public static final ActionRegistryEntry DECONSTRUCT = make("deconstruct",
new OperationAction(HexPattern.fromAngles("aaqwqaa", HexDir.SOUTH_WEST)));
public static final ActionRegistryEntry SNAP_LIST = make("snap_list",
new ActionRegistryEntry(HexPattern.fromAngles("eawdq", HexDir.EAST), OpSnapList.INSTANCE));

// Xplat interops
static {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@
replace: "Surgeon's Exaltation",
construct: "Speaker's Distillation",
deconstruct: "Speaker's Decomposition",
snap_list: "Extirpating Gambit",

get_entity: "Entity Purification",
"get_entity/": {
Expand Down Expand Up @@ -789,10 +790,13 @@
swap: "Jester's Gambit",
rotate: "Rotation Gambit",
rotate_reverse: "Rotation Gambit II",
swap_one_three: "Reflecting Gambit",
swap_two_three: "Bubbling Gambit",
duplicate: "Gemini Decomposition",
over: "Prospector's Gambit",
tuck: "Undertaker's Gambit",
"2dup": "Dioscuri Gambit",
duplicate_many: "Dioscuri Gambit II",
duplicate_n: "Gemini Gambit",
stack_len: "Flock's Reflection",
fisherman: "Fisherman's Gambit",
Expand Down Expand Up @@ -923,7 +927,10 @@
"eval/cc": "Iris' Gambit",
for_each: "Thoth's Gambit",
halt: "Charon's Gambit",
truly_halt: "Janus' Gambit",
continue: "Atalanta's Gambit",
thanatos: "Thanatos' Reflection",
nop: "Tutu's Gambit",

"interop/": {
"gravity/": {
Expand Down Expand Up @@ -1053,6 +1060,7 @@
no_spell_circle: "requires a spell circle",
others_name: "Tried to invade the privacy of %s's soul",
"others_name.self": "Tried to divulge my Name too recklessly",
needs_loop_context: "needs to be cast within a skippable meta-evaluation pattern",

divide_by_zero: {
divide: "Attempted to divide %s by %s",
Expand Down Expand Up @@ -1509,6 +1517,9 @@
"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_loop_context.title": "Absent Metaevaluation",
needs_loop_context: "I attempted to draw $(l:patterns/meta#hexcasting:continue)$(action)Atalanta's Gambit/$ while not using $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit/$.$(br2)Causes dark blue sparks.",

"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.",

Expand Down Expand Up @@ -1877,10 +1888,13 @@
swap: "Swaps the top two iotas of the stack.",
rotate: "Yanks the iota third from the top of the stack to the top. [0, 1, 2] becomes [1, 2, 0].",
rotate_reverse: "Yanks the top iota to the third position. [0, 1, 2] becomes [2, 0, 1].",
swap_one_three: "Swaps the top and third-from-the-top iota. [0, 1, 2] becomes [2, 1 0].",
swap_two_three: "Swaps the second-from-the-top and third-from-the-top iota. [0, 1, 2] becomes [1, 0, 2].",
duplicate: "Duplicates the top iota of the stack.",
over: "Copy the second-to-last iota of the stack to the top. [0, 1] becomes [0, 1, 0].",
tuck: "Copy the top iota of the stack, then put it under the second iota. [0, 1] becomes [1, 0, 1].",
"2dup": "Copy the top two iotas of the stack. [0, 1] becomes [0, 1, 0, 1].",
duplicate_many: "Copy the top n iotas of the stack while preserving order.",
stack_len: "Pushes the size of the stack as a number to the top of the stack. (For example, a stack of [0, 1] will become [0, 1, 2].)",
duplicate_n: "Removes the number at the top of the stack, then copies the top iota of the stack that number of times. (A count of 2 results in two of the iota on the stack, not three.)",
fisherman: "Grabs the element in the stack indexed by the number and brings it to the top. If the number is negative, instead moves the top element of the stack down that many elements.",
Expand Down Expand Up @@ -1958,6 +1972,7 @@
splat: "Remove the list at the top of the stack, then push its contents to the stack.",
construct: "Remove the top iota, then add it as the first element to the list at the top of the stack.",
deconstruct: "Remove the first iota from the list at the top of the stack, then push that iota to the stack.",
snap_list: "Remove the number at the top of the stack, then split the list at the top of the stack at the given index into a list of what came before, the iota, and what comes after.",
},

patterns_as_iotas: {
Expand Down Expand Up @@ -2006,11 +2021,17 @@

"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.",


"truly_halt.1": "This pattern forcibly halts a _Hex, ignoring any $(l:patterns/meta#hexcasting:eval)$(action)Hermes'/$ or $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambits/$ that might otherwise contain $(l:patterns/meta#hexcasting:halt)$(action)Charon's Gambit/$.",

"continue.1": "This pattern halts the current iteration of $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit/$, immediately causing the next iteration, if any, to start. Mishaps if cast outside of a special context like $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit/$.",

"eval/cc.1": "Cast a pattern or list of patterns from the stack exactly like $(l:patterns/meta#hexcasting:eval)$(action)Hermes' Gambit/$, except that a unique \"Jump\" iota is pushed to the stack beforehand. ",
"eval/cc.2": "When the \"Jump\"-iota is executed, it'll skip the rest of the patterns and jump directly to the end of the pattern list.$(p)While this may seem redundant given $(l:patterns/meta#hexcasting:halt)$(action)Charon's Gambit/$ exists, this allows you to exit $(italic)nested/$ $(l:patterns/meta#hexcasting:eval)$(action)Hermes'/$ invocations in a controlled way, where Charon only allows you to exit one.$(p)The \"Jump\" iota will apparently stay on the stack even after execution is finished... better not think about the implications of that.",

"thanatos.1": "Adds the number of patterns a _Hex is still capable of evaluating to the stack. This is reduced by one for each pattern cast by the _Hex."
"thanatos.1": "Adds the number of patterns a _Hex is still capable of evaluating to the stack. This is reduced by one for each pattern cast by the _Hex.",

"nop.1": "Does nothing. Executing it does not consume an operation, consume media, produce particles, or have any other impact on the world.",
},

circle_patterns: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
"title": "hexcasting.page.mishaps.needs_parens.title",
"text": "hexcasting.page.mishaps.needs_parens"
},
{
"type": "patchouli:text",
"title": "hexcasting.page.mishaps.needs_loop_context.title",
"text": "hexcasting.page.mishaps.needs_loop_context"
},
{
"type": "patchouli:text",
"title": "hexcasting.page.mishaps.too_many_patterns.title",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@
"input": "list",
"output": "list, any",
"text": "hexcasting.page.lists.deconstruct"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:snap_list",
"anchor": "hexcasting:snap_list",
"input": "list, number",
"output": "list, any, list",
"text": "hexcasting.page.lists.snap_list"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@
"type": "patchouli:text",
"text": "hexcasting.page.meta.halt.2"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:truly_halt",
"anchor": "hexcasting:truly_halt",
"text": "hexcasting.page.meta.truly_halt.1"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:continue",
"anchor": "hexcasting:continue",
"text": "hexcasting.page.meta.continue.1"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:nop",
"anchor": "hexcasting:nop",
"text": "hexcasting.page.meta.nop.1"
},
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:thanatos",
Expand Down
Loading
Loading