/* * 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 java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.net.URL; import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.WeakHashMap; import javax.imageio.ImageIO; public class MiniMap extends Widget { private static final Coord VRSZ = new Coord(84, 84); private static final Color VRFILL = new Color(128, 128, 128, 96); private static final Color VRBORDER = new Color(200, 96, 200, 216); static Map<String, Tex> grids = new WeakHashMap<String, Tex>(); static Map<String, Tex> simpleTex = new WeakHashMap<String, Tex>(); static Set<String> loading = new HashSet<String>(); static Loader loader = new Loader(); static Coord mappingStartPoint = null; static long mappingSession = 0; static Map<String, Coord> gridsHashes = new TreeMap<String, Coord>(); static Map<Coord, String> coordHashes = new TreeMap<Coord, String>(); public Map<Coord, Tex> caveTex = new TreeMap<Coord, Tex>(); public static final Tex bg = Resource.loadtex("gfx/hud/mmap/ptex"); public static final Tex nomap = Resource.loadtex("gfx/hud/mmap/nomap"); public static final Resource plx = Resource.load("gfx/hud/mmap/x"); public Coord off, doff; boolean hidden = false, grid = false;; MapView mv; boolean dm = false; public int scale = 4; double scales[] = { 0.5, 0.66, 0.8, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2 }; private Tex VR; public double getScale() { return scales[scale]; } public void setScale(int scale) { this.scale = Math.max(0, Math.min(scale, scales.length - 1)); } static class Loader implements Runnable { Thread me = null; private InputStream getreal(String nm) throws IOException { URL url = new URL(Config.mapurl, nm + ".png"); URLConnection c = url.openConnection(); c.addRequestProperty("User-Agent", "Haven/1.0"); InputStream s = c.getInputStream(); /* * I've commented this out, since it seems that the JNLP * PersistenceService (or at least Sun's implementation of it) is * SLOWER THAN SNAILS, so this caused more problems than it solved. * * if(ResCache.global != null) { StreamTee tee = new StreamTee(s); * tee.setncwe(); tee.attach(ResCache.global.store("mm/" + nm)); s = * tee; } */ return (s); } private InputStream getcached(String nm) throws IOException { /* * if(ResCache.global == null) throw(new * FileNotFoundException("No resource cache installed")); * return(ResCache.global.fetch("mm/" + nm)); */ if (mappingSession > 0) { String fileName; if (gridsHashes.containsKey(nm)) { Coord coordinates = gridsHashes.get(nm); fileName = "tile_" + coordinates.x + "_" + coordinates.y; } else { fileName = nm; } File inputfile = new File("map/" + Utils.sessdate(mappingSession) + "/" + fileName + ".png"); if (!inputfile.exists()) throw (new FileNotFoundException("Minimap cache not found")); return new FileInputStream(inputfile); } throw (new FileNotFoundException("No resource cache installed")); } public void run() { try { while (true) { String grid; synchronized (grids) { grid = null; for (String cg : loading) { grid = cg; break; } } if (grid == null) break; try { InputStream in; boolean cached; try { in = getcached(grid); cached = true; } catch (FileNotFoundException e) { in = getreal(grid); cached = false; } BufferedImage img; try { img = ImageIO.read(in); if ((!cached) & (mappingSession > 0)) { String fileName; if (gridsHashes.containsKey(grid)) { Coord coordinates = gridsHashes.get(grid); fileName = "tile_" + coordinates.x + "_" + coordinates.y; } else { fileName = grid; } File outputfile = new File("map/" + Utils.sessdate(mappingSession) + "/" + fileName + ".png"); ImageIO.write(img, "png", outputfile); } } finally { Utils.readtileof(in); in.close(); } Tex tex = new TexI(img); synchronized (grids) { grids.put(grid, tex); loading.remove(grid); } } catch (IOException e) { synchronized (grids) { grids.put(grid, null); loading.remove(grid); } } } } finally { synchronized (this) { me = null; } } } void start() { synchronized (this) { if (me == null) { me = new HackThread(this, "Minimap loader"); me.setDaemon(true); me.start(); } } } void req(String nm) { synchronized (grids) { if (loading.contains(nm)) return; loading.add(nm); start(); } } } public static void newMappingSession() { long newSession = System.currentTimeMillis(); String date = Utils.sessdate(newSession); try { (new File("map/" + date)).mkdirs(); Writer currentSessionFile = new FileWriter("map/currentsession.js"); currentSessionFile.write("var currentSession = '" + date + "';\n"); currentSessionFile.close(); mappingSession = newSession; gridsHashes.clear(); coordHashes.clear(); } catch (IOException ex) { } } public MiniMap(Coord c, Coord sz, Widget parent, MapView mv) { super(c, sz, parent); this.mv = mv; off = new Coord(); BufferedImage bi = new BufferedImage(VRSZ.x + 1, VRSZ.y + 1, BufferedImage.TYPE_INT_ARGB); Graphics2D gr = bi.createGraphics(); gr.setColor(VRFILL); gr.fillRect(0, 0, VRSZ.x, VRSZ.y); gr.setColor(VRBORDER); gr.drawRect(0, 0, VRSZ.x, VRSZ.y); gr.drawImage(bi, null, 0, 0); VR = new TexI(bi); newMappingSession(); } public static Tex getgrid(final String nm) { return (AccessController.doPrivileged(new PrivilegedAction<Tex>() { public Tex run() { synchronized (grids) { if (grids.containsKey(nm)) { return (grids.get(nm)); } else { loader.req(nm); return (null); } } } })); } public static Tex getsimple(final String nm) { synchronized (simpleTex) { if (simpleTex.containsKey(nm)) { return simpleTex.get(nm); } return null; } } public Coord xlate(Coord c, boolean in) { if (in) { return c.div(getScale()); } else { return c.mul(getScale()); } } public BufferedImage getCurrentMapTile() { double scale = getScale(); Coord hsz = sz.div(scale); Coord tc = mv.mc.div(tilesz); Coord ulg = tc.div(cmaps); while ((ulg.x * cmaps.x) - tc.x + (hsz.x / 2) > 0) ulg.x--; while ((ulg.y * cmaps.y) - tc.y + (hsz.y / 2) > 0) ulg.y--; for (int y = ulg.y; (y * cmaps.y) - tc.y + (hsz.y / 2) < hsz.y; y++) { for (int x = ulg.x; (x * cmaps.x) - tc.x + (hsz.x / 2) < hsz.x; x++) { Coord cg = new Coord(x, y); Coord gwc = cg.mul(cmaps).add(tc.inv()).add(hsz.div(2)); Gob player = ui.sess.glob.oc.getgob(ui.mainview.playergob); Coord ptc = hsz.div(2).sub(tc).add(player.getc().div(tilesz)); if (ptc.x >= gwc.x && ptc.x <= gwc.x+cmaps.x && ptc.y >= gwc.y && ptc.y <= gwc.y+cmaps.y) { Grid grid; synchronized (ui.sess.glob.map.grids) { grid = ui.sess.glob.map.grids.get(cg); } String mnm = (grid == null) ? coordHashes.get(cg) : grid.mnm;; Tex tex = getsimple(mnm); return ((TexI)tex).back; } } } return null; } public void draw(GOut og) { double scale = getScale(); Coord hsz = sz.div(scale); Coord tc = mv.mc.div(tilesz).add(off.div(scale)); Coord ulg = tc.div(cmaps); while ((ulg.x * cmaps.x) - tc.x + (hsz.x / 2) > 0) ulg.x--; while ((ulg.y * cmaps.y) - tc.y + (hsz.y / 2) > 0) ulg.y--; if (!hidden) { Coord s = bg.sz(); for (int y = 0; (y * s.y) < sz.y; y++) { for (int x = 0; (x * s.x) < sz.x; x++) { og.image(bg, new Coord(x * s.x, y * s.y)); } } } GOut g = og.reclip(og.ul.mul((1 - scale) / scale), hsz); g.gl.glPushMatrix(); g.scale(scale); synchronized (caveTex) { synchronized (simpleTex) { for (int y = ulg.y; (y * cmaps.y) - tc.y + (hsz.y / 2) < hsz.y; y++) { for (int x = ulg.x; (x * cmaps.x) - tc.x + (hsz.x / 2) < hsz.x; x++) { Coord cg = new Coord(x, y); if (mappingStartPoint == null) { mappingStartPoint = new Coord(cg); } Grid grid; synchronized (ui.sess.glob.map.req) { synchronized (ui.sess.glob.map.grids) { grid = ui.sess.glob.map.grids.get(cg); if (grid == null) ui.sess.glob.map.request(cg); } } Coord relativeCoordinates = cg.sub(mappingStartPoint); String mnm = null; if (grid == null) { mnm = coordHashes.get(relativeCoordinates); } else { mnm = grid.mnm; } Tex tex = null; if (mnm != null) { caveTex.clear(); if (!gridsHashes.containsKey(mnm)) { if ((Math.abs(relativeCoordinates.x) > 450) || (Math.abs(relativeCoordinates.y) > 450)) { newMappingSession(); mappingStartPoint = cg; relativeCoordinates = new Coord(0, 0); } gridsHashes.put(mnm, relativeCoordinates); coordHashes.put(relativeCoordinates, mnm); } else { Coord coordinates = gridsHashes.get(mnm); if (!coordinates.equals(relativeCoordinates)) { mappingStartPoint = mappingStartPoint.add(relativeCoordinates.sub(coordinates)); } } if (grid != null) { simpleTex.put(mnm, grid.getTex()); } if (!Config.simplemap) { tex = getgrid(mnm); } if (Config.simplemap || tex == null) { tex = getsimple(mnm); } } else { if (grid != null) { tex = grid.getTex(); if (tex != null) { caveTex.put(cg, tex); } } tex = caveTex.get(cg); } if (tex == null) continue; if (!hidden) g.image(tex, cg.mul(cmaps).add(tc.inv()).add(hsz.div(2))); } } } } // grid if (grid && !hidden) { g.chcolor(200, 32, 64, 255); Coord c1, c2; c1 = new Coord(); c2 = new Coord(hsz.x, 0); for (int y = ulg.y + 1; (y * cmaps.y) - tc.y + (hsz.y / 2) < hsz.y; y++) { c1.y = (y * cmaps.y) - tc.y + (hsz.y / 2); c2.y = c1.y; g.line(c1, c2, 1); } c1 = new Coord(); c2 = new Coord(0, hsz.y); for (int x = ulg.x + 1; (x * cmaps.x) - tc.x + (hsz.x / 2) < hsz.x; x++) { c1.x = (x * cmaps.x) - tc.x + (hsz.x / 2); c2.x = c1.x; g.line(c1, c2, 1); } g.chcolor(); } // end of grid if ((!plx.loading) && (!hidden)) { // highlight items Coord c0 = hsz.div(2).sub(tc); Coord isz = new Coord(20, 20); Coord psz = new Coord(5, 5); Coord c; if (Config.showViewDistance) { Gob player = ui.sess.glob.oc.getgob(mv.playergob); if (player != null && (c = player.getc()) != null) { c = c0.add(c.div(tilesz)); g.aimage(VR, c, 0.5, 0.5); } } if (Config.radar) { if (Config.dontScaleMMIcons) { isz = isz.div(scale); psz = psz.div(scale); } synchronized (ui.sess.glob.oc) { for (Gob gob : ui.sess.glob.oc) { c = gob.getc(); if (c == null) { continue; } String name = gob.resname(); if (name == null) { continue; } ; c = c0.add(c.div(tilesz)); if (gob.isHighlight() && Config.highlightItemList.contains(name)) { Tex tx = Config.hlcfg.get(name).geticon(); g.aimage(tx, c, isz, 0.5, 0.5); } if (gob.isHuman()) { if (gob.id == ui.mainview.playergob) { continue; } KinInfo kin = gob.getattr(KinInfo.class); if (kin != null) { g.chcolor(BuddyWnd.gc[kin.group]); } else { g.chcolor(); } g.fellipse(c, psz); g.chcolor(); } if (Config.showBeast && gob.isBeast()) { Tex tx = Config.hlcfg.get(gob.beastname).geticon(); g.aimage(tx, c, isz, 0.5, 0.5); } } } } synchronized (ui.sess.glob.party.memb) { for (Party.Member m : ui.sess.glob.party.memb.values()) { Coord ptc = m.getc(); if (ptc == null) continue; ptc = c0.add(ptc.div(tilesz)); g.chcolor(m.col.getRed(), m.col.getGreen(), m.col.getBlue(), 128); g.image(plx.layer(Resource.imgc).tex(), ptc.add(plx.layer(Resource.negc).cc.inv())); g.chcolor(); } } } g.gl.glPopMatrix(); super.draw(og); } public boolean isCave() { synchronized (caveTex) { return !caveTex.isEmpty(); } } public void saveCaveMaps() { synchronized (caveTex) { Coord rc = null; String sess = Utils.sessdate(System.currentTimeMillis()); File outputfile = new File("cave/" + sess); try { Writer currentSessionFile = new FileWriter("cave/currentsession.js"); currentSessionFile.write("var currentSession = '" + sess + "';\n"); currentSessionFile.close(); } catch (IOException e1) { } outputfile.mkdirs(); for (Coord c : caveTex.keySet()) { if (rc == null) { rc = c; } TexI tex = (TexI) caveTex.get(c); c = c.sub(rc); String fileName = "tile_" + c.x + "_" + c.y; outputfile = new File("cave/" + sess + "/" + fileName + ".png"); try { ImageIO.write(tex.back, "png", outputfile); } catch (IOException e) { } } } } public void saveSimpleMaps() { synchronized (simpleTex) { Coord rc = null; String sess = Utils.sessdate(System.currentTimeMillis()); File outputfile = new File("simplemap/" + sess); try { Writer currentSessionFile = new FileWriter("simplemap/currentsession.js"); currentSessionFile.write("var currentSession = '" + sess + "';\n"); currentSessionFile.close(); } catch (IOException e1) { } outputfile.mkdirs(); for (String sc : simpleTex.keySet()) { Coord c = gridsHashes.get(sc); if (c == null) { continue; } if (rc == null) { rc = c; } TexI tex = (TexI) simpleTex.get(sc); c = c.sub(rc); String fileName = "tile_" + c.x + "_" + c.y; outputfile = new File("simplemap/" + sess + "/" + fileName + ".png"); try { ImageIO.write(tex.back, "png", outputfile); } catch (IOException e) { } } } } public boolean mousedown(Coord c, int button) { if (button == 1) { ui.grabmouse(this); dm = true; doff = c; } return (true); } public boolean mouseup(Coord c, int button) { if (dm) { ui.grabmouse(null); dm = false; return true; } else { return super.mouseup(c, button); } } public void mousemove(Coord c) { if (dm) { off = off.add(doff.sub(c)); doff = c; } else { super.mousemove(c); } } public void hide() { hidden = true; } public void show() { hidden = false; } }