diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6e96ec..276e76b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Moved certain lang keys for type iotas from Hexal into MoreIotas, since MoreIotas is what actually implements type iotas. - Phase Block now ensures that the caster has permission to break the target block. - Trade now mishaps if the list contains more than 2 motes, and makes it clear that the list should match a single trade offer. +- The mishap when providing something non-placeable to Place Block II now specifies all valid input types. ### Fixed @@ -46,6 +47,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fixed an error when passing null or an empty mote to Use Item On. - Fixed a variety of broken lang keys in the config menu. - Fixed Trade allowing trades even when not enough input items were present. +- Fixed Place Block II failing silently if the caster doesn't have any items matching the specified type. ## `0.3.1` - 2025-10-30 diff --git a/Common/src/main/java/ram/talia/hexal/api/OperatorUtils.kt b/Common/src/main/java/ram/talia/hexal/api/OperatorUtils.kt index b5aa10a8..a468a59a 100644 --- a/Common/src/main/java/ram/talia/hexal/api/OperatorUtils.kt +++ b/Common/src/main/java/ram/talia/hexal/api/OperatorUtils.kt @@ -246,7 +246,7 @@ fun List.getBlockTypeOrBlockItemStackOrBlockMote(idx: Int, argc: Int = 0): return x.itemStack?.let { if (it.item is BlockItem) Anyone.second(it) else null } if (x is MoteIota) return x.selfOrNull()?.let { if (it.item is BlockItem) Anyone.third(it) else null } - throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "type.block") + throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "placeable") } fun List.getGate(idx: Int, argc: Int = 0): GateIota { diff --git a/Common/src/main/java/ram/talia/hexal/api/casting/mishaps/MishapItemNotFound.kt b/Common/src/main/java/ram/talia/hexal/api/casting/mishaps/MishapItemNotFound.kt new file mode 100644 index 00000000..81fc0ce5 --- /dev/null +++ b/Common/src/main/java/ram/talia/hexal/api/casting/mishaps/MishapItemNotFound.kt @@ -0,0 +1,18 @@ +package ram.talia.hexal.api.casting.mishaps + +import at.petrak.hexcasting.api.casting.eval.CastingEnvironment +import at.petrak.hexcasting.api.casting.iota.Iota +import at.petrak.hexcasting.api.casting.mishaps.Mishap +import at.petrak.hexcasting.api.pigment.FrozenPigment +import net.minecraft.world.item.DyeColor + +class MishapItemNotFound(val wanted: Iota) : Mishap() { + override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenPigment = + dyeColor(DyeColor.BROWN) + + override fun execute(env: CastingEnvironment, errorCtx: Context, stack: MutableList) { + env.mishapEnvironment.dropHeldItems() + } + + override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = error("item_not_found", wanted.display()) +} \ No newline at end of file diff --git a/Common/src/main/java/ram/talia/hexal/common/casting/actions/spells/OpPlaceType.kt b/Common/src/main/java/ram/talia/hexal/common/casting/actions/spells/OpPlaceType.kt index 675aada2..bb5ed2ae 100644 --- a/Common/src/main/java/ram/talia/hexal/common/casting/actions/spells/OpPlaceType.kt +++ b/Common/src/main/java/ram/talia/hexal/common/casting/actions/spells/OpPlaceType.kt @@ -7,6 +7,7 @@ import at.petrak.hexcasting.api.casting.eval.CastingEnvironment import at.petrak.hexcasting.api.casting.getBlockPos import at.petrak.hexcasting.api.casting.iota.Iota import at.petrak.hexcasting.api.casting.mishaps.MishapBadBlock +import at.petrak.hexcasting.api.casting.mishaps.MishapBadOffhandItem import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota import at.petrak.hexcasting.xplat.IXplatAbstractions import net.minecraft.core.BlockPos @@ -26,6 +27,7 @@ import net.minecraft.world.phys.Vec3 import ram.talia.hexal.api.config.HexalConfig import ram.talia.hexal.api.getBlockTypeOrBlockItemStackOrBlockMote import ram.talia.hexal.api.casting.iota.MoteIota +import ram.talia.hexal.api.casting.mishaps.MishapItemNotFound import ram.talia.hexal.api.util.Anyone object OpPlaceType : SpellAction { @@ -33,7 +35,7 @@ object OpPlaceType : SpellAction { override fun execute(args: List, env: CastingEnvironment): SpellAction.Result { val block = args.getBlockTypeOrBlockItemStackOrBlockMote(0, argc) ?: - throw MishapInvalidIota.ofType(args[0], 1, "type.block.able") + throw MishapInvalidIota.of(args[0], 1, "placeable") val pos = args.getBlockPos(1, argc) env.assertVecInRange(pos.center) @@ -49,14 +51,27 @@ object OpPlaceType : SpellAction { if (!worldState.canBeReplaced(placeContext)) throw MishapBadBlock.of(pos, "replaceable") + // TODO: queryForMatchingStack() only searches the hotbar. This should be changed either to use a custom + // function once hex 0.11.4 makes getUsableStacks() public, or to specify the full inventory once + // https://github.com/FallingColors/HexMod/issues/1052 gets implemented. + val placeeStack = block.flatMap( + { block -> env.queryForMatchingStack { it.item is BlockItem && (it.item as BlockItem).block == block }?.copy() }, + { itemStack -> env.queryForMatchingStack { ItemStack.isSameItemSameTags(it, itemStack) }?.copy() }, + { itemIota -> if (itemIota.item is BlockItem) itemIota.record?.toStack()?.takeUnless { it.isEmpty } else null } + ) + if (placeeStack == null) { + if (block.isC) throw MishapInvalidIota.of(args[0], 1, "placeable") + else throw MishapItemNotFound(args[0]) + } + return SpellAction.Result( - Spell(pos, block), + Spell(pos, placeeStack, block), HexalConfig.server.placeTypeCost, listOf(ParticleSpray.cloud(Vec3.atCenterOf(pos), 1.0)) ) } - private data class Spell(val pos: BlockPos, val blockOrMoteIota: Anyone) : RenderedSpell { + private data class Spell(val pos: BlockPos, val placeeStack: ItemStack, val blockOrMoteIota: Anyone) : RenderedSpell { override fun cast(env: CastingEnvironment) { val caster = env.caster @@ -65,11 +80,6 @@ object OpPlaceType : SpellAction { ) val bstate = env.world.getBlockState(pos) - val placeeStack = blockOrMoteIota.flatMap( - { block -> env.queryForMatchingStack { it.item is BlockItem && (it.item as BlockItem).block == block }?.copy() }, - { itemStack -> env.queryForMatchingStack { ItemStack.isSameItemSameTags(it, itemStack) }?.copy() }, - { itemIota -> if (itemIota.item is BlockItem) itemIota.record?.toStack()?.takeUnless { it.isEmpty } else null } - ) ?: return if (!IXplatAbstractions.INSTANCE.isPlacingAllowed(env.world, pos, placeeStack, env.caster)) return diff --git a/Common/src/main/resources/assets/hexal/lang/en_us.json b/Common/src/main/resources/assets/hexal/lang/en_us.json index 5e34c38e..3d5f5ec4 100644 --- a/Common/src/main/resources/assets/hexal/lang/en_us.json +++ b/Common/src/main/resources/assets/hexal/lang/en_us.json @@ -13,6 +13,7 @@ "hexcasting.mishap.others_wisp": "That wisp is owned by %s, not you.", "hexcasting.mishap.no_links": "%s has no links.", "hexcasting.mishap.self_link": "Tried to link %s to itself.", + "hexcasting.mishap.item_not_found": "Couldn't find any item matching %s in the hotbar.", "hexcasting.mishap.no_bound_storage": "No bound Mote Nexus.", "hexcasting.mishap.storage_unloaded": "Bound Mote Nexus is unloaded.", "hexcasting.mishap.excessive_reproduction": "Wisp %s tried to reproduce excessively; only 1 child per tick.", @@ -44,6 +45,7 @@ "hexcasting.mishap.invalid_value.cant_combine_motes": "two motes that can be combined", "hexcasting.mishap.invalid_value.mote_empty": "a non-empty mote", "hexcasting.mishap.invalid_value.mote_not_size_one": "a mote with no NBT data or of size one", + "hexcasting.mishap.invalid_value.placeable": "a mote, item type, or item stack that can be placed", "hexcasting.mishap.invalid_value.villager_trade": "a list of motes matching a single trade offer", "hexcasting.mishap.invalid_value.gate.offset": "an offset of at most %f blocks",