diff --git a/src/flambe/Entity.hx b/src/flambe/Entity.hx index d1983d46..7642d48e 100644 --- a/src/flambe/Entity.hx +++ b/src/flambe/Entity.hx @@ -44,6 +44,12 @@ using Lambda; /** This entity's first component. */ public var firstComponent (default, null) :Component = null; + + //Use setZOrder() to set this value instead of zOrder = x + public var zOrder : Int = 0; + public var orderOfArrival : Int = 1; + + public static var globalOrderOfArrival : Int = 1; public function new () { @@ -165,39 +171,80 @@ using Lambda; return untyped _compMap[name]; } - /** + /** * Adds a child to this entity. * @param append Whether to add the entity to the end or beginning of the child list. * @returns This instance, for chaining. */ - public function addChild (entity :Entity, append :Bool=true) + public function addChild (entity :Entity, append :Bool = true, ?zOrder : Int) { if (entity.parent != null) { entity.parent.removeChild(entity); } entity.parent = this; - if (append) { - // Append it to the child list - var tail = null, p = firstChild; - while (p != null) { - tail = p; - p = p.next; - } - if (tail != null) { - tail.next = entity; - } else { - firstChild = entity; - } - - } else { - // Prepend it to the child list - entity.next = firstChild; - firstChild = entity; - } + if (append) { + var tail = null, p = firstChild; + + while (p != null) { + tail = p; + p = p.next; + } + if (tail != null) { + if (zOrder == null) { + zOrder = tail.zOrder; + } + if (tail.zOrder <= zOrder) { + tail.next = entity; + } else { + var p = firstChild; + var pre : Entity = null; + while (p != null) { + if (p.zOrder > zOrder) { + if (pre != null) { + pre.next = entity; + entity.next = p; + } else { + entity.next = firstChild; + firstChild = entity; + } + break; + } else { + pre = p; + p = p.next; + } + } + } + } else { + firstChild = entity; + if (zOrder == null) { + zOrder = 0; + } + } + } else { + if (firstChild == null) { + zOrder = 0; + } else { + zOrder = firstChild.zOrder - 1; + } + entity.next = firstChild; + firstChild = entity; + } + + entity.zOrder = zOrder; return this; } + + public function setZOrder(z : Int) { + if (this.zOrder == z) { + return; + } else { + this.zOrder = z; + this.parent.addChild(this, true, this.zOrder); + } + + } public function removeChild (entity :Entity) { diff --git a/src/flambe/math/Rectangle.hx b/src/flambe/math/Rectangle.hx index 6fae01ef..15f430e1 100644 --- a/src/flambe/math/Rectangle.hx +++ b/src/flambe/math/Rectangle.hx @@ -71,6 +71,37 @@ class Rectangle { return x == other.x && y == other.y && width == other.width && height == other.height; } + + public function intersect(rect : Rectangle) : Bool { + return !(rectGetMaxX() < rect.rectGetMinX() || + rect.rectGetMaxX() < rectGetMinX() || + rectGetMaxY() < rect.rectGetMinY() || + rect.rectGetMaxY() < rectGetMinY()); + } + + public function rectGetMaxX() : Float{ + return (x + width); + } + + public function rectGetMidX() : Float { + return ((x + width) / 2); + } + + public function rectGetMinX() : Float { + return x; + } + + public function rectGetMaxY() : Float{ + return (y + height); + } + + public function rectGetMidY() : Float { + return ((y + height) / 2); + } + + public function rectGetMinY() : Float { + return this.y; + } #if debug @:keep #end public function toString () :String { diff --git a/src/flambe/math/Size.hx b/src/flambe/math/Size.hx new file mode 100644 index 00000000..8ed36c9e --- /dev/null +++ b/src/flambe/math/Size.hx @@ -0,0 +1,35 @@ +package flambe.math; + +/** + * ... + * @author Ang Li(李昂) + */ +class Size +{ + + public var width : Float; + public var height : Float; + public function new(?width : Float = 0, ?height : Float = 0) + { + this.width = width; + this.height = height; + } + + public function setSize(width : Float , height : Float) { + this.width = width; + this.height = height; + } + + public function equals(size : Size) : Bool { + if (this.width == size.width && this.height == size.height) { + return true; + } else { + return false; + } + } + + public function toString() : String { + return '$width x $height'; + } + +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXBase64.hx b/src/flambe/tilemap/TMXBase64.hx new file mode 100644 index 00000000..0163092f --- /dev/null +++ b/src/flambe/tilemap/TMXBase64.hx @@ -0,0 +1,180 @@ +package flambe.tilemap; + +import haxe.io.Bytes; +import haxe.io.BytesInput; +import haxe.zip.InflateImpl; +#if flash +import flash.utils.ByteArray; +import flash.utils.Endian; +#end + +class TMXBase64 +{ + + public function new() + { + + } + private static inline var BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + public static function decode(input : String ) : String { + input = StringTools.ltrim(input); + input = StringTools.rtrim(input); + var output : Array = []; + var enc1 : Int; + var enc2 : Int; + var enc3 : Int; + var enc4 : Int; + var i : Int = 0; + var chr1 : Int; + var chr2 : Int; + var chr3 : Int; + + while (i < input.length) { + enc1 = BASE64_CHARS.indexOf(input.charAt(i++)); + enc2 = BASE64_CHARS.indexOf(input.charAt(i++)); + enc3 = BASE64_CHARS.indexOf(input.charAt(i++)); + enc4 = BASE64_CHARS.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output.push(String.fromCharCode(chr1)); + if (enc3 != 64) { + output.push(String.fromCharCode(chr2)); + } + if (enc4 != 64) { + output.push(String.fromCharCode(chr3)); + } + } + + var o : String = output.join(''); + var count : Int = 0; + for (i in output) { + var x = i.charCodeAt(0); + count++; + } + return o; + } + + public static function decodeAsArray(input : String , lineWidth : Int , ?bytes : Int = 4) : Array { + var dec : String = decode(input); + var ar : Array = []; + var len : Int = Std.int(dec.length / bytes); + + for (i in 0...len) { + ar[i] = 0; + var j = bytes - 1; + while (j >= 0) { + var t = dec.charCodeAt((i * bytes) + j) << (j * 8); + ar[i] += t; + j--; + } + } + return ar; + } + + private static function decodeAsOneArray(input : String , lineWidth : Int , ?bytes : Int = 4) : Array { + var dec : String = decode(input); + var ar : Array = []; + var len : Int = Std.int(dec.length / bytes); + + for (i in 0...len) { + ar[i] = 0; + var j = bytes - 1; + while (j >= 0) { + var t = dec.charCodeAt((i * bytes) + j) << (j * 8); + ar[i] += t; + j--; + } + } + return ar; + } + + private static function decodeAsArrayBytes(byte : Bytes , lineWidth : Int , ?bytes : Int = 4) : Array { + var ar : Array = []; + var len : Int = Std.int(byte.length / bytes); + + for (i in 0...len) { + ar[i] = 0; + var j = bytes - 1; + while (j >= 0) { + var t = byte.get((i * bytes) + j) << (j * 8); + ar[i] += t; + j--; + } + } + + return ar; + } + + #if js + public static function unzip(input : String, lineWidth : Int ) : Array { + var tempString1 : String = decode(input); + var arr : Array = TMXBase64.decodeAsOneArray(input, 0, 1); + + var b = Bytes.ofData(cast arr); + var bytes : Bytes = InflateImpl.run(new BytesInput(b)); + var ret : Array = TMXBase64.decodeAsArrayBytes(bytes, lineWidth); + + return ret; + } + #end + + #if flash + public static function unzip(chunk : String, lineWidth : Int) : Array { + var result:Array = new Array(); + var data:ByteArray = base64ToByteArray(chunk); + data.uncompress(); + data.endian = Endian.LITTLE_ENDIAN; + while(data.position < data.length) + { + + result.push(data.readInt()); + } + return result; + } + + private static function base64ToByteArray(data:String):ByteArray + { + var output:ByteArray = new ByteArray(); + + var lookup:Array = new Array(); + var c:Int; + for (c in 0...BASE64_CHARS.length) + { + lookup[BASE64_CHARS.charCodeAt(c)] = c; + } + + var i:Int = 0; + while (i < data.length - 3) + { + if (data.charAt(i) == " " || data.charAt(i) == "\n") + { + i++; continue; + } + + var a0:Int = lookup[data.charCodeAt(i)]; + var a1:Int = lookup[data.charCodeAt(i + 1)]; + var a2:Int = lookup[data.charCodeAt(i + 2)]; + var a3:Int = lookup[data.charCodeAt(i + 3)]; + + //convert to and write 3 bytes + if(a1 < 64) + output.writeByte((a0 << 2) + ((a1 & 0x30) >> 4)); + if(a2 < 64) + output.writeByte(((a1 & 0x0f) << 4) + ((a2 & 0x3c) >> 2)); + if(a3 < 64) + output.writeByte(((a2 & 0x03) << 6) + a3); + + i += 4; + } + + output.position = 0; + return output; + } + #end + + +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXGZip.hx b/src/flambe/tilemap/TMXGZip.hx new file mode 100644 index 00000000..da328a85 --- /dev/null +++ b/src/flambe/tilemap/TMXGZip.hx @@ -0,0 +1,743 @@ +package flambe.tilemap; + +/** + * Haxe Port of https://github.com/cocos2d/cocos2d-html5/blob/develop/cocos2d/platform/gzip.js + * @author Ang Li(李昂) + */ +class TMXGZip +{ + var data : String; + var debug : Bool; + var gpflags : Int; + var files : Int; + var unzipped : Array>; + var buf32k : Array; + var bIdx : Int; + var modeZIP : Bool; + var bytepos : Int; + var bb : Int; + var bits : Int; + var nameBuf : Array; + var fileout : Array; + var literalTree : Array; + var distanceTree : Array; + var treepos : Int = 0; + var Places : Array; + var len : Int = 0; + var fpos : Array; + var flens : Array; + var fmax : Int; + + var outputArr : Array; + + public static var LITERALS : Int = 288; + public static var NAMEMAX : Int = 256; + public static var bitReverse : Array = [ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff + ]; + + public static var cplens : Array = [ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + ]; + + public static var cplext : Array= [ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 + ]; + /* 99==invalid */ + public static var cpdist : Array= [ + 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0007, 0x0009, 0x000d, + 0x0011, 0x0019, 0x0021, 0x0031, 0x0041, 0x0061, 0x0081, 0x00c1, + 0x0101, 0x0181, 0x0201, 0x0301, 0x0401, 0x0601, 0x0801, 0x0c01, + 0x1001, 0x1801, 0x2001, 0x3001, 0x4001, 0x6001 + ]; + + public static var cpdext : Array= [ + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 + ]; + + public static var border : Array= [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + + public function new(data : String) + { + this.data = data; + this.debug = false; + this.gpflags = 0; + this.files = 0; + this.unzipped = []; + this.buf32k = new Array(); + this.bIdx = 0; + this.modeZIP = false; + this.bytepos = 0; + this.bb = 1; + this.bits = 0; + this.nameBuf = []; + this.fileout = new Array(); + this.literalTree = new Array(); + this.literalTree[LITERALS - 1] = new HufNode(); + this.distanceTree = new Array(); + this.distanceTree[31] = new HufNode(); + this.treepos = 0; + this.Places = null; + this.len = 0; + this.fpos = new Array(); + this.fpos[16] = 0; + this.fpos[0] = 0; + this.flens = new Array(); + this.fmax = 0; + } + + public static function gunzip(data : String) : String { + var gzip : TMXGZip= new TMXGZip(data); + return gzip.gunzipLocal(); + } + + + public function gunzipLocal() : String { + outputArr = new Array(); + this.nextFile(); + return this.unzipped[0][0]; + } + + + public function readByte() : Int{ + this.bits += 8; + if (this.bytepos < this.data.length) { + return this.data.charCodeAt(this.bytepos++); + } else { + return -1; + } + } + + public function byteAlign() { + this.bb = 1; + } + + public function readBit() : Int { + this.bits++; + var carry = this.bb & 1; + this.bb >>= 1; + if (this.bb == 0) { + this.bb = this.readByte(); + carry = this.bb & 1; + this.bb = (this.bb >> 1) | 0x80; + } + return carry; + } + + public function readBits(a : Int) { + var res : Int = 0; + var i : Int = a; + while (i != 0) { + res = (res << 1) | this.readBit(); + i--; + } + + if (a != 0) { + res = bitReverse[res] >> (8 - a); + } + return res; + } + + public function flushBuffer() { + this.bIdx = 0; + } + + public function addBuffer(a : Int) { + this.buf32k[this.bIdx++] = a; + this.outputArr.push(String.fromCharCode(a)); + if (this.bIdx == 0x8000) this.bIdx = 0; + } + + public function isPat() : Int { + while (true) { + if (this.fpos[this.len] >= this.fmax) return -1; + if (this.flens[this.fpos[this.len]] == this.len) return this.fpos[this.len]++; + this.fpos[this.len]++; + } + } + + public function rec() : Int { + //trace(Places.length); + //trace('treepos = $treepos'); + var curplace : HufNode = this.Places[this.treepos]; + var tmp : Int ; + //if (this.debug) document.write("
len:"+this.len+" treepos:"+this.treepos); + if (this.len == 17) { //war 17 + return -1; + } + this.treepos++; + this.len++; + + tmp = this.isPat(); + //if (this.debug) document.write("
IsPat "+tmp); + if (tmp >= 0) { + curplace.b0 = tmp; + /* leaf cell for 0-bit */ + //if (this.debug) document.write("
b0 "+curplace.b0); + } else { + /* Not a Leaf cell */ + curplace.b0 = 0x8000; + //if (this.debug) document.write("
b0 "+curplace.b0); + if (this.rec() != 0) return -1; + } + tmp = this.isPat(); + if (tmp >= 0) { + curplace.b1 = tmp; + /* leaf cell for 1-bit */ + //if (this.debug) document.write("
b1 "+curplace.b1); + curplace.jump = null; + /* Just for the display routine */ + } else { + /* Not a Leaf cell */ + curplace.b1 = 0x8000; + //if (this.debug) document.write("
b1 "+curplace.b1); + curplace.jump = this.Places[this.treepos]; + curplace.jumppos = this.treepos; + if (this.rec() != 0) return -1; + } + this.len--; + return 0; + } + + public function createTree(currentTree : Array, numval : Int , lengths : Array, show : Int ) : Int { + this.Places = currentTree; + this.treepos = 0; + this.flens = lengths; + this.fmax = numval; + + for (i in 0...17) { + this.fpos[i] = 0; + } + + this.len = 0; + if (this.rec() != 0) { + return -1; + } + + return 0; + } + + public function decodeValue(currentTree : Array) : Int { + var len : Int = 0; + var xtreepos : Int = 0; + var X : HufNode = currentTree[xtreepos]; + var b : Int = 0; + + /* decode one symbol of the data */ + while (true) { + b = this.readBit(); + // if (this.debug) document.write("b="+b); + if (b != 0) { + if ((X.b1 & 0x8000) == 0) { + // if (this.debug) document.write("ret1"); + return X.b1; + /* If leaf node, return data */ + } + X = X.jump; + len = currentTree.length; + for (i in 0...len) { + if (currentTree[i] == X) { + xtreepos = i; + break; + } + } + } else { + if ((X.b0 & 0x8000) == 0) { + // if (this.debug) document.write("ret2"); + return X.b0; + /* If leaf node, return data */ + } + xtreepos++; + X = currentTree[xtreepos]; + } + } + // if (this.debug) document.write("ret3"); + + return -1; + } + + public function deflateLoop() : Int { + var last : Int, c : Int , type : Int, len : Int = 0, i : Int ; + do { + last = this.readBit(); + type = this.readBits(2); + //trace('type = $type'); + if (type == 0) { + var blockLen, cSum : Int = 0; + + // Stored + this.byteAlign(); + blockLen = this.readByte(); + blockLen |= (this.readByte() << 8); + + cSum = this.readByte(); + cSum |= (this.readByte() << 8); + + if (((blockLen ^ ~cSum) & 0xffff) != 0) { + throw "BlockLen checksum mismatch\n"; // FIXME: use throw + } + while ((blockLen--) != 0) { + c = this.readByte(); + this.addBuffer(c); + } + } else if (type == 1) { + var j : Int ; + + /* Fixed Huffman tables -- fixed decode routine */ + while (true) { + /* + 256 0000000 0 + : : : + 279 0010111 23 + 0 00110000 48 + : : : + 143 10111111 191 + 280 11000000 192 + : : : + 287 11000111 199 + 144 110010000 400 + : : : + 255 111111111 511 + + Note the bit order! + */ + j = (bitReverse[this.readBits(7)] >> 1); + if (j > 23) { + j = (j << 1) | this.readBit(); + /* 48..255 */ + + if (j > 199) { /* 200..255 */ + j -= 128; + /* 72..127 */ + j = (j << 1) | this.readBit(); + /* 144..255 << */ + } else { /* 48..199 */ + j -= 48; + /* 0..151 */ + if (j > 143) { + j = j + 136; + /* 280..287 << */ + /* 0..143 << */ + } + } + } else { /* 0..23 */ + j += 256; + /* 256..279 << */ + } + if (j < 256) { + this.addBuffer(j); + } else if (j == 256) { + /* EOF */ + break; // FIXME: make this the loop-condition + } else { + var len, dist; + + j -= 256 + 1; + /* bytes + EOF */ + len = this.readBits(cplext[j]) + cplens[j]; + + j = bitReverse[this.readBits(5)] >> 3; + if (cpdext[j] > 8) { + dist = this.readBits(8); + dist |= (this.readBits(cpdext[j] - 8) << 8); + } else { + dist = this.readBits(cpdext[j]); + } + dist += cpdist[j]; + + for (j in 0...len) { + var c = this.buf32k[(this.bIdx - dist) & 0x7fff]; + this.addBuffer(c); + } + } + } // while + + } else if (type == 2) { + var j : Int , n : Int, literalCodes : Int, distCodes : Int, lenCodes : Int; + var ll : Array= new Array(); // "static" just to preserve stack + + // Dynamic Huffman tables + + literalCodes = 257 + this.readBits(5); + distCodes = 1 + this.readBits(5); + lenCodes = 4 + this.readBits(4); + for (j in 0...19) { + ll[j] = 0; + } + + // Get the decode tree code lengths + + for (j in 0...lenCodes) { + ll[border[j]] = this.readBits(3); + } + len = this.distanceTree.length; + //trace('len = $len'); + for (i in 0... len) this.distanceTree[i] = new HufNode(); + if (this.createTree(this.distanceTree, 19, ll, 0) != 0) { + this.flushBuffer(); + return 1; + } + // if (this.debug) { + // document.write("
distanceTree"); + // for(var a=0;a"+this.distanceTree[a].b0+" "+this.distanceTree[a].b1+" "+this.distanceTree[a].jump+" "+this.distanceTree[a].jumppos); + // } + // } + + //read in literal and distance code lengths + n = literalCodes + distCodes; + i = 0; + var z = -1; + // if (this.debug) document.write("
n="+n+" bits: "+this.bits+"
"); + while (i < n) { + z++; + j = this.decodeValue(this.distanceTree); + // if (this.debug) document.write("
"+z+" i:"+i+" decode: "+j+" bits "+this.bits+"
"); + if (j < 16) { // length of code in bits (0..15) + ll[i++] = j; + } else if (j == 16) { // repeat last length 3 to 6 times + var l; + j = 3 + this.readBits(2); + if (i + j > n) { + this.flushBuffer(); + return 1; + } + l = (i != 0) ? ll[i - 1] : 0; + while ( (j--) != 0) { + ll[i++] = l; + } + } else { + if (j == 17) { // 3 to 10 zero length codes + j = 3 + this.readBits(3); + } else { // j == 18: 11 to 138 zero length codes + j = 11 + this.readBits(7); + } + if (i + j > n) { + this.flushBuffer(); + return 1; + } + while ((j--) != 0) { + ll[i++] = 0; + } + } + } // while + + // Can overwrite tree decode tree as it is not used anymore + len = this.literalTree.length; + for (i in 0...len) + this.literalTree[i] = new HufNode(); + if (this.createTree(this.literalTree, literalCodes, ll, 0) != 0) { + this.flushBuffer(); + return 1; + } + len = this.literalTree.length; + for (i in 0...len) this.distanceTree[i] = new HufNode(); + var ll2 = new Array(); + for (i in literalCodes...ll.length) ll2[i - literalCodes] = ll[i]; + if (this.createTree(this.distanceTree, distCodes, ll2, 0) != 0) { + this.flushBuffer(); + return 1; + } + // if (this.debug) document.write("
literalTree"); + while (true) { + j = this.decodeValue(this.literalTree); + if (j >= 256) { // In C64: if carry set + var len, dist : Int; + j -= 256; + if (j == 0) { + // EOF + break; + } + j--; + len = this.readBits(cplext[j]) + cplens[j]; + + j = this.decodeValue(this.distanceTree); + if (cpdext[j] > 8) { + dist = this.readBits(8); + dist |= (this.readBits(cpdext[j] - 8) << 8); + } else { + dist = this.readBits(cpdext[j]); + } + dist += cpdist[j]; + while ((len--) != 0) { + var c = this.buf32k[(this.bIdx - dist) & 0x7fff]; + this.addBuffer(c); + } + } else { + this.addBuffer(j); + } + } // while + } + } while (last == 0); + this.flushBuffer(); + + this.byteAlign(); + return 0; + } + + public function unzipFile(name : String) : String { + this.gunzipLocal(); + for (i in 0...this.unzipped.length) { + if (this.unzipped[i][1] == name) { + return this.unzipped[i][0]; + } + } + return null; + } + + public function nextFile() { + // if (this.debug) alert("NEXTFILE"); + + this.outputArr = []; + this.modeZIP = false; + + var tmp : Array = []; + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + // if (this.debug) alert("type: "+tmp[0]+" "+tmp[1]); + + if (tmp[0] == 0x78 && tmp[1] == 0xda) { //GZIP + // if (this.debug) alert("GEONExT-GZIP"); + this.deflateLoop(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), "geonext.gxt"]; + this.files++; + } + if (tmp[0] == 0x1f && tmp[1] == 0x8b) { //GZIP + // if (this.debug) alert("GZIP"); + this.skipdir(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), "file"]; + this.files++; + } + if (tmp[0] == 0x50 && tmp[1] == 0x4b) { //ZIP + this.modeZIP = true; + tmp[2] = this.readByte(); + tmp[3] = this.readByte(); + if (tmp[2] == 0x03 && tmp[3] == 0x04) { + //MODE_ZIP + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + // if (this.debug) alert("ZIP-Version: "+tmp[1]+" "+tmp[0]/10+"."+tmp[0]%10); + + this.gpflags = this.readByte(); + this.gpflags |= (this.readByte() << 8); + // if (this.debug) alert("gpflags: "+this.gpflags); + + var method = this.readByte(); + method |= (this.readByte() << 8); + // if (this.debug) alert("method: "+method); + + this.readByte(); + this.readByte(); + this.readByte(); + this.readByte(); + + // var crc = this.readByte(); + // crc |= (this.readByte()<<8); + // crc |= (this.readByte()<<16); + // crc |= (this.readByte()<<24); + + var compSize = this.readByte(); + compSize |= (this.readByte() << 8); + compSize |= (this.readByte() << 16); + compSize |= (this.readByte() << 24); + + var size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + + // if (this.debug) alert("local CRC: "+crc+"\nlocal Size: "+size+"\nlocal CompSize: "+compSize); + + var filelen : Int = this.readByte(); + filelen |= (this.readByte() << 8); + + var extralen = this.readByte(); + extralen |= (this.readByte() << 8); + + // if (this.debug) alert("filelen "+filelen); + var i = 0; + this.nameBuf = []; + var c : Int; + while ((filelen--) != 0) { + c = this.readByte(); + if (String.fromCharCode(c) == "/" || String.fromCharCode(c) == ":") { + i = 0; + } else if (i < NAMEMAX - 1) { + this.nameBuf[i++] = String.fromCharCode(c); + } + } + // if (this.debug) alert("nameBuf: "+this.nameBuf); + + if (this.fileout == null) this.fileout = this.nameBuf; + + i = 0; + while (i < extralen) { + c = this.readByte(); + i++; + } + + // if (size = 0 && this.fileOut.charAt(this.fileout.length-1)=="/"){ + // //skipdir + // // if (this.debug) alert("skipdir"); + // } + if (method == 8) { + this.deflateLoop(); + // if (this.debug) alert(this.outputArr.join('')); + this.unzipped[this.files] = [this.outputArr.join(''), this.nameBuf.join('')]; + this.files++; + } + this.skipdir(); + } + } + } + + public function skipdir() : Int{ + var tmp : Array = []; + var compSize : Int, size : Int, os : Int, i : Int , c : Int; + + if ((this.gpflags & 8) != 0) { + tmp[0] = this.readByte(); + tmp[1] = this.readByte(); + tmp[2] = this.readByte(); + tmp[3] = this.readByte(); + + // if (tmp[0] == 0x50 && tmp[1] == 0x4b && tmp[2] == 0x07 && tmp[3] == 0x08) { + // crc = this.readByte(); + // crc |= (this.readByte()<<8); + // crc |= (this.readByte()<<16); + // crc |= (this.readByte()<<24); + // } else { + // crc = tmp[0] | (tmp[1]<<8) | (tmp[2]<<16) | (tmp[3]<<24); + // } + + compSize = this.readByte(); + compSize |= (this.readByte() << 8); + compSize |= (this.readByte() << 16); + compSize |= (this.readByte() << 24); + + size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + } + + if (this.modeZIP) this.nextFile(); + + tmp[0] = this.readByte(); + if (tmp[0] != 8) { + // if (this.debug) alert("Unknown compression method!"); + return 0; + } + + this.gpflags = this.readByte(); + // if (this.debug && (this.gpflags & ~(0x1f))) alert("Unknown flags set!"); + + this.readByte(); + this.readByte(); + this.readByte(); + this.readByte(); + + this.readByte(); + os = this.readByte(); + + if ((this.gpflags & 4)!= 0) { + tmp[0] = this.readByte(); + tmp[2] = this.readByte(); + this.len = tmp[0] + 256 * tmp[1]; + // if (this.debug) alert("Extra field size: "+this.len); + for (i in 0...this.len) + this.readByte(); + } + + if ((this.gpflags & 8)!=0) { + i = 0; + this.nameBuf = []; + + while ((c = this.readByte()) != 0) { + if (String.fromCharCode(c) == "7" || String.fromCharCode(c) == ":") + i = 0; + if (i < NAMEMAX - 1) + this.nameBuf[i++] = String.fromCharCode(c); + } + //this.nameBuf[i] = "\0"; + // if (this.debug) alert("original file name: "+this.nameBuf); + } + + //if ((this.gpflags & 16) != 0) { + //while (c = this.readByte() != 0) { // FIXME: looks like they read to the end of the stream, should be doable more efficiently + //FILE COMMENT + //} + //} + + if ((this.gpflags & 2) != 0) { + this.readByte(); + this.readByte(); + } + + this.deflateLoop(); + + // crc = this.readByte(); + // crc |= (this.readByte()<<8); + // crc |= (this.readByte()<<16); + // crc |= (this.readByte()<<24); + + size = this.readByte(); + size |= (this.readByte() << 8); + size |= (this.readByte() << 16); + size |= (this.readByte() << 24); + + if (this.modeZIP) { + this.nextFile(); + } + return 0; + } +} + +class HufNode { + public var b0 = 0; + public var b1 = 0; + public var jumppos : Int = -1; + public var jump : HufNode; + + public function new() { + + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXLayer.hx b/src/flambe/tilemap/TMXLayer.hx new file mode 100644 index 00000000..f39dd70f --- /dev/null +++ b/src/flambe/tilemap/TMXLayer.hx @@ -0,0 +1,245 @@ +package flambe.tilemap; + +import flambe.Component; +import flambe.Entity; +import flambe.math.Point; +import dev.math.Rectangle; +import flambe.util.Assert; +import haxe.Int64; +import dev.tilemap.TMXXMLParser; +import dev.math.Size; + +/** + * + * @author Ang Li(李昂) + */ +class TMXLayer +{ + var _layerSize : Size; + var _mapTileSize : Size; + var _tiles : Array; + var _tileSet : TMXTilesetInfo; + var _layerOrientation : Int; + var _properties : Map; + var _layerName : String; + var _opacity : Int; + var _minGID : Int; + var _maxGID : Int; + var _useAutomaticVertexZ : Bool; + var _vertexZvalue : Int; + + var _root : Entity; + var _sprites : Array; + public function new() + { + this._layerSize = new Size(); + this._mapTileSize = new Size(); + this._opacity = 255; + this._layerName = ""; + _tiles = new Array(); + _properties = new Map(); + _useAutomaticVertexZ = false; + + _root = new Entity(); + _sprites = new Array(); + } + + public function getVertexZvalue() : Int { + return this._vertexZvalue; + } + + public function isAutomaticVertexZ() : Bool { + return _useAutomaticVertexZ; + } + + public function getLayerSize() : Size { + return this._layerSize; + } + + public function setLayerSize(v : Size) { + this._layerSize = v; + } + + public function getLayerName() : String { + return this._layerName; + } + + public function setLayerName(layerName : String) { + this._layerName = layerName; + } + + public function getMapTileSize() : Size { + return this._mapTileSize; + } + + public function setMapTileSize(v : Size) { + this._mapTileSize = v; + } + + public function getTiles() : Array { + return this._tiles; + } + + public function setTiles(v : Array) { + this._tiles = v; + } + + //public function getTileSet() : TMXTilesetInfo { + //return this._tileSet; + //} + // + //public function setTileSet(v : TMXTilesetInfo) { + //this._tileSet = v; + //} + + public function getLayerOrientation() : Int { + return this._layerOrientation; + } + + public function setLayerOrientation(v : Int) { + this._layerOrientation = v; + } + + public function getProperties() : Map < String, String > { + return this._properties; + } + + public function setProperties(v : Map < String, String > ) { + this._properties = v; + } + + public function getTileGIDAt(pos : Point) : Int { + Assert.that(pos.x < this._layerSize.width && pos.y < this._layerSize.height && pos.x >= 0 && pos.y >= 0, "TMXLayer: invalid position"); + var tile = this._tiles[Std.int(pos.y * this._layerSize.width) + Std.int(pos.x)]; + return tile; + } + + var _mapInfo : TMXMapInfo = null; + var _layerInfo : TMXLayerInfo = null; + public function initWithTilesetInfo(layerInfo : TMXLayerInfo, mapInfo : TMXMapInfo) : Bool { + var size = layerInfo._layerSize; + _mapInfo = mapInfo; + this._layerInfo = layerInfo; + + this._layerSize = layerInfo._layerSize; + this._layerName = layerInfo.name; + this._tiles = layerInfo._tiles; + this._minGID = layerInfo._minGID; + this._maxGID = layerInfo._maxGID; + this.setProperties(layerInfo.getProperties()); + this._opacity = layerInfo._opacity; + this._parseInternalProperties(); + + this._mapTileSize = mapInfo.getTileSize(); + this._layerOrientation = mapInfo.getOrientation(); + + this._vertexZvalue = 0; + return true; + } + + public function setupTiles() + { + var count : Int = 0; + for (row in 0...Std.int(_layerInfo._layerSize.height)) { + for (col in 0...Std.int(_layerInfo._layerSize.width)) { + var gid = _layerInfo._tiles[Std.int(col + row * _layerInfo._layerSize.width)]; + if (gid == 0) { + continue; + } else { + var tilesetInfo : TMXTilesetInfo = getTilesetInfo(gid); + var o : Int = this._layerOrientation; + var x : Float = 0; + var y : Float = 0; + if (o == TMXTiledMap.TMX_ORIENTATION_ORTHO) { + x = col * this._mapTileSize.width; + y = row * _mapTileSize.height; + } else if (o == TMXTiledMap.TMX_ORIENTATION_ISO) { + x = this._mapTileSize.width / 2 + * ( this._layerSize.height + col - row - 1) ; + y = this._mapTileSize.height / 2 + * (row + col + 2) - tilesetInfo._tileSize.height; + } + + var rect : Rectangle = tilesetInfo.rectForGID(gid); + var sprite : TMXSprite = new TMXSprite(rect, tilesetInfo.texture, TMXTiledMap.useViewport); + sprite.setXY(x, y); + sprite.alpha._ = this._opacity / 255; + _root.addChild(new Entity().add(sprite), true, _vertexZForPos(row, col)); + _sprites.push(sprite); + } + } + } + } + + private function getTilesetInfo(gid : Int) : TMXTilesetInfo { + var a = _mapInfo.getTilesets(); + for (i in 0..._mapInfo.getTilesets().length) { + if (a[i + 1] != null) { + if (gid >= a[i].firstGid && gid < a[i + 1].firstGid) { + return a[i]; + } + } else { + return a[i]; + } + } + return null; + } + + private function _vertexZForPos(row : Int, col : Int) : Int { + var ret : Int = 0; + var maxVal = 0; + if (this._useAutomaticVertexZ) { + switch(this._layerOrientation) { + case TMXTiledMap.TMX_ORIENTATION_ISO : + ret = row + col; + case TMXTiledMap.TMX_ORIENTATION_ORTHO : + ret = row; + default : + null; + } + } else { + ret = this._vertexZvalue; + } + + return ret; + } + + public static function create(layerInfo : TMXLayerInfo, mapInfo : TMXMapInfo) { + var ret = new TMXLayer(); + if (ret.initWithTilesetInfo(layerInfo, mapInfo)) { + return ret; + } + + return null; + } + + /** + * Return the value for the specific property name + * @param propertyName + * @return + */ + public function getProperty(propertyName : String) : String { + return this._properties[propertyName]; + } + + private function _parseInternalProperties() { + // if vertex=automatic, then tiles will be rendered using vertexz + var vertexz : String = this.getProperty("vertexz"); + if (vertexz != null) { + if (vertexz == "automatic") { + this._useAutomaticVertexZ = true; + } else + this._vertexZvalue = Std.parseInt(vertexz); + } + } + + public function getRoot() : Entity { + return _root; + } + + public function clear() { + for (s in _sprites) { + s.dispose(); + } + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXObject.hx b/src/flambe/tilemap/TMXObject.hx new file mode 100644 index 00000000..d656b7ab --- /dev/null +++ b/src/flambe/tilemap/TMXObject.hx @@ -0,0 +1,32 @@ +package flambe.tilemap; + +/** + * + * @author Ang Li(李昂) + */ +class TMXObject +{ + public var name : String; + public var x : Float; + public var y : Float; + public var width : Float; + public var height : Float; + public var type : String; + public var gid : Int; + public var realX : Float = -1; + public var realY : Float = -1; + + public var properties : Map; + public function new(?name : String, ?x : Float, ?y : Float, ?type : String, ?width : Float, ?height : Float, ?gid : Int = -1) + { + this.name = name; + this.x = x; + this.y = y; + this.height = height; + this.width = width; + this.type = type; + this.gid = gid; + properties = new Map(); + } + +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXObjectGroup.hx b/src/flambe/tilemap/TMXObjectGroup.hx new file mode 100644 index 00000000..d701c189 --- /dev/null +++ b/src/flambe/tilemap/TMXObjectGroup.hx @@ -0,0 +1,68 @@ +package flambe.tilemap; +import flambe.math.Point; + +/** + * + * @author Ang Li(李昂) + */ +class TMXObjectGroup +{ + var _groupName : String = ""; + var _positionOffset : Point; + var _properties : Map; + var _objects : Array; + public var isRender : Bool = false; + public var idx : Int = 0; + public function new() + { + _properties = new Map(); + _objects = new Array(); + } + + public function getPositionOffset() : Point { + return this._positionOffset; + } + + public function setPositionOffset(v : Point) { + this._positionOffset = v; + } + + public function getProperties() : Map < String, String > { + return this._properties; + } + + public function setProperties(name : String, value : String) { + this._properties.set(name, value); + } + + public function getGroupName() : String { + return this._groupName; + } + + public function setGroupName(s : String) { + this._groupName = s; + } + + public function propertyNamed(propertyName) : String { + return this._properties[propertyName]; + } + + public function objectNamed(objectName : String) : TMXObject { + if (this._objects != null && this._objects.length > 0) { + for (o in this._objects) { + if (o.name == objectName) { + return o; + } + } + } + return null; + } + + public function getObjects() : Array { + return this._objects; + } + + public function setObjects(object : TMXObject) { + this._objects.push(object); + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXSprite.hx b/src/flambe/tilemap/TMXSprite.hx new file mode 100644 index 00000000..998ac089 --- /dev/null +++ b/src/flambe/tilemap/TMXSprite.hx @@ -0,0 +1,54 @@ +package flambe.tilemap; +import flambe.display.Graphics; +import flambe.display.Sprite; +import flambe.tilemap.TMXXMLParser; +import flambe.math.Point; +import flambe.math.Rectangle; +import flambe.display.Texture; + +/** + * + * @author Ang Li(李昂) + */ +class TMXSprite extends Sprite +{ + var texture : Texture; + var rect : Rectangle; + var isTiledMap : Bool; + + public function new(rect : Rectangle, texture : Texture, ?isTiledMap : Bool = false) + { + super(); + this.rect = rect; + this.isTiledMap = isTiledMap; + this.texture = texture; + } + + override public function draw(g:Graphics) + { + if (isTiledMap) { + var t : Rectangle = new Rectangle(this.x._, this.y._, rect.width, rect.height); + if (t.intersect(TMXTiledMap.viewport)) { + g.drawSubImage(texture, 0, 0, rect.x, rect.y, rect.width, rect.height); + } + } else { + g.drawSubImage(texture, 0, 0, rect.x, rect.y, rect.width, rect.height); + } + } + + override public function getNaturalHeight():Float + { + if (this.rect == null) { + return 0; + } + return rect.height; + } + + override public function getNaturalWidth():Float + { + if (this.rect == null) { + return 0; + } + return rect.width; + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXTileProperty.hx b/src/flambe/tilemap/TMXTileProperty.hx new file mode 100644 index 00000000..1c4a5dd8 --- /dev/null +++ b/src/flambe/tilemap/TMXTileProperty.hx @@ -0,0 +1,17 @@ +package flambe.tilemap; + +/** + * + * @author Ang Li(李昂) + */ +class TMXTileProperty +{ + public var name : String; + public var value : String; + public function new(?name :String="", ?value : String = "") + { + this.name = name; + this.value = value; + } + +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXTiledMap.hx b/src/flambe/tilemap/TMXTiledMap.hx new file mode 100644 index 00000000..6fef2e8b --- /dev/null +++ b/src/flambe/tilemap/TMXTiledMap.hx @@ -0,0 +1,241 @@ +package flambe.tilemap; +import flambe.display.SpriteFrame; +import flambe.asset.AssetPack; +import flambe.Component; +import flambe.display.Sprite; +import flambe.Entity; +import flambe.tilemap.TMXXMLParser; +import flambe.math.Point; +import flambe.math.Rectangle; +import flambe.math.Size; +import flambe.util.Assert; + +/** + * + * @author Ang Li(李昂) + */ +class TMXTiledMap +{ + public static var useViewport : Bool = false; + public static var viewport : Rectangle = new Rectangle(); + + public static var TMX_ORIENTATION_ORTHO : Int = 0; + + public static var TMX_ORIENTATION_HEX : Int = 1; + + public static var TMX_ORIENTATION_ISO : Int = 2; + + + //var _tiledMap : TiledMap; + + var _mapSize : Size; + var _tileSize : Size; + var _properties : Array < Map < String, String >> ; + var _objectGroups : Array; + var _mapOrientation : Int; + var _TMXLayers : Array; + var _tileProperties : Map < Int, Map < String, String >> ; + + var _root : Entity; + var _pack : AssetPack; + var _sprite : Sprite; + public function new(pack : AssetPack, tmxFile : String, ?resourcePath : String) + { + _mapOrientation = 0; + _mapSize = new Size(); + _tileSize = new Size(); + _objectGroups = new Array(); + _TMXLayers = new Array(); + _pack = pack; + _root = new Entity(); + initWithTMXFile(tmxFile, resourcePath); + _sprite = new Sprite(); + } + + public function getMapSize() : Size { + return this._mapSize; + } + + public function setMapSize(v : Size) { + this._mapSize = v; + } + + public function getTileSize() : Size { + return this._tileSize; + } + + public function setTileSize(v : Size) { + this._tileSize = v; + } + + public function getMapOrientation() : Int { + return this._mapOrientation; + } + + public function setMapOrientation(v : Int) { + this._mapOrientation = v; + } + + public function getObjectGroups() : Array { + return this._objectGroups; + } + + public function setObjectGroups(v : Array) { + this._objectGroups = v; + } + + /** + * Return properties dictionary for tile GID + * @param GID + * @return + */ + public function propertiesForGID(GID : Int) : Map { + return this._tileProperties[GID]; + } + + public function initWithTMXFile(tmxFile : String, ?resourcePath : String) : Bool { + Assert.that(tmxFile != null && tmxFile.length > 0, "TMXTiledMap: tmx file should not be nil"); + var mapInfo : TMXMapInfo = TMXMapInfo.create(this._pack, tmxFile, resourcePath); + if (mapInfo == null) { + return false; + } + + Assert.that(mapInfo.getTilesets().length != 0, "TMXTiledMap: Map not found. Please check the filename."); + this._buildWithMapInfo(mapInfo); + return true; + } + + private function _buildWithMapInfo(mapInfo : TMXMapInfo) { + this._mapSize = mapInfo.getMapSize(); + this._tileSize = mapInfo.getTileSize(); + this._mapOrientation = mapInfo.getOrientation(); + this._objectGroups = mapInfo.getObjectGroups(); + this._properties = mapInfo.getProperties(); + this._tileProperties = mapInfo.getTileProperties(); + + var layers = mapInfo.getLayers(); + if (layers != null) { + for (l in layers) { + var child : TMXLayer = this._parseLayer(l, mapInfo); + _root.addChild(child.getRoot(), true, l.idx); + this._TMXLayers.push(child); + } + } + + var objects = mapInfo.getObjectGroups(); + for (obj in objects) { + if (obj.isRender) { + for (o in obj.getObjects()) { + if (o.gid != -1) { + var tilesets : Array = mapInfo.getTilesets(); + for (tileset in tilesets) { + if ((o.gid >= tileset.firstGid && o.gid <= tileset.lastGid) + || (o.gid >= tileset.firstGid && tileset.lastGid == 0)) { + if (this._mapOrientation == TMX_ORIENTATION_ISO) { + var rect : Rectangle = tileset.rectForGID(o.gid); + var sprite : TMXSprite = new TMXSprite(rect, tileset.texture); + sprite.setAnchor(sprite.getNaturalWidth() / 2, sprite.getNaturalHeight()); + + var x = o.x; + var y = o.y; + + var tileWidth = this.getTileSize().width; + var tileHeight = this.getTileSize().height; + var edge = tileHeight; + + var yt : Float= y / edge; + var xt : Float = x / edge; + var retx = tileWidth / 2 + * ( this.getMapSize().height + xt - yt); + var rety = tileHeight / 2 + * (xt + yt); + + o.realX = retx; + o.realY = rety; + o.width = rect.width; + o.height = rect.height; + sprite.setXY(retx, rety); + _root.addChild(new Entity().add(sprite), true, obj.idx); + } + } + } + } + } + } + } + } + + private function _parseLayer(layerInfo, mapInfo) : TMXLayer { + var layer : TMXLayer = TMXLayer.create(layerInfo, mapInfo); + layer.setupTiles(); + return layer; + } + + private function _tilesetForLayer(layerInfo : TMXLayerInfo, mapInfo : TMXMapInfo) : TMXTilesetInfo { + var size = layerInfo._layerSize; + var tilesets = mapInfo.getTilesets(); + if (tilesets != null) { + var i = tilesets.length - 1; + while ( i >= 0) { + var tileset = tilesets[i]; + if (tileset != null) { + for (y in 0...Std.int(size.height)) { + for (x in 0...Std.int(size.width)) { + var gid = layerInfo._tiles[Std.int(y * size.width) + x]; + if (gid != 0) { + if (((gid & TMXXMLParser.TMX_TILE_FLIPPED_MASK)>>>0) >= tileset.firstGid) { + return tileset; + } + } + + } + } + } + i--; + } + } + return null; + } + + /** + * return the TMXLayer for the specific layer + * @param layerName + * @return + */ + + public function getLayer(layerName : String) : TMXLayer { + Assert.that(layerName != null && layerName.length > 0, "Invalid layer name!"); + + for (l in _TMXLayers) { + if (l.getLayerName() == layerName) { + return l; + } + } + return null; + + } + + public function getObjectGroup(groupName : String) : TMXObjectGroup { + Assert.that(groupName != null && groupName.length > 0, "Invalid group name!"); + if (this._objectGroups != null) { + for (o in _objectGroups) { + if (o != null && o.getGroupName() == groupName) { + return o; + } + } + } + return null; + } + + public function getRoot() : Entity { + return this._root; + } + + public function clear() { + this._root.dispose(); + for (l in this._TMXLayers) { + l.getRoot().dispose(); + l.clear(); + } + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXXMLParser.hx b/src/flambe/tilemap/TMXXMLParser.hx new file mode 100644 index 00000000..2869367b --- /dev/null +++ b/src/flambe/tilemap/TMXXMLParser.hx @@ -0,0 +1,662 @@ +package flambe.tilemap; +import flambe.animation.AnimatedFloat; +import flambe.asset.AssetPack; +import flambe.display.Texture; +import flambe.input.PointerEvent; +import flambe.math.Point; +import flambe.math.Rectangle; +import flambe.math.Size; +import flambe.util.Logger; + +/** + * + * @author Ang Li(李昂) + */ +class TMXXMLParser +{ + /** + * @constant + * @type Number + */ + public static var TMX_LAYER_ATTRIB_NONE : Int = 1 << 0; + /** + * @constant + * @type Number + */ + public static var TMX_LAYER_ATTRIB_BASE64 : Int = 1 << 1; + /** + * @constant + * @type Number + */ + public static var TMX_LAYER_ATTRIB_GZIP : Int = 1 << 2; + /** + * @constant + * @type Number + */ + public static var TMX_LAYER_ATTRIB_ZLIB : Int = 1 << 3; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_NONE : Int = 0; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_MAP : Int = 1; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_LAYER : Int = 2; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_OBJECTGROUP : Int = 3; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_OBJECT : Int = 4; + + /** + * @constant + * @type Number + */ + public static var TMX_PROPERTY_TILE : Int = 5; + + /** + * @constant + * @type Number + */ + public static var TMX_TILE_HORIZONTAL_FLAG : Int = 0x80000000; + + + /** + * @constant + * @type Number + */ + public static var TMX_TILE_VERTICAL_FLAG : Int = 0x40000000; + + /** + * @constant + * @type Number + */ + public static var TMX_TILE_DIAGONAL_FLAG : Int = 0x20000000; + + /** + * @constant + * @type Number + */ + public static var TMX_TILE_FLIPPED_ALL : Int = (TMX_TILE_HORIZONTAL_FLAG | TMX_TILE_VERTICAL_FLAG | TMX_TILE_DIAGONAL_FLAG) >>> 0; + + /** + * @constant + * @type Number + */ + public static var TMX_TILE_FLIPPED_MASK : Int = (~(TMX_TILE_FLIPPED_ALL)) >>> 0; + + public function new() + { + + } + +} + +class TMXLayerInfo { + public var _properties : Map; + public var name : String = ""; + public var _layerSize : Size; + public var _tiles : Array; + public var visible : Bool; + public var _opacity : Int; + public var ownTiles : Bool = true; + public var _minGID : Int = 100000; + public var _maxGID : Int = 0; + public var offset : Point; + public var idx : Int = 0; + + public function new() { + offset = new Point(); + _properties = new Map(); + _tiles = new Array(); + } + + public function getProperties() : Map < String, String > { + return this._properties; + } + + public function setProperties(name : String, value : String ) { + this._properties.set(name, value); + } +} + +class TMXTilesetInfo { + public var name : String = ""; + public var firstGid : Int = 0; + public var lastGid : Int = 0; + public var _tileSize : Size; + public var spacing : Float; + public var margin : Float; + public var sourceImage : String; + public var sourceImageWidth : Float; + public var sourceImageHeight : Float; + public var imageSize : Size; + + public var texture : Texture; + + public function new() { + _tileSize = new Size(); + } + public function rectForGID(gid : Int) : Rectangle { + var rect = new Rectangle(); + rect.width = this._tileSize.width; + rect.height = this._tileSize.height; + gid = gid - this.firstGid; + var max_x = (this.imageSize.width - this.margin * 2 + this.spacing) / (this._tileSize.width + this.spacing); + rect.x = (gid % max_x) * (this._tileSize.width + this.spacing) + this.margin; + + rect.y = Std.int((gid / max_x)) * (this._tileSize.height + this.spacing) + this.margin; + return rect; + } +} + +class TMXMapInfo { + var _orientation : Int; + var _mapSize : Size; + var _tileSize : Size; + var _layers : Array; + var _tileSets : Array; + var _objectGroups : Array; + var _parentElement : Int; + var _parentGID : Int; + var _layerAttribs : Int; + var _storingCharacters : Bool = false; + var _properties : Array>; + + var _TMXFileName : String; + var _currentString : String; + var _tileProperties : Map < Int, Map < String, String >> ; + var _resources : String; + + var pack : AssetPack; + var idx : Int = 0; + + private function new(pack : AssetPack) { + this._tileProperties = new Map < Int, Map < String, String >> (); + this._properties = new Array < Map < String, String >> (); + this.pack = pack; + } + + public function getOrientation() :Int { + return this._orientation; + } + + public function setOrientation(v : Int) { + this._orientation = v; + } + + public function getMapSize() : Size { + return this._mapSize; + } + + public function setMapSize(v : Size) { + this._mapSize = v; + } + + public function getTileSize() : Size { + return this._tileSize; + } + + public function setTileSize(v : Size) { + this._tileSize = v; + } + + public function getLayers() : Array { + return this._layers; + } + + public function setLayers(v : TMXLayerInfo) { + this._layers.push(v); + } + + public function getTilesets() : Array { + return this._tileSets; + } + + public function setTilesets(v : TMXTilesetInfo) { + this._tileSets.push(v); + } + + public function getObjectGroups() : Array { + return this._objectGroups; + } + + public function setObjectGroups(v : TMXObjectGroup) { + this._objectGroups.push(v); + } + + //public function getParentElement : Int { + //public function getParentElement : Int { + //return this._parentElement; + //} + // + //public function setParentElement(v : Int) { + //this._parentElement = v; + //} + + public function getParentGID() : Int { + return this._parentGID; + } + + public function setParentGID(v : Int) { + this._parentGID = v; + } + + public function getLayerAttribs() : Int { + return this._layerAttribs; + } + + public function setLayerAttribs(v : Int) { + this._layerAttribs = v; + } + + public function getStoringCharacters() : Bool { + return this._storingCharacters; + } + + public function setStoringCharacters(v : Bool) { + this._storingCharacters = v; + } + + public function getProperties() : Array < Map < String, String >> { + return this._properties; + } + + public function setProperties(v : Map < String, String > ) { + this._properties.push(v); + } + + public function initWithTMXFile(tmxFile : String, resourcePath : String) { + this._initernalInit(tmxFile, resourcePath); + return this.parseXMLFile(this._TMXFileName); + } + + public function parseXMLFile(tmxFile : String) { + var map : Xml = Xml.parse(this.pack.getFile(tmxFile).toString()).firstElement(); + + + var version = map.get("version"); + var orientationStd = map.get("orientation"); + + var mapSize : Size = new Size(); + mapSize.width = Std.parseFloat(map.get("width")); + mapSize.height = Std.parseFloat(map.get("height")); + this.setMapSize(mapSize); + + mapSize = new Size(); + mapSize.width = Std.parseFloat(map.get("tilewidth")); + mapSize.height = Std.parseFloat(map.get("tileheight")); + this.setTileSize(mapSize); + + switch (orientationStd) { + case "orthogonal" : + this.setOrientation(TMXTiledMap.TMX_ORIENTATION_ORTHO); + case "isometric" : + this.setOrientation(TMXTiledMap.TMX_ORIENTATION_ISO); + case "hexagonal" : + this.setOrientation(TMXTiledMap.TMX_ORIENTATION_HEX); + default : + null; + } + + var indexOfTileset : Int = 0; + for (elem in map.elements()) { + switch (elem.nodeName) { + case "tileset" : + this.loadTileset(elem); + //trace("tileset"); + case "layer": + this.loadLayer(elem); + idx++; + case "objectgroup" : + this.loadObjectGroup(elem); + idx++; + case "properties" : + this.loadProperties(elem); + } + } + } + + private function loadProperties(elem : Xml) { + for (e in elem.elements()) { + switch(e.nodeName) { + case "property" : + var map : Map = new Map(); + map[e.get("name")] = e.get("value"); + this.setProperties(map); + default : null; + } + } + } + + + private function loadTileset(elem : Xml) { + var tileset : TMXTilesetInfo = new TMXTilesetInfo(); + tileset.name = elem.get("name"); + tileset.firstGid = Std.parseInt(elem.get("firstgid")); + + + var marginStr = elem.get("margin"); + if (marginStr == null) { + marginStr = "0"; + } + + var spacingStr = elem.get("spacing"); + if (spacingStr == null) { + spacingStr = "0"; + } + tileset.margin = Std.parseInt(marginStr); + tileset.spacing = Std.parseInt(spacingStr); + var tilesetSize : Size = new Size(); + tilesetSize.width = Std.parseFloat(elem.get("tilewidth")); + tilesetSize.height = Std.parseFloat(elem.get("tileheight")); + tileset._tileSize = tilesetSize; + + + for (e in elem.elements()) { + switch(e.nodeName) { + case "image" : + var imgSource = e.get("source"); + imgSource = imgSource.split(".")[0]; + if (imgSource != null) { + if (this._resources != null) { + imgSource = this._resources + "/" + imgSource; + } else { + + } + } + tileset.sourceImage = imgSource; + tileset.texture = this.pack.getTexture(tileset.sourceImage); + tileset.sourceImageWidth = Std.parseFloat(e.get("width")); + tileset.sourceImageHeight = Std.parseFloat(e.get("height")); + tileset.imageSize = new Size(tileset.sourceImageWidth, tileset.sourceImageHeight); + if (this._tileSets.length != 0) { + this._tileSets[this._tileSets.length - 1].lastGid = tileset.firstGid - 1; + } + this.setTilesets(tileset); + case "tile": + var info = this._tileSets[this._tileSets.length - 1]; + var id = Std.parseInt(e.get("id")); + if (id == null) { + id = 0; + } + this.setParentGID(info.firstGid + id); + var dict : Map = new Map(); + for (p in e.elements()) { + switch (p.nodeName) { + case "properties" : + for (pro in p.elements()) { + switch (pro.nodeName) { + case "property" : + var name = pro.get("name"); + var value = pro.get("value"); + + dict.set(name, value); + + } + } + + } + } + this._tileProperties.set(this.getParentGID(), dict); + default : null; + } + } + + } + + private function loadLayer(elem : Xml) { + var layer : TMXLayerInfo = new TMXLayerInfo(); + layer.name = elem.get("name"); + layer.idx = this.idx; + + var layerSize : Size = new Size(); + layerSize.width = Std.parseFloat(elem.get("width")); + layerSize.height = Std.parseFloat(elem.get("height")); + layer._layerSize = layerSize; + + var visible = elem.get("visible"); + if (visible == "0") { + layer.visible = false; + } else { + layer.visible = true; + } + + var opacity :String = elem.get("opacity"); + if (opacity == null) { + opacity = "1"; + } + + + layer._opacity = Std.parseInt(Std.string(255 * Std.parseFloat(opacity))); + + //var x = Std.parseFloat(elem.get("x")); + //var y = Std.parseFloat(elem.get("y")); + + var x = elem.get("x"); + var y = elem.get("y"); + + if (x == null) { + x = "0"; + } + + if (y == null) { + y = "0"; + } + + layer.offset = new Point(Std.parseFloat(x), Std.parseFloat(y)); + + var nodeValue :String = ""; + + for (e in elem.elements()) { + switch (e.nodeName) { + case "data": + loadData(e, layer); + case "properties": + loadLayerPros(e, layer); + } + } + this.setLayers(layer); + } + private function loadData(xml : Xml, layer : TMXLayerInfo) { + var encoding = xml.get("encoding"); + var compression = xml.get("compression"); + + if (compression == null) { + compression = ""; + } + + var isCompression : Bool = false; + switch(compression) { + case "gzip" : + layer._tiles = TMXZipUtils.unzipBase64AsArray(xml.firstChild().nodeValue, Std.int(layer._layerSize.width), 4); + case "zlib" : + //isCompression = true; + layer._tiles = TMXBase64.unzip(xml.firstChild().nodeValue, Std.int(layer._layerSize.width)); + //layer._tiles = CCBase64.decodeAsArray(xml.firstChild().nodeValue, Std.int(layer._layerSize.width), isCompression); + case "": + if (encoding == "base64") { + layer._tiles = TMXBase64.decodeAsArray(xml.firstChild().nodeValue, Std.int(layer._layerSize.width)); + } else if (encoding == "csv") { + layer._tiles = csvToArray(xml.firstChild().nodeValue); + } else { + //XML format + var indexX = 0; + var indexY = 0; + var widthMap = this._mapSize.width; + var heightMap = this._mapSize.height; + var tilesRow : Array = new Array(); + for (elem in xml.elements()) { + switch (elem.nodeName) { + case "tile" : + //var g : Int = Std.parseInt(elem.get("gid")); + //tilesRow.push(g); + // + //if (indexX == widthMap - 1) { + //indexX = 0; + //indexY++; + //layer._tiles.push(tilesRow); + //tilesRow = []; + //} else { + //indexX++; + //} + layer._tiles.push(Std.parseInt(elem.get("gid"))); + + default: null; + } + } + } + default : null; + } + + } + + private function loadLayerPros(xml : Xml, layer : TMXLayerInfo) { + for (elem in xml.elements()) { + layer.setProperties(elem.get("name"), elem.get("value")); + } + } + + private function loadObjectGroup(xml : Xml) { + var objectGroup = new TMXObjectGroup(); + objectGroup.setGroupName(xml.get("name")); + + var x : Float; + var y : Float; + var xStr : String = xml.get("x"); + var yStr : String = xml.get("y"); + if (xStr == null) { + x = 0; + } else { + x = Std.parseFloat(xStr); + } + + if (yStr == null) { + y = 0; + } else { + y = Std.parseFloat(yStr); + } + objectGroup.setPositionOffset(new Point(x * this.getTileSize().width, + y * this.getTileSize().height)); + for (elem in xml.elements()) { + var object : TMXObject = new TMXObject(); + switch (elem.nodeName) { + case "object" : + + object.name = elem.get("name"); + object.type = elem.get("type"); + object.x = Std.parseInt(elem.get("x")); + object.y = Std.parseInt(elem.get("y")); + object.width = Std.parseInt(elem.get("width")); + object.height = Std.parseInt(elem.get("height")); + + if (elem.get("gid") != null) { + object.gid = Std.parseInt(elem.get("gid")); + objectGroup.isRender = true; + objectGroup.idx = this.idx; + } else { + object.gid = -1; + } + case "properties": + object.properties.set(elem.get("name"), elem.get("value")); + } + + objectGroup.setObjects(object); + } + this.setObjectGroups(objectGroup); + } + + + public function getTileProperties(): Map>{ + return this._tileProperties; + } + public function setTileProperties() { + + } + + public function getCurrentString() : String { + return this._currentString; + } + + public function setCurrentString(currentString : String) { + this._currentString = currentString; + } + + public function setTMXFileName(fileName : String) { + this._TMXFileName = fileName; + + } + + public function _initernalInit(tmxFileName, resourcePath) { + this._tileSets = new Array(); + this._layers = new Array(); + + this._TMXFileName = tmxFileName; + + if (resourcePath != null) { + this._resources = resourcePath; + } + + this._objectGroups = new Array(); + + this._currentString = ""; + this._storingCharacters = false; + this._layerAttribs = TMXXMLParser.TMX_LAYER_ATTRIB_NONE; + this._parentElement = TMXXMLParser.TMX_PROPERTY_NONE; + } + + /** + * https://github.com/po8rewq/HaxeFlixelTiled/blob/master/org/flixel/tmx/TmxLayer.hx + * @param input + * @return + */ + public static function csvToArray(input:String):Array + { + var result:Array = new Array(); + var rows:Array = input.split("\n"); + var row:String; + for (row in rows) + { + if (row == "") continue; + var resultRow:Array = new Array(); + var entries:Array = row.split(","); + //trace(entries); + var entry:String; + for (entry in entries) { + var t = Std.parseInt(entry); + if (t != null) { + result.push(t); //convert to int + } + } + + + } + + //trace(result); + return result; + } + + public static function create(pack : AssetPack, tmxFile : String, ?resourcePath : String) : TMXMapInfo { + var ret : TMXMapInfo = new TMXMapInfo(pack); + ret.initWithTMXFile(tmxFile, resourcePath); + + return ret; + } +} \ No newline at end of file diff --git a/src/flambe/tilemap/TMXZipUtils.hx b/src/flambe/tilemap/TMXZipUtils.hx new file mode 100644 index 00000000..5e64c5db --- /dev/null +++ b/src/flambe/tilemap/TMXZipUtils.hx @@ -0,0 +1,36 @@ +package flambe.tilemap; + +/** + * + * @author Ang Li(李昂) + */ +class TMXZipUtils +{ + + public function new() + { + + } + + public static function unzipBase64(input : String) : String { + var tmpInput = TMXBase64.decode(input); + return TMXGZip.gunzip(tmpInput); + } + + public static function unzipBase64AsArray(input : String, lineWidth : Int, ?bytes : Int = 1) : Array{ + var dec : String = unzipBase64(input); + var ar : Array = []; + var len : Int = Std.int(dec.length / bytes); + + for (i in 0...len) { + ar[i] = 0; + var j = bytes - 1; + while (j >= 0) { + var t = dec.charCodeAt((i * bytes) + j) << (j * 8); + ar[i] += t; + j--; + } + } + return ar; + } +} \ No newline at end of file