From 240f021194311f5e2927ea03eaa559914eae7bc8 Mon Sep 17 00:00:00 2001 From: "yrsegal@gmail.com" Date: Thu, 25 Aug 2022 21:09:51 -0400 Subject: [PATCH] Fluid creation hexes can insert partial, destroy fluid can drain tanks Closes #216 --- .../common/casting/RegisterPatterns.java | 16 +++- .../OpCreateLava.kt => OpCreateFluid.kt} | 43 ++++------- .../casting/operators/spells/OpCreateWater.kt | 74 ------------------- .../{OpDestroyWater.kt => OpDestroyFluid.kt} | 23 ++++-- .../hexcasting/xplat/IXplatAbstractions.java | 3 +- .../assets/hexcasting/lang/en_us.json | 8 +- .../fabric/xplat/FabricXplatImpl.java | 40 ++++++++-- .../forge/xplat/ForgeXplatImpl.java | 26 +++++-- 8 files changed, 107 insertions(+), 126 deletions(-) rename Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/{great/OpCreateLava.kt => OpCreateFluid.kt} (50%) delete mode 100644 Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateWater.kt rename Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/{OpDestroyWater.kt => OpDestroyFluid.kt} (87%) diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/RegisterPatterns.java b/Common/src/main/java/at/petrak/hexcasting/common/casting/RegisterPatterns.java index 23d08e1c..169889f9 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/RegisterPatterns.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/RegisterPatterns.java @@ -38,6 +38,10 @@ import at.petrak.hexcasting.common.casting.operators.stack.*; import at.petrak.hexcasting.common.lib.HexItems; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LayeredCauldronBlock; +import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.Vec3; import static at.petrak.hexcasting.api.HexAPI.modLoc; @@ -201,10 +205,13 @@ public class RegisterPatterns { modLoc("colorize"), OpColorize.INSTANCE); PatternRegistry.mapPattern(HexPattern.fromAngles("aqawqadaq", HexDir.SOUTH_EAST), modLoc("create_water"), - OpCreateWater.INSTANCE); + new OpCreateFluid(false, ManaConstants.DUST_UNIT, + Items.WATER_BUCKET, + Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, LayeredCauldronBlock.MAX_FILL_LEVEL), + Fluids.WATER)); PatternRegistry.mapPattern(HexPattern.fromAngles("dedwedade", HexDir.SOUTH_WEST), modLoc("destroy_water"), - OpDestroyWater.INSTANCE); + OpDestroyFluid.INSTANCE); PatternRegistry.mapPattern(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), modLoc("ignite"), OpIgnite.INSTANCE); PatternRegistry.mapPattern(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), modLoc("extinguish"), @@ -292,7 +299,10 @@ public class RegisterPatterns { PatternRegistry.mapPattern(HexPattern.fromAngles("eawwaeawawaa", HexDir.NORTH_WEST), modLoc("flight"), OpFlight.INSTANCE, true); PatternRegistry.mapPattern(HexPattern.fromAngles("eaqawqadaqd", HexDir.EAST), - modLoc("create_lava"), OpCreateLava.INSTANCE, true); + modLoc("create_lava"), new OpCreateFluid(true, ManaConstants.CRYSTAL_UNIT, + Items.LAVA_BUCKET, + Blocks.LAVA_CAULDRON.defaultBlockState(), + Fluids.LAVA), true); PatternRegistry.mapPattern( HexPattern.fromAngles("wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq", HexDir.EAST), modLoc("teleport"), OpTeleport.INSTANCE, true); diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpCreateLava.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt similarity index 50% rename from Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpCreateLava.kt rename to Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt index 1a819def..c3c1421c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpCreateLava.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateFluid.kt @@ -1,22 +1,19 @@ -package at.petrak.hexcasting.common.casting.operators.spells.great +package at.petrak.hexcasting.common.casting.operators.spells -import at.petrak.hexcasting.api.HexAPI -import at.petrak.hexcasting.api.misc.ManaConstants import at.petrak.hexcasting.api.spell.* import at.petrak.hexcasting.api.spell.casting.CastingContext import at.petrak.hexcasting.xplat.IXplatAbstractions import net.minecraft.core.BlockPos import net.minecraft.world.item.BucketItem +import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.Items -import net.minecraft.world.level.block.AbstractCauldronBlock import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.material.Fluids +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.Fluid import net.minecraft.world.phys.Vec3 -object OpCreateLava : SpellOperator { +class OpCreateFluid(override val isGreat: Boolean, val cost: Int, val bucket: Item, val cauldron: BlockState, val fluid: Fluid) : SpellOperator { override val argc = 1 - override val isGreat = true override fun execute( args: List>, ctx: CastingContext @@ -25,20 +22,20 @@ object OpCreateLava : SpellOperator { ctx.assertVecInRange(target) return Triple( - Spell(target), - ManaConstants.CRYSTAL_UNIT, - listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0)), + Spell(target, bucket, cauldron, fluid), + cost, + listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0)) ) } - private data class Spell(val target: Vec3) : RenderedSpell { + private data class Spell(val target: Vec3, val bucket: Item, val cauldron: BlockState, val fluid: Fluid) : RenderedSpell { override fun cast(ctx: CastingContext) { val pos = BlockPos(target) if (!ctx.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isPlacingAllowed( ctx.world, pos, - ItemStack(Items.LAVA_BUCKET), + ItemStack(bucket), ctx.caster ) ) @@ -46,24 +43,16 @@ object OpCreateLava : SpellOperator { val state = ctx.world.getBlockState(pos) - if (state.block is AbstractCauldronBlock) - ctx.world.setBlock(pos, Blocks.LAVA_CAULDRON.defaultBlockState(), 3) + if (state.block == Blocks.CAULDRON) + ctx.world.setBlock(pos, cauldron, 3) else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid( ctx.world, ctx.castingHand, pos, - ItemStack(Items.LAVA_BUCKET), - Fluids.LAVA - ) - ) { - // Just steal bucket code lmao - val charlie = Items.LAVA_BUCKET - if (charlie is BucketItem) { - // make the player null so we don't give them a usage statistic for example - charlie.emptyContents(null, ctx.world, pos, null) - } else { - HexAPI.LOGGER.warn("Items.LAVA_BUCKET wasn't a BucketItem?") - } + fluid + ) && bucket is BucketItem) { + // make the player null so we don't give them a usage statistic for example + bucket.emptyContents(null, ctx.world, pos, null) } } } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateWater.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateWater.kt deleted file mode 100644 index 90d03499..00000000 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateWater.kt +++ /dev/null @@ -1,74 +0,0 @@ -package at.petrak.hexcasting.common.casting.operators.spells - -import at.petrak.hexcasting.api.HexAPI -import at.petrak.hexcasting.api.misc.ManaConstants -import at.petrak.hexcasting.api.spell.* -import at.petrak.hexcasting.api.spell.casting.CastingContext -import at.petrak.hexcasting.xplat.IXplatAbstractions -import net.minecraft.core.BlockPos -import net.minecraft.world.item.BucketItem -import net.minecraft.world.item.ItemStack -import net.minecraft.world.item.Items -import net.minecraft.world.level.block.AbstractCauldronBlock -import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.block.LayeredCauldronBlock -import net.minecraft.world.level.material.Fluids -import net.minecraft.world.phys.Vec3 - -object OpCreateWater : SpellOperator { - override val argc = 1 - override fun execute( - args: List>, - ctx: CastingContext - ): Triple> { - val target = args.getChecked(0, argc) - ctx.assertVecInRange(target) - - return Triple( - Spell(target), - ManaConstants.DUST_UNIT, - listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0)) - ) - } - - private data class Spell(val target: Vec3) : RenderedSpell { - override fun cast(ctx: CastingContext) { - val pos = BlockPos(target) - - if (!ctx.canEditBlockAt(pos) - || !IXplatAbstractions.INSTANCE.isPlacingAllowed( - ctx.world, - pos, - ItemStack(Items.WATER_BUCKET), - ctx.caster - ) - ) - return - val state = ctx.world.getBlockState(pos) - - if (state.block is AbstractCauldronBlock) - ctx.world.setBlock( - pos, - Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), - 3 - ) - else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid( - ctx.world, - ctx.castingHand, - pos, - ItemStack(Items.WATER_BUCKET), - Fluids.WATER - ) - ) { - // Just steal bucket code lmao - val charlie = Items.WATER_BUCKET - if (charlie is BucketItem) { - // make the player null so we don't give them a usage statistic for example - charlie.emptyContents(null, ctx.world, pos, null) - } else { - HexAPI.LOGGER.warn("Items.WATER_BUCKET wasn't a BucketItem?") - } - } - } - } -} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyWater.kt b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyFluid.kt similarity index 87% rename from Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyWater.kt rename to Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyFluid.kt index bb8f620f..d9baf1fb 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyWater.kt +++ b/Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyFluid.kt @@ -9,16 +9,13 @@ import net.minecraft.core.Direction import net.minecraft.core.particles.ParticleTypes import net.minecraft.sounds.SoundEvents import net.minecraft.sounds.SoundSource -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.Blocks -import net.minecraft.world.level.block.BucketPickup -import net.minecraft.world.level.block.LiquidBlock +import net.minecraft.world.level.block.* import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.material.Fluids import net.minecraft.world.level.material.Material import net.minecraft.world.phys.Vec3 -object OpDestroyWater : SpellOperator { +object OpDestroyFluid : SpellOperator { override val argc = 1 override fun execute( args: List>, @@ -39,10 +36,24 @@ object OpDestroyWater : SpellOperator { private data class Spell(val target: Vec3) : RenderedSpell { override fun cast(ctx: CastingContext) { + val basePos = BlockPos(target) + + // Try draining from fluid handlers first, and if so, don't do the normal behavior + if (ctx.canEditBlockAt(basePos)) { + if (IXplatAbstractions.INSTANCE.drainAllFluid(ctx.world, basePos)) { + return + } else { + val state = ctx.world.getBlockState(basePos) + if (state.block is AbstractCauldronBlock && state.block != Blocks.CAULDRON) { + ctx.world.setBlock(basePos, Blocks.CAULDRON.defaultBlockState(), 3) + return + } + } + } + // SpongeBlock.java val todo = ArrayDeque() val seen = HashSet() - val basePos = BlockPos(target) // a little extra range on the initial cast to make it feel more intuitive for (xShift in -2..2) for (yShift in -2..2) for (zShift in -2..2) { 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 8654eb1d..2b6b3659 100644 --- a/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java +++ b/Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java @@ -120,8 +120,9 @@ public interface IXplatAbstractions { BlockEntityType createBlockEntityType(BiFunction func, Block... blocks); - boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, ItemStack stack, Fluid fluid); + boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, Fluid fluid); + boolean drainAllFluid(Level level, BlockPos pos); // misc diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.json b/Common/src/main/resources/assets/hexcasting/lang/en_us.json index 39f518e1..9de13af6 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.json +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.json @@ -310,7 +310,7 @@ "hexcasting.spell.hexcasting:recharge": "Recharge Item", "hexcasting.spell.hexcasting:erase": "Erase Item", "hexcasting.spell.hexcasting:create_water": "Create Water", - "hexcasting.spell.hexcasting:destroy_water": "Destroy Water", + "hexcasting.spell.hexcasting:destroy_water": "Destroy Liquid", "hexcasting.spell.hexcasting:ignite": "Ignite Block", "hexcasting.spell.hexcasting:extinguish": "Extinguish Area", "hexcasting.spell.hexcasting:conjure_block": "Conjure Block", @@ -920,8 +920,8 @@ "hexcasting.entry.blockworks": "Blockworks", "hexcasting.page.blockworks.place_block": "Remove a location from the stack, then pick a block item and place it at the given location.$(br)Costs a negligible amount of _media.", "hexcasting.page.blockworks.break_block": "Remove a location from the stack, then break the block at the given location. This spell can break nearly anything a Diamond Pickaxe can break.$(br)Costs a bit more than one $(l:items/amethyst)$(item)Amethyst Dust/$.", - "hexcasting.page.blockworks.create_water": "Summon a block of water (or insert a bucket's worth) into a block at the given position. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", - "hexcasting.page.blockworks.destroy_water": "Destroy a great deal of liquid (not just water) around the given position. Costs about two $(l:items/amethyst)$(item)Charged Amethyst/$.", + "hexcasting.page.blockworks.create_water": "Summon a block of water (or insert up to a bucket's worth) into a block at the given position. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", + "hexcasting.page.blockworks.destroy_water": "Drains either a liquid container at, or a body of liquid around, the given position. Costs about two $(l:items/amethyst)$(item)Charged Amethyst/$.", "hexcasting.page.blockworks.conjure_block": "Conjure an ethereal, but solid, block that sparkles with my pigment at the given position. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", "hexcasting.page.blockworks.conjure_light": "Conjure a magical light that softly glows with my pigment at the given position. Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", "hexcasting.page.blockworks.bonemeal": "Encourage a plant or sapling at the target position to grow, as if $(item)Bonemeal/$ was applied. Costs a bit more than one $(l:items/amethyst)$(item)Amethyst Dust/$.", @@ -958,7 +958,7 @@ "hexcasting.page.colorize": "I must be holding a $(l:items/pigments)$(item)Pigment/$ in my other hand to cast this spell. When I do, it will consume the dye and permanently change my mind's coloration (at least, until I cast the spell again). Costs about one $(l:items/amethyst)$(item)Amethyst Dust/$.", - "hexcasting.page.create_lava.1": "Summon a block of lava or insert a bucket's worth into a block at the given position. Costs about one $(l:items/amethyst)$(item)Charged Amethyst/$.", + "hexcasting.page.create_lava.1": "Summon a block of lava (or insert up to a bucket's worth) into a block at the given position. Costs about one $(l:items/amethyst)$(item)Charged Amethyst/$.", "hexcasting.page.create_lava.2": "It may be advisable to keep my knowledge of this spell secret. A certain faction of botanists get... touchy about it, or so I've heard.$(br2)Well, no one said tracing the deep secrets of the universe was going to be an easy time.", "hexcasting.entry.weather_manip": "Weather Manipulation", diff --git a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java index c57f7100..7e289421 100644 --- a/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java +++ b/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java @@ -31,12 +31,11 @@ import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.fabricmc.fabric.api.networking.v1.PlayerLookup; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; -import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.storage.Storage; -import net.fabricmc.fabric.api.transfer.v1.storage.StorageUtil; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.ModContainer; import net.minecraft.advancements.critereon.ItemPredicate; @@ -252,10 +251,41 @@ public class FabricXplatImpl implements IXplatAbstractions { @Override @SuppressWarnings("UnstableApiUsage") - public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, ItemStack stack, Fluid fluid) { + public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, Fluid fluid) { Storage target = FluidStorage.SIDED.find(level, pos, Direction.UP); - Storage emptyFrom = FluidStorage.ITEM.find(stack, ContainerItemContext.withInitial(stack)); - return StorageUtil.move(emptyFrom, target, (f) -> true, FluidConstants.BUCKET, null) > 0; + if (target == null) + return false; + try (Transaction transaction = Transaction.openOuter()) { + long insertedAmount = target.insert(FluidVariant.of(fluid), FluidConstants.BUCKET, transaction); + if (insertedAmount > 0) { + transaction.commit(); + return true; + } + } + return false; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public boolean drainAllFluid(Level level, BlockPos pos) { + Storage target = FluidStorage.SIDED.find(level, pos, Direction.UP); + if (target == null) + return false; + try (Transaction transaction = Transaction.openOuter()) { + boolean any = false; + for (var view : target.iterable(transaction)) { + long extracted = view.extract(view.getResource(), view.getAmount(), transaction); + if (extracted > 0) { + any = true; + } + } + + if (any) { + transaction.commit(); + return true; + } + } + return false; } @Override diff --git a/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java b/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java index 8e7e9a26..916c1626 100644 --- a/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java +++ b/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java @@ -82,6 +82,8 @@ import java.util.List; import java.util.Optional; import java.util.function.BiFunction; +import static net.minecraftforge.fluids.capability.IFluidHandler.FluidAction.EXECUTE; + public class ForgeXplatImpl implements IXplatAbstractions { @Override public Platform platform() { @@ -310,14 +312,26 @@ public class ForgeXplatImpl implements IXplatAbstractions { } @Override - public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, ItemStack stack, Fluid fluid) { + public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, Fluid fluid) { Optional handler = FluidUtil.getFluidHandler(level, pos, Direction.UP).resolve(); - if (handler.isPresent() && - FluidUtil.tryEmptyContainer(stack, handler.get(), FluidAttributes.BUCKET_VOLUME, null, true).isSuccess()) { - return true; + return handler.isPresent() && + handler.get().fill(new FluidStack(fluid, FluidAttributes.BUCKET_VOLUME), EXECUTE) > 0; + } + + @Override + public boolean drainAllFluid(Level level, BlockPos pos) { + Optional handler = FluidUtil.getFluidHandler(level, pos, Direction.UP).resolve(); + if (handler.isPresent()) { + boolean any = false; + IFluidHandler pool = handler.get(); + for (int i = 0; i < pool.getTanks(); i++) { + if (!pool.drain(pool.getFluidInTank(i), EXECUTE).isEmpty()) { + any = true; + } + } + return any; } - return FluidUtil.tryPlaceFluid(null, level, hand, pos, stack, new FluidStack( - fluid, FluidAttributes.BUCKET_VOLUME)).isSuccess(); + return false; } @Override