/* * This file is part of the Haven & Hearth game client. * Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and * Björn Johannessen <johannessen.bjorn@gmail.com> * * Redistribution and/or modification of this file is subject to the * terms of the GNU Lesser General Public License, version 3, as * published by the Free Software Foundation. * * 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 General Public License for more details. * * Other parts of this source tree adhere to other copying * rights. Please see the file `COPYING' in the root directory of the * source tree for details. * * A copy the GNU Lesser General Public License is distributed along * with the source tree of which this file is a part in the file * `doc/LPGL-3'. If it is missing for any reason, please see the Free * Software Foundation's website at <http://www.fsf.org/>, or write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA */ package haven; import static haven.MCache.cmaps; import static haven.MCache.tilesz; import haven.MCache.Grid; import haven.MCache.Overlay; import haven.Resource.Tile; import java.awt.Color; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import ender.HLInfo; public class MapView extends Widget implements DTarget, Console.Directory { static Color[] olc = new Color[31]; static Map<String, Class<? extends Camera>> camtypes = new HashMap<String, Class<? extends Camera>>(); public Coord mc, mousepos, pmousepos; Camera cam; Sprite.Part[] clickable = {}; List<Sprite.Part> obscured = Collections.emptyList(); private int[] visol = new int[31]; private long olftimer = 0; private int olflash = 0; Grabber grab = null; ILM mask; final MCache map; final Glob glob; Collection<Gob> plob = null; boolean plontile; int plrad = 0; int playergob = -1; public Profile prof = new Profile(300); private Profile.Frame curf; Coord plfpos = null; long lastmove = 0; Sprite.Part obscpart = null; Gob obscgob = null; static Text.Foundry polownertf = new Text.Foundry("serif", 20); public Text polownert = null; public String polowner = null; public Gob onmouse; public Coord moveto = null; long polchtm = 0; int si = 4; double _scale = 1; double scales[] = {0.5, 0.66, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2}; Map<String, Integer> radiuses; int beast_check_delay = 0; long lastah = 0; public double getScale() { return Config.zoom?_scale:1; } public void setScale(double value) { _scale = value; mask.setScale(value); } public static final Comparator<Sprite.Part> clickcmp = new Comparator<Sprite.Part>() { public int compare(Sprite.Part a, Sprite.Part b) { return(-Sprite.partidcmp.compare(a, b)); } }; static { Widget.addtype("mapview", new WidgetFactory() { public Widget create(Coord c, Widget parent, Object[] args) { Coord sz = MainFrame.getInnerSize(); Coord mc = (Coord)args[1]; int pgob = -1; if(args.length > 2) pgob = (Integer)args[2]; return(new MapView(c, sz, parent, mc, pgob)); } }); olc[0] = new Color(255, 0, 128); olc[1] = new Color(0, 0, 255); olc[2] = new Color(255, 0, 0); olc[3] = new Color(128, 0, 255); olc[16] = new Color(0, 255, 0); olc[17] = new Color(255, 255, 0); } public interface Grabber { void mmousedown(Coord mc, int button); void mmouseup(Coord mc, int button); void mmousemove(Coord mc); } @SuppressWarnings("serial") public static class GrabberException extends RuntimeException{} public static class Camera { public void setpos(MapView mv, Gob player, Coord sz) {} public boolean click(MapView mv, Coord sc, Coord mc, int button) { return(false); } public void move(MapView mv, Coord sc, Coord mc) {} public boolean release(MapView mv, Coord sc, Coord mc, int button) { return(false); } public void moved(MapView mv) {} public static void borderize(MapView mv, Gob player, Coord sz, Coord border) { if(Config.noborders){return;} Coord mc = mv.mc; Coord oc = m2s(mc).inv(); int bt = -((sz.y / 2) - border.y); int bb = (sz.y / 2) - border.y; int bl = -((sz.x / 2) - border.x); int br = (sz.x / 2) - border.x; Coord sc = m2s(player.getc()).add(oc); if(sc.x < bl) mc = mc.add(s2m(new Coord(sc.x - bl, 0))); if(sc.x > br) mc = mc.add(s2m(new Coord(sc.x - br, 0))); if(sc.y < bt) mc = mc.add(s2m(new Coord(0, sc.y - bt))); if(sc.y > bb) mc = mc.add(s2m(new Coord(0, sc.y - bb))); mv.mc = mc; } public void reset() {} } private static abstract class DragCam extends Camera { Coord o, mo; boolean dragging = false; boolean needreset = false; public boolean click(MapView mv, Coord sc, Coord mc, int button) { if(button == 2) { mv.ui.grabmouse(mv); o = sc; mo = null; dragging = true; return(true); } return(false); } public void move(MapView mv, Coord sc, Coord mc) { if(dragging) { Coord off = sc.add(o.inv()); if((mo == null) && (off.dist(Coord.z) > 5)) mo = mv.mc; if(mo != null) { mv.mc = mo.add(s2m(off).inv()); moved(mv); } } } public void reset(){ needreset = true; } public boolean release(MapView mv, Coord sc, Coord mc, int button) { if((button == 2) && dragging) { mv.ui.grabmouse(null); dragging = false; if(mo == null) { mv.mc = mc; moved(mv); } return(true); } return(false); } } static class OrigCam extends Camera { public final Coord border = new Coord(250, 150); public void setpos(MapView mv, Gob player, Coord sz) { borderize(mv, player, sz, border); } public boolean click(MapView mv, Coord sc, Coord mc, int button) { if(button == 1) mv.mc = mc; return(false); } } static {camtypes.put("orig", OrigCam.class);} static class OrigCam2 extends DragCam { public final Coord border = new Coord(250, 125); private final double v; private Coord tgt = null; private long lmv; public OrigCam2(double v) { this.v = Math.log(v) / 0.02; /* 1 / 50 FPS = 0.02 s */ } public OrigCam2() { this(0.9); } public OrigCam2(String... args) { this((args.length < 1)?0.9:Double.parseDouble(args[0])); } public void setpos(MapView mv, Gob player, Coord sz) { if(needreset){ needreset = false; mv.mc = player.getc(); } if(tgt != null) { if(mv.mc.dist(tgt) < 10) { tgt = null; } else { long now = System.currentTimeMillis(); double dt = (now - lmv) / 1000.0; lmv = now; mv.mc = tgt.add(mv.mc.add(tgt.inv()).mul(Math.exp(v * dt))); } } borderize(mv, player, sz, border); } public boolean click(MapView mv, Coord sc, Coord mc, int button) { if((button == 1) && (mv.ui.root.cursor == RootWidget.defcurs)) { tgt = mc; lmv = System.currentTimeMillis(); } return(super.click(mv, sc, mc, button)); } public void moved(MapView mv) { tgt = null; } public void reset() { super.reset(); tgt = null; } } static {camtypes.put("clicktgt", OrigCam2.class);} static class WrapCam extends Camera { public final Coord region = new Coord(200, 150); public void setpos(MapView mv, Gob player, Coord sz) { Coord sc = m2s(player.getc().add(mv.mc.inv())); if(sc.x < -region.x) mv.mc = mv.mc.add(s2m(new Coord(-region.x * 2, 0))); if(sc.x > region.x) mv.mc = mv.mc.add(s2m(new Coord(region.x * 2, 0))); if(sc.y < -region.y) mv.mc = mv.mc.add(s2m(new Coord(0, -region.y * 2))); if(sc.y > region.y) mv.mc = mv.mc.add(s2m(new Coord(0, region.y * 2))); } } static {camtypes.put("kingsquest", WrapCam.class);} static class BorderCam extends DragCam { public final Coord border = new Coord(250, 150); public void setpos(MapView mv, Gob player, Coord sz) { if(needreset){ needreset = false; mv.mc = player.getc(); } borderize(mv, player, sz, border); } } static {camtypes.put("border", BorderCam.class);} static class PredictCam extends DragCam { private double xa = 0, ya = 0; private boolean reset = true; private final double speed = 0.15, rspeed = 0.15; private double sincemove = 0; private long last = System.currentTimeMillis(); public void setpos(MapView mv, Gob player, Coord sz) { long now = System.currentTimeMillis(); double dt = ((double)(now - last)) / 1000.0; last = now; if(needreset){ needreset = false; mv.mc = player.getc(); } Coord mc = mv.mc.add(s2m(sz.add(mv.sz.inv()).div(2))); Coord sc = m2s(player.getc()).add(m2s(mc).inv()); if(reset) { xa = (double)sc.x / (double)sz.x; ya = (double)sc.y / (double)sz.y; if(xa < -0.25) xa = -0.25; if(xa > 0.25) xa = 0.25; if(ya < -0.15) ya = -0.15; if(ya > 0.25) ya = 0.25; reset = false; } Coord vsz = sz.div(16); Coord vc = new Coord((int)(sz.x * xa), (int)(sz.y * ya)); boolean moved = false; if(sc.x < vc.x - vsz.x) { if(xa < 0.25) xa += speed * dt; moved = true; mc = mc.add(s2m(new Coord(sc.x - (vc.x - vsz.x) - 4, 0))); } if(sc.x > vc.x + vsz.x) { if(xa > -0.25) xa -= speed * dt; moved = true; mc = mc.add(s2m(new Coord(sc.x - (vc.x + vsz.x) + 4, 0))); } if(sc.y < vc.y - vsz.y) { if(ya < 0.25) ya += speed * dt; moved = true; mc = mc.add(s2m(new Coord(0, sc.y - (vc.y - vsz.y) - 2))); } if(sc.y > vc.y + vsz.y) { if(ya > -0.15) ya -= speed * dt; moved = true; mc = mc.add(s2m(new Coord(0, sc.y - (vc.y + vsz.y) + 2))); } if(!moved) { sincemove += dt; if(sincemove > 1) { if(xa < -0.1) xa += rspeed * dt; if(xa > 0.1) xa -= rspeed * dt; if(ya < -0.1) ya += rspeed * dt; if(ya > 0.1) ya -= rspeed * dt; } } else { sincemove = 0; } mv.mc = mc.add(s2m(mv.sz.add(sz.inv()).div(2))); } public void moved(MapView mv) { reset = true; } public void reset(){ reset = true; xa = ya = 0; super.reset(); } } static {camtypes.put("predict", PredictCam.class);} static class FixedCam extends DragCam { public final Coord border = new Coord(250, 150); private Coord off = Coord.z; private boolean setoff = false; public void setpos(MapView mv, Gob player, Coord sz) { if(setoff) { borderize(mv, player, sz, border); off = mv.mc.add(player.getc().inv()); setoff = false; } mv.mc = player.getc().add(off); } public void moved(MapView mv) { setoff = true; } public void reset() { off = Coord.z; } } static {camtypes.put("fixed", FixedCam.class);} static class CakeCam extends Camera { private Coord border = new Coord(250, 150); private Coord size, center, diff; public void setpos(MapView mv, Gob player, Coord sz) { if(size == null || !size.equals(sz)) { size = new Coord(sz); center = size.div(2); diff = center.sub(border); } if(player != null && mv.pmousepos != null) mv.mc = player.getc().sub(s2m(center.sub(mv.pmousepos).mul(diff).div(center))); } } static {camtypes.put("cake", CakeCam.class);} static class FixedCakeCam extends DragCam { public final Coord border = new Coord(250, 150); private Coord size, center, diff; private boolean setoff = false; private Coord off = Coord.z; private Coord tgt = null; private Coord cur = off; private double vel = 0.2; public FixedCakeCam(double vel) { this.vel = Math.min(1.0, Math.max(0.1, vel)); } public FixedCakeCam(String... args) { this(args.length < 1 ? 0.2 : Double.parseDouble(args[0])); } public void setpos(MapView mv, Gob player, Coord sz) { if(setoff) { borderize(mv, player, sz, border); off = mv.mc.add(player.getc().inv()); setoff = false; } if(mv.pmousepos != null && (mv.pmousepos.x == 0 || mv.pmousepos.x == sz.x - 1 || mv.pmousepos.y == 0 || mv.pmousepos.y == sz.y - 1)) { if(size == null || !size.equals(sz)) { size = new Coord(sz); center = size.div(2); diff = center.sub(border); } if(player != null && mv.pmousepos != null) tgt = player.getc().sub(s2m(center.sub(mv.pmousepos).mul(diff).div(center))).sub(player.getc()); } else { tgt = off; } cur = cur.add(tgt.sub(cur).mul(vel)); mv.mc = player.getc().add(cur); } public void moved(MapView mv) { setoff = true; } public void reset(){ off = new Coord(); } } static {camtypes.put("fixedcake", FixedCakeCam.class);} private class Loading extends Exception {} private static Camera makecam(Class<? extends Camera> ct, String... args) throws ClassNotFoundException { try { try { Constructor<? extends Camera> cons = ct.getConstructor(String [].class); return(cons.newInstance(new Object[] {args})); } catch(IllegalAccessException e) { } catch(NoSuchMethodException e) { } try { return(ct.newInstance()); } catch(IllegalAccessException e) { } } catch(InstantiationException e) { throw(new Error(e)); } catch(InvocationTargetException e) { if(e.getCause() instanceof RuntimeException) throw((RuntimeException)e.getCause()); throw(new RuntimeException(e)); } throw(new ClassNotFoundException("No valid constructor found for camera " + ct.getName())); } private static Camera restorecam() { Class<? extends Camera> ct = camtypes.get(Utils.getpref("defcam", "border")); if(ct == null) return(new BorderCam()); String[] args = (String [])Utils.deserialize(Utils.getprefb("camargs", null)); if(args == null) args = new String[0]; try { return(makecam(ct, args)); } catch(ClassNotFoundException e) { return(new BorderCam()); } } public MapView(Coord c, Coord sz, Widget parent, Coord mc, int playergob) { super(c, sz, parent); isui = false; this.mc = mc; this.playergob = playergob; this.cam = restorecam(); setcanfocus(true); glob = ui.sess.glob; map = glob.map; mask = new ILM(MainFrame.getScreenSize(), glob.oc); radiuses = new HashMap<String, Integer>(); } public void resetcam(){ if(cam != null){ cam.reset(); } } public static Coord m2s(Coord c) { return(new Coord((c.x * 2) - (c.y * 2), c.x + c.y)); } public static Coord s2m(Coord c) { return(new Coord((c.x / 4) + (c.y / 2), (c.y / 2) - (c.x / 4))); } static Coord viewoffset(Coord sz, Coord vc) { return(m2s(vc).inv().add(sz.div(2))); } public void grab(Grabber grab) { this.grab = grab; } public void release(Grabber grab) { if(this.grab == grab) this.grab = null; } private Gob gobatpos(Coord c) { for(Sprite.Part d : obscured) { Gob gob = (Gob)d.owner; if(gob == null) continue; if(d.checkhit(c.add(gob.sc.inv()))) return(gob); } for(Sprite.Part d : clickable) { Gob gob = (Gob)d.owner; if(gob == null) continue; if(d.checkhit(c.add(gob.sc.inv()))) return(gob); } return(null); } public boolean mousedown(Coord c, int button) { setfocus(this); Coord c0 = c; c = new Coord((int)(c.x/getScale()), (int)(c.y/getScale())); Gob hit = gobatpos(c); Coord mc = s2m(c.add(viewoffset(sz, this.mc).inv())); if(grab != null) { try{ grab.mmousedown(mc, button); return true; } catch (GrabberException e){} } if((cam != null) && cam.click(this, c, mc, button)) { /* Nothing */ } else if(plob != null) { Gob gob = null; for(Gob g : plob) gob = g; wdgmsg("place", gob.rc, button, ui.modflags()); } else { if(hit == null){ if(ui.modshift && (button == 1)){ glob.oc.enqueue(mc); glob.oc.checkqueue(); } else { glob.oc.clearqueue(); wdgmsg("click", c0, mc, button, ui.modflags()); } } else { if(ui.modmeta){ ui.chat.getawnd().wdgmsg("msg","@$["+hit.id+"]"); } else { wdgmsg("click", c0, mc, button, ui.modflags(), hit.id, hit.getc()); } } } return(true); } public boolean mouseup(Coord c, int button) { c = new Coord((int)(c.x/getScale()), (int)(c.y/getScale())); Coord mc = s2m(c.add(viewoffset(sz, this.mc).inv())); if(grab != null) { try { grab.mmouseup(mc, button); return(true); } catch (GrabberException e){} } if((cam != null) && cam.release(this, c, mc, button)) { return(true); } else { return(true); } } public void mousemove(Coord c) { c = new Coord((int)(c.x/getScale()), (int)(c.y/getScale())); this.pmousepos = c; Coord mc = s2m(c.add(viewoffset(sz, this.mc).inv())); this.mousepos = mc; Gob hit = gobatpos(c); if(hit == null){ tip = null; tips = null; } Collection<Gob> plob = this.plob; if(cam != null) cam.move(this, c, mc); if(grab != null) { try{ grab.mmousemove(mc); return; } catch (GrabberException e){} } if(plob != null) { Gob gob = null; for(Gob g : plob) gob = g; boolean plontile = this.plontile ^ ui.modshift; gob.move(plontile?tilify(mc):mc); } else if(hit != null && ui.modshift){ String s; s = "Res found on gob " + hit.id; String names[] = hit.resnames(); if(names.length > 0){ for(String name : names){ if(name.contains("gfx/borka")){ name = name.replace("gfx/borka/",""); if(name.contains("/")) name = name.substring(0,name.indexOf("/")); if(name.equals("body") || name.startsWith("hair") || s.contains(name)) continue; } s += "\n"+name; } } if(tip == null || !tips.equals(s)){ tips = s; tip = null; tooltip(null,false); } } } String tips; Text tip = null; public Object tooltip(Coord c,boolean again){ if(tip != null) return(tip); if(tips != null && !tips.equals("")){ tip = RichText.render(tips,200); } return(tip); } public boolean mousewheel(Coord c, int amount) { if(!Config.zoom) return false; si = Math.min(8, Math.max(0, si - amount)); setScale(scales[si]); return(true); } public void move(Coord mc) { this.mc = mc; } private static Coord tilify(Coord c) { c = c.div(tilesz); c = c.mul(tilesz); c = c.add(tilesz.div(2)); return(c); } private void unflashol() { for(int i = 0; i < visol.length; i++) { if((olflash & (1 << i)) != 0) visol[i]--; } olflash = 0; olftimer = 0; } public void uimsg(String msg, Object... args) { if(msg == "move") { move((Coord)args[0]); if(cam != null) cam.moved(this); } else if(msg == "flashol") { unflashol(); olflash = (Integer)args[0]; for(int i = 0; i < visol.length; i++) { if((olflash & (1 << i)) != 0) visol[i]++; } olftimer = System.currentTimeMillis() + (Integer)args[1]; } else if(msg == "place") { Collection<Gob> plob = this.plob; if(plob != null) { this.plob = null; glob.oc.lrem(plob); } plob = new LinkedList<Gob>(); plontile = (Integer)args[2] != 0; Gob gob = new Gob(glob, plontile?tilify(mousepos):mousepos); Resource res = Resource.load((String)args[0], (Integer)args[1]); gob.setattr(new ResDrawable(gob, res)); plob.add(gob); glob.oc.ladd(plob); if(args.length > 3) { plrad = (Integer)args[3]; radiuses.put(res.name, plrad); if(res.name.equals("gfx/terobjs/bhive")) radiuses.put("gfx/terobjs/bhived", plrad); } this.plob = plob; } else if(msg == "unplace") { if(plob != null) glob.oc.lrem(plob); plob = null; plrad = 0; } else if(msg == "polowner") { String o = ((String)args[0]).intern(); if(o != polowner) { if(o.length() == 0) { if(this.polowner != null) this.polownert = polownertf.render("Leaving " + this.polowner); this.polowner = null; } else { this.polowner = o; this.polownert = polownertf.render("Entering " + o); } this.polchtm = System.currentTimeMillis(); } } else { super.uimsg(msg, args); } } public void enol(int... overlays) { for(int ol : overlays) visol[ol]++; } public void disol(int... overlays) { for(int ol : overlays) visol[ol]--; } private int gettilen(Coord tc) throws Loading { int r = map.gettilen(tc); if(r == -1) throw(new Loading()); return(r); } private Tile getground(Coord tc) throws Loading { Tile r = map.getground(tc); if(r == null) throw(new Loading()); return(r); } private Tile[] gettrans(Coord tc) throws Loading { Tile[] r = map.gettrans(tc); if(r == null) throw(new Loading()); return(r); } private int getol(Coord tc) throws Loading { int ol = map.getol(tc); if(ol == -1) throw(new Loading()); return(ol); } private void drawtile(GOut g, Coord tc, Coord sc) { Tile t; try { t = getground(tc); //t = gettile(tc).ground.pick(0); g.image(t.tex(), sc); //g.setColor(FlowerMenu.pink); //Utils.drawtext(g, Integer.toString(t.i), sc); for(Tile tt : gettrans(tc)) { g.image(tt.tex(), sc); } } catch (Loading e) {} } private void drawol(GOut g, Coord tc, Coord sc) { int ol; int i; double w = 2; try { ol = getol(tc); if(ol == 0) return; Coord c1 = sc; Coord c2 = sc.add(m2s(new Coord(0, tilesz.y))); Coord c3 = sc.add(m2s(new Coord(tilesz.x, tilesz.y))); Coord c4 = sc.add(m2s(new Coord(tilesz.x, 0))); for(i = 0; i < olc.length; i++) { if(olc[i] == null) continue; if(((ol & (1 << i)) == 0) || (visol[i] < 1)) continue; Color fc = new Color(olc[i].getRed(), olc[i].getGreen(), olc[i].getBlue(), 32); g.chcolor(fc); g.frect(c1, c2, c3, c4); if(((ol & ~getol(tc.add(new Coord(-1, 0)))) & (1 << i)) != 0) { g.chcolor(olc[i]); g.line(c2, c1, w); } if(((ol & ~getol(tc.add(new Coord(0, -1)))) & (1 << i)) != 0) { g.chcolor(olc[i]); g.line(c1.add(1, 0), c4.add(1, 0), w); } if(((ol & ~getol(tc.add(new Coord(1, 0)))) & (1 << i)) != 0) { g.chcolor(olc[i]); g.line(c4.add(1, 0), c3.add(1, 0), w); } if(((ol & ~getol(tc.add(new Coord(0, 1)))) & (1 << i)) != 0) { g.chcolor(olc[i]); g.line(c3, c2, w); } } g.chcolor(Color.WHITE); } catch (Loading e) {} } private void drawradius(GOut g, Coord c, int radius) { g.fellipse(c, new Coord((int)(radius * 4 * Math.sqrt(0.5)), (int)(radius * 2 * Math.sqrt(0.5)))); } private void drawplobeffect(GOut g) { if(plob == null) return; Gob gob = null; for(Gob tg : plob) gob = tg; if(gob.sc == null) return; if(plrad > 0) { String name = gob.resname(); g.chcolor(0, 255, 0, 32); synchronized (glob.oc) { for (Gob tg : glob.oc) if ((tg.sc != null) && (tg.resname() == name)) drawradius(g, tg.sc, plrad); } g.chcolor(); } } private void draweffectradius(GOut g) { String name; g.chcolor(0, 255, 0, 32); synchronized (glob.oc) { for (Gob tg : glob.oc) { name = tg.resname(); if (radiuses.containsKey(name) && (tg.sc != null)) { drawradius(g, tg.sc, radiuses.get(name)); } } } g.chcolor(); } private void drawobjradius(GOut g) { synchronized (glob.oc) { for (Gob gob : glob.oc) { if(gob.sc == null){continue;} if (Config.showBeast && gob.isBeast()) { HLInfo inf = Config.hlcfg.get(gob.beastname); g.chcolor(inf.col.getRed(), inf.col.getGreen(), inf.col.getBlue(), 96); drawradius(g, gob.sc, 100); } if(gob.isHuman() && !ui.sess.glob.party.memb.keySet().contains(gob.id)){ g.chcolor(255, 0, 255, 96); drawradius(g, gob.sc, 10); if (Config.autohearth) { autohearth(gob); } } if(gob.isHighlight() && Config.highlightItemList.contains(gob.resname())){ g.chcolor(255, 128, 64, 96); drawradius(g, gob.sc, 10); } } } g.chcolor(); } private void autohearth(Gob gob) { if (System.currentTimeMillis() > lastah) { KinInfo kin = gob.getattr(KinInfo.class); if(kin == null){ if (Config.hearthunknown) { hearth(); } } else { if (kin.group == 2) { if (Config.hearthred) hearth(); } } } } private void hearth() { String[] action = {"theTrav", "hearth"}; UI.instance.mnu.wdgmsg("act", (Object[])action); lastah = System.currentTimeMillis() + 5000; } private void drawtracking(GOut g) { g.chcolor(255, 0, 255, 128); Coord oc = viewoffset(sz, mc); for(int i = 0; i<TrackingWnd.instances.size(); i++){ TrackingWnd wnd = TrackingWnd.instances.get(i); if(wnd.pos == null){continue;} Coord c = m2s(wnd.pos).add(oc); g.fellipse(c, new Coord(100, 50), wnd.a1, wnd.a2); } g.chcolor(); } private boolean follows(Gob g1, Gob g2) { Following flw; if((flw = g1.getattr(Following.class)) != null) { if(flw.tgt() == g2) return(true); } if((flw = g2.getattr(Following.class)) != null) { if(flw.tgt() == g1) return(true); } return(false); } private List<Sprite.Part> findobsc() { ArrayList<Sprite.Part> obsc = new ArrayList<Sprite.Part>(); if(obscgob == null) return(obsc); boolean adding = false; for(Sprite.Part p : clickable) { Gob gob = (Gob)p.owner; if(gob == null) continue; if(gob == obscgob) { adding = true; continue; } if(follows(gob, obscgob)) continue; if(adding && obscpart.checkhit(gob.sc.add(obscgob.sc.inv()))) obsc.add(p); } return(obsc); } private void drawols(GOut g, Coord sc) { synchronized(map.grids){ for(Coord gc : map.grids.keySet()){ Grid grid = map.grids.get(gc); for(Overlay lol : grid.ols ){ int id = getolid(lol.mask); if(visol[id] < 1){ continue; } Coord c0 = gc.mul(cmaps); drawol2(g, id, c0.add(lol.c1), c0.add(lol.c2), sc); } } } for(Overlay lol : map.ols ){ int id = getolid(lol.mask); if(visol[id] < 1){ continue; } drawol2(g, id, lol.c1, lol.c2, sc); } g.chcolor(); } private int getolid(int mask){ for(int i=0; i<olc.length; i++){ if((mask & (1 << i)) != 0){ return i; } } return 0; } private void drawol2(GOut g, int id, Coord c0, Coord cx, Coord sc){ cx = cx.add(1,1); Coord c1 = m2s(c0.mul(tilesz)).add(sc); Coord c2 = m2s(new Coord(c0.x, cx.y).mul(tilesz)).add(sc); Coord c3 = m2s(cx.mul(tilesz)).add(sc); Coord c4 = m2s(new Coord(cx.x, c0.y).mul(tilesz)).add(sc); Color fc = new Color(olc[id].getRed(), olc[id].getGreen(), olc[id].getBlue(), 32); g.chcolor(fc); g.frect(c1, c2, c3, c4); cx = cx.sub(1,1); drawline(g, new Coord(0,-1), c0.y, id, c0, cx, sc); drawline(g, new Coord(0,1), cx.y, id, c0, cx, sc); drawline(g, new Coord(1,0), cx.x, id, c0, cx, sc); drawline(g, new Coord(-1,0), c0.x, id, c0, cx, sc); g.chcolor(); } private void drawline(GOut g, Coord d, int med, int id, Coord c0, Coord cx, Coord sc){ Coord m = d.abs(); Coord r = m.swap(); Coord off = m.mul(med).add(d.add(m).div(2)); int min = c0.mul(r).sum(); int max = cx.mul(r).sum()+1; boolean t=false; int begin = min; int ol = 1<<id; g.chcolor(olc[id]); for(int i=min; i<=max; i++){ Coord c = r.mul(i).add(m.mul(med)).add(d); int ol2; try{ol2 = getol(c);}catch(Loading e){ol2 = ol;} if(t){ if(((ol2&ol)!=0)||i==max){ t = false; Coord cb = m2s(tilesz.mul(r.mul(begin).add(off))).add(sc); Coord ce = m2s(tilesz.mul(r.mul(i).add(off))).add(sc); g.line(cb, ce, 2); } } else { if((ol2&ol)==0){ t = true; begin = i; } } } } public void drawmap(GOut g) { int x, y, i; int stw, sth; Coord oc, tc, ctc, sc; if(Config.profile) curf = prof.new Frame(); stw = (tilesz.x * 4) - 2; sth = tilesz.y * 2; oc = viewoffset(sz, mc); tc = mc.div(tilesz); tc.x += -(sz.x / (2 * stw)) - (sz.y / (2 * sth)) - 2; tc.y += (sz.x / (2 * stw)) - (sz.y / (2 * sth)); for(y = 0; y < (sz.y / sth) + 2; y++) { for(x = 0; x < (sz.x / stw) + 3; x++) { for(i = 0; i < 2; i++) { ctc = tc.add(new Coord(x + y, -x + y + i)); sc = m2s(ctc.mul(tilesz)).add(oc); sc.x -= tilesz.x * 2; drawtile(g, ctc, sc); sc.x += tilesz.x * 2; if(!Config.newclaim){drawol(g, ctc, sc);} } } } if(Config.newclaim){drawols(g, oc);} if(Config.grid){ g.chcolor(new Color(40, 40, 40)); Coord c1, c2, d; d = tc.mul(tilesz); int hy = (sz.y / sth)*tilesz.y; int hx = (sz.x / stw)*tilesz.x; c1 = d.add(0, 0); c2 = d.add(5*hx/2,0); for(y = d.y - hy; y < d.y + hy; y = y + tilesz.y){ c1.y = y; c2.y = c1.y; g.line(m2s(c1).add(oc), m2s(c2).add(oc), 1); } c1 = d.add(0, -hy); c2 = d.add(0, hy); for(x = d.x; x < d.x + 5*hx/2; x = x + tilesz.x){ c1.x = x; c2.x = c1.x; g.line(m2s(c1).add(oc), m2s(c2).add(oc), 1); } g.chcolor(); } if(curf != null) curf.tick("map"); if(Config.showRadius) draweffectradius(g); else drawplobeffect(g); if(Config.radar){drawobjradius(g);} drawtracking(g); if(curf != null) curf.tick("plobeff"); final List<Sprite.Part> sprites = new ArrayList<Sprite.Part>(); ArrayList<Speaking> speaking = new ArrayList<Speaking>(); ArrayList<KinInfo> kin = new ArrayList<KinInfo>(); class GobMapper implements Sprite.Drawer { Gob cur = null; Sprite.Part.Effect fx = null; int szo = 0; public void chcur(Gob cur) { this.cur = cur; GobHealth hlt = cur.getattr(GobHealth.class); fx = null; if(hlt != null) fx = hlt.getfx(); if(cur.highlight != null){ long t = System.currentTimeMillis() - cur.highlight.time; if(t > 5000){ cur.highlight = null; } } if(cur.highlight != null){ fx = cur.highlight; } Following flw = cur.getattr(Following.class); szo = 0; if(flw != null) szo = flw.szo; } public void addpart(Sprite.Part p) { p.effect = fx; if((p.ul.x >= sz.x) || (p.ul.y >= sz.y) || (p.lr.x < 0) || (p.lr.y < 0)) return; sprites.add(p); p.owner = cur; p.szo = szo; } } if(Config.showHidden && Config.hide) { g.chcolor(255, 0, 0, 128); synchronized(glob.oc) { for(Gob gob : glob.oc) { Resource res = gob.getres(); if(!gob.hide || ((res != null)&&(res.skiphighlight))) continue; Resource.Neg neg = gob.getneg(); if(neg == null) continue; if((neg.bs.x > 0) && (neg.bs.y > 0)) { Coord c1 = gob.getc().add(neg.bc); Coord c2 = c1.add(neg.bs); g.frect(m2s(c1).add(oc), m2s(new Coord(c2.x, c1.y)).add(oc), m2s(c2).add(oc), m2s(new Coord(c1.x, c2.y)).add(oc)); } } } g.chcolor(); } GobMapper drawer = new GobMapper(); synchronized(glob.oc) { for(Gob gob : glob.oc) { drawer.chcur(gob); Coord dc = m2s(gob.getc()).add(oc); gob.sc = dc; gob.drawsetup(drawer, dc, sz); Speaking s = gob.getattr(Speaking.class); if(s != null) speaking.add(s); KinInfo k = gob.getattr(KinInfo.class); if(k != null) kin.add(k); } if(curf != null) curf.tick("setup"); Collections.sort(sprites, Sprite.partidcmp); { Sprite.Part[] clickable = new Sprite.Part[sprites.size()]; for(int o = 0, u = clickable.length - 1; o < clickable.length; o++, u--) clickable[u] = sprites.get(o); this.clickable = clickable; } if(curf != null) curf.tick("sort"); onmouse = null; if(pmousepos != null) onmouse = gobatpos(pmousepos); obscured = findobsc(); if(curf != null) curf.tick("obsc"); for(Sprite.Part part : sprites) { if(part.effect != null) part.draw(part.effect.apply(g)); else part.draw(g); } for(Sprite.Part part : obscured) { GOut g2 = new GOut(g); GobHealth hlt; if((part.owner != null) && (part.owner instanceof Gob) && ((hlt = ((Gob)part.owner).getattr(GobHealth.class)) != null)) g2.chcolor(255, (int)(hlt.asfloat() * 255), 0, 255); else g2.chcolor(255, 255, 0, 255); part.drawol(g2); } if(curf != null) curf.tick("draw"); //Illumination g.gl.glPopMatrix(); GOut gilm = g.reclip(Coord.z, hsz); gilm.image(mask, Coord.z); g.gl.glPushMatrix(); g.scale(getScale()); //***************** long now = System.currentTimeMillis(); RootWidget.names_ready = (RootWidget.screenshot && Config.sshot_nonames); if(!RootWidget.names_ready){ for(KinInfo k : kin) { Tex t = k.rendered(); Coord gc = k.gob.sc; String name = k.gob.resname(); boolean isother = name.contains("hearth") || name.contains("skeleton"); if(gc.isect(Coord.z, sz)) { if(k.seen == 0) k.seen = now; int tm = (int)(now - k.seen); Color show = null; boolean auto = (k.type & 1) == 0; if((isother && Config.showOtherNames)||(!isother && Config.showNames)||(k.gob == onmouse)) { show = Color.WHITE; } else if(auto && (tm < 7500)) { show = Utils.clipcol(255, 255, 255, 255 - ((255 * tm) / 7500)); } if(show != null) { g.chcolor(show); g.image(t, gc.add(-t.sz().x / 2, -40 - t.sz().y)); g.chcolor(); } } else { k.seen = 0; } } } if(!Config.muteChat){ for(Speaking s : speaking) { s.draw(g, s.gob.sc.add(s.off)); } } if(curf != null) { curf.tick("aux"); curf.fin(); curf = null; } //System.out.println(curf); } } public void drawarrows(GOut g) { Coord oc = viewoffset(sz, mc); Coord hsz = sz.div(2); double ca = -Coord.z.angle(hsz); for(Party.Member m : glob.party.memb.values()) { //Gob gob = glob.oc.getgob(id); Coord mc = m.getc(); if(mc == null) continue; Coord sc = m2s(mc).add(oc); if(!sc.isect(Coord.z, sz)) { double a = -hsz.angle(sc); Coord ac; if((a > ca) && (a < -ca)) { ac = new Coord(sz.x, hsz.y - (int)(Math.tan(a) * hsz.x)); } else if((a > -ca) && (a < Math.PI + ca)) { ac = new Coord(hsz.x - (int)(Math.tan(a - Math.PI / 2) * hsz.y), 0); } else if((a > -Math.PI - ca) && (a < ca)) { ac = new Coord(hsz.x + (int)(Math.tan(a + Math.PI / 2) * hsz.y), sz.y); } else { ac = new Coord(0, hsz.y + (int)(Math.tan(a) * hsz.x)); } g.chcolor(m.col); Coord bc = ac.add(Coord.sc(a, -10)); g.line(bc, bc.add(Coord.sc(a, -40)), 2); g.line(bc, bc.add(Coord.sc(a + Math.PI / 4, -10)), 2); g.line(bc, bc.add(Coord.sc(a - Math.PI / 4, -10)), 2); g.chcolor(Color.WHITE); } } } private void checkplmove() { Gob pl; long now = System.currentTimeMillis(); if((playergob != -1) && ((pl = glob.oc.getgob(playergob)) != null) && (pl.sc != null)) { Coord plp = pl.getc(); if((plfpos == null) || !plfpos.equals(plp)) { lastmove = now; plfpos = plp; if((obscpart != null) && !obscpart.checkhit(pl.sc.add(obscgob.sc.inv()))) { obscpart = null; obscgob = null; } } else if(now - lastmove > 500) { for(Sprite.Part p : clickable) { Gob gob = (Gob)p.owner; if((gob == null) || (gob.sc == null)) continue; if(gob == pl) break; if(p.checkhit(pl.sc.add(gob.sc.inv()))) { obscpart = p; obscgob = gob; break; } } } } } private void checkmappos() { if(cam == null) return; Coord sz = this.sz; SlenHud slen = ui.slen; if(slen != null) sz = sz.add(0, -slen.foldheight()); Gob player = glob.oc.getgob(playergob); if(player != null) cam.setpos(this, player, sz); } public void draw(GOut og) { if(moveto != null){ wdgmsg("click", moveto, moveto, 1, 0); moveto = null; } hsz = MainFrame.getInnerSize(); sz = hsz.mul(1/getScale()); GOut g = og.reclip(Coord.z, sz); g.gl.glPushMatrix(); g.scale(getScale()); checkmappos(); Coord requl = mc.add(-500, -500).div(tilesz).div(cmaps); Coord reqbr = mc.add(500, 500).div(tilesz).div(cmaps); Coord cgc = new Coord(0, 0); for(cgc.y = requl.y; cgc.y <= reqbr.y; cgc.y++) { for(cgc.x = requl.x; cgc.x <= reqbr.x; cgc.x++) { if(map.grids.get(cgc) == null) map.request(new Coord(cgc)); } } long now = System.currentTimeMillis(); if((olftimer != 0) && (olftimer < now)) unflashol(); map.sendreqs(); checkplmove(); // try { if(((mask.amb = glob.amblight) == null) || Config.nightvision) mask.amb = new Color(0, 0, 0, 0); drawmap(g); //movement highlight if(Config.showgobpath||Config.showothergobpath){ drawGobPath(g); } if(Config.showpath){ drawPlayerPath(g); } //############### drawarrows(g); g.chcolor(Color.WHITE); if(Config.dbtext) g.atext(mc.toString(), new Coord(10, 560), 0, 1); // } catch(Loading l) { // String text = "Loading..."; // g.chcolor(Color.BLACK); // g.frect(Coord.z, sz); // g.chcolor(Color.WHITE); // g.atext(text, sz, 0.5, 0.5); // } long poldt = now - polchtm; if((polownert != null) && (poldt < 6000)) { int a; if(poldt < 1000) a = (int)((255 * poldt) / 1000); else if(poldt < 4000) a = 255; else a = (int)((255 * (2000 - (poldt - 4000))) / 2000); g.chcolor(255, 255, 255, a); g.aimage(polownert.tex(), sz.div(2), 0.5, 0.5); g.chcolor(); } g.gl.glPopMatrix(); super.draw(og); } private void drawPlayerPath(GOut g) { Coord oc = viewoffset(sz, mc); Coord pc, cc; Moving m; LinMove lm; Gob player = glob.oc.getgob(playergob); if(player != null){ m = player.getattr(Moving.class); g.chcolor(Color.GREEN); if((m != null) && (m instanceof LinMove)){ lm = (LinMove)m; pc = m2s(lm.t).add(oc); g.line(player.sc, pc, 2); for(Coord c:glob.oc.movequeue){ cc = m2s(c).add(oc); g.line(pc, cc, 2); pc = cc; } } g.chcolor(); } } private void drawGobPath(GOut g) { Moving m; LinMove lm; Coord oc = viewoffset(sz, mc); g.chcolor(Color.ORANGE); synchronized (glob.oc) { for (Gob gob : glob.oc){ if ((Config.showgobpath && gob.isHuman()) || (Config.showothergobpath && !gob.isHuman())) { if(gob.sc == null){continue;} m = gob.getattr(Moving.class); if((m!=null)&&(m instanceof LinMove)){ lm = (LinMove) m; g.line(gob.sc, m2s(lm.t).add(oc), 2); } } } } g.chcolor(); } public boolean drop(Coord cc, Coord ul) { wdgmsg("drop", ui.modflags()); return(true); } public boolean iteminteract(Coord cc, Coord ul) { Coord cc0 = cc; cc = new Coord((int) (cc.x/getScale()), (int)(cc.y/getScale())); Gob hit = gobatpos(cc); Coord mc = s2m(cc.add(viewoffset(sz, this.mc).inv())); if(hit == null) wdgmsg("itemact", cc0, mc, ui.modflags()); else wdgmsg("itemact", cc0, mc, ui.modflags(), hit.id, hit.getc()); return(true); } private Map<String, Console.Command> cmdmap = new TreeMap<String, Console.Command>(); { cmdmap.put("cam", new Console.Command() { public void run(Console cons, String[] args) { if(args.length >= 2) { Class<? extends Camera> ct = camtypes.get(args[1]); String[] cargs = new String[args.length - 2]; System.arraycopy(args, 2, cargs, 0, cargs.length); if(ct != null) { try { MapView.this.cam = makecam(ct, cargs); Utils.setpref("defcam", args[1]); Utils.setprefb("camargs", Utils.serialize(cargs)); } catch(ClassNotFoundException e) { throw(new RuntimeException("no such camera: " + args[1])); } } else { throw(new RuntimeException("no such camera: " + args[1])); } } } }); cmdmap.put("plol", new Console.Command() { public void run(Console cons, String[] args) { Indir<Resource> res = Resource.load(args[1]).indir(); Message sdt; if(args.length > 2) sdt = new Message(0, Utils.hex2byte(args[2])); else sdt = new Message(0); Gob pl; if((playergob != -1) && ((pl = glob.oc.getgob(playergob)) != null)) pl.ols.add(new Gob.Overlay(-1, res, sdt)); } }); } public Map<String, Console.Command> findcmds() { return(cmdmap); } }