// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.objects; import engine.InterestManagement.WorldGrid; import engine.gameManager.BuildingManager; import engine.math.Bounds; import engine.math.FastMath; import engine.math.Vector3f; import engine.math.Vector3fImmutable; import engine.server.MBServerStatics; import java.util.ArrayList; import java.util.HashMap; public class Regions { public int room; public boolean outside; public int level; public final boolean stairs; public final boolean exit; public static HashMap FurnitureRegionMap = new HashMap<>(); public Vector3fImmutable lowLerp; public Vector3fImmutable highLerp; public Vector3f center; public float regionDistanceSquared; public ArrayList regionPoints; public int parentBuildingID; public Regions(ArrayList regionPoints, int level, int room, boolean outside, boolean exit,boolean stairs, Vector3f center, int parentBuildingID) { super(); this.level = level; this.room = room; this.outside = (outside); this.regionPoints = regionPoints; this.exit = exit; this.stairs = stairs; this.center = center; this.parentBuildingID = parentBuildingID; //order regionpoints clockwise starting from top left, and ending bottom left. ArrayList top = new ArrayList<>(); ArrayList bottom = new ArrayList<>(); for (Vector3f point : this.regionPoints){ if (point.y > center.y) top.add(point); else if (point.y < center.y) bottom.add(point); } if (top.size() == 2 && bottom.size() == 2){ Vector3f topLeft = Vector3f.min(top.get(0), top.get(1)); Vector3f topRight = Vector3f.max(top.get(0), top.get(1)); Vector3f topCenter = topLeft.lerp(topRight, .5f); Vector3f bottomLeft = Vector3f.min(bottom.get(0), bottom.get(1)); Vector3f bottomRight = Vector3f.max(bottom.get(0), bottom.get(1)); Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); this.lowLerp = new Vector3fImmutable(bottomCenter); this.highLerp = new Vector3fImmutable(topCenter); } else if (top.size() == 2 && bottom.size() == 1){ Vector3f topLeft = Vector3f.min(top.get(0), top.get(1)); Vector3f topRight = Vector3f.max(top.get(0), top.get(1)); Vector3f topCenter = topLeft.lerp(topRight, .5f); Vector3f topCopy = topRight.subtract2D(topLeft); topCopy.normalize(); float topMagnitude = topRight.subtract2D(topLeft).length(); topCopy.multLocal(topMagnitude); Vector3f bottomLeft = null; Vector3f bottomRight = null; if (bottom.get(0).distance2D(topLeft) <= bottom.get(0).distance2D(topRight)) bottomLeft = bottom.get(0); else if (bottom.get(0).distance2D(topRight) <= bottom.get(0).distance2D(topLeft)) bottomRight = bottom.get(0); //find bottom right point if (bottomLeft != null){ bottomRight = bottomLeft.add(topCopy); }else if (bottomRight != null){ bottomLeft = bottomRight.subtract(topCopy); } Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); this.lowLerp = new Vector3fImmutable(bottomCenter); this.highLerp = new Vector3fImmutable(topCenter); }else if (bottom.size() == 2 && top.size() == 1){ Vector3f topLeft = Vector3f.min(bottom.get(0), bottom.get(1)); Vector3f topRight = Vector3f.max(bottom.get(0), bottom.get(1)); Vector3f topCenter = topLeft.lerp(topRight, .5f); Vector3f topCopy = topRight.subtract2D(topLeft); topCopy.normalize(); float topMagnitude = topRight.subtract2D(topLeft).length(); topCopy.multLocal(topMagnitude); Vector3f bottomLeft = null; Vector3f bottomRight = null; if (top.get(0).distance2D(topLeft) < top.get(0).distance2D(topRight)) bottomLeft = bottom.get(0); else if (top.get(0).distance2D(topRight) < top.get(0).distance2D(topLeft)) bottomRight = bottom.get(0); //find bottom right point if (bottomLeft != null){ bottomRight = bottomLeft.add(topCopy); }else if (bottomRight != null){ bottomLeft = bottomRight.subtract(topCopy); } Vector3f bottomCenter = bottomLeft.lerp(bottomRight, .5f); this.lowLerp = new Vector3fImmutable(bottomCenter); this.highLerp = new Vector3fImmutable(topCenter); } if (this.lowLerp == null) this.lowLerp = new Vector3fImmutable(this.regionPoints.get(0)); if (this.highLerp == null){ this.highLerp = new Vector3fImmutable(this.regionPoints.get(2)); } this.regionDistanceSquared = this.lowLerp.distanceSquared2D(this.highLerp); } public int getRoom() { return room; } public boolean isOutside() { return outside; } public int getLevel() { return level; } public boolean collides(Vector3fImmutable collisionPoint){ //test if inside triangle // Regions either have 3 or 4 points if (this.regionPoints.size() == 3){ float regionArea = FastMath.area(regionPoints.get(0).x, regionPoints.get(0).z, regionPoints.get(1).x, regionPoints.get(1).z, regionPoints.get(2).x, regionPoints.get(2).z); float collisionArea1 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(0).x, regionPoints.get(0).z,regionPoints.get(1).x, regionPoints.get(1).z); float collisionArea2 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(1).x, regionPoints.get(1).z,regionPoints.get(2).x, regionPoints.get(2).z); float collisionArea3 = FastMath.area(collisionPoint.x, collisionPoint.z, regionPoints.get(0).x, regionPoints.get(0).z,regionPoints.get(2).x, regionPoints.get(2).z); if ((collisionArea1 + collisionArea2 + collisionArea3) == regionArea) return true; }else{ int i; int j; for (i = 0, j = this.regionPoints.size() - 1; i < this.regionPoints.size(); j = i++) { if ((regionPoints.get(i).z > collisionPoint.z) != (regionPoints.get(j).z > collisionPoint.z) && (collisionPoint.x < (regionPoints.get(j).x - regionPoints.get(i).x) * (collisionPoint.z - regionPoints.get(i).z) / (regionPoints.get(j).z-regionPoints.get(i).z) + regionPoints.get(i).x)) { return true; } } } return false; } public boolean isPointInPolygon( Vector3fImmutable point) { boolean inside = false; for (int i = 0, j = regionPoints.size()-1; i < regionPoints.size(); j = i++) { if (((regionPoints.get(i).z > point.z) != (regionPoints.get(j).z > point.z)) && (point.x < (regionPoints.get(j).x - regionPoints.get(i).x) * (point.z - regionPoints.get(i).z) / (regionPoints.get(j).z - regionPoints.get(i).z) + regionPoints.get(i).x)) inside = !inside; } return inside; } public static boolean CanEnterRegion(AbstractWorldObject worldObject, Regions toEnter){ if (worldObject.region == null) if (toEnter.level == 0 || toEnter.room == -1 || toEnter.exit) return true; else return false; if (worldObject.region.equals(toEnter)) return true; if (worldObject.region.level == toEnter.level) return true; //next region is stairs, if they are on the same level as stairs or 1 up, world object can enter. if (toEnter.stairs) if (worldObject.region.level == toEnter.level || toEnter.level - 1 == worldObject.region.level) return true; if (worldObject.region.stairs) { boolean movingUp = false; boolean movingDown = false; float yLerp = worldObject.region.lerpY(worldObject); if (yLerp == (worldObject.region.highLerp.y)) movingUp = true; else if (yLerp == (worldObject.region.lowLerp.y)) movingDown = true; //Stairs are always considered on the bottom floor. if (movingUp) { if (toEnter.level == worldObject.region.level + 1) return true; } else if (movingDown) if (toEnter.level == worldObject.region.level) return true; } return false; } public float lerpY (AbstractWorldObject lerper){ Vector3fImmutable lengthVector = this.highLerp.subtract2D(this.lowLerp); Vector3fImmutable characterVector = lerper.getLoc().subtract2D(this.lowLerp); float lengthVectorMagnitude = lengthVector.magnitude(); float characterVectorMagnitude = characterVector.magnitude(); float percentDistance = characterVectorMagnitude/lengthVectorMagnitude; float interpolatedY = this.lowLerp.interpolate(this.highLerp, percentDistance).y; if (interpolatedY > this.highLerp.y) interpolatedY = this.highLerp.y; else if (interpolatedY < this.lowLerp.y) interpolatedY = this.lowLerp.y; return interpolatedY; } public boolean isStairs() { return stairs; } public boolean isExit() { return exit; } public static float GetMagnitudeOfRegionSlope(Regions region){ Vector3fImmutable lengthVector = region.highLerp.subtract2D(region.lowLerp); return lengthVector.magnitude(); } public static float GetMagnitudeOfPlayerOnRegionSlope(Regions region, PlayerCharacter player){ Vector3fImmutable characterVector = player.getLoc().subtract2D(region.lowLerp); return characterVector.magnitude(); } public static float SlopeLerpPercent(PlayerCharacter player, Regions region){ float lengthVectorMagnitude = Regions.GetMagnitudeOfRegionSlope(region); float characterVectorMagnitude = Regions.GetMagnitudeOfPlayerOnRegionSlope(region, player); float percentDistance = characterVectorMagnitude/lengthVectorMagnitude * 2; return percentDistance; } public static boolean CanEnterFromOutsideBuilding(Building building,Regions region){ if (!region.outside) return false; if (region.lowLerp.y - building.getLoc().y > 1 ) return false; return true; } public static boolean CanEnterNextLevel(Regions fromRegion,Regions toRegion, AbstractWorldObject worldObject){ if (fromRegion == null) return false; if (toRegion == null) return false; // regions are the same, no need to go any further. if (fromRegion.equals(toRegion)) return true; //cant move up a level without stairs. if (!fromRegion.stairs) return false; boolean movingUp = false; Vector3fImmutable closestPoint = Vector3fImmutable.ClosestPointOnLine(fromRegion.lowLerp, fromRegion.highLerp, worldObject.getLoc()); //Closest point of a region higher than current region will always return highlerp. if (closestPoint.equals(fromRegion.highLerp)) movingUp = true; //Stairs are always considered on the bottom floor. if (movingUp){ if(toRegion.level != fromRegion.level + 1) return false; }else if (toRegion.level != fromRegion.level) return false; return true; } public static boolean IsGroundLevel(Regions region, Building building){ if (region.lowLerp.y - building.getLoc().y > 1) return false; return true; } public static Building GetBuildingForRegion(Regions region){ return BuildingManager.getBuildingFromCache(region.parentBuildingID); } public static Regions GetRegionForTeleport(Vector3fImmutable location){ Regions region = null; //Find building for (AbstractWorldObject awo:WorldGrid.getObjectsInRangePartial(location, MBServerStatics.STRUCTURE_LOAD_RANGE, MBServerStatics.MASK_BUILDING)){ Building building = (Building)awo; if (!Bounds.collide(location, building.getBounds())) continue; //find regions that intersect x and z, check if object can enter. for (Regions toEnter: building.getBounds().getRegions()){ if (toEnter.isPointInPolygon(location)){ if (region == null) region = toEnter; else // we're using a low level to high level tree structure, database not always in order low to high. //check for highest level index. if(region != null && toEnter.highLerp.y > region.highLerp.y) region = toEnter; } } } return region; } }