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"
},