/* * 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.Graphics; 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>(); static 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 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; } }