Public Repository for the Magicbane Shadowbane Emulator
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.

385 lines
12 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 int room;
public boolean outside;
public int level;
public final boolean stairs;
public final boolean exit;
public static HashMap<Integer,Regions> FurnitureRegionMap = new HashMap<>();
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 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;
}
}