335 lines
12 KiB
Java
335 lines
12 KiB
Java
package net.minecraft.world.item;
|
|
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.LinkedHashMultiset;
|
|
import com.google.common.collect.Multiset;
|
|
import com.google.common.collect.Multisets;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.BiomeTags;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EquipmentSlot;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.component.MapPostProcessing;
|
|
import net.minecraft.world.item.context.UseOnContext;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.material.MapColor;
|
|
import net.minecraft.world.level.material.MapColor.Brightness;
|
|
import net.minecraft.world.level.saveddata.maps.MapId;
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class MapItem extends Item {
|
|
public static final int IMAGE_WIDTH = 128;
|
|
public static final int IMAGE_HEIGHT = 128;
|
|
|
|
public MapItem(Item.Properties properties) {
|
|
super(properties);
|
|
}
|
|
|
|
public static ItemStack create(ServerLevel level, int x, int z, byte scale, boolean trackingPosition, boolean unlimitedTracking) {
|
|
ItemStack itemStack = new ItemStack(Items.FILLED_MAP);
|
|
MapId mapId = createNewSavedData(level, x, z, scale, trackingPosition, unlimitedTracking, level.dimension());
|
|
itemStack.set(DataComponents.MAP_ID, mapId);
|
|
return itemStack;
|
|
}
|
|
|
|
@Nullable
|
|
public static MapItemSavedData getSavedData(@Nullable MapId mapId, Level level) {
|
|
return mapId == null ? null : level.getMapData(mapId);
|
|
}
|
|
|
|
@Nullable
|
|
public static MapItemSavedData getSavedData(ItemStack stack, Level level) {
|
|
MapId mapId = stack.get(DataComponents.MAP_ID);
|
|
return getSavedData(mapId, level);
|
|
}
|
|
|
|
private static MapId createNewSavedData(
|
|
ServerLevel level, int x, int z, int scale, boolean trackingPosition, boolean unlimitedTracking, ResourceKey<Level> dimension
|
|
) {
|
|
MapItemSavedData mapItemSavedData = MapItemSavedData.createFresh(x, z, (byte)scale, trackingPosition, unlimitedTracking, dimension);
|
|
MapId mapId = level.getFreeMapId();
|
|
level.setMapData(mapId, mapItemSavedData);
|
|
return mapId;
|
|
}
|
|
|
|
public void update(Level level, Entity viewer, MapItemSavedData data) {
|
|
if (level.dimension() == data.dimension && viewer instanceof Player) {
|
|
int i = 1 << data.scale;
|
|
int j = data.centerX;
|
|
int k = data.centerZ;
|
|
int l = Mth.floor(viewer.getX() - j) / i + 64;
|
|
int m = Mth.floor(viewer.getZ() - k) / i + 64;
|
|
int n = 128 / i;
|
|
if (level.dimensionType().hasCeiling()) {
|
|
n /= 2;
|
|
}
|
|
|
|
MapItemSavedData.HoldingPlayer holdingPlayer = data.getHoldingPlayer((Player)viewer);
|
|
holdingPlayer.step++;
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
BlockPos.MutableBlockPos mutableBlockPos2 = new BlockPos.MutableBlockPos();
|
|
boolean bl = false;
|
|
|
|
for (int o = l - n + 1; o < l + n; o++) {
|
|
if ((o & 15) == (holdingPlayer.step & 15) || bl) {
|
|
bl = false;
|
|
double d = 0.0;
|
|
|
|
for (int p = m - n - 1; p < m + n; p++) {
|
|
if (o >= 0 && p >= -1 && o < 128 && p < 128) {
|
|
int q = Mth.square(o - l) + Mth.square(p - m);
|
|
boolean bl2 = q > (n - 2) * (n - 2);
|
|
int r = (j / i + o - 64) * i;
|
|
int s = (k / i + p - 64) * i;
|
|
Multiset<MapColor> multiset = LinkedHashMultiset.create();
|
|
LevelChunk levelChunk = level.getChunk(SectionPos.blockToSectionCoord(r), SectionPos.blockToSectionCoord(s));
|
|
if (!levelChunk.isEmpty()) {
|
|
int t = 0;
|
|
double e = 0.0;
|
|
if (level.dimensionType().hasCeiling()) {
|
|
int u = r + s * 231871;
|
|
u = u * u * 31287121 + u * 11;
|
|
if ((u >> 20 & 1) == 0) {
|
|
multiset.add(Blocks.DIRT.defaultBlockState().getMapColor(level, BlockPos.ZERO), 10);
|
|
} else {
|
|
multiset.add(Blocks.STONE.defaultBlockState().getMapColor(level, BlockPos.ZERO), 100);
|
|
}
|
|
|
|
e = 100.0;
|
|
} else {
|
|
for (int u = 0; u < i; u++) {
|
|
for (int v = 0; v < i; v++) {
|
|
mutableBlockPos.set(r + u, 0, s + v);
|
|
int w = levelChunk.getHeight(Heightmap.Types.WORLD_SURFACE, mutableBlockPos.getX(), mutableBlockPos.getZ()) + 1;
|
|
BlockState blockState;
|
|
if (w <= level.getMinY()) {
|
|
blockState = Blocks.BEDROCK.defaultBlockState();
|
|
} else {
|
|
do {
|
|
mutableBlockPos.setY(--w);
|
|
blockState = levelChunk.getBlockState(mutableBlockPos);
|
|
} while (blockState.getMapColor(level, mutableBlockPos) == MapColor.NONE && w > level.getMinY());
|
|
|
|
if (w > level.getMinY() && !blockState.getFluidState().isEmpty()) {
|
|
int x = w - 1;
|
|
mutableBlockPos2.set(mutableBlockPos);
|
|
|
|
BlockState blockState2;
|
|
do {
|
|
mutableBlockPos2.setY(x--);
|
|
blockState2 = levelChunk.getBlockState(mutableBlockPos2);
|
|
t++;
|
|
} while (x > level.getMinY() && !blockState2.getFluidState().isEmpty());
|
|
|
|
blockState = this.getCorrectStateForFluidBlock(level, blockState, mutableBlockPos);
|
|
}
|
|
}
|
|
|
|
data.checkBanners(level, mutableBlockPos.getX(), mutableBlockPos.getZ());
|
|
e += (double)w / (i * i);
|
|
multiset.add(blockState.getMapColor(level, mutableBlockPos));
|
|
}
|
|
}
|
|
}
|
|
|
|
t /= i * i;
|
|
MapColor mapColor = Iterables.getFirst(Multisets.copyHighestCountFirst(multiset), MapColor.NONE);
|
|
Brightness brightness;
|
|
if (mapColor == MapColor.WATER) {
|
|
double f = t * 0.1 + (o + p & 1) * 0.2;
|
|
if (f < 0.5) {
|
|
brightness = Brightness.HIGH;
|
|
} else if (f > 0.9) {
|
|
brightness = Brightness.LOW;
|
|
} else {
|
|
brightness = Brightness.NORMAL;
|
|
}
|
|
} else {
|
|
double f = (e - d) * 4.0 / (i + 4) + ((o + p & 1) - 0.5) * 0.4;
|
|
if (f > 0.6) {
|
|
brightness = Brightness.HIGH;
|
|
} else if (f < -0.6) {
|
|
brightness = Brightness.LOW;
|
|
} else {
|
|
brightness = Brightness.NORMAL;
|
|
}
|
|
}
|
|
|
|
d = e;
|
|
if (p >= 0 && q < n * n && (!bl2 || (o + p & 1) != 0)) {
|
|
bl |= data.updateColor(o, p, mapColor.getPackedId(brightness));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private BlockState getCorrectStateForFluidBlock(Level level, BlockState state, BlockPos pos) {
|
|
FluidState fluidState = state.getFluidState();
|
|
return !fluidState.isEmpty() && !state.isFaceSturdy(level, pos, Direction.UP) ? fluidState.createLegacyBlock() : state;
|
|
}
|
|
|
|
private static boolean isBiomeWatery(boolean[] wateryMap, int xSample, int zSample) {
|
|
return wateryMap[zSample * 128 + xSample];
|
|
}
|
|
|
|
public static void renderBiomePreviewMap(ServerLevel serverLevel, ItemStack stack) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(stack, serverLevel);
|
|
if (mapItemSavedData != null) {
|
|
if (serverLevel.dimension() == mapItemSavedData.dimension) {
|
|
int i = 1 << mapItemSavedData.scale;
|
|
int j = mapItemSavedData.centerX;
|
|
int k = mapItemSavedData.centerZ;
|
|
boolean[] bls = new boolean[16384];
|
|
int l = j / i - 64;
|
|
int m = k / i - 64;
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
|
|
for (int n = 0; n < 128; n++) {
|
|
for (int o = 0; o < 128; o++) {
|
|
Holder<Biome> holder = serverLevel.getBiome(mutableBlockPos.set((l + o) * i, 0, (m + n) * i));
|
|
bls[n * 128 + o] = holder.is(BiomeTags.WATER_ON_MAP_OUTLINES);
|
|
}
|
|
}
|
|
|
|
for (int n = 1; n < 127; n++) {
|
|
for (int o = 1; o < 127; o++) {
|
|
int p = 0;
|
|
|
|
for (int q = -1; q < 2; q++) {
|
|
for (int r = -1; r < 2; r++) {
|
|
if ((q != 0 || r != 0) && isBiomeWatery(bls, n + q, o + r)) {
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Brightness brightness = Brightness.LOWEST;
|
|
MapColor mapColor = MapColor.NONE;
|
|
if (isBiomeWatery(bls, n, o)) {
|
|
mapColor = MapColor.COLOR_ORANGE;
|
|
if (p > 7 && o % 2 == 0) {
|
|
switch ((n + (int)(Mth.sin(o + 0.0F) * 7.0F)) / 8 % 5) {
|
|
case 0:
|
|
case 4:
|
|
brightness = Brightness.LOW;
|
|
break;
|
|
case 1:
|
|
case 3:
|
|
brightness = Brightness.NORMAL;
|
|
break;
|
|
case 2:
|
|
brightness = Brightness.HIGH;
|
|
}
|
|
} else if (p > 7) {
|
|
mapColor = MapColor.NONE;
|
|
} else if (p > 5) {
|
|
brightness = Brightness.NORMAL;
|
|
} else if (p > 3) {
|
|
brightness = Brightness.LOW;
|
|
} else if (p > 1) {
|
|
brightness = Brightness.LOW;
|
|
}
|
|
} else if (p > 0) {
|
|
mapColor = MapColor.COLOR_BROWN;
|
|
if (p > 3) {
|
|
brightness = Brightness.NORMAL;
|
|
} else {
|
|
brightness = Brightness.LOWEST;
|
|
}
|
|
}
|
|
|
|
if (mapColor != MapColor.NONE) {
|
|
mapItemSavedData.setColor(n, o, mapColor.getPackedId(brightness));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void inventoryTick(ItemStack stack, ServerLevel level, Entity entity, @Nullable EquipmentSlot slot) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(stack, level);
|
|
if (mapItemSavedData != null) {
|
|
if (entity instanceof Player player) {
|
|
mapItemSavedData.tickCarriedBy(player, stack);
|
|
}
|
|
|
|
if (!mapItemSavedData.locked && slot != null && slot.getType() == EquipmentSlot.Type.HAND) {
|
|
this.update(level, entity, mapItemSavedData);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCraftedPostProcess(ItemStack stack, Level level) {
|
|
MapPostProcessing mapPostProcessing = stack.remove(DataComponents.MAP_POST_PROCESSING);
|
|
if (mapPostProcessing != null) {
|
|
if (level instanceof ServerLevel serverLevel) {
|
|
switch (mapPostProcessing) {
|
|
case LOCK:
|
|
lockMap(stack, serverLevel);
|
|
break;
|
|
case SCALE:
|
|
scaleMap(stack, serverLevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void scaleMap(ItemStack stack, ServerLevel level) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(stack, level);
|
|
if (mapItemSavedData != null) {
|
|
MapId mapId = level.getFreeMapId();
|
|
level.setMapData(mapId, mapItemSavedData.scaled());
|
|
stack.set(DataComponents.MAP_ID, mapId);
|
|
}
|
|
}
|
|
|
|
private static void lockMap(ItemStack stack, ServerLevel level) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(stack, level);
|
|
if (mapItemSavedData != null) {
|
|
MapId mapId = level.getFreeMapId();
|
|
MapItemSavedData mapItemSavedData2 = mapItemSavedData.locked();
|
|
level.setMapData(mapId, mapItemSavedData2);
|
|
stack.set(DataComponents.MAP_ID, mapId);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public InteractionResult useOn(UseOnContext context) {
|
|
BlockState blockState = context.getLevel().getBlockState(context.getClickedPos());
|
|
if (blockState.is(BlockTags.BANNERS)) {
|
|
if (!context.getLevel().isClientSide) {
|
|
MapItemSavedData mapItemSavedData = getSavedData(context.getItemInHand(), context.getLevel());
|
|
if (mapItemSavedData != null && !mapItemSavedData.toggleBanner(context.getLevel(), context.getClickedPos())) {
|
|
return InteractionResult.FAIL;
|
|
}
|
|
}
|
|
|
|
return InteractionResult.SUCCESS;
|
|
} else {
|
|
return super.useOn(context);
|
|
}
|
|
}
|
|
}
|