/* * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod * Copyright (C) 2021 cyoung06 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package kr.syeyoung.dungeonsguide.dungeon.roomfinder; import com.google.common.collect.Sets; import kr.syeyoung.dungeonsguide.dungeon.DungeonContext; import kr.syeyoung.dungeonsguide.dungeon.MapProcessor; import kr.syeyoung.dungeonsguide.dungeon.data.DungeonRoomInfo; import kr.syeyoung.dungeonsguide.dungeon.doorfinder.DungeonDoor; import kr.syeyoung.dungeonsguide.dungeon.events.DungeonStateChangeEvent; import kr.syeyoung.dungeonsguide.dungeon.mechanics.DungeonMechanic; import kr.syeyoung.dungeonsguide.dungeon.mechanics.DungeonRoomDoor; import kr.syeyoung.dungeonsguide.pathfinding.NodeProcessorDungeonRoom; import kr.syeyoung.dungeonsguide.roomedit.EditingContext; import kr.syeyoung.dungeonsguide.roomprocessor.ProcessorFactory; import kr.syeyoung.dungeonsguide.roomprocessor.RoomProcessor; import kr.syeyoung.dungeonsguide.roomprocessor.RoomProcessorGenerator; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.pathfinding.PathEntity; import net.minecraft.pathfinding.PathFinder; import net.minecraft.pathfinding.PathPoint; import net.minecraft.util.BlockPos; import net.minecraft.world.IBlockAccess; import javax.vecmath.Vector2d; import java.awt.*; import java.util.*; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @Getter public class DungeonRoom { private final List unitPoints; private final short shape; private final byte color; private final BlockPos min; private final BlockPos max; private final Point minRoomPt; private final DungeonContext context; private final List doors = new ArrayList(); private DungeonRoomInfo dungeonRoomInfo; private final int unitWidth; // X private final int unitHeight; // Z @Setter private int totalSecrets = -1; private RoomState currentState = RoomState.DISCOVERED; private Map cached = null; public Map getMechanics() { if (cached == null || EditingContext.getEditingContext() != null) { cached = new HashMap(dungeonRoomInfo.getMechanics()); int index = 0; for (DungeonDoor door : doors) { if (door.isExist()) cached.put((door.isRequiresKey() ? "withergate" : "gate")+"-"+(++index), new DungeonRoomDoor(door)); } } return cached; } public void setCurrentState(RoomState currentState) { context.createEvent(new DungeonStateChangeEvent(unitPoints.get(0), dungeonRoomInfo.getName(), this.currentState, currentState)); this.currentState = currentState; } @Getter private final PathFinder pathFinder; public ScheduledFuture> createEntityPathTo(IBlockAccess blockaccess, Entity entityIn, BlockPos targetPos, float dist) { return asyncPathFinder.schedule(() -> { PathEntity latest = pathFinder.createEntityPathTo(blockaccess, entityIn, targetPos, dist); if (latest != null) { List poses = new ArrayList<>(); for (int i = 0; i < latest.getCurrentPathLength(); i++) { PathPoint pathPoint = latest.getPathPointFromIndex(i); poses.add(getMin().add(pathPoint.xCoord, pathPoint.yCoord, pathPoint.zCoord)); } return poses; } return new ArrayList<>(); }, 0, TimeUnit.MILLISECONDS); } private static final ScheduledExecutorService asyncPathFinder = Executors.newScheduledThreadPool(2); @Getter private final NodeProcessorDungeonRoom nodeProcessorDungeonRoom; @Getter private final Map roomContext = new HashMap(); @AllArgsConstructor @Getter public enum RoomState { DISCOVERED(0), COMPLETE_WITHOUT_SECRETS(0), FINISHED(0), FAILED(-14); private final int scoreModifier; } private RoomProcessor roomProcessor; public DungeonRoom(List points, short shape, byte color, BlockPos min, BlockPos max, DungeonContext context) { this.unitPoints = points; this.shape = shape; this.color = color; this.min = min; this.max = max; this.context = context; minRoomPt = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); for (Point pt : unitPoints) { if (pt.x < minRoomPt.x) minRoomPt.x = pt.x; if (pt.y < minRoomPt.y) minRoomPt.y = pt.y; } unitWidth = (int) Math.ceil(max.getX() - min.getX() / 32.0); unitHeight = (int) Math.ceil(max.getZ() - min.getZ() / 32.0); buildDoors(); buildRoom(); nodeProcessorDungeonRoom = new NodeProcessorDungeonRoom(this); pathFinder = new PathFinder(nodeProcessorDungeonRoom); updateRoomProcessor(); } private static final Set directions = Sets.newHashSet(new Vector2d(0,16), new Vector2d(0, -16), new Vector2d(16, 0), new Vector2d(-16 , 0)); private void buildDoors() { Set positions = new HashSet(); for (Point p:unitPoints) { BlockPos pos = context.getMapProcessor().roomPointToWorldPoint(p).add(16,0,16); for (Vector2d vector2d : directions){ BlockPos doorLoc = pos.add(vector2d.x, 0, vector2d.y); if (positions.contains(doorLoc)) positions.remove(doorLoc); else positions.add(doorLoc); } } for (BlockPos door : positions) { doors.add(new DungeonDoor(context.getWorld(), door)); } } private RoomMatcher roomMatcher = null; private void buildRoom() { if (roomMatcher == null) roomMatcher = new RoomMatcher(this); DungeonRoomInfo dungeonRoomInfo = roomMatcher.match(); if (dungeonRoomInfo == null) { dungeonRoomInfo = roomMatcher.createNew(); if (color == 18) dungeonRoomInfo.setProcessorId("bossroom"); } this.dungeonRoomInfo = dungeonRoomInfo; totalSecrets = dungeonRoomInfo.getTotalSecrets(); } public void updateRoomProcessor() { RoomProcessorGenerator roomProcessorGenerator = ProcessorFactory.getRoomProcessorGenerator(dungeonRoomInfo.getProcessorId()); if (roomProcessorGenerator == null) this.roomProcessor = null; else this.roomProcessor = roomProcessorGenerator.createNew(this); } public Block getAbsoluteBlockAt(int x, int y, int z) { // validate x y z's BlockPos pos = new BlockPos(x,y,z); if (canAccessAbsolute(pos)) { return this.context.getWorld().getChunkFromBlockCoords(pos).getBlock(pos); } return null; } public Block getRelativeBlockAt(int x, int y, int z) { // validate x y z's if (canAccessRelative(x,z)) { BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ()); return this.context.getWorld().getChunkFromBlockCoords(pos).getBlock(pos); } return null; } public BlockPos getRelativeBlockPosAt(int x, int y, int z) { BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ()); return pos; } public int getRelativeBlockDataAt(int x, int y, int z) { // validate x y z's if (canAccessRelative(x,z)) { BlockPos pos = new BlockPos(x,y,z).add(min.getX(),min.getY(),min.getZ()); return this.context.getWorld().getChunkFromBlockCoords(pos).getBlockMetadata(pos); } return -1; } public int getAbsoluteBlockDataAt(int x, int y, int z) { // validate x y z's BlockPos pos = new BlockPos(x,y,z); if (canAccessAbsolute(pos)) { return this.context.getWorld().getChunkFromBlockCoords(pos).getBlockMetadata(pos); } return -1; } public boolean canAccessAbsolute(BlockPos pos) { MapProcessor mapProcessor = this.context.getMapProcessor(); Point roomPt = mapProcessor.worldPointToRoomPoint(pos); roomPt.translate(-minRoomPt.x, -minRoomPt.y); return (shape >>(roomPt.y *4 +roomPt.x) & 0x1) > 0; } public boolean canAccessRelative(int x, int z) { return x>= 0 && z >= 0 && (shape >>((z/32) *4 +(x/32)) & 0x1) > 0; } }