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..2ca8ddbabb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/HexAPI.java @@ -1,5 +1,6 @@ package at.petrak.hexcasting.api; +import at.petrak.hexcasting.api.addldata.ADIotaHolder; import at.petrak.hexcasting.api.addldata.ADMediaHolder; import at.petrak.hexcasting.api.casting.ActionRegistryEntry; import at.petrak.hexcasting.api.casting.castables.SpecialHandler; @@ -8,15 +9,16 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions; import com.google.common.base.Suppliers; import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ArmorItem; @@ -213,6 +215,26 @@ default ArmorMaterial robesMaterial() { return DUMMY_ARMOR_MATERIAL; } + @FunctionalInterface + interface IBlockyIotaProvider{ + ADIotaHolder getIotaHolder(ServerLevel level, BlockPos pos); + } + + /** + * Register's an iota holder provider for a block to be used with Chronicler's. + *

+ * NOTE: should implement IBlockyIotaProvider on the block or ADIotaHolder on the block entity if possible. + * This method should only be used in cases where that's not feasible, such as an optional hex + * dependency or adding a provider to another mod's block. + *

+ * @param blockID the resloc/id for the block + * @return if the holder was successfully registered + * @see IXplatAbstractions#findDataHolder(BlockPos, ServerLevel) + */ + default boolean registerBlockyIotaHolder(ResourceLocation blockID, IBlockyIotaProvider holder){ + return false; + } + /** * Location in the userdata of the ravenmind */ diff --git a/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java b/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java index 7b8fcc1c95..0ce2d446cb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/addldata/ADIotaHolder.java @@ -34,4 +34,24 @@ default Iota emptyIota() { * @return whether it is possible to write to this IotaHolder */ boolean writeable(); + + // a helper for read-only holders + interface ADIotaHolderReadOnly extends ADIotaHolder{ + default boolean writeIota(@Nullable Iota iota, boolean simulate){ + return false; + } + + default boolean writeable(){ return false; } + } + + // wraps an iota in an iota holder + static ADIotaHolder ofStatic(Iota iota){ + return new ADIotaHolderReadOnly(){ + @Nullable + public CompoundTag readIotaTag(){ return IotaType.serialize(iota); } + + @Nullable + public Iota readIota(ServerLevel world) { return iota; } + }; + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt index 26f39da2f3..23a94f56ca 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/ActionUtils.kt @@ -275,6 +275,20 @@ fun List.getLongOrList(idx: Int, argc: Int = 0): Either { ) } + +fun List.getEntityOrBlockPos(idx: Int, argc: Int = 0): Either { + val datum = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) } + return when (datum) { + is EntityIota -> Either.left(datum.entity) + is Vec3Iota -> Either.right(BlockPos.containing(datum.vec3)) + else -> throw MishapInvalidIota.of( + datum, + if (argc == 0) idx else argc - (idx + 1), + "entity_or_vector" + ) + } +} + fun evaluatable(datum: Iota, reverseIdx: Int): Either = when (datum) { is ListIota -> Either.right(datum.list) diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapBad.kt b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapBad.kt new file mode 100644 index 0000000000..a5752090da --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/mishaps/MishapBad.kt @@ -0,0 +1,24 @@ +package at.petrak.hexcasting.api.casting.mishaps + +import com.mojang.datafixers.util.Either +import net.minecraft.core.BlockPos +import net.minecraft.world.entity.Entity +import net.minecraft.world.item.ItemStack +import net.minecraft.world.phys.Vec3 + +// a silly stupid wrapper for throwing arbitrary badness mishaps (the ones that have a .of(thing, stub) constructor. +class MishapBad { + companion object { + @JvmStatic + fun of(thing: Any, stub: String): Mishap { + return when(thing){ + is Either<*, *> -> thing.map({l -> of(l, stub)}, {r -> of(r, stub)}) + is Entity -> MishapBadEntity.of(thing, stub) + is BlockPos -> MishapBadBlock.of(thing, stub) + is Vec3 -> MishapBadLocation(thing, stub) + is ItemStack -> MishapBadOffhandItem.of(thing, stub) + else -> throw IllegalArgumentException() + } + } + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java index db3f96ac14..ba5ead599c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicRecord.java @@ -1,7 +1,11 @@ package at.petrak.hexcasting.common.blocks.akashic; +import at.petrak.hexcasting.api.HexAPI.IBlockyIotaProvider; +import at.petrak.hexcasting.api.addldata.ADIotaHolder; import at.petrak.hexcasting.api.casting.iota.Iota; import at.petrak.hexcasting.api.casting.iota.IotaType; +import at.petrak.hexcasting.api.casting.iota.ListIota; +import at.petrak.hexcasting.api.casting.iota.PatternIota; import at.petrak.hexcasting.api.casting.math.HexPattern; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; @@ -9,7 +13,10 @@ import net.minecraft.world.level.block.Block; import org.jetbrains.annotations.Nullable; -public class BlockAkashicRecord extends Block { +import java.util.ArrayList; +import java.util.List; + +public class BlockAkashicRecord extends Block implements IBlockyIotaProvider { public BlockAkashicRecord(Properties p_49795_) { super(p_49795_); } @@ -61,4 +68,19 @@ Iota lookupPattern(BlockPos herePos, HexPattern key, ServerLevel slevel) { } // TODO get comparators working again and also cache the number of iotas somehow? + + @Override + public ADIotaHolder getIotaHolder(ServerLevel level, BlockPos recPos) { + List labels = new ArrayList<>(); + AkashicFloodfiller.floodFillFor(recPos, level, + (pos, bs, world) -> { + if(world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile + && tile.getPattern() != null + ){ + labels.add(new PatternIota(tile.getPattern())); + } + return false; + }); + return ADIotaHolder.ofStatic(new ListIota(labels)); + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java index d6e4cd8041..bf7d101436 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockEntityAkashicBookshelf.java @@ -1,17 +1,20 @@ package at.petrak.hexcasting.common.blocks.akashic; +import at.petrak.hexcasting.api.addldata.ADIotaHolder.ADIotaHolderReadOnly; import at.petrak.hexcasting.api.block.HexBlockEntity; import at.petrak.hexcasting.api.casting.iota.Iota; import at.petrak.hexcasting.api.casting.iota.IotaType; +import at.petrak.hexcasting.api.casting.iota.PatternIota; import at.petrak.hexcasting.api.casting.math.HexPattern; import at.petrak.hexcasting.client.render.HexPatternPoints; import at.petrak.hexcasting.common.lib.HexBlockEntities; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; -public class BlockEntityAkashicBookshelf extends HexBlockEntity { +public class BlockEntityAkashicBookshelf extends HexBlockEntity implements ADIotaHolderReadOnly { public static final String TAG_PATTERN = "pattern"; public static final String TAG_IOTA = "iota"; public static final String TAG_DUMMY = "dummy"; @@ -89,4 +92,20 @@ protected void loadModData(CompoundTag tag) { this.iotaTag = null; } } + + @Override + public CompoundTag readIotaTag(){ + if(pattern == null){ + return IotaType.serialize(emptyIota()); + } + return IotaType.serialize(new PatternIota(pattern)); + } + + @Override + public Iota readIota(ServerLevel world) { + if(pattern == null){ + return emptyIota(); + } + return new PatternIota(pattern); + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntitySlate.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntitySlate.java index 9a0ec965bc..d831e75b0f 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntitySlate.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntitySlate.java @@ -1,15 +1,21 @@ package at.petrak.hexcasting.common.blocks.circles; +import at.petrak.hexcasting.api.addldata.ADIotaHolder; import at.petrak.hexcasting.api.block.HexBlockEntity; +import at.petrak.hexcasting.api.casting.iota.Iota; +import at.petrak.hexcasting.api.casting.iota.IotaType; +import at.petrak.hexcasting.api.casting.iota.NullIota; +import at.petrak.hexcasting.api.casting.iota.PatternIota; import at.petrak.hexcasting.api.casting.math.HexPattern; import at.petrak.hexcasting.common.lib.HexBlockEntities; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; -public class BlockEntitySlate extends HexBlockEntity { +public class BlockEntitySlate extends HexBlockEntity implements ADIotaHolder { public static final String TAG_PATTERN = "pattern"; @Nullable @@ -42,4 +48,39 @@ protected void loadModData(CompoundTag tag) { } } + @Override + public CompoundTag readIotaTag(){ + if(pattern == null){ + return IotaType.serialize(emptyIota()); + } + return IotaType.serialize(new PatternIota(pattern)); + } + + @Override + public Iota readIota(ServerLevel world) { + if(pattern == null){ + return emptyIota(); + } + return new PatternIota(pattern); + } + + @Override + public boolean writeIota(@Nullable Iota iota, boolean simulate){ + if(!simulate){ + if(iota instanceof PatternIota pIota){ + this.pattern = pIota.getPattern(); + sync(); + } + if(iota instanceof NullIota || iota == null){ + this.pattern = null; + sync(); + } + } + return iota instanceof PatternIota || iota instanceof NullIota || iota == null; + } + + @Override + public boolean writeable(){ + return true; + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerRead.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerRead.kt index 4fd4a69f8b..63490908e5 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerRead.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerRead.kt @@ -2,9 +2,9 @@ package at.petrak.hexcasting.common.casting.actions.rw import at.petrak.hexcasting.api.casting.castables.ConstMediaAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment -import at.petrak.hexcasting.api.casting.getEntity +import at.petrak.hexcasting.api.casting.getEntityOrBlockPos import at.petrak.hexcasting.api.casting.iota.Iota -import at.petrak.hexcasting.api.casting.mishaps.MishapBadEntity +import at.petrak.hexcasting.api.casting.mishaps.MishapBad import at.petrak.hexcasting.xplat.IXplatAbstractions object OpTheCoolerRead : ConstMediaAction { @@ -14,16 +14,18 @@ object OpTheCoolerRead : ConstMediaAction { args: List, env: CastingEnvironment ): List { - val target = args.getEntity(0, argc) + val target = args.getEntityOrBlockPos(0, argc) - env.assertEntityInRange(target) + target.map(env::assertEntityInRange, env::assertPosInRange) - val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target) - ?: throw MishapBadEntity.of(target, "iota.read") + val datumHolder = target.map( + IXplatAbstractions.INSTANCE::findDataHolder, + {pos -> IXplatAbstractions.INSTANCE.findDataHolder(pos, env.world)}) + ?: throw MishapBad.of(target, "iota.read") val datum = datumHolder.readIota(env.world) ?: datumHolder.emptyIota() - ?: throw MishapBadEntity.of(target, "iota.read") + ?: throw MishapBad.of(target, "iota.read") return listOf(datum) } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerReadable.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerReadable.kt index aa118182af..7ebed435f7 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerReadable.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerReadable.kt @@ -3,7 +3,7 @@ package at.petrak.hexcasting.common.casting.actions.rw import at.petrak.hexcasting.api.casting.asActionResult import at.petrak.hexcasting.api.casting.castables.ConstMediaAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment -import at.petrak.hexcasting.api.casting.getEntity +import at.petrak.hexcasting.api.casting.getEntityOrBlockPos import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.xplat.IXplatAbstractions @@ -14,10 +14,13 @@ object OpTheCoolerReadable : ConstMediaAction { args: List, env: CastingEnvironment ): List { - val target = args.getEntity(0, argc) - env.assertEntityInRange(target) + val target = args.getEntityOrBlockPos(0, argc) - val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target) + target.map(env::assertEntityInRange, env::assertPosInRange) + + val datumHolder = target.map( + IXplatAbstractions.INSTANCE::findDataHolder, + {pos -> IXplatAbstractions.INSTANCE.findDataHolder(pos, env.world)}) ?: return false.asActionResult datumHolder.readIota(env.world) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWritable.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWritable.kt index 598ae8b145..6025362ff1 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWritable.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWritable.kt @@ -3,7 +3,7 @@ package at.petrak.hexcasting.common.casting.actions.rw import at.petrak.hexcasting.api.casting.asActionResult import at.petrak.hexcasting.api.casting.castables.ConstMediaAction import at.petrak.hexcasting.api.casting.eval.CastingEnvironment -import at.petrak.hexcasting.api.casting.getEntity +import at.petrak.hexcasting.api.casting.getEntityOrBlockPos import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.iota.NullIota import at.petrak.hexcasting.xplat.IXplatAbstractions @@ -15,10 +15,13 @@ object OpTheCoolerWritable : ConstMediaAction { args: List, env: CastingEnvironment ): List { - val target = args.getEntity(0, argc) - env.assertEntityInRange(target) + val target = args.getEntityOrBlockPos(0, argc) - val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target) + target.map(env::assertEntityInRange, env::assertPosInRangeForEditing) + + val datumHolder = target.map( + IXplatAbstractions.INSTANCE::findDataHolder, + {pos -> IXplatAbstractions.INSTANCE.findDataHolder(pos, env.world)}) ?: return false.asActionResult val success = datumHolder.writeIota(NullIota(), true) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWrite.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWrite.kt index 27e02a8f97..ac726bf358 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWrite.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/rw/OpTheCoolerWrite.kt @@ -5,11 +5,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.getEntity +import at.petrak.hexcasting.api.casting.getEntityOrBlockPos import at.petrak.hexcasting.api.casting.iota.Iota -import at.petrak.hexcasting.api.casting.mishaps.MishapBadEntity +import at.petrak.hexcasting.api.casting.mishaps.MishapBad import at.petrak.hexcasting.api.casting.mishaps.MishapOthersName import at.petrak.hexcasting.xplat.IXplatAbstractions +import net.minecraft.core.BlockPos import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.phys.Vec3 @@ -19,16 +20,18 @@ object OpTheCoolerWrite : SpellAction { args: List, env: CastingEnvironment ): SpellAction.Result { - val target = args.getEntity(0, argc) + val target = args.getEntityOrBlockPos(0, argc) val datum = args[1] - env.assertEntityInRange(target) + target.map(env::assertEntityInRange, env::assertPosInRangeForEditing) - val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target) - ?: throw MishapBadEntity.of(target, "iota.write") + val datumHolder = target.map( + IXplatAbstractions.INSTANCE::findDataHolder, + {pos -> IXplatAbstractions.INSTANCE.findDataHolder(pos, env.world)}) + ?: throw MishapBad.of(target, "iota.write") if (!datumHolder.writeIota(datum, true)) - throw MishapBadEntity.of(target, "iota.write") + throw MishapBad.of(target, "iota.write") // We pass null here so that even the own caster won't be allowed into a focus. // Otherwise, you could sentinel scout to people and remotely write their names into things using a cleric circle. @@ -36,12 +39,13 @@ object OpTheCoolerWrite : SpellAction { if (trueName != null) throw MishapOthersName(trueName) - val burstPos = if (target is ItemEntity) { - // Special case these because the render is way above the entity - target.position().add(0.0, 3.0 / 8.0, 0.0) - } else { - target.position() - } + val burstPos = target.map({ent -> if (ent is ItemEntity) { + // Special case these because the render is way above the entity + ent.position().add(0.0, 3.0 / 8.0, 0.0) + } else { + ent.position() + }}, BlockPos::getCenter) + return SpellAction.Result( Spell(datum, datumHolder), 0, diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpIgnite.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpIgnite.kt index eeb7fae5cf..e7a501f596 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpIgnite.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/actions/spells/OpIgnite.kt @@ -4,12 +4,8 @@ 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.getBlockPos -import at.petrak.hexcasting.api.casting.getEntity -import at.petrak.hexcasting.api.casting.iota.EntityIota +import at.petrak.hexcasting.api.casting.getEntityOrBlockPos import at.petrak.hexcasting.api.casting.iota.Iota -import at.petrak.hexcasting.api.casting.iota.Vec3Iota -import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota import at.petrak.hexcasting.api.misc.MediaConstants import at.petrak.hexcasting.ktxt.UseOnContext import at.petrak.hexcasting.xplat.IXplatAbstractions @@ -18,6 +14,7 @@ import net.minecraft.core.Direction import net.minecraft.server.level.ServerPlayer import net.minecraft.world.InteractionHand import net.minecraft.world.entity.Entity +import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items @@ -30,27 +27,22 @@ object OpIgnite : SpellAction { args: List, env: CastingEnvironment ): SpellAction.Result { - when (val target = args[0]) { - is EntityIota -> { - val entity = args.getEntity(0, argc) - env.assertEntityInRange(entity) - return SpellAction.Result( - EntitySpell(entity), - MediaConstants.DUST_UNIT, - listOf(ParticleSpray.burst(entity.position(), 1.0)) - ) - } - is Vec3Iota -> { - val block = args.getBlockPos(0, argc) - env.assertPosInRangeForEditing(block) - return SpellAction.Result( - BlockSpell(block), - MediaConstants.DUST_UNIT, - listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(block)), 1.0)) - ) - } - else -> throw MishapInvalidIota.ofType(target, 0, "entity_or_vector") - } + val target = args.getEntityOrBlockPos(0, argc) + + target.map(env::assertEntityInRange, env::assertPosInRange) + + val burstPos = target.map({ent -> if (ent is ItemEntity) { + // Special case these because the render is way above the entity + ent.position().add(0.0, 3.0 / 8.0, 0.0) + } else { + ent.position() + }}, BlockPos::getCenter) + + return SpellAction.Result( + target.map(OpIgnite::EntitySpell, OpIgnite::BlockSpell), + MediaConstants.DUST_UNIT, + listOf(ParticleSpray.burst(burstPos, 1.0)) + ) } private data class BlockSpell(val pos: BlockPos) : RenderedSpell { diff --git a/Common/src/main/java/at/petrak/hexcasting/common/impl/HexAPIImpl.java b/Common/src/main/java/at/petrak/hexcasting/common/impl/HexAPIImpl.java index 0ff996b907..2720d0547c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/impl/HexAPIImpl.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/impl/HexAPIImpl.java @@ -1,22 +1,27 @@ package at.petrak.hexcasting.common.impl; import at.petrak.hexcasting.api.HexAPI; +import at.petrak.hexcasting.api.addldata.ADIotaHolder; import at.petrak.hexcasting.api.addldata.ADMediaHolder; import at.petrak.hexcasting.api.pigment.FrozenPigment; import at.petrak.hexcasting.api.player.Sentinel; import at.petrak.hexcasting.xplat.IXplatAbstractions; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ArmorItem; import net.minecraft.world.item.ArmorMaterial; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.block.Block; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -145,4 +150,33 @@ public float getKnockbackResistance() { public ArmorMaterial robesMaterial() { return ARMOR_MATERIAL; } + + + private static final ConcurrentMap DIRECT_BLOCKY_IOTA_PROVIDER + = new ConcurrentHashMap<>(); + + @Override + public boolean registerBlockyIotaHolder(ResourceLocation blockID, IBlockyIotaProvider holder){ + if(DIRECT_BLOCKY_IOTA_PROVIDER.get(blockID) != null){ + HexAPI.LOGGER.warn("Attempted to register a blocky iota provider for already registered block: " + blockID); + return false; + } + DIRECT_BLOCKY_IOTA_PROVIDER.put(blockID, holder); + return true; + } + + // properly exposed at IXplatAbstractions#findDataHolder(BlockPos, ServerLevel) for consistency + @Nullable + public static ADIotaHolder getBlockyIotaHolder(ServerLevel level, BlockPos pos){ + Block block = level.getBlockState(pos).getBlock(); + if(block instanceof IBlockyIotaProvider provider){ + return provider.getIotaHolder(level, pos); + } + if(level.getBlockEntity(pos) instanceof ADIotaHolder holder){ + return holder; + } + IBlockyIotaProvider provider = DIRECT_BLOCKY_IOTA_PROVIDER.get(BuiltInRegistries.BLOCK.getKey(block)); + if(provider == null) return null; + return provider.getIotaHolder(level, pos); + } } diff --git a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java index 86dbc24586..4d0473ce16 100644 --- a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java +++ b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java @@ -19,6 +19,7 @@ import at.petrak.hexcasting.api.player.AltioraAbility; import at.petrak.hexcasting.api.player.FlightAbility; import at.petrak.hexcasting.api.player.Sentinel; +import at.petrak.hexcasting.common.impl.HexAPIImpl; import at.petrak.hexcasting.common.msgs.IMessage; import at.petrak.hexcasting.interop.pehkui.PehkuiInterop; import com.mojang.authlib.GameProfile; @@ -124,6 +125,11 @@ public interface IXplatAbstractions { @Nullable ADIotaHolder findDataHolder(Entity entity); + @Nullable + default ADIotaHolder findDataHolder(BlockPos pos, ServerLevel world){ + return HexAPIImpl.getBlockyIotaHolder(world, pos); + } + @Nullable ADHexHolder findHexHolder(ItemStack stack); diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index 7b98d7825d..c6c3396365 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1006,6 +1006,12 @@ "": "Expected %s at %s, but got %s", sapling: "a sapling", replaceable: "somewhere to place a block", + iota: { + "": "a place to store iotas", + read: "a place to read iotas from", + write: "a place to write iotas to", + readonly: "a place that will accept %s", + } }, "circle.bool_directrix": { @@ -1788,12 +1794,12 @@ "2": "There may be other entities I can interact with in this way. For example, a $(l:items/scroll)$(item)Scroll/$ hung on the wall can have its pattern read off of it.$(br2)However, it seems I am unable to save a reference to another player, only me. I suppose an entity reference is similar to the idea of a True Name; perhaps Nature is helping to keep our Names out of the hands of enemies. If I want a friend to have my Name I can make a $(l:items/focus)$(item)Focus/$ for them.", read: "Copy the iota stored in the item in my other hand and add it to the stack.", write: "Remove the top iota from the stack, and save it into the item in my other hand.", - "read/entity": "Like $(l:patterns/readwrite#hexcasting:read)$(action)Scribe's Reflection/$, but the iota is read out of an entity instead of my other hand.", - "write/entity": "Like $(l:patterns/readwrite#hexcasting:read)$(action)Scribe's Gambit/$, but the iota is written to an entity instead of my other hand.$(br2)Interestingly enough, it looks like I cannot write my own Name using this spell. I get a sense that I might be endangered if I could.", + "read/entity": "Like $(l:patterns/readwrite#hexcasting:read)$(action)Scribe's Reflection/$, but the iota is read out of an entity or block instead of my other hand.", + "write/entity": "Like $(l:patterns/readwrite#hexcasting:read)$(action)Scribe's Gambit/$, but the iota is written to an entity or block instead of my other hand.$(br2)Interestingly enough, it looks like I cannot write my own Name using this spell. I get a sense that I might be endangered if I could.", readable: "If the item in my other hand holds an iota I can read, returns True. Otherwise, returns False.", - "readable/entity": "Like $(l:patterns/readwrite#hexcasting:readable)$(action)Auditor's Reflection/$, but the readability of an entity is checked instead of my other hand.", + "readable/entity": "Like $(l:patterns/readwrite#hexcasting:readable)$(action)Auditor's Reflection/$, but the readability of an entity or block is checked instead of my other hand.", writable: "If I could save an iota into the item in my other hand, returns True. Otherwise, returns False.", - "writable/entity": "Like $(l:patterns/readwrite#hexcasting:writable)$(action)Assessor's Reflection/$, but the writability of an entity is checked instead of my other hand.", + "writable/entity": "Like $(l:patterns/readwrite#hexcasting:writable)$(action)Assessor's Reflection/$, but the writability of an entity or block is checked instead of my other hand.", "local.title": "The Ravenmind", local: "Items are not the only places I can store information, however. I am also able to store that information in the _media of the _Hex itself, much like the stack, but separate. Texts refer to this as the $(l:patterns/readwrite#hexcasting:local)$(thing)ravenmind/$. It holds a single iota, much like a $(l:items/focus)$(item)Focus/$, and begins with $(l:casting/influences)$(thing)Null/$ like the same. It is preserved between iterations of $(l:patterns/meta#hexcasting:for_each)$(action)Thoth's Gambit/$, but only lasts as long as the _Hex it's a part of. Once I stop casting, the value will be lost.", "write/local": "Removes the top iota from the stack, and saves it to my $(l:patterns/readwrite#hexcasting:local)$(thing)ravenmind/$, storing it there until I stop casting the _Hex.", diff --git a/Common/src/main/resources/assets/hexcasting/lang/ru_ru.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/ru_ru.flatten.json5 index 1b94caa75f..92bc56c2e2 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/ru_ru.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/ru_ru.flatten.json5 @@ -977,6 +977,12 @@ "": "Ожидал %s при %s, но получил %s", sapling: "саженец", replaceable: "куда-нибудь поместить блок", + iota: { + "": "место для хранения йоты", + read: "место, откуда можно читать йоты", + write: "место, куда можно записывать йоты", + readonly: "место, которое будет принимать %s", + } }, "circle.bool_directrix": { diff --git a/Common/src/main/resources/assets/hexcasting/lang/zh_cn.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/zh_cn.flatten.json5 index bb7ff4f4fd..119035e018 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/zh_cn.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/zh_cn.flatten.json5 @@ -992,6 +992,12 @@ "": "本应在%2$s处接受%1$s,而实际接受了%3$s", sapling: "一个树苗", replaceable: "一个可放置方块的地方", + iota: { + "": "一个可以存储iota的地方", + read: "一个可以读出iota的地方", + write: "一个可以写入iota的地方", + readonly: "一个能够接受%s的地方", + } }, "circle.bool_directrix": { diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json index c661d10d73..afbf78df90 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/patterns/readwrite.json @@ -28,7 +28,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:read/entity", "anchor": "hexcasting:read/entity", - "input": "entity", + "input": "entity | vec", "output": "any", "text": "hexcasting.page.readwrite.read/entity" }, @@ -36,7 +36,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:write/entity", "anchor": "hexcasting:write/entity", - "input": "entity, any", + "input": "entity | vec, any", "output": "", "text": "hexcasting.page.readwrite.write/entity" }, @@ -52,7 +52,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:readable/entity", "anchor": "hexcasting:readable/entity", - "input": "entity", + "input": "entity | vec", "output": "bool", "text": "hexcasting.page.readwrite.readable/entity" }, @@ -68,7 +68,7 @@ "type": "hexcasting:pattern", "op_id": "hexcasting:writable/entity", "anchor": "hexcasting:writable/entity", - "input": "entity", + "input": "entity | vec", "output": "bool", "text": "hexcasting.page.readwrite.writable/entity" },