diff --git a/src/main/java/com/Da_Technomancer/crossroads/EventHandlerCommon.java b/src/main/java/com/Da_Technomancer/crossroads/EventHandlerCommon.java index 8aaa94ed0..850e32953 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/EventHandlerCommon.java +++ b/src/main/java/com/Da_Technomancer/crossroads/EventHandlerCommon.java @@ -5,6 +5,7 @@ import com.Da_Technomancer.crossroads.api.CRMaterialLibrary; import com.Da_Technomancer.crossroads.api.CRReflection; import com.Da_Technomancer.crossroads.api.Capabilities; +import com.Da_Technomancer.crossroads.api.MiscUtil; import com.Da_Technomancer.crossroads.api.alchemy.AtmosChargeSavedData; import com.Da_Technomancer.crossroads.api.crafting.CraftingUtil; import com.Da_Technomancer.crossroads.api.technomancy.EnumGoggleLenses; @@ -28,7 +29,9 @@ import com.Da_Technomancer.crossroads.items.CRItems; import com.Da_Technomancer.crossroads.items.technomancy.TechnomancyArmor; import com.Da_Technomancer.crossroads.world.CRWorldGen; +import com.Da_Technomancer.essentials.api.ConfigUtil; import com.Da_Technomancer.essentials.api.ReflectionUtil; + import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.registries.Registries; @@ -73,9 +76,11 @@ import net.minecraftforge.event.entity.EntityAttributeCreationEvent; import net.minecraftforge.event.entity.living.*; import net.minecraftforge.event.entity.player.PlayerEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.event.level.ExplosionEvent; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.event.config.ModConfigEvent; @@ -481,6 +486,17 @@ public void enviroBootsProtect(LivingAttackEvent e){ e.setCanceled(true); } } + @SubscribeEvent + public void allowWrenchWithSneakOffhand(PlayerInteractEvent.RightClickBlock e){ + //Let me explain what the heck this does: + //So default vanilla behavior is that shift-right-clicking with an item in your main hand lets the block react to the item + //BUT if you shift right click with an item in your main hand, but with ANY item in your off-hand, the block doesn't get a chance to react at all + //Which is really annoying, because a lot of CR machines need to be adjusted by shift-right-clicking with a wrench, and that doesn't work if you also use your offhand for stuff + //So this specifically allows shift-right-click wrenching CR blocks to still work when you have something in your offhand + if(ConfigUtil.isWrench(e.getItemStack()) && MiscUtil.getRegistryName(e.getLevel().getBlockState(e.getPos()).getBlock(), Registries.BLOCK).getNamespace().equals(Crossroads.MODID)){ + e.setUseBlock(Result.ALLOW); + } + } @SubscribeEvent @SuppressWarnings("unused") diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/AlchemyUtil.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/AlchemyUtil.java index ef13ba783..e26a6c8f7 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/AlchemyUtil.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/AlchemyUtil.java @@ -16,8 +16,15 @@ import net.minecraftforge.registries.ForgeRegistries; import javax.annotation.Nullable; + +import org.apache.commons.lang3.tuple.Pair; + import java.awt.*; import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Random; +import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; public class AlchemyUtil{ @@ -53,9 +60,9 @@ public static void releaseChemical(Level world, BlockPos pos, ReagentMap reags){ int[] liqCol = new int[4]; int[] gasCol = new int[4]; - ArrayList effectsSol = new ArrayList<>(reags.size()); - ArrayList effectsLiq = new ArrayList<>(reags.size()); - ArrayList effectsGas = new ArrayList<>(reags.size()); + ArrayList effectsSol = new ArrayList<>(reags.keySetSize()); + ArrayList effectsLiq = new ArrayList<>(reags.keySetSize()); + ArrayList effectsGas = new ArrayList<>(reags.keySetSize()); double tempC = reags.getTempC(); for(IReagent reag : reags.keySetReag()){ @@ -226,15 +233,7 @@ public static double getInputFluidTemp(IReagent reagent, double biomeTemp){ return Math.max(HeatUtil.ABSOLUTE_ZERO, reagent.getMeltingPoint()); } - private static class QueuedEffect{ - - private final IAlchEffect effect; - private final int qty; - - private QueuedEffect(@Nullable IAlchEffect effect, int qty){ - this.effect = effect; - this.qty = qty; - } + private record QueuedEffect(@Nullable IAlchEffect effect, int qty){ private void perform(Level world, BlockPos pos, ReagentMap reags, EnumMatterPhase phase){ if(effect != null){ @@ -242,4 +241,105 @@ private void perform(Level world, BlockPos pos, ReagentMap reags, EnumMatterPhas } } } + + private static final Random RANDOM = new Random(); + + /** + * Transfers some of the stored reagents from one map to another. Meant for conduit transfers. + * For more precise control of the choice of reagents to be transferred, see MiscUtil::withdrawExact + * @param source Source reagent map. Will be modified. + * @param destination Destination reagent map. Will be modified. + * @param toTransferMax Maximum total amount of reagent to be transferred + * @param maxAllowedTransferOfType Function providing maximum quantity of a given reagent type we're allowed to transfer, on a per-type basis. Separate limit from toTransferMax (which applies to the total)- the more limiting of the two will be enforced. Use Integer.MAX_VALUE for no limit. Negative values treated equivalent to 0. + * @return Total quantity that was actually transferred + */ + public static int transferSomeReagents(ReagentMap source, ReagentMap destination, int toTransferMax, Function maxAllowedTransferOfType){ + Set sourceKeySet = source.keySetReag(); + if(sourceKeySet.isEmpty() || toTransferMax <= 0){ + return 0; + } + // Optimization for a very common case: Source is size 1 + if(sourceKeySet.size() == 1){ + for(IReagent reag : sourceKeySet){ + //Only one reag type + int toTransfer = Math.min(source.getQty(reag), Math.min(toTransferMax, maxAllowedTransferOfType.apply(reag))); + destination.transferReagent(reag, toTransfer, source); + return toTransfer; + } + } + + //Optimization for a very common case: toTransferMax is 1 + if(toTransferMax == 1){ + //Build a map of reagents we can transfer + ArrayList> transferLimits = new ArrayList<>(sourceKeySet.size());//Per-reagent limits on transfer + int totalWeight = 0;//For weighted-random selection from transferLimits map. Weights are the transfer limit value + for(IReagent reag : sourceKeySet){ + int perReagLimit = Math.min(source.getQty(reag), maxAllowedTransferOfType.apply(reag)); + if(perReagLimit > 0){ + transferLimits.add(Pair.of(reag, perReagLimit)); + totalWeight += perReagLimit; + } + } + //Can't transfer more than the total transfer limits of the available reagents + toTransferMax = Math.min(toTransferMax, totalWeight); + assert toTransferMax == 0 || toTransferMax == 1; + //Randomly select a unit to transfer + if(toTransferMax == 1){ + //Pick a reagent type to transfer at random, selection weighted by allowed transfer quantity + int selection = RANDOM.nextInt(totalWeight); + for(Pair transferEntry : transferLimits){ + selection -= transferEntry.getRight(); + if(selection < 0){ + //Transfer 1 unit of this reagent and re-roll + destination.transferReagent(transferEntry.getLeft(), 1, source); +// //Update the transfer limits table, as we've depleted amount available by 1 +// int prevLimit = transferLimits.get(transferEntry); +// int newLimit = Math.min(prevLimit, source.getQty(transferEntry)); +// if(newLimit < prevLimit){ +// totalWeight -= prevLimit - newLimit; +// transferLimits.replace(transferEntry, newLimit); +// } + break; + } + } + return 1; + } + return 0; + } + + //Full algorithm + //Build a map of reagents we can transfer + LinkedHashMap transferLimits = new LinkedHashMap<>(sourceKeySet.size());//Per-reagent limits on transfer, actively updated. + int totalWeight = 0;//For weighted-random selection from transferLimits map. Weights are the transfer limit value + for(IReagent reag : sourceKeySet){ + int perReagLimit = Math.min(source.getQty(reag), maxAllowedTransferOfType.apply(reag)); + if(perReagLimit > 0){ + transferLimits.put(reag, perReagLimit); + totalWeight += perReagLimit; + } + } + //Can't transfer more than the total transfer limits of the available reagents + toTransferMax = Math.min(toTransferMax, totalWeight); + //Transfer is done 1 randomly-selected unit at a time (not very efficient, but as of writing, there are literally 0 use-cases that transfer more than 1 unit total anyway, so doesn't matter) + for(int i = 0; i < toTransferMax; i++){ + //Pick a reagent type to transfer at random, selection weighted by allowed transfer quantity + int selection = RANDOM.nextInt(totalWeight); + for(IReagent transferEntry : transferLimits.keySet()){ + selection -= transferLimits.get(transferEntry); + if(selection < 0){ + //Transfer 1 unit of this reagent and re-roll + destination.transferReagent(transferEntry, 1, source); + //Update the transfer limits table, as we've depleted amount available by 1 + int prevLimit = transferLimits.get(transferEntry); + int newLimit = Math.min(prevLimit, source.getQty(transferEntry)); + if(newLimit < prevLimit){ + totalWeight -= prevLimit - newLimit; + transferLimits.replace(transferEntry, newLimit); + } + break; + } + } + } + return toTransferMax; + } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumMatterPhase.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumMatterPhase.java index d2bc6a630..f0455b6c7 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumMatterPhase.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumMatterPhase.java @@ -1,5 +1,7 @@ package com.Da_Technomancer.crossroads.api.alchemy; +import net.minecraft.core.Direction; + public enum EnumMatterPhase{ //Order affects rendering in ReagentRenderer @@ -30,4 +32,8 @@ public boolean flowsUp(){ public boolean flowsDown(){ return flows && flowsDown; } + + public boolean canFlow(Direction toDirection){ + return flows() && (toDirection != Direction.UP || flowsUp) && (toDirection != Direction.DOWN || flowsDown); + } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumTransferMode.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumTransferMode.java index f3b74afdf..112d0ed3b 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumTransferMode.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/EnumTransferMode.java @@ -34,7 +34,7 @@ public boolean isConnection(){ public boolean connectsWith(EnumTransferMode otherMode){ if(this == otherMode){ - return false; + return false;//Also prevents 'generic' BOTH connections connecting to one-another- i.e., vessels can't connect to vessels, only to conduits } return switch(otherMode){ case NONE -> false; diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/IChemicalHandler.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/IChemicalHandler.java index 354ddf310..954c06b6c 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/IChemicalHandler.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/IChemicalHandler.java @@ -3,6 +3,7 @@ import net.minecraft.core.Direction; import javax.annotation.Nonnull; +import java.util.function.Function; /** * Allows the transfer of alchemical reagents and heat. @@ -23,24 +24,47 @@ public interface IChemicalHandler{ double getTemp(); /** + * Attempts to insert all reagents in reag into this handler. Handler will accept as many as it chooses to, leave the rest in reag * @param reag A standard reagent storage map. Moved reagents will be taken from it directly, so it should be mutable and write back to the caller. * @param side The side this is calling (for programming convenience- allows returning the same handler to multiple sides). * @param caller An IChemicalHandler calling this for transferring heat. - * @return Whether anything in reag was changed. + * @return Whether anything in reag was changed. + * @deprecated Switch to another version */ + @Deprecated default boolean insertReagents(ReagentMap reag, Direction side, @Nonnull IChemicalHandler caller){ return insertReagents(reag, side, caller, false); } /** + * Attempts to insert all reagents in reag into this handler. Handler will accept as many as it chooses to, leave the rest in reag * @param reag A standard reagent storage map. Moved reagents will be taken from it directly, so it should be mutable and write back to the caller. * @param side The side this is calling (for programming convenience- allows returning the same handler to multiple sides). * @param caller An IChemicalHandler calling this for transferring heat. - * @param ignorePhase If true, ignore phase movement rules. - * @return Whether anything in reag was changed. + * @param ignorePhase If true, ignore phase movement rules. + * @return Whether anything in reag was changed. */ - boolean insertReagents(ReagentMap reag, Direction side, @Nonnull IChemicalHandler caller, boolean ignorePhase); - + default boolean insertReagents(ReagentMap reag, Direction side, @Nonnull IChemicalHandler caller, boolean ignorePhase){ + return insertReagents(reag, side, caller, ignorePhase ? (reagent) -> Integer.MAX_VALUE : new Function(){ + private final double sourceTemp = reag.getTempC(); + + @Override + public Integer apply(IReagent reagent){ + return reagent.getPhase(sourceTemp).canFlow(side.getOpposite()) ? Integer.MAX_VALUE : 0; + } + }); + } + + /** + * Attempts to insert all reagents in reag into this handler. Handler will accept as many as it chooses to, leave the rest in reag + * @param reag A standard reagent storage map. Moved reagents will be taken from it directly, so it should be mutable and write back to the caller. + * @param side The side this is calling (for programming convenience- allows returning the same handler to multiple sides). + * @param caller An IChemicalHandler calling this for transferring heat. + * @param maximumTransferQuantities A function setting a maximum amount of each type of reagent that is allowed to be transferred from reag. The destination is allowed to set its own limits. Can be used to enforce phase movement rules. Use Integer.MAX_VALUE for no limit. + * @return Whether anything in reag was changed. + */ + boolean insertReagents(ReagentMap reag, Direction side, @Nonnull IChemicalHandler caller, Function maximumTransferQuantities); + @Nonnull EnumTransferMode getMode(Direction side); diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentHolderTE.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentHolderTE.java index 15e87369f..5768c4686 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentHolderTE.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentHolderTE.java @@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Map; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -104,14 +105,14 @@ public void addInfo(ArrayList chat, Player player, BlockHitResult hit int qty = contents.getQty(type); if(qty > 0){ total++; - if(total <= 4){ - chat.add(Component.translatable("tt.crossroads.boilerplate.alchemy_content", type.getName(), qty)); - }else{ - break; - } + chat.add(Component.translatable("tt.crossroads.boilerplate.alchemy_content", type.getName(), qty)); } } - if(total > 4){ + if(total > 5){ + //Only display the first 4, then combine the rest into one line + for(int i = 0; i < total - 4; i++){ + chat.remove(chat.size() - 1); + } chat.add(Component.translatable("tt.crossroads.boilerplate.alchemy_excess", total - 4)); } } @@ -176,7 +177,7 @@ protected void correctReag(){ } for(IReagent type : toRemove){ - contents.remove(type); + contents.remove(type.getID()); } } @@ -222,20 +223,21 @@ protected void performTransfer(boolean ignorePhase){ for(int i = 0; i < 6; i++){ if(modes[i].isOutput()){ Direction side = Direction.from3DDataValue(i); + Direction opposite = side.getOpposite(); BlockEntity te = level.getBlockEntity(worldPosition.relative(side)); LazyOptional otherOpt; - if(contents.getTotalQty() <= 0 || te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, side.getOpposite())).isPresent()){ + if(contents.getTotalQty() <= 0 || te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, opposite)).isPresent()){ continue; } IChemicalHandler otherHandler = otherOpt.orElseThrow(NullPointerException::new); - EnumContainerType otherChannel = otherHandler.getChannel(side.getOpposite()); - EnumTransferMode otherMode = otherHandler.getMode(side.getOpposite()); + EnumContainerType otherChannel = otherHandler.getChannel(opposite); + EnumTransferMode otherMode = otherHandler.getMode(opposite); if(!channel.connectsWith(otherChannel) || !modes[i].connectsWith(otherMode)){ continue; } - if(otherHandler.insertReagents(contents, side.getOpposite(), handler, ignorePhase)){ + if(otherHandler.insertReagents(contents, opposite, handler, ignorePhase)){ lastActTick = worldTick; correctReag(); setChanged(); @@ -452,40 +454,14 @@ public int getTransferCapacity(){ } @Override - public boolean insertReagents(ReagentMap reag, Direction side, IChemicalHandler caller, boolean ignorePhase){ + public boolean insertReagents(ReagentMap reag, Direction side, @Nonnull IChemicalHandler caller, Function maximumTransferQuantities){ if(getMode(side).isInput() && (transferCapacity() != 1 || lastActTick != level.getGameTime())){ int space = getTransferCapacity() - contents.getTotalQty(); if(space <= 0){ return false; } - double callerTemp = reag.getTempC(); - boolean changed = false; - - //Map the movable reags to an int array of quantities so we can use the relevant MiscUtil method - IReagent[] mapping = new IReagent[reag.size()]; - int[] preQty = new int[mapping.length]; - int index = 0; - for(IReagent type : reag.keySetReag()){ - mapping[index] = type; - ReagentStack r = reag.getStack(type); - EnumMatterPhase phase; - if(!r.isEmpty() && (ignorePhase || (phase = type.getPhase(callerTemp)).flows() && (side != Direction.UP || phase.flowsDown()) && (side != Direction.DOWN || phase.flowsUp()))){ - preQty[index] = r.getAmount(); - }else{ - preQty[index] = 0;//Set the pre-qty to 0 to indicate no transfer of that type is allowed - } - index++; - } - - //Use the MiscUtil method - int[] toTrans = MiscUtil.withdrawExact(preQty, space); - for(int i = 0; i < toTrans.length; i++){ - if(toTrans[i] > 0){ - //Transfer each reagent individually, in qty calculated by withdrawExact - changed = true; - contents.transferReagent(mapping[i], toTrans[i], reag); - } - } + int moved = AlchemyUtil.transferSomeReagents(reag, contents, space, maximumTransferQuantities); + boolean changed = moved > 0; if(changed){ dirtyReag = true; diff --git a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentMap.java b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentMap.java index 3cd2255e3..c3abdce1e 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentMap.java +++ b/src/main/java/com/Da_Technomancer/crossroads/api/alchemy/ReagentMap.java @@ -22,7 +22,7 @@ public ReagentMap(){ } public void transferReagent(String id, int amount, ReagentMap srcMap){ - amount = Math.min(amount, srcMap.getQty(id)); + amount = Math.max(0, Math.min(amount, srcMap.getQty(id))); addReagent(id, amount, srcMap.getTempC()); srcMap.removeReagent(id, amount); @@ -191,6 +191,10 @@ public void refresh(){ heat = 0; } } + + private CompoundTag getAsCompoundTag(){ + return write(new CompoundTag()); + } public CompoundTag write(CompoundTag nbt){ nbt.putDouble("he", heat); @@ -239,4 +243,25 @@ public Set keySetReag(){ } return keySetCache; } + + /** + * @return Number of reagent types present in non-zero quantity + */ + public int keySetSize(){ + return keySetReag().size(); + } + + /** + * @deprecated as a warning - check whether you should be using keySetSize for your application + * @return Size of the underlying map, including entries with empty values + */ + @Deprecated(forRemoval = false) + @Override + public int size(){ + return super.size(); + } + + public ReagentMap copy(){ + return readFromNBT(getAsCompoundTag()); + } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTube.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTube.java index 15a9f8883..1ad38577d 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTube.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTube.java @@ -1,7 +1,10 @@ package com.Da_Technomancer.crossroads.blocks.alchemy; +import com.Da_Technomancer.crossroads.api.Capabilities; import com.Da_Technomancer.crossroads.api.CRProperties; +import com.Da_Technomancer.crossroads.api.alchemy.EnumContainerType; import com.Da_Technomancer.crossroads.api.alchemy.EnumTransferMode; +import com.Da_Technomancer.crossroads.api.alchemy.IChemicalHandler; import com.Da_Technomancer.crossroads.api.templates.ConduitBlock; import com.Da_Technomancer.crossroads.blocks.CRBlocks; import com.Da_Technomancer.essentials.api.ITickableTileEntity; @@ -14,6 +17,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.common.util.LazyOptional; import javax.annotation.Nullable; @@ -92,14 +96,26 @@ public BlockEntityTicker getTicker(Level pLevel, Bloc @Override protected EnumTransferMode getValueForPlacement(Level world, BlockPos pos, Direction side, @Nullable BlockEntity neighTE){ //If adjacent to another pipe, set the initial mode based on the other pipe for continuous flow - if(neighTE instanceof AlchemicalTubeTileEntity){ - EnumTransferMode otherMode = ((AlchemicalTubeTileEntity) neighTE).getModes()[side.getOpposite().get3DDataValue()]; + if(neighTE instanceof AlchemicalTubeTileEntity neighTube){ + EnumTransferMode otherMode = neighTube.getModes()[side.getOpposite().get3DDataValue()]; if(otherMode == EnumTransferMode.OUTPUT){ return EnumTransferMode.INPUT; }else if(otherMode == EnumTransferMode.INPUT){ return EnumTransferMode.OUTPUT; } } + Direction opposite = side.getOpposite(); + LazyOptional otherOpt; + if(neighTE == null || !(otherOpt = neighTE.getCapability(Capabilities.CHEMICAL_CAPABILITY, opposite)).isPresent()) { + return EnumTransferMode.INPUT; + } + IChemicalHandler otherHandler = otherOpt.orElseThrow(NullPointerException::new); + //If adjacent to another alchemy thing, set initial mode for continuous flow + if(otherHandler.getChannel(side.getOpposite()).connectsWith(crystal ? EnumContainerType.CRYSTAL : EnumContainerType.GLASS)){ + if(otherHandler.getMode(side.getOpposite()) == EnumTransferMode.INPUT){ + return EnumTransferMode.OUTPUT; + } + } return EnumTransferMode.INPUT; } @@ -109,7 +125,7 @@ protected void onAdjusted(Level world, BlockPos pos, BlockState newState, Direct BlockEntity neighTE = world.getBlockEntity(pos.relative(facing)); //Check the neighbor is another conduit with the same channel - if(neighTE instanceof AlchemicalTubeTileEntity && ((AlchemicalTube) neighTE.getBlockState().getBlock()).crystal == crystal){ + if(neighTE instanceof AlchemicalTubeTileEntity neighTube && ((AlchemicalTube) neighTE.getBlockState().getBlock()).crystal == crystal){ //Adjust the neighboring pipe alongside this one EnumTransferMode otherMode; switch(newVal){ @@ -125,7 +141,7 @@ protected void onAdjusted(Level world, BlockPos pos, BlockState newState, Direct break; } - ((AlchemicalTubeTileEntity) neighTE).setData(facing.getOpposite().get3DDataValue(), newVal.isConnection(), otherMode); + neighTube.setData(facing.getOpposite().get3DDataValue(), newVal.isConnection(), otherMode); } } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTubeTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTubeTileEntity.java index 3f2690f6a..20c2d95d1 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTubeTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/AlchemicalTubeTileEntity.java @@ -104,18 +104,19 @@ protected void performTransfer(boolean ignorePhase){ for(int i = 0; i < 6; i++){ if(modes[i].isConnection()){ Direction side = Direction.from3DDataValue(i); + Direction opposite = side.getOpposite(); BlockEntity te = level.getBlockEntity(worldPosition.relative(side)); LazyOptional otherOpt; - if(te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, side.getOpposite())).isPresent()){ + if(te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, opposite)).isPresent()){ setData(i, false, modes[i]); continue; } IChemicalHandler otherHandler = otherOpt.orElseThrow(NullPointerException::new); - EnumContainerType otherChannel = otherHandler.getChannel(side.getOpposite()); - EnumTransferMode otherMode = otherHandler.getMode(side.getOpposite()); - if(!channel.connectsWith(otherChannel) || !modes[i].connectsWith(otherMode)){ + EnumContainerType otherChannel = otherHandler.getChannel(opposite); + EnumTransferMode _otherMode = otherHandler.getMode(opposite); + if(!channel.connectsWith(otherChannel)/* || !modes[i].connectsWith(otherMode)*/){ setData(i, false, modes[i]); continue; } @@ -123,7 +124,7 @@ protected void performTransfer(boolean ignorePhase){ if(contents.getTotalQty() == 0 || !modes[i].isOutput()){ continue; } - if(otherHandler.insertReagents(contents, side.getOpposite(), handler, ignorePhase)){ + if(otherHandler.insertReagents(contents, opposite, handler, ignorePhase)){ lastActTick = worldTick; correctReag(); setChanged(); diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/ChemicalVentTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/ChemicalVentTileEntity.java index a912a0b1b..aebb6cbda 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/ChemicalVentTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/ChemicalVentTileEntity.java @@ -16,6 +16,7 @@ import net.minecraftforge.common.util.LazyOptional; import java.util.HashSet; +import java.util.function.Function; public class ChemicalVentTileEntity extends BlockEntity implements ITickableTileEntity{ @@ -99,25 +100,12 @@ public double getTemp(){ } @Override - public boolean insertReagents(ReagentMap reag, Direction side, IChemicalHandler caller, boolean ignorePhase){ - double callerTemp = reag.getTempK(); - - HashSet validIds = new HashSet<>(4); - - for(IReagent type : reag.keySetReag()){ - ReagentStack r = reag.getStack(type); - if(!r.isEmpty()){ - EnumMatterPhase phase = type.getPhase(HeatUtil.toCelcius(callerTemp)); - if(ignorePhase || (phase.flows() && (side != Direction.UP || phase.flowsDown()) && (side != Direction.DOWN || phase.flowsUp()))){ - validIds.add(type.getID()); - } - } - } - + public boolean insertReagents(ReagentMap reag, Direction side, IChemicalHandler caller, Function maximumTransferQuantities){ boolean acted = false; - for(String id : validIds){ - int moved = reag.getQty(id); - if(moved != 0){ + HashSet reagentTypes = new HashSet<>(reag.keySetReag()); + for(IReagent id : reagentTypes){ + int moved = Math.min(reag.getQty(id), maximumTransferQuantities.apply(id)); + if(moved > 0){ reags.transferReagent(id, moved, reag); acted = true; } diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/FlowLimiterTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/FlowLimiterTileEntity.java index dc14e77c0..e4b9a0663 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/FlowLimiterTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/FlowLimiterTileEntity.java @@ -18,6 +18,7 @@ import net.minecraftforge.common.util.LazyOptional; import org.apache.commons.lang3.tuple.Pair; import org.joml.Vector3f; +import java.util.function.Function; public class FlowLimiterTileEntity extends ReagentHolderTE{ @@ -77,34 +78,31 @@ protected void performTransfer(boolean ignorePhase){ if(modes[i].isOutput()){ Direction side = Direction.from3DDataValue(i); BlockEntity te = level.getBlockEntity(worldPosition.relative(side)); + Direction opposite = side.getOpposite(); + LazyOptional otherOpt; - if(contents.getTotalQty() <= 0 || te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, side.getOpposite())).isPresent()){ + if(contents.getTotalQty() <= 0 || te == null || !(otherOpt = te.getCapability(Capabilities.CHEMICAL_CAPABILITY, opposite)).isPresent()){ continue; } + IChemicalHandler otherHandler = otherOpt.orElseThrow(NullPointerException::new); - EnumContainerType otherChannel = otherHandler.getChannel(side.getOpposite()); - EnumTransferMode otherMode = otherHandler.getMode(side.getOpposite()); + EnumContainerType otherChannel = otherHandler.getChannel(opposite); + EnumTransferMode otherMode = otherHandler.getMode(opposite); if(!channel.connectsWith(otherChannel) || !modes[i].connectsWith(otherMode)){ continue; } - int limit = LIMITS[limitIndex]; - ReagentMap transferReag = new ReagentMap(); - for(IReagent type : contents.keySetReag()){ - int qty = contents.getQty(type); - int specificLimit = Math.min(qty, limit - otherHandler.getContent(type)); - if(specificLimit > 0){ - transferReag.transferReagent(type, specificLimit, contents); + //Implements the custom flow limiting behavior + final int limit = LIMITS[limitIndex]; + final Function flowLimits = (IReagent reagent) -> { + if(!reagent.getPhase(correctTemp()).canFlow(side)){ + return 0; } - } - - boolean changed = otherHandler.insertReagents(transferReag, side.getOpposite(), handler); - for(IReagent type : transferReag.keySetReag()){ - contents.transferReagent(type, transferReag.getQty(type), transferReag); - } - if(changed){ + return limit - otherHandler.getContent(reagent); + }; + if(otherHandler.insertReagents(contents, opposite, handler, flowLimits)){ lastActTick = worldTick; correctReag(); setChanged(); diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/VoltusGeneratorTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/VoltusGeneratorTileEntity.java index 2d7501f6a..85c29f1c8 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/VoltusGeneratorTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/alchemy/VoltusGeneratorTileEntity.java @@ -23,6 +23,7 @@ import net.minecraftforge.energy.IEnergyStorage; import java.util.ArrayList; +import java.util.function.Function; public class VoltusGeneratorTileEntity extends BlockEntity implements ITickableTileEntity, IInfoTE{ @@ -166,17 +167,16 @@ public double getTemp(){ } @Override - public boolean insertReagents(ReagentMap reag, Direction side, IChemicalHandler caller, boolean ignorePhase){ + public boolean insertReagents(ReagentMap reag, Direction side, IChemicalHandler caller, Function maximumTransferQuantities){ //Only allows insertion of voltus - if(voltusAmount >= VOLTUS_CAPACITY || reag.getQty(EnumReagents.ELEM_CHARGE.id()) == 0){ - return false; + int moved = Math.min(Math.min(reag.getQty(EnumReagents.ELEM_CHARGE.id()), VOLTUS_CAPACITY - voltusAmount), maximumTransferQuantities.apply(ReagentManager.getReagent(EnumReagents.ELEM_CHARGE.id()))); + if(moved > 0){ + voltusAmount += moved; + reag.removeReagent(EnumReagents.ELEM_CHARGE.id(), moved); + setChanged(); + return true; } - - int moved = Math.min(reag.getQty(EnumReagents.ELEM_CHARGE.id()), VOLTUS_CAPACITY - voltusAmount); - voltusAmount += moved; - reag.removeReagent(EnumReagents.ELEM_CHARGE.id(), moved); - setChanged(); - return true; + return false; } @Override diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/Dynamo.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/Dynamo.java index 7e6a1e6aa..d2188bc04 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/Dynamo.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/Dynamo.java @@ -2,9 +2,11 @@ import com.Da_Technomancer.crossroads.CRConfig; import com.Da_Technomancer.crossroads.api.CRProperties; +import com.Da_Technomancer.crossroads.api.CircuitUtil; import com.Da_Technomancer.crossroads.blocks.CRBlocks; import com.Da_Technomancer.essentials.api.ConfigUtil; import com.Da_Technomancer.essentials.api.ITickableTileEntity; +import com.Da_Technomancer.essentials.api.redstone.IReadable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; @@ -32,7 +34,7 @@ import javax.annotation.Nullable; import java.util.List; -public class Dynamo extends BaseEntityBlock{ +public class Dynamo extends BaseEntityBlock implements IReadable{ private static final VoxelShape[] SHAPES = new VoxelShape[2]; static{ @@ -95,4 +97,22 @@ public BlockState getStateForPlacement(BlockPlaceContext context){ protected void createBlockStateDefinition(StateDefinition.Builder builder){ builder.add(CRProperties.HORIZ_FACING); } + + @Override + public float read(Level level, BlockPos blockPos, BlockState blockState){ + if(level.getBlockEntity(blockPos) instanceof DynamoTileEntity dynamoTE){ + return dynamoTE.getLastAddedFE(); + } + return 0; + } + + @Override + public boolean hasAnalogOutputSignal(BlockState pState){ + return true; + } + + @Override + public int getAnalogOutputSignal(BlockState pState, Level pLevel, BlockPos pPos){ + return CircuitUtil.clampToVanilla(read(pLevel, pPos, pState)); + } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/DynamoTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/DynamoTileEntity.java index d29f2af64..1af2e3a95 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/DynamoTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/DynamoTileEntity.java @@ -9,14 +9,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.energy.IEnergyStorage; +import java.util.ArrayList; + public class DynamoTileEntity extends ModuleTE{ public static final BlockEntityType TYPE = CRTileEntity.createType(DynamoTileEntity::new, CRBlocks.dynamo); @@ -25,6 +30,20 @@ public class DynamoTileEntity extends ModuleTE{ public static final int INERTIA = 200; public static final double POWER_MULT = 20; + private int lastAddedFE; + private int lastAddedFECapacity;//For display purposes + + @Override + public void addInfo(ArrayList chat, Player player, BlockHitResult hit){ + chat.clear();//Replace any existing FE line + chat.add(Component.translatable("tt.crossroads.dynamo.fe_info", fe, CHARGE_CAPACITY, lastAddedFE, lastAddedFECapacity)); + super.addInfo(chat, player, hit); + } + + public int getLastAddedFE(){ + return lastAddedFE; + } + private int fe = 0; public DynamoTileEntity(BlockPos pos, BlockState state){ @@ -48,8 +67,11 @@ public void serverTick(){ int operations = (int) Math.min(Math.abs(energy), POWER_MULT * Math.abs(axleHandler.getSpeed())); if(operations > 0){ axleHandler.addEnergy(-operations, false); - fe += operations * CRConfig.electPerJoule.get(); + int lastFE = fe; + lastAddedFECapacity = operations * CRConfig.electPerJoule.get(); + fe += lastAddedFECapacity; fe = Math.min(fe, CHARGE_CAPACITY); + lastAddedFE = fe - lastFE; setChanged(); } @@ -79,13 +101,14 @@ public void setBlockState(BlockState stateIn){ public void load(CompoundTag nbt){ super.load(nbt); fe = nbt.getInt("charge"); + lastAddedFE = nbt.getInt("added_fe"); } @Override public void saveAdditional(CompoundTag nbt){ super.saveAdditional(nbt); nbt.putInt("charge", fe); - + nbt.putInt("added_fe", lastAddedFE); } @Override diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoil.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoil.java index 259fe8628..e4e0de2f6 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoil.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoil.java @@ -109,11 +109,7 @@ public InteractionResult use(BlockState state, Level worldIn, BlockPos pos, Play } if(!worldIn.isClientSide && worldIn.getBlockEntity(pos) instanceof TeslaCoilTileEntity te){ - if(heldItem.isEmpty()){ - playerIn.setItemInHand(hand, te.removeBattery()); - }else{ - playerIn.setItemInHand(hand, te.addBattery(heldItem)); - } + te.swapBattery(heldItem, playerIn, hand); } return InteractionResult.SUCCESS; } diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTileEntity.java index a2cfbb692..d92e2763f 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTileEntity.java @@ -6,8 +6,6 @@ import com.Da_Technomancer.crossroads.api.templates.IInfoTE; import com.Da_Technomancer.crossroads.blocks.CRBlocks; import com.Da_Technomancer.crossroads.blocks.CRTileEntity; -import com.Da_Technomancer.crossroads.items.CRItems; -import com.Da_Technomancer.crossroads.items.LeydenJar; import com.Da_Technomancer.essentials.api.IItemStorage; import com.Da_Technomancer.essentials.api.ITickableTileEntity; import com.Da_Technomancer.essentials.api.packets.ILongReceiver; @@ -18,6 +16,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Containers; +import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; @@ -91,7 +90,7 @@ public int getTotalFE(){ return storedSelf; } - protected ItemStack removeBattery(){ + private ItemStack removeBattery(){ if(!battery.isEmpty()){ ItemStack result = battery; battery = ItemStack.EMPTY; @@ -115,6 +114,30 @@ protected ItemStack addBattery(ItemStack newBattery){ return newBattery; } + protected void swapBattery(ItemStack newBattery, Player player, InteractionHand hand){ + //For players clicking the tesla coil with a held 'battery' item or an empty hand + if(newBattery.isEmpty()){ + player.setItemInHand(hand, removeBattery()); + return; + } + if(newBattery.getCapability(ForgeCapabilities.ENERGY) != null){ + if(battery.isEmpty()){ + player.setItemInHand(hand, addBattery(newBattery)); + }else{ + //Need to swap out existing battery + ItemStack removedBattery = removeBattery(); + player.setItemInHand(hand, addBattery(newBattery)); + if(player.getItemInHand(hand).isEmpty()){ + player.setItemInHand(hand, removedBattery); + }else{ + if(!player.addItem(removedBattery)){ + player.drop(removedBattery, true, false); + } + } + } + } + } + public void setTotalFE(int totalFE){ //Client-side: storedSelf = FE storage //No battery: storedSelf = FE storage diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTopTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTopTileEntity.java index e095a124d..d8d97b659 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTopTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/electric/TeslaCoilTopTileEntity.java @@ -30,7 +30,7 @@ public class TeslaCoilTopTileEntity extends BlockEntity implements IInfoTE, ILinkTE{ - public static final BlockEntityType TYPE = CRTileEntity.createType(TeslaCoilTopTileEntity::new, CRBlocks.teslaCoilTopAttack, CRBlocks.teslaCoilTopDecorative, CRBlocks.teslaCoilTopDistance, CRBlocks.teslaCoilTopEfficiency, CRBlocks.teslaCoilTopNormal); + public static final BlockEntityType TYPE = CRTileEntity.createType(TeslaCoilTopTileEntity::new, CRBlocks.teslaCoilTopAttack, CRBlocks.teslaCoilTopDecorative, CRBlocks.teslaCoilTopDistance, CRBlocks.teslaCoilTopEfficiency, CRBlocks.teslaCoilTopNormal, CRBlocks.teslaCoilTopIntensity); public static final int[] COLOR_CODES = {0xFFECCFFF, 0xFFFCDFFF, 0xFFFFFAFF}; private static final int[] ATTACK_COLOR_CODES = {0xFFFFCCCC, 0xFFFFFFCC, 0xFFFFFAFA}; diff --git a/src/main/java/com/Da_Technomancer/crossroads/blocks/rotary/BlastFurnaceTileEntity.java b/src/main/java/com/Da_Technomancer/crossroads/blocks/rotary/BlastFurnaceTileEntity.java index 01b67ba49..34a1011a0 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/blocks/rotary/BlastFurnaceTileEntity.java +++ b/src/main/java/com/Da_Technomancer/crossroads/blocks/rotary/BlastFurnaceTileEntity.java @@ -121,7 +121,7 @@ public void serverTick(){ return; } BlastFurnaceRec recipe = recOpt.get(); - if(carbon < recipe.getSlag() || inventory[2].getCount() + recipe.getSlag() > CRItems.slag.getMaxStackSize(inventory[2]) || (!fluids[0].isEmpty() && (!BlockUtil.sameFluid(recipe.getOutput(), fluids[0]) || fluidProps[0].capacity < fluids[0].getAmount() + recipe.getOutput().getAmount()))){ + if(carbon < recipe.getSlag() || (!inventory[2].isEmpty() && inventory[2].getCount() + recipe.getSlag() > inventory[2].getMaxStackSize()) || (!fluids[0].isEmpty() && (!BlockUtil.sameFluid(recipe.getOutput(), fluids[0]) || fluidProps[0].capacity < fluids[0].getAmount() + recipe.getOutput().getAmount()))){ //The fluid and slag outputs need to fit, and we need enough carbon progress = 0; updateWorldState(false); diff --git a/src/main/java/com/Da_Technomancer/crossroads/crafting/ReagentRec.java b/src/main/java/com/Da_Technomancer/crossroads/crafting/ReagentRec.java index f49eeafd8..f1292c338 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/crafting/ReagentRec.java +++ b/src/main/java/com/Da_Technomancer/crossroads/crafting/ReagentRec.java @@ -28,6 +28,7 @@ import java.awt.*; import java.util.HashMap; import java.util.Locale; +import java.util.Objects; import java.util.function.Function; public class ReagentRec implements Recipe, IReagent{ @@ -71,7 +72,21 @@ public ReagentRec(ResourceLocation location, String group, String id, double mel } @Override - public boolean matches(Container inv, Level worldIn){ + public boolean equals(Object o){ + if(o == null || getClass() != o.getClass()){ + return false; + } + ReagentRec that = (ReagentRec) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode(){ + return Objects.hashCode(id); + } + + @Override + public boolean matches(Container input, Level worldIn){ return true; } @@ -316,7 +331,7 @@ public void toNetwork(FriendlyByteBuf buffer, ReagentRec recipe){ private static final HashMap containTypeMap = new HashMap<>(3);//No register method for this, as it maps to an enum private static final HashMap> flameRadiusMap = new HashMap<>(5); - private static final HashMap effectMap = new HashMap<>(19); + private static final HashMap effectMap = new HashMap<>(20); /** * Adds a new alchemy effect, which reagents can use by setting their effect to the passed id @@ -369,6 +384,7 @@ public static void registerFlameFormula(String id, Function fl registerEffect("terraform_jungle", new JungleTerraformEffect()); registerEffect("terraform_end", new EndTerraformEffect()); registerEffect("terraform_flower_forest", new FlowerForestTerraformEffect()); + registerEffect("hydrate", new HydrateEffect()); } private enum ContainRequirements{ diff --git a/src/main/java/com/Da_Technomancer/crossroads/effects/alchemy_effects/HydrateEffect.java b/src/main/java/com/Da_Technomancer/crossroads/effects/alchemy_effects/HydrateEffect.java new file mode 100644 index 000000000..1fcb9b095 --- /dev/null +++ b/src/main/java/com/Da_Technomancer/crossroads/effects/alchemy_effects/HydrateEffect.java @@ -0,0 +1,58 @@ +package com.Da_Technomancer.crossroads.effects.alchemy_effects; + +import com.Da_Technomancer.crossroads.api.alchemy.EnumMatterPhase; +import com.Da_Technomancer.crossroads.api.alchemy.IAlchEffect; +import com.Da_Technomancer.crossroads.api.alchemy.ReagentMap; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.animal.axolotl.Axolotl; +import net.minecraft.world.entity.projectile.ThrownPotion; +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; + +public class HydrateEffect implements IAlchEffect{ + + + @Override + public void doEffect(Level world, BlockPos pos, int amount, EnumMatterPhase phase, ReagentMap reags){ + if(phase == EnumMatterPhase.LIQUID){ + // Based on vanilla's splash water bottle + AABB aabb = new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1F, pos.getY() + 1F, pos.getZ() + 1F); + + for(LivingEntity livingentity : world.getEntitiesOfClass(LivingEntity.class, aabb, ThrownPotion.WATER_SENSITIVE_OR_ON_FIRE)){ + if(livingentity.isSensitiveToWater()){ + livingentity.hurt(world.damageSources().magic(), 1.0F); + } + + if(livingentity.isOnFire() && livingentity.isAlive()){ + livingentity.extinguishFire(); + } + } + + for(Axolotl axolotl : world.getEntitiesOfClass(Axolotl.class, aabb)){ + axolotl.rehydrate(); + } + + BlockState state = world.getBlockState(pos); + if(state.is(BlockTags.FIRE)){ + world.destroyBlock(pos, false, null); + }else if(AbstractCandleBlock.isLit(state)){ + AbstractCandleBlock.extinguish(null, state, world, pos); + }else if(CampfireBlock.isLitCampfire(state)){ + world.levelEvent(null, 1009, pos, 0); + CampfireBlock.dowse(null, world, pos, state); + world.setBlockAndUpdate(pos, state.setValue(CampfireBlock.LIT, Boolean.FALSE)); + } + } + } + + @Override + public Component getName(){ + return Component.translatable("effect.hydrate"); + } +} diff --git a/src/main/java/com/Da_Technomancer/crossroads/integration/jei/ReagInfoCategory.java b/src/main/java/com/Da_Technomancer/crossroads/integration/jei/ReagInfoCategory.java index 3218b6b40..39ba66057 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/integration/jei/ReagInfoCategory.java +++ b/src/main/java/com/Da_Technomancer/crossroads/integration/jei/ReagInfoCategory.java @@ -3,11 +3,14 @@ import com.Da_Technomancer.crossroads.Crossroads; import com.Da_Technomancer.crossroads.api.MiscUtil; import com.Da_Technomancer.crossroads.api.alchemy.IReagent; +import com.Da_Technomancer.crossroads.api.crafting.FluidIngredient; import com.Da_Technomancer.crossroads.api.heat.HeatUtil; import com.Da_Technomancer.crossroads.items.CRItems; import com.google.common.collect.ImmutableList; import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.gui.builder.IIngredientAcceptor; import mezz.jei.api.gui.builder.IRecipeLayoutBuilder; +import mezz.jei.api.gui.builder.IRecipeSlotBuilder; import mezz.jei.api.gui.drawable.IDrawable; import mezz.jei.api.gui.ingredient.IRecipeSlotsView; import mezz.jei.api.helpers.IGuiHelper; @@ -23,7 +26,9 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.material.Fluid; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -97,11 +102,25 @@ public void setRecipe(IRecipeLayoutBuilder builder, IReagent recipe, IFocusGroup TagKey jeiSolids = recipe.getJEISolids(); Ingredient itemForm = Ingredient.of(jeiSolids); if(!itemForm.isEmpty()){ - builder.addSlot(RecipeIngredientRole.INPUT, 19, 1).addIngredients(itemForm); + builder.addSlot(RecipeIngredientRole.INPUT, 21, 3).addIngredients(itemForm); builder.addInvisibleIngredients(RecipeIngredientRole.OUTPUT).addIngredients(itemForm); } }catch(Exception e){ Crossroads.logger.error(String.format("Failed to load item form of reagent %1$s for JEI integration", recipe.getName())); } + + FluidIngredient fluidForm = recipe.getFluid(); + int fluidQty = recipe.getFluidQty(); + Collection fluidForms = fluidForm.getMatchedFluids(); + if(!fluidForms.isEmpty() && fluidQty > 0){ + IRecipeSlotBuilder fluidInputSlotBuilder = builder.addSlot(RecipeIngredientRole.INPUT, 39, 3); + fluidInputSlotBuilder.setFluidRenderer(1, false, 16, 16); + IIngredientAcceptor fluidOutputSlotBuilder = builder.addInvisibleIngredients(RecipeIngredientRole.OUTPUT); + //Might have multiple matched fluid types - put them all in the same slot + for(Fluid fluid : fluidForms){ + fluidInputSlotBuilder.addFluidStack(fluid, fluidQty); + fluidOutputSlotBuilder.addFluidStack(fluid, fluidQty); + } + } } } diff --git a/src/main/java/com/Da_Technomancer/crossroads/items/alchemy/PhilStone.java b/src/main/java/com/Da_Technomancer/crossroads/items/alchemy/PhilStone.java index e7b817921..4dff09526 100644 --- a/src/main/java/com/Da_Technomancer/crossroads/items/alchemy/PhilStone.java +++ b/src/main/java/com/Da_Technomancer/crossroads/items/alchemy/PhilStone.java @@ -24,10 +24,10 @@ public PhilStone(boolean pracStone){ public boolean onEntityItemUpdate(ItemStack stack, ItemEntity entity){ if(entity.onGround()){ AABB entityBox = entity.getBoundingBox(); - clearBlock(entity.level(), MiscUtil.blockPos(entityBox.maxX, entityBox.minY - 0.05D, entityBox.maxZ)); - clearBlock(entity.level(), MiscUtil.blockPos(entityBox.maxX, entityBox.minY - 0.05D, entityBox.minZ)); - clearBlock(entity.level(), MiscUtil.blockPos(entityBox.minX, entityBox.minY - 0.05D, entityBox.maxZ)); - clearBlock(entity.level(), MiscUtil.blockPos(entityBox.minX, entityBox.minY - 0.05D, entityBox.minZ)); + clearBlock(entity.level(), new BlockPos((int) entityBox.maxX, (int) (entityBox.minY - 0.05D), (int) entityBox.maxZ)); + clearBlock(entity.level(), new BlockPos((int) entityBox.maxX, (int) (entityBox.minY - 0.05D), (int) entityBox.minZ)); + clearBlock(entity.level(), new BlockPos((int) entityBox.minX, (int) (entityBox.minY - 0.05D), (int) entityBox.maxZ)); + clearBlock(entity.level(), new BlockPos((int) entityBox.minX, (int) (entityBox.minY - 0.05D), (int) entityBox.minZ)); } return false; } diff --git a/src/main/resources/assets/crossroads/lang/en_us.json b/src/main/resources/assets/crossroads/lang/en_us.json index 857b8677c..d152d2b72 100644 --- a/src/main/resources/assets/crossroads/lang/en_us.json +++ b/src/main/resources/assets/crossroads/lang/en_us.json @@ -480,7 +480,8 @@ "tt.crossroads.wind_turbine.power": "Produces: ±(%1$d-%2$d) J/t, (avg ±%3$d J/t)", "tt.crossroads.wind_turbine.limits": "Useless above %1$frad/s; Comparators read power output", "tt.crossroads.dynamo.power": "Produces: %1$fFE/J", - "tt.crossroads.dynamo.usage": "Consumes: -%1$dω J/t)", + "tt.crossroads.dynamo.usage": "Consumes: -%1$dω J/t", + "tt.crossroads.dynamo.fe_info": "Generating +%3$d/%4$dFE/t - Charge: %1$d/%2$dFE", "tt.crossroads.fat_collector.tier": "%1$d%% efficiency when above %2$d°C", "tt.crossroads.edible_blob.food": "Hunger: %1$d", "tt.crossroads.edible_blob.sat": "Saturation: %1$d", @@ -1043,6 +1044,7 @@ "effect.terraform_jungle": "Create Jungle", "effect.terraform_end": "Create End", "effect.terraform_flower_forest": "Create Flower Forest", + "effect.hydrate": "Hydrate (liquid only)", "path.technomancy": "Technomancy", diff --git a/src/main/resources/assets/crossroads/textures/item/wheezewort_seeds.png b/src/main/resources/assets/crossroads/textures/item/wheezewort_seeds.png index 39d27e7fa..5ec5b3dd8 100644 Binary files a/src/main/resources/assets/crossroads/textures/item/wheezewort_seeds.png and b/src/main/resources/assets/crossroads/textures/item/wheezewort_seeds.png differ diff --git a/src/main/resources/data/crossroads/recipes/fluid_cooling/obsidian.json b/src/main/resources/data/crossroads/recipes/fluid_cooling/obsidian.json index 909648133..f1994c8ad 100644 --- a/src/main/resources/data/crossroads/recipes/fluid_cooling/obsidian.json +++ b/src/main/resources/data/crossroads/recipes/fluid_cooling/obsidian.json @@ -4,5 +4,5 @@ "fluid_amount": 1000, "item": "obsidian", "max_temp": 2250, - "temp_change": 1250 + "temp_change": 1000 } \ No newline at end of file diff --git a/src/main/resources/data/crossroads/recipes/reagents/water.json b/src/main/resources/data/crossroads/recipes/reagents/water.json index 2880aec32..04c2a7f04 100644 --- a/src/main/resources/data/crossroads/recipes/reagents/water.json +++ b/src/main/resources/data/crossroads/recipes/reagents/water.json @@ -12,5 +12,6 @@ "base": "C80060FF", "gas": "80FFFFFF", "solid": "A0AAE1FF" - } + }, + "effect": "hydrate" } \ No newline at end of file diff --git a/src/main/resources/data/crossroads/worldgen/placed_feature/ore_ruby.json b/src/main/resources/data/crossroads/worldgen/placed_feature/ore_ruby.json index b516470c3..950200a55 100644 --- a/src/main/resources/data/crossroads/worldgen/placed_feature/ore_ruby.json +++ b/src/main/resources/data/crossroads/worldgen/placed_feature/ore_ruby.json @@ -7,7 +7,7 @@ }, { "type": "minecraft:count", - "count": 100 + "count": 150 }, { "type": "minecraft:in_square" @@ -15,12 +15,12 @@ { "type": "minecraft:height_range", "height": { - "type": "uniform", + "type": "trapezoid", "min_inclusive": { "absolute": 2 }, "max_inclusive": { - "absolute": 117 + "absolute": 100 } } } diff --git a/src/main/resources/python/docs/src/en_us/entries/alchemy/fluid_injector.json b/src/main/resources/python/docs/src/en_us/entries/alchemy/fluid_injector.json index 0dedbd792..41b7d5562 100644 --- a/src/main/resources/python/docs/src/en_us/entries/alchemy/fluid_injector.json +++ b/src/main/resources/python/docs/src/en_us/entries/alchemy/fluid_injector.json @@ -16,7 +16,7 @@ }, { "type": "patchouli:text", - "text": "Only certain $(l:essentials:intro/alchemy)reagents/$ and fluids can be converted- most $(l:essentials:intro/alchemy)reagents/$ have no liquid associated with it.$(br2)100mB $(l:essentials:intro/fluid)distilled water/$ <-> water/$$(br2)16mB $(l:essentials:intro/fluid)molten iron/$ <-> iron/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten gold/$ <-> gold/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten copper/$ <-> copper/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten tin/$ <-> tin/$ (1 unit = 1 nugget)$(br2)" + "text": "Only certain $(l:essentials:intro/alchemy)reagents/$ and fluids can be converted- most $(l:essentials:intro/alchemy)reagents/$ have no liquid associated with it.$(br2)125mB $(l:essentials:intro/fluid)distilled water/$ <-> water/$$(br2)16mB $(l:essentials:intro/fluid)molten iron/$ <-> iron/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten gold/$ <-> gold/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten copper/$ <-> copper/$ (1 unit = 1 nugget)$(br2)16mB $(l:essentials:intro/fluid)molten tin/$ <-> tin/$ (1 unit = 1 nugget)$(br2)" } ], "extra_recipe_mappings": {