Implemented layers.
authornotzed@gmail.com <notzed@gmail.com@b8b59bfb-1aa4-4687-8f88-a62eeb14c21e>
Tue, 5 Mar 2013 10:25:06 +0000 (10:25 +0000)
committernotzed@gmail.com <notzed@gmail.com@b8b59bfb-1aa4-4687-8f88-a62eeb14c21e>
Tue, 5 Mar 2013 10:25:06 +0000 (10:25 +0000)
git-svn-id: file:///home/notzed/svn/duskz/trunk@10 b8b59bfb-1aa4-4687-8f88-a62eeb14c21e

DuskCommon/src/duskz/protocol/MapMessage.java
DuskServer/src/duskz/io/Tiled.java
DuskServer/src/duskz/proto/Junk.java
DuskServer/src/duskz/server/DuskEngine.java
DuskServer/src/duskz/server/entity/Item.java
DuskServer/src/duskz/server/entity/LivingThing.java
DuskServer/src/duskz/server/entity/TileMap.java
DuskZ/src/duskz/client/ClientMap.java
DuskZ/src/duskz/client/Dusk.java
DuskZ/src/duskz/client/fx/MainFrameFX.java

index 5917aaa..7b5c128 100644 (file)
@@ -34,18 +34,24 @@ public class MapMessage extends DuskMessage {
        public short y;
        public short width;
        public short height;
-       public short[] map;
+       // TODO: decide whether i want a ground layer, or entities to contain the layer they're rendered on
+       // Latter is more flexible.
+       public short groundLayer;
+       public short layerCount;
+       public short[][] map;
 
        public MapMessage() {
        }
 
-       public MapMessage(int name, int width, int height, int locx, int locy, short[] map) {
+       public MapMessage(int name, int width, int height, int locx, int locy, int groundLayer, int layerCount, short[][] map) {
                super(name);
 
                this.x = (short) locx;
                this.y = (short) locy;
                this.width = (short) width;
                this.height = (short) height;
+               this.groundLayer = (short)groundLayer;
+               this.layerCount = (short)layerCount;
                this.map = map;
        }
 
@@ -56,8 +62,13 @@ public class MapMessage extends DuskMessage {
                ostream.writeShort(y);
                ostream.writeShort(width);
                ostream.writeShort(height);
-               for (int i = 0; i < width * height; i++)
-                       ostream.writeShort(map[i]);
+               ostream.writeShort(groundLayer);
+               ostream.writeShort(layerCount);
+               for (int l = 0; l < layerCount; l++) {
+                       short[] layer = map[l];
+                       for (int i = 0; i < width * height; i++)
+                               ostream.writeShort(layer[i]);
+               }
        }
 
        @Override
@@ -69,10 +80,16 @@ public class MapMessage extends DuskMessage {
                y = istream.readShort();
                width = istream.readShort();
                height = istream.readShort();
+               groundLayer = istream.readShort();
+               layerCount = istream.readShort();
                len = width * height;
-               map = new short[len];
-               for (int i = 0; i < len; i++) {
-                       map[i] = istream.readShort();
+               map = new short[layerCount][];
+               for (int l = 0; l < layerCount; l++) {
+                       short[] layer = new short[width * height];
+                       map[l] = layer;
+                       for (int i = 0; i < len; i++) {
+                               layer[i] = istream.readShort();
+                       }
                }
        }
 
index b8aeda1..bb6e37c 100644 (file)
@@ -27,6 +27,7 @@ import duskz.io.tiled.Data;
 import duskz.io.tiled.Image;
 import duskz.io.tiled.Layer;
 import duskz.io.tiled.Map;
+import duskz.io.tiled.Properties;
 import duskz.io.tiled.Property;
 import duskz.io.tiled.Tile;
 import duskz.io.tiled.Tileset;
@@ -34,6 +35,7 @@ import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -65,7 +67,7 @@ import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 
 /**
- * Tiled loader/converter.  This is only experimental at this stage.
+ * Tiled loader/converter. This is only experimental at this stage.
  *
  * @author notzed
  */
@@ -543,11 +545,128 @@ public class Tiled {
                        Logger.getLogger(Tiled.class.getName()).log(Level.SEVERE, null, ex);
                }
        }
+
+       static void tiledToDusk(File src, File dst) throws IOException {
+               try {
+                       JAXBContext jc = JAXBContext.newInstance(Map.class);
+                       Unmarshaller u = jc.createUnmarshaller();
+                       Map tmap = (Map) u.unmarshal(src);
+
+                       System.out.println("Loaded map size: " + tmap.getWidth() + "x" + tmap.getHeight());
+
+                       List<TileInfo> tileInfo = new ArrayList<>();
+                       int tid = 0;
+
+                       // TODO: write out tileset info somewhere
+
+
+                       TileMap map = new TileMap(tmap.getWidth(), tmap.getHeight());
+
+                       int lid = 0;
+
+                       int nlayers = 0;
+                       int groundid = 0;
+                       int twidth = 0;
+                       int theight = 0;
+                       for (Object o : tmap.getLayerOrObjectgroup()) {
+                               if (o instanceof Layer) {
+                                       Layer l = (Layer) o;
+
+                                       if (l.getName() != null && l.getName().equals("ground")) {
+                                               twidth = l.getWidth();
+                                               theight = l.getHeight();
+                                               groundid = nlayers;
+                                       }
+
+                                       nlayers++;
+                               }
+                       }
+
+                       System.out.printf("Found %d layers\n", nlayers);
+                       System.out.printf("Ground on layer %d\n", groundid);
+                       // Create map 
+                       try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(dst))) {
+                               // Write header
+                               dos.writeInt(TileMap.MAGIC_LAYERED);
+                               dos.writeInt(0);
+                               dos.writeInt(0);
+                               dos.writeInt(twidth);
+                               dos.writeInt(theight);
+                               dos.writeInt(groundid);
+                               dos.writeInt(nlayers);
+
+                               int layerid = 0;
+                               for (Object o : tmap.getLayerOrObjectgroup()) {
+                                       if (o instanceof Layer) {
+                                               Layer l = (Layer) o;
+                                               int lwidth = l.getWidth();
+                                               int lheight = l.getHeight();
+                                               int len = l.getWidth() * l.getHeight();
+                                               int layer[] = new int[len];
+
+                                               // Read in layer and perform some optimisation on it
+                                               try (ImageInputStream dis = new MemoryCacheImageInputStream(decodeArray(l.getData().getvalue()))) {
+                                                       dis.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+                                                       for (int i = 0; i < len; i++) {
+                                                               layer[i] = dis.readInt();
+                                                       }
+
+                                               }
+
+                                               // Find bounds of used squares (if not ground layer)
+                                               int minx = lwidth - 1;
+                                               int maxx = 0;
+                                               int miny = lheight - 1;
+                                               int maxy = 0;
+
+                                               if (layerid != groundid) {
+                                                       for (int y = 0; y < lheight; y++) {
+                                                               for (int x = 0; x < lwidth; x++) {
+                                                                       if (layer[x + y * lwidth] != 0) {
+                                                                               minx = Math.min(x, minx);
+                                                                               miny = Math.min(y, miny);
+                                                                               maxx = Math.max(x, maxx);
+                                                                               maxy = Math.max(y, maxy);
+                                                                       }
+                                                               }
+                                                       }
+                                               } else {
+                                                       minx = 0;
+                                                       maxx = lwidth - 1;
+                                                       miny = 0;
+                                                       maxy = lheight - 1;
+                                               }
+
+                                               System.out.printf("Writing layer %d '%s' at %d,%d size %d,%d\n",
+                                                               layerid, l.getName(), minx, miny, maxx - minx + 1, maxy - miny + 1);
+
+                                               // Write out used area
+                                               dos.writeInt(minx);
+                                               dos.writeInt(miny);
+                                               dos.writeInt(maxx - minx + 1);
+                                               dos.writeInt(maxy - miny + 1);
+                                               for (int y = miny; y <= maxy; y++) {
+                                                       for (int x = minx; x <= maxx; x++) {
+                                                               dos.writeShort(layer[x + y * lwidth]);
+                                                       }
+                                               }
+                                       }
+
+                                       layerid++;
+                               }
+                       }
+
+               } catch (JAXBException ex) {
+                       Logger.getLogger(Tiled.class.getName()).log(Level.SEVERE, null, ex);
+               }
+       }
        // test load/save itled format map
 
        public static void main(String[] args) throws IOException {
-               testimport(new File("/home/notzed/house.tmx"),
-                               new File("/home/notzed/house.jar"));
+               //testimport(new File("/home/notzed/house.tmx"),
+               //              new File("/home/notzed/house.jar"));
+               tiledToDusk(new File("/home/notzed/test-map.tmx"),
+                               new File("/home/notzed/test-map.bin"));
                //testexport();
        }
 }
index 55a337e..b28f67e 100644 (file)
@@ -5,18 +5,27 @@ package duskz.proto;
 
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilePermission;
 import java.io.IOException;
+import java.lang.reflect.ReflectPermission;
+import java.net.NetPermission;
 import java.security.AccessControlContext;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.security.CodeSource;
+import java.security.Permission;
 import java.security.Permissions;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
+import java.util.Map.Entry;
+import java.util.PropertyPermission;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
 import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
 import javax.script.ScriptEngineManager;
@@ -66,6 +75,21 @@ public class Junk {
                }
        }
 
+       public static class Hidden {
+
+               void none() {
+                       System.out.println("none done");
+               }
+
+               public void tryPublic() {
+                       System.out.println("public done");
+               }
+
+               private void tryPrivate() {
+                       System.out.println("private done");
+               }
+       }
+
        public static class Thing {
 
                FileOutputStream fos;
@@ -78,6 +102,10 @@ public class Junk {
                        }
                }
 
+               public Hidden getHidden() {
+                       return new Hidden();
+               }
+
                public boolean isPet() {
                        return false;
                }
@@ -105,9 +133,318 @@ public class Junk {
                        }
                }
        }
+
+       static class ScriptManager extends SecurityManager {
+
+               @Override
+               public void checkSecurityAccess(String target) {
+                       System.out.println("check security access: " + target);
+                       super.checkSecurityAccess(target);
+               }
+
+               @Override
+               public void checkAccess(Thread t) {
+                       System.out.println("Check access thread: " + t);
+                       super.checkAccess(t);
+               }
+
+               @Override
+               public void checkAccess(ThreadGroup g) {
+                       System.out.println("Check access threadgroup: " + g);
+                       super.checkAccess(g);
+               }
+
+               @Override
+               public void checkDelete(String file) {
+                       System.out.println("check delete");
+                       super.checkDelete(file);
+               }
+
+               @Override
+               public void checkCreateClassLoader() {
+                       System.out.println("checkcreateclassloader");
+                       super.checkCreateClassLoader();
+               }
+
+               @Override
+               public void checkExec(String cmd) {
+                       System.out.println("check exec: " + cmd);
+                       super.checkExec(cmd);
+               }
+
+               @Override
+               public void checkExit(int status) {
+                       System.out.println("check exit");
+                       super.checkExit(status);
+               }
+
+               @Override
+               public void checkLink(String lib) {
+                       System.out.println("checklink: " + lib);
+                       super.checkLink(lib);
+               }
+
+               @Override
+               public void checkPackageDefinition(String pkg) {
+                       System.out.println("check package deifnition: " + pkg);
+                       super.checkPackageDefinition(pkg);
+               }
+
+               @Override
+               public void checkMemberAccess(Class<?> clazz, int which) {
+                       System.out.println("check member access: " + clazz + " " + which);
+                       super.checkMemberAccess(clazz, which);
+               }
+
+               @Override
+               public void checkPackageAccess(String pkg) {
+                       System.out.println("check package: " + pkg);
+                       super.checkPackageAccess(pkg);
+
+               }
+
+               @Override
+               public void checkPermission(Permission perm) {
+                       System.out.println("check permission: " + perm);
+
+                       if (perm instanceof FilePermission) {
+                               FilePermission fp = (FilePermission) perm;
+                               String path = fp.getName();
+
+                               // Check whitelist
+                               if (path.startsWith("/usr/java"))
+                                       return;
+                       } else if (perm instanceof NetPermission) {
+                               NetPermission np = (NetPermission) perm;
+                               String name = np.getName();
+
+                               if (name.equals("specifyStreamHandler")) {
+                                       return;
+                               }
+                       } else if (perm instanceof PropertyPermission) {
+                               PropertyPermission pp = (PropertyPermission) perm;
+                               String name = pp.getName();
+
+                               if (name.startsWith(("rhino."))) {
+                                       return;
+                               }
+                               return;
+                       } else if (perm instanceof ReflectPermission) {
+                               ReflectPermission rp = (ReflectPermission) perm;
+                               int depth = 0;
+                               System.out.println("bt\n");
+                               for (StackTraceElement elem : Thread.currentThread().getStackTrace()) {
+                                       //System.out.println(" " + elem);
+                                       //if (elem.getClassName().equals("com.sun.script.javascript.RhinoScriptEngine");
+                                       System.out.println(" " + elem.getClassName() + " " + elem.getMethodName());
+
+                                       String cl = elem.getClassName();
+                                       String m = elem.getMethodName();
+                                       if (cl.equals("com.sun.script.javascript.RhinoScriptEngine")
+                                                       && m.equals("getRuntimeScope")) {
+                                               depth++;
+                                       } else if (cl.equals("sun.org.mozilla.javascript.internal.NativeJavaMethod")
+                                                       && m.equals("call")) {
+                                               depth++;
+                                       }
+                               }
+                               System.out.println("match depth = " + depth);
+                               if (depth == 1)
+                                       return;
+                               super.checkPermission(perm);
+                               return;
+                       }
+
+                       //super.checkPermission(perm);
+               }
+
+               @Override
+               public void checkPermission(Permission perm, Object context) {
+                       System.out.println("check permission: " + perm + " ctx: " + context);
+                       super.checkPermission(perm, context);
+               }
+       }
+
+       static class ScriptLoader extends ClassLoader {
+
+               @Override
+               protected Package getPackage(String name) {
+                       System.out.println("get package: " + name);
+                       return super.getPackage(name);
+               }
+
+               @Override
+               protected Package[] getPackages() {
+                       System.out.println("get packages");
+                       return super.getPackages();
+               }
+
+               @Override
+               public Class<?> loadClass(String name) throws ClassNotFoundException {
+                       System.out.println("load class: " + name);
+                       return super.loadClass(name);
+               }
+
+               @Override
+               protected Class<?> findClass(String name) throws ClassNotFoundException {
+                       System.out.println("find class: " + name);
+                       return super.findClass(name);
+               }
+       }
        static ScriptEngine engine;
 
+       interface MobScript {
+               void onBattle(Thing thing);
+               //int getCost();
+       };
+       
        public static void main(String[] args) throws ScriptException, InterruptedException {
+               if (true) {
+                       Thing thing = new Thing();
+                       final ScriptEngineManager factory = new ScriptEngineManager();
+                       engine = factory.getEngineByName("JavaScript");
+
+
+                       engine.eval("a=1;");
+                       engine.eval("println(a);");
+
+                       String bob = ("var bob = {"
+                                       + " cost:1,\n"
+                                       + " description:'evil bob',\n"
+                                       + " onBattle: function(thing) {\n"
+                                       + "println('on battle!');\n"
+                                       + " }\n"
+                                       + "};\n"
+                                       + "");
+
+                       Compilable comp = ((Compilable)engine);
+                       CompiledScript cbob = comp.compile(bob);
+                       
+                       
+                       engine.eval(bob);
+                       
+                       engine.eval("var bob = {\n"
+                                       + "cost:1,\n"
+                                       + "description:'kind bob',\n"
+                                       + "onBattle: function(thing) {\n"
+                                       + " println('im a pacifist!');\n"
+                                       + "},"
+                                       + "onFlee: function(thing) {"
+                                       + "}"
+                                       + "}\n");
+                                       
+                       
+                       engine.eval(""
+                                       + "onBattle= function(thing) {"
+                                       + "println('on battle!');"
+                                       + " };"
+                                       + ""
+                                       + "");
+
+                       //MobScript m = ((Invocable)engine).getInterface(MobScript.class);
+                       //System.out.println("by interface");
+                       //m.onBattle(thing);
+                       
+                       engine.eval("var vars = { name: 'a', type: 'shit' };");
+
+                       engine.eval("function a() { println('first a'); }");
+                       engine.eval("a();");
+                       engine.eval("function a() { println('second a'); }");
+                       engine.eval("a();onBattle();");
+
+                       System.out.println("bob.cost = " + engine.eval("bob.cost"));
+                       System.out.println("bob.desc = " + engine.eval("bob.description"));
+                       
+                       try {
+                       Invocable iv = (Invocable)engine;
+                               iv.invokeMethod(engine.get("bob"), "onBattle", thing);
+                               
+                               MobScript a = ((Invocable)engine).getInterface(engine.get("bob"), MobScript.class);
+                               System.out.print("foo - ");
+                               a.onBattle(thing);
+                       //      System.out.println("cost? = " + a.getCost());
+                       } catch (NoSuchMethodException ex) {
+                               Logger.getLogger(Junk.class.getName()).log(Level.SEVERE, null, ex);
+                       }
+                       
+                       for (Entry<String, Object> e : engine.getBindings(ScriptContext.ENGINE_SCOPE).entrySet()) {
+                               System.out.println("`" + e.getKey() + "'= " + e.getValue() + " " + e.getValue().getClass().getName());
+                       }
+
+                       System.out.println("vars = " + engine.eval("bob.toSource();"));
+
+                       //engine.setContext(null);
+                       return;
+               }
+               if (true) {
+                       final ScriptLoader loader = new ScriptLoader();
+                       final ScriptEngineManager factory = new ScriptEngineManager(loader);
+                       engine = factory.getEngineByName("JavaScript");
+
+                       System.setSecurityManager(new ScriptManager());
+
+                       Permissions perms = new Permissions();
+                       //perms.add(new RuntimePermission("accessClassInPackage.sun.util.resources"));
+                       //perms.add(new AllPermission());
+                       //perms.add(new FilePermission("hack.txt", "read,write"));
+                       //      perms.add(new NetPermission("*"));
+                       //perms.add(new RuntimePermission("accessDeclaredMembers"));
+                       //perms.add(new RuntimePermission("accessClassInPackage"));
+                       // Cast to Certificate[] required because of ambiguity:
+                       ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), perms);
+                       AccessControlContext ac = new AccessControlContext(new ProtectionDomain[]{domain});
+
+                       // This doesn't work: security permissions are lost in the next thing.
+                       // bloody odd if you ask me
+                       AccessController.doPrivileged(new PrivilegedAction<ScriptEngine>() {
+                               void eval(String what) throws ScriptException {
+                                       System.out.println("\n\n****************\neval: " + what);
+                                       System.out.println();
+                                       engine.eval(what);
+                               }
+
+                               @Override
+                               public ScriptEngine run() {
+                                       try {
+                                               System.out.println("start script\n");
+                                               engine.put("thing", new Thing());
+                                               //              engine.eval("java.lang.System.out.println('system.out.println');");
+                                               //eval("thing.moveTo(1,1);");
+                                               eval("thing.getHidden().tryPublic();");
+                                               //              eval("thing.getHidden().none();");
+                                               eval("thing.getHidden().tryPrivate();");
+
+                                               try (FileOutputStream fos = new FileOutputStream("hack-a")) {
+                                                       fos.write(1);
+                                                       System.out.println("wrote hack-a");
+                                               } catch (IOException x) {
+                                                       x.printStackTrace();
+                                               } catch (AccessControlException x) {
+                                                       x.printStackTrace(System.out);
+                                               }
+
+                                               return null;
+                                       } catch (SecurityException ex) {
+                                               ex.printStackTrace();
+                                       } catch (ScriptException ex) {
+                                               ex.printStackTrace();
+                                       }
+                                       return null;
+                               }
+                       }, ac);
+                       return;
+               }
+               if (true) {
+                       Thing thing = new Thing();
+
+                       //System.setSecurityManager(new ScriptManager());
+
+                       ScriptEngineManager factory = new ScriptEngineManager(new ScriptLoader());
+                       engine = factory.getEngineByName("JavaScript");
+                       engine.eval("java.lang.System.out.println('system.out.println');");
+
+                       return;
+               }
                //new Junk().chatMessage(null, 16, 16, null);
                //new Junk().chatMessage(null, 2, 2, null);
                // create a script engine manager
@@ -200,7 +537,7 @@ public class Junk {
                                };
                                Thread t = new Thread(r);
                                t.start();
-                               
+
                                try {
                                        engine.eval("println(\"hello world\");");
                                        engine.eval("trigger.chat('this is a chat message from sandbox?');");
index 61eec18..f1f7a4a 100644 (file)
@@ -174,8 +174,13 @@ public class DuskEngine implements Runnable, DuskProtocol {
                        loadPrefs();
 
                        // Load Map
-                       File newmap = new File("shortmapx");
+                       File newmap = new File("defMaps/main");
                        if (newmap.exists()) {
+                               // FIXME: load multiple maps here
+                               log.printMessage(Log.INFO, "Loading Layered Map...");
+                               map = TileMap.loadLayered(newmap);
+                               log.printMessage(Log.VERBOSE, map.getCols() + "x" + map.getRows() + "x" + map.getLayerCount());
+                       } else if ((newmap = new File("shortmapx")).exists()) {
                                log.printMessage(Log.INFO, "Loading Map...");
                                map = TileMap.loadMapX(newmap);
                                log.printMessage(Log.VERBOSE, map.getCols() + "/" + map.getRows());
index 66fa8a7..7821b36 100644 (file)
@@ -64,11 +64,7 @@ public class Item extends DuskObject {
                        strOnDropScript = null;
        public String strOnUseScript = null,
                        strOnWearScript = null,
-                       strOnUnWearScript = null,
-                       strOnOpenScript = null,
-                       strOnCloseScript = null,
-                       strOnAddItemScript = null,
-                       strOnRemoveItemScript = null;
+                       strOnUnWearScript = null;
        public int intCost,
                        intType = -1,
                        intKind = -1,
index 90d3fb5..bb41604 100644 (file)
@@ -1678,18 +1678,29 @@ public class LivingThing extends DuskObject implements Runnable, DuskProtocol {
 
                int width = r * 2 + 1;
                int height = r * 2 + 1;
-               short[] tiles = new short[width * height];
+               short[] tiles = null;
                int i = 0;
-               //System.out.printf("map at %d,%d\n", x, y);
-               for (int my = y - r; my <= y + r; my++) {
-                       for (int mx = x - r; mx <= x + r; mx++) {
-                               tiles[i++] = game.map.inside(mx, my) ? game.map.getTile(mx, my) : 0;
-                               //System.out.printf("%s%3d", game.map.inside(mx, my) ? " " : "*", tiles[i-1]);
+               int nlayers = game.map.getLayerCount();
+               int nused = 0;
+               short[][] layers = new short[nlayers][];
+               int groundLayer = 0;
+               for (int l=0;l<nlayers;l++) {
+                       if (tiles == null)
+                               tiles = new short[width*height];
+                       
+                       short[] visible = game.map.getRegion(l, x-r, y-r, width, height, tiles);
+                       
+                       if (visible != null) {
+                               tiles = null;
+                               
+                               if (l == game.map.getGroundLayer())
+                                       groundLayer = nused;
+                               
+                               layers[nused++] = visible;
                        }
-                       //System.out.println("\n");
                }
-
-               send(new MapMessage(MSG_UPDATE_MAP, width, height, x, y, tiles));
+               
+               send(new MapMessage(MSG_UPDATE_MAP, width, height, x, y, groundLayer, nused, layers));
        }
 
        public void startBattle(LivingThing opponent) {
index 7d9d6cc..7b88d3f 100644 (file)
@@ -50,6 +50,11 @@ public class TileMap implements Iterable<TileMap.MapData> {
         * Entities, they are chained off each other for efficiency reasons
         */
        private DuskObject entities[];
+       /**
+        * Layers
+        */
+       private int groundLayer;
+       private TileLayer layers[];
        /**
         * privileges for each cell. This appears to be unimplemented in Dusk so
         * isn't here either.
@@ -88,6 +93,14 @@ public class TileMap implements Iterable<TileMap.MapData> {
                return cols;
        }
 
+       public int getLayerCount() {
+               return layers.length;
+       }
+
+       public int getGroundLayer() {
+               return groundLayer;
+       }
+
        public void saveMap(File path) throws IOException {
                path.delete();
                try (RandomAccessFile rafFile = new RandomAccessFile(path, "rw")) {
@@ -100,8 +113,115 @@ public class TileMap implements Iterable<TileMap.MapData> {
                        }
                }
        }
+
+       static class TileLayer {
+
+               int x, y;
+               int width, height;
+               private short[] tiles;
+
+               public TileLayer(int x, int y, int width, int height) {
+                       this.x = x;
+                       this.y = y;
+                       this.width = width;
+                       this.height = height;
+                       this.tiles = new short[width * height];
+               }
+
+               public TileLayer(int x, int y, int width, int height, short[] tiles) {
+                       this.x = x;
+                       this.y = y;
+                       this.width = width;
+                       this.height = height;
+                       this.tiles = tiles;
+               }
+
+               public boolean inside(int tx, int ty) {
+                       tx -= this.x;
+                       ty -= this.y;
+                       return tx >= 0 && tx < width
+                                       && ty >= 0 && ty < height;
+               }
+
+               public short getTile(int tx, int ty) {
+                       tx -= this.x;
+                       ty -= this.y;
+                       if (tx >= 0 && tx < width
+                                       && ty >= 0 && ty < height) {
+                               return tiles[tx + ty * width];
+                       } else
+                               return 0;
+               }
+       }
        public static final int FORMAT_BYTE = 0;
        public static final int FORMAT_SHORT = 1;
+       // 'mapz'
+       // TODO: gzip?
+       public static final int MAGIC_LAYERED = 0x7a70616d;
+
+       /**
+        * Load a layered map. Format is:
+        * magic: int
+        * version: int 0
+        * flags: int (0 - short)
+        * width: int must contain all layers
+        * height: int must contain all layers
+        * layer ground: int index of ground layer, ground layer must be same size as map
+        * layer count: int
+        * then layer count {
+        * layer x: int
+        * layer y: int
+        * layer width: int
+        * layer height: int
+        * layer data: short width*height in row major format
+        * }
+        *
+        * @param path
+        * @return
+        * @throws IOException
+        */
+       public static TileMap loadLayered(File path) throws IOException {
+               TileMap map;
+
+               try (DataInputStream mapFile = new DataInputStream(new FileInputStream(path))) {
+                       int magic = mapFile.readInt();
+                       int version = mapFile.readInt();
+                       int flags = mapFile.readInt();
+                       int cols = mapFile.readInt();
+                       int rows = mapFile.readInt();
+                       int groundLayer = mapFile.readInt();
+                       int layerCount = mapFile.readInt();
+
+                       if (magic != MAGIC_LAYERED
+                                       || version != 0) {
+                               throw new IOException("Invalid format/magic/unknown version");
+                       }
+
+                       map = new TileMap(cols, rows);
+
+                       map.groundLayer = groundLayer;
+                       map.layers = new TileLayer[layerCount];
+                       for (int l = 0; l < layerCount; l++) {
+                               int tx = mapFile.readInt();
+                               int ty = mapFile.readInt();
+                               int twidth = mapFile.readInt();
+                               int theight = mapFile.readInt();
+                               TileLayer tl;
+
+                               if (l == groundLayer)
+                                       tl = new TileLayer(tx, ty, twidth, theight, map.tiles);
+                               else
+                                       tl = new TileLayer(tx, ty, twidth, theight);
+
+                               map.layers[l] = tl;
+
+                               for (int i = 0; i < twidth * theight; i++) {
+                                       tl.tiles[i] = mapFile.readShort();
+                               }
+                       }
+               }
+               return map;
+       }
 
        // Map in row major format (i.e. more efficient)
        public static TileMap loadMapX(File path) throws IOException {
@@ -116,6 +236,9 @@ public class TileMap implements Iterable<TileMap.MapData> {
                                        map.setTile(x, y, mapFile.readShort());
                                }
                        }
+
+                       map.layers = new TileLayer[1];
+                       map.layers[0] = new TileLayer(0, 0, cols, rows, map.tiles);
                }
                return map;
        }
@@ -135,6 +258,8 @@ public class TileMap implements Iterable<TileMap.MapData> {
                                                map.setTile(x, y, mapFile.readShort());
                                }
                        }
+                       map.layers = new TileLayer[1];
+                       map.layers[0] = new TileLayer(0, 0, cols, rows, map.tiles);
                }
                return map;
        }
@@ -174,6 +299,66 @@ public class TileMap implements Iterable<TileMap.MapData> {
                                && y >= 0 && y < rows;
        }
 
+       public boolean inside(int layer, int x, int y) {
+               return x >= 0 && x < cols
+                               && y >= 0 && y < rows;
+       }
+
+       /**
+        * Calculate if the layer is empty over the given region
+        *
+        * @param layer
+        * @param x
+        * @param y
+        * @param width
+        * @param height
+        * @return
+        */
+       public boolean empty(int layer, int x, int y, int width, int height) {
+               TileLayer tl = layers[layer];
+
+               for (int ty = y; ty < y + height; ty++) {
+                       for (int tx = x; tx < x + width; tx++) {
+                               if (tl.getTile(tx, ty) != 0)
+                                       return false;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Get the region bounded by the supplied coordinates.
+        * If the region is all empty, then null is returned.
+        *
+        * @param layer
+        * @param x
+        * @param y
+        * @param width
+        * @param height
+        * @param region if supplied use this array otherwise allocate one
+        * @return
+        */
+       public short[] getRegion(int layer, int tx, int ty, int width, int height, short[] region) {
+               TileLayer tl = layers[layer];
+               if (region == null)
+                       region = new short[width * height];
+               boolean empty = true;
+
+               for (int y = 0; y < height; y++) {
+                       for (int x = 0; x < width; x++) {
+                               short t = tl.getTile(tx + x, ty + y);
+
+                               region[x + width * y] = t;
+
+                               empty &= t == 0;
+                       }
+               }
+               if (empty)
+                       return null;
+               else
+                       return region;
+       }
+
        public void setTile(int x, int y, int t) {
                tiles[x + y * cols] = (short) t;
        }
@@ -182,6 +367,10 @@ public class TileMap implements Iterable<TileMap.MapData> {
                return tiles[x + y * cols];
        }
 
+       public short getTile(int layer, int x, int y) {
+               return tiles[x + y * cols];
+       }
+
        public synchronized List<DuskObject> getEntities(int x, int y, List<DuskObject> list) {
                if (list == null)
                        list = new ArrayList<>();
@@ -328,7 +517,10 @@ public class TileMap implements Iterable<TileMap.MapData> {
 
                public int x, y;
                public final List<DuskObject> entities = new ArrayList<>();
+               // Tileid of ground layer
                public short tile;
+               // Tileid of visible layers in correct order
+               public short[] layers = new short[TileMap.this.layers.length];
 
                protected void setData(int x, int y) {
                        this.x = x;
@@ -340,6 +532,9 @@ public class TileMap implements Iterable<TileMap.MapData> {
                        } else {
                                this.tile = 0;
                        }
+                       for (int l = 0; l < layers.length; l++) {
+                               layers[l] = TileMap.this.layers[l].getTile(x, y);
+                       }
                }
        }
 
index 5055ac3..7e4b131 100644 (file)
@@ -21,6 +21,7 @@
  */
 package duskz.client;
 
+import duskz.protocol.MapMessage;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -45,10 +46,14 @@ public class ClientMap {
         * Offset of map (top-left corner)
         */
        public int offx, offy;
+       /**
+        * Which level is currently the ground level
+        */
+       private int groundLevel;
        /**
         * Tile indices for images
         */
-       private short[] tiles;
+       short[][] levels;
        /**
         * Entities by id
         */
@@ -76,7 +81,8 @@ public class ClientMap {
                this.cols = cols;
                this.rows = rows;
 
-               tiles = new short[cols * rows];
+               levels = new short[1][cols * rows];
+               groundLevel = 0;
        }
 
        public Collection<Entity> getEntities() {
@@ -88,11 +94,19 @@ public class ClientMap {
        }
 
        public int getTile(int x, int y) {
-               return tiles[x + y * cols];
+               return levels[groundLevel][x + y * cols];
+       }
+
+       public int getTile(int level, int x, int y) {
+               return levels[level][x + y * cols];
        }
 
-       public void setTile(int x, int y, int tile) {
-               tiles[x + y * cols] = (short) tile;
+       public int getGroundLevel() {
+               return groundLevel;
+       }
+
+       public int getLevelCount() {
+               return levels.length;
        }
 
        private Integer locationKey(int x, int y) {
@@ -196,19 +210,24 @@ public class ClientMap {
                                && ly >= 0 && ly < rows;
        }
 
-       public synchronized void updateTiles(int offx, int offy, short[] tiles) throws IOException {
+       //public synchronized void updateTiles(int offx, int offy, short[] tiles) throws IOException {
+       public synchronized void updateTiles(MapMessage mm) throws IOException {
+
                //for (int x = 0; x < cols; x++) {
                //      for (int y = 0; y < rows; y++) {
                //              setTile(x, y, Short.parseShort(instream.readLine()));
                //      }
                //}
-               if (tiles.length != rows * cols) {
+               if (mm.width != cols || mm.height != rows) {
                        throw new IOException("Protocol error incorrect map size");
                }
-               System.arraycopy(tiles, 0, this.tiles, 0, tiles.length);
 
-               this.offx = offx;
-               this.offy = offy;
+               //System.out.printf("Got map update %dx%d size %dx%dx%d\n", mm.x, mm.y, mm.width, mm.height, mm.layerCount);
+               
+               this.levels = mm.map;
+               this.groundLevel = mm.groundLayer;
+               this.offx = mm.x - (cols - 1) / 2;
+               this.offy = mm.y - (rows - 1) / 2;
 
                // Prune out of range thingies
                List<Entity> removing = new ArrayList<>();
index 6008d55..1a9d237 100644 (file)
@@ -328,7 +328,7 @@ public class Dusk implements Runnable, DuskProtocol {
                                                MapMessage mm = (MapMessage) dm;
 
                                                status.updateLocation(mm.x, mm.y);
-                                               map.updateTiles(mm.x - (map.cols - 1) / 2, mm.y - (map.rows - 1) / 2, mm.map);
+                                               map.updateTiles(mm);
                                                update();
                                                frame.setStatus(status);
                                                //frame.info.setText("HP: " + inthp + "/" + intmaxhp + " MP: " + intsp + "/" + intmaxsp + " Loc: " + LocX + "/" + LocY);
index 60823f7..cf7a40f 100644 (file)
@@ -861,8 +861,8 @@ public class MainFrameFX extends StackPane implements GUI {
                        }
                });
        }
-       //DataManagerFX data;
-       Image tileImage;
+       DataManagerFX data;
+       //Image tileImage;
        int tileSize;
        Image playerImage;
        int playerSize;
@@ -871,51 +871,59 @@ public class MainFrameFX extends StackPane implements GUI {
 
        @Override
        public void setImages(String tiles, int tileSize, String players, int playerSize, String sprites, int spriteSize) {
-               //try {
-               System.out.println("set tile image");
-               // FIXME: put sprites into data manager
-               //data = new DataManagerFX("/home/notzed/house.jar");
-               //data.open();
-               tileImage = new Image(tiles, false);
-               this.tileSize = tileSize;
-               playerImage = new Image(players, false);
-               this.playerSize = playerSize;
-               spriteImage = new Image(sprites, false);
-               this.spriteSize = spriteSize;
-               //} catch (IOException ex) {
-               //      Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex);
-               //}
+               try {
+                       System.out.println("set tile image");
+                       // FIXME: put sprites into data manager
+                       data = new DataManagerFX("/home/notzed/house.jar");
+                       data.open();
+                       //tileImage = new Image(tiles, false);
+                       this.tileSize = tileSize;
+                       playerImage = new Image(players, false);
+                       this.playerSize = playerSize;
+                       spriteImage = new Image(sprites, false);
+                       this.spriteSize = spriteSize;
+               } catch (IOException ex) {
+                       Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex);
+               }
        }
 
        @Override
        public void updateMap(ClientMap map) {
-               // First ugly cut - just create a whole set of tiles
+               // Note this has map synchronized already
+
+               // Since we get a whole update for the map every time, there isn't much we
+               // can practically do apart from simply build a whole new page to display.
                System.out.println("update map");
-               //if (data == null) {
-               if (tileImage == null) {
+               if (data == null) {
+                       //if (tileImage == null) {
                        System.out.println("Map not ready yet");
                        return;
                }
 
                final ArrayList<Node> children = new ArrayList<>();
                final ArrayList<Node> upper = new ArrayList<>();
+               int levelCount = map.getLevelCount();
 
                // Build map
-               for (int y = 0; y < map.rows; y++) {
-                       for (int x = 0; x < map.cols; x++) {
-                               ImageView iv = new ImageView(tileImage);
-                               int tileid = map.getTile(x, y);
-                               iv.setViewport(new Rectangle2D(tileid * tileSize, 0, tileSize, tileSize));
-                               iv.relocate(x * tileSize, y * tileSize);
-                               children.add(iv);
-
-                               //children.add(data.createTile(map.getTile(x, y), x, y, tileSize, tileSize));
-
-                               // TODO: names always? on top
-                               Collection<Entity> ents = map.getEntities(x + map.offx, y + map.offy);
-                               if (ents != null) {
-                                       for (Entity e : ents) {
-                                               drawEntity(e, map.offx, map.offy, children, upper);
+               for (int l = 0; l < levelCount; l++) {
+                       for (int y = 0; y < map.rows; y++) {
+                               // Draw tiles first for whole row
+                               for (int x = 0; x < map.cols; x++) {
+                                       int tileid = map.getTile(l, x, y);
+                                       if (tileid != 0) {
+                                               children.add(data.createTile(tileid, x, y, tileSize, tileSize));
+                                       }
+                               }
+                               
+                               // Now check for entities over this layer row
+                               if (l == map.getGroundLevel()) {
+                                       for (int x = 0; x < map.cols; x++) {
+                                               Collection<Entity> ents = map.getEntities(x + map.offx, y + map.offy);
+                                               if (ents != null) {
+                                                       for (Entity e : ents) {
+                                                               drawEntity(e, map.offx, map.offy, children, upper);
+                                                       }
+                                               }
                                        }
                                }
                        }