added Cypher's CastingEnvironmentComponent system, with some extensions.

This commit is contained in:
Talia-12 2023-08-01 21:52:32 +10:00
parent 4c9e380ea8
commit 9904e3adb4
8 changed files with 171 additions and 14 deletions

View file

@ -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<Consumer<CastingEnvironment>> createEventListeners = new ArrayList<>();
/**
* Add a listener that will be called whenever a new CastingEnvironment is created.
*/
public static void addCreateEventListener(Consumer<CastingEnvironment> 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<CastingEnvironmentComponent.Key<?>, @NotNull CastingEnvironmentComponent> componentMap = new HashMap<>();
private final List<PostExecutionComponent> postExecutionComponents = new ArrayList<>();
private final List<ExtractMediaComponent> extractMediaComponents = new ArrayList<>();
private final List<IsVecInRangeComponent> isVecInRangeComponents = new ArrayList<>();
private final List<HasEditPermissionsAtComponent> hasEditPermissionsAtComponents = new ArrayList<>();
protected CastingEnvironment(ServerLevel world) {
this.world = world;
}
@ -56,6 +91,39 @@ public abstract class CastingEnvironment {
*/
public abstract MishapEnvironment getMishapEnvironment();
public <T extends CastingEnvironmentComponent> 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 extends CastingEnvironmentComponent> T getExtension(@NotNull CastingEnvironmentComponent.Key<T> key) {
return (T) componentMap.get(key);
}
/**
* If something about this ARE itself is invalid, mishap.
* <p>
@ -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.
* <p>
* 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 ...
* <p>
* Doesn't take into account being out of the <em>world</em>.
*/
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 ...
* <p>
* Doesn't take into account being out of the <em>world</em>.
*/
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());
}
/**

View file

@ -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<C extends CastingEnvironmentComponent> {}
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);
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}
/**

View file

@ -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;

View file

@ -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

View file

@ -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.
*/