/* * 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; } }