add eval/cc, aka continuation, aka OpEvalBreakable, aka Iris

This commit is contained in:
Alwinfy 2022-11-15 20:06:25 -05:00
parent 03dd09c6de
commit cd64f6394e
No known key found for this signature in database
GPG key ID: 2CCB99445F0C949E
10 changed files with 170 additions and 3 deletions

View file

@ -275,9 +275,10 @@ fun List<Iota>.getLongOrList(idx: Int, argc: Int = 0): Either<Long, SpellList> {
) )
} }
fun evaluatable(datum: Iota, reverseIdx: Int): Either<HexPattern, SpellList> = fun evaluatable(datum: Iota, reverseIdx: Int): Either<Iota, SpellList> =
when (datum) { when (datum) {
is PatternIota -> Either.left(datum.pattern) is PatternIota -> Either.left(datum)
is ContinuationIota -> Either.left(datum)
is ListIota -> Either.right(datum.list) is ListIota -> Either.right(datum.list)
else -> throw MishapInvalidIota( else -> throw MishapInvalidIota(
datum, datum,

View file

@ -15,6 +15,7 @@ import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.iota.Iota import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.iota.PatternIota import at.petrak.hexcasting.api.spell.iota.PatternIota
import at.petrak.hexcasting.api.spell.iota.ContinuationIota
import at.petrak.hexcasting.api.spell.math.HexDir import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.math.HexPattern import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.* import at.petrak.hexcasting.api.spell.mishaps.*
@ -150,6 +151,13 @@ class CastingHarness private constructor(
return if (iota is PatternIota) { return if (iota is PatternIota) {
updateWithPattern(iota.pattern, world, continuation) updateWithPattern(iota.pattern, world, continuation)
} else if (iota is ContinuationIota) {
CastResult(
iota.continuation,
null,
ResolvedPatternType.EVALUATED,
listOf()
)
} else { } else {
CastResult( CastResult(
continuation, continuation,

View file

@ -1,5 +1,12 @@
package at.petrak.hexcasting.api.spell.casting package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.serializeToNBT
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.server.level.ServerLevel
/** /**
* A continuation during the execution of a spell. * A continuation during the execution of a spell.
*/ */
@ -9,4 +16,32 @@ sealed interface SpellContinuation {
data class NotDone(val frame: ContinuationFrame, val next: SpellContinuation) : SpellContinuation data class NotDone(val frame: ContinuationFrame, val next: SpellContinuation) : SpellContinuation
fun pushFrame(frame: ContinuationFrame): SpellContinuation = NotDone(frame, this) fun pushFrame(frame: ContinuationFrame): SpellContinuation = NotDone(frame, this)
fun serializeToNBT() = NBTBuilder {
TAG_FRAME %= list(getNBTFrames())
}
fun getNBTFrames(): List<CompoundTag> {
var self = this
val frames = mutableListOf<CompoundTag>()
while (self is NotDone) {
frames.add(self.frame.serializeToNBT())
self = self.next
}
return frames
}
companion object {
const val TAG_FRAME = "frame"
@JvmStatic
fun fromNBT(nbt: CompoundTag, world: ServerLevel): SpellContinuation {
val frames = nbt.getList(TAG_FRAME, Tag.TAG_COMPOUND)
var result: SpellContinuation = Done
for (frame in frames.asReversed()) {
if (frame is CompoundTag) {
result = result.pushFrame(ContinuationFrame.fromNBT(frame, world))
}
}
return result
}
}
} }

View file

@ -0,0 +1,62 @@
package at.petrak.hexcasting.api.spell.iota;
import at.petrak.hexcasting.api.spell.casting.SpellContinuation;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An iota storing a continuation (in essence an execution state).
*/
public class ContinuationIota extends Iota {
public static final Component DISPLAY = Component.translatable("hexcasting.tooltip.jump_iota").withStyle(ChatFormatting.RED);
public ContinuationIota(SpellContinuation cont) {
super(HexIotaTypes.CONTINUATION, cont);
}
public SpellContinuation getContinuation() {
return (SpellContinuation) this.payload;
}
@Override
public boolean isTruthy() {
return true;
}
@Override
public boolean toleratesOther(Iota that) {
return typesMatch(this, that) && that instanceof ContinuationIota cont && cont.getContinuation().equals(getContinuation());
}
@Override
public @NotNull
Tag serialize() {
return getContinuation().serializeToNBT();
}
public static IotaType<ContinuationIota> TYPE = new IotaType<>() {
@Nullable
@Override
public ContinuationIota deserialize(Tag tag, ServerLevel world) throws IllegalArgumentException {
var compoundTag = HexUtils.downcast(tag, CompoundTag.TYPE);
return new ContinuationIota(SpellContinuation.fromNBT(compoundTag, world));
}
@Override
public Component display(Tag tag) {
return DISPLAY;
}
@Override
public int color() {
return 0xff_cc0000;
}
};
}

View file

@ -18,6 +18,7 @@ import at.petrak.hexcasting.common.casting.operators.circles.OpCircleBounds;
import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusDir; import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusDir;
import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusPos; import at.petrak.hexcasting.common.casting.operators.circles.OpImpetusPos;
import at.petrak.hexcasting.common.casting.operators.eval.OpEval; import at.petrak.hexcasting.common.casting.operators.eval.OpEval;
import at.petrak.hexcasting.common.casting.operators.eval.OpEvalBreakable;
import at.petrak.hexcasting.common.casting.operators.eval.OpForEach; import at.petrak.hexcasting.common.casting.operators.eval.OpForEach;
import at.petrak.hexcasting.common.casting.operators.eval.OpHalt; import at.petrak.hexcasting.common.casting.operators.eval.OpHalt;
import at.petrak.hexcasting.common.casting.operators.lists.*; import at.petrak.hexcasting.common.casting.operators.lists.*;
@ -343,6 +344,8 @@ public class RegisterPatterns {
// eval being a space filling curve feels apt doesn't it // eval being a space filling curve feels apt doesn't it
PatternRegistry.mapPattern(HexPattern.fromAngles("deaqq", HexDir.SOUTH_EAST), modLoc("eval"), PatternRegistry.mapPattern(HexPattern.fromAngles("deaqq", HexDir.SOUTH_EAST), modLoc("eval"),
OpEval.INSTANCE); OpEval.INSTANCE);
PatternRegistry.mapPattern(HexPattern.fromAngles("deaqqdaa", HexDir.SOUTH_EAST), modLoc("eval/cc"),
OpEvalBreakable.INSTANCE);
PatternRegistry.mapPattern(HexPattern.fromAngles("aqdee", HexDir.SOUTH_WEST), modLoc("halt"), PatternRegistry.mapPattern(HexPattern.fromAngles("aqdee", HexDir.SOUTH_WEST), modLoc("halt"),
OpHalt.INSTANCE); OpHalt.INSTANCE);

View file

@ -33,7 +33,7 @@ object OpEval : Action {
continuation.pushFrame(ContinuationFrame.FinishEval) // install a break-boundary after eval continuation.pushFrame(ContinuationFrame.FinishEval) // install a break-boundary after eval
} }
val instrsList = instrs.map({ SpellList.LList(0, listOf(PatternIota(it))) }, { it }) val instrsList = instrs.map({ SpellList.LList(0, listOf(it)) }, { it })
val frame = ContinuationFrame.Evaluate(instrsList) val frame = ContinuationFrame.Evaluate(instrsList)
return OperationResult(newCont.pushFrame(frame), stack, ravenmind, listOf()) return OperationResult(newCont.pushFrame(frame), stack, ravenmind, listOf())
} }

View file

@ -0,0 +1,41 @@
package at.petrak.hexcasting.common.casting.operators.eval
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.OperationResult
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.evaluatable
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ContinuationIota
object OpEvalBreakable : Action {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult {
val datum = stack.removeLast()
val instrs = evaluatable(datum, 0)
instrs.ifRight {
ctx.incDepth()
}
// if not installed already...
// also, never make a break boundary when evaluating just one pattern
val newCont =
if (instrs.left().isPresent || (continuation is SpellContinuation.NotDone && continuation.frame is ContinuationFrame.FinishEval)) {
continuation
} else {
continuation.pushFrame(ContinuationFrame.FinishEval) // install a break-boundary after eval
}
val instrsList = instrs.map({ SpellList.LList(0, listOf(it)) }, { it })
val frame = ContinuationFrame.Evaluate(instrsList)
stack.add(ContinuationIota(continuation))
return OperationResult(newCont.pushFrame(frame), stack, ravenmind, listOf())
}
}

View file

@ -188,6 +188,7 @@ public class HexIotaTypes {
public static final IotaType<PatternIota> PATTERN = type("pattern", PatternIota.TYPE); public static final IotaType<PatternIota> PATTERN = type("pattern", PatternIota.TYPE);
public static final IotaType<GarbageIota> GARBAGE = type("garbage", GarbageIota.TYPE); public static final IotaType<GarbageIota> GARBAGE = type("garbage", GarbageIota.TYPE);
public static final IotaType<Vec3Iota> VEC3 = type("vec3", Vec3Iota.TYPE); public static final IotaType<Vec3Iota> VEC3 = type("vec3", Vec3Iota.TYPE);
public static final IotaType<ContinuationIota> CONTINUATION = type("continuation", ContinuationIota.TYPE);
private static <U extends Iota, T extends IotaType<U>> T type(String name, T type) { private static <U extends Iota, T extends IotaType<U>> T type(String name, T type) {

View file

@ -149,6 +149,7 @@
"hexcasting.tooltip.list_contents": "[%s]", "hexcasting.tooltip.list_contents": "[%s]",
"hexcasting.tooltip.pattern_iota": "HexPattern(%s)", "hexcasting.tooltip.pattern_iota": "HexPattern(%s)",
"hexcasting.tooltip.null_iota": "NULL", "hexcasting.tooltip.null_iota": "NULL",
"hexcasting.tooltip.jump_iota": "[JUMP]",
"hexcasting.tooltip.boolean_true": "True", "hexcasting.tooltip.boolean_true": "True",
"hexcasting.tooltip.boolean_false": "False", "hexcasting.tooltip.boolean_false": "False",
@ -448,6 +449,7 @@
"hexcasting.spell.hexcasting:close_paren": "Retrospection", "hexcasting.spell.hexcasting:close_paren": "Retrospection",
"hexcasting.spell.hexcasting:escape": "Consideration", "hexcasting.spell.hexcasting:escape": "Consideration",
"hexcasting.spell.hexcasting:eval": "Hermes' Gambit", "hexcasting.spell.hexcasting:eval": "Hermes' Gambit",
"hexcasting.spell.hexcasting:eval/cc": "Iris' Gambit",
"hexcasting.spell.hexcasting:for_each": "Thoth's Gambit", "hexcasting.spell.hexcasting:for_each": "Thoth's Gambit",
"hexcasting.spell.hexcasting:halt": "Charon's Gambit", "hexcasting.spell.hexcasting:halt": "Charon's Gambit",
"hexcasting.spell.hexcasting:number": "Numerical Reflection: %s", "hexcasting.spell.hexcasting:number": "Numerical Reflection: %s",
@ -994,6 +996,8 @@
"hexcasting.page.meta.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.", "hexcasting.page.meta.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.",
"hexcasting.page.meta.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.", "hexcasting.page.meta.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.",
"hexcasting.page.meta.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.", "hexcasting.page.meta.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.",
"hexcasting.page.meta.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. ",
"hexcasting.page.meta.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.",
"hexcasting.entry.circle_patterns": "Spell Circle Patterns", "hexcasting.entry.circle_patterns": "Spell Circle Patterns",
"hexcasting.page.circle_patterns.disclaimer": "These patterns must be cast from a $(l:greatwork/spellcircles)$(item)Spell Circle/$; trying to cast them through a $(l:items/staff)$(item)Staff/$ will fail rather spectacularly.", "hexcasting.page.circle_patterns.disclaimer": "These patterns must be cast from a $(l:greatwork/spellcircles)$(item)Spell Circle/$; trying to cast them through a $(l:items/staff)$(item)Staff/$ will fail rather spectacularly.",

View file

@ -18,6 +18,18 @@
"type": "patchouli:text", "type": "patchouli:text",
"text": "hexcasting.page.meta.eval.2" "text": "hexcasting.page.meta.eval.2"
}, },
{
"type": "hexcasting:pattern",
"op_id": "hexcasting:eval/cc",
"anchor": "hexcasting:eval/cc",
"input": "[pattern] | pattern",
"output": "many",
"text": "hexcasting.page.meta.eval/cc.1"
},
{
"type": "patchouli:text",
"text": "hexcasting.page.meta.eval/cc.2"
},
{ {
"type": "hexcasting:pattern", "type": "hexcasting:pattern",
"op_id": "hexcasting:for_each", "op_id": "hexcasting:for_each",