310 lines
15 KiB
Java
310 lines
15 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*
|
|
* Could not load the following classes:
|
|
* com.google.common.collect.Iterables
|
|
* com.google.common.collect.LinkedHashMultiset
|
|
* com.google.common.collect.Multiset
|
|
* com.google.common.collect.Multisets
|
|
* org.jspecify.annotations.Nullable
|
|
*/
|
|
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.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
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.saveddata.maps.MapId;
|
|
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
|
|
import org.jspecify.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 originX, int originZ, byte scale, boolean trackPosition, boolean unlimitedTracking) {
|
|
ItemStack map = new ItemStack(Items.FILLED_MAP);
|
|
MapId newId = MapItem.createNewSavedData(level, originX, originZ, scale, trackPosition, unlimitedTracking, level.dimension());
|
|
map.set(DataComponents.MAP_ID, newId);
|
|
return map;
|
|
}
|
|
|
|
public static @Nullable MapItemSavedData getSavedData(@Nullable MapId id, Level level) {
|
|
return id == null ? null : level.getMapData(id);
|
|
}
|
|
|
|
public static @Nullable MapItemSavedData getSavedData(ItemStack itemStack, Level level) {
|
|
MapId id = itemStack.get(DataComponents.MAP_ID);
|
|
return MapItem.getSavedData(id, level);
|
|
}
|
|
|
|
private static MapId createNewSavedData(ServerLevel level, int xSpawn, int zSpawn, int scale, boolean trackingPosition, boolean unlimitedTracking, ResourceKey<Level> dimension) {
|
|
MapItemSavedData newData = MapItemSavedData.createFresh(xSpawn, zSpawn, (byte)scale, trackingPosition, unlimitedTracking, dimension);
|
|
MapId id = level.getFreeMapId();
|
|
level.setMapData(id, newData);
|
|
return id;
|
|
}
|
|
|
|
public void update(Level level, Entity player, MapItemSavedData data) {
|
|
if (level.dimension() != data.dimension || !(player instanceof Player)) {
|
|
return;
|
|
}
|
|
int scale = 1 << data.scale;
|
|
int centerX = data.centerX;
|
|
int centerZ = data.centerZ;
|
|
int playerImgX = Mth.floor(player.getX() - (double)centerX) / scale + 64;
|
|
int playerImgY = Mth.floor(player.getZ() - (double)centerZ) / scale + 64;
|
|
int radius = 128 / scale;
|
|
if (level.dimensionType().hasCeiling()) {
|
|
radius /= 2;
|
|
}
|
|
MapItemSavedData.HoldingPlayer holdingPlayer = data.getHoldingPlayer((Player)player);
|
|
++holdingPlayer.step;
|
|
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
|
|
BlockPos.MutableBlockPos belowPos = new BlockPos.MutableBlockPos();
|
|
boolean foundConsecutiveChanges = false;
|
|
for (int imgX = playerImgX - radius + 1; imgX < playerImgX + radius; ++imgX) {
|
|
if ((imgX & 0xF) != (holdingPlayer.step & 0xF) && !foundConsecutiveChanges) continue;
|
|
foundConsecutiveChanges = false;
|
|
double previousAverageAreaHeight = 0.0;
|
|
for (int imgY = playerImgY - radius - 1; imgY < playerImgY + radius; ++imgY) {
|
|
double diff;
|
|
if (imgX < 0 || imgY < -1 || imgX >= 128 || imgY >= 128) continue;
|
|
int distanceToPlayerSqr = Mth.square(imgX - playerImgX) + Mth.square(imgY - playerImgY);
|
|
boolean ditherBlack = distanceToPlayerSqr > (radius - 2) * (radius - 2);
|
|
int averagingAreaMinX = (centerX / scale + imgX - 64) * scale;
|
|
int averagingAreaMinZ = (centerZ / scale + imgY - 64) * scale;
|
|
LinkedHashMultiset colorCount = LinkedHashMultiset.create();
|
|
LevelChunk chunk = level.getChunk(SectionPos.blockToSectionCoord(averagingAreaMinX), SectionPos.blockToSectionCoord(averagingAreaMinZ));
|
|
if (chunk.isEmpty()) continue;
|
|
int waterDepth = 0;
|
|
double averageAreaHeight = 0.0;
|
|
if (level.dimensionType().hasCeiling()) {
|
|
int ceilingNoise = averagingAreaMinX + averagingAreaMinZ * 231871;
|
|
if (((ceilingNoise = ceilingNoise * ceilingNoise * 31287121 + ceilingNoise * 11) >> 20 & 1) == 0) {
|
|
colorCount.add((Object)Blocks.DIRT.defaultBlockState().getMapColor(level, BlockPos.ZERO), 10);
|
|
} else {
|
|
colorCount.add((Object)Blocks.STONE.defaultBlockState().getMapColor(level, BlockPos.ZERO), 100);
|
|
}
|
|
averageAreaHeight = 100.0;
|
|
} else {
|
|
for (int averagingAreaDeltaX = 0; averagingAreaDeltaX < scale; ++averagingAreaDeltaX) {
|
|
for (int averagingAreaDeltaZ = 0; averagingAreaDeltaZ < scale; ++averagingAreaDeltaZ) {
|
|
BlockState state;
|
|
blockPos.set(averagingAreaMinX + averagingAreaDeltaX, 0, averagingAreaMinZ + averagingAreaDeltaZ);
|
|
int columnY = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, blockPos.getX(), blockPos.getZ()) + 1;
|
|
if (columnY > level.getMinY()) {
|
|
do {
|
|
blockPos.setY(--columnY);
|
|
} while ((state = chunk.getBlockState(blockPos)).getMapColor(level, blockPos) == MapColor.NONE && columnY > level.getMinY());
|
|
if (columnY > level.getMinY() && !state.getFluidState().isEmpty()) {
|
|
BlockState belowBlock;
|
|
int solidY = columnY - 1;
|
|
belowPos.set(blockPos);
|
|
do {
|
|
belowPos.setY(solidY--);
|
|
belowBlock = chunk.getBlockState(belowPos);
|
|
++waterDepth;
|
|
} while (solidY > level.getMinY() && !belowBlock.getFluidState().isEmpty());
|
|
state = this.getCorrectStateForFluidBlock(level, state, blockPos);
|
|
}
|
|
} else {
|
|
state = Blocks.BEDROCK.defaultBlockState();
|
|
}
|
|
data.checkBanners(level, blockPos.getX(), blockPos.getZ());
|
|
averageAreaHeight += (double)columnY / (double)(scale * scale);
|
|
colorCount.add((Object)state.getMapColor(level, blockPos));
|
|
}
|
|
}
|
|
}
|
|
MapColor color = (MapColor)Iterables.getFirst((Iterable)Multisets.copyHighestCountFirst((Multiset)colorCount), (Object)MapColor.NONE);
|
|
MapColor.Brightness brightness = color == MapColor.WATER ? ((diff = (double)(waterDepth /= scale * scale) * 0.1 + (double)(imgX + imgY & 1) * 0.2) < 0.5 ? MapColor.Brightness.HIGH : (diff > 0.9 ? MapColor.Brightness.LOW : MapColor.Brightness.NORMAL)) : ((diff = (averageAreaHeight - previousAverageAreaHeight) * 4.0 / (double)(scale + 4) + ((double)(imgX + imgY & 1) - 0.5) * 0.4) > 0.6 ? MapColor.Brightness.HIGH : (diff < -0.6 ? MapColor.Brightness.LOW : MapColor.Brightness.NORMAL));
|
|
previousAverageAreaHeight = averageAreaHeight;
|
|
if (imgY < 0 || distanceToPlayerSqr >= radius * radius || ditherBlack && (imgX + imgY & 1) == 0) continue;
|
|
foundConsecutiveChanges |= data.updateColor(imgX, imgY, color.getPackedId(brightness));
|
|
}
|
|
}
|
|
}
|
|
|
|
private BlockState getCorrectStateForFluidBlock(Level level, BlockState state, BlockPos pos) {
|
|
FluidState fluidState = state.getFluidState();
|
|
if (!fluidState.isEmpty() && !state.isFaceSturdy(level, pos, Direction.UP)) {
|
|
return fluidState.createLegacyBlock();
|
|
}
|
|
return state;
|
|
}
|
|
|
|
private static boolean isBiomeWatery(boolean[] isBiomeWatery, int x, int z) {
|
|
return isBiomeWatery[z * 128 + x];
|
|
}
|
|
|
|
public static void renderBiomePreviewMap(ServerLevel level, ItemStack mapItemStack) {
|
|
MapItemSavedData data = MapItem.getSavedData(mapItemStack, (Level)level);
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
if (level.dimension() != data.dimension) {
|
|
return;
|
|
}
|
|
int scale = 1 << data.scale;
|
|
int centerX = data.centerX;
|
|
int centerZ = data.centerZ;
|
|
boolean[] isBiomeWatery = new boolean[16384];
|
|
int unscaledStartX = centerX / scale - 64;
|
|
int unscaledStartZ = centerZ / scale - 64;
|
|
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
|
for (int row = 0; row < 128; ++row) {
|
|
for (int column = 0; column < 128; ++column) {
|
|
Holder<Biome> biome = level.getBiome(pos.set((unscaledStartX + column) * scale, 0, (unscaledStartZ + row) * scale));
|
|
isBiomeWatery[row * 128 + column] = biome.is(BiomeTags.WATER_ON_MAP_OUTLINES);
|
|
}
|
|
}
|
|
for (int mx = 1; mx < 127; ++mx) {
|
|
for (int mz = 1; mz < 127; ++mz) {
|
|
int waterCount = 0;
|
|
for (int dx = -1; dx < 2; ++dx) {
|
|
for (int dz = -1; dz < 2; ++dz) {
|
|
if (dx == 0 && dz == 0 || !MapItem.isBiomeWatery(isBiomeWatery, mx + dx, mz + dz)) continue;
|
|
++waterCount;
|
|
}
|
|
}
|
|
MapColor.Brightness brightness = MapColor.Brightness.LOWEST;
|
|
MapColor newColor = MapColor.NONE;
|
|
if (MapItem.isBiomeWatery(isBiomeWatery, mx, mz)) {
|
|
newColor = MapColor.COLOR_ORANGE;
|
|
if (waterCount > 7 && mz % 2 == 0) {
|
|
switch ((mx + (int)(Mth.sin((float)mz + 0.0f) * 7.0f)) / 8 % 5) {
|
|
case 0:
|
|
case 4: {
|
|
brightness = MapColor.Brightness.LOW;
|
|
break;
|
|
}
|
|
case 1:
|
|
case 3: {
|
|
brightness = MapColor.Brightness.NORMAL;
|
|
break;
|
|
}
|
|
case 2: {
|
|
brightness = MapColor.Brightness.HIGH;
|
|
}
|
|
}
|
|
} else if (waterCount > 7) {
|
|
newColor = MapColor.NONE;
|
|
} else if (waterCount > 5) {
|
|
brightness = MapColor.Brightness.NORMAL;
|
|
} else if (waterCount > 3) {
|
|
brightness = MapColor.Brightness.LOW;
|
|
} else if (waterCount > 1) {
|
|
brightness = MapColor.Brightness.LOW;
|
|
}
|
|
} else if (waterCount > 0) {
|
|
newColor = MapColor.COLOR_BROWN;
|
|
brightness = waterCount > 3 ? MapColor.Brightness.NORMAL : MapColor.Brightness.LOWEST;
|
|
}
|
|
if (newColor == MapColor.NONE) continue;
|
|
data.setColor(mx, mz, newColor.getPackedId(brightness));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void inventoryTick(ItemStack itemStack, ServerLevel level, Entity owner, @Nullable EquipmentSlot slot) {
|
|
MapItemSavedData data = MapItem.getSavedData(itemStack, (Level)level);
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
if (owner instanceof Player) {
|
|
Player player = (Player)owner;
|
|
data.tickCarriedBy(player, itemStack);
|
|
}
|
|
if (!data.locked && slot != null && slot.getType() == EquipmentSlot.Type.HAND) {
|
|
this.update(level, owner, data);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCraftedPostProcess(ItemStack itemStack, Level level) {
|
|
MapPostProcessing postProcessing = itemStack.remove(DataComponents.MAP_POST_PROCESSING);
|
|
if (postProcessing == null) {
|
|
return;
|
|
}
|
|
if (level instanceof ServerLevel) {
|
|
ServerLevel serverLevel = (ServerLevel)level;
|
|
switch (postProcessing) {
|
|
case LOCK: {
|
|
MapItem.lockMap(itemStack, serverLevel);
|
|
break;
|
|
}
|
|
case SCALE: {
|
|
MapItem.scaleMap(itemStack, serverLevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void scaleMap(ItemStack itemStack, ServerLevel level) {
|
|
MapItemSavedData original = MapItem.getSavedData(itemStack, (Level)level);
|
|
if (original != null) {
|
|
MapId id = level.getFreeMapId();
|
|
level.setMapData(id, original.scaled());
|
|
itemStack.set(DataComponents.MAP_ID, id);
|
|
}
|
|
}
|
|
|
|
private static void lockMap(ItemStack map, ServerLevel level) {
|
|
MapItemSavedData mapData = MapItem.getSavedData(map, (Level)level);
|
|
if (mapData != null) {
|
|
MapId id = level.getFreeMapId();
|
|
MapItemSavedData newData = mapData.locked();
|
|
level.setMapData(id, newData);
|
|
map.set(DataComponents.MAP_ID, id);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public InteractionResult useOn(UseOnContext context) {
|
|
BlockState clicked = context.getLevel().getBlockState(context.getClickedPos());
|
|
if (clicked.is(BlockTags.BANNERS)) {
|
|
MapItemSavedData data;
|
|
if (!context.getLevel().isClientSide() && (data = MapItem.getSavedData(context.getItemInHand(), context.getLevel())) != null && !data.toggleBanner(context.getLevel(), context.getClickedPos())) {
|
|
return InteractionResult.FAIL;
|
|
}
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
return super.useOn(context);
|
|
}
|
|
}
|
|
|