You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
14 KiB
376 lines
14 KiB
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . |
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· |
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ |
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ |
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ |
|
// 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 static HashMap<Integer, Regions> FurnitureRegionMap = new HashMap<>(); |
|
public final boolean stairs; |
|
public final boolean exit; |
|
public int room; |
|
public boolean outside; |
|
public int level; |
|
public Vector3fImmutable lowLerp; |
|
public Vector3fImmutable highLerp; |
|
public Vector3f center; |
|
public float regionDistanceSquared; |
|
|
|
public ArrayList<Vector3f> regionPoints; |
|
|
|
public int parentBuildingID; |
|
|
|
|
|
public Regions(ArrayList<Vector3f> 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<Vector3f> top = new ArrayList<>(); |
|
ArrayList<Vector3f> 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 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 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, 128, MBServerStatics.MASK_BUILDING)) { |
|
Building building = (Building) awo; |
|
if (!Bounds.collide(location, building.getBounds())) |
|
continue; |
|
if(building != null) { |
|
region = BuildingManager.GetRegion(building, location.x, location.y, location.z); |
|
} |
|
//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; |
|
} |
|
|
|
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 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; |
|
} |
|
}
|
|
|