/*
* This file is part of the OdinMS Maple Story Server Copyright (C) 2008 ~ 2010
* Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan
* Christian Meyer <vimes@odinms.de>
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation. You may not use, modify or distribute this
* program under any other version of the GNU Affero General Public License.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package javastory.channel.maps;
import java.awt.Point;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import com.google.common.collect.Lists;
public class FootholdTree {
private static final byte MAX_DEPTH = 8;
private FootholdTree nw = null;
private FootholdTree ne = null;
private FootholdTree sw = null;
private FootholdTree se = null;
private final List<Foothold> footholds = Lists.newLinkedList();
private final Point p1;
private final Point p2;
private final Point center;
private int depth = 0;
private int maxDropX;
private int minDropX;
public FootholdTree(final Point p1, final Point p2) {
this.p1 = p1;
this.p2 = p2;
this.center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
}
public FootholdTree(final Point p1, final Point p2, final int depth) {
this.p1 = p1;
this.p2 = p2;
this.depth = depth;
this.center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
}
public final void insert(final Foothold f) {
if (this.depth == 0) {
if (f.getX1() > this.maxDropX) {
this.maxDropX = f.getX1();
}
if (f.getX1() < this.minDropX) {
this.minDropX = f.getX1();
}
if (f.getX2() > this.maxDropX) {
this.maxDropX = f.getX2();
}
if (f.getX2() < this.minDropX) {
this.minDropX = f.getX2();
}
}
if (this.depth == MAX_DEPTH || f.getX1() >= this.p1.x && f.getX2() <= this.p2.x && f.getY1() >= this.p1.y && f.getY2() <= this.p2.y) {
this.footholds.add(f);
} else {
if (this.nw == null) {
this.nw = new FootholdTree(this.p1, this.center, this.depth + 1);
this.ne = new FootholdTree(new Point(this.center.x, this.p1.y), new Point(this.p2.x, this.center.y), this.depth + 1);
this.sw = new FootholdTree(new Point(this.p1.x, this.center.y), new Point(this.center.x, this.p2.y), this.depth + 1);
this.se = new FootholdTree(this.center, this.p2, this.depth + 1);
}
if (f.getX2() <= this.center.x && f.getY2() <= this.center.y) {
this.nw.insert(f);
} else if (f.getX1() > this.center.x && f.getY2() <= this.center.y) {
this.ne.insert(f);
} else if (f.getX2() <= this.center.x && f.getY1() > this.center.y) {
this.sw.insert(f);
} else {
this.se.insert(f);
}
}
}
private List<Foothold> getRelevants(final Point p) {
return this.getRelevants(p, new LinkedList<Foothold>());
}
private List<Foothold> getRelevants(final Point p, final List<Foothold> list) {
list.addAll(this.footholds);
if (this.nw != null) {
if (p.x <= this.center.x && p.y <= this.center.y) {
this.nw.getRelevants(p, list);
} else if (p.x > this.center.x && p.y <= this.center.y) {
this.ne.getRelevants(p, list);
} else if (p.x <= this.center.x && p.y > this.center.y) {
this.sw.getRelevants(p, list);
} else {
this.se.getRelevants(p, list);
}
}
return list;
}
private Foothold findWallR(final Point p1, final Point p2) {
Foothold ret;
for (final Foothold f : this.footholds) {
// if (f.isWall()) System.out.println(f.getX1() + " " + f.getX2());
if (f.isWall() && f.getX1() >= p1.x && f.getX1() <= p2.x && f.getY1() >= p1.y && f.getY2() <= p1.y) {
return f;
}
}
if (this.nw != null) {
if (p1.x <= this.center.x && p1.y <= this.center.y) {
ret = this.nw.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if ((p1.x > this.center.x || p2.x > this.center.x) && p1.y <= this.center.y) {
ret = this.ne.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if (p1.x <= this.center.x && p1.y > this.center.y) {
ret = this.sw.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if ((p1.x > this.center.x || p2.x > this.center.x) && p1.y > this.center.y) {
ret = this.se.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
}
return null;
}
public final Foothold findWall(final Point p1, final Point p2) {
if (p1.y != p2.y) {
throw new IllegalArgumentException();
}
return this.findWallR(p1, p2);
}
// To be refined, still inaccurate :(
public final boolean checkRelevantFH(final short fromx, final short fromy, final short tox, final short toy) {
Foothold fhdata = null;
for (final Foothold fh : this.footholds) { // From
if (fh.getX1() <= fromx && fh.getX2() >= fromx && fh.getY1() <= fromy && fh.getY2() >= fromy) { // monster pos is within
fhdata = fh;
break;
}
}
for (final Foothold fh2 : this.footholds) { // To
if (fh2.getX1() <= tox && fh2.getX2() >= tox && fh2.getY1() <= toy && fh2.getY2() >= toy) { // monster pos is within
if (!(fhdata.getId() == fh2.getId() || fh2.getId() == fhdata.getNextId() || fh2.getId() == fhdata.getPrevId())) {
System.out.println("Couldn't find the correct pos for next/prev");
return false;
}
return true;
}
}
return false;
}
public final Foothold findBelow(final Point p) {
final List<Foothold> relevants = this.getRelevants(p);
// find fhs with matching x coordinates
final List<Foothold> xMatches = Lists.newLinkedList();
for (final Foothold fh : relevants) {
if (fh.getX1() <= p.x && fh.getX2() >= p.x) {
xMatches.add(fh);
}
}
Collections.sort(xMatches);
for (final Foothold fh : xMatches) {
if (!fh.isWall() && fh.getY1() != fh.getY2()) {
int calcY;
final double s1 = Math.abs(fh.getY2() - fh.getY1());
final double s2 = Math.abs(fh.getX2() - fh.getX1());
final double s4 = Math.abs(p.x - fh.getX1());
final double alpha = Math.atan(s2 / s1);
final double beta = Math.atan(s1 / s2);
final double s5 = Math.cos(alpha) * (s4 / Math.cos(beta));
if (fh.getY2() < fh.getY1()) {
calcY = fh.getY1() - (int) s5;
} else {
calcY = fh.getY1() + (int) s5;
}
if (calcY >= p.y) {
return fh;
}
} else if (!fh.isWall()) {
if (fh.getY1() >= p.y) {
return fh;
}
}
}
return null;
}
public final int getX1() {
return this.p1.x;
}
public final int getX2() {
return this.p2.x;
}
public final int getY1() {
return this.p1.y;
}
public final int getY2() {
return this.p2.y;
}
public final int getMaxDropX() {
return this.maxDropX;
}
public final int getMinDropX() {
return this.minDropX;
}
}