Create/src/main/java/com/simibubi/create/content/equipment/armor/BacktankUtil.java
simibubi 6819fc1c42 Issue. Gesundheit
- Fixed an incompatibility with Supplementaries leading to a server crash
- Fixed fallback damage bar colour for air powered tools
- Various NPE guards
- Fixed sequenced assembly recipes conflicting when starting with a filling step
- Potato cannons can no longer plant crops on the side of farmland
- Fixed Mechanical rollers consuming filter items despite not supporting them
- Fixed brass tunnels not refunding previous filter items when changed
- Fixed catalyst ingredients getting consumed in the basin
2023-07-04 17:43:54 +02:00

198 lines
6.6 KiB
Java

package com.simibubi.create.content.equipment.armor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import com.simibubi.create.AllEnchantments;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllTags;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket;
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.DistExecutor;
public class BacktankUtil {
private static final List<Function<LivingEntity, List<ItemStack>>> BACKTANK_SUPPLIERS = new ArrayList<>();
static {
addBacktankSupplier(entity -> {
List<ItemStack> stacks = new ArrayList<>();
for (ItemStack itemStack : entity.getArmorSlots())
if (AllTags.AllItemTags.PRESSURIZED_AIR_SOURCES.matches(itemStack))
stacks.add(itemStack);
return stacks;
});
}
public static List<ItemStack> getAllWithAir(LivingEntity entity) {
List<ItemStack> all = new ArrayList<>();
for (Function<LivingEntity, List<ItemStack>> supplier : BACKTANK_SUPPLIERS) {
List<ItemStack> result = supplier.apply(entity);
for (ItemStack stack : result)
if (hasAirRemaining(stack))
all.add(stack);
}
// Sort with ascending order (we want to prioritize the most empty so things actually run out)
all.sort((a, b) -> Float.compare(getAir(a), getAir(b)));
return all;
}
public static boolean hasAirRemaining(ItemStack backtank) {
return getAir(backtank) > 0;
}
public static float getAir(ItemStack backtank) {
CompoundTag tag = backtank.getOrCreateTag();
return Math.min(tag.getFloat("Air"), maxAir(backtank));
}
public static void consumeAir(LivingEntity entity, ItemStack backtank, float i) {
CompoundTag tag = backtank.getOrCreateTag();
int maxAir = maxAir(backtank);
float air = getAir(backtank);
float newAir = Math.max(air - i, 0);
tag.putFloat("Air", Math.min(newAir, maxAir));
backtank.setTag(tag);
if (!(entity instanceof ServerPlayer player))
return;
sendWarning(player, air, newAir, maxAir / 10f);
sendWarning(player, air, newAir, 1);
}
private static void sendWarning(ServerPlayer player, float air, float newAir, float threshold) {
if (newAir > threshold)
return;
if (air <= threshold)
return;
boolean depleted = threshold == 1;
MutableComponent component = Lang.translateDirect(depleted ? "backtank.depleted" : "backtank.low");
AllSoundEvents.DENY.play(player.level, null, player.blockPosition(), 1, 1.25f);
AllSoundEvents.STEAM.play(player.level, null, player.blockPosition(), .5f, .5f);
player.connection.send(new ClientboundSetTitlesAnimationPacket(10, 40, 10));
player.connection.send(new ClientboundSetSubtitleTextPacket(
Components.literal("\u26A0 ").withStyle(depleted ? ChatFormatting.RED : ChatFormatting.GOLD)
.append(component.withStyle(ChatFormatting.GRAY))));
player.connection.send(new ClientboundSetTitleTextPacket(Components.immutableEmpty()));
}
public static int maxAir(ItemStack backtank) {
return maxAir(EnchantmentHelper.getItemEnchantmentLevel(AllEnchantments.CAPACITY.get(), backtank));
}
public static int maxAir(int enchantLevel) {
return AllConfigs.server().equipment.airInBacktank.get()
+ AllConfigs.server().equipment.enchantedBacktankCapacity.get() * enchantLevel;
}
public static int maxAirWithoutEnchants() {
return AllConfigs.server().equipment.airInBacktank.get();
}
public static boolean canAbsorbDamage(LivingEntity entity, int usesPerTank) {
if (usesPerTank == 0)
return true;
if (entity instanceof Player && ((Player) entity).isCreative())
return true;
List<ItemStack> backtanks = getAllWithAir(entity);
if (backtanks.isEmpty())
return false;
float cost = ((float) maxAirWithoutEnchants()) / usesPerTank;
consumeAir(entity, backtanks.get(0), cost);
return true;
}
// For Air-using tools
public static boolean isBarVisible(ItemStack stack, int usesPerTank) {
if (usesPerTank == 0)
return false;
Player player = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> () -> Minecraft.getInstance().player);
if (player == null)
return false;
List<ItemStack> backtanks = getAllWithAir(player);
if (backtanks.isEmpty())
return stack.isDamaged();
return true;
}
public static int getBarWidth(ItemStack stack, int usesPerTank) {
if (usesPerTank == 0)
return 13;
Player player = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> () -> Minecraft.getInstance().player);
if (player == null)
return 13;
List<ItemStack> backtanks = getAllWithAir(player);
if (backtanks.isEmpty())
return Math.round(13.0F - (float) stack.getDamageValue() / stack.getMaxDamage() * 13.0F);
if (backtanks.size() == 1)
return backtanks.get(0)
.getItem()
.getBarWidth(backtanks.get(0));
// If there is more than one backtank, average the bar widths.
int sumBarWidth = backtanks.stream()
.map(backtank -> backtank.getItem()
.getBarWidth(backtank))
.reduce(0, Integer::sum);
return Math.round((float) sumBarWidth / backtanks.size());
}
public static int getBarColor(ItemStack stack, int usesPerTank) {
if (usesPerTank == 0)
return 0;
Player player = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> () -> Minecraft.getInstance().player);
if (player == null)
return 0;
List<ItemStack> backtanks = getAllWithAir(player);
// Fallback colour
if (backtanks.isEmpty())
return Mth.hsvToRgb(Math.max(0.0F, 1.0F - (float) stack.getDamageValue() / stack.getMaxDamage()) / 3.0F,
1.0F, 1.0F);
// Just return the "first" backtank for the bar color since that's the one we are consuming from
return backtanks.get(0)
.getItem()
.getBarColor(backtanks.get(0));
}
/**
* Use this method to add custom entry points to the backtank item stack supplier, e.g. getting them from custom
* slots or items.
*/
public static void addBacktankSupplier(Function<LivingEntity, List<ItemStack>> supplier) {
BACKTANK_SUPPLIERS.add(supplier);
}
}