package com.hypixel.hytale.builtin.portals.ui;
import com.hypixel.hytale.builtin.portals.utils.posqueries.generators.SearchCircular;
import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.FitsAPortal;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalSpawn;
import com.hypixel.hytale.server.core.modules.collision.WorldUtil;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class PortalSpawnFinder {
public PortalSpawnFinder() {
}
@Nullable
public static Transform computeSpawnTransform(World world, PortalSpawn config) {
Vector3d spawn = findSpawnByThrowingDarts(world, config);
if (spawn == null) {
spawn = findFallbackPositionOnGround(world, config);
HytaleLogger.getLogger().at(Level.INFO).log("Had to use fallback spawn for portal spawn");
}
if (spawn == null) {
HytaleLogger.getLogger().at(Level.INFO).log("Both dart and fallback spawn finder failed for portal spawn");
return null;
} else {
Vector3f direction = Vector3f.lookAt(spawn).scale(-1.0F);
direction.setPitch(0.0F);
direction.setRoll(0.0F);
return new Transform(spawn.clone().add(0.0, 0.5, 0.0), direction);
}
}
@Nullable
private static Vector3d findSpawnByThrowingDarts(World world, PortalSpawn config) {
Vector3d center = config.getCenter().toVector3d();
center.setY((double)config.getCheckSpawnY());
int halfwayThrows = config.getChunkDartThrows() / 2;
for(int chunkDart = 0; chunkDart < config.getChunkDartThrows(); ++chunkDart) {
Vector3d pointd = (Vector3d)(new SearchCircular((double)config.getMinRadius(), (double)config.getMaxRadius(), 1)).execute(world, center).orElse((Object)null);
if (pointd != null) {
Vector3i point = pointd.toVector3i();
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(point.x, point.z));
BlockType firstBlock = chunk.getBlockType(point.x, point.y, point.z);
if (firstBlock != null) {
BlockMaterial firstBlockMat = firstBlock.getMaterial();
if (firstBlockMat != BlockMaterial.Solid) {
boolean checkIfPortalFitsNice = chunkDart < halfwayThrows;
Vector3d spawn = findGroundWithinChunk(chunk, config, checkIfPortalFitsNice);
if (spawn != null) {
HytaleLogger.Api var10000 = HytaleLogger.getLogger().at(Level.INFO);
String var10001 = String.valueOf(spawn);
var10000.log("Found fragment spawn at " + var10001 + " after " + (chunkDart + 1) + " chunk scan(s)");
return spawn;
}
}
}
}
}
return null;
}
@Nullable
private static Vector3d findGroundWithinChunk(WorldChunk chunk, PortalSpawn config, boolean checkIfPortalFitsNice) {
int chunkBlockX = ChunkUtil.minBlock(chunk.getX());
int chunkBlockZ = ChunkUtil.minBlock(chunk.getZ());
ThreadLocalRandom rand = ThreadLocalRandom.current();
for(int i = 0; i < config.getChecksPerChunk(); ++i) {
int x = chunkBlockX + rand.nextInt(2, 14);
int z = chunkBlockZ + rand.nextInt(2, 14);
Vector3d point = findWithGroundBelow(chunk, x, config.getCheckSpawnY(), z, config.getScanHeight(), false);
if (point != null && (!checkIfPortalFitsNice || FitsAPortal.check(chunk.getWorld(), point))) {
return point;
}
}
return null;
}
@Nullable
private static Vector3d findWithGroundBelow(WorldChunk chunk, int x, int y, int z, int scanHeight, boolean fluidsAreAcceptable) {
World world = chunk.getWorld();
ChunkStore chunkStore = world.getChunkStore();
Ref<ChunkStore> chunkRef = chunk.getReference();
Store<ChunkStore> chunkStoreAccessor = chunkStore.getStore();
ChunkColumn chunkColumnComponent = (ChunkColumn)chunkStoreAccessor.getComponent(chunkRef, ChunkColumn.getComponentType());
BlockChunk blockChunkComponent = (BlockChunk)chunkStoreAccessor.getComponent(chunkRef, BlockChunk.getComponentType());
for(int dy = 0; dy < scanHeight; ++dy) {
Material selfMat = getMaterial(chunkStoreAccessor, chunkColumnComponent, blockChunkComponent, (double)x, (double)(y - dy), (double)z);
Material belowMat = getMaterial(chunkStoreAccessor, chunkColumnComponent, blockChunkComponent, (double)x, (double)(y - dy - 1), (double)z);
boolean selfValid = selfMat == PortalSpawnFinder.Material.AIR || fluidsAreAcceptable && selfMat == PortalSpawnFinder.Material.FLUID;
if (!selfValid) {
break;
}
if (belowMat == PortalSpawnFinder.Material.SOLID) {
return new Vector3d((double)x, (double)(y - dy), (double)z);
}
}
return null;
}
private static Material getMaterial(@Nonnull ComponentAccessor<ChunkStore> chunkStore, @Nonnull ChunkColumn chunkColumnComponent, @Nonnull BlockChunk blockChunkComponent, double x, double y, double z) {
int blockX = (int)x;
int blockY = (int)y;
int blockZ = (int)z;
int fluidId = WorldUtil.getFluidIdAtPosition(chunkStore, chunkColumnComponent, blockX, blockY, blockZ);
if (fluidId != 0) {
return PortalSpawnFinder.Material.FLUID;
} else {
BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(blockY);
int blockId = blockSection.get(blockX, blockY, blockZ);
BlockType blockType = (BlockType)BlockType.getAssetMap().getAsset(blockId);
if (blockType == null) {
return PortalSpawnFinder.Material.UNKNOWN;
} else {
Material var10000;
switch (blockType.getMaterial()) {
case Solid -> var10000 = PortalSpawnFinder.Material.SOLID;
case Empty -> var10000 = PortalSpawnFinder.Material.AIR;
default -> throw new MatchException((String)null, (Throwable)null);
}
return var10000;
}
}
}
@Nullable
private static Vector3d findFallbackPositionOnGround(World world, PortalSpawn config) {
Vector3i center = config.getCenter();
WorldChunk centerChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(center.x, center.z));
return findWithGroundBelow(centerChunk, 0, 319, 0, 319, true);
}
private static enum Material {
SOLID,
FLUID,
AIR,
UNKNOWN;
private Material() {
}
}
}