From 967d6e93242a50046f02ca4dd9d7c70499565121 Mon Sep 17 00:00:00 2001 From: "notzed@gmail.com" Date: Tue, 5 Mar 2013 10:25:06 +0000 Subject: [PATCH] Implemented layers. git-svn-id: file:///home/notzed/svn/duskz/trunk@10 b8b59bfb-1aa4-4687-8f88-a62eeb14c21e --- DuskCommon/src/duskz/protocol/MapMessage.java | 31 +- DuskServer/src/duskz/io/Tiled.java | 125 ++++++- DuskServer/src/duskz/proto/Junk.java | 339 +++++++++++++++++- DuskServer/src/duskz/server/DuskEngine.java | 7 +- DuskServer/src/duskz/server/entity/Item.java | 6 +- .../src/duskz/server/entity/LivingThing.java | 29 +- .../src/duskz/server/entity/TileMap.java | 195 ++++++++++ DuskZ/src/duskz/client/ClientMap.java | 39 +- DuskZ/src/duskz/client/Dusk.java | 2 +- DuskZ/src/duskz/client/fx/MainFrameFX.java | 76 ++-- 10 files changed, 778 insertions(+), 71 deletions(-) diff --git a/DuskCommon/src/duskz/protocol/MapMessage.java b/DuskCommon/src/duskz/protocol/MapMessage.java index 5917aaa..7b5c128 100644 --- a/DuskCommon/src/duskz/protocol/MapMessage.java +++ b/DuskCommon/src/duskz/protocol/MapMessage.java @@ -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(); + } } } diff --git a/DuskServer/src/duskz/io/Tiled.java b/DuskServer/src/duskz/io/Tiled.java index b8aeda1..bb6e37c 100644 --- a/DuskServer/src/duskz/io/Tiled.java +++ b/DuskServer/src/duskz/io/Tiled.java @@ -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 = 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(); } } diff --git a/DuskServer/src/duskz/proto/Junk.java b/DuskServer/src/duskz/proto/Junk.java index 55a337e..b28f67e 100644 --- a/DuskServer/src/duskz/proto/Junk.java +++ b/DuskServer/src/duskz/proto/Junk.java @@ -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 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() { + 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?');"); diff --git a/DuskServer/src/duskz/server/DuskEngine.java b/DuskServer/src/duskz/server/DuskEngine.java index 61eec18..f1f7a4a 100644 --- a/DuskServer/src/duskz/server/DuskEngine.java +++ b/DuskServer/src/duskz/server/DuskEngine.java @@ -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()); diff --git a/DuskServer/src/duskz/server/entity/Item.java b/DuskServer/src/duskz/server/entity/Item.java index 66fa8a7..7821b36 100644 --- a/DuskServer/src/duskz/server/entity/Item.java +++ b/DuskServer/src/duskz/server/entity/Item.java @@ -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, diff --git a/DuskServer/src/duskz/server/entity/LivingThing.java b/DuskServer/src/duskz/server/entity/LivingThing.java index 90d3fb5..bb41604 100644 --- a/DuskServer/src/duskz/server/entity/LivingThing.java +++ b/DuskServer/src/duskz/server/entity/LivingThing.java @@ -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 { * 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 { 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 { } } } + + 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 { 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 { 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 { && 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 { return tiles[x + y * cols]; } + public short getTile(int layer, int x, int y) { + return tiles[x + y * cols]; + } + public synchronized List getEntities(int x, int y, List list) { if (list == null) list = new ArrayList<>(); @@ -328,7 +517,10 @@ public class TileMap implements Iterable { public int x, y; public final List 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 { } else { this.tile = 0; } + for (int l = 0; l < layers.length; l++) { + layers[l] = TileMap.this.layers[l].getTile(x, y); + } } } diff --git a/DuskZ/src/duskz/client/ClientMap.java b/DuskZ/src/duskz/client/ClientMap.java index 5055ac3..7e4b131 100644 --- a/DuskZ/src/duskz/client/ClientMap.java +++ b/DuskZ/src/duskz/client/ClientMap.java @@ -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 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 removing = new ArrayList<>(); diff --git a/DuskZ/src/duskz/client/Dusk.java b/DuskZ/src/duskz/client/Dusk.java index 6008d55..1a9d237 100644 --- a/DuskZ/src/duskz/client/Dusk.java +++ b/DuskZ/src/duskz/client/Dusk.java @@ -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); diff --git a/DuskZ/src/duskz/client/fx/MainFrameFX.java b/DuskZ/src/duskz/client/fx/MainFrameFX.java index 60823f7..cf7a40f 100644 --- a/DuskZ/src/duskz/client/fx/MainFrameFX.java +++ b/DuskZ/src/duskz/client/fx/MainFrameFX.java @@ -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 children = new ArrayList<>(); final ArrayList 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 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 ents = map.getEntities(x + map.offx, y + map.offy); + if (ents != null) { + for (Entity e : ents) { + drawEntity(e, map.offx, map.offy, children, upper); + } + } } } } -- 2.39.2