add eval/cc, aka continuation, aka OpEvalBreakable, aka Iris
This commit is contained in:
parent
03dd09c6de
commit
cd64f6394e
10 changed files with 170 additions and 3 deletions
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Reference in a new issue