OpenEndedPipeEffect API (#15)

This commit is contained in:
TropheusJ 2025-02-20 19:08:18 -05:00 committed by GitHub
parent 84cc8086e8
commit 5281a42d54
Failed to generate hash of commit
10 changed files with 240 additions and 182 deletions

View file

@ -0,0 +1,23 @@
package com.simibubi.create;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import com.simibubi.create.api.registry.SimpleRegistry;
import com.simibubi.create.impl.effect.LavaEffectHandler;
import com.simibubi.create.impl.effect.MilkEffectHandler;
import com.simibubi.create.impl.effect.PotionEffectHandler;
import com.simibubi.create.impl.effect.TeaEffectHandler;
import com.simibubi.create.impl.effect.WaterEffectHandler;
import net.minecraft.tags.FluidTags;
import net.minecraftforge.common.Tags;
public class AllOpenPipeEffectHandlers {
public static void registerDefaults() {
OpenPipeEffectHandler.REGISTRY.registerProvider(SimpleRegistry.Provider.forFluidTag(FluidTags.WATER, new WaterEffectHandler()));
OpenPipeEffectHandler.REGISTRY.registerProvider(SimpleRegistry.Provider.forFluidTag(FluidTags.LAVA, new LavaEffectHandler()));
OpenPipeEffectHandler.REGISTRY.registerProvider(SimpleRegistry.Provider.forFluidTag(Tags.Fluids.MILK, new MilkEffectHandler()));
OpenPipeEffectHandler.REGISTRY.register(AllFluids.POTION.getSource(), new PotionEffectHandler());
OpenPipeEffectHandler.REGISTRY.register(AllFluids.TEA.getSource(), new TeaEffectHandler());
}
}

View file

@ -173,6 +173,7 @@ public class Create {
AllMovementBehaviours.registerDefaults();
AllInteractionBehaviours.registerDefaults();
AllContraptionMovementSettings.registerDefaults();
AllOpenPipeEffectHandlers.registerDefaults();
// --
AllAdvancements.register();

View file

@ -0,0 +1,28 @@
package com.simibubi.create.api.effect;
import com.simibubi.create.api.registry.SimpleRegistry;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
/**
* Interface for custom behavior for fluids spilling out of open pipes. Examples:
* <ul>
* <li>Potions: applying potion effects</li>
* <li>Milk: clearing effects</li>
* <li>Water: extinguishing fire</li>
* </ul>
*/
@FunctionalInterface
public interface OpenPipeEffectHandler {
SimpleRegistry<Fluid, OpenPipeEffectHandler> REGISTRY = SimpleRegistry.create();
/**
* @param area the area to apply effects in
* @param fluid the fluid in the pipe. Do not modify, it will do nothing
*/
void apply(Level level, AABB area, FluidStack fluid);
}

View file

@ -17,6 +17,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.material.Fluid;
/**
* A simple registry mapping between objects with identity semantics.
@ -121,6 +122,14 @@ public interface SimpleRegistry<K, V> {
static <V> Provider<EntityType<?>, V> forEntityTag(TagKey<EntityType<?>> tag, V value) {
return new TagProviderImpl<>(tag, EntityType::builtInRegistryHolder, value);
}
/**
* Shortcut for {@link #forTag} when the registry's type is Fluid.
*/
@SuppressWarnings("deprecation")
static <V> Provider<Fluid, V> forFluidTag(TagKey<Fluid> tag, V value) {
return new TagProviderImpl<>(tag, Fluid::builtInRegistryHolder, value);
}
}
/**

View file

@ -2,14 +2,11 @@ package com.simibubi.create.content.fluids;
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import com.simibubi.create.AllFluids;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import com.simibubi.create.content.fluids.pipes.VanillaFluidTargets;
import com.simibubi.create.content.fluids.potion.PotionFluidHandler;
import com.simibubi.create.foundation.advancement.AdvancementBehaviour;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.fluid.FluidHelper;
@ -22,19 +19,8 @@ import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.AbstractCandleBlock;
import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
@ -42,7 +28,7 @@ import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.common.Tags;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
@ -50,16 +36,6 @@ import net.minecraftforge.fluids.capability.templates.FluidTank;
public class OpenEndedPipe extends FlowSource {
private static final List<IEffectHandler> EFFECT_HANDLERS = new ArrayList<>();
static {
registerEffectHandler(new PotionEffectHandler());
registerEffectHandler(new MilkEffectHandler());
registerEffectHandler(new WaterEffectHandler());
registerEffectHandler(new LavaEffectHandler());
registerEffectHandler(new TeaEffectHandler());
}
private Level world;
private BlockPos pos;
private AABB aoe;
@ -68,9 +44,6 @@ public class OpenEndedPipe extends FlowSource {
private BlockPos outputPos;
private boolean wasPulling;
private FluidStack cachedFluid;
private List<MobEffectInstance> cachedEffects;
public OpenEndedPipe(BlockFace face) {
super(face);
fluidHandler = new OpenEndFluidHandler();
@ -81,10 +54,6 @@ public class OpenEndedPipe extends FlowSource {
aoe = aoe.expandTowards(0, -1, 0);
}
public static void registerEffectHandler(IEffectHandler handler) {
EFFECT_HANDLERS.add(handler);
}
public Level getWorld() {
return world;
}
@ -245,23 +214,6 @@ public class OpenEndedPipe extends FlowSource {
return true;
}
private boolean canApplyEffects(FluidStack fluid) {
for (IEffectHandler handler : EFFECT_HANDLERS) {
if (handler.canApplyEffects(this, fluid)) {
return true;
}
}
return false;
}
private void applyEffects(FluidStack fluid) {
for (IEffectHandler handler : EFFECT_HANDLERS) {
if (handler.canApplyEffects(this, fluid)) {
handler.applyEffects(this, fluid);
}
}
}
private class OpenEndFluidHandler extends FluidTank {
public OpenEndFluidHandler() {
@ -287,14 +239,22 @@ public class OpenEndedPipe extends FlowSource {
setFluid(FluidStack.EMPTY);
if (wasPulling)
wasPulling = false;
if (canApplyEffects(resource) && !hasBlockState)
OpenPipeEffectHandler effectHandler = OpenPipeEffectHandler.REGISTRY.get(resource.getFluid());
if (effectHandler != null && !hasBlockState)
resource = FluidHelper.copyStackWithAmount(resource, 1);
int fill = super.fill(resource, action);
if (action.simulate())
return fill;
if (!resource.isEmpty())
applyEffects(resource);
if (effectHandler != null && !resource.isEmpty()) {
// resource should be copied before giving it to the handler.
// if hasBlockState is false, it was already copied above.
FluidStack exposed = hasBlockState ? resource.copy() : resource;
effectHandler.apply(world, aoe, exposed);
}
if (getFluidAmount() == 1000 || !hasBlockState)
if (provideFluidToSpace(containedFluidStack, false))
setFluid(FluidStack.EMPTY);
@ -352,133 +312,4 @@ public class OpenEndedPipe extends FlowSource {
}
}
public interface IEffectHandler {
boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid);
void applyEffects(OpenEndedPipe pipe, FluidStack fluid);
}
public static class PotionEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return fluid.getFluid()
.isSame(AllFluids.POTION.get());
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
if (pipe.cachedFluid == null || pipe.cachedEffects == null || !fluid.isFluidEqual(pipe.cachedFluid)) {
FluidStack copy = fluid.copy();
copy.setAmount(250);
ItemStack bottle = PotionFluidHandler.fillBottle(new ItemStack(Items.GLASS_BOTTLE), fluid);
pipe.cachedEffects = PotionUtils.getMobEffects(bottle);
}
if (pipe.cachedEffects.isEmpty())
return;
List<LivingEntity> entities = pipe.getWorld()
.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
for (MobEffectInstance effectInstance : pipe.cachedEffects) {
MobEffect effect = effectInstance.getEffect();
if (effect.isInstantenous()) {
effect.applyInstantenousEffect(null, null, entity, effectInstance.getAmplifier(), 0.5D);
} else {
entity.addEffect(new MobEffectInstance(effectInstance));
}
}
}
}
}
public static class MilkEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return FluidHelper.isTag(fluid, Tags.Fluids.MILK);
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities =
world.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
ItemStack curativeItem = new ItemStack(Items.MILK_BUCKET);
for (LivingEntity entity : entities)
entity.curePotionEffects(curativeItem);
}
}
public static class WaterEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return FluidHelper.isTag(fluid, FluidTags.WATER);
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<Entity> entities = world.getEntities((Entity) null, pipe.getAOE(), Entity::isOnFire);
for (Entity entity : entities)
entity.clearFire();
BlockPos.betweenClosedStream(pipe.getAOE())
.forEach(pos -> dowseFire(world, pos));
}
// Adapted from ThrownPotion
private static void dowseFire(Level level, BlockPos pos) {
BlockState state = level.getBlockState(pos);
if (state.is(BlockTags.FIRE)) {
level.removeBlock(pos, false);
} else if (AbstractCandleBlock.isLit(state)) {
AbstractCandleBlock.extinguish(null, state, level, pos);
} else if (CampfireBlock.isLitCampfire(state)) {
level.levelEvent(null, 1009, pos, 0);
CampfireBlock.dowse(null, level, pos, state);
level.setBlockAndUpdate(pos, state.setValue(CampfireBlock.LIT, false));
}
}
}
public static class LavaEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return FluidHelper.isTag(fluid, FluidTags.LAVA);
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<Entity> entities = world.getEntities((Entity) null, pipe.getAOE(), entity -> !entity.fireImmune());
for (Entity entity : entities)
entity.setSecondsOnFire(3);
}
}
public static class TeaEffectHandler implements IEffectHandler {
@Override
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
return fluid.getFluid().isSame(AllFluids.TEA.get());
}
@Override
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
Level world = pipe.getWorld();
if (world.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities = world
.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
entity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 21, 0, false, false, false));
}
}
}
}

View file

@ -0,0 +1,24 @@
package com.simibubi.create.impl.effect;
import java.util.List;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
public class LavaEffectHandler implements OpenPipeEffectHandler {
@Override
public void apply(Level level, AABB area, FluidStack fluid) {
if (level.getGameTime() % 5 != 0)
return;
List<Entity> entities = level.getEntities((Entity) null, area, entity -> !entity.fireImmune());
for (Entity entity : entities) {
entity.setSecondsOnFire(3);
}
}
}

View file

@ -0,0 +1,27 @@
package com.simibubi.create.impl.effect;
import java.util.List;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
public class MilkEffectHandler implements OpenPipeEffectHandler {
@Override
public void apply(Level level, AABB area, FluidStack fluid) {
if (level.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities = level.getEntitiesOfClass(LivingEntity.class, area, LivingEntity::isAffectedByPotions);
ItemStack curativeItem = new ItemStack(Items.MILK_BUCKET);
for (LivingEntity entity : entities) {
entity.curePotionEffects(curativeItem);
}
}
}

View file

@ -0,0 +1,45 @@
package com.simibubi.create.impl.effect;
import java.util.List;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import com.simibubi.create.content.fluids.potion.PotionFluidHandler;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.alchemy.PotionUtils;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
public class PotionEffectHandler implements OpenPipeEffectHandler {
@Override
public void apply(Level level, AABB area, FluidStack fluid) {
List<MobEffectInstance> effects = getEffects(fluid);
if (effects.isEmpty())
return;
List<LivingEntity> entities = level.getEntitiesOfClass(LivingEntity.class, area, LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
for (MobEffectInstance effectInstance : effects) {
MobEffect effect = effectInstance.getEffect();
if (effect.isInstantenous()) {
effect.applyInstantenousEffect(null, null, entity, effectInstance.getAmplifier(), 0.5D);
} else {
entity.addEffect(new MobEffectInstance(effectInstance));
}
}
}
}
private static List<MobEffectInstance> getEffects(FluidStack fluid) {
FluidStack copy = fluid.copy();
copy.setAmount(250);
ItemStack bottle = PotionFluidHandler.fillBottle(new ItemStack(Items.GLASS_BOTTLE), copy);
return PotionUtils.getMobEffects(bottle);
}
}

View file

@ -0,0 +1,26 @@
package com.simibubi.create.impl.effect;
import java.util.List;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
public class TeaEffectHandler implements OpenPipeEffectHandler {
@Override
public void apply(Level level, AABB area, FluidStack fluid) {
if (level.getGameTime() % 5 != 0)
return;
List<LivingEntity> entities = level.getEntitiesOfClass(LivingEntity.class, area, LivingEntity::isAffectedByPotions);
for (LivingEntity entity : entities) {
entity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 21, 0, false, false, false));
}
}
}

View file

@ -0,0 +1,44 @@
package com.simibubi.create.impl.effect;
import java.util.List;
import com.simibubi.create.api.effect.OpenPipeEffectHandler;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.AbstractCandleBlock;
import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.fluids.FluidStack;
public class WaterEffectHandler implements OpenPipeEffectHandler {
@Override
public void apply(Level level, AABB area, FluidStack fluid) {
if (level.getGameTime() % 5 != 0)
return;
List<Entity> entities = level.getEntities((Entity) null, area, Entity::isOnFire);
for (Entity entity : entities)
entity.clearFire();
BlockPos.betweenClosedStream(area).forEach(pos -> dowseFire(level, pos));
}
// Adapted from ThrownPotion
private static void dowseFire(Level level, BlockPos pos) {
BlockState state = level.getBlockState(pos);
if (state.is(BlockTags.FIRE)) {
level.removeBlock(pos, false);
} else if (AbstractCandleBlock.isLit(state)) {
AbstractCandleBlock.extinguish(null, state, level, pos);
} else if (CampfireBlock.isLitCampfire(state)) {
level.levelEvent(null, 1009, pos, 0);
CampfireBlock.dowse(null, level, pos, state);
level.setBlockAndUpdate(pos, state.setValue(CampfireBlock.LIT, false));
}
}
}