diff --git a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java index 8ddde5669b..d99462fe29 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java @@ -213,17 +213,6 @@ default ArmorMaterial robesMaterial() { return DUMMY_ARMOR_MATERIAL; } - /** - * Location in the userdata of the ravenmind - */ - String RAVENMIND_USERDATA = modLoc("ravenmind").toString(); - /** - * Location in the userdata of the number of ops executed - */ - String OP_COUNT_USERDATA = modLoc("op_count").toString(); - - String MARKED_MOVED_USERDATA = modLoc("impulsed").toString(); - static HexAPI instance() { return INSTANCE.get(); } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt index a3963d7640..c26c2023be 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/Action.kt @@ -30,9 +30,6 @@ interface Action { * remember to increment the op count, sil vous plait. * * A particle effect at the cast site and various messages and advancements are done automagically. - * - * The userdata tag is copied for you, so you don't need to worry about mutation messing up things - * behind the scenes. */ @Throws(Mishap::class) fun operate( diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt index 3405830d62..8f80526e8a 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/castables/SpellAction.kt @@ -29,8 +29,8 @@ interface SpellAction : Action { @Throws(Mishap::class) - fun executeWithUserdata( - args: List, env: CastingEnvironment, userData: CompoundTag + fun executeWithImage( + args: List, env: CastingEnvironment, image: CastingImage ): Result { return this.execute(args, env) } @@ -44,8 +44,7 @@ interface SpellAction : Action { for (_i in 0 until this.argc) stack.removeLast() // execute! - val userDataMut = image.userData.copy() - val result = this.executeWithUserdata(args, env, userDataMut) + val result = this.executeWithImage(args, env, image.copy(stack = stack)) val sideEffects = mutableListOf() @@ -65,7 +64,7 @@ interface SpellAction : Action { for (spray in result.particles) sideEffects.add(OperatorSideEffect.Particles(spray)) - val image2 = image.copy(stack = stack, opsConsumed = image.opsConsumed + result.opCount, userData = userDataMut) + val image2 = image.copy(stack = stack, opsConsumed = image.opsConsumed + result.opCount) val sound = if (this.hasCastingSound(env)) HexEvalSounds.SPELL else HexEvalSounds.MUTE return OperationResult(image2, sideEffects, continuation, sound) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java index 54e033b013..5093978f27 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironment.java @@ -48,12 +48,12 @@ public abstract class CastingEnvironment { /** * Stores all listeners that should be notified whenever a CastingEnvironment is initialised. */ - private static final List> createEventListeners = new ArrayList<>(); + private static final List> createEventListeners = new ArrayList<>(); /** * Add a listener that will be called whenever a new CastingEnvironment is created. */ - public static void addCreateEventListener(BiConsumer listener) { + public static void addCreateEventListener(BiConsumer listener) { createEventListeners.add(listener); } @@ -71,10 +71,10 @@ public static void addCreateEventListener(Consumer listener) private boolean createEventTriggered = false; - public final void triggerCreateEvent(CompoundTag userData) { + public final void triggerCreateEvent(CastingImage image) { if (!createEventTriggered) { for (var listener : createEventListeners) - listener.accept(this, userData); + listener.accept(this, image); createEventTriggered = true; } } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt index cf887a117b..4ea7c638b6 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingImage.kt @@ -3,6 +3,9 @@ package at.petrak.hexcasting.api.casting.eval.vm import at.petrak.hexcasting.api.HexAPI import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota.Companion.TAG_ESCAPED import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota.Companion.TAG_IOTAS +import at.petrak.hexcasting.api.casting.eval.vm.components.CastingImageComponent +import at.petrak.hexcasting.api.casting.eval.vm.components.CastingImageComponents +import at.petrak.hexcasting.api.casting.eval.vm.components.ComponentType import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.IotaType import at.petrak.hexcasting.api.casting.iota.ListIota @@ -10,6 +13,7 @@ import at.petrak.hexcasting.api.utils.* import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.Tag +import net.minecraft.resources.ResourceLocation import net.minecraft.server.level.ServerLevel import net.minecraft.world.entity.Entity @@ -24,9 +28,9 @@ data class CastingImage private constructor( val escapeNext: Boolean, val opsConsumed: Long, - val userData: CompoundTag + val components: Map, CastingImageComponent> ) { - constructor() : this(listOf(), 0, listOf(), false, 0, CompoundTag()) + constructor() : this(listOf(), 0, listOf(), false, 0, emptyMap()) data class ParenthesizedIota(val iota: Iota, val escaped: Boolean) { companion object { @@ -72,6 +76,12 @@ data class CastingImage private constructor( */ fun withResetEscape() = this.copy(parenCount = 0, parenthesized = listOf(), escapeNext = false) + @Suppress("UNCHECKED_CAST") + fun getComponent(type: ComponentType): T? = this.components[type] as? T + fun withComponent(type: ComponentType, value: T): CastingImage = copy(components = this.components + (type to value)) + fun withoutComponent(type: ComponentType): CastingImage = copy(components = this.components - type) + fun removeTransientComponents(): CastingImage = copy(components = this.components.filterKeys { !it.transient }) + fun serializeToNbt() = NBTBuilder { TAG_STACK %= stack.serializeToNBT() @@ -80,7 +90,12 @@ data class CastingImage private constructor( TAG_PARENTHESIZED %= parenthesized.serializeToNBT() TAG_OPS_CONSUMED %= opsConsumed - TAG_USERDATA %= userData + val componentsTag = CompoundTag() + for ((type, component) in components) { + val serialized = type.uncheckedSerialize(component) + componentsTag.put(type.id.toString(), serialized) + } + TAG_COMPONENTS %= componentsTag } companion object { @@ -89,7 +104,7 @@ data class CastingImage private constructor( const val TAG_PARENTHESIZED = "parenthesized" const val TAG_ESCAPE_NEXT = "escape_next" const val TAG_OPS_CONSUMED = "ops_consumed" - const val TAG_USERDATA = "userdata" + const val TAG_COMPONENTS = "components" @JvmStatic fun loadFromNbt(tag: CompoundTag, world: ServerLevel): CastingImage { @@ -101,10 +116,14 @@ data class CastingImage private constructor( stack.add(datum) } - val userData = if (tag.contains(TAG_USERDATA)) { - tag.getCompound(TAG_USERDATA) - } else { - CompoundTag() + val components = mutableMapOf, CastingImageComponent>() + if (tag.contains(TAG_COMPONENTS, Tag.TAG_COMPOUND.toInt())) { + val componentsTag = tag.getCompound(TAG_COMPONENTS) + for (id in componentsTag.allKeys) { + val type = CastingImageComponents.getById(ResourceLocation(id)) ?: continue + val value = type.safeDeserialize(componentsTag.getCompound(id), world) ?: continue + components[type] = value + } } val parenthesized = mutableListOf() @@ -120,23 +139,11 @@ data class CastingImage private constructor( val escapeNext = tag.getBoolean(TAG_ESCAPE_NEXT) val opsUsed = tag.getLong(TAG_OPS_CONSUMED) - CastingImage(stack, parenCount, parenthesized, escapeNext, opsUsed, userData) + CastingImage(stack, parenCount, parenthesized, escapeNext, opsUsed, components) } catch (exn: Exception) { HexAPI.LOGGER.warn("error while loading a CastingImage", exn) CastingImage() } } - - @JvmStatic - fun checkAndMarkGivenMotion(userData: CompoundTag, entity: Entity): Boolean { - val marked = userData.getOrCreateCompound(HexAPI.MARKED_MOVED_USERDATA) - return if (marked.contains(entity.stringUUID)) { - true - } else { - marked.putBoolean(entity.stringUUID, true) - userData.putCompound(HexAPI.MARKED_MOVED_USERDATA, marked) - false - } - } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt index 11503b0b6f..93078d323c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/CastingVM.kt @@ -6,6 +6,8 @@ import at.petrak.hexcasting.api.casting.SpellList import at.petrak.hexcasting.api.casting.eval.* import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect import at.petrak.hexcasting.api.casting.eval.vm.CastingImage.ParenthesizedIota +import at.petrak.hexcasting.api.casting.eval.vm.components.CastingImageComponents +import at.petrak.hexcasting.api.casting.eval.vm.components.ComponentType import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.IotaType import at.petrak.hexcasting.api.casting.iota.ListIota @@ -24,7 +26,7 @@ import net.minecraft.server.level.ServerLevel */ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { init { - env.triggerCreateEvent(image.userData) + env.triggerCreateEvent(image) } /** @@ -86,6 +88,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { if (lastResolutionType.success) ResolvedPatternType.EVALUATED else ResolvedPatternType.ERRORED } + this.image = this.image.removeTransientComponents() val (stackDescs, ravenmind) = generateDescs() val isStackClear = image.stack.isEmpty() && image.parenCount == 0 && !image.escapeNext && ravenmind == null @@ -160,8 +163,9 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { fun generateDescs(): Pair, CompoundTag?> { val stackDescs = this.image.stack.map { IotaType.serialize(it) } - val ravenmind = if (this.image.userData.contains(HexAPI.RAVENMIND_USERDATA)) { - this.image.userData.getCompound(HexAPI.RAVENMIND_USERDATA) + val ravenmindComponent = this.image.getComponent(CastingImageComponents.RAVENMIND) + val ravenmind = if (ravenmindComponent != null) { + IotaType.serialize(ravenmindComponent.iota) } else null return Pair(stackDescs, ravenmind) } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponent.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponent.kt new file mode 100644 index 0000000000..311543a7ad --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponent.kt @@ -0,0 +1,37 @@ +package at.petrak.hexcasting.api.casting.eval.vm.components + +import net.minecraft.nbt.CompoundTag +import net.minecraft.server.level.ServerLevel +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import net.minecraft.resources.ResourceLocation + +/** + * A single instance of component data attached to a [CastingImage]. + * + * Components are the replacement for storing arbitrary data in [CastingImage]s. + * Instead of reaching into an untyped [CompoundTag] and constantly deserializing / reserializing, you declare a [ComponentType], + * register it, and patterns simply call [CastingImage.getComponent] or [CastingImage.withComponent]. + */ +interface CastingImageComponent + +/** + * Describes a type of component: how to serialize, deserialize, and whether components of this type are transient. + * There may only be one component of a given type per [CastingImage]. + * + * @param T The [CastingImageComponent] type this describes. + * @param id The identifier for the type, e.g. `"hexcasting:ravenmind"`. + * @param transient If `true`, components of this type are stripped by [CastingImage.removeTransientComponents]. + * Use this for per-cast state that must not bleed across spell-circle slate jumps or separate staff patterns. + * Currently used only for impulse cost accumulator. + */ +abstract class ComponentType(val id: ResourceLocation) { + open val transient: Boolean = false + abstract fun serialize(value: T): CompoundTag + abstract fun deserialize(tag: CompoundTag, world: ServerLevel): T + + @Suppress("UNCHECKED_CAST") + fun uncheckedSerialize(value: Any): CompoundTag = serialize(value as T) + fun safeDeserialize(tag: CompoundTag, world: ServerLevel): T? = runCatching { deserialize(tag, world) }.getOrNull() + override fun equals(other: Any?) = other is ComponentType<*> && other.id == id + override fun hashCode() = id.hashCode() +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponents.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponents.kt new file mode 100644 index 0000000000..558bc3a281 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/CastingImageComponents.kt @@ -0,0 +1,26 @@ +package at.petrak.hexcasting.api.casting.eval.vm.components + +import at.petrak.hexcasting.api.HexAPI.modLoc +import net.minecraft.resources.ResourceLocation + +object CastingImageComponents { + val RAVENMIND: ComponentType = GenericIotaComponentType(modLoc("ravenmind")) + val IMPULSE_SCALING: ComponentType = ImpulseScalingComponentType + + private val registry = HashMap>() + + @JvmStatic + fun registerComponents() { + register(RAVENMIND) + register(IMPULSE_SCALING) + } + + fun register(type: ComponentType): ComponentType { + val existing = registry.putIfAbsent(type.id, type) + if (existing != null && existing != type) + throw AssertionError("A component type of id '${type.id}' is already registered by a different object.") + return type + } + + fun getById(id: ResourceLocation): ComponentType<*>? = registry[id] +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/GenericIotaComponent.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/GenericIotaComponent.kt new file mode 100644 index 0000000000..8f0eec1247 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/GenericIotaComponent.kt @@ -0,0 +1,14 @@ +package at.petrak.hexcasting.api.casting.eval.vm.components + +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.iota.IotaType +import net.minecraft.nbt.CompoundTag +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel + +data class GenericIotaComponent(val iota: Iota) : CastingImageComponent + +class GenericIotaComponentType(id: ResourceLocation) : ComponentType(id) { + override fun serialize(value: GenericIotaComponent): CompoundTag = IotaType.serialize(value.iota) + override fun deserialize(tag: CompoundTag, world: ServerLevel): GenericIotaComponent = GenericIotaComponent(IotaType.deserialize(tag, world)) +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/ImpulseScalingComponent.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/ImpulseScalingComponent.kt new file mode 100644 index 0000000000..bbcbfaadf4 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/vm/components/ImpulseScalingComponent.kt @@ -0,0 +1,31 @@ +package at.petrak.hexcasting.api.casting.eval.vm.components + +import at.petrak.hexcasting.api.HexAPI.modLoc +import at.petrak.hexcasting.api.utils.putList +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.ListTag +import net.minecraft.nbt.StringTag +import net.minecraft.nbt.Tag +import net.minecraft.server.level.ServerLevel +import java.util.UUID + +data class ImpulseScalingComponent(val impulsedEntities: HashSet) : CastingImageComponent + +object ImpulseScalingComponentType : ComponentType(modLoc("impulse_scaling")) { + override val transient: Boolean = true + + override fun serialize(value: ImpulseScalingComponent): CompoundTag { + val set = ListTag().apply { + value.impulsedEntities.forEach { uuid -> add(StringTag.valueOf(uuid.toString())) } + } + val tag = CompoundTag() + tag.putList("set", set) + return tag + } + + override fun deserialize(tag: CompoundTag, world: ServerLevel): ImpulseScalingComponent { + val set = HashSet() + tag.getList("set", Tag.TAG_STRING.toInt()).forEach { uuid -> set.add(UUID.fromString(uuid.asString)) } + return ImpulseScalingComponent(set) + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/directrix/BlockBooleanDirectrix.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/directrix/BlockBooleanDirectrix.java index 6d99767bce..940cb130ec 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/directrix/BlockBooleanDirectrix.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/directrix/BlockBooleanDirectrix.java @@ -63,7 +63,7 @@ public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Di ? bs.getValue(FACING).getOpposite() : bs.getValue(FACING); var imageOut = imageIn.copy(stack, imageIn.getParenCount(), imageIn.getParenthesized(), - imageIn.getEscapeNext(), imageIn.getOpsConsumed(), imageIn.getUserData()); + imageIn.getEscapeNext(), imageIn.getOpsConsumed(), imageIn.getComponents()); return new ControlFlow.Continue(imageOut, List.of(this.exitPositionFromDirection(pos, outputDir))); } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPeekLocal.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPeekLocal.kt index 375b5bbcc0..26de4ff6bb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPeekLocal.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPeekLocal.kt @@ -1,11 +1,11 @@ package at.petrak.hexcasting.common.casting.actions.local -import at.petrak.hexcasting.api.HexAPI 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.eval.vm.components.CastingImageComponents import at.petrak.hexcasting.api.casting.iota.IotaType import at.petrak.hexcasting.api.casting.iota.NullIota import at.petrak.hexcasting.common.lib.hex.HexEvalSounds @@ -13,16 +13,8 @@ import at.petrak.hexcasting.common.lib.hex.HexEvalSounds object OpPeekLocal : Action { override fun operate(env: CastingEnvironment, image: CastingImage, continuation: SpellContinuation): OperationResult { val stack = image.stack.toMutableList() - - val rm = if (image.userData.contains(HexAPI.RAVENMIND_USERDATA)) { - IotaType.deserialize(image.userData.getCompound(HexAPI.RAVENMIND_USERDATA), env.world) - } else { - NullIota() - } - stack.add(rm) - - // does not mutate userdata - val image2 = image.withUsedOp().copy(stack = stack) - return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) + val ravenmindComponent = image.getComponent(CastingImageComponents.RAVENMIND) + stack.add(ravenmindComponent?.iota ?: NullIota()) + return OperationResult(image.withUsedOp().copy(stack = stack), listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPushLocal.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPushLocal.kt index 59c8a7e078..9e3ac104a9 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPushLocal.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/local/OpPushLocal.kt @@ -6,6 +6,8 @@ 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.eval.vm.components.CastingImageComponents +import at.petrak.hexcasting.api.casting.eval.vm.components.GenericIotaComponent import at.petrak.hexcasting.api.casting.iota.IotaType import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.common.lib.hex.HexEvalSounds @@ -19,12 +21,11 @@ object OpPushLocal : Action { throw MishapNotEnoughArgs(1, 0) val newLocal = stack.removeLast() - if (newLocal.type == HexIotaTypes.NULL) - image.userData.remove(HexAPI.RAVENMIND_USERDATA) + val newImage = if (newLocal.type == HexIotaTypes.NULL) + image.withoutComponent(CastingImageComponents.RAVENMIND) else - image.userData.put(HexAPI.RAVENMIND_USERDATA, IotaType.serialize(newLocal)) + image.withComponent(CastingImageComponents.RAVENMIND, GenericIotaComponent(newLocal)) - val image2 = image.withUsedOp().copy(stack = stack) - return OperationResult(image2, listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) + return OperationResult(newImage.withUsedOp().copy(stack = stack), listOf(), continuation, HexEvalSounds.NORMAL_EXECUTE) } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpAddMotion.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpAddMotion.kt index 7f45168f85..6f592e41a4 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpAddMotion.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpAddMotion.kt @@ -5,11 +5,12 @@ import at.petrak.hexcasting.api.casting.RenderedSpell import at.petrak.hexcasting.api.casting.castables.SpellAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.eval.vm.CastingImage +import at.petrak.hexcasting.api.casting.eval.vm.components.CastingImageComponents +import at.petrak.hexcasting.api.casting.eval.vm.components.ImpulseScalingComponent import at.petrak.hexcasting.api.casting.getEntity import at.petrak.hexcasting.api.casting.getVec3 import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.misc.MediaConstants -import net.minecraft.nbt.CompoundTag import net.minecraft.world.entity.Entity import net.minecraft.world.phys.Vec3 @@ -18,27 +19,26 @@ object OpAddMotion : SpellAction { get() = 2 // for bug #387 - val MAX_MOTION: Double = 8192.0 + const val MAX_MOTION: Double = 8192.0 - override fun executeWithUserdata( - args: List, - env: CastingEnvironment, - userData: CompoundTag - ): SpellAction.Result { + override fun executeWithImage(args: List, env: CastingEnvironment, image: CastingImage): SpellAction.Result { val target = args.getEntity(0, argc) val motion = args.getVec3(1, argc) env.assertEntityInRange(target) var motionForCost = motion.lengthSqr() - if (CastingImage.checkAndMarkGivenMotion(userData, target)) + val updatedImpulseComponent = image.getComponent(CastingImageComponents.IMPULSE_SCALING) ?: ImpulseScalingComponent(HashSet()) + if (updatedImpulseComponent.impulsedEntities.contains(target.uuid)) motionForCost++ + else + updatedImpulseComponent.impulsedEntities.add(target.uuid) val shrunkMotion = if (motion.lengthSqr() > MAX_MOTION * MAX_MOTION) motion.normalize().scale(MAX_MOTION) else motion return SpellAction.Result( - Spell(target, shrunkMotion), + Spell(target, shrunkMotion, updatedImpulseComponent), (motionForCost * MediaConstants.DUST_UNIT).toLong(), listOf( ParticleSpray( @@ -55,10 +55,12 @@ object OpAddMotion : SpellAction { throw IllegalStateException() } - private data class Spell(val target: Entity, val motion: Vec3) : RenderedSpell { - override fun cast(env: CastingEnvironment) { + private data class Spell(val target: Entity, val motion: Vec3, val newImpulseComponent: ImpulseScalingComponent) : RenderedSpell { + override fun cast(env: CastingEnvironment) {} + override fun cast(env: CastingEnvironment, image: CastingImage): CastingImage { target.push(motion.x, motion.y, motion.z) target.hurtMarked = true // Whyyyyy + return image.withComponent(CastingImageComponents.IMPULSE_SCALING, newImpulseComponent) } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/misc/RegisterMisc.java b/Common/src/main/java/at/petrak/hexcasting/common/misc/RegisterMisc.java index 2559748264..888c799026 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/misc/RegisterMisc.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/misc/RegisterMisc.java @@ -1,6 +1,7 @@ package at.petrak.hexcasting.common.misc; import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.api.casting.eval.vm.components.CastingImageComponents; import at.petrak.hexcasting.mixin.accessor.AccessorAbstractArrow; import at.petrak.hexcasting.mixin.accessor.AccessorVillager; import net.minecraft.server.level.ServerPlayer; @@ -33,6 +34,8 @@ public static void register() { allay.getBrain().eraseMemory(MemoryModuleType.LIKED_PLAYER); HexAPI.instance().defaultBrainsweepingBehavior().accept(allay); }); + + CastingImageComponents.registerComponents(); } private static Vec3 arrowVelocitizer(AbstractArrow arrow) {