From fc5d95700fc4a10dac8007bbcbfe1abbeafcbb3a Mon Sep 17 00:00:00 2001 From: poolcritter Date: Thu, 18 Jun 2026 03:11:32 -0400 Subject: [PATCH] fix(hexic/views): make transfer spells properly rendered Change-Id: Ifaf4f93d9cfb89b6acccb25453a335466a6a6964 No-Tests: not hexable Skip-Changelog: nobody noticed --- .../scala/org/eu/net/pool/hexic/views.scala | 116 +++++++++++++----- 1 file changed, 85 insertions(+), 31 deletions(-) diff --git a/project/hexic/src/main/scala/org/eu/net/pool/hexic/views.scala b/project/hexic/src/main/scala/org/eu/net/pool/hexic/views.scala index 7c7646c..de6dcf4 100644 --- a/project/hexic/src/main/scala/org/eu/net/pool/hexic/views.scala +++ b/project/hexic/src/main/scala/org/eu/net/pool/hexic/views.scala @@ -1,11 +1,15 @@ package org.eu.net.pool.hexic +import at.petrak.hexcasting.api.casting.RenderedSpell import at.petrak.hexcasting.api.casting.arithmetic.Arithmetic import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus -import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.eval.{CastingEnvironment, OperationResult} import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv +import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage import at.petrak.hexcasting.api.casting.iota.* -import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapInvalidIota, MishapOthersName} +import at.petrak.hexcasting.api.casting.mishaps.{Mishap, MishapInvalidIota, MishapNotEnoughArgs, MishapOthersName} +import at.petrak.hexcasting.common.lib.hex.HexEvalSounds import com.mojang.serialization.Codec import com.samsthenerd.inline.api.InlineAPI import com.samsthenerd.inline.api.data.ItemInlineData @@ -259,6 +263,16 @@ object SlotReference extends Registrar[SlotReference.Type[?]]("slot"): object id: def unapply(x: Identifier) = (x.getNamespace, x.getPath) +case class Consumable[+T](val value: T): + private var wasConsumed = false + def isConsumed = wasConsumed + def consume(): Unit = + wasConsumed = true +object Consumable: + given [T: Using.Releasable as r] => Using.Releasable[Consumable[T]] = c => + if !c.isConsumed then + r.release(c.value) + def initViews() = hexXplat.getIotaTypeRegistry("transfer_type") = VariantIota hexXplat.getIotaTypeRegistry("inventory_view") = BoxedView @@ -316,36 +330,74 @@ def initViews() = case Seq(_: BoxedView.Instance, perp) => throw MishapInvalidIota(perp, 0, VariantIota.typeName) case Seq(perp, _) => throw MishapInvalidIota(perp, 1, BoxedView.typeName) Patterns.register("moveconcept", se"wawdwawqdewewedqwawdwaw"): - Patterns.mkConstAction(4): - case Seq(isIota[BoxedView.Instance, 3](BoxedView.Instance(from)), isIota[BoxedView.Instance, 2](BoxedView.Instance(into)), typ: VariantIota[?], isIota[DoubleIota, 0](count)) => - val key = ClassTag(typ.data.getClass) - val scale = conceptScale(key) - @tailrec def negotiate(limit: Long): Seq[Iota] = - val compromise = Using.resource(Transaction.openOuter()): + Patterns.mkAction: (img, cont) => + img.getStack.toSeq match + case stack + :+ isIota[BoxedView.Instance, 3](BoxedView.Instance(from)) + :+ isIota[BoxedView.Instance, 2](BoxedView.Instance(into)) + :+ isIota[VariantIota.Any, 1](typ) + :+ isIota[DoubleIota, 0](count) + => + val variant: TransferVariant[?] = typ.data + val key = ClassTag(variant.getClass) + val scale = conceptScale(key) + @tailrec def negotiate(limit: Long): (Transaction, Long) = + val compromise = Using.resource(Consumable(Transaction.openOuter())): + case cx@Consumable(tx@given TransactionContext) => + val extracted = (from: InventoryView).tryExtract(variant, limit) + val inserted = (into: InventoryView).tryInsert(variant, extracted) + if extracted == inserted then + cx.consume() + return (tx, inserted) + else + inserted + negotiate(compromise) + val initialLimit = Using.resource(Transaction.openOuter()): case tx@given TransactionContext => - val extracted = (from: InventoryView).tryExtract((typ: VariantIota[?]).data, limit) - val inserted = (into: InventoryView).tryInsert((typ: VariantIota[?]).data, extracted) - if extracted == inserted then - tx.commit() - return Seq(DoubleIota(inserted / scale)) - else - inserted - negotiate(compromise) - val initialLimit = Using.resource(Transaction.openOuter()): - case tx@given TransactionContext => - val value = (into: InventoryView).tryInsert(typ.data, (scale * count.getDouble).toLong) - tx.abort() - value - negotiate(initialLimit) - + val value = (into: InventoryView).tryInsert(variant, (scale * count.getDouble).toLong) + tx.abort() + value + val (tx, actual) = negotiate(initialLimit) + OperationResult(newImage = img(stack = stack), + sideEffects = Seq( + OperatorSideEffect.AttemptSpell( + new RenderedSpell with FinalizedSpell: + override def cast(env: CastingEnvironment): Unit = sys.error("use cast(CastingEnvironment, CastingImage) instead") + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = + tx.commit() + img.withStack(_ :+ DoubleIota(actual / scale)) + override def postCast(env: CastingEnvironment): Unit = + tx.close() + , true, true + ) + ), + newContinuation = cont, + sound = HexEvalSounds.SPELL) + case stack => throw MishapNotEnoughArgs(got = stack.size, expected = 4) Patterns.register("moveentity", se"edeeewawdweaaddaqwqwqaddaaewdwawewdqd"): - Patterns.mkConstAction(3): - case Seq(isIota[BoxedView.Instance, 2](from), isIota[BoxedView.Instance, 1](into), isIota[DoubleIota, 0](count)) => - Using.resource(Transaction.openOuter()): - case tx@given TransactionContext => - val count = from.view.entities.count(into.view.teleportEntity) - if count > 0 then tx.commit() - Seq(DoubleIota(count)) + Patterns.mkAction: (img, cont) => + img.getStack.toSeq match + case stack :+ isIota[BoxedView.Instance, 2](from) :+ isIota[BoxedView.Instance, 1](into) :+ isIota[DoubleIota, 0](maxCount) => + Using.resource(Consumable(Transaction.openOuter())): + case cx@Consumable(tx@given TransactionContext) => + val count = LazyList.from(from.view.entities).filter(into.view.teleportEntity).take(maxCount.asIntOrThrow(0)).length + cx.consume() + OperationResult(newImage = img(stack = stack), + sideEffects = Seq( + OperatorSideEffect.AttemptSpell( + new RenderedSpell with FinalizedSpell: + override def cast(env: CastingEnvironment): Unit = sys.error("use cast(CastingEnvironment, CastingImage) instead") + override def cast(env: CastingEnvironment, img: CastingImage): CastingImage = + tx.commit() + img.withStack(_ :+ DoubleIota(count)) + override def postCast(env: CastingEnvironment): Unit = + tx.close() + , true, true + ) + ), + newContinuation = cont, + sound = HexEvalSounds.SPELL) + case stack => throw MishapNotEnoughArgs(got = stack.size, expected = 3) Patterns.register("thinkaboutit", e"awqawewaqw"): Patterns.mkConstAction(1): case Seq(iota) => @@ -396,7 +448,8 @@ case class VariantIota[T: ClassTag](data: TransferVariant[T], key: RegistryKey[V override def serialize: NbtElement = data.toNbt tap(_.putString("type", key.getValue.toString)) //noinspection UnstableApiUsage -object VariantIota extends IotaType[VariantIota[?]], Registrar[VariantIota.Reader]("transfer_variants"): +object VariantIota extends IotaType[VariantIota.Any], Registrar[VariantIota.Reader]("transfer_variants"): + type Any = VariantIota[?] given IotaType[VariantIota[?]] = this type Reader = NbtCompound => Option[VariantIota.TaggedVariant] def apply[T: ClassTag](data: TransferVariant[T], id: Identifier): VariantIota[T] = apply(data, RegistryKey.of(key, id)) @@ -463,6 +516,7 @@ object VariantIota extends IotaType[VariantIota[?]], Registrar[VariantIota.Reade type T = SingletonVariant def variant = SingletonVariant.heat def display: Text = Text.literal("Heat").styled(_.withColor(0xe08355))) +given IotaType[VariantIota.Any] = VariantIota //noinspection UnstableApiUsage class SingletonVariant extends TransferVariant[SingletonVariant]: -- 2.51.2