package net.shadowfacts.simplemultipart.util; import com.google.common.collect.ImmutableMap; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.state.PropertyContainer; import net.minecraft.state.StateFactory; import net.minecraft.state.property.Property; import net.minecraft.util.BlockHitResult; import net.minecraft.util.HitResult; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.shape.VoxelShape; import net.minecraft.world.BlockView; import net.minecraft.world.World; import net.shadowfacts.simplemultipart.SimpleMultipart; import net.shadowfacts.simplemultipart.container.MultipartContainer; import net.shadowfacts.simplemultipart.multipart.Multipart; import net.shadowfacts.simplemultipart.multipart.MultipartState; import java.util.*; /** * Helper methods relating to multiparts. * * @author shadowfacts * @since 0.1.0 */ public class MultipartHelper { /** * Performs a ray trace to determine the hit multipart. * * @param container The container being ray traced in. * @param world The world. * @param pos The position of the container. * @param player The player performing the ray trace. * @return A hit result for the hit multipart. {@ode null} if not part was hit. */ public static MultipartHitResult rayTrace(MultipartContainer container, BlockView world, BlockPos pos, PlayerEntity player) { // copied from BoatItem::use float var6 = MathHelper.lerp(1.0F, player.prevPitch, player.pitch); float var7 = MathHelper.lerp(1.0F, player.prevYaw, player.yaw); double var8 = MathHelper.lerp(1.0D, player.prevX, player.x); double var10 = MathHelper.lerp(1.0D, player.prevY, player.y) + (double)player.getEyeHeight(); double var12 = MathHelper.lerp(1.0D, player.prevZ, player.z); Vec3d start = new Vec3d(var8, var10, var12); float var15 = MathHelper.cos(-var7 * 0.017453292F - 3.1415927F); float var16 = MathHelper.sin(-var7 * 0.017453292F - 3.1415927F); float var17 = -MathHelper.cos(-var6 * 0.017453292F); float var18 = MathHelper.sin(-var6 * 0.017453292F); float var19 = var16 * var17; float var21 = var15 * var17; Vec3d end = start.add((double)var19 * 5.0D, (double)var18 * 5.0D, (double)var21 * 5.0D); return rayTrace(container, world, pos, start, end); } /** * Performs a ray trace to determine the hit multipart. * * @param container The container being ray traced in. * @param world The world. * @param pos The position of the container. * @param start The start position for the ray. * @param end The end position for the ray. * @return A hit result for the hit multipart. {@code null} if no part was hit. */ public static MultipartHitResult rayTrace(MultipartContainer container, BlockView world, BlockPos pos, Vec3d start, Vec3d end) { return container.getParts().stream() .map(view -> { VoxelShape shape = view.getState().getBoundingShape(view); BlockHitResult result = shape.rayTrace(start, end, pos); return result == null ? null : new MultipartHitResult(result, view); }) .filter(Objects::nonNull) .min(Comparator.comparingDouble(hit -> hit.getPos().subtract(start).lengthSquared())) .orElse(null); } /** * Creates a NBT representation of the given multipart state. * * @param state The multipart state. * @return A compound tag containing the multipart and it's property values. */ public static CompoundTag serializeMultipartState(MultipartState state) { CompoundTag tag = new CompoundTag(); tag.putString("Name", SimpleMultipart.MULTIPART.getId(state.getMultipart()).toString()); ImmutableMap, Comparable> propertyMap = state.getEntries(); if (!propertyMap.isEmpty()) { CompoundTag propertyTag = new CompoundTag(); for (Map.Entry, Comparable> e : propertyMap.entrySet()) { Property property = e.getKey(); String str = getValueAsString(state, property); propertyTag.putString(property.getName(), str); } tag.put("Properties", propertyTag); } return tag; } private static , T extends Comparable> String getValueAsString(C state, Property property) { return property.getValueAsString(state.get(property)); } /** * Retrieves the multipart state specified by the given tag. * * @param tag A tag containing the multipart information. * Generated by {@link MultipartHelper#serializeMultipartState(MultipartState)} * @return The multipart state represented by the tag. */ public static MultipartState deserializeMultipartState(CompoundTag tag) { if (!tag.containsKey("Name", 8)) { return null; } else { Multipart part = SimpleMultipart.MULTIPART.get(new Identifier(tag.getString("Name"))); MultipartState state = part.getDefaultState(); if (tag.containsKey("Properties", 10)) { CompoundTag propertyTag = tag.getCompound("Properties"); StateFactory stateFactory = part.getStateFactory(); for (String propertyName : propertyTag.getKeys()) { Property property = stateFactory.getProperty(propertyName); if (property != null) { String valueStr = propertyTag.getString(propertyName); state = withProperty(state, property, valueStr); } } } return state; } } private static , T extends Comparable> C withProperty(C state, Property property, String valueString) { Optional value = property.getValue(valueString); if (!value.isPresent()) { // TODO: logging return state; } return state.with(property, value.get()); } }