Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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<QueuedEffect> effectsSol = new ArrayList<>(reags.size());
ArrayList<QueuedEffect> effectsLiq = new ArrayList<>(reags.size());
ArrayList<QueuedEffect> effectsGas = new ArrayList<>(reags.size());
ArrayList<QueuedEffect> effectsSol = new ArrayList<>(reags.keySetSize());
ArrayList<QueuedEffect> effectsLiq = new ArrayList<>(reags.keySetSize());
ArrayList<QueuedEffect> effectsGas = new ArrayList<>(reags.keySetSize());

double tempC = reags.getTempC();
for(IReagent reag : reags.keySetReag()){
Expand Down Expand Up @@ -226,20 +233,113 @@ 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){
effect.doEffect(world, pos, qty, phase, reags);
}
}
}

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<IReagent, Integer> maxAllowedTransferOfType){
Set<IReagent> 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<Pair<IReagent, Integer>> 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<IReagent, Integer> 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<IReagent, Integer> 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;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.Da_Technomancer.crossroads.api.alchemy;

import net.minecraft.core.Direction;

public enum EnumMatterPhase{

//Order affects rendering in ReagentRenderer
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<IReagent, Integer>(){
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<IReagent, Integer> maximumTransferQuantities);

@Nonnull
EnumTransferMode getMode(Direction side);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -104,14 +105,14 @@ public void addInfo(ArrayList<Component> 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));
}
}
Expand Down Expand Up @@ -176,7 +177,7 @@ protected void correctReag(){
}

for(IReagent type : toRemove){
contents.remove(type);
contents.remove(type.getID());
}
}

Expand Down Expand Up @@ -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<IChemicalHandler> 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();
Expand Down Expand Up @@ -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<IReagent, Integer> 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;
Expand Down
Loading