fix up mishaps some more and fix a few spells

This commit is contained in:
petrak@ 2023-02-15 00:50:46 -06:00
parent 2233c7697d
commit 1750f29a69
12 changed files with 120 additions and 85 deletions

View file

@ -112,6 +112,12 @@ public interface HexAPI {
* Location in the userdata of the ravenmind
*/
String RAVENMIND_USERDATA = modLoc("ravenmind").toString();
/**
* Location in the userdata of the evaluation depth
*/
String EVAL_DEPTH_USERDATA = modLoc("eval_depth").toString();
String MARKED_MOVED_USERDATA = modLoc("impulsed").toString();
static HexAPI instance() {
return INSTANCE.get();

View file

@ -22,6 +22,12 @@ interface SpellAction : Action {
ctx: CastingEnvironment
): Triple<RenderedSpell, Int, List<ParticleSpray>>?
fun executeWithUserdata(
args: List<Iota>, ctx: CastingEnvironment, userData: CompoundTag
): Triple<RenderedSpell, Int, List<ParticleSpray>>? {
return this.execute(args, ctx)
}
override fun operate(
env: CastingEnvironment,
stack: MutableList<Iota>,
@ -32,7 +38,8 @@ interface SpellAction : Action {
throw MishapNotEnoughArgs(this.argc, stack.size)
val args = stack.takeLast(this.argc)
for (_i in 0 until this.argc) stack.removeLast()
val executeResult = this.execute(args, env) ?: return OperationResult(stack, userData, listOf(), continuation)
val executeResult = this.executeWithUserdata(args, env, userData)
?: return OperationResult(stack, userData, listOf(), continuation)
val (spell, media, particles) = executeResult
val sideEffects = mutableListOf<OperatorSideEffect>()

View file

@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway;
import at.petrak.hexcasting.api.casting.mishaps.MishapLocationTooFarAway;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.utils.HexUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
@ -20,9 +21,7 @@ import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
/**
@ -32,12 +31,9 @@ import java.util.function.Predicate;
*/
public abstract class CastingEnvironment {
protected final ServerLevel world;
// TODO move this to the env?
protected final Set<Entity> entitiesGivenMotion;
protected CastingEnvironment(ServerLevel world) {
this.world = world;
this.entitiesGivenMotion = new HashSet<>();
}
public final ServerLevel getWorld() {
@ -59,8 +55,7 @@ public abstract class CastingEnvironment {
public abstract MishapEnvironment getMishapEnvironment();
/**
* Get the sound that this I/O module makes.
* <p>
* Get the sound that this I/O module makes upon receiving a pattern
*/
public abstract EvalSound getSoundType();
@ -162,12 +157,10 @@ public abstract class CastingEnvironment {
}
}
public void markEntityAsImpulsed(Entity e) {
this.entitiesGivenMotion.add(e);
}
public abstract InteractionHand castingHand();
public boolean hasBeenGivenMotion(Entity e) {
return this.entitiesGivenMotion.contains(e);
public InteractionHand otherHand() {
return HexUtils.otherHand(this.castingHand());
}
/**

View file

@ -3,10 +3,13 @@ package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.IotaType
import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooDeep
import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.utils.*
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.Entity
/**
* The state of a casting VM, containing the stack and all
@ -18,10 +21,9 @@ data class CastingImage private constructor(
val parenthesized: List<Iota>,
val escapeNext: Boolean,
val iterationSteps: Int,
val userData: CompoundTag
) {
public constructor() : this(listOf(), 0, listOf(), false, CompoundTag())
constructor() : this(listOf(), 0, listOf(), false, CompoundTag())
fun serializeToNbt() = NBTBuilder {
TAG_STACK %= stack.serializeToNBT()
@ -33,17 +35,6 @@ data class CastingImage private constructor(
TAG_USERDATA %= userData
}
/**
* Throws if we get too deep.
*/
fun incDepth(): CastingImage {
val maxAllowedDepth = HexConfig.server().maxRecurseDepth()
if (this.iterationSteps + 1 > maxAllowedDepth) {
throw MishapEvalTooDeep()
}
return copy(iterationSteps = iterationSteps + 1)
}
companion object {
const val TAG_STACK = "stack"
const val TAG_PAREN_COUNT = "open_parens"
@ -87,5 +78,31 @@ data class CastingImage private constructor(
CastingImage()
}
}
/**
* Throws if we get too deep.
*/
@JvmStatic
fun incDepth(userData: CompoundTag): CompoundTag {
val maxAllowedDepth = HexConfig.server().maxRecurseDepth()
val depth = userData.getInt(HexAPI.EVAL_DEPTH_USERDATA)
if (depth + 1 > maxAllowedDepth) {
throw MishapEvalTooDeep()
}
userData.putInt(HexAPI.EVAL_DEPTH_USERDATA, depth + 1)
return userData
}
@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)
false
}
}
}
}

View file

@ -83,7 +83,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
// ALSO TODO need to add reader macro-style things
try {
this.handleParentheses(iota)?.let { (data, resolutionType) ->
return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
return@executeInner CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.ADD_PATTERN)
}
} catch (e: MishapTooManyCloseParens) {
// This is ridiculous and needs to be fixed

View file

@ -12,7 +12,7 @@ class MishapImmuneEntity(val entity: Entity) : Mishap() {
dyeColor(DyeColor.BLUE)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
yeetHeldItemsTowards(ctx, entity.position())
ctx.mishapEnvironment.yeetHeldItemsTowards(entity.position())
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =

View file

@ -5,6 +5,8 @@ import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers;
import at.petrak.hexcasting.api.casting.ParticleSpray;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
import at.petrak.hexcasting.api.casting.eval.MishapEnvironment;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
@ -13,6 +15,7 @@ import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.mod.HexStatistics;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.MediaHelper;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
@ -44,6 +47,11 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment {
return this.caster;
}
@Override
public EvalSound getSoundType() {
return HexEvalSounds.ADD_PATTERN;
}
@Override
protected List<ItemStack> getUsableStacks(StackDiscoveryMode mode) {
return switch (mode) {
@ -167,9 +175,13 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment {
return this.caster.position();
}
@Override
public MishapEnvironment getMishapEnvironment() {
return new PlayerBasedMishapEnv(this.caster);
}
protected void sendMishapMsgToPlayer(OperatorSideEffect.DoMishap mishap) {
var msg = mishap.getMishap().errorMessageWithName(this, mishap.getErrorCtx());
this.caster.sendSystemMessage(msg);
}
}

View file

@ -4,13 +4,11 @@ import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.casting.eval.CastResult;
import at.petrak.hexcasting.api.casting.eval.ExecutionClientView;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
import at.petrak.hexcasting.api.casting.iota.PatternIota;
import at.petrak.hexcasting.api.casting.math.HexCoord;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexStatistics;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.common.network.MsgNewSpellPatternAck;
import at.petrak.hexcasting.common.network.MsgNewSpellPatternSyn;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
@ -51,52 +49,45 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
}
public static void handleNewPatternOnServer(ServerPlayer sender, MsgNewSpellPatternSyn msg) {
var held = sender.getItemInHand(msg.handUsed());
if (held.is(HexTags.Items.STAVES)) {
boolean autoFail = false;
boolean cheatedPatternOverlap = false;
List<ResolvedPattern> resolvedPatterns = msg.resolvedPatterns();
if (!resolvedPatterns.isEmpty()) {
var allPoints = new HashSet<HexCoord>();
for (int i = 0; i < resolvedPatterns.size() - 1; i++) {
ResolvedPattern pat = resolvedPatterns.get(i);
allPoints.addAll(pat.getPattern().positions(pat.getOrigin()));
}
var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1);
var currentSpellPoints = currentResolvedPattern.getPattern()
.positions(currentResolvedPattern.getOrigin());
if (currentSpellPoints.stream().anyMatch(allPoints::contains)) {
autoFail = true;
}
List<ResolvedPattern> resolvedPatterns = msg.resolvedPatterns();
if (!resolvedPatterns.isEmpty()) {
var allPoints = new HashSet<HexCoord>();
for (int i = 0; i < resolvedPatterns.size() - 1; i++) {
ResolvedPattern pat = resolvedPatterns.get(i);
allPoints.addAll(pat.getPattern().positions(pat.getOrigin()));
}
sender.awardStat(HexStatistics.PATTERNS_DRAWN);
var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed());
ExecutionClientView clientInfo;
if (autoFail) {
var descs = harness.generateDescs();
clientInfo = new ExecutionClientView(harness.getStack().isEmpty(), ResolvedPatternType.INVALID,
descs.getFirst(), descs.getSecond(), descs.getThird(), harness.getParenCount());
} else {
clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
var currentResolvedPattern = resolvedPatterns.get(resolvedPatterns.size() - 1);
var currentSpellPoints = currentResolvedPattern.getPattern()
.positions(currentResolvedPattern.getOrigin());
if (currentSpellPoints.stream().anyMatch(allPoints::contains)) {
cheatedPatternOverlap = true;
}
if (clientInfo.isStackClear()) {
IXplatAbstractions.INSTANCE.setHarness(sender, null);
IXplatAbstractions.INSTANCE.setPatterns(sender, List.of());
} else {
IXplatAbstractions.INSTANCE.setHarness(sender, harness);
if (!resolvedPatterns.isEmpty()) {
resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType());
}
IXplatAbstractions.INSTANCE.setPatterns(sender, resolvedPatterns);
}
IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender,
new MsgNewSpellPatternAck(clientInfo, resolvedPatterns.size() - 1));
}
if (cheatedPatternOverlap) {
return;
}
sender.awardStat(HexStatistics.PATTERNS_DRAWN);
var harness = IXplatAbstractions.INSTANCE.getStaffHarness(sender, msg.handUsed());
ExecutionClientView clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
if (clientInfo.isStackClear()) {
IXplatAbstractions.INSTANCE.setHarness(sender, null);
IXplatAbstractions.INSTANCE.setPatterns(sender, List.of());
} else {
IXplatAbstractions.INSTANCE.setHarness(sender, harness);
if (!resolvedPatterns.isEmpty()) {
resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType());
}
IXplatAbstractions.INSTANCE.setPatterns(sender, resolvedPatterns);
}
IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender,
new MsgNewSpellPatternAck(clientInfo, resolvedPatterns.size() - 1));
}
}

View file

@ -1,9 +1,11 @@
package at.petrak.hexcasting.common.casting.operators.eval
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.asActionResult
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.FrameEvaluate
import at.petrak.hexcasting.api.casting.eval.vm.FrameFinishEval
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
@ -23,9 +25,9 @@ object OpEval : Action {
val datum = stack.removeLastOrNull() ?: throw MishapNotEnoughArgs(1, 0)
val instrs = evaluatable(datum, 0)
val stack = instrs.ifRight {
// FIXME: Casting depth increment not implemented yet
//env.incDepth()
instrs.ifRight {
CastingImage.incDepth(userData)
it.asActionResult
}
// if not installed already...
@ -39,6 +41,6 @@ object OpEval : Action {
val instrsList = instrs.map({ SpellList.LList(0, listOf(PatternIota(it))) }, { it })
val frame = FrameEvaluate(instrsList, true)
return OperationResult(stack, userData, listOf(), newCont.pushFrame(frame))
return OperationResult(listOf(), userData, listOf(), newCont.pushFrame(frame))
}
}

View file

@ -4,10 +4,12 @@ import at.petrak.hexcasting.api.casting.ParticleSpray
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.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,17 +20,18 @@ object OpAddMotion : SpellAction {
// for bug #387
val MAX_MOTION: Double = 8192.0
override fun execute(
override fun executeWithUserdata(
args: List<Iota>,
ctx: CastingEnvironment
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
ctx: CastingEnvironment,
userData: CompoundTag
): Triple<RenderedSpell, Int, List<ParticleSpray>>? {
val target = args.getEntity(0, argc)
val motion = args.getVec3(1, argc)
ctx.assertEntityInRange(target)
var motionForCost = motion.lengthSqr()
if (ctx.hasBeenGivenMotion(target))
if (CastingImage.checkAndMarkGivenMotion(userData, target))
motionForCost++
ctx.markEntityAsMotionAdded(target)
val shrunkMotion = if (motion.lengthSqr() > MAX_MOTION * MAX_MOTION)
motion.normalize().scale(MAX_MOTION)
@ -48,6 +51,10 @@ object OpAddMotion : SpellAction {
)
}
override fun execute(args: List<Iota>, ctx: CastingEnvironment): Triple<RenderedSpell, Int, List<ParticleSpray>>? {
throw IllegalStateException()
}
private data class Spell(val target: Entity, val motion: Vec3) : RenderedSpell {
override fun cast(ctx: CastingEnvironment) {
target.push(motion.x, motion.y, motion.z)

View file

@ -49,7 +49,7 @@ class OpCreateFluid(val cost: Int, val bucket: Item, val cauldron: BlockState, v
ctx.world.setBlock(pos, cauldron, 3)
else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid(
ctx.world,
ctx.castingHand,
ctx.castingHand(),
pos,
fluid
) && bucket is BucketItem) {

View file

@ -15,14 +15,14 @@ public class HexEvalSounds {
public static final EvalSound NOTHING = make("nothing",
new EvalSound(null, Integer.MIN_VALUE));
public static final EvalSound OPERATOR = make("operator",
public static final EvalSound ADD_PATTERN = make("operator",
new EvalSound(HexSounds.ADD_PATTERN, 0));
public static final EvalSound SPELL = make("spell",
new EvalSound(HexSounds.ACTUALLY_CAST, 1000));
public static final EvalSound HERMES = make("hermes",
new EvalSound(HexSounds.CAST_HERMES, 2000));
public static final EvalSound THOTH = make("thoth",
new EvalSound(HexSounds.CAST_THOTH, 2000));
new EvalSound(HexSounds.CAST_THOTH, 2500));
public static final EvalSound MUTE = make("mute",
new EvalSound(null, 3000));