From 9904e3adb43311e5119ee9e61510a78790091971 Mon Sep 17 00:00:00 2001 From: Talia-12 Date: Tue, 1 Aug 2023 21:52:32 +1000 Subject: [PATCH] added Cypher's CastingEnvironmentComponent system, with some extensions. --- .../api/casting/eval/CastingEnvironment.java | 115 +++++++++++++++++- .../eval/CastingEnvironmentComponent.java | 41 +++++++ .../api/casting/eval/env/CircleCastEnv.java | 8 +- .../casting/eval/env/PackagedItemCastEnv.java | 6 +- .../casting/eval/env/PlayerBasedCastEnv.java | 8 +- .../api/casting/eval/env/StaffCastEnv.java | 2 +- .../eval/sideeffects/OperatorSideEffect.kt | 1 - .../api/casting/eval/vm/CastingVM.kt | 4 + 8 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironmentComponent.java 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 dc0a7bb1..fc23f51a 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 @@ -16,15 +16,21 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import java.util.function.Predicate; import static at.petrak.hexcasting.api.HexAPI.modLoc; +import static at.petrak.hexcasting.api.casting.eval.CastingEnvironmentComponent.*; /** * Environment within which hexes are cast. @@ -32,8 +38,37 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc; * Stuff like "the player with a staff," "the player with a trinket," "spell circles," */ public abstract class CastingEnvironment { + /** + * Stores all listeners that should be notified whenever a CastingEnvironment is initialised. + */ + private static final List> createEventListeners = new ArrayList<>(); + + /** + * Add a listener that will be called whenever a new CastingEnvironment is created. + */ + public static void addCreateEventListener(Consumer listener) { + createEventListeners.add(listener); + } + + private boolean createEventTriggered = false; + + public final void triggerCreateEvent() { + if (!createEventTriggered) { + for (var listener : createEventListeners) + listener.accept(this); + createEventTriggered = true; + } + } + + protected final ServerLevel world; + protected Map, @NotNull CastingEnvironmentComponent> componentMap = new HashMap<>(); + private final List postExecutionComponents = new ArrayList<>(); + private final List extractMediaComponents = new ArrayList<>(); + private final List isVecInRangeComponents = new ArrayList<>(); + private final List hasEditPermissionsAtComponents = new ArrayList<>(); + protected CastingEnvironment(ServerLevel world) { this.world = world; } @@ -56,6 +91,39 @@ public abstract class CastingEnvironment { */ public abstract MishapEnvironment getMishapEnvironment(); + public void addExtension(@NotNull T extension) { + componentMap.put(extension.getKey(), extension); + if (extension instanceof PostExecutionComponent postExecutionComponent) + postExecutionComponents.add(postExecutionComponent); + if (extension instanceof ExtractMediaComponent extractMediaComponent) + extractMediaComponents.add(extractMediaComponent); + if (extension instanceof IsVecInRangeComponent isVecInRangeComponent) + isVecInRangeComponents.add(isVecInRangeComponent); + if (extension instanceof HasEditPermissionsAtComponent hasEditPermissionsAtComponent) + hasEditPermissionsAtComponents.add(hasEditPermissionsAtComponent); + } + + public void removeExtension(@NotNull CastingEnvironmentComponent.Key key) { + var extension = componentMap.remove(key); + if (extension == null) + return; + + if (extension instanceof PostExecutionComponent postExecutionComponent) + postExecutionComponents.remove(postExecutionComponent); + if (extension instanceof ExtractMediaComponent extractMediaComponent) + extractMediaComponents.remove(extractMediaComponent); + if (extension instanceof IsVecInRangeComponent isVecInRangeComponent) + isVecInRangeComponents.remove(isVecInRangeComponent); + if (extension instanceof HasEditPermissionsAtComponent hasEditPermissionsAtComponent) + hasEditPermissionsAtComponents.remove(hasEditPermissionsAtComponent); + } + + @Nullable + @SuppressWarnings("unchecked") + public T getExtension(@NotNull CastingEnvironmentComponent.Key key) { + return (T) componentMap.get(key); + } + /** * If something about this ARE itself is invalid, mishap. *

@@ -89,7 +157,10 @@ public abstract class CastingEnvironment { /** * Do whatever you like after a pattern is executed. */ - public abstract void postExecution(CastResult result); + public void postExecution(CastResult result) { + for (var postExecutionComponent : postExecutionComponents) + postExecutionComponent.onPostExecution(result); + } public abstract Vec3 mishapSprayPos(); @@ -114,19 +185,53 @@ public abstract class CastingEnvironment { * If there was enough media found, it will return less or equal to zero; if there wasn't, it will be * positive. */ - public abstract long extractMedia(long cost); + public long extractMedia(long cost) { + for (var extractMediaComponent : extractMediaComponents) + cost = extractMediaComponent.onExtractMedia(cost); + return extractMediaEnvironment(cost); + } + + /** + * Attempt to extract the given amount of media. Returns the amount of media left in the cost. + *

+ * If there was enough media found, it will return less or equal to zero; if there wasn't, it will be + * positive. + */ + protected abstract long extractMediaEnvironment(long cost); /** * Get if the vec is close enough, to the player or sentinel ... *

* Doesn't take into account being out of the world. */ - public abstract boolean isVecInRange(Vec3 vec); + public boolean isVecInRange(Vec3 vec) { + boolean isInRange = isVecInRangeEnvironment(vec); + for (var isVecInRangeComponent : isVecInRangeComponents) + isInRange = isVecInRangeComponent.onIsVecInRange(vec, isInRange); + return isInRange; + } + + /** + * Get if the vec is close enough, to the player or sentinel ... + *

+ * Doesn't take into account being out of the world. + */ + protected abstract boolean isVecInRangeEnvironment(Vec3 vec); /** * Return whether the caster can edit blocks at the given permission (i.e. not adventure mode, etc.) */ - public abstract boolean hasEditPermissionsAt(BlockPos vec); + public boolean hasEditPermissionsAt(BlockPos pos) { + boolean hasEditPermissionsAt = hasEditPermissionsAtEnvironment(pos); + for (var hasEditPermissionsAtComponent : hasEditPermissionsAtComponents) + hasEditPermissionsAt = hasEditPermissionsAtComponent.onHasEditPermissionsAt(pos, hasEditPermissionsAt); + return hasEditPermissionsAt; + } + + /** + * Return whether the caster can edit blocks at the given permission (i.e. not adventure mode, etc.) + */ + protected abstract boolean hasEditPermissionsAtEnvironment(BlockPos pos); public final boolean isVecInWorld(Vec3 vec) { return this.world.isInWorldBounds(BlockPos.containing(vec)) @@ -138,7 +243,7 @@ public abstract class CastingEnvironment { } public final boolean isEntityInRange(Entity e) { - return this.isVecInRange(e.position()); + return e instanceof Player || this.isVecInRange(e.position()); } /** diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironmentComponent.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironmentComponent.java new file mode 100644 index 00000000..e3c184be --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/CastingEnvironmentComponent.java @@ -0,0 +1,41 @@ +package at.petrak.hexcasting.api.casting.eval; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; + +public interface CastingEnvironmentComponent { + Key getKey(); + + interface Key {} + + interface PostExecutionComponent extends CastingEnvironmentComponent { + /** + * Do whatever you like after a pattern is executed. + */ + void onPostExecution(CastResult result); + } + + interface ExtractMediaComponent extends CastingEnvironmentComponent { + /** + * Receives the cost that is being extracted, should return the + * remaining cost after deducting whatever cost source this component + * is responsible for (should be >= 0). All Components are executed + * before the CastingEnvironment's extractMedia is executed. + */ + long onExtractMedia(long cost); + } + + interface IsVecInRangeComponent extends CastingEnvironmentComponent { + /** + * Receives the vec, and the current return value, and returns the new return value. + */ + boolean onIsVecInRange(Vec3 vec, boolean current); + } + + interface HasEditPermissionsAtComponent extends CastingEnvironmentComponent { + /** + * Receives the vec, and the current return value, and returns the new return value. + */ + boolean onHasEditPermissionsAt(BlockPos pos, boolean current); + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java index 9e6e7fb8..7bbda1ea 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java @@ -73,6 +73,8 @@ public class CircleCastEnv extends CastingEnvironment { @Override public void postExecution(CastResult result) { + super.postExecution(result); + // we always want to play this sound one at a time var sound = result.getSound().sound(); if (sound != null) { @@ -103,7 +105,7 @@ public class CircleCastEnv extends CastingEnvironment { } @Override - public long extractMedia(long cost) { + public long extractMediaEnvironment(long cost) { var entity = this.getImpetus(); if (entity == null) return cost; @@ -120,7 +122,7 @@ public class CircleCastEnv extends CastingEnvironment { } @Override - public boolean isVecInRange(Vec3 vec) { + public boolean isVecInRangeEnvironment(Vec3 vec) { var caster = this.execState.getCaster(this.world); if (caster != null) { var sentinel = HexAPI.instance().getSentinel(caster); @@ -137,7 +139,7 @@ public class CircleCastEnv extends CastingEnvironment { } @Override - public boolean hasEditPermissionsAt(BlockPos vec) { + public boolean hasEditPermissionsAtEnvironment(BlockPos pos) { return true; } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PackagedItemCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PackagedItemCastEnv.java index cc694ae7..2a736d55 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PackagedItemCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PackagedItemCastEnv.java @@ -37,12 +37,14 @@ public class PackagedItemCastEnv extends PlayerBasedCastEnv { } @Override - public long extractMedia(long costLeft) { + public long extractMediaEnvironment(long costLeft) { if (this.caster.isCreative()) return 0; var casterStack = this.caster.getItemInHand(this.castingHand); var casterHexHolder = IXplatAbstractions.INSTANCE.findHexHolder(casterStack); + if (casterHexHolder == null) + return costLeft; var canCastFromInv = casterHexHolder.canDrawMediaFromInventory(); var casterMediaHolder = IXplatAbstractions.INSTANCE.findMediaHolder(casterStack); @@ -69,6 +71,8 @@ public class PackagedItemCastEnv extends PlayerBasedCastEnv { public FrozenPigment getPigment() { var casterStack = this.caster.getItemInHand(this.castingHand); var casterHexHolder = IXplatAbstractions.INSTANCE.findHexHolder(casterStack); + if (casterHexHolder == null) + return IXplatAbstractions.INSTANCE.getPigment(this.caster); var hexHolderPigment = casterHexHolder.getPigment(); if (hexHolderPigment != null) return hexHolderPigment; diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PlayerBasedCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PlayerBasedCastEnv.java index ad4ad43f..53961c3a 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PlayerBasedCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/PlayerBasedCastEnv.java @@ -53,6 +53,8 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment { @Override public void postExecution(CastResult result) { + super.postExecution(result); + for (var sideEffect : result.getSideEffects()) { if (sideEffect instanceof OperatorSideEffect.DoMishap doMishap) { this.sendMishapMsgToPlayer(doMishap); @@ -165,7 +167,7 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment { } @Override - public boolean isVecInRange(Vec3 vec) { + public boolean isVecInRangeEnvironment(Vec3 vec) { var sentinel = HexAPI.instance().getSentinel(this.caster); if (sentinel != null && sentinel.extendsRange() @@ -179,8 +181,8 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment { } @Override - public boolean hasEditPermissionsAt(BlockPos vec) { - return this.caster.gameMode.getGameModeForPlayer() != GameType.ADVENTURE && this.world.mayInteract(this.caster, vec); + public boolean hasEditPermissionsAtEnvironment(BlockPos pos) { + return this.caster.gameMode.getGameModeForPlayer() != GameType.ADVENTURE && this.world.mayInteract(this.caster, pos); } /** diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/StaffCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/StaffCastEnv.java index 8c92d2d1..9b63ece8 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/StaffCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/StaffCastEnv.java @@ -52,7 +52,7 @@ public class StaffCastEnv extends PlayerBasedCastEnv { } @Override - public long extractMedia(long cost) { + public long extractMediaEnvironment(long cost) { if (this.caster.isCreative()) return 0; diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt index b1f41850..2ea80b19 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/sideeffects/OperatorSideEffect.kt @@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.casting.eval.sideeffects import at.petrak.hexcasting.api.casting.ParticleSpray import at.petrak.hexcasting.api.casting.RenderedSpell -import at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv import at.petrak.hexcasting.api.casting.eval.vm.CastingVM import at.petrak.hexcasting.api.casting.mishaps.Mishap import at.petrak.hexcasting.api.mod.HexStatistics 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 92ccd50d..d2cdfbdb 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 @@ -23,6 +23,10 @@ import net.minecraft.server.level.ServerLevel * [CastingEnvironment] to affect the world. */ class CastingVM(var image: CastingImage, val env: CastingEnvironment) { + init { + env.triggerCreateEvent() + } + /** * Execute a single iota. */