and now for the part of the show where we refactor every single operator

This commit is contained in:
gamma-delta 2022-06-13 22:02:50 -05:00
parent 8261063208
commit 71c3cade9b
20 changed files with 275 additions and 147 deletions

View file

@ -5,10 +5,10 @@ import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.misc.ManaConstants;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.spell.casting.CastingContext;
import at.petrak.hexcasting.api.spell.casting.CastingHarness;
import at.petrak.hexcasting.api.spell.casting.SpellCircleContext;
import at.petrak.hexcasting.api.spell.iota.PatternIota;
import at.petrak.hexcasting.api.utils.ManaHelper;
import at.petrak.hexcasting.common.lib.HexItems;
import at.petrak.hexcasting.common.lib.HexSounds;
@ -287,7 +287,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
if (bs.getBlock() instanceof BlockCircleComponent cc) {
var newPattern = cc.getPattern(tracked, bs, this.level);
if (newPattern != null) {
var info = harness.executeIota(LegacySpellDatum.make(newPattern), splayer.getLevel());
var info = harness.executeIota(new PatternIota(newPattern), splayer.getLevel());
if (info.getMakesCastSound()) {
makeSound = true;
}

View file

@ -1,8 +1,9 @@
package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
/**
@ -15,7 +16,12 @@ interface ConstManaOperator : Operator {
fun execute(args: List<Iota>, ctx: CastingContext): List<Iota>
override fun operate(continuation: SpellContinuation, stack: MutableList<Iota>, local: Iota, ctx: CastingContext): OperationResult {
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ctx: CastingContext
): OperationResult {
if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc)

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
import net.minecraft.world.phys.Vec3
/**
@ -21,7 +22,12 @@ interface Operator {
*
* A particle effect at the cast site and various messages and advancements are done automagically.
*/
fun operate(continuation: SpellContinuation, stack: MutableList<Iota>, local: Iota, ctx: CastingContext): OperationResult
fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
local: Iota,
ctx: CastingContext
): OperationResult
/**
* Do you need to be enlightened to use this operator? (i.e. is this operator a Great Pattern)

View file

@ -2,6 +2,7 @@
package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.iota.*
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
@ -9,14 +10,20 @@ import at.petrak.hexcasting.api.utils.asTranslatedComponent
import com.mojang.datafixers.util.Either
import com.mojang.math.Vector3f
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.npc.Villager
import net.minecraft.world.phys.Vec3
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.roundToLong
fun numOrVec(datum: Iota, reverseIdx: Int): Either<Double, Vec3> =
when (datum.payload) {
is Double -> Either.left(datum.payload)
is Vec3 -> Either.right(datum.payload)
when (datum) {
is DoubleIota -> Either.left(datum.double)
is Vec3Iota -> Either.right(datum.vec3)
else -> throw MishapInvalidIota(
datum,
reverseIdx,
@ -25,9 +32,9 @@ fun numOrVec(datum: Iota, reverseIdx: Int): Either<Double, Vec3> =
}
fun numOrList(datum: Iota, reverseIdx: Int): Either<Double, SpellList> =
when (datum.payload) {
is Double -> Either.left(datum.payload)
is SpellList -> Either.right(datum.payload)
when (datum) {
is DoubleIota -> Either.left(datum.double)
is ListIota -> Either.right(datum.list)
else -> throw MishapInvalidIota(
datum,
reverseIdx,
@ -35,38 +42,176 @@ fun numOrList(datum: Iota, reverseIdx: Int): Either<Double, SpellList> =
)
}
fun spellListOf(vararg vs: Any): List<Iota> {
val out = ArrayList<Iota>(vs.size)
for (v in vs) {
out.add(LegacySpellDatum.make(v))
}
return out
}
inline fun <reified T : Any> List<Iota>.getChecked(idx: Int, argc: Int = 0): T {
fun List<Iota>.getDouble(idx: Int, argc: Int = 0): Double {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x.payload is T)
return x.payload
else
throw MishapInvalidIota.ofClass(x, if (argc == 0) idx else argc - (idx + 1), T::class.java)
if (x is DoubleIota) {
return x.double
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "double")
}
}
fun List<Iota>.getEntity(idx: Int, argc: Int = 0): Entity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
return x.entity
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity")
}
}
inline val Boolean.asSpellResult get() = spellListOf(if (this) 1.0 else 0.0)
inline val Double.asSpellResult get() = spellListOf(this)
inline val Number.asSpellResult get() = spellListOf(this.toDouble())
fun List<Iota>.getList(idx: Int, argc: Int = 0): SpellList {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is ListIota) {
return x.list
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "list")
}
}
inline val SpellList.asSpellResult get() = spellListOf(this)
inline val List<Iota>.asSpellResult get() = spellListOf(this)
fun List<Iota>.getPattern(idx: Int, argc: Int = 0): HexPattern {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is PatternIota) {
return x.pattern
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "pattern")
}
}
inline val Widget.asSpellResult get() = spellListOf(this)
fun List<Iota>.getVec3(idx: Int, argc: Int = 0): Vec3 {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is Vec3Iota) {
return x.vec3
} else {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "vector")
}
}
inline val BlockPos.asSpellResult get() = spellListOf(Vec3.atCenterOf(this))
inline val Vector3f.asSpellResult get() = spellListOf(Vec3(this))
inline val Vec3.asSpellResult get() = spellListOf(this)
// Helpers
inline val Entity?.asSpellResult get() = spellListOf(this ?: Widget.NULL)
inline val HexPattern.asSpellResult get() = spellListOf(this)
fun List<Iota>.getItemEntity(idx: Int, argc: Int = 0): ItemEntity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
if (e is ItemEntity)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.item")
}
fun List<Iota>.getPlayer(idx: Int, argc: Int = 0): ServerPlayer {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
if (e is ServerPlayer)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.player")
}
fun List<Iota>.getVillager(idx: Int, argc: Int = 0): Villager {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
if (e is Villager)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.villager")
}
fun List<Iota>.getLivingEntity(idx: Int, argc: Int = 0): LivingEntity {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
if (e is LivingEntity)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.living")
}
fun List<Iota>.getPositiveDoubleB(idx: Int, argc: Int = 0): Double {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
if (0 <= double) {
return double
}
}
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "double.positive")
}
fun List<Iota>.getDoubleBetween(idx: Int, min: Double, max: Double, argc: Int = 0): Double {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
if (double in min..max) {
return double
}
}
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "double.between", min, max)
}
fun List<Iota>.getInt(idx: Int, argc: Int = 0): Int {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
val rounded = double.roundToInt()
if (abs(double - rounded) <= DoubleIota.TOLERANCE) {
return rounded
}
}
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "int")
}
fun List<Iota>.getLong(idx: Int, argc: Int = 0): Long {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
val rounded = double.roundToLong()
if (abs(double - rounded) <= DoubleIota.TOLERANCE) {
return rounded
}
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "int") // shh we're lying
}
fun List<Iota>.getPositiveInt(idx: Int, argc: Int = 0): Int {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
val rounded = double.roundToInt()
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded > 0) {
return rounded
}
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "int.positive")
}
fun List<Iota>.getIntBetween(idx: Int, min: Int, max: Int, argc: Int = 0): Int {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is DoubleIota) {
val double = x.double
val rounded = double.roundToInt()
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded in min..max) {
return rounded
}
}
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "double.between", min, max)
}
inline val Boolean.asSpellResult get() = listOf(DoubleIota(if (this) 1.0 else 0.0))
inline val Double.asSpellResult get() = listOf(DoubleIota(this))
inline val Number.asSpellResult get() = listOf(DoubleIota(this.toDouble()))
inline val SpellList.asSpellResult get() = listOf(ListIota(this))
inline val List<Iota>.asSpellResult get() = listOf(ListIota(this))
inline val BlockPos.asSpellResult get() = listOf(Vec3Iota(Vec3.atCenterOf(this)))
inline val Vector3f.asSpellResult get() = listOf(Vec3Iota(Vec3(this)))
inline val Vec3.asSpellResult get() = listOf(Vec3Iota(this))
inline val Entity?.asSpellResult get() = listOf(if (this == null) NullIota.INSTANCE else EntityIota(this))
inline val HexPattern.asSpellResult get() = listOf(PatternIota(this))
private const val TOLERANCE = 0.0001

View file

@ -1,29 +0,0 @@
package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext
import java.util.*
/**
* Miscellaneous spell datums used as markers, etc.
*
* They act as operators that push themselves.
*/
enum class Widget : ConstManaOperator {
NULL,
OPEN_PAREN, CLOSE_PAREN, ESCAPE,
GARBAGE;
override val argc: Int
get() = 0
override fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> =
this.asSpellResult
companion object {
@JvmStatic
fun fromString(key: String): Widget {
val lowercaseKey = key.lowercase(Locale.ROOT)
return values().firstOrNull { it.name.lowercase(Locale.ROOT) == lowercaseKey } ?: GARBAGE
}
}
}

View file

@ -8,8 +8,12 @@ import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.mod.HexItemTags
import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.Operator
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.Widget
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.iota.NullIota
import at.petrak.hexcasting.api.spell.iota.PatternIota
import at.petrak.hexcasting.api.spell.math.HexDir
@ -17,6 +21,7 @@ import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.*
import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker
import at.petrak.hexcasting.common.lib.HexIotaTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
@ -240,7 +245,7 @@ class CastingHarness private constructor(
}
}
fun generateDescs() = stack.map { it.type.display(it) }
fun generateDescs() = stack.map(Iota::display)
/**
* Return the functional update represented by the current state (for use with `copy`)
@ -300,7 +305,7 @@ class CastingHarness private constructor(
val newParenCount = this.parenCount - 1
if (newParenCount == 0) {
val newStack = this.stack.toMutableList()
newStack.add(LegacySpellDatum.make(this.parenthesized.toList()))
newStack.add(ListIota(this.parenthesized.toList()))
this.getFunctionalData().copy(
stack = newStack,
parenCount = newParenCount,
@ -438,7 +443,7 @@ class CastingHarness private constructor(
fun serializeToNBT() = NBTBuilder {
TAG_STACK %= stack.serializeToNBT()
TAG_LOCAL %= ravenmind.serializeToNBT()
TAG_LOCAL %= HexIotaTypes.serialize(ravenmind)
TAG_PAREN_COUNT %= parenCount
TAG_ESCAPE_NEXT %= escapeNext
@ -463,23 +468,16 @@ class CastingHarness private constructor(
val stack = mutableListOf<Iota>()
val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
for (subtag in stackTag) {
val datum = LegacySpellDatum.fromNBT(subtag.asCompound, ctx.world)
val datum = HexIotaTypes.deserialize(subtag.asCompound, ctx.world)
stack.add(datum)
}
val localTag = nbt.getCompound(TAG_LOCAL)
val localIota =
if (localTag.size() == 1) LegacySpellDatum.fromNBT(localTag, ctx.world) else LegacySpellDatum.make(
Widget.NULL
)
val localIota = HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
val parenthesized = mutableListOf<Iota>()
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
for (subtag in parenTag) {
if (subtag.asCompound.size() != 1)
parenthesized.add(LegacySpellDatum.make(HexPattern.fromNBT(subtag.asCompound)))
else
parenthesized.add(LegacySpellDatum.fromNBT(subtag.asCompound, ctx.world))
parenthesized.add(HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world))
}
val parenCount = nbt.getInt(TAG_PAREN_COUNT)

View file

@ -127,7 +127,7 @@ sealed interface ContinuationFrame {
override fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>> {
val newStack = baseStack?.toMutableList() ?: mutableListOf()
acc.addAll(stack)
newStack.add(ListIota(SpellList.LList(acc)))
newStack.add(ListIota(acc))
return true to newStack
}
@ -157,7 +157,7 @@ sealed interface ContinuationFrame {
.pushFrame(Evaluate(code))
} else {
// Else, dump our final list onto the stack.
ListIota(SpellList.LList(acc)) to continuation
ListIota(acc) to continuation
}
val tStack = stack.toMutableList()
tStack.add(stackTop)

View file

@ -13,7 +13,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class EntityIota extends Iota {
protected EntityIota(@NotNull Entity e) {
public EntityIota(@NotNull Entity e) {
super(HexIotaTypes.ENTITY, e);
}
@ -36,6 +36,11 @@ public class EntityIota extends Iota {
return out;
}
@Override
public Component display() {
return this.getEntity().getName();
}
public static IotaType<EntityIota> TYPE = new IotaType<>() {
@Nullable
@Override
@ -65,11 +70,6 @@ public class EntityIota extends Iota {
return Component.Serializer.fromJsonLenient(nameJson);
}
@Override
public Component display(EntityIota iota) {
return iota.getEntity().getName();
}
@Override
public int color() {
return 0;

View file

@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable;
public class GarbageIota extends Iota {
private static final Object NULL_SUBSTITUTE = new Object();
public static final GarbageIota INSTANCE = new GarbageIota();
public GarbageIota() {
// We have to pass *something* here, but there's nothing that actually needs to go there,

View file

@ -2,13 +2,14 @@ package at.petrak.hexcasting.api.spell.iota;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull;
public abstract class Iota {
@NotNull
protected final Object payload;
@NotNull
private final IotaType<?> type;
protected final IotaType<?> type;
protected Iota(@NotNull IotaType<?> type, @NotNull Object payload) {
this.type = type;
@ -37,6 +38,10 @@ public abstract class Iota {
*/
abstract public @NotNull Tag serialize();
public Component display() {
return this.type.display(this.serialize());
}
/**
* Helper method to see if two iotas have the same type.
*/

View file

@ -26,10 +26,6 @@ public abstract class IotaType<T extends Iota> {
*/
public abstract Component display(Tag tag);
public Component display(T iota) {
return this.display(iota.serialize());
}
/**
* Get the color associated with this datum type.
*/

View file

@ -12,6 +12,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* This is a <i>wrapper</i> for {@link SpellList}.
@ -21,6 +22,10 @@ public class ListIota extends Iota {
super(HexIotaTypes.LIST, list);
}
public ListIota(@NotNull List<Iota> list) {
this(new SpellList.LList(list));
}
public SpellList getList() {
return (SpellList) this.payload;
}
@ -78,7 +83,7 @@ public class ListIota extends Iota {
out.add(subiota);
}
return new ListIota(new SpellList.LList(out));
return new ListIota(out);
}
@Override

View file

@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nullable;
public class NullIota extends Iota {
private static final Object NULL_SUBSTITUTE = new Object();
public static final NullIota INSTANCE = new NullIota();
public NullIota() {
// We have to pass *something* here, but there's nothing that actually needs to go there,
// so we just do this i guess

View file

@ -2,9 +2,11 @@ package at.petrak.hexcasting.api.spell.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.Widget
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.DoubleIota
import at.petrak.hexcasting.api.spell.iota.GarbageIota
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.Vec3Iota
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor
@ -16,7 +18,7 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
dyeColor(DyeColor.RED)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
stack.add(LegacySpellDatum.make(Widget.GARBAGE))
stack.add(GarbageIota.INSTANCE)
trulyHurt(ctx.caster, HexDamageSources.OVERCAST, ctx.caster.health / 2)
}
@ -27,14 +29,14 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
private const val PREFIX = "hexcasting.mishap.divide_by_zero"
@JvmStatic
fun of(operand1: Any, operand2: Any, suffix: String = "divide"): MishapDivideByZero {
fun of(operand1: Iota, operand2: Iota, suffix: String = "divide"): MishapDivideByZero {
if (suffix == "exponent")
return MishapDivideByZero(translate(operand1), powerOf(operand2), suffix)
return MishapDivideByZero(translate(operand1), translate(operand2), suffix)
}
@JvmStatic
fun tan(angle: Double): MishapDivideByZero {
fun tan(angle: DoubleIota): MishapDivideByZero {
val translatedAngle = translate(angle)
return MishapDivideByZero(
"$PREFIX.sin".asTranslatedComponent(translatedAngle),
@ -58,16 +60,16 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
fun powerOf(power: Component) = "$PREFIX.power".asTranslatedComponent(power)
@JvmStatic
fun powerOf(datum: Any) = when (datum) {
0.0 -> zerothPower
else -> powerOf(LegacySpellDatum.make(datum).display())
fun powerOf(datum: Iota): Component = when {
datum is DoubleIota && datum.double == 0.0 -> zerothPower
else -> datum.display()
}
@JvmStatic
fun translate(datum: Any): Component = when (datum) {
0.0 -> zero
Vec3.ZERO -> zeroVector
else -> LegacySpellDatum.make(datum).display()
fun translate(datum: Iota): Component = when {
datum is DoubleIota && datum.double == 0.0 -> zero
datum is Vec3Iota && datum.vec3 == Vec3.ZERO -> zeroVector
else -> datum.display()
}
}
}

View file

@ -1,20 +1,12 @@
package at.petrak.hexcasting.api.spell.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.Widget
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.iota.GarbageIota
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.npc.Villager
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.DyeColor
import net.minecraft.world.phys.Vec3
/**
* The value failed some kind of predicate.
@ -30,32 +22,25 @@ class MishapInvalidIota(
dyeColor(DyeColor.GRAY)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
stack[stack.size - 1 - reverseIdx] = LegacySpellDatum.make(Widget.GARBAGE)
stack[stack.size - 1 - reverseIdx] = GarbageIota.INSTANCE;
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
error("invalid_value", actionName(errorCtx.action), expected, reverseIdx,
perpetrator.display())
error(
"invalid_value", actionName(errorCtx.action), expected, reverseIdx,
perpetrator.display()
)
companion object {
@JvmStatic
fun ofClass(perpetrator: Iota, reverseIdx: Int, cls: Class<*>): MishapInvalidIota {
val key = "hexcasting.mishap.invalid_value.class." + when {
Double::class.java.isAssignableFrom(cls) || Double::class.javaObjectType.isAssignableFrom(cls) -> "double"
Vec3::class.java.isAssignableFrom(cls) -> "vector"
SpellList::class.java.isAssignableFrom(cls) -> "list"
Widget::class.java.isAssignableFrom(cls) -> "widget"
HexPattern::class.java.isAssignableFrom(cls) -> "pattern"
fun ofType(perpetrator: Iota, reverseIdx: Int, name: String): MishapInvalidIota {
return of(perpetrator, reverseIdx, "class.$name")
}
ItemEntity::class.java.isAssignableFrom(cls) -> "entity.item"
Player::class.java.isAssignableFrom(cls) -> "entity.player"
Villager::class.java.isAssignableFrom(cls) -> "entity.villager"
LivingEntity::class.java.isAssignableFrom(cls) -> "entity.living"
Entity::class.java.isAssignableFrom(cls) -> "entity"
else -> "unknown"
}
return MishapInvalidIota(perpetrator, reverseIdx, key.asTranslatedComponent)
@JvmStatic
fun of(perpetrator: Iota, reverseIdx: Int, name: String, vararg translations: Any): MishapInvalidIota {
val key = "hexcasting.mishap.invalid_value.$name"
return MishapInvalidIota(perpetrator, reverseIdx, key.asTranslatedComponent(*translations))
}
}
}

View file

@ -2,7 +2,6 @@
package at.petrak.hexcasting.api.utils
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.math.HexCoord
@ -237,7 +236,7 @@ fun Iterable<Iota>.serializeToNBT() =
if (HexIotaTypes.isTooLargeToSerialize(this))
ListTag()
else
ListIota(SpellList.LList(this.toList())).serialize()
ListIota(this.toList()).serialize()
// Copy the impl from forge
fun ItemStack.serializeToNBT(): CompoundTag {

View file

@ -25,7 +25,7 @@ class OpPotionEffect(
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<LivingEntity>(0, argc)
if (target is ArmorStand)
throw MishapInvalidIota.ofClass(LegacySpellDatum.make(target), 0, LivingEntity::class.java)
throw MishapInvalidIota.ofType(LegacySpellDatum.make(target), 0, LivingEntity::class.java)
val duration = max(args.getChecked(1, argc), 0.0)
ctx.assertEntityInRange(target)
val potency = if (this.allowPotency)

View file

@ -1,12 +1,13 @@
package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem;
import at.petrak.hexcasting.api.spell.DatumType;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.spell.iota.PatternIota;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.gui.PatternTooltipGreeble;
import at.petrak.hexcasting.common.entities.EntityWallScroll;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@ -54,14 +55,16 @@ public class ItemScroll extends Item implements IotaHolderItem {
if (pattern == null) {
return null;
}
// We store only the data part of the iota; pretend the rest of it's there
var out = new CompoundTag();
out.put(LegacySpellDatum.TAG_PATTERN, pattern);
out.putString(HexIotaTypes.KEY_TYPE, "hexcasting:pattern");
out.put(HexIotaTypes.KEY_DATA, pattern);
return out;
}
@Override
public boolean canWrite(ItemStack stack, Iota datum) {
return datum != null && datum.getType() == DatumType.PATTERN && !NBTHelper.hasCompound(stack, TAG_PATTERN);
return datum instanceof PatternIota && !NBTHelper.hasCompound(stack, TAG_PATTERN);
}
@Override

View file

@ -14,10 +14,7 @@ import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
import java.util.function.BiConsumer;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
@ -112,7 +109,6 @@ public class HexIotaTypes {
* }
* </code>
*/
@Nullable
public static Iota deserialize(CompoundTag tag, ServerLevel world) {
var type = getTypeFromTag(tag);
if (type == null) {
@ -122,10 +118,16 @@ public class HexIotaTypes {
if (dataKey == null) {
return null;
}
return type.deserialize(tag, world);
Iota deserialized;
try {
deserialized = Objects.requireNonNullElse(type.deserialize(tag, world), NullIota.INSTANCE);
} catch (IllegalArgumentException exn) {
HexAPI.LOGGER.warn("Caught an exception deserializing an iota", exn);
deserialized = GarbageIota.INSTANCE;
}
return deserialized;
}
@Nullable
public static Component getDisplay(CompoundTag tag) {
var type = getTypeFromTag(tag);
if (type == null) {

View file

@ -386,8 +386,10 @@
"hexcasting.mishap.invalid_value.numvec": "a number or vector",
"hexcasting.mishap.invalid_value.numlist": "a number or list",
"hexcasting.mishap.invalid_value.list.pattern": "a list of patterns",
"hexcasting.mishap.invalid_value.int": "an integer",
"hexcasting.mishap.invalid_value.double.positive": "a positive number",
"hexcasting.mishap.invalid_value.double.between": "a number between %d and %d",
"hexcasting.mishap.invalid_value.int": "an integer",
"hexcasting.mishap.invalid_value.int.positive": "a positive integer",
"hexcasting.mishap.invalid_value.int.between": "an integer between %d and %d",
"hexcasting.mishap.not_enough_args": "%s expected %s or more arguments but the stack was only %s tall",
"hexcasting.mishap.too_many_close_parens": "Used Retrospection without first using Introspection",