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

View file

@ -1,8 +1,9 @@
package at.petrak.hexcasting.api.spell package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext 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.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 import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
/** /**
@ -15,7 +16,12 @@ interface ConstManaOperator : Operator {
fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> 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) if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size) throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc) 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.CastingContext
import at.petrak.hexcasting.api.spell.casting.SpellContinuation import at.petrak.hexcasting.api.spell.casting.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
import net.minecraft.world.phys.Vec3 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. * 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) * 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 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.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs 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.datafixers.util.Either
import com.mojang.math.Vector3f import com.mojang.math.Vector3f
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity 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 net.minecraft.world.phys.Vec3
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.roundToInt
import kotlin.math.roundToLong
fun numOrVec(datum: Iota, reverseIdx: Int): Either<Double, Vec3> = fun numOrVec(datum: Iota, reverseIdx: Int): Either<Double, Vec3> =
when (datum.payload) { when (datum) {
is Double -> Either.left(datum.payload) is DoubleIota -> Either.left(datum.double)
is Vec3 -> Either.right(datum.payload) is Vec3Iota -> Either.right(datum.vec3)
else -> throw MishapInvalidIota( else -> throw MishapInvalidIota(
datum, datum,
reverseIdx, reverseIdx,
@ -25,9 +32,9 @@ fun numOrVec(datum: Iota, reverseIdx: Int): Either<Double, Vec3> =
} }
fun numOrList(datum: Iota, reverseIdx: Int): Either<Double, SpellList> = fun numOrList(datum: Iota, reverseIdx: Int): Either<Double, SpellList> =
when (datum.payload) { when (datum) {
is Double -> Either.left(datum.payload) is DoubleIota -> Either.left(datum.double)
is SpellList -> Either.right(datum.payload) is ListIota -> Either.right(datum.list)
else -> throw MishapInvalidIota( else -> throw MishapInvalidIota(
datum, datum,
reverseIdx, reverseIdx,
@ -35,38 +42,176 @@ fun numOrList(datum: Iota, reverseIdx: Int): Either<Double, SpellList> =
) )
} }
fun spellListOf(vararg vs: Any): List<Iota> { fun List<Iota>.getDouble(idx: Int, argc: Int = 0): Double {
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 {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x.payload is T) if (x is DoubleIota) {
return x.payload return x.double
else } else {
throw MishapInvalidIota.ofClass(x, if (argc == 0) idx else argc - (idx + 1), T::class.java) 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) fun List<Iota>.getList(idx: Int, argc: Int = 0): SpellList {
inline val Double.asSpellResult get() = spellListOf(this) val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
inline val Number.asSpellResult get() = spellListOf(this.toDouble()) 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) fun List<Iota>.getPattern(idx: Int, argc: Int = 0): HexPattern {
inline val List<Iota>.asSpellResult get() = spellListOf(this) 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)) // Helpers
inline val Vector3f.asSpellResult get() = spellListOf(Vec3(this))
inline val Vec3.asSpellResult get() = spellListOf(this)
inline val Entity?.asSpellResult get() = spellListOf(this ?: Widget.NULL) fun List<Iota>.getItemEntity(idx: Int, argc: Int = 0): ItemEntity {
inline val HexPattern.asSpellResult get() = spellListOf(this) 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 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.HexConfig
import at.petrak.hexcasting.api.mod.HexItemTags import at.petrak.hexcasting.api.mod.HexItemTags
import at.petrak.hexcasting.api.mod.HexStatistics 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.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.iota.NullIota import at.petrak.hexcasting.api.spell.iota.NullIota
import at.petrak.hexcasting.api.spell.iota.PatternIota import at.petrak.hexcasting.api.spell.iota.PatternIota
import at.petrak.hexcasting.api.spell.math.HexDir 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.spell.mishaps.*
import at.petrak.hexcasting.api.utils.* import at.petrak.hexcasting.api.utils.*
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker
import at.petrak.hexcasting.common.lib.HexIotaTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag 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`) * 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 val newParenCount = this.parenCount - 1
if (newParenCount == 0) { if (newParenCount == 0) {
val newStack = this.stack.toMutableList() val newStack = this.stack.toMutableList()
newStack.add(LegacySpellDatum.make(this.parenthesized.toList())) newStack.add(ListIota(this.parenthesized.toList()))
this.getFunctionalData().copy( this.getFunctionalData().copy(
stack = newStack, stack = newStack,
parenCount = newParenCount, parenCount = newParenCount,
@ -438,7 +443,7 @@ class CastingHarness private constructor(
fun serializeToNBT() = NBTBuilder { fun serializeToNBT() = NBTBuilder {
TAG_STACK %= stack.serializeToNBT() TAG_STACK %= stack.serializeToNBT()
TAG_LOCAL %= ravenmind.serializeToNBT() TAG_LOCAL %= HexIotaTypes.serialize(ravenmind)
TAG_PAREN_COUNT %= parenCount TAG_PAREN_COUNT %= parenCount
TAG_ESCAPE_NEXT %= escapeNext TAG_ESCAPE_NEXT %= escapeNext
@ -463,23 +468,16 @@ class CastingHarness private constructor(
val stack = mutableListOf<Iota>() val stack = mutableListOf<Iota>()
val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND) val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
for (subtag in stackTag) { for (subtag in stackTag) {
val datum = LegacySpellDatum.fromNBT(subtag.asCompound, ctx.world) val datum = HexIotaTypes.deserialize(subtag.asCompound, ctx.world)
stack.add(datum) stack.add(datum)
} }
val localTag = nbt.getCompound(TAG_LOCAL) val localIota = HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world)
val localIota =
if (localTag.size() == 1) LegacySpellDatum.fromNBT(localTag, ctx.world) else LegacySpellDatum.make(
Widget.NULL
)
val parenthesized = mutableListOf<Iota>() val parenthesized = mutableListOf<Iota>()
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND) val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
for (subtag in parenTag) { for (subtag in parenTag) {
if (subtag.asCompound.size() != 1) parenthesized.add(HexIotaTypes.deserialize(nbt.getCompound(TAG_LOCAL), ctx.world))
parenthesized.add(LegacySpellDatum.make(HexPattern.fromNBT(subtag.asCompound)))
else
parenthesized.add(LegacySpellDatum.fromNBT(subtag.asCompound, ctx.world))
} }
val parenCount = nbt.getInt(TAG_PAREN_COUNT) 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>> { override fun breakDownwards(stack: List<Iota>): Pair<Boolean, List<Iota>> {
val newStack = baseStack?.toMutableList() ?: mutableListOf() val newStack = baseStack?.toMutableList() ?: mutableListOf()
acc.addAll(stack) acc.addAll(stack)
newStack.add(ListIota(SpellList.LList(acc))) newStack.add(ListIota(acc))
return true to newStack return true to newStack
} }
@ -157,7 +157,7 @@ sealed interface ContinuationFrame {
.pushFrame(Evaluate(code)) .pushFrame(Evaluate(code))
} else { } else {
// Else, dump our final list onto the stack. // Else, dump our final list onto the stack.
ListIota(SpellList.LList(acc)) to continuation ListIota(acc) to continuation
} }
val tStack = stack.toMutableList() val tStack = stack.toMutableList()
tStack.add(stackTop) tStack.add(stackTop)

View file

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

View file

@ -14,6 +14,7 @@ import org.jetbrains.annotations.Nullable;
public class GarbageIota extends Iota { public class GarbageIota extends Iota {
private static final Object NULL_SUBSTITUTE = new Object(); private static final Object NULL_SUBSTITUTE = new Object();
public static final GarbageIota INSTANCE = new GarbageIota();
public GarbageIota() { public GarbageIota() {
// We have to pass *something* here, but there's nothing that actually needs to go there, // 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 at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public abstract class Iota { public abstract class Iota {
@NotNull @NotNull
protected final Object payload; protected final Object payload;
@NotNull @NotNull
private final IotaType<?> type; protected final IotaType<?> type;
protected Iota(@NotNull IotaType<?> type, @NotNull Object payload) { protected Iota(@NotNull IotaType<?> type, @NotNull Object payload) {
this.type = type; this.type = type;
@ -37,6 +38,10 @@ public abstract class Iota {
*/ */
abstract public @NotNull Tag serialize(); 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. * 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 abstract Component display(Tag tag);
public Component display(T iota) {
return this.display(iota.serialize());
}
/** /**
* Get the color associated with this datum type. * 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 org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
/** /**
* This is a <i>wrapper</i> for {@link SpellList}. * This is a <i>wrapper</i> for {@link SpellList}.
@ -21,6 +22,10 @@ public class ListIota extends Iota {
super(HexIotaTypes.LIST, list); super(HexIotaTypes.LIST, list);
} }
public ListIota(@NotNull List<Iota> list) {
this(new SpellList.LList(list));
}
public SpellList getList() { public SpellList getList() {
return (SpellList) this.payload; return (SpellList) this.payload;
} }
@ -78,7 +83,7 @@ public class ListIota extends Iota {
out.add(subiota); out.add(subiota);
} }
return new ListIota(new SpellList.LList(out)); return new ListIota(out);
} }
@Override @Override

View file

@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nullable;
public class NullIota extends Iota { public class NullIota extends Iota {
private static final Object NULL_SUBSTITUTE = new Object(); private static final Object NULL_SUBSTITUTE = new Object();
public static final NullIota INSTANCE = new NullIota();
public NullIota() { public NullIota() {
// We have to pass *something* here, but there's nothing that actually needs to go there, // We have to pass *something* here, but there's nothing that actually needs to go there,
// so we just do this i guess // 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.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources 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.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 at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor import net.minecraft.world.item.DyeColor
@ -16,7 +18,7 @@ class MishapDivideByZero(val operand1: Component, val operand2: Component, val s
dyeColor(DyeColor.RED) dyeColor(DyeColor.RED)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) { 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) 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" private const val PREFIX = "hexcasting.mishap.divide_by_zero"
@JvmStatic @JvmStatic
fun of(operand1: Any, operand2: Any, suffix: String = "divide"): MishapDivideByZero { fun of(operand1: Iota, operand2: Iota, suffix: String = "divide"): MishapDivideByZero {
if (suffix == "exponent") if (suffix == "exponent")
return MishapDivideByZero(translate(operand1), powerOf(operand2), suffix) return MishapDivideByZero(translate(operand1), powerOf(operand2), suffix)
return MishapDivideByZero(translate(operand1), translate(operand2), suffix) return MishapDivideByZero(translate(operand1), translate(operand2), suffix)
} }
@JvmStatic @JvmStatic
fun tan(angle: Double): MishapDivideByZero { fun tan(angle: DoubleIota): MishapDivideByZero {
val translatedAngle = translate(angle) val translatedAngle = translate(angle)
return MishapDivideByZero( return MishapDivideByZero(
"$PREFIX.sin".asTranslatedComponent(translatedAngle), "$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) fun powerOf(power: Component) = "$PREFIX.power".asTranslatedComponent(power)
@JvmStatic @JvmStatic
fun powerOf(datum: Any) = when (datum) { fun powerOf(datum: Iota): Component = when {
0.0 -> zerothPower datum is DoubleIota && datum.double == 0.0 -> zerothPower
else -> powerOf(LegacySpellDatum.make(datum).display()) else -> datum.display()
} }
@JvmStatic @JvmStatic
fun translate(datum: Any): Component = when (datum) { fun translate(datum: Iota): Component = when {
0.0 -> zero datum is DoubleIota && datum.double == 0.0 -> zero
Vec3.ZERO -> zeroVector datum is Vec3Iota && datum.vec3 == Vec3.ZERO -> zeroVector
else -> LegacySpellDatum.make(datum).display() else -> datum.display()
} }
} }
} }

View file

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

View file

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

View file

@ -25,7 +25,7 @@ class OpPotionEffect(
): Triple<RenderedSpell, Int, List<ParticleSpray>> { ): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<LivingEntity>(0, argc) val target = args.getChecked<LivingEntity>(0, argc)
if (target is ArmorStand) 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) val duration = max(args.getChecked(1, argc), 0.0)
ctx.assertEntityInRange(target) ctx.assertEntityInRange(target)
val potency = if (this.allowPotency) val potency = if (this.allowPotency)

View file

@ -1,12 +1,13 @@
package at.petrak.hexcasting.common.items; package at.petrak.hexcasting.common.items;
import at.petrak.hexcasting.api.item.IotaHolderItem; 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.Iota;
import at.petrak.hexcasting.api.spell.iota.PatternIota;
import at.petrak.hexcasting.api.spell.math.HexPattern; import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.gui.PatternTooltipGreeble; import at.petrak.hexcasting.client.gui.PatternTooltipGreeble;
import at.petrak.hexcasting.common.entities.EntityWallScroll; import at.petrak.hexcasting.common.entities.EntityWallScroll;
import at.petrak.hexcasting.common.lib.HexIotaTypes;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
@ -54,14 +55,16 @@ public class ItemScroll extends Item implements IotaHolderItem {
if (pattern == null) { if (pattern == null) {
return null; return null;
} }
// We store only the data part of the iota; pretend the rest of it's there
var out = new CompoundTag(); 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; return out;
} }
@Override @Override
public boolean canWrite(ItemStack stack, Iota datum) { 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 @Override

View file

@ -14,10 +14,7 @@ import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque; import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import static at.petrak.hexcasting.api.HexAPI.modLoc; import static at.petrak.hexcasting.api.HexAPI.modLoc;
@ -112,7 +109,6 @@ public class HexIotaTypes {
* } * }
* </code> * </code>
*/ */
@Nullable
public static Iota deserialize(CompoundTag tag, ServerLevel world) { public static Iota deserialize(CompoundTag tag, ServerLevel world) {
var type = getTypeFromTag(tag); var type = getTypeFromTag(tag);
if (type == null) { if (type == null) {
@ -122,10 +118,16 @@ public class HexIotaTypes {
if (dataKey == null) { if (dataKey == null) {
return 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) { public static Component getDisplay(CompoundTag tag) {
var type = getTypeFromTag(tag); var type = getTypeFromTag(tag);
if (type == null) { if (type == null) {

View file

@ -386,8 +386,10 @@
"hexcasting.mishap.invalid_value.numvec": "a number or vector", "hexcasting.mishap.invalid_value.numvec": "a number or vector",
"hexcasting.mishap.invalid_value.numlist": "a number or list", "hexcasting.mishap.invalid_value.numlist": "a number or list",
"hexcasting.mishap.invalid_value.list.pattern": "a list of patterns", "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.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.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.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", "hexcasting.mishap.too_many_close_parens": "Used Retrospection without first using Introspection",