Fluid creation hexes can insert partial, destroy fluid can drain tanks

Closes #216
This commit is contained in:
yrsegal@gmail.com 2022-08-25 21:09:51 -04:00
parent 6418e9b95e
commit 240f021194
8 changed files with 107 additions and 126 deletions

View file

@ -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);

View file

@ -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<SpellDatum<*>>,
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)
}
}
}

View file

@ -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<SpellDatum<*>>,
ctx: CastingContext
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
val target = args.getChecked<Vec3>(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?")
}
}
}
}
}

View file

@ -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<SpellDatum<*>>,
@ -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<BlockPos>()
val seen = HashSet<BlockPos>()
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) {

View file

@ -120,8 +120,9 @@ public interface IXplatAbstractions {
<T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> 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

View file

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

View file

@ -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<FluidVariant> target = FluidStorage.SIDED.find(level, pos, Direction.UP);
Storage<FluidVariant> 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<FluidVariant> 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

View file

@ -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<IFluidHandler> 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<IFluidHandler> 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