executing patterns as spells

:)
This commit is contained in:
yrsegal@gmail.com 2022-05-10 19:42:10 -04:00
parent 65b04c0372
commit fff3cf5691
15 changed files with 172 additions and 120 deletions

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.cap; package at.petrak.hexcasting.api.cap;
import at.petrak.hexcasting.api.spell.math.HexPattern; import at.petrak.hexcasting.api.spell.SpellDatum;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
@ -9,10 +10,12 @@ public interface SpellHolder {
boolean canDrawManaFromInventory(); boolean canDrawManaFromInventory();
boolean hasSpell();
@Nullable @Nullable
List<HexPattern> getPatterns(); List<SpellDatum<?>> getPatterns(ServerLevel level);
void writePatterns(List<HexPattern> patterns, int mana); void writePatterns(List<SpellDatum<?>> patterns, int mana);
void clearPatterns(); void clearPatterns();
} }

View file

@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.mod.HexApiSounds;
import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper; import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import at.petrak.hexcasting.api.spell.ParticleSpray; import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.api.spell.casting.CastingContext; import at.petrak.hexcasting.api.spell.casting.CastingContext;
import at.petrak.hexcasting.api.spell.casting.CastingHarness; import at.petrak.hexcasting.api.spell.casting.CastingHarness;
import at.petrak.hexcasting.api.spell.casting.SpellCircleContext; import at.petrak.hexcasting.api.spell.casting.SpellCircleContext;
@ -310,7 +311,7 @@ public abstract class BlockEntityAbstractImpetus extends PaucalBlockEntity imple
if (bs.getBlock() instanceof BlockCircleComponent cc) { if (bs.getBlock() instanceof BlockCircleComponent cc) {
var newPattern = cc.getPattern(tracked, bs, this.level); var newPattern = cc.getPattern(tracked, bs, this.level);
if (newPattern != null) { if (newPattern != null) {
var info = harness.executeNewPattern(newPattern, splayer.getLevel()); var info = harness.executeNewIota(SpellDatum.make(newPattern), splayer.getLevel());
if (info.getWasSpellCast()) { if (info.getWasSpellCast()) {
castSpell = true; castSpell = true;
if (info.getHasCastingSound()) { if (info.getHasCastingSound()) {

View file

@ -1,6 +1,7 @@
package at.petrak.hexcasting.api.item; package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.spell.math.HexPattern; import at.petrak.hexcasting.api.spell.SpellDatum;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -10,10 +11,12 @@ public interface SpellHolderItem extends ManaHolderItem {
boolean canDrawManaFromInventory(ItemStack stack); boolean canDrawManaFromInventory(ItemStack stack);
@Nullable boolean hasSpell(ItemStack stack);
List<HexPattern> getPatterns(ItemStack stack);
void writePatterns(ItemStack stack, List<HexPattern> patterns, int mana); @Nullable
List<SpellDatum<?>> getSpell(ItemStack stack, ServerLevel level);
void writePatterns(ItemStack stack, List<SpellDatum<?>> patterns, int mana);
void clearPatterns(ItemStack stack); void clearPatterns(ItemStack stack);
} }

View file

@ -9,15 +9,10 @@ import at.petrak.hexcasting.api.mod.HexConfig
import at.petrak.hexcasting.api.mod.HexItemTags import at.petrak.hexcasting.api.mod.HexItemTags
import at.petrak.hexcasting.api.mod.HexStatistics import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.player.HexPlayerDataHelper import at.petrak.hexcasting.api.player.HexPlayerDataHelper
import at.petrak.hexcasting.api.spell.Operator import at.petrak.hexcasting.api.spell.*
import at.petrak.hexcasting.api.spell.ParticleSpray import at.petrak.hexcasting.api.spell.math.HexDir
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.Widget
import at.petrak.hexcasting.api.spell.math.HexPattern import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.Mishap import at.petrak.hexcasting.api.spell.mishaps.*
import at.petrak.hexcasting.api.spell.mishaps.MishapDisallowedSpell
import at.petrak.hexcasting.api.spell.mishaps.MishapError
import at.petrak.hexcasting.api.spell.mishaps.MishapTooManyCloseParens
import at.petrak.hexcasting.api.utils.HexDamageSources import at.petrak.hexcasting.api.utils.HexDamageSources
import at.petrak.hexcasting.api.utils.ManaHelper import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.api.utils.asCompound import at.petrak.hexcasting.api.utils.asCompound
@ -39,7 +34,7 @@ class CastingHarness private constructor(
var stack: MutableList<SpellDatum<*>>, var stack: MutableList<SpellDatum<*>>,
var localIota: SpellDatum<*>, var localIota: SpellDatum<*>,
var parenCount: Int, var parenCount: Int,
var parenthesized: List<HexPattern>, var parenthesized: List<SpellDatum<*>>,
var escapeNext: Boolean, var escapeNext: Boolean,
val ctx: CastingContext, val ctx: CastingContext,
val prepackagedColorizer: FrozenColorizer? // for trinkets with colorizers val prepackagedColorizer: FrozenColorizer? // for trinkets with colorizers
@ -52,33 +47,46 @@ class CastingHarness private constructor(
) : this(mutableListOf(), SpellDatum.make(Widget.NULL), 0, mutableListOf(), false, ctx, prepackagedColorizer) ) : this(mutableListOf(), SpellDatum.make(Widget.NULL), 0, mutableListOf(), false, ctx, prepackagedColorizer)
/** /**
* Given a pattern, do all the updating/side effects/etc required. * Given an iota, do all the updating/side effects/etc required.
*/ */
fun executeNewPattern(newPat: HexPattern, world: ServerLevel): ControllerInfo { fun executeNewIota(iota: SpellDatum<*>, world: ServerLevel): ControllerInfo {
val result = this.getUpdate(newPat, world) val result = this.getUpdate(iota, world)
this.applyFunctionalData(result.newData) this.applyFunctionalData(result.newData)
return this.performSideEffects(result.sideEffects) return this.performSideEffects(result.sideEffects)
} }
fun getUpdate(iota: SpellDatum<*>, world: ServerLevel): CastResult {
// wouldn't it be nice to be able to go paren'
// i guess i'll call it paren2
val paren2 = this.handleParentheses(iota)
if (paren2 != null) {
return CastResult(
paren2,
listOf()
)
}
return if (iota.getType() == DatumType.PATTERN) {
updateWithPattern(iota.payload as HexPattern, world)
} else {
CastResult(
this.getFunctionalData(),
listOf(OperatorSideEffect.DoMishap(MishapUnescapedValue(iota),
Mishap.Context(HexPattern(HexDir.WEST), null))),
)
}
}
/** /**
* When the server gets a packet from the client with a new pattern, * When the server gets a packet from the client with a new pattern,
* handle it functionally. * handle it functionally.
*/ */
fun getUpdate(newPat: HexPattern, world: ServerLevel): CastResult { fun updateWithPattern(newPat: HexPattern, world: ServerLevel): CastResult {
if (this.ctx.spellCircle == null) if (this.ctx.spellCircle == null)
this.ctx.caster.awardStat(HexStatistics.PATTERNS_DRAWN) this.ctx.caster.awardStat(HexStatistics.PATTERNS_DRAWN)
var operatorIdPair: Pair<Operator, ResourceLocation>? = null var operatorIdPair: Pair<Operator, ResourceLocation>? = null
try { try {
// wouldn't it be nice to be able to go paren'
// i guess i'll call it paren2
val paren2 = this.handleParentheses(newPat)
if (paren2 != null) {
return CastResult(
paren2,
listOf(),
)
}
// Don't catch this one // Don't catch this one
operatorIdPair = PatternRegistry.matchPatternAndID(newPat, world) operatorIdPair = PatternRegistry.matchPatternAndID(newPat, world)
@ -186,17 +194,19 @@ class CastingHarness private constructor(
* Return a non-null value if we handled this in some sort of parenthesey way, * Return a non-null value if we handled this in some sort of parenthesey way,
* either escaping it onto the stack or changing the parenthese-handling state. * either escaping it onto the stack or changing the parenthese-handling state.
*/ */
private fun handleParentheses(newPat: HexPattern): FunctionalData? { private fun handleParentheses(iota: SpellDatum<*>): FunctionalData? {
val operator = try { val operator = (iota.payload as? HexPattern)?.let {
PatternRegistry.matchPattern(newPat, this.ctx.world) try {
} catch (mishap: Mishap) { PatternRegistry.matchPattern(it, this.ctx.world)
null } catch (mishap: Mishap) {
null
}
} }
return if (this.parenCount > 0) { return if (this.parenCount > 0) {
if (this.escapeNext) { if (this.escapeNext) {
val newParens = this.parenthesized.toMutableList() val newParens = this.parenthesized.toMutableList()
newParens.add(newPat) newParens.add(iota)
this.getFunctionalData().copy( this.getFunctionalData().copy(
escapeNext = false, escapeNext = false,
parenthesized = newParens parenthesized = newParens
@ -208,7 +218,7 @@ class CastingHarness private constructor(
} else if (operator == Widget.OPEN_PAREN) { } else if (operator == Widget.OPEN_PAREN) {
// we have escaped the parens onto the stack; we just also record our count. // we have escaped the parens onto the stack; we just also record our count.
val newParens = this.parenthesized.toMutableList() val newParens = this.parenthesized.toMutableList()
newParens.add(newPat) newParens.add(iota)
this.getFunctionalData().copy( this.getFunctionalData().copy(
parenthesized = newParens, parenthesized = newParens,
parenCount = this.parenCount + 1 parenCount = this.parenCount + 1
@ -217,7 +227,7 @@ class CastingHarness private constructor(
val newParenCount = this.parenCount - 1 val newParenCount = this.parenCount - 1
if (newParenCount == 0) { if (newParenCount == 0) {
val newStack = this.stack.toMutableList() val newStack = this.stack.toMutableList()
newStack.add(SpellDatum.make(this.parenthesized.map { SpellDatum.make(it) })) newStack.add(SpellDatum.make(this.parenthesized.toList()))
this.getFunctionalData().copy( this.getFunctionalData().copy(
stack = newStack, stack = newStack,
parenCount = newParenCount, parenCount = newParenCount,
@ -229,7 +239,7 @@ class CastingHarness private constructor(
// we have this situation: "(()" // we have this situation: "(()"
// we need to add the close paren // we need to add the close paren
val newParens = this.parenthesized.toMutableList() val newParens = this.parenthesized.toMutableList()
newParens.add(newPat) newParens.add(iota)
this.getFunctionalData().copy( this.getFunctionalData().copy(
parenCount = newParenCount, parenCount = newParenCount,
parenthesized = newParens parenthesized = newParens
@ -237,14 +247,14 @@ class CastingHarness private constructor(
} }
} else { } else {
val newParens = this.parenthesized.toMutableList() val newParens = this.parenthesized.toMutableList()
newParens.add(newPat) newParens.add(iota)
this.getFunctionalData().copy( this.getFunctionalData().copy(
parenthesized = newParens parenthesized = newParens
) )
} }
} else if (this.escapeNext) { } else if (this.escapeNext) {
val newStack = this.stack.toMutableList() val newStack = this.stack.toMutableList()
newStack.add(SpellDatum.make(newPat)) newStack.add(iota)
this.getFunctionalData().copy( this.getFunctionalData().copy(
stack = newStack, stack = newStack,
escapeNext = false, escapeNext = false,
@ -390,10 +400,13 @@ class CastingHarness private constructor(
val localTag = nbt.getCompound(TAG_LOCAL) val localTag = nbt.getCompound(TAG_LOCAL)
val localIota = SpellDatum.DeserializeFromNBT(localTag, ctx.world) val localIota = SpellDatum.DeserializeFromNBT(localTag, ctx.world)
val parenthesized = mutableListOf<HexPattern>() val parenthesized = mutableListOf<SpellDatum<*>>()
val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND) val parenTag = nbt.getList(TAG_PARENTHESIZED, Tag.TAG_COMPOUND)
for (subtag in parenTag) { for (subtag in parenTag) {
parenthesized.add(HexPattern.DeserializeFromNBT(subtag.asCompound)) if (subtag.asCompound.size() > 1)
parenthesized.add(SpellDatum.make(HexPattern.DeserializeFromNBT(subtag.asCompound)))
else
parenthesized.add(SpellDatum.DeserializeFromNBT(nbt, ctx.world))
} }
val parenCount = nbt.getInt(TAG_PAREN_COUNT) val parenCount = nbt.getInt(TAG_PAREN_COUNT)

View file

@ -1,7 +1,6 @@
package at.petrak.hexcasting.api.spell.casting package at.petrak.hexcasting.api.spell.casting
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.math.HexPattern
/** /**
* A change to the data in a CastHarness after a pattern is drawn. * A change to the data in a CastHarness after a pattern is drawn.
@ -11,7 +10,7 @@ import at.petrak.hexcasting.api.spell.math.HexPattern
data class FunctionalData( data class FunctionalData(
val stack: List<SpellDatum<*>>, val stack: List<SpellDatum<*>>,
val parenCount: Int, val parenCount: Int,
val parenthesized: List<HexPattern>, val parenthesized: List<SpellDatum<*>>,
val escapeNext: Boolean, val escapeNext: Boolean,
) { ) {
/** /**

View file

@ -0,0 +1,31 @@
package at.petrak.hexcasting.api.spell.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.DatumType
import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.Widget
import at.petrak.hexcasting.api.spell.casting.CastingContext
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor
/**
* The value was a naked iota without being Considered or Retrospected.
*/
class MishapUnescapedValue(
val perpetrator: SpellDatum<*>
) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.GRAY)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<SpellDatum<*>>) {
val idx = stack.indexOfLast { it.getType() == DatumType.LIST }
if (idx != -1)
stack[idx] = SpellDatum.make(Widget.GARBAGE)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context): Component =
error(
"unescaped",
perpetrator.display()
)
}

View file

@ -277,7 +277,7 @@ public class RegisterClientStuff {
private static void registerPackagedSpellOverrides(ItemPackagedSpell item) { private static void registerPackagedSpellOverrides(ItemPackagedSpell item) {
ItemProperties.register(item, ItemPackagedSpell.HAS_PATTERNS_PRED, ItemProperties.register(item, ItemPackagedSpell.HAS_PATTERNS_PRED,
(stack, level, holder, holderID) -> (stack, level, holder, holderID) ->
item.getPatterns(stack) != null ? 1f : 0f item.hasSpell(stack) ? 1f : 0f
); );
} }

View file

@ -7,9 +7,6 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.CastingHarness import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import net.minecraft.network.chat.TranslatableComponent
object OpEval : Operator { object OpEval : Operator {
override fun operate(stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
@ -23,13 +20,8 @@ object OpEval : Operator {
val sideEffects = mutableListOf<OperatorSideEffect>() val sideEffects = mutableListOf<OperatorSideEffect>()
for (pat in instrs) { for (insn in instrs) {
val pattern = if (pat.payload is HexPattern) { val res = harness.getUpdate(insn, ctx.world)
pat.payload
} else {
throw MishapInvalidIota(SpellDatum.make(instrs), 0, TranslatableComponent("hexcasting.mishap.invalid_value.list.pattern"))
}
val res = harness.getUpdate(pattern, ctx.world)
sideEffects.addAll(res.sideEffects) sideEffects.addAll(res.sideEffects)
if (res.sideEffects.any { it is OperatorSideEffect.DoMishap }) { if (res.sideEffects.any { it is OperatorSideEffect.DoMishap }) {
break break

View file

@ -7,10 +7,7 @@ import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.CastingHarness import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect import at.petrak.hexcasting.api.spell.casting.OperatorSideEffect
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import net.minecraft.network.chat.TranslatableComponent
object OpForEach : Operator { object OpForEach : Operator {
override fun operate(stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult { override fun operate(stack: MutableList<SpellDatum<*>>, local: SpellDatum<*>, ctx: CastingContext): OperationResult {
@ -33,17 +30,8 @@ object OpForEach : Operator {
harness.stack.addAll(stack) harness.stack.addAll(stack)
harness.stack.add(subdatum) harness.stack.add(subdatum)
harness.localIota = localIota harness.localIota = localIota
for (pat in instrs) { for (insn in instrs) {
val pattern = if (pat.payload is HexPattern) { val res = harness.getUpdate(insn, ctx.world)
pat.payload
} else {
throw MishapInvalidIota(
SpellDatum.make(instrs),
1,
TranslatableComponent("hexcasting.mishap.invalid_value.list.pattern")
)
}
val res = harness.getUpdate(pattern, ctx.world)
sideEffects.addAll(res.sideEffects) sideEffects.addAll(res.sideEffects)
if (res.sideEffects.any { it is OperatorSideEffect.DoMishap }) { if (res.sideEffects.any { it is OperatorSideEffect.DoMishap }) {
return OperationResult(harness.stack, harness.localIota, sideEffects) return OperationResult(harness.stack, harness.localIota, sideEffects)

View file

@ -20,13 +20,13 @@ class OpErase : SpellOperator {
val spellHolder = HexCapabilities.getCapability(it, HexCapabilities.SPELL) val spellHolder = HexCapabilities.getCapability(it, HexCapabilities.SPELL)
val datumHolder = HexCapabilities.getCapability(it, HexCapabilities.DATUM) val datumHolder = HexCapabilities.getCapability(it, HexCapabilities.DATUM)
(spellHolder.isPresent && spellHolder.get().patterns != null) || (spellHolder.isPresent && spellHolder.get().hasSpell()) ||
(datumHolder.isPresent && datumHolder.get().writeDatum(null, true)) (datumHolder.isPresent && datumHolder.get().writeDatum(null, true))
} }
val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL) val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL)
val datumHolder = HexCapabilities.getCapability(handStack, HexCapabilities.DATUM) val datumHolder = HexCapabilities.getCapability(handStack, HexCapabilities.DATUM)
if ((!spellHolder.isPresent || spellHolder.get().patterns == null) && if ((!spellHolder.isPresent || !spellHolder.get().hasSpell()) &&
(!datumHolder.isPresent || datumHolder.get().readDatum(ctx.world) == null || (!datumHolder.isPresent || datumHolder.get().readDatum(ctx.world) == null ||
!datumHolder.get().writeDatum(null, true))) { !datumHolder.get().writeDatum(null, true))) {
throw MishapBadOffhandItem.of(handStack, hand, "eraseable") throw MishapBadOffhandItem.of(handStack, hand, "eraseable")
@ -41,13 +41,13 @@ class OpErase : SpellOperator {
val spellHolder = HexCapabilities.getCapability(it, HexCapabilities.SPELL) val spellHolder = HexCapabilities.getCapability(it, HexCapabilities.SPELL)
val datumHolder = HexCapabilities.getCapability(it, HexCapabilities.DATUM) val datumHolder = HexCapabilities.getCapability(it, HexCapabilities.DATUM)
(spellHolder.isPresent && spellHolder.get().patterns != null) || (spellHolder.isPresent && spellHolder.get().hasSpell()) ||
(datumHolder.isPresent && datumHolder.get().writeDatum(null, true)) (datumHolder.isPresent && datumHolder.get().writeDatum(null, true))
} }
val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL) val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL)
val datumHolder = HexCapabilities.getCapability(handStack, HexCapabilities.DATUM) val datumHolder = HexCapabilities.getCapability(handStack, HexCapabilities.DATUM)
if (spellHolder.isPresent && spellHolder.get().patterns != null) if (spellHolder.isPresent && spellHolder.get().hasSpell())
spellHolder.get().clearPatterns() spellHolder.get().clearPatterns()
if (datumHolder.isPresent && datumHolder.get().writeDatum(null, true)) if (datumHolder.isPresent && datumHolder.get().writeDatum(null, true))

View file

@ -7,13 +7,10 @@ import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.SpellDatum import at.petrak.hexcasting.api.spell.SpellDatum
import at.petrak.hexcasting.api.spell.SpellOperator import at.petrak.hexcasting.api.spell.SpellOperator
import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.utils.ManaHelper import at.petrak.hexcasting.api.utils.ManaHelper
import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell import at.petrak.hexcasting.common.items.magic.ItemPackagedSpell
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.entity.item.ItemEntity
class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int) : SpellOperator { class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int) : SpellOperator {
@ -23,18 +20,17 @@ class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int)
ctx: CastingContext ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> { ): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val entity = args.getChecked<ItemEntity>(0) val entity = args.getChecked<ItemEntity>(0)
val patternsRaw = args.getChecked<List<SpellDatum<*>>>(1) val patterns = args.getChecked<List<SpellDatum<*>>>(1)
val patterns = patternsRaw.map { val (handStack, hand) = ctx.getHeldItemToOperateOn {
if (it.payload is HexPattern) val spellHolder = HexCapabilities.getCapability(it, HexCapabilities.SPELL)
it.payload it.`is`(itemType) && spellHolder.isPresent && !spellHolder.get().hasSpell()
else
throw MishapInvalidIota(SpellDatum.make(patternsRaw), 0, TranslatableComponent("hexcasting.mishap.invalid_value.list.pattern"))
} }
val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL)
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.`is`(itemType) }
if (!handStack.`is`(itemType)) { if (!handStack.`is`(itemType)) {
throw MishapBadOffhandItem(handStack, hand, itemType.description) throw MishapBadOffhandItem(handStack, hand, itemType.description)
} else if (spellHolder.get().hasSpell()) {
throw MishapBadOffhandItem.of(handStack, hand, "iota.write")
} }
ctx.assertEntityInRange(entity) ctx.assertEntityInRange(entity)
@ -53,12 +49,12 @@ class OpMakePackagedSpell<T : ItemPackagedSpell>(val itemType: T, val cost: Int)
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.Burst(entity.position(), 0.5))) return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.Burst(entity.position(), 0.5)))
} }
private inner class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell { private inner class Spell(val itemEntity: ItemEntity, val patterns: List<SpellDatum<*>>) : RenderedSpell {
override fun cast(ctx: CastingContext) { override fun cast(ctx: CastingContext) {
val (handStack) = ctx.getHeldItemToOperateOn { it.`is`(itemType) } val (handStack) = ctx.getHeldItemToOperateOn { it.`is`(itemType) }
val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL) val spellHolder = HexCapabilities.getCapability(handStack, HexCapabilities.SPELL)
if (spellHolder.isPresent if (spellHolder.isPresent
&& spellHolder.get().patterns == null && !spellHolder.get().hasSpell()
&& itemEntity.isAlive && itemEntity.isAlive
) { ) {
val entityStack = itemEntity.item.copy() val entityStack = itemEntity.item.copy()

View file

@ -2,6 +2,7 @@ package at.petrak.hexcasting.common.items.magic;
import at.petrak.hexcasting.HexMod; import at.petrak.hexcasting.HexMod;
import at.petrak.hexcasting.api.item.SpellHolderItem; import at.petrak.hexcasting.api.item.SpellHolderItem;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.api.spell.casting.CastingContext; import at.petrak.hexcasting.api.spell.casting.CastingContext;
import at.petrak.hexcasting.api.spell.casting.CastingHarness; import at.petrak.hexcasting.api.spell.casting.CastingHarness;
import at.petrak.hexcasting.api.utils.NBTHelper; import at.petrak.hexcasting.api.utils.NBTHelper;
@ -11,6 +12,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.stats.Stat; import net.minecraft.stats.Stat;
@ -50,23 +52,32 @@ public abstract class ItemPackagedSpell extends ItemManaHolder implements SpellH
} }
@Override @Override
public @Nullable List<HexPattern> getPatterns(ItemStack stack) { public boolean hasSpell(ItemStack stack) {
return NBTHelper.hasCompound(stack, TAG_PATTERNS);
}
@Override
public @Nullable List<SpellDatum<?>> getSpell(ItemStack stack, ServerLevel level) {
var patsTag = NBTHelper.getList(stack, TAG_PATTERNS, Tag.TAG_COMPOUND); var patsTag = NBTHelper.getList(stack, TAG_PATTERNS, Tag.TAG_COMPOUND);
if (patsTag == null) if (patsTag == null)
return null; return null;
var out = new ArrayList<HexPattern>(); var out = new ArrayList<SpellDatum<?>>();
for (var patTag : patsTag) { for (var patTag : patsTag) {
out.add(HexPattern.DeserializeFromNBT((CompoundTag) patTag)); CompoundTag tag = NBTHelper.getAsCompound(patTag);
if (tag.size() > 1)
out.add(SpellDatum.make(HexPattern.DeserializeFromNBT(tag)));
else
out.add(SpellDatum.DeserializeFromNBT(tag, level));
} }
return out; return out;
} }
@Override @Override
public void writePatterns(ItemStack stack, List<HexPattern> patterns, int mana) { public void writePatterns(ItemStack stack, List<SpellDatum<?>> patterns, int mana) {
ListTag patsTag = new ListTag(); ListTag patsTag = new ListTag();
for (HexPattern pat : patterns) for (SpellDatum<?> pat : patterns)
patsTag.add(pat.serializeToNBT()); patsTag.add(pat.serializeToNBT());
NBTHelper.putList(stack, TAG_PATTERNS, patsTag); NBTHelper.putList(stack, TAG_PATTERNS, patsTag);
@ -83,19 +94,23 @@ public abstract class ItemPackagedSpell extends ItemManaHolder implements SpellH
@Override @Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand usedHand) { public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand usedHand) {
var stack = player.getItemInHand(usedHand); var stack = player.getItemInHand(usedHand);
List<HexPattern> patterns = getPatterns(stack); if (!hasSpell(stack)) {
if (patterns == null) {
return InteractionResultHolder.fail(stack); return InteractionResultHolder.fail(stack);
} }
if (world.isClientSide) { if (world.isClientSide) {
return InteractionResultHolder.success(stack); return InteractionResultHolder.success(stack);
} }
List<SpellDatum<?>> instrs = getSpell(stack, (ServerLevel) world);
if (instrs == null) {
return InteractionResultHolder.fail(stack);
}
var sPlayer = (ServerPlayer) player; var sPlayer = (ServerPlayer) player;
var ctx = new CastingContext(sPlayer, usedHand); var ctx = new CastingContext(sPlayer, usedHand);
var harness = new CastingHarness(ctx); var harness = new CastingHarness(ctx);
for (var pattern : patterns) { for (var insn : instrs) {
var info = harness.executeNewPattern(pattern, sPlayer.getLevel()); var info = harness.executeNewIota(insn, sPlayer.getLevel());
if (info.getWasPrevPatternInvalid()) { if (info.getWasPrevPatternInvalid()) {
break; break;
} }

View file

@ -1,15 +1,13 @@
package at.petrak.hexcasting.common.lib; package at.petrak.hexcasting.common.lib;
import at.petrak.hexcasting.api.cap.Colorizer; import at.petrak.hexcasting.api.cap.*;
import at.petrak.hexcasting.api.cap.DataHolder; import at.petrak.hexcasting.api.item.ColorizerItem;
import at.petrak.hexcasting.api.cap.ManaHolder; import at.petrak.hexcasting.api.item.DataHolderItem;
import at.petrak.hexcasting.api.cap.SpellHolder; import at.petrak.hexcasting.api.item.ManaHolderItem;
import at.petrak.hexcasting.api.item.SpellHolderItem;
import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.item.*;
import at.petrak.hexcasting.api.spell.SpellDatum; import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.api.cap.HexCapabilities;
import at.petrak.hexcasting.common.items.HexItems; import at.petrak.hexcasting.common.items.HexItems;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -18,7 +16,9 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeConfigSpec; import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.capabilities.*; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.NonNullSupplier; import net.minecraftforge.common.util.NonNullSupplier;
import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.AttachCapabilitiesEvent;
@ -51,44 +51,48 @@ public class HexCapabilityHandler {
public static void attachCaps(AttachCapabilitiesEvent<ItemStack> evt) { public static void attachCaps(AttachCapabilitiesEvent<ItemStack> evt) {
ItemStack stack = evt.getObject(); ItemStack stack = evt.getObject();
if (stack.getItem() instanceof ManaHolderItem holder) if (stack.getItem() instanceof ManaHolderItem holder)
evt.addCapability(MANA_HOLDER_CAPABILITY, provide(HexCapabilities.MANA, evt.addCapability(MANA_HOLDER_CAPABILITY, provide(stack, HexCapabilities.MANA,
() -> new ItemBasedManaHolder(holder, stack))); () -> new ItemBasedManaHolder(holder, stack)));
else if (stack.is(HexItems.AMETHYST_DUST.get())) else if (stack.is(HexItems.AMETHYST_DUST.get()))
evt.addCapability(MANA_ITEM_CAPABILITY, provide(HexCapabilities.MANA, evt.addCapability(MANA_ITEM_CAPABILITY, provide(stack, HexCapabilities.MANA,
() -> new StaticManaHolder(HexConfig.dustManaAmount, 3, stack))); () -> new StaticManaHolder(HexConfig.dustManaAmount, 3, stack)));
else if (stack.is(Items.AMETHYST_SHARD)) else if (stack.is(Items.AMETHYST_SHARD))
evt.addCapability(MANA_ITEM_CAPABILITY, provide(HexCapabilities.MANA, evt.addCapability(MANA_ITEM_CAPABILITY, provide(stack, HexCapabilities.MANA,
() -> new StaticManaHolder(HexConfig.shardManaAmount, 2, stack))); () -> new StaticManaHolder(HexConfig.shardManaAmount, 2, stack)));
else if (stack.is(HexItems.CHARGED_AMETHYST.get())) else if (stack.is(HexItems.CHARGED_AMETHYST.get()))
evt.addCapability(MANA_ITEM_CAPABILITY, provide(HexCapabilities.MANA, evt.addCapability(MANA_ITEM_CAPABILITY, provide(stack, HexCapabilities.MANA,
() -> new StaticManaHolder(HexConfig.chargedCrystalManaAmount, 1, stack))); () -> new StaticManaHolder(HexConfig.chargedCrystalManaAmount, 1, stack)));
if (stack.getItem() instanceof DataHolderItem holder) if (stack.getItem() instanceof DataHolderItem holder)
evt.addCapability(DATA_HOLDER_CAPABILITY, provide(HexCapabilities.DATUM, evt.addCapability(DATA_HOLDER_CAPABILITY, provide(stack, HexCapabilities.DATUM,
() -> new ItemBasedDataHolder(holder, stack))); () -> new ItemBasedDataHolder(holder, stack)));
else if (stack.is(Items.PUMPKIN_PIE)) // haha yes else if (stack.is(Items.PUMPKIN_PIE)) // haha yes
evt.addCapability(DATA_ITEM_CAPABILITY, provide(HexCapabilities.DATUM, evt.addCapability(DATA_ITEM_CAPABILITY, provide(stack, HexCapabilities.DATUM,
() -> new StaticDatumHolder((s) -> SpellDatum.make(Math.PI * s.getCount()), stack))); () -> new StaticDatumHolder((s) -> SpellDatum.make(Math.PI * s.getCount()), stack)));
if (stack.getItem() instanceof SpellHolderItem holder) if (stack.getItem() instanceof SpellHolderItem holder)
evt.addCapability(SPELL_HOLDER_CAPABILITY, provide(HexCapabilities.SPELL, evt.addCapability(SPELL_HOLDER_CAPABILITY, provide(stack, HexCapabilities.SPELL,
() -> new ItemBasedSpellHolder(holder, stack))); () -> new ItemBasedSpellHolder(holder, stack)));
if (stack.getItem() instanceof ColorizerItem colorizer) if (stack.getItem() instanceof ColorizerItem colorizer)
evt.addCapability(COLORIZER_CAPABILITY, provide(HexCapabilities.COLOR, evt.addCapability(COLORIZER_CAPABILITY, provide(stack, HexCapabilities.COLOR,
() -> new ItemBasedColorizer(colorizer, stack))); () -> new ItemBasedColorizer(colorizer, stack)));
} }
private static <CAP> SimpleProvider<CAP> provide(Capability<CAP> capability, NonNullSupplier<CAP> supplier) { private static <CAP> SimpleProvider<CAP> provide(ItemStack stack, Capability<CAP> capability, NonNullSupplier<CAP> supplier) {
return new SimpleProvider<>(capability, LazyOptional.of(supplier)); return new SimpleProvider<>(stack, capability, LazyOptional.of(supplier));
} }
private record SimpleProvider<CAP>(Capability<CAP> capability, private record SimpleProvider<CAP>(ItemStack stack,
Capability<CAP> capability,
LazyOptional<CAP> instance) implements ICapabilityProvider { LazyOptional<CAP> instance) implements ICapabilityProvider {
@NotNull @NotNull
@Override @Override
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) { public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
if (stack.isEmpty())
return LazyOptional.empty();
return cap == capability ? instance.cast() : LazyOptional.empty(); return cap == capability ? instance.cast() : LazyOptional.empty();
} }
} }
@ -245,12 +249,17 @@ public class HexCapabilityHandler {
} }
@Override @Override
public @Nullable List<HexPattern> getPatterns() { public boolean hasSpell() {
return holder.getPatterns(stack); return holder.hasSpell(stack);
} }
@Override @Override
public void writePatterns(List<HexPattern> patterns, int mana) { public @Nullable List<SpellDatum<?>> getPatterns(ServerLevel level) {
return holder.getSpell(stack, level);
}
@Override
public void writePatterns(List<SpellDatum<?>> patterns, int mana) {
holder.writePatterns(stack, patterns, mana); holder.writePatterns(stack, patterns, mana);
} }

View file

@ -1,12 +1,13 @@
package at.petrak.hexcasting.common.network; package at.petrak.hexcasting.common.network;
import at.petrak.hexcasting.api.mod.HexItemTags;
import at.petrak.hexcasting.api.player.HexPlayerDataHelper; import at.petrak.hexcasting.api.player.HexPlayerDataHelper;
import at.petrak.hexcasting.api.spell.SpellDatum;
import at.petrak.hexcasting.api.spell.casting.ControllerInfo; import at.petrak.hexcasting.api.spell.casting.ControllerInfo;
import at.petrak.hexcasting.api.spell.casting.ResolvedPattern; import at.petrak.hexcasting.api.spell.casting.ResolvedPattern;
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternValidity; import at.petrak.hexcasting.api.spell.casting.ResolvedPatternValidity;
import at.petrak.hexcasting.api.spell.math.HexCoord; import at.petrak.hexcasting.api.spell.math.HexCoord;
import at.petrak.hexcasting.api.spell.math.HexPattern; import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.mod.HexItemTags;
import at.petrak.hexcasting.common.lib.HexSounds; import at.petrak.hexcasting.common.lib.HexSounds;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
@ -74,7 +75,7 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
if (autoFail) { if (autoFail) {
clientInfo = new ControllerInfo(false, false, harness.getStack().isEmpty(), true, harness.generateDescs()); clientInfo = new ControllerInfo(false, false, harness.getStack().isEmpty(), true, harness.generateDescs());
} else { } else {
clientInfo = harness.executeNewPattern(this.pattern, sender.getLevel()); clientInfo = harness.executeNewIota(SpellDatum.make(this.pattern), sender.getLevel());
if (clientInfo.getWasSpellCast() && clientInfo.getHasCastingSound()) { if (clientInfo.getWasSpellCast() && clientInfo.getHasCastingSound()) {
sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(), sender.level.playSound(null, sender.getX(), sender.getY(), sender.getZ(),

View file

@ -336,6 +336,7 @@
"hexcasting.spell.unknown": "Special Handler", "hexcasting.spell.unknown": "Special Handler",
"hexcasting.mishap.invalid_pattern": "That pattern isn't associated with any action", "hexcasting.mishap.invalid_pattern": "That pattern isn't associated with any action",
"hexcasting.mishap.unescaped": "Expected to evaluate a pattern, but evaluated %s instead",
"hexcasting.mishap.invalid_value": "%s expected %s at index %s of the stack, but got %s", "hexcasting.mishap.invalid_value": "%s expected %s at index %s of the stack, but got %s",
"hexcasting.mishap.invalid_value.class.double": "a number", "hexcasting.mishap.invalid_value.class.double": "a number",
"hexcasting.mishap.invalid_value.class.vector": "a vector", "hexcasting.mishap.invalid_value.class.vector": "a vector",