Skip to content
Open
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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.
}
Original file line number Diff line number Diff line change
@@ -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
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
Loading
Loading