// Generated by Construct 2, the HTML5 game and app creator :: http://www.scirra.com var cr = {}; cr.plugins_ = {}; cr.behaviors = {}; if (typeof Object.getPrototypeOf !== "function") { if (typeof "test".__proto__ === "object") { Object.getPrototypeOf = function(object) { return object.__proto__; }; } else { Object.getPrototypeOf = function(object) { return object.constructor.prototype; }; } } (function(){ cr.logexport = function (msg) { if (window.console && window.console.log) window.console.log(msg); }; cr.logerror = function (msg) { if (window.console && window.console.error) window.console.error(msg); }; cr.seal = function(x) { return x; }; cr.freeze = function(x) { return x; }; cr.is_undefined = function (x) { return typeof x === "undefined"; }; cr.is_number = function (x) { return typeof x === "number"; }; cr.is_string = function (x) { return typeof x === "string"; }; cr.isPOT = function (x) { return x > 0 && ((x - 1) & x) === 0; }; cr.nextHighestPowerOfTwo = function(x) { --x; for (var i = 1; i < 32; i <<= 1) { x = x | x >> i; } return x + 1; } cr.abs = function (x) { return (x < 0 ? -x : x); }; cr.max = function (a, b) { return (a > b ? a : b); }; cr.min = function (a, b) { return (a < b ? a : b); }; cr.PI = Math.PI; cr.round = function (x) { return (x + 0.5) | 0; }; cr.floor = function (x) { if (x >= 0) return x | 0; else return (x | 0) - 1; // correctly round down when negative }; cr.ceil = function (x) { var f = x | 0; return (f === x ? f : f + 1); }; function Vector2(x, y) { this.x = x; this.y = y; cr.seal(this); }; Vector2.prototype.offset = function (px, py) { this.x += px; this.y += py; return this; }; Vector2.prototype.mul = function (px, py) { this.x *= px; this.y *= py; return this; }; cr.vector2 = Vector2; cr.segments_intersect = function(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { var max_ax, min_ax, max_ay, min_ay, max_bx, min_bx, max_by, min_by; if (a1x < a2x) { min_ax = a1x; max_ax = a2x; } else { min_ax = a2x; max_ax = a1x; } if (b1x < b2x) { min_bx = b1x; max_bx = b2x; } else { min_bx = b2x; max_bx = b1x; } if (max_ax < min_bx || min_ax > max_bx) return false; if (a1y < a2y) { min_ay = a1y; max_ay = a2y; } else { min_ay = a2y; max_ay = a1y; } if (b1y < b2y) { min_by = b1y; max_by = b2y; } else { min_by = b2y; max_by = b1y; } if (max_ay < min_by || min_ay > max_by) return false; var dpx = b1x - a1x + b2x - a2x; var dpy = b1y - a1y + b2y - a2y; var qax = a2x - a1x; var qay = a2y - a1y; var qbx = b2x - b1x; var qby = b2y - b1y; var d = cr.abs(qay * qbx - qby * qax); var la = qbx * dpy - qby * dpx; if (cr.abs(la) > d) return false; var lb = qax * dpy - qay * dpx; return cr.abs(lb) <= d; }; function Rect(left, top, right, bottom) { this.set(left, top, right, bottom); cr.seal(this); }; Rect.prototype.set = function (left, top, right, bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; }; Rect.prototype.copy = function (r) { this.left = r.left; this.top = r.top; this.right = r.right; this.bottom = r.bottom; }; Rect.prototype.width = function () { return this.right - this.left; }; Rect.prototype.height = function () { return this.bottom - this.top; }; Rect.prototype.offset = function (px, py) { this.left += px; this.top += py; this.right += px; this.bottom += py; return this; }; Rect.prototype.normalize = function () { var temp = 0; if (this.left > this.right) { temp = this.left; this.left = this.right; this.right = temp; } if (this.top > this.bottom) { temp = this.top; this.top = this.bottom; this.bottom = temp; } }; Rect.prototype.intersects_rect = function (rc) { return !(rc.right < this.left || rc.bottom < this.top || rc.left > this.right || rc.top > this.bottom); }; Rect.prototype.intersects_rect_off = function (rc, ox, oy) { return !(rc.right + ox < this.left || rc.bottom + oy < this.top || rc.left + ox > this.right || rc.top + oy > this.bottom); }; Rect.prototype.contains_pt = function (x, y) { return (x >= this.left && x <= this.right) && (y >= this.top && y <= this.bottom); }; Rect.prototype.equals = function (r) { return this.left === r.left && this.top === r.top && this.right === r.right && this.bottom === r.bottom; }; cr.rect = Rect; function Quad() { this.tlx = 0; this.tly = 0; this.trx = 0; this.try_ = 0; // is a keyword otherwise! this.brx = 0; this.bry = 0; this.blx = 0; this.bly = 0; cr.seal(this); }; Quad.prototype.set_from_rect = function (rc) { this.tlx = rc.left; this.tly = rc.top; this.trx = rc.right; this.try_ = rc.top; this.brx = rc.right; this.bry = rc.bottom; this.blx = rc.left; this.bly = rc.bottom; }; Quad.prototype.set_from_rotated_rect = function (rc, a) { if (a === 0) { this.set_from_rect(rc); } else { var sin_a = Math.sin(a); var cos_a = Math.cos(a); var left_sin_a = rc.left * sin_a; var top_sin_a = rc.top * sin_a; var right_sin_a = rc.right * sin_a; var bottom_sin_a = rc.bottom * sin_a; var left_cos_a = rc.left * cos_a; var top_cos_a = rc.top * cos_a; var right_cos_a = rc.right * cos_a; var bottom_cos_a = rc.bottom * cos_a; this.tlx = left_cos_a - top_sin_a; this.tly = top_cos_a + left_sin_a; this.trx = right_cos_a - top_sin_a; this.try_ = top_cos_a + right_sin_a; this.brx = right_cos_a - bottom_sin_a; this.bry = bottom_cos_a + right_sin_a; this.blx = left_cos_a - bottom_sin_a; this.bly = bottom_cos_a + left_sin_a; } }; Quad.prototype.offset = function (px, py) { this.tlx += px; this.tly += py; this.trx += px; this.try_ += py; this.brx += px; this.bry += py; this.blx += px; this.bly += py; return this; }; var minresult = 0; var maxresult = 0; function minmax4(a, b, c, d) { if (a < b) { if (c < d) { if (a < c) minresult = a; else minresult = c; if (b > d) maxresult = b; else maxresult = d; } else { if (a < d) minresult = a; else minresult = d; if (b > c) maxresult = b; else maxresult = c; } } else { if (c < d) { if (b < c) minresult = b; else minresult = c; if (a > d) maxresult = a; else maxresult = d; } else { if (b < d) minresult = b; else minresult = d; if (a > c) maxresult = a; else maxresult = c; } } }; Quad.prototype.bounding_box = function (rc) { minmax4(this.tlx, this.trx, this.brx, this.blx); rc.left = minresult; rc.right = maxresult; minmax4(this.tly, this.try_, this.bry, this.bly); rc.top = minresult; rc.bottom = maxresult; }; Quad.prototype.contains_pt = function (x, y) { var tlx = this.tlx; var tly = this.tly; var v0x = this.trx - tlx; var v0y = this.try_ - tly; var v1x = this.brx - tlx; var v1y = this.bry - tly; var v2x = x - tlx; var v2y = y - tly; var dot00 = v0x * v0x + v0y * v0y var dot01 = v0x * v1x + v0y * v1y var dot02 = v0x * v2x + v0y * v2y var dot11 = v1x * v1x + v1y * v1y var dot12 = v1x * v2x + v1y * v2y var invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01); var u = (dot11 * dot02 - dot01 * dot12) * invDenom; var v = (dot00 * dot12 - dot01 * dot02) * invDenom; if ((u >= 0.0) && (v > 0.0) && (u + v < 1)) return true; v0x = this.blx - tlx; v0y = this.bly - tly; var dot00 = v0x * v0x + v0y * v0y var dot01 = v0x * v1x + v0y * v1y var dot02 = v0x * v2x + v0y * v2y invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01); u = (dot11 * dot02 - dot01 * dot12) * invDenom; v = (dot00 * dot12 - dot01 * dot02) * invDenom; return (u >= 0.0) && (v > 0.0) && (u + v < 1); }; Quad.prototype.at = function (i, xory) { if (xory) { switch (i) { case 0: return this.tlx; case 1: return this.trx; case 2: return this.brx; case 3: return this.blx; case 4: return this.tlx; default: return this.tlx; } } else { switch (i) { case 0: return this.tly; case 1: return this.try_; case 2: return this.bry; case 3: return this.bly; case 4: return this.tly; default: return this.tly; } } }; Quad.prototype.midX = function () { return (this.tlx + this.trx + this.brx + this.blx) / 4; }; Quad.prototype.midY = function () { return (this.tly + this.try_ + this.bry + this.bly) / 4; }; Quad.prototype.intersects_segment = function (x1, y1, x2, y2) { if (this.contains_pt(x1, y1) || this.contains_pt(x2, y2)) return true; var a1x, a1y, a2x, a2y; var i; for (i = 0; i < 4; i++) { a1x = this.at(i, true); a1y = this.at(i, false); a2x = this.at(i + 1, true); a2y = this.at(i + 1, false); if (cr.segments_intersect(x1, y1, x2, y2, a1x, a1y, a2x, a2y)) return true; } return false; }; Quad.prototype.intersects_quad = function (rhs) { var midx = rhs.midX(); var midy = rhs.midY(); if (this.contains_pt(midx, midy)) return true; midx = this.midX(); midy = this.midY(); if (rhs.contains_pt(midx, midy)) return true; var a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y; var i, j; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { a1x = this.at(i, true); a1y = this.at(i, false); a2x = this.at(i + 1, true); a2y = this.at(i + 1, false); b1x = rhs.at(j, true); b1y = rhs.at(j, false); b2x = rhs.at(j + 1, true); b2y = rhs.at(j + 1, false); if (cr.segments_intersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) return true; } } return false; }; cr.quad = Quad; cr.RGB = function (red, green, blue) { return Math.max(Math.min(red, 255), 0) | (Math.max(Math.min(green, 255), 0) << 8) | (Math.max(Math.min(blue, 255), 0) << 16); }; cr.GetRValue = function (rgb) { return rgb & 0xFF; }; cr.GetGValue = function (rgb) { return (rgb & 0xFF00) >> 8; }; cr.GetBValue = function (rgb) { return (rgb & 0xFF0000) >> 16; }; cr.shallowCopy = function (a, b, allowOverwrite) { var attr; for (attr in b) { if (b.hasOwnProperty(attr)) { ; a[attr] = b[attr]; } } return a; }; cr.arrayRemove = function (arr, index) { var i, len; index = cr.floor(index); if (index < 0 || index >= arr.length) return; // index out of bounds for (i = index, len = arr.length - 1; i < len; i++) arr[i] = arr[i + 1]; cr.truncateArray(arr, len); }; cr.truncateArray = function (arr, index) { arr.length = index; }; cr.clearArray = function (arr) { cr.truncateArray(arr, 0); }; cr.shallowAssignArray = function (dest, src) { cr.clearArray(dest); var i, len; for (i = 0, len = src.length; i < len; ++i) dest[i] = src[i]; }; cr.appendArray = function (a, b) { a.push.apply(a, b); }; cr.fastIndexOf = function (arr, item) { var i, len; for (i = 0, len = arr.length; i < len; ++i) { if (arr[i] === item) return i; } return -1; }; cr.arrayFindRemove = function (arr, item) { var index = cr.fastIndexOf(arr, item); if (index !== -1) cr.arrayRemove(arr, index); }; cr.clamp = function(x, a, b) { if (x < a) return a; else if (x > b) return b; else return x; }; cr.to_radians = function(x) { return x / (180.0 / cr.PI); }; cr.to_degrees = function(x) { return x * (180.0 / cr.PI); }; cr.clamp_angle_degrees = function (a) { a %= 360; // now in (-360, 360) range if (a < 0) a += 360; // now in [0, 360) range return a; }; cr.clamp_angle = function (a) { a %= 2 * cr.PI; // now in (-2pi, 2pi) range if (a < 0) a += 2 * cr.PI; // now in [0, 2pi) range return a; }; cr.to_clamped_degrees = function (x) { return cr.clamp_angle_degrees(cr.to_degrees(x)); }; cr.to_clamped_radians = function (x) { return cr.clamp_angle(cr.to_radians(x)); }; cr.angleTo = function(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; return Math.atan2(dy, dx); }; cr.angleDiff = function (a1, a2) { if (a1 === a2) return 0; var s1 = Math.sin(a1); var c1 = Math.cos(a1); var s2 = Math.sin(a2); var c2 = Math.cos(a2); var n = s1 * s2 + c1 * c2; if (n >= 1) return 0; if (n <= -1) return cr.PI; return Math.acos(n); }; cr.angleRotate = function (start, end, step) { var ss = Math.sin(start); var cs = Math.cos(start); var se = Math.sin(end); var ce = Math.cos(end); if (Math.acos(ss * se + cs * ce) > step) { if (cs * se - ss * ce > 0) return cr.clamp_angle(start + step); else return cr.clamp_angle(start - step); } else return cr.clamp_angle(end); }; cr.angleClockwise = function (a1, a2) { var s1 = Math.sin(a1); var c1 = Math.cos(a1); var s2 = Math.sin(a2); var c2 = Math.cos(a2); return c1 * s2 - s1 * c2 <= 0; }; cr.rotatePtAround = function (px, py, a, ox, oy, getx) { if (a === 0) return getx ? px : py; var sin_a = Math.sin(a); var cos_a = Math.cos(a); px -= ox; py -= oy; var left_sin_a = px * sin_a; var top_sin_a = py * sin_a; var left_cos_a = px * cos_a; var top_cos_a = py * cos_a; px = left_cos_a - top_sin_a; py = top_cos_a + left_sin_a; px += ox; py += oy; return getx ? px : py; } cr.distanceTo = function(x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; return Math.sqrt(dx*dx + dy*dy); }; cr.xor = function (x, y) { return !x !== !y; }; cr.lerp = function (a, b, x) { return a + (b - a) * x; }; cr.unlerp = function (a, b, c) { if (a === b) return 0; // avoid divide by 0 return (c - a) / (b - a); }; cr.anglelerp = function (a, b, x) { var diff = cr.angleDiff(a, b); if (cr.angleClockwise(b, a)) { return a + diff * x; } else { return a - diff * x; } }; cr.qarp = function (a, b, c, x) { return cr.lerp(cr.lerp(a, b, x), cr.lerp(b, c, x), x); }; cr.cubic = function (a, b, c, d, x) { return cr.lerp(cr.qarp(a, b, c, x), cr.qarp(b, c, d, x), x); }; cr.cosp = function (a, b, x) { return (a + b + (a - b) * Math.cos(x * Math.PI)) / 2; }; cr.hasAnyOwnProperty = function (o) { var p; for (p in o) { if (o.hasOwnProperty(p)) return true; } return false; }; cr.wipe = function (obj) { var p; for (p in obj) { if (obj.hasOwnProperty(p)) delete obj[p]; } }; var startup_time = +(new Date()); cr.performance_now = function() { if (typeof window["performance"] !== "undefined") { var winperf = window["performance"]; if (typeof winperf.now !== "undefined") return winperf.now(); else if (typeof winperf["webkitNow"] !== "undefined") return winperf["webkitNow"](); else if (typeof winperf["mozNow"] !== "undefined") return winperf["mozNow"](); else if (typeof winperf["msNow"] !== "undefined") return winperf["msNow"](); } return Date.now() - startup_time; }; var isChrome = false; var isSafari = false; var isiOS = false; var isEjecta = false; if (typeof window !== "undefined") // not c2 editor { isChrome = /chrome/i.test(navigator.userAgent) || /chromium/i.test(navigator.userAgent); isSafari = !isChrome && /safari/i.test(navigator.userAgent); isiOS = /(iphone|ipod|ipad)/i.test(navigator.userAgent); isEjecta = window["c2ejecta"]; } var supports_set = ((!isSafari && !isEjecta && !isiOS) && (typeof Set !== "undefined" && typeof Set.prototype["forEach"] !== "undefined")); function ObjectSet_() { this.s = null; this.items = null; // lazy allocated (hopefully results in better GC performance) this.item_count = 0; if (supports_set) { this.s = new Set(); } this.values_cache = []; this.cache_valid = true; cr.seal(this); }; ObjectSet_.prototype.contains = function (x) { if (this.isEmpty()) return false; if (supports_set) return this.s["has"](x); else return (this.items && this.items.hasOwnProperty(x)); }; ObjectSet_.prototype.add = function (x) { if (supports_set) { if (!this.s["has"](x)) { this.s["add"](x); this.cache_valid = false; } } else { var str = x.toString(); var items = this.items; if (!items) { this.items = {}; this.items[str] = x; this.item_count = 1; this.cache_valid = false; } else if (!items.hasOwnProperty(str)) { items[str] = x; this.item_count++; this.cache_valid = false; } } }; ObjectSet_.prototype.remove = function (x) { if (this.isEmpty()) return; if (supports_set) { if (this.s["has"](x)) { this.s["delete"](x); this.cache_valid = false; } } else if (this.items) { var str = x.toString(); var items = this.items; if (items.hasOwnProperty(str)) { delete items[str]; this.item_count--; this.cache_valid = false; } } }; ObjectSet_.prototype.clear = function (/*wipe_*/) { if (this.isEmpty()) return; if (supports_set) { this.s["clear"](); // best! } else { this.items = null; // creates garbage; will lazy allocate on next add() this.item_count = 0; } cr.clearArray(this.values_cache); this.cache_valid = true; }; ObjectSet_.prototype.isEmpty = function () { return this.count() === 0; }; ObjectSet_.prototype.count = function () { if (supports_set) return this.s["size"]; else return this.item_count; }; var current_arr = null; var current_index = 0; function set_append_to_arr(x) { current_arr[current_index++] = x; }; ObjectSet_.prototype.update_cache = function () { if (this.cache_valid) return; if (supports_set) { cr.clearArray(this.values_cache); current_arr = this.values_cache; current_index = 0; this.s["forEach"](set_append_to_arr); ; current_arr = null; current_index = 0; } else { var values_cache = this.values_cache; cr.clearArray(values_cache); var p, n = 0, items = this.items; if (items) { for (p in items) { if (items.hasOwnProperty(p)) values_cache[n++] = items[p]; } } ; } this.cache_valid = true; }; ObjectSet_.prototype.valuesRef = function () { this.update_cache(); return this.values_cache; }; cr.ObjectSet = ObjectSet_; var tmpSet = new cr.ObjectSet(); cr.removeArrayDuplicates = function (arr) { var i, len; for (i = 0, len = arr.length; i < len; ++i) { tmpSet.add(arr[i]); } cr.shallowAssignArray(arr, tmpSet.valuesRef()); tmpSet.clear(); }; cr.arrayRemoveAllFromObjectSet = function (arr, remset) { if (supports_set) cr.arrayRemoveAll_set(arr, remset.s); else cr.arrayRemoveAll_arr(arr, remset.valuesRef()); }; cr.arrayRemoveAll_set = function (arr, s) { var i, j, len, item; for (i = 0, j = 0, len = arr.length; i < len; ++i) { item = arr[i]; if (!s["has"](item)) // not an item to remove arr[j++] = item; // keep it } cr.truncateArray(arr, j); }; cr.arrayRemoveAll_arr = function (arr, rem) { var i, j, len, item; for (i = 0, j = 0, len = arr.length; i < len; ++i) { item = arr[i]; if (cr.fastIndexOf(rem, item) === -1) // not an item to remove arr[j++] = item; // keep it } cr.truncateArray(arr, j); }; function KahanAdder_() { this.c = 0; this.y = 0; this.t = 0; this.sum = 0; cr.seal(this); }; KahanAdder_.prototype.add = function (v) { this.y = v - this.c; this.t = this.sum + this.y; this.c = (this.t - this.sum) - this.y; this.sum = this.t; }; KahanAdder_.prototype.reset = function () { this.c = 0; this.y = 0; this.t = 0; this.sum = 0; }; cr.KahanAdder = KahanAdder_; cr.regexp_escape = function(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); }; function CollisionPoly_(pts_array_) { this.pts_cache = []; this.bboxLeft = 0; this.bboxTop = 0; this.bboxRight = 0; this.bboxBottom = 0; this.convexpolys = null; // for physics behavior to cache separated polys this.set_pts(pts_array_); cr.seal(this); }; CollisionPoly_.prototype.set_pts = function(pts_array_) { this.pts_array = pts_array_; this.pts_count = pts_array_.length / 2; // x, y, x, y... in array this.pts_cache.length = pts_array_.length; this.cache_width = -1; this.cache_height = -1; this.cache_angle = 0; }; CollisionPoly_.prototype.is_empty = function() { return !this.pts_array.length; }; CollisionPoly_.prototype.update_bbox = function () { var myptscache = this.pts_cache; var bboxLeft_ = myptscache[0]; var bboxRight_ = bboxLeft_; var bboxTop_ = myptscache[1]; var bboxBottom_ = bboxTop_; var x, y, i = 1, i2, len = this.pts_count; for ( ; i < len; ++i) { i2 = i*2; x = myptscache[i2]; y = myptscache[i2+1]; if (x < bboxLeft_) bboxLeft_ = x; if (x > bboxRight_) bboxRight_ = x; if (y < bboxTop_) bboxTop_ = y; if (y > bboxBottom_) bboxBottom_ = y; } this.bboxLeft = bboxLeft_; this.bboxRight = bboxRight_; this.bboxTop = bboxTop_; this.bboxBottom = bboxBottom_; }; CollisionPoly_.prototype.set_from_rect = function(rc, offx, offy) { this.pts_cache.length = 8; this.pts_count = 4; var myptscache = this.pts_cache; myptscache[0] = rc.left - offx; myptscache[1] = rc.top - offy; myptscache[2] = rc.right - offx; myptscache[3] = rc.top - offy; myptscache[4] = rc.right - offx; myptscache[5] = rc.bottom - offy; myptscache[6] = rc.left - offx; myptscache[7] = rc.bottom - offy; this.cache_width = rc.right - rc.left; this.cache_height = rc.bottom - rc.top; this.update_bbox(); }; CollisionPoly_.prototype.set_from_quad = function(q, offx, offy, w, h) { this.pts_cache.length = 8; this.pts_count = 4; var myptscache = this.pts_cache; myptscache[0] = q.tlx - offx; myptscache[1] = q.tly - offy; myptscache[2] = q.trx - offx; myptscache[3] = q.try_ - offy; myptscache[4] = q.brx - offx; myptscache[5] = q.bry - offy; myptscache[6] = q.blx - offx; myptscache[7] = q.bly - offy; this.cache_width = w; this.cache_height = h; this.update_bbox(); }; CollisionPoly_.prototype.set_from_poly = function (r) { this.pts_count = r.pts_count; cr.shallowAssignArray(this.pts_cache, r.pts_cache); this.bboxLeft = r.bboxLeft; this.bboxTop - r.bboxTop; this.bboxRight = r.bboxRight; this.bboxBottom = r.bboxBottom; }; CollisionPoly_.prototype.cache_poly = function(w, h, a) { if (this.cache_width === w && this.cache_height === h && this.cache_angle === a) return; // cache up-to-date this.cache_width = w; this.cache_height = h; this.cache_angle = a; var i, i2, i21, len, x, y; var sina = 0; var cosa = 1; var myptsarray = this.pts_array; var myptscache = this.pts_cache; if (a !== 0) { sina = Math.sin(a); cosa = Math.cos(a); } for (i = 0, len = this.pts_count; i < len; i++) { i2 = i*2; i21 = i2+1; x = myptsarray[i2] * w; y = myptsarray[i21] * h; myptscache[i2] = (x * cosa) - (y * sina); myptscache[i21] = (y * cosa) + (x * sina); } this.update_bbox(); }; CollisionPoly_.prototype.contains_pt = function (a2x, a2y) { var myptscache = this.pts_cache; if (a2x === myptscache[0] && a2y === myptscache[1]) return true; var i, i2, imod, len = this.pts_count; var a1x = this.bboxLeft - 110; var a1y = this.bboxTop - 101; var a3x = this.bboxRight + 131 var a3y = this.bboxBottom + 120; var b1x, b1y, b2x, b2y; var count1 = 0, count2 = 0; for (i = 0; i < len; i++) { i2 = i*2; imod = ((i+1)%len)*2; b1x = myptscache[i2]; b1y = myptscache[i2+1]; b2x = myptscache[imod]; b2y = myptscache[imod+1]; if (cr.segments_intersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) count1++; if (cr.segments_intersect(a3x, a3y, a2x, a2y, b1x, b1y, b2x, b2y)) count2++; } return (count1 % 2 === 1) || (count2 % 2 === 1); }; CollisionPoly_.prototype.intersects_poly = function (rhs, offx, offy) { var rhspts = rhs.pts_cache; var mypts = this.pts_cache; if (this.contains_pt(rhspts[0] + offx, rhspts[1] + offy)) return true; if (rhs.contains_pt(mypts[0] - offx, mypts[1] - offy)) return true; var i, i2, imod, leni, j, j2, jmod, lenj; var a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y; for (i = 0, leni = this.pts_count; i < leni; i++) { i2 = i*2; imod = ((i+1)%leni)*2; a1x = mypts[i2]; a1y = mypts[i2+1]; a2x = mypts[imod]; a2y = mypts[imod+1]; for (j = 0, lenj = rhs.pts_count; j < lenj; j++) { j2 = j*2; jmod = ((j+1)%lenj)*2; b1x = rhspts[j2] + offx; b1y = rhspts[j2+1] + offy; b2x = rhspts[jmod] + offx; b2y = rhspts[jmod+1] + offy; if (cr.segments_intersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y)) return true; } } return false; }; CollisionPoly_.prototype.intersects_segment = function (offx, offy, x1, y1, x2, y2) { var mypts = this.pts_cache; if (this.contains_pt(x1 - offx, y1 - offy)) return true; var i, leni, i2, imod; var a1x, a1y, a2x, a2y; for (i = 0, leni = this.pts_count; i < leni; i++) { i2 = i*2; imod = ((i+1)%leni)*2; a1x = mypts[i2] + offx; a1y = mypts[i2+1] + offy; a2x = mypts[imod] + offx; a2y = mypts[imod+1] + offy; if (cr.segments_intersect(x1, y1, x2, y2, a1x, a1y, a2x, a2y)) return true; } return false; }; CollisionPoly_.prototype.mirror = function (px) { var i, leni, i2; for (i = 0, leni = this.pts_count; i < leni; ++i) { i2 = i*2; this.pts_cache[i2] = px * 2 - this.pts_cache[i2]; } }; CollisionPoly_.prototype.flip = function (py) { var i, leni, i21; for (i = 0, leni = this.pts_count; i < leni; ++i) { i21 = i*2+1; this.pts_cache[i21] = py * 2 - this.pts_cache[i21]; } }; CollisionPoly_.prototype.diag = function () { var i, leni, i2, i21, temp; for (i = 0, leni = this.pts_count; i < leni; ++i) { i2 = i*2; i21 = i2+1; temp = this.pts_cache[i2]; this.pts_cache[i2] = this.pts_cache[i21]; this.pts_cache[i21] = temp; } }; cr.CollisionPoly = CollisionPoly_; function SparseGrid_(cellwidth_, cellheight_) { this.cellwidth = cellwidth_; this.cellheight = cellheight_; this.cells = {}; }; SparseGrid_.prototype.totalCellCount = 0; SparseGrid_.prototype.getCell = function (x_, y_, create_if_missing) { var ret; var col = this.cells[x_]; if (!col) { if (create_if_missing) { ret = allocGridCell(this, x_, y_); this.cells[x_] = {}; this.cells[x_][y_] = ret; return ret; } else return null; } ret = col[y_]; if (ret) return ret; else if (create_if_missing) { ret = allocGridCell(this, x_, y_); this.cells[x_][y_] = ret; return ret; } else return null; }; SparseGrid_.prototype.XToCell = function (x_) { return cr.floor(x_ / this.cellwidth); }; SparseGrid_.prototype.YToCell = function (y_) { return cr.floor(y_ / this.cellheight); }; SparseGrid_.prototype.update = function (inst, oldrange, newrange) { var x, lenx, y, leny, cell; if (oldrange) { for (x = oldrange.left, lenx = oldrange.right; x <= lenx; ++x) { for (y = oldrange.top, leny = oldrange.bottom; y <= leny; ++y) { if (newrange && newrange.contains_pt(x, y)) continue; // is still in this cell cell = this.getCell(x, y, false); // don't create if missing if (!cell) continue; // cell does not exist yet cell.remove(inst); if (cell.isEmpty()) { freeGridCell(cell); this.cells[x][y] = null; } } } } if (newrange) { for (x = newrange.left, lenx = newrange.right; x <= lenx; ++x) { for (y = newrange.top, leny = newrange.bottom; y <= leny; ++y) { if (oldrange && oldrange.contains_pt(x, y)) continue; // is still in this cell this.getCell(x, y, true).insert(inst); } } } }; SparseGrid_.prototype.queryRange = function (rc, result) { var x, lenx, ystart, y, leny, cell; x = this.XToCell(rc.left); ystart = this.YToCell(rc.top); lenx = this.XToCell(rc.right); leny = this.YToCell(rc.bottom); for ( ; x <= lenx; ++x) { for (y = ystart; y <= leny; ++y) { cell = this.getCell(x, y, false); if (!cell) continue; cell.dump(result); } } }; cr.SparseGrid = SparseGrid_; function RenderGrid_(cellwidth_, cellheight_) { this.cellwidth = cellwidth_; this.cellheight = cellheight_; this.cells = {}; }; RenderGrid_.prototype.totalCellCount = 0; RenderGrid_.prototype.getCell = function (x_, y_, create_if_missing) { var ret; var col = this.cells[x_]; if (!col) { if (create_if_missing) { ret = allocRenderCell(this, x_, y_); this.cells[x_] = {}; this.cells[x_][y_] = ret; return ret; } else return null; } ret = col[y_]; if (ret) return ret; else if (create_if_missing) { ret = allocRenderCell(this, x_, y_); this.cells[x_][y_] = ret; return ret; } else return null; }; RenderGrid_.prototype.XToCell = function (x_) { return cr.floor(x_ / this.cellwidth); }; RenderGrid_.prototype.YToCell = function (y_) { return cr.floor(y_ / this.cellheight); }; RenderGrid_.prototype.update = function (inst, oldrange, newrange) { var x, lenx, y, leny, cell; if (oldrange) { for (x = oldrange.left, lenx = oldrange.right; x <= lenx; ++x) { for (y = oldrange.top, leny = oldrange.bottom; y <= leny; ++y) { if (newrange && newrange.contains_pt(x, y)) continue; // is still in this cell cell = this.getCell(x, y, false); // don't create if missing if (!cell) continue; // cell does not exist yet cell.remove(inst); if (cell.isEmpty()) { freeRenderCell(cell); this.cells[x][y] = null; } } } } if (newrange) { for (x = newrange.left, lenx = newrange.right; x <= lenx; ++x) { for (y = newrange.top, leny = newrange.bottom; y <= leny; ++y) { if (oldrange && oldrange.contains_pt(x, y)) continue; // is still in this cell this.getCell(x, y, true).insert(inst); } } } }; RenderGrid_.prototype.queryRange = function (left, top, right, bottom, result) { var x, lenx, ystart, y, leny, cell; x = this.XToCell(left); ystart = this.YToCell(top); lenx = this.XToCell(right); leny = this.YToCell(bottom); for ( ; x <= lenx; ++x) { for (y = ystart; y <= leny; ++y) { cell = this.getCell(x, y, false); if (!cell) continue; cell.dump(result); } } }; RenderGrid_.prototype.markRangeChanged = function (rc) { var x, lenx, ystart, y, leny, cell; x = rc.left; ystart = rc.top; lenx = rc.right; leny = rc.bottom; for ( ; x <= lenx; ++x) { for (y = ystart; y <= leny; ++y) { cell = this.getCell(x, y, false); if (!cell) continue; cell.is_sorted = false; } } }; cr.RenderGrid = RenderGrid_; var gridcellcache = []; function allocGridCell(grid_, x_, y_) { var ret; SparseGrid_.prototype.totalCellCount++; if (gridcellcache.length) { ret = gridcellcache.pop(); ret.grid = grid_; ret.x = x_; ret.y = y_; return ret; } else return new cr.GridCell(grid_, x_, y_); }; function freeGridCell(c) { SparseGrid_.prototype.totalCellCount--; c.objects.clear(); if (gridcellcache.length < 1000) gridcellcache.push(c); }; function GridCell_(grid_, x_, y_) { this.grid = grid_; this.x = x_; this.y = y_; this.objects = new cr.ObjectSet(); }; GridCell_.prototype.isEmpty = function () { return this.objects.isEmpty(); }; GridCell_.prototype.insert = function (inst) { this.objects.add(inst); }; GridCell_.prototype.remove = function (inst) { this.objects.remove(inst); }; GridCell_.prototype.dump = function (result) { cr.appendArray(result, this.objects.valuesRef()); }; cr.GridCell = GridCell_; var rendercellcache = []; function allocRenderCell(grid_, x_, y_) { var ret; RenderGrid_.prototype.totalCellCount++; if (rendercellcache.length) { ret = rendercellcache.pop(); ret.grid = grid_; ret.x = x_; ret.y = y_; return ret; } else return new cr.RenderCell(grid_, x_, y_); }; function freeRenderCell(c) { RenderGrid_.prototype.totalCellCount--; c.reset(); if (rendercellcache.length < 1000) rendercellcache.push(c); }; function RenderCell_(grid_, x_, y_) { this.grid = grid_; this.x = x_; this.y = y_; this.objects = []; // array which needs to be sorted by Z order this.is_sorted = true; // whether array is in correct sort order or not this.pending_removal = new cr.ObjectSet(); this.any_pending_removal = false; }; RenderCell_.prototype.isEmpty = function () { if (!this.objects.length) { ; ; return true; } if (this.objects.length > this.pending_removal.count()) return false; ; this.flush_pending(); // takes fast path and just resets state return true; }; RenderCell_.prototype.insert = function (inst) { if (this.pending_removal.contains(inst)) { this.pending_removal.remove(inst); if (this.pending_removal.isEmpty()) this.any_pending_removal = false; return; } if (this.objects.length) { var top = this.objects[this.objects.length - 1]; if (top.get_zindex() > inst.get_zindex()) this.is_sorted = false; // 'inst' should be somewhere beneath 'top' this.objects.push(inst); } else { this.objects.push(inst); this.is_sorted = true; } ; }; RenderCell_.prototype.remove = function (inst) { this.pending_removal.add(inst); this.any_pending_removal = true; if (this.pending_removal.count() >= 30) this.flush_pending(); }; RenderCell_.prototype.flush_pending = function () { ; if (!this.any_pending_removal) return; // not changed if (this.pending_removal.count() === this.objects.length) { this.reset(); return; } cr.arrayRemoveAllFromObjectSet(this.objects, this.pending_removal); this.pending_removal.clear(); this.any_pending_removal = false; }; function sortByInstanceZIndex(a, b) { return a.zindex - b.zindex; }; RenderCell_.prototype.ensure_sorted = function () { if (this.is_sorted) return; // already sorted this.objects.sort(sortByInstanceZIndex); this.is_sorted = true; }; RenderCell_.prototype.reset = function () { cr.clearArray(this.objects); this.is_sorted = true; this.pending_removal.clear(); this.any_pending_removal = false; }; RenderCell_.prototype.dump = function (result) { this.flush_pending(); this.ensure_sorted(); if (this.objects.length) result.push(this.objects); }; cr.RenderCell = RenderCell_; var fxNames = [ "lighter", "xor", "copy", "destination-over", "source-in", "destination-in", "source-out", "destination-out", "source-atop", "destination-atop"]; cr.effectToCompositeOp = function(effect) { if (effect <= 0 || effect >= 11) return "source-over"; return fxNames[effect - 1]; // not including "none" so offset by 1 }; cr.setGLBlend = function(this_, effect, gl) { if (!gl) return; this_.srcBlend = gl.ONE; this_.destBlend = gl.ONE_MINUS_SRC_ALPHA; switch (effect) { case 1: // lighter (additive) this_.srcBlend = gl.ONE; this_.destBlend = gl.ONE; break; case 2: // xor break; // todo case 3: // copy this_.srcBlend = gl.ONE; this_.destBlend = gl.ZERO; break; case 4: // destination-over this_.srcBlend = gl.ONE_MINUS_DST_ALPHA; this_.destBlend = gl.ONE; break; case 5: // source-in this_.srcBlend = gl.DST_ALPHA; this_.destBlend = gl.ZERO; break; case 6: // destination-in this_.srcBlend = gl.ZERO; this_.destBlend = gl.SRC_ALPHA; break; case 7: // source-out this_.srcBlend = gl.ONE_MINUS_DST_ALPHA; this_.destBlend = gl.ZERO; break; case 8: // destination-out this_.srcBlend = gl.ZERO; this_.destBlend = gl.ONE_MINUS_SRC_ALPHA; break; case 9: // source-atop this_.srcBlend = gl.DST_ALPHA; this_.destBlend = gl.ONE_MINUS_SRC_ALPHA; break; case 10: // destination-atop this_.srcBlend = gl.ONE_MINUS_DST_ALPHA; this_.destBlend = gl.SRC_ALPHA; break; } }; cr.round6dp = function (x) { return Math.round(x * 1000000) / 1000000; }; /* var localeCompare_options = { "usage": "search", "sensitivity": "accent" }; var has_localeCompare = !!"a".localeCompare; var localeCompare_works1 = (has_localeCompare && "a".localeCompare("A", undefined, localeCompare_options) === 0); var localeCompare_works2 = (has_localeCompare && "a".localeCompare("รก", undefined, localeCompare_options) !== 0); var supports_localeCompare = (has_localeCompare && localeCompare_works1 && localeCompare_works2); */ cr.equals_nocase = function (a, b) { if (typeof a !== "string" || typeof b !== "string") return false; if (a.length !== b.length) return false; if (a === b) return true; /* if (supports_localeCompare) { return (a.localeCompare(b, undefined, localeCompare_options) === 0); } else { */ return a.toLowerCase() === b.toLowerCase(); }; cr.isCanvasInputEvent = function (e) { var target = e.target; if (!target) return true; if (target === document || target === window) return true; if (document && document.body && target === document.body) return true; if (cr.equals_nocase(target.tagName, "canvas")) return true; return false; }; }()); var MatrixArray=typeof Float32Array!=="undefined"?Float32Array:Array,glMatrixArrayType=MatrixArray,vec3={},mat3={},mat4={},quat4={};vec3.create=function(a){var b=new MatrixArray(3);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2]);return b};vec3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];return b};vec3.add=function(a,b,c){if(!c||a===c)return a[0]+=b[0],a[1]+=b[1],a[2]+=b[2],a;c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];return c}; vec3.subtract=function(a,b,c){if(!c||a===c)return a[0]-=b[0],a[1]-=b[1],a[2]-=b[2],a;c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];return c};vec3.negate=function(a,b){b||(b=a);b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];return b};vec3.scale=function(a,b,c){if(!c||a===c)return a[0]*=b,a[1]*=b,a[2]*=b,a;c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;return c}; vec3.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=Math.sqrt(c*c+d*d+e*e);if(g){if(g===1)return b[0]=c,b[1]=d,b[2]=e,b}else return b[0]=0,b[1]=0,b[2]=0,b;g=1/g;b[0]=c*g;b[1]=d*g;b[2]=e*g;return b};vec3.cross=function(a,b,c){c||(c=a);var d=a[0],e=a[1],a=a[2],g=b[0],f=b[1],b=b[2];c[0]=e*b-a*f;c[1]=a*g-d*b;c[2]=d*f-e*g;return c};vec3.length=function(a){var b=a[0],c=a[1],a=a[2];return Math.sqrt(b*b+c*c+a*a)};vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]}; vec3.direction=function(a,b,c){c||(c=a);var d=a[0]-b[0],e=a[1]-b[1],a=a[2]-b[2],b=Math.sqrt(d*d+e*e+a*a);if(!b)return c[0]=0,c[1]=0,c[2]=0,c;b=1/b;c[0]=d*b;c[1]=e*b;c[2]=a*b;return c};vec3.lerp=function(a,b,c,d){d||(d=a);d[0]=a[0]+c*(b[0]-a[0]);d[1]=a[1]+c*(b[1]-a[1]);d[2]=a[2]+c*(b[2]-a[2]);return d};vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"}; mat3.create=function(a){var b=new MatrixArray(9);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8]);return b};mat3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return b};mat3.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=1;a[5]=0;a[6]=0;a[7]=0;a[8]=1;return a}; mat3.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[5];a[1]=a[3];a[2]=a[6];a[3]=c;a[5]=a[7];a[6]=d;a[7]=e;return a}b[0]=a[0];b[1]=a[3];b[2]=a[6];b[3]=a[1];b[4]=a[4];b[5]=a[7];b[6]=a[2];b[7]=a[5];b[8]=a[8];return b};mat3.toMat4=function(a,b){b||(b=mat4.create());b[15]=1;b[14]=0;b[13]=0;b[12]=0;b[11]=0;b[10]=a[8];b[9]=a[7];b[8]=a[6];b[7]=0;b[6]=a[5];b[5]=a[4];b[4]=a[3];b[3]=0;b[2]=a[2];b[1]=a[1];b[0]=a[0];return b}; mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};mat4.create=function(a){var b=new MatrixArray(16);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15]);return b}; mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a}; mat4.transpose=function(a,b){if(!b||a===b){var c=a[1],d=a[2],e=a[3],g=a[6],f=a[7],h=a[11];a[1]=a[4];a[2]=a[8];a[3]=a[12];a[4]=c;a[6]=a[9];a[7]=a[13];a[8]=d;a[9]=g;a[11]=a[14];a[12]=e;a[13]=f;a[14]=h;return a}b[0]=a[0];b[1]=a[4];b[2]=a[8];b[3]=a[12];b[4]=a[1];b[5]=a[5];b[6]=a[9];b[7]=a[13];b[8]=a[2];b[9]=a[6];b[10]=a[10];b[11]=a[14];b[12]=a[3];b[13]=a[7];b[14]=a[11];b[15]=a[15];return b}; mat4.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],g=a[4],f=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],n=a[11],o=a[12],m=a[13],p=a[14],a=a[15];return o*k*h*e-j*m*h*e-o*f*l*e+g*m*l*e+j*f*p*e-g*k*p*e-o*k*d*i+j*m*d*i+o*c*l*i-b*m*l*i-j*c*p*i+b*k*p*i+o*f*d*n-g*m*d*n-o*c*h*n+b*m*h*n+g*c*p*n-b*f*p*n-j*f*d*a+g*k*d*a+j*c*h*a-b*k*h*a-g*c*l*a+b*f*l*a}; mat4.inverse=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],n=a[10],o=a[11],m=a[12],p=a[13],r=a[14],s=a[15],A=c*h-d*f,B=c*i-e*f,t=c*j-g*f,u=d*i-e*h,v=d*j-g*h,w=e*j-g*i,x=k*p-l*m,y=k*r-n*m,z=k*s-o*m,C=l*r-n*p,D=l*s-o*p,E=n*s-o*r,q=1/(A*E-B*D+t*C+u*z-v*y+w*x);b[0]=(h*E-i*D+j*C)*q;b[1]=(-d*E+e*D-g*C)*q;b[2]=(p*w-r*v+s*u)*q;b[3]=(-l*w+n*v-o*u)*q;b[4]=(-f*E+i*z-j*y)*q;b[5]=(c*E-e*z+g*y)*q;b[6]=(-m*w+r*t-s*B)*q;b[7]=(k*w-n*t+o*B)*q;b[8]=(f*D-h*z+j*x)*q; b[9]=(-c*D+d*z-g*x)*q;b[10]=(m*v-p*t+s*A)*q;b[11]=(-k*v+l*t-o*A)*q;b[12]=(-f*C+h*y-i*x)*q;b[13]=(c*C-d*y+e*x)*q;b[14]=(-m*u+p*B-r*A)*q;b[15]=(k*u-l*B+n*A)*q;return b};mat4.toRotationMat=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; mat4.toMat3=function(a,b){b||(b=mat3.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[4];b[4]=a[5];b[5]=a[6];b[6]=a[8];b[7]=a[9];b[8]=a[10];return b};mat4.toInverseMat3=function(a,b){var c=a[0],d=a[1],e=a[2],g=a[4],f=a[5],h=a[6],i=a[8],j=a[9],k=a[10],l=k*f-h*j,n=-k*g+h*i,o=j*g-f*i,m=c*l+d*n+e*o;if(!m)return null;m=1/m;b||(b=mat3.create());b[0]=l*m;b[1]=(-k*d+e*j)*m;b[2]=(h*d-e*f)*m;b[3]=n*m;b[4]=(k*c-e*i)*m;b[5]=(-h*c+e*g)*m;b[6]=o*m;b[7]=(-j*c+d*i)*m;b[8]=(f*c-d*g)*m;return b}; mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],n=a[9],o=a[10],m=a[11],p=a[12],r=a[13],s=a[14],a=a[15],A=b[0],B=b[1],t=b[2],u=b[3],v=b[4],w=b[5],x=b[6],y=b[7],z=b[8],C=b[9],D=b[10],E=b[11],q=b[12],F=b[13],G=b[14],b=b[15];c[0]=A*d+B*h+t*l+u*p;c[1]=A*e+B*i+t*n+u*r;c[2]=A*g+B*j+t*o+u*s;c[3]=A*f+B*k+t*m+u*a;c[4]=v*d+w*h+x*l+y*p;c[5]=v*e+w*i+x*n+y*r;c[6]=v*g+w*j+x*o+y*s;c[7]=v*f+w*k+x*m+y*a;c[8]=z*d+C*h+D*l+E*p;c[9]=z*e+C*i+D*n+E*r;c[10]=z*g+C* j+D*o+E*s;c[11]=z*f+C*k+D*m+E*a;c[12]=q*d+F*h+G*l+b*p;c[13]=q*e+F*i+G*n+b*r;c[14]=q*g+F*j+G*o+b*s;c[15]=q*f+F*k+G*m+b*a;return c};mat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],b=b[2];c[0]=a[0]*d+a[4]*e+a[8]*b+a[12];c[1]=a[1]*d+a[5]*e+a[9]*b+a[13];c[2]=a[2]*d+a[6]*e+a[10]*b+a[14];return c}; mat4.multiplyVec4=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2],b=b[3];c[0]=a[0]*d+a[4]*e+a[8]*g+a[12]*b;c[1]=a[1]*d+a[5]*e+a[9]*g+a[13]*b;c[2]=a[2]*d+a[6]*e+a[10]*g+a[14]*b;c[3]=a[3]*d+a[7]*e+a[11]*g+a[15]*b;return c}; mat4.translate=function(a,b,c){var d=b[0],e=b[1],b=b[2],g,f,h,i,j,k,l,n,o,m,p,r;if(!c||a===c)return a[12]=a[0]*d+a[4]*e+a[8]*b+a[12],a[13]=a[1]*d+a[5]*e+a[9]*b+a[13],a[14]=a[2]*d+a[6]*e+a[10]*b+a[14],a[15]=a[3]*d+a[7]*e+a[11]*b+a[15],a;g=a[0];f=a[1];h=a[2];i=a[3];j=a[4];k=a[5];l=a[6];n=a[7];o=a[8];m=a[9];p=a[10];r=a[11];c[0]=g;c[1]=f;c[2]=h;c[3]=i;c[4]=j;c[5]=k;c[6]=l;c[7]=n;c[8]=o;c[9]=m;c[10]=p;c[11]=r;c[12]=g*d+j*e+o*b+a[12];c[13]=f*d+k*e+m*b+a[13];c[14]=h*d+l*e+p*b+a[14];c[15]=i*d+n*e+r*b+a[15]; return c};mat4.scale=function(a,b,c){var d=b[0],e=b[1],b=b[2];if(!c||a===c)return a[0]*=d,a[1]*=d,a[2]*=d,a[3]*=d,a[4]*=e,a[5]*=e,a[6]*=e,a[7]*=e,a[8]*=b,a[9]*=b,a[10]*=b,a[11]*=b,a;c[0]=a[0]*d;c[1]=a[1]*d;c[2]=a[2]*d;c[3]=a[3]*d;c[4]=a[4]*e;c[5]=a[5]*e;c[6]=a[6]*e;c[7]=a[7]*e;c[8]=a[8]*b;c[9]=a[9]*b;c[10]=a[10]*b;c[11]=a[11]*b;c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15];return c}; mat4.rotate=function(a,b,c,d){var e=c[0],g=c[1],c=c[2],f=Math.sqrt(e*e+g*g+c*c),h,i,j,k,l,n,o,m,p,r,s,A,B,t,u,v,w,x,y,z;if(!f)return null;f!==1&&(f=1/f,e*=f,g*=f,c*=f);h=Math.sin(b);i=Math.cos(b);j=1-i;b=a[0];f=a[1];k=a[2];l=a[3];n=a[4];o=a[5];m=a[6];p=a[7];r=a[8];s=a[9];A=a[10];B=a[11];t=e*e*j+i;u=g*e*j+c*h;v=c*e*j-g*h;w=e*g*j-c*h;x=g*g*j+i;y=c*g*j+e*h;z=e*c*j+g*h;e=g*c*j-e*h;g=c*c*j+i;d?a!==d&&(d[12]=a[12],d[13]=a[13],d[14]=a[14],d[15]=a[15]):d=a;d[0]=b*t+n*u+r*v;d[1]=f*t+o*u+s*v;d[2]=k*t+m*u+A* v;d[3]=l*t+p*u+B*v;d[4]=b*w+n*x+r*y;d[5]=f*w+o*x+s*y;d[6]=k*w+m*x+A*y;d[7]=l*w+p*x+B*y;d[8]=b*z+n*e+r*g;d[9]=f*z+o*e+s*g;d[10]=k*z+m*e+A*g;d[11]=l*z+p*e+B*g;return d};mat4.rotateX=function(a,b,c){var d=Math.sin(b),b=Math.cos(b),e=a[4],g=a[5],f=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11];c?a!==c&&(c[0]=a[0],c[1]=a[1],c[2]=a[2],c[3]=a[3],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a;c[4]=e*b+i*d;c[5]=g*b+j*d;c[6]=f*b+k*d;c[7]=h*b+l*d;c[8]=e*-d+i*b;c[9]=g*-d+j*b;c[10]=f*-d+k*b;c[11]=h*-d+l*b;return c}; mat4.rotateY=function(a,b,c){var d=Math.sin(b),b=Math.cos(b),e=a[0],g=a[1],f=a[2],h=a[3],i=a[8],j=a[9],k=a[10],l=a[11];c?a!==c&&(c[4]=a[4],c[5]=a[5],c[6]=a[6],c[7]=a[7],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a;c[0]=e*b+i*-d;c[1]=g*b+j*-d;c[2]=f*b+k*-d;c[3]=h*b+l*-d;c[8]=e*d+i*b;c[9]=g*d+j*b;c[10]=f*d+k*b;c[11]=h*d+l*b;return c}; mat4.rotateZ=function(a,b,c){var d=Math.sin(b),b=Math.cos(b),e=a[0],g=a[1],f=a[2],h=a[3],i=a[4],j=a[5],k=a[6],l=a[7];c?a!==c&&(c[8]=a[8],c[9]=a[9],c[10]=a[10],c[11]=a[11],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a;c[0]=e*b+i*d;c[1]=g*b+j*d;c[2]=f*b+k*d;c[3]=h*b+l*d;c[4]=e*-d+i*b;c[5]=g*-d+j*b;c[6]=f*-d+k*b;c[7]=h*-d+l*b;return c}; mat4.frustum=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=e*2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=e*2/i;f[6]=0;f[7]=0;f[8]=(b+a)/h;f[9]=(d+c)/i;f[10]=-(g+e)/j;f[11]=-1;f[12]=0;f[13]=0;f[14]=-(g*e*2)/j;f[15]=0;return f};mat4.perspective=function(a,b,c,d,e){a=c*Math.tan(a*Math.PI/360);b*=a;return mat4.frustum(-b,b,-a,a,c,d,e)}; mat4.ortho=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=2/i;f[6]=0;f[7]=0;f[8]=0;f[9]=0;f[10]=-2/j;f[11]=0;f[12]=-(a+b)/h;f[13]=-(d+c)/i;f[14]=-(g+e)/j;f[15]=1;return f}; mat4.lookAt=function(a,b,c,d){d||(d=mat4.create());var e,g,f,h,i,j,k,l,n=a[0],o=a[1],a=a[2];g=c[0];f=c[1];e=c[2];c=b[1];j=b[2];if(n===b[0]&&o===c&&a===j)return mat4.identity(d);c=n-b[0];j=o-b[1];k=a-b[2];l=1/Math.sqrt(c*c+j*j+k*k);c*=l;j*=l;k*=l;b=f*k-e*j;e=e*c-g*k;g=g*j-f*c;(l=Math.sqrt(b*b+e*e+g*g))?(l=1/l,b*=l,e*=l,g*=l):g=e=b=0;f=j*g-k*e;h=k*b-c*g;i=c*e-j*b;(l=Math.sqrt(f*f+h*h+i*i))?(l=1/l,f*=l,h*=l,i*=l):i=h=f=0;d[0]=b;d[1]=f;d[2]=c;d[3]=0;d[4]=e;d[5]=h;d[6]=j;d[7]=0;d[8]=g;d[9]=i;d[10]=k;d[11]= 0;d[12]=-(b*n+e*o+g*a);d[13]=-(f*n+h*o+i*a);d[14]=-(c*n+j*o+k*a);d[15]=1;return d};mat4.fromRotationTranslation=function(a,b,c){c||(c=mat4.create());var d=a[0],e=a[1],g=a[2],f=a[3],h=d+d,i=e+e,j=g+g,a=d*h,k=d*i;d*=j;var l=e*i;e*=j;g*=j;h*=f;i*=f;f*=j;c[0]=1-(l+g);c[1]=k+f;c[2]=d-i;c[3]=0;c[4]=k-f;c[5]=1-(a+g);c[6]=e+h;c[7]=0;c[8]=d+i;c[9]=e-h;c[10]=1-(a+l);c[11]=0;c[12]=b[0];c[13]=b[1];c[14]=b[2];c[15]=1;return c}; mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"};quat4.create=function(a){var b=new MatrixArray(4);a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3]);return b};quat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];return b}; quat4.calculateW=function(a,b){var c=a[0],d=a[1],e=a[2];if(!b||a===b)return a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e)),a;b[0]=c;b[1]=d;b[2]=e;b[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return b};quat4.inverse=function(a,b){if(!b||a===b)return a[0]*=-1,a[1]*=-1,a[2]*=-1,a;b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];b[3]=a[3];return b};quat4.length=function(a){var b=a[0],c=a[1],d=a[2],a=a[3];return Math.sqrt(b*b+c*c+d*d+a*a)}; quat4.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=Math.sqrt(c*c+d*d+e*e+g*g);if(f===0)return b[0]=0,b[1]=0,b[2]=0,b[3]=0,b;f=1/f;b[0]=c*f;b[1]=d*f;b[2]=e*f;b[3]=g*f;return b};quat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2],a=a[3],f=b[0],h=b[1],i=b[2],b=b[3];c[0]=d*b+a*f+e*i-g*h;c[1]=e*b+a*h+g*f-d*i;c[2]=g*b+a*i+d*h-e*f;c[3]=a*b-d*f-e*h-g*i;return c}; quat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2],b=a[0],f=a[1],h=a[2],a=a[3],i=a*d+f*g-h*e,j=a*e+h*d-b*g,k=a*g+b*e-f*d,d=-b*d-f*e-h*g;c[0]=i*a+d*-b+j*-h-k*-f;c[1]=j*a+d*-f+k*-b-i*-h;c[2]=k*a+d*-h+i*-f-j*-b;return c};quat4.toMat3=function(a,b){b||(b=mat3.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c*=i;var l=d*h;d*=i;e*=i;f*=g;h*=g;g*=i;b[0]=1-(l+e);b[1]=k+g;b[2]=c-h;b[3]=k-g;b[4]=1-(j+e);b[5]=d+f;b[6]=c+h;b[7]=d-f;b[8]=1-(j+l);return b}; quat4.toMat4=function(a,b){b||(b=mat4.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c*=i;var l=d*h;d*=i;e*=i;f*=g;h*=g;g*=i;b[0]=1-(l+e);b[1]=k+g;b[2]=c-h;b[3]=0;b[4]=k-g;b[5]=1-(j+e);b[6]=d+f;b[7]=0;b[8]=c+h;b[9]=d-f;b[10]=1-(j+l);b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; quat4.slerp=function(a,b,c,d){d||(d=a);var e=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3],g,f;if(Math.abs(e)>=1)return d!==a&&(d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=a[3]),d;g=Math.acos(e);f=Math.sqrt(1-e*e);if(Math.abs(f)<0.001)return d[0]=a[0]*0.5+b[0]*0.5,d[1]=a[1]*0.5+b[1]*0.5,d[2]=a[2]*0.5+b[2]*0.5,d[3]=a[3]*0.5+b[3]*0.5,d;e=Math.sin((1-c)*g)/f;c=Math.sin(c*g)/f;d[0]=a[0]*e+b[0]*c;d[1]=a[1]*e+b[1]*c;d[2]=a[2]*e+b[2]*c;d[3]=a[3]*e+b[3]*c;return d}; quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"}; (function() { var MAX_VERTICES = 8000; // equates to 2500 objects being drawn var MAX_INDICES = (MAX_VERTICES / 2) * 3; // 6 indices for every 4 vertices var MAX_POINTS = 8000; var MULTI_BUFFERS = 4; // cycle 4 buffers to try and avoid blocking var BATCH_NULL = 0; var BATCH_QUAD = 1; var BATCH_SETTEXTURE = 2; var BATCH_SETOPACITY = 3; var BATCH_SETBLEND = 4; var BATCH_UPDATEMODELVIEW = 5; var BATCH_RENDERTOTEXTURE = 6; var BATCH_CLEAR = 7; var BATCH_POINTS = 8; var BATCH_SETPROGRAM = 9; var BATCH_SETPROGRAMPARAMETERS = 10; var BATCH_SETTEXTURE1 = 11; var BATCH_SETCOLOR = 12; var BATCH_SETDEPTHTEST = 13; var BATCH_SETEARLYZMODE = 14; /* var lose_ext = null; window.lose_context = function () { if (!lose_ext) { console.log("WEBGL_lose_context not supported"); return; } lose_ext.loseContext(); }; window.restore_context = function () { if (!lose_ext) { console.log("WEBGL_lose_context not supported"); return; } lose_ext.restoreContext(); }; */ var tempMat4 = mat4.create(); function GLWrap_(gl, isMobile, enableFrontToBack) { this.isIE = /msie/i.test(navigator.userAgent) || /trident/i.test(navigator.userAgent); this.width = 0; // not yet known, wait for call to setSize() this.height = 0; this.enableFrontToBack = !!enableFrontToBack; this.isEarlyZPass = false; this.isBatchInEarlyZPass = false; this.currentZ = 0; this.zNear = 1; this.zFar = 1000; this.zIncrement = ((this.zFar - this.zNear) / 32768); this.zA = this.zFar / (this.zFar - this.zNear); this.zB = this.zFar * this.zNear / (this.zNear - this.zFar); this.kzA = 65536 * this.zA; this.kzB = 65536 * this.zB; this.cam = vec3.create([0, 0, 100]); // camera position this.look = vec3.create([0, 0, 0]); // lookat position this.up = vec3.create([0, 1, 0]); // up vector this.worldScale = vec3.create([1, 1, 1]); // world scaling factor this.enable_mipmaps = true; this.matP = mat4.create(); // perspective matrix this.matMV = mat4.create(); // model view matrix this.lastMV = mat4.create(); this.currentMV = mat4.create(); this.gl = gl; this.version = (this.gl.getParameter(this.gl.VERSION).indexOf("WebGL 2") === 0 ? 2 : 1); this.initState(); }; GLWrap_.prototype.initState = function () { var gl = this.gl; var i, len; this.lastOpacity = 1; this.lastTexture0 = null; // last bound to TEXTURE0 this.lastTexture1 = null; // last bound to TEXTURE1 this.currentOpacity = 1; gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.disable(gl.CULL_FACE); gl.disable(gl.STENCIL_TEST); gl.disable(gl.DITHER); if (this.enableFrontToBack) { gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); } else { gl.disable(gl.DEPTH_TEST); } this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); this.lastSrcBlend = gl.ONE; this.lastDestBlend = gl.ONE_MINUS_SRC_ALPHA; this.vertexData = new Float32Array(MAX_VERTICES * (this.enableFrontToBack ? 3 : 2)); this.texcoordData = new Float32Array(MAX_VERTICES * 2); this.pointData = new Float32Array(MAX_POINTS * 4); this.pointBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer); gl.bufferData(gl.ARRAY_BUFFER, this.pointData.byteLength, gl.DYNAMIC_DRAW); this.vertexBuffers = new Array(MULTI_BUFFERS); this.texcoordBuffers = new Array(MULTI_BUFFERS); for (i = 0; i < MULTI_BUFFERS; i++) { this.vertexBuffers[i] = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffers[i]); gl.bufferData(gl.ARRAY_BUFFER, this.vertexData.byteLength, gl.DYNAMIC_DRAW); this.texcoordBuffers[i] = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordBuffers[i]); gl.bufferData(gl.ARRAY_BUFFER, this.texcoordData.byteLength, gl.DYNAMIC_DRAW); } this.curBuffer = 0; this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); var indexData = new Uint16Array(MAX_INDICES); i = 0, len = MAX_INDICES; var fv = 0; while (i < len) { indexData[i++] = fv; // top left indexData[i++] = fv + 1; // top right indexData[i++] = fv + 2; // bottom right (first tri) indexData[i++] = fv; // top left indexData[i++] = fv + 2; // bottom right indexData[i++] = fv + 3; // bottom left fv += 4; } gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); this.vertexPtr = 0; this.texPtr = 0; this.pointPtr = 0; var fsSource, vsSource; this.shaderPrograms = []; fsSource = [ "varying mediump vec2 vTex;", "uniform lowp float opacity;", "uniform lowp sampler2D samplerFront;", "void main(void) {", " gl_FragColor = texture2D(samplerFront, vTex);", " gl_FragColor *= opacity;", "}" ].join("\n"); if (this.enableFrontToBack) { vsSource = [ "attribute highp vec3 aPos;", "attribute mediump vec2 aTex;", "varying mediump vec2 vTex;", "uniform highp mat4 matP;", "uniform highp mat4 matMV;", "void main(void) {", " gl_Position = matP * matMV * vec4(aPos.x, aPos.y, aPos.z, 1.0);", " vTex = aTex;", "}" ].join("\n"); } else { vsSource = [ "attribute highp vec2 aPos;", "attribute mediump vec2 aTex;", "varying mediump vec2 vTex;", "uniform highp mat4 matP;", "uniform highp mat4 matMV;", "void main(void) {", " gl_Position = matP * matMV * vec4(aPos.x, aPos.y, 0.0, 1.0);", " vTex = aTex;", "}" ].join("\n"); } var shaderProg = this.createShaderProgram({src: fsSource}, vsSource, ""); ; this.shaderPrograms.push(shaderProg); // Default shader is always shader 0 fsSource = [ "uniform mediump sampler2D samplerFront;", "varying lowp float opacity;", "void main(void) {", " gl_FragColor = texture2D(samplerFront, gl_PointCoord);", " gl_FragColor *= opacity;", "}" ].join("\n"); var pointVsSource = [ "attribute vec4 aPos;", "varying float opacity;", "uniform mat4 matP;", "uniform mat4 matMV;", "void main(void) {", " gl_Position = matP * matMV * vec4(aPos.x, aPos.y, 0.0, 1.0);", " gl_PointSize = aPos.z;", " opacity = aPos.w;", "}" ].join("\n"); shaderProg = this.createShaderProgram({src: fsSource}, pointVsSource, ""); ; this.shaderPrograms.push(shaderProg); // Point shader is always shader 1 fsSource = [ "varying mediump vec2 vTex;", "uniform lowp sampler2D samplerFront;", "void main(void) {", " if (texture2D(samplerFront, vTex).a < 1.0)", " discard;", // discarding non-opaque fragments "}" ].join("\n"); var shaderProg = this.createShaderProgram({src: fsSource}, vsSource, ""); ; this.shaderPrograms.push(shaderProg); // Early-Z shader is always shader 2 fsSource = [ "uniform lowp vec4 colorFill;", "void main(void) {", " gl_FragColor = colorFill;", "}" ].join("\n"); var shaderProg = this.createShaderProgram({src: fsSource}, vsSource, ""); ; this.shaderPrograms.push(shaderProg); // Fill-color shader is always shader 3 for (var shader_name in cr.shaders) { if (cr.shaders.hasOwnProperty(shader_name)) this.shaderPrograms.push(this.createShaderProgram(cr.shaders[shader_name], vsSource, shader_name)); } gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, null); this.batch = []; this.batchPtr = 0; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; this.lastProgram = -1; // start -1 so first switchProgram can do work this.currentProgram = -1; // current program during batch execution this.currentShader = null; this.fbo = gl.createFramebuffer(); this.renderToTex = null; this.depthBuffer = null; this.attachedDepthBuffer = false; // wait until first size call to attach, otherwise it has no storage if (this.enableFrontToBack) { this.depthBuffer = gl.createRenderbuffer(); } this.tmpVec3 = vec3.create([0, 0, 0]); ; var pointsizes = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE); this.minPointSize = pointsizes[0]; this.maxPointSize = pointsizes[1]; if (this.maxPointSize > 2048) this.maxPointSize = 2048; ; this.switchProgram(0); cr.seal(this); }; function GLShaderProgram(gl, shaderProgram, name) { this.gl = gl; this.shaderProgram = shaderProgram; this.name = name; this.locAPos = gl.getAttribLocation(shaderProgram, "aPos"); this.locATex = gl.getAttribLocation(shaderProgram, "aTex"); this.locMatP = gl.getUniformLocation(shaderProgram, "matP"); this.locMatMV = gl.getUniformLocation(shaderProgram, "matMV"); this.locOpacity = gl.getUniformLocation(shaderProgram, "opacity"); this.locColorFill = gl.getUniformLocation(shaderProgram, "colorFill"); this.locSamplerFront = gl.getUniformLocation(shaderProgram, "samplerFront"); this.locSamplerBack = gl.getUniformLocation(shaderProgram, "samplerBack"); this.locDestStart = gl.getUniformLocation(shaderProgram, "destStart"); this.locDestEnd = gl.getUniformLocation(shaderProgram, "destEnd"); this.locSeconds = gl.getUniformLocation(shaderProgram, "seconds"); this.locPixelWidth = gl.getUniformLocation(shaderProgram, "pixelWidth"); this.locPixelHeight = gl.getUniformLocation(shaderProgram, "pixelHeight"); this.locLayerScale = gl.getUniformLocation(shaderProgram, "layerScale"); this.locLayerAngle = gl.getUniformLocation(shaderProgram, "layerAngle"); this.locViewOrigin = gl.getUniformLocation(shaderProgram, "viewOrigin"); this.locScrollPos = gl.getUniformLocation(shaderProgram, "scrollPos"); this.hasAnyOptionalUniforms = !!(this.locPixelWidth || this.locPixelHeight || this.locSeconds || this.locSamplerBack || this.locDestStart || this.locDestEnd || this.locLayerScale || this.locLayerAngle || this.locViewOrigin || this.locScrollPos); this.lpPixelWidth = -999; // set to something unlikely so never counts as cached on first set this.lpPixelHeight = -999; this.lpOpacity = 1; this.lpDestStartX = 0.0; this.lpDestStartY = 0.0; this.lpDestEndX = 1.0; this.lpDestEndY = 1.0; this.lpLayerScale = 1.0; this.lpLayerAngle = 0.0; this.lpViewOriginX = 0.0; this.lpViewOriginY = 0.0; this.lpScrollPosX = 0.0; this.lpScrollPosY = 0.0; this.lpSeconds = 0.0; this.lastCustomParams = []; this.lpMatMV = mat4.create(); if (this.locOpacity) gl.uniform1f(this.locOpacity, 1); if (this.locColorFill) gl.uniform4f(this.locColorFill, 1.0, 1.0, 1.0, 1.0); if (this.locSamplerFront) gl.uniform1i(this.locSamplerFront, 0); if (this.locSamplerBack) gl.uniform1i(this.locSamplerBack, 1); if (this.locDestStart) gl.uniform2f(this.locDestStart, 0.0, 0.0); if (this.locDestEnd) gl.uniform2f(this.locDestEnd, 1.0, 1.0); if (this.locLayerScale) gl.uniform1f(this.locLayerScale, 1.0); if (this.locLayerAngle) gl.uniform1f(this.locLayerAngle, 0.0); if (this.locViewOrigin) gl.uniform2f(this.locViewOrigin, 0.0, 0.0); if (this.locScrollPos) gl.uniform2f(this.locScrollPos, 0.0, 0.0); if (this.locSeconds) gl.uniform1f(this.locSeconds, 0.0); this.hasCurrentMatMV = false; // matMV needs updating }; function areMat4sEqual(a, b) { return a[0]===b[0]&&a[1]===b[1]&&a[2]===b[2]&&a[3]===b[3]&& a[4]===b[4]&&a[5]===b[5]&&a[6]===b[6]&&a[7]===b[7]&& a[8]===b[8]&&a[9]===b[9]&&a[10]===b[10]&&a[11]===b[11]&& a[12]===b[12]&&a[13]===b[13]&&a[14]===b[14]&&a[15]===b[15]; }; GLShaderProgram.prototype.updateMatMV = function (mv) { if (areMat4sEqual(this.lpMatMV, mv)) return; // no change, save the expensive GL call mat4.set(mv, this.lpMatMV); this.gl.uniformMatrix4fv(this.locMatMV, false, mv); }; GLWrap_.prototype.createShaderProgram = function(shaderEntry, vsSource, name) { var gl = this.gl; var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, shaderEntry.src); gl.compileShader(fragmentShader); if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { ; gl.deleteShader(fragmentShader); return null; } var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vsSource); gl.compileShader(vertexShader); if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { ; gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); return null; } var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, fragmentShader); gl.attachShader(shaderProgram, vertexShader); gl.linkProgram(shaderProgram); if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { ; gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); gl.deleteProgram(shaderProgram); return null; } gl.useProgram(shaderProgram); gl.deleteShader(fragmentShader); gl.deleteShader(vertexShader); var ret = new GLShaderProgram(gl, shaderProgram, name); ret.extendBoxHorizontal = shaderEntry.extendBoxHorizontal || 0; ret.extendBoxVertical = shaderEntry.extendBoxVertical || 0; ret.crossSampling = !!shaderEntry.crossSampling; ret.preservesOpaqueness = !!shaderEntry.preservesOpaqueness; ret.animated = !!shaderEntry.animated; ret.parameters = shaderEntry.parameters || []; var i, len; for (i = 0, len = ret.parameters.length; i < len; i++) { ret.parameters[i][1] = gl.getUniformLocation(shaderProgram, ret.parameters[i][0]); ret.lastCustomParams.push(0); gl.uniform1f(ret.parameters[i][1], 0); } cr.seal(ret); return ret; }; GLWrap_.prototype.getShaderIndex = function(name_) { var i, len; for (i = 0, len = this.shaderPrograms.length; i < len; i++) { if (this.shaderPrograms[i].name === name_) return i; } return -1; }; GLWrap_.prototype.project = function (x, y, out) { var mv = this.matMV; var proj = this.matP; var fTempo = [0, 0, 0, 0, 0, 0, 0, 0]; fTempo[0] = mv[0]*x+mv[4]*y+mv[12]; fTempo[1] = mv[1]*x+mv[5]*y+mv[13]; fTempo[2] = mv[2]*x+mv[6]*y+mv[14]; fTempo[3] = mv[3]*x+mv[7]*y+mv[15]; fTempo[4] = proj[0]*fTempo[0]+proj[4]*fTempo[1]+proj[8]*fTempo[2]+proj[12]*fTempo[3]; fTempo[5] = proj[1]*fTempo[0]+proj[5]*fTempo[1]+proj[9]*fTempo[2]+proj[13]*fTempo[3]; fTempo[6] = proj[2]*fTempo[0]+proj[6]*fTempo[1]+proj[10]*fTempo[2]+proj[14]*fTempo[3]; fTempo[7] = -fTempo[2]; if(fTempo[7]===0.0) //The w value return; fTempo[7]=1.0/fTempo[7]; fTempo[4]*=fTempo[7]; fTempo[5]*=fTempo[7]; fTempo[6]*=fTempo[7]; out[0]=(fTempo[4]*0.5+0.5)*this.width; out[1]=(fTempo[5]*0.5+0.5)*this.height; }; GLWrap_.prototype.setSize = function(w, h, force) { if (this.width === w && this.height === h && !force) return; this.endBatch(); var gl = this.gl; this.width = w; this.height = h; gl.viewport(0, 0, w, h); mat4.lookAt(this.cam, this.look, this.up, this.matMV); if (this.enableFrontToBack) { mat4.ortho(-w/2, w/2, h/2, -h/2, this.zNear, this.zFar, this.matP); this.worldScale[0] = 1; this.worldScale[1] = 1; } else { mat4.perspective(45, w / h, this.zNear, this.zFar, this.matP); var tl = [0, 0]; var br = [0, 0]; this.project(0, 0, tl); this.project(1, 1, br); this.worldScale[0] = 1 / (br[0] - tl[0]); this.worldScale[1] = -1 / (br[1] - tl[1]); } var i, len, s; for (i = 0, len = this.shaderPrograms.length; i < len; i++) { s = this.shaderPrograms[i]; s.hasCurrentMatMV = false; if (s.locMatP) { gl.useProgram(s.shaderProgram); gl.uniformMatrix4fv(s.locMatP, false, this.matP); } } gl.useProgram(this.shaderPrograms[this.lastProgram].shaderProgram); gl.bindTexture(gl.TEXTURE_2D, null); gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, null); gl.activeTexture(gl.TEXTURE0); this.lastTexture0 = null; this.lastTexture1 = null; if (this.depthBuffer) { gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); gl.bindRenderbuffer(gl.RENDERBUFFER, this.depthBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); if (!this.attachedDepthBuffer) { gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, this.depthBuffer); this.attachedDepthBuffer = true; } gl.bindRenderbuffer(gl.RENDERBUFFER, null); gl.bindFramebuffer(gl.FRAMEBUFFER, null); this.renderToTex = null; } }; GLWrap_.prototype.resetModelView = function () { mat4.lookAt(this.cam, this.look, this.up, this.matMV); mat4.scale(this.matMV, this.worldScale); }; GLWrap_.prototype.translate = function (x, y) { if (x === 0 && y === 0) return; this.tmpVec3[0] = x;// * this.worldScale[0]; this.tmpVec3[1] = y;// * this.worldScale[1]; this.tmpVec3[2] = 0; mat4.translate(this.matMV, this.tmpVec3); }; GLWrap_.prototype.scale = function (x, y) { if (x === 1 && y === 1) return; this.tmpVec3[0] = x; this.tmpVec3[1] = y; this.tmpVec3[2] = 1; mat4.scale(this.matMV, this.tmpVec3); }; GLWrap_.prototype.rotateZ = function (a) { if (a === 0) return; mat4.rotateZ(this.matMV, a); }; GLWrap_.prototype.updateModelView = function() { if (areMat4sEqual(this.lastMV, this.matMV)) return; var b = this.pushBatch(); b.type = BATCH_UPDATEMODELVIEW; if (b.mat4param) mat4.set(this.matMV, b.mat4param); else b.mat4param = mat4.create(this.matMV); mat4.set(this.matMV, this.lastMV); this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; /* var debugBatch = false; jQuery(document).mousedown( function(info) { if (info.which === 2) debugBatch = true; } ); */ GLWrap_.prototype.setEarlyZIndex = function (i) { if (!this.enableFrontToBack) return; if (i > 32760) i = 32760; this.currentZ = this.cam[2] - this.zNear - i * this.zIncrement; }; function GLBatchJob(type_, glwrap_) { this.type = type_; this.glwrap = glwrap_; this.gl = glwrap_.gl; this.opacityParam = 0; // for setOpacity() this.startIndex = 0; // for quad() this.indexCount = 0; // " this.texParam = null; // for setTexture() this.mat4param = null; // for updateModelView() this.shaderParams = []; // for user parameters cr.seal(this); }; GLBatchJob.prototype.doSetEarlyZPass = function () { var gl = this.gl; var glwrap = this.glwrap; if (this.startIndex !== 0) // enable { gl.depthMask(true); // enable depth writes gl.colorMask(false, false, false, false); // disable color writes gl.disable(gl.BLEND); // no color writes so disable blend gl.bindFramebuffer(gl.FRAMEBUFFER, glwrap.fbo); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); gl.clear(gl.DEPTH_BUFFER_BIT); // auto-clear depth buffer gl.bindFramebuffer(gl.FRAMEBUFFER, null); glwrap.isBatchInEarlyZPass = true; } else { gl.depthMask(false); // disable depth writes, only test existing depth values gl.colorMask(true, true, true, true); // enable color writes gl.enable(gl.BLEND); // turn blending back on glwrap.isBatchInEarlyZPass = false; } }; GLBatchJob.prototype.doSetTexture = function () { this.gl.bindTexture(this.gl.TEXTURE_2D, this.texParam); }; GLBatchJob.prototype.doSetTexture1 = function () { var gl = this.gl; gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, this.texParam); gl.activeTexture(gl.TEXTURE0); }; GLBatchJob.prototype.doSetOpacity = function () { var o = this.opacityParam; var glwrap = this.glwrap; glwrap.currentOpacity = o; var curProg = glwrap.currentShader; if (curProg.locOpacity && curProg.lpOpacity !== o) { curProg.lpOpacity = o; this.gl.uniform1f(curProg.locOpacity, o); } }; GLBatchJob.prototype.doQuad = function () { this.gl.drawElements(this.gl.TRIANGLES, this.indexCount, this.gl.UNSIGNED_SHORT, this.startIndex); }; GLBatchJob.prototype.doSetBlend = function () { this.gl.blendFunc(this.startIndex, this.indexCount); }; GLBatchJob.prototype.doUpdateModelView = function () { var i, len, s, shaderPrograms = this.glwrap.shaderPrograms, currentProgram = this.glwrap.currentProgram; for (i = 0, len = shaderPrograms.length; i < len; i++) { s = shaderPrograms[i]; if (i === currentProgram && s.locMatMV) { s.updateMatMV(this.mat4param); s.hasCurrentMatMV = true; } else s.hasCurrentMatMV = false; } mat4.set(this.mat4param, this.glwrap.currentMV); }; GLBatchJob.prototype.doRenderToTexture = function () { var gl = this.gl; var glwrap = this.glwrap; if (this.texParam) { if (glwrap.lastTexture1 === this.texParam) { gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, null); glwrap.lastTexture1 = null; gl.activeTexture(gl.TEXTURE0); } gl.bindFramebuffer(gl.FRAMEBUFFER, glwrap.fbo); if (!glwrap.isBatchInEarlyZPass) { gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texParam, 0); } } else { if (!glwrap.enableFrontToBack) { gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); } gl.bindFramebuffer(gl.FRAMEBUFFER, null); } }; GLBatchJob.prototype.doClear = function () { var gl = this.gl; var mode = this.startIndex; if (mode === 0) // clear whole surface { gl.clearColor(this.mat4param[0], this.mat4param[1], this.mat4param[2], this.mat4param[3]); gl.clear(gl.COLOR_BUFFER_BIT); } else if (mode === 1) // clear rectangle { gl.enable(gl.SCISSOR_TEST); gl.scissor(this.mat4param[0], this.mat4param[1], this.mat4param[2], this.mat4param[3]); gl.clearColor(0, 0, 0, 0); gl.clear(gl.COLOR_BUFFER_BIT); gl.disable(gl.SCISSOR_TEST); } else // clear depth { gl.clear(gl.DEPTH_BUFFER_BIT); } }; GLBatchJob.prototype.doSetDepthTestEnabled = function () { var gl = this.gl; var enable = this.startIndex; if (enable !== 0) { gl.enable(gl.DEPTH_TEST); } else { gl.disable(gl.DEPTH_TEST); } }; GLBatchJob.prototype.doPoints = function () { var gl = this.gl; var glwrap = this.glwrap; if (glwrap.enableFrontToBack) gl.disable(gl.DEPTH_TEST); var s = glwrap.shaderPrograms[1]; gl.useProgram(s.shaderProgram); if (!s.hasCurrentMatMV && s.locMatMV) { s.updateMatMV(glwrap.currentMV); s.hasCurrentMatMV = true; } gl.enableVertexAttribArray(s.locAPos); gl.bindBuffer(gl.ARRAY_BUFFER, glwrap.pointBuffer); gl.vertexAttribPointer(s.locAPos, 4, gl.FLOAT, false, 0, 0); gl.drawArrays(gl.POINTS, this.startIndex / 4, this.indexCount); s = glwrap.currentShader; gl.useProgram(s.shaderProgram); if (s.locAPos >= 0) { gl.enableVertexAttribArray(s.locAPos); gl.bindBuffer(gl.ARRAY_BUFFER, glwrap.vertexBuffers[glwrap.curBuffer]); gl.vertexAttribPointer(s.locAPos, glwrap.enableFrontToBack ? 3 : 2, gl.FLOAT, false, 0, 0); } if (s.locATex >= 0) { gl.enableVertexAttribArray(s.locATex); gl.bindBuffer(gl.ARRAY_BUFFER, glwrap.texcoordBuffers[glwrap.curBuffer]); gl.vertexAttribPointer(s.locATex, 2, gl.FLOAT, false, 0, 0); } if (glwrap.enableFrontToBack) gl.enable(gl.DEPTH_TEST); }; GLBatchJob.prototype.doSetProgram = function () { var gl = this.gl; var glwrap = this.glwrap; var s = glwrap.shaderPrograms[this.startIndex]; // recycled param to save memory glwrap.currentProgram = this.startIndex; // current batch program glwrap.currentShader = s; gl.useProgram(s.shaderProgram); // switch to if (!s.hasCurrentMatMV && s.locMatMV) { s.updateMatMV(glwrap.currentMV); s.hasCurrentMatMV = true; } if (s.locOpacity && s.lpOpacity !== glwrap.currentOpacity) { s.lpOpacity = glwrap.currentOpacity; gl.uniform1f(s.locOpacity, glwrap.currentOpacity); } if (s.locAPos >= 0) { gl.enableVertexAttribArray(s.locAPos); gl.bindBuffer(gl.ARRAY_BUFFER, glwrap.vertexBuffers[glwrap.curBuffer]); gl.vertexAttribPointer(s.locAPos, glwrap.enableFrontToBack ? 3 : 2, gl.FLOAT, false, 0, 0); } if (s.locATex >= 0) { gl.enableVertexAttribArray(s.locATex); gl.bindBuffer(gl.ARRAY_BUFFER, glwrap.texcoordBuffers[glwrap.curBuffer]); gl.vertexAttribPointer(s.locATex, 2, gl.FLOAT, false, 0, 0); } } GLBatchJob.prototype.doSetColor = function () { var s = this.glwrap.currentShader; var mat4param = this.mat4param; this.gl.uniform4f(s.locColorFill, mat4param[0], mat4param[1], mat4param[2], mat4param[3]); }; GLBatchJob.prototype.doSetProgramParameters = function () { var i, len, s = this.glwrap.currentShader; var gl = this.gl; var mat4param = this.mat4param; if (s.locSamplerBack && this.glwrap.lastTexture1 !== this.texParam) { gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, this.texParam); this.glwrap.lastTexture1 = this.texParam; gl.activeTexture(gl.TEXTURE0); } var v = mat4param[0]; var v2; if (s.locPixelWidth && v !== s.lpPixelWidth) { s.lpPixelWidth = v; gl.uniform1f(s.locPixelWidth, v); } v = mat4param[1]; if (s.locPixelHeight && v !== s.lpPixelHeight) { s.lpPixelHeight = v; gl.uniform1f(s.locPixelHeight, v); } v = mat4param[2]; v2 = mat4param[3]; if (s.locDestStart && (v !== s.lpDestStartX || v2 !== s.lpDestStartY)) { s.lpDestStartX = v; s.lpDestStartY = v2; gl.uniform2f(s.locDestStart, v, v2); } v = mat4param[4]; v2 = mat4param[5]; if (s.locDestEnd && (v !== s.lpDestEndX || v2 !== s.lpDestEndY)) { s.lpDestEndX = v; s.lpDestEndY = v2; gl.uniform2f(s.locDestEnd, v, v2); } v = mat4param[6]; if (s.locLayerScale && v !== s.lpLayerScale) { s.lpLayerScale = v; gl.uniform1f(s.locLayerScale, v); } v = mat4param[7]; if (s.locLayerAngle && v !== s.lpLayerAngle) { s.lpLayerAngle = v; gl.uniform1f(s.locLayerAngle, v); } v = mat4param[8]; v2 = mat4param[9]; if (s.locViewOrigin && (v !== s.lpViewOriginX || v2 !== s.lpViewOriginY)) { s.lpViewOriginX = v; s.lpViewOriginY = v2; gl.uniform2f(s.locViewOrigin, v, v2); } v = mat4param[10]; v2 = mat4param[11]; if (s.locScrollPos && (v !== s.lpScrollPosX || v2 !== s.lpScrollPosY)) { s.lpScrollPosX = v; s.lpScrollPosY = v2; gl.uniform2f(s.locScrollPos, v, v2); } v = mat4param[12]; if (s.locSeconds && v !== s.lpSeconds) { s.lpSeconds = v; gl.uniform1f(s.locSeconds, v); } if (s.parameters.length) { for (i = 0, len = s.parameters.length; i < len; i++) { v = this.shaderParams[i]; if (v !== s.lastCustomParams[i]) { s.lastCustomParams[i] = v; gl.uniform1f(s.parameters[i][1], v); } } } }; GLWrap_.prototype.pushBatch = function () { if (this.batchPtr === this.batch.length) this.batch.push(new GLBatchJob(BATCH_NULL, this)); return this.batch[this.batchPtr++]; }; GLWrap_.prototype.endBatch = function () { if (this.batchPtr === 0) return; if (this.gl.isContextLost()) return; var gl = this.gl; if (this.pointPtr > 0) { gl.bindBuffer(gl.ARRAY_BUFFER, this.pointBuffer); gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.pointData.subarray(0, this.pointPtr)); if (s && s.locAPos >= 0 && s.name === "") gl.vertexAttribPointer(s.locAPos, 4, gl.FLOAT, false, 0, 0); } if (this.vertexPtr > 0) { var s = this.currentShader; gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffers[this.curBuffer]); gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexData.subarray(0, this.vertexPtr)); if (s && s.locAPos >= 0 && s.name !== "") gl.vertexAttribPointer(s.locAPos, this.enableFrontToBack ? 3 : 2, gl.FLOAT, false, 0, 0); gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordBuffers[this.curBuffer]); gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.texcoordData.subarray(0, this.texPtr)); if (s && s.locATex >= 0 && s.name !== "") gl.vertexAttribPointer(s.locATex, 2, gl.FLOAT, false, 0, 0); } var i, len, b; for (i = 0, len = this.batchPtr; i < len; i++) { b = this.batch[i]; switch (b.type) { case 1: b.doQuad(); break; case 2: b.doSetTexture(); break; case 3: b.doSetOpacity(); break; case 4: b.doSetBlend(); break; case 5: b.doUpdateModelView(); break; case 6: b.doRenderToTexture(); break; case 7: b.doClear(); break; case 8: b.doPoints(); break; case 9: b.doSetProgram(); break; case 10: b.doSetProgramParameters(); break; case 11: b.doSetTexture1(); break; case 12: b.doSetColor(); break; case 13: b.doSetDepthTestEnabled(); break; case 14: b.doSetEarlyZPass(); break; } } this.batchPtr = 0; this.vertexPtr = 0; this.texPtr = 0; this.pointPtr = 0; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; this.isBatchInEarlyZPass = false; this.curBuffer++; if (this.curBuffer >= MULTI_BUFFERS) this.curBuffer = 0; }; GLWrap_.prototype.setOpacity = function (op) { if (op === this.lastOpacity) return; if (this.isEarlyZPass) return; // ignore var b = this.pushBatch(); b.type = BATCH_SETOPACITY; b.opacityParam = op; this.lastOpacity = op; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.setTexture = function (tex) { if (tex === this.lastTexture0) return; ; var b = this.pushBatch(); b.type = BATCH_SETTEXTURE; b.texParam = tex; this.lastTexture0 = tex; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.setBlend = function (s, d) { if (s === this.lastSrcBlend && d === this.lastDestBlend) return; if (this.isEarlyZPass) return; // ignore var b = this.pushBatch(); b.type = BATCH_SETBLEND; b.startIndex = s; // recycle params to save memory b.indexCount = d; this.lastSrcBlend = s; this.lastDestBlend = d; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.isPremultipliedAlphaBlend = function () { return (this.lastSrcBlend === this.gl.ONE && this.lastDestBlend === this.gl.ONE_MINUS_SRC_ALPHA); }; GLWrap_.prototype.setAlphaBlend = function () { this.setBlend(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); }; GLWrap_.prototype.setNoPremultiplyAlphaBlend = function () { this.setBlend(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); }; var LAST_VERTEX = MAX_VERTICES * 2 - 8; GLWrap_.prototype.quad = function(tlx, tly, trx, try_, brx, bry, blx, bly) { if (this.vertexPtr >= LAST_VERTEX) this.endBatch(); var v = this.vertexPtr; // vertex cursor var t = this.texPtr; var vd = this.vertexData; // vertex data array var td = this.texcoordData; // texture coord data array var currentZ = this.currentZ; if (this.hasQuadBatchTop) { this.batch[this.batchPtr - 1].indexCount += 6; } else { var b = this.pushBatch(); b.type = BATCH_QUAD; b.startIndex = this.enableFrontToBack ? v : (v / 2) * 3; b.indexCount = 6; this.hasQuadBatchTop = true; this.hasPointBatchTop = false; } if (this.enableFrontToBack) { vd[v++] = tlx; vd[v++] = tly; vd[v++] = currentZ; vd[v++] = trx; vd[v++] = try_; vd[v++] = currentZ; vd[v++] = brx; vd[v++] = bry; vd[v++] = currentZ; vd[v++] = blx; vd[v++] = bly; vd[v++] = currentZ; } else { vd[v++] = tlx; vd[v++] = tly; vd[v++] = trx; vd[v++] = try_; vd[v++] = brx; vd[v++] = bry; vd[v++] = blx; vd[v++] = bly; } td[t++] = 0; td[t++] = 0; td[t++] = 1; td[t++] = 0; td[t++] = 1; td[t++] = 1; td[t++] = 0; td[t++] = 1; this.vertexPtr = v; this.texPtr = t; }; GLWrap_.prototype.quadTex = function(tlx, tly, trx, try_, brx, bry, blx, bly, rcTex) { if (this.vertexPtr >= LAST_VERTEX) this.endBatch(); var v = this.vertexPtr; // vertex cursor var t = this.texPtr; var vd = this.vertexData; // vertex data array var td = this.texcoordData; // texture coord data array var currentZ = this.currentZ; if (this.hasQuadBatchTop) { this.batch[this.batchPtr - 1].indexCount += 6; } else { var b = this.pushBatch(); b.type = BATCH_QUAD; b.startIndex = this.enableFrontToBack ? v : (v / 2) * 3; b.indexCount = 6; this.hasQuadBatchTop = true; this.hasPointBatchTop = false; } var rc_left = rcTex.left; var rc_top = rcTex.top; var rc_right = rcTex.right; var rc_bottom = rcTex.bottom; if (this.enableFrontToBack) { vd[v++] = tlx; vd[v++] = tly; vd[v++] = currentZ; vd[v++] = trx; vd[v++] = try_; vd[v++] = currentZ; vd[v++] = brx; vd[v++] = bry; vd[v++] = currentZ; vd[v++] = blx; vd[v++] = bly; vd[v++] = currentZ; } else { vd[v++] = tlx; vd[v++] = tly; vd[v++] = trx; vd[v++] = try_; vd[v++] = brx; vd[v++] = bry; vd[v++] = blx; vd[v++] = bly; } td[t++] = rc_left; td[t++] = rc_top; td[t++] = rc_right; td[t++] = rc_top; td[t++] = rc_right; td[t++] = rc_bottom; td[t++] = rc_left; td[t++] = rc_bottom; this.vertexPtr = v; this.texPtr = t; }; GLWrap_.prototype.quadTexUV = function(tlx, tly, trx, try_, brx, bry, blx, bly, tlu, tlv, tru, trv, bru, brv, blu, blv) { if (this.vertexPtr >= LAST_VERTEX) this.endBatch(); var v = this.vertexPtr; // vertex cursor var t = this.texPtr; var vd = this.vertexData; // vertex data array var td = this.texcoordData; // texture coord data array var currentZ = this.currentZ; if (this.hasQuadBatchTop) { this.batch[this.batchPtr - 1].indexCount += 6; } else { var b = this.pushBatch(); b.type = BATCH_QUAD; b.startIndex = this.enableFrontToBack ? v : (v / 2) * 3; b.indexCount = 6; this.hasQuadBatchTop = true; this.hasPointBatchTop = false; } if (this.enableFrontToBack) { vd[v++] = tlx; vd[v++] = tly; vd[v++] = currentZ; vd[v++] = trx; vd[v++] = try_; vd[v++] = currentZ; vd[v++] = brx; vd[v++] = bry; vd[v++] = currentZ; vd[v++] = blx; vd[v++] = bly; vd[v++] = currentZ; } else { vd[v++] = tlx; vd[v++] = tly; vd[v++] = trx; vd[v++] = try_; vd[v++] = brx; vd[v++] = bry; vd[v++] = blx; vd[v++] = bly; } td[t++] = tlu; td[t++] = tlv; td[t++] = tru; td[t++] = trv; td[t++] = bru; td[t++] = brv; td[t++] = blu; td[t++] = blv; this.vertexPtr = v; this.texPtr = t; }; GLWrap_.prototype.convexPoly = function(pts) { var pts_count = pts.length / 2; ; var tris = pts_count - 2; // 3 points = 1 tri, 4 points = 2 tris, 5 points = 3 tris etc. var last_tri = tris - 1; var p0x = pts[0]; var p0y = pts[1]; var i, i2, p1x, p1y, p2x, p2y, p3x, p3y; for (i = 0; i < tris; i += 2) // draw 2 triangles at a time { i2 = i * 2; p1x = pts[i2 + 2]; p1y = pts[i2 + 3]; p2x = pts[i2 + 4]; p2y = pts[i2 + 5]; if (i === last_tri) { this.quad(p0x, p0y, p1x, p1y, p2x, p2y, p2x, p2y); } else { p3x = pts[i2 + 6]; p3y = pts[i2 + 7]; this.quad(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y); } } }; var LAST_POINT = MAX_POINTS - 4; GLWrap_.prototype.point = function(x_, y_, size_, opacity_) { if (this.pointPtr >= LAST_POINT) this.endBatch(); var p = this.pointPtr; // point cursor var pd = this.pointData; // point data array if (this.hasPointBatchTop) { this.batch[this.batchPtr - 1].indexCount++; } else { var b = this.pushBatch(); b.type = BATCH_POINTS; b.startIndex = p; b.indexCount = 1; this.hasPointBatchTop = true; this.hasQuadBatchTop = false; } pd[p++] = x_; pd[p++] = y_; pd[p++] = size_; pd[p++] = opacity_; this.pointPtr = p; }; GLWrap_.prototype.switchProgram = function (progIndex) { if (this.lastProgram === progIndex) return; // no change var shaderProg = this.shaderPrograms[progIndex]; if (!shaderProg) { if (this.lastProgram === 0) return; // already on default shader progIndex = 0; shaderProg = this.shaderPrograms[0]; } var b = this.pushBatch(); b.type = BATCH_SETPROGRAM; b.startIndex = progIndex; this.lastProgram = progIndex; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.programUsesDest = function (progIndex) { var s = this.shaderPrograms[progIndex]; return !!(s.locDestStart || s.locDestEnd); }; GLWrap_.prototype.programUsesCrossSampling = function (progIndex) { var s = this.shaderPrograms[progIndex]; return !!(s.locDestStart || s.locDestEnd || s.crossSampling); }; GLWrap_.prototype.programPreservesOpaqueness = function (progIndex) { return this.shaderPrograms[progIndex].preservesOpaqueness; }; GLWrap_.prototype.programExtendsBox = function (progIndex) { var s = this.shaderPrograms[progIndex]; return s.extendBoxHorizontal !== 0 || s.extendBoxVertical !== 0; }; GLWrap_.prototype.getProgramBoxExtendHorizontal = function (progIndex) { return this.shaderPrograms[progIndex].extendBoxHorizontal; }; GLWrap_.prototype.getProgramBoxExtendVertical = function (progIndex) { return this.shaderPrograms[progIndex].extendBoxVertical; }; GLWrap_.prototype.getProgramParameterType = function (progIndex, paramIndex) { return this.shaderPrograms[progIndex].parameters[paramIndex][2]; }; GLWrap_.prototype.programIsAnimated = function (progIndex) { return this.shaderPrograms[progIndex].animated; }; GLWrap_.prototype.setProgramParameters = function (backTex, pixelWidth, pixelHeight, destStartX, destStartY, destEndX, destEndY, layerScale, layerAngle, viewOriginLeft, viewOriginTop, scrollPosX, scrollPosY, seconds, params) { var i, len; var s = this.shaderPrograms[this.lastProgram]; var b, mat4param, shaderParams; if (s.hasAnyOptionalUniforms || params.length) { b = this.pushBatch(); b.type = BATCH_SETPROGRAMPARAMETERS; if (b.mat4param) mat4.set(this.matMV, b.mat4param); else b.mat4param = mat4.create(); mat4param = b.mat4param; mat4param[0] = pixelWidth; mat4param[1] = pixelHeight; mat4param[2] = destStartX; mat4param[3] = destStartY; mat4param[4] = destEndX; mat4param[5] = destEndY; mat4param[6] = layerScale; mat4param[7] = layerAngle; mat4param[8] = viewOriginLeft; mat4param[9] = viewOriginTop; mat4param[10] = scrollPosX; mat4param[11] = scrollPosY; mat4param[12] = seconds; if (s.locSamplerBack) { ; b.texParam = backTex; } else b.texParam = null; if (params.length) { shaderParams = b.shaderParams; shaderParams.length = params.length; for (i = 0, len = params.length; i < len; i++) shaderParams[i] = params[i]; } this.hasQuadBatchTop = false; this.hasPointBatchTop = false; } }; GLWrap_.prototype.clear = function (r, g, b_, a) { var b = this.pushBatch(); b.type = BATCH_CLEAR; b.startIndex = 0; // clear all mode if (!b.mat4param) b.mat4param = mat4.create(); b.mat4param[0] = r; b.mat4param[1] = g; b.mat4param[2] = b_; b.mat4param[3] = a; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.clearRect = function (x, y, w, h) { if (w < 0 || h < 0) return; // invalid clear area var b = this.pushBatch(); b.type = BATCH_CLEAR; b.startIndex = 1; // clear rect mode if (!b.mat4param) b.mat4param = mat4.create(); b.mat4param[0] = x; b.mat4param[1] = y; b.mat4param[2] = w; b.mat4param[3] = h; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.clearDepth = function () { var b = this.pushBatch(); b.type = BATCH_CLEAR; b.startIndex = 2; // clear depth mode this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.setEarlyZPass = function (e) { if (!this.enableFrontToBack) return; // no depth buffer in use e = !!e; if (this.isEarlyZPass === e) return; // no change var b = this.pushBatch(); b.type = BATCH_SETEARLYZMODE; b.startIndex = (e ? 1 : 0); this.hasQuadBatchTop = false; this.hasPointBatchTop = false; this.isEarlyZPass = e; this.renderToTex = null; if (this.isEarlyZPass) { this.switchProgram(2); // early Z program } else { this.switchProgram(0); // normal rendering } }; GLWrap_.prototype.setDepthTestEnabled = function (e) { if (!this.enableFrontToBack) return; // no depth buffer in use var b = this.pushBatch(); b.type = BATCH_SETDEPTHTEST; b.startIndex = (e ? 1 : 0); this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.fullscreenQuad = function () { mat4.set(this.lastMV, tempMat4); this.resetModelView(); this.updateModelView(); var halfw = this.width / 2; var halfh = this.height / 2; this.quad(-halfw, halfh, halfw, halfh, halfw, -halfh, -halfw, -halfh); mat4.set(tempMat4, this.matMV); this.updateModelView(); }; GLWrap_.prototype.setColorFillMode = function (r_, g_, b_, a_) { this.switchProgram(3); var b = this.pushBatch(); b.type = BATCH_SETCOLOR; if (!b.mat4param) b.mat4param = mat4.create(); b.mat4param[0] = r_; b.mat4param[1] = g_; b.mat4param[2] = b_; b.mat4param[3] = a_; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; GLWrap_.prototype.setTextureFillMode = function () { ; this.switchProgram(0); }; GLWrap_.prototype.restoreEarlyZMode = function () { ; this.switchProgram(2); }; GLWrap_.prototype.present = function () { this.endBatch(); this.gl.flush(); /* if (debugBatch) { ; debugBatch = false; } */ }; function nextHighestPowerOfTwo(x) { --x; for (var i = 1; i < 32; i <<= 1) { x = x | x >> i; } return x + 1; } var all_textures = []; var textures_by_src = {}; GLWrap_.prototype.contextLost = function () { cr.clearArray(all_textures); textures_by_src = {}; }; var BF_RGBA8 = 0; var BF_RGB8 = 1; var BF_RGBA4 = 2; var BF_RGB5_A1 = 3; var BF_RGB565 = 4; GLWrap_.prototype.loadTexture = function (img, tiling, linearsampling, pixelformat, tiletype, nomip) { tiling = !!tiling; linearsampling = !!linearsampling; var tex_key = img.src + "," + tiling + "," + linearsampling + (tiling ? ("," + tiletype) : ""); var webGL_texture = null; if (typeof img.src !== "undefined" && textures_by_src.hasOwnProperty(tex_key)) { webGL_texture = textures_by_src[tex_key]; webGL_texture.c2refcount++; return webGL_texture; } this.endBatch(); ; var gl = this.gl; var isPOT = (cr.isPOT(img.width) && cr.isPOT(img.height)); webGL_texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, webGL_texture); gl.pixelStorei(gl["UNPACK_PREMULTIPLY_ALPHA_WEBGL"], true); var internalformat = gl.RGBA; var format = gl.RGBA; var type = gl.UNSIGNED_BYTE; if (pixelformat && !this.isIE) { switch (pixelformat) { case BF_RGB8: internalformat = gl.RGB; format = gl.RGB; break; case BF_RGBA4: type = gl.UNSIGNED_SHORT_4_4_4_4; break; case BF_RGB5_A1: type = gl.UNSIGNED_SHORT_5_5_5_1; break; case BF_RGB565: internalformat = gl.RGB; format = gl.RGB; type = gl.UNSIGNED_SHORT_5_6_5; break; } } if (this.version === 1 && !isPOT && tiling) { var canvas = document.createElement("canvas"); canvas.width = cr.nextHighestPowerOfTwo(img.width); canvas.height = cr.nextHighestPowerOfTwo(img.height); var ctx = canvas.getContext("2d"); if (typeof ctx["imageSmoothingEnabled"] !== "undefined") { ctx["imageSmoothingEnabled"] = linearsampling; } else { ctx["webkitImageSmoothingEnabled"] = linearsampling; ctx["mozImageSmoothingEnabled"] = linearsampling; ctx["msImageSmoothingEnabled"] = linearsampling; } ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height); gl.texImage2D(gl.TEXTURE_2D, 0, internalformat, format, type, canvas); } else gl.texImage2D(gl.TEXTURE_2D, 0, internalformat, format, type, img); if (tiling) { if (tiletype === "repeat-x") { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } else if (tiletype === "repeat-y") { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } if (linearsampling) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); if ((isPOT || this.version >= 2) && this.enable_mipmaps && !nomip) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); gl.generateMipmap(gl.TEXTURE_2D); } else gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); } gl.bindTexture(gl.TEXTURE_2D, null); this.lastTexture0 = null; webGL_texture.c2width = img.width; webGL_texture.c2height = img.height; webGL_texture.c2refcount = 1; webGL_texture.c2texkey = tex_key; all_textures.push(webGL_texture); textures_by_src[tex_key] = webGL_texture; return webGL_texture; }; GLWrap_.prototype.createEmptyTexture = function (w, h, linearsampling, _16bit, tiling) { this.endBatch(); var gl = this.gl; if (this.isIE) _16bit = false; var webGL_texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, webGL_texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, _16bit ? gl.UNSIGNED_SHORT_4_4_4_4 : gl.UNSIGNED_BYTE, null); if (tiling) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, linearsampling ? gl.LINEAR : gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, linearsampling ? gl.LINEAR : gl.NEAREST); gl.bindTexture(gl.TEXTURE_2D, null); this.lastTexture0 = null; webGL_texture.c2width = w; webGL_texture.c2height = h; all_textures.push(webGL_texture); return webGL_texture; }; GLWrap_.prototype.videoToTexture = function (video_, texture_, _16bit) { this.endBatch(); var gl = this.gl; if (this.isIE) _16bit = false; gl.bindTexture(gl.TEXTURE_2D, texture_); gl.pixelStorei(gl["UNPACK_PREMULTIPLY_ALPHA_WEBGL"], true); try { gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, _16bit ? gl.UNSIGNED_SHORT_4_4_4_4 : gl.UNSIGNED_BYTE, video_); } catch (e) { if (console && console.error) console.error("Error updating WebGL texture: ", e); } gl.bindTexture(gl.TEXTURE_2D, null); this.lastTexture0 = null; }; GLWrap_.prototype.deleteTexture = function (tex) { if (!tex) return; if (typeof tex.c2refcount !== "undefined" && tex.c2refcount > 1) { tex.c2refcount--; return; } this.endBatch(); if (tex === this.lastTexture0) { this.gl.bindTexture(this.gl.TEXTURE_2D, null); this.lastTexture0 = null; } if (tex === this.lastTexture1) { this.gl.activeTexture(this.gl.TEXTURE1); this.gl.bindTexture(this.gl.TEXTURE_2D, null); this.gl.activeTexture(this.gl.TEXTURE0); this.lastTexture1 = null; } cr.arrayFindRemove(all_textures, tex); if (typeof tex.c2texkey !== "undefined") delete textures_by_src[tex.c2texkey]; this.gl.deleteTexture(tex); }; GLWrap_.prototype.estimateVRAM = function () { var total = this.width * this.height * 4 * 2; var i, len, t; for (i = 0, len = all_textures.length; i < len; i++) { t = all_textures[i]; total += (t.c2width * t.c2height * 4); } return total; }; GLWrap_.prototype.textureCount = function () { return all_textures.length; }; GLWrap_.prototype.setRenderingToTexture = function (tex) { if (tex === this.renderToTex) return; ; var b = this.pushBatch(); b.type = BATCH_RENDERTOTEXTURE; b.texParam = tex; this.renderToTex = tex; this.hasQuadBatchTop = false; this.hasPointBatchTop = false; }; cr.GLWrap = GLWrap_; }()); ; (function() { var raf = window["requestAnimationFrame"] || window["mozRequestAnimationFrame"] || window["webkitRequestAnimationFrame"] || window["msRequestAnimationFrame"] || window["oRequestAnimationFrame"]; function Runtime(canvas) { if (!canvas || (!canvas.getContext && !canvas["dc"])) return; if (canvas["c2runtime"]) return; else canvas["c2runtime"] = this; var self = this; this.isCrosswalk = /crosswalk/i.test(navigator.userAgent) || /xwalk/i.test(navigator.userAgent) || !!(typeof window["c2isCrosswalk"] !== "undefined" && window["c2isCrosswalk"]); this.isCordova = this.isCrosswalk || (typeof window["device"] !== "undefined" && (typeof window["device"]["cordova"] !== "undefined" || typeof window["device"]["phonegap"] !== "undefined")) || (typeof window["c2iscordova"] !== "undefined" && window["c2iscordova"]); this.isPhoneGap = this.isCordova; this.isDirectCanvas = !!canvas["dc"]; this.isAppMobi = (typeof window["AppMobi"] !== "undefined" || this.isDirectCanvas); this.isCocoonJs = !!window["c2cocoonjs"]; this.isEjecta = !!window["c2ejecta"]; if (this.isCocoonJs) { CocoonJS["App"]["onSuspended"].addEventListener(function() { self["setSuspended"](true); }); CocoonJS["App"]["onActivated"].addEventListener(function () { self["setSuspended"](false); }); } if (this.isEjecta) { document.addEventListener("pagehide", function() { self["setSuspended"](true); }); document.addEventListener("pageshow", function() { self["setSuspended"](false); }); document.addEventListener("resize", function () { self["setSize"](window.innerWidth, window.innerHeight); }); } this.isDomFree = (this.isDirectCanvas || this.isCocoonJs || this.isEjecta); this.isMicrosoftEdge = /edge\//i.test(navigator.userAgent); this.isIE = (/msie/i.test(navigator.userAgent) || /trident/i.test(navigator.userAgent) || /iemobile/i.test(navigator.userAgent)) && !this.isMicrosoftEdge; this.isTizen = /tizen/i.test(navigator.userAgent); this.isAndroid = /android/i.test(navigator.userAgent) && !this.isTizen && !this.isIE && !this.isMicrosoftEdge; // IE mobile and Tizen masquerade as Android this.isiPhone = (/iphone/i.test(navigator.userAgent) || /ipod/i.test(navigator.userAgent)) && !this.isIE && !this.isMicrosoftEdge; // treat ipod as an iphone; IE mobile masquerades as iPhone this.isiPad = /ipad/i.test(navigator.userAgent); this.isiOS = this.isiPhone || this.isiPad || this.isEjecta; this.isiPhoneiOS6 = (this.isiPhone && /os\s6/i.test(navigator.userAgent)); this.isChrome = (/chrome/i.test(navigator.userAgent) || /chromium/i.test(navigator.userAgent)) && !this.isIE && !this.isMicrosoftEdge; // note true on Chromium-based webview on Android 4.4+; IE 'Edge' mode also pretends to be Chrome this.isAmazonWebApp = /amazonwebappplatform/i.test(navigator.userAgent); this.isFirefox = /firefox/i.test(navigator.userAgent); this.isSafari = /safari/i.test(navigator.userAgent) && !this.isChrome && !this.isIE && !this.isMicrosoftEdge; // Chrome and IE Mobile masquerade as Safari this.isWindows = /windows/i.test(navigator.userAgent); this.isNWjs = (typeof window["c2nodewebkit"] !== "undefined" || typeof window["c2nwjs"] !== "undefined" || /nodewebkit/i.test(navigator.userAgent) || /nwjs/i.test(navigator.userAgent)); this.isNodeWebkit = this.isNWjs; // old name for backwards compat this.isArcade = (typeof window["is_scirra_arcade"] !== "undefined"); this.isWindows8App = !!(typeof window["c2isWindows8"] !== "undefined" && window["c2isWindows8"]); this.isWindows8Capable = !!(typeof window["c2isWindows8Capable"] !== "undefined" && window["c2isWindows8Capable"]); this.isWindowsPhone8 = !!(typeof window["c2isWindowsPhone8"] !== "undefined" && window["c2isWindowsPhone8"]); this.isWindowsPhone81 = !!(typeof window["c2isWindowsPhone81"] !== "undefined" && window["c2isWindowsPhone81"]); this.isWindows10 = !!window["cr_windows10"]; this.isWinJS = (this.isWindows8App || this.isWindows8Capable || this.isWindowsPhone81 || this.isWindows10); // note not WP8.0 this.isBlackberry10 = !!(typeof window["c2isBlackberry10"] !== "undefined" && window["c2isBlackberry10"]); this.isAndroidStockBrowser = (this.isAndroid && !this.isChrome && !this.isCrosswalk && !this.isFirefox && !this.isAmazonWebApp && !this.isDomFree); this.devicePixelRatio = 1; this.isMobile = (this.isCordova || this.isCrosswalk || this.isAppMobi || this.isCocoonJs || this.isAndroid || this.isiOS || this.isWindowsPhone8 || this.isWindowsPhone81 || this.isBlackberry10 || this.isTizen || this.isEjecta); if (!this.isMobile) { this.isMobile = /(blackberry|bb10|playbook|palm|symbian|nokia|windows\s+ce|phone|mobile|tablet|kindle|silk)/i.test(navigator.userAgent); } this.isWKWebView = !!(this.isiOS && this.isCordova && window["webkit"]); this.httpServer = null; this.httpServerUrl = ""; if (this.isWKWebView) { this.httpServer = (cordova && cordova["plugins"] && cordova["plugins"]["CorHttpd"]) ? cordova["plugins"]["CorHttpd"] : null; } if (typeof cr_is_preview !== "undefined" && !this.isNWjs && (window.location.search === "?nw" || /nodewebkit/i.test(navigator.userAgent) || /nwjs/i.test(navigator.userAgent))) { this.isNWjs = true; } this.isDebug = (typeof cr_is_preview !== "undefined" && window.location.search.indexOf("debug") > -1); this.canvas = canvas; this.canvasdiv = document.getElementById("c2canvasdiv"); this.gl = null; this.glwrap = null; this.glUnmaskedRenderer = "(unavailable)"; this.enableFrontToBack = false; this.earlyz_index = 0; this.ctx = null; this.fullscreenOldMarginCss = ""; this.firstInFullscreen = false; this.oldWidth = 0; // for restoring non-fullscreen canvas after fullscreen this.oldHeight = 0; this.canvas.oncontextmenu = function (e) { if (e.preventDefault) e.preventDefault(); return false; }; this.canvas.onselectstart = function (e) { if (e.preventDefault) e.preventDefault(); return false; }; if (this.isDirectCanvas) window["c2runtime"] = this; if (this.isNWjs) { window["ondragover"] = function(e) { e.preventDefault(); return false; }; window["ondrop"] = function(e) { e.preventDefault(); return false; }; if (window["nwgui"] && window["nwgui"]["App"]["clearCache"]) window["nwgui"]["App"]["clearCache"](); } if (this.isAndroidStockBrowser && typeof jQuery !== "undefined") { jQuery("canvas").parents("*").css("overflow", "visible"); } this.width = canvas.width; this.height = canvas.height; this.draw_width = this.width; this.draw_height = this.height; this.cssWidth = this.width; this.cssHeight = this.height; this.lastWindowWidth = window.innerWidth; this.lastWindowHeight = window.innerHeight; this.forceCanvasAlpha = false; // note: now unused, left for backwards compat since plugins could modify it this.redraw = true; this.isSuspended = false; if (!Date.now) { Date.now = function now() { return +new Date(); }; } this.plugins = []; this.types = {}; this.types_by_index = []; this.behaviors = []; this.layouts = {}; this.layouts_by_index = []; this.eventsheets = {}; this.eventsheets_by_index = []; this.wait_for_textures = []; // for blocking until textures loaded this.triggers_to_postinit = []; this.all_global_vars = []; this.all_local_vars = []; this.solidBehavior = null; this.jumpthruBehavior = null; this.shadowcasterBehavior = null; this.deathRow = {}; this.hasPendingInstances = false; // true if anything exists in create row or death row this.isInClearDeathRow = false; this.isInOnDestroy = 0; // needs to support recursion so increments and decrements and is true if > 0 this.isRunningEvents = false; this.isEndingLayout = false; this.createRow = []; this.isLoadingState = false; this.saveToSlot = ""; this.loadFromSlot = ""; this.loadFromJson = null; // set to string when there is something to try to load this.lastSaveJson = ""; this.signalledContinuousPreview = false; this.suspendDrawing = false; // for hiding display until continuous preview loads this.fireOnCreateAfterLoad = []; // for delaying "On create" triggers until loading complete this.dt = 0; this.dt1 = 0; this.minimumFramerate = 30; this.logictime = 0; // used to calculate CPUUtilisation this.cpuutilisation = 0; this.timescale = 1.0; this.kahanTime = new cr.KahanAdder(); this.wallTime = new cr.KahanAdder(); this.last_tick_time = 0; this.fps = 0; this.last_fps_time = 0; this.tickcount = 0; this.execcount = 0; this.framecount = 0; // for fps this.objectcount = 0; this.changelayout = null; this.destroycallbacks = []; this.event_stack = []; this.event_stack_index = -1; this.localvar_stack = [[]]; this.localvar_stack_index = 0; this.trigger_depth = 0; // recursion depth for triggers this.pushEventStack(null); this.loop_stack = []; this.loop_stack_index = -1; this.next_uid = 0; this.next_puid = 0; // permanent unique ids this.layout_first_tick = true; this.family_count = 0; this.suspend_events = []; this.raf_id = -1; this.timeout_id = -1; this.isloading = true; this.loadingprogress = 0; this.isNodeFullscreen = false; this.stackLocalCount = 0; // number of stack-based local vars for recursion this.audioInstance = null; this.had_a_click = false; this.isInUserInputEvent = false; this.objects_to_pretick = new cr.ObjectSet(); this.objects_to_tick = new cr.ObjectSet(); this.objects_to_tick2 = new cr.ObjectSet(); this.registered_collisions = []; this.temp_poly = new cr.CollisionPoly([]); this.temp_poly2 = new cr.CollisionPoly([]); this.allGroups = []; // array of all event groups this.groups_by_name = {}; this.cndsBySid = {}; this.actsBySid = {}; this.varsBySid = {}; this.blocksBySid = {}; this.running_layout = null; // currently running layout this.layer_canvas = null; // for layers "render-to-texture" this.layer_ctx = null; this.layer_tex = null; this.layout_tex = null; this.layout_canvas = null; this.layout_ctx = null; this.is_WebGL_context_lost = false; this.uses_background_blending = false; // if any shader uses background blending, so entire layout renders to texture this.fx_tex = [null, null]; this.fullscreen_scaling = 0; this.files_subfolder = ""; // path with project files this.objectsByUid = {}; // maps every in-use UID (as a string) to its instance this.loaderlogos = null; this.snapshotCanvas = null; this.snapshotData = ""; this.objectRefTable = []; this.requestProjectData(); }; Runtime.prototype.requestProjectData = function () { var self = this; if (this.isWKWebView) { var loadDataJsFn = function () { self.fetchLocalFileViaCordovaAsText("data.js", function (str) { self.loadProject(JSON.parse(str)); }, function (err) { alert("Error fetching data.js"); }); }; if (this.httpServer) { this.httpServer["startServer"]({ "port": 0, "localhost_only": true }, function (url) { self.httpServerUrl = url; loadDataJsFn(); }, function (err) { console.log("Error starting local server: " + err + ". Video playback will not work."); loadDataJsFn(); }); } else { console.log("Local server unavailable. Video playback will not work."); loadDataJsFn(); } return; } var xhr; if (this.isWindowsPhone8) xhr = new ActiveXObject("Microsoft.XMLHTTP"); else xhr = new XMLHttpRequest(); var datajs_filename = "data.js"; if (this.isWindows8App || this.isWindowsPhone8 || this.isWindowsPhone81 || this.isWindows10) datajs_filename = "data.json"; xhr.open("GET", datajs_filename, true); var supportsJsonResponse = false; if (!this.isDomFree && ("response" in xhr) && ("responseType" in xhr)) { try { xhr["responseType"] = "json"; supportsJsonResponse = (xhr["responseType"] === "json"); } catch (e) { supportsJsonResponse = false; } } if (!supportsJsonResponse && ("responseType" in xhr)) { try { xhr["responseType"] = "text"; } catch (e) {} } if ("overrideMimeType" in xhr) { try { xhr["overrideMimeType"]("application/json; charset=utf-8"); } catch (e) {} } if (this.isWindowsPhone8) { xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; self.loadProject(JSON.parse(xhr["responseText"])); }; } else { xhr.onload = function () { if (supportsJsonResponse) { self.loadProject(xhr["response"]); // already parsed by browser } else { if (self.isEjecta) { var str = xhr["responseText"]; str = str.substr(str.indexOf("{")); // trim any BOM self.loadProject(JSON.parse(str)); } else { self.loadProject(JSON.parse(xhr["responseText"])); // forced to sync parse JSON } } }; xhr.onerror = function (e) { cr.logerror("Error requesting " + datajs_filename + ":"); cr.logerror(e); }; } xhr.send(); }; Runtime.prototype.initRendererAndLoader = function () { var self = this; var i, len, j, lenj, k, lenk, t, s, l, y; this.isRetina = ((!this.isDomFree || this.isEjecta || this.isCordova) && this.useHighDpi && !this.isAndroidStockBrowser); if (this.fullscreen_mode === 0 && this.isiOS) this.isRetina = false; this.devicePixelRatio = (this.isRetina ? (window["devicePixelRatio"] || window["webkitDevicePixelRatio"] || window["mozDevicePixelRatio"] || window["msDevicePixelRatio"] || 1) : 1); this.ClearDeathRow(); var attribs; if (this.fullscreen_mode > 0) this["setSize"](window.innerWidth, window.innerHeight, true); this.canvas.addEventListener("webglcontextlost", function (ev) { ev.preventDefault(); self.onContextLost(); cr.logexport("[Construct 2] WebGL context lost"); window["cr_setSuspended"](true); // stop rendering }, false); this.canvas.addEventListener("webglcontextrestored", function (ev) { self.glwrap.initState(); self.glwrap.setSize(self.glwrap.width, self.glwrap.height, true); self.layer_tex = null; self.layout_tex = null; self.fx_tex[0] = null; self.fx_tex[1] = null; self.onContextRestored(); self.redraw = true; cr.logexport("[Construct 2] WebGL context restored"); window["cr_setSuspended"](false); // resume rendering }, false); try { if (this.enableWebGL && (this.isCocoonJs || this.isEjecta || !this.isDomFree)) { attribs = { "alpha": true, "depth": false, "antialias": false, "powerPreference": "high-performance", "failIfMajorPerformanceCaveat": true }; this.gl = (this.canvas.getContext("webgl2", attribs) || this.canvas.getContext("webgl", attribs) || this.canvas.getContext("experimental-webgl", attribs)); } } catch (e) { } if (this.gl) { var isWebGL2 = (this.gl.getParameter(this.gl.VERSION).indexOf("WebGL 2") === 0); var debug_ext = this.gl.getExtension("WEBGL_debug_renderer_info"); if (debug_ext) { var unmasked_vendor = this.gl.getParameter(debug_ext.UNMASKED_VENDOR_WEBGL); var unmasked_renderer = this.gl.getParameter(debug_ext.UNMASKED_RENDERER_WEBGL); this.glUnmaskedRenderer = unmasked_renderer + " [" + unmasked_vendor + "]"; } if (this.enableFrontToBack) this.glUnmaskedRenderer += " [front-to-back enabled]"; ; if (!this.isDomFree) { this.overlay_canvas = document.createElement("canvas"); jQuery(this.overlay_canvas).appendTo(this.canvas.parentNode); this.overlay_canvas.oncontextmenu = function (e) { return false; }; this.overlay_canvas.onselectstart = function (e) { return false; }; this.overlay_canvas.width = Math.round(this.cssWidth * this.devicePixelRatio); this.overlay_canvas.height = Math.round(this.cssHeight * this.devicePixelRatio); jQuery(this.overlay_canvas).css({"width": this.cssWidth + "px", "height": this.cssHeight + "px"}); this.positionOverlayCanvas(); this.overlay_ctx = this.overlay_canvas.getContext("2d"); } this.glwrap = new cr.GLWrap(this.gl, this.isMobile, this.enableFrontToBack); this.glwrap.setSize(this.canvas.width, this.canvas.height); this.glwrap.enable_mipmaps = (this.downscalingQuality !== 0); this.ctx = null; for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; for (j = 0, lenj = t.effect_types.length; j < lenj; j++) { s = t.effect_types[j]; s.shaderindex = this.glwrap.getShaderIndex(s.id); s.preservesOpaqueness = this.glwrap.programPreservesOpaqueness(s.shaderindex); this.uses_background_blending = this.uses_background_blending || this.glwrap.programUsesDest(s.shaderindex); } } for (i = 0, len = this.layouts_by_index.length; i < len; i++) { l = this.layouts_by_index[i]; for (j = 0, lenj = l.effect_types.length; j < lenj; j++) { s = l.effect_types[j]; s.shaderindex = this.glwrap.getShaderIndex(s.id); s.preservesOpaqueness = this.glwrap.programPreservesOpaqueness(s.shaderindex); } l.updateActiveEffects(); // update preserves opaqueness flag for (j = 0, lenj = l.layers.length; j < lenj; j++) { y = l.layers[j]; for (k = 0, lenk = y.effect_types.length; k < lenk; k++) { s = y.effect_types[k]; s.shaderindex = this.glwrap.getShaderIndex(s.id); s.preservesOpaqueness = this.glwrap.programPreservesOpaqueness(s.shaderindex); this.uses_background_blending = this.uses_background_blending || this.glwrap.programUsesDest(s.shaderindex); } y.updateActiveEffects(); // update preserves opaqueness flag } } } else { if (this.fullscreen_mode > 0 && this.isDirectCanvas) { ; this.canvas = null; document.oncontextmenu = function (e) { return false; }; document.onselectstart = function (e) { return false; }; this.ctx = AppMobi["canvas"]["getContext"]("2d"); try { this.ctx["samplingMode"] = this.linearSampling ? "smooth" : "sharp"; this.ctx["globalScale"] = 1; this.ctx["HTML5CompatibilityMode"] = true; this.ctx["imageSmoothingEnabled"] = this.linearSampling; } catch(e){} if (this.width !== 0 && this.height !== 0) { this.ctx.width = this.width; this.ctx.height = this.height; } } if (!this.ctx) { ; if (this.isCocoonJs) { attribs = { "antialias": !!this.linearSampling, "alpha": true }; this.ctx = this.canvas.getContext("2d", attribs); } else { attribs = { "alpha": true }; this.ctx = this.canvas.getContext("2d", attribs); } this.setCtxImageSmoothingEnabled(this.ctx, this.linearSampling); } this.overlay_canvas = null; this.overlay_ctx = null; } this.tickFunc = function (timestamp) { self.tick(false, timestamp); }; if (window != window.top && !this.isDomFree && !this.isWinJS && !this.isWindowsPhone8) { document.addEventListener("mousedown", function () { window.focus(); }, true); document.addEventListener("touchstart", function () { window.focus(); }, true); } if (typeof cr_is_preview !== "undefined") { if (this.isCocoonJs) console.log("[Construct 2] In preview-over-wifi via CocoonJS mode"); if (window.location.search.indexOf("continuous") > -1) { cr.logexport("Reloading for continuous preview"); this.loadFromSlot = "__c2_continuouspreview"; this.suspendDrawing = true; } if (this.pauseOnBlur && !this.isMobile) { jQuery(window).focus(function () { self["setSuspended"](false); }); jQuery(window).blur(function () { var parent = window.parent; if (!parent || !parent.document.hasFocus()) self["setSuspended"](true); }); } } window.addEventListener("blur", function () { self.onWindowBlur(); }); if (!this.isDomFree) { var unfocusFormControlFunc = function (e) { if (cr.isCanvasInputEvent(e) && document["activeElement"] && document["activeElement"] !== document.getElementsByTagName("body")[0] && document["activeElement"].blur) { try { document["activeElement"].blur(); } catch (e) {} } } if (typeof PointerEvent !== "undefined") { document.addEventListener("pointerdown", unfocusFormControlFunc); } else if (window.navigator["msPointerEnabled"]) { document.addEventListener("MSPointerDown", unfocusFormControlFunc); } else { document.addEventListener("touchstart", unfocusFormControlFunc); } document.addEventListener("mousedown", unfocusFormControlFunc); } if (this.fullscreen_mode === 0 && this.isRetina && this.devicePixelRatio > 1) { this["setSize"](this.original_width, this.original_height, true); } this.tryLockOrientation(); this.getready(); // determine things to preload this.go(); // run loading screen this.extra = {}; cr.seal(this); }; var webkitRepaintFlag = false; Runtime.prototype["setSize"] = function (w, h, force) { var offx = 0, offy = 0; var neww = 0, newh = 0, intscale = 0; if (this.lastWindowWidth === w && this.lastWindowHeight === h && !force) return; this.lastWindowWidth = w; this.lastWindowHeight = h; var mode = this.fullscreen_mode; var orig_aspect, cur_aspect; var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || !!document["msFullscreenElement"] || document["fullScreen"] || this.isNodeFullscreen) && !this.isCordova; if (!isfullscreen && this.fullscreen_mode === 0 && !force) return; // ignore size events when not fullscreen and not using a fullscreen-in-browser mode if (isfullscreen && this.fullscreen_scaling > 0) mode = this.fullscreen_scaling; var dpr = this.devicePixelRatio; if (mode >= 4) { orig_aspect = this.original_width / this.original_height; cur_aspect = w / h; if (cur_aspect > orig_aspect) { neww = h * orig_aspect; if (mode === 5) // integer scaling { intscale = (neww * dpr) / this.original_width; if (intscale > 1) intscale = Math.floor(intscale); else if (intscale < 1) intscale = 1 / Math.ceil(1 / intscale); neww = this.original_width * intscale / dpr; newh = this.original_height * intscale / dpr; offx = (w - neww) / 2; offy = (h - newh) / 2; w = neww; h = newh; } else { offx = (w - neww) / 2; w = neww; } } else { newh = w / orig_aspect; if (mode === 5) // integer scaling { intscale = (newh * dpr) / this.original_height; if (intscale > 1) intscale = Math.floor(intscale); else if (intscale < 1) intscale = 1 / Math.ceil(1 / intscale); neww = this.original_width * intscale / dpr; newh = this.original_height * intscale / dpr; offx = (w - neww) / 2; offy = (h - newh) / 2; w = neww; h = newh; } else { offy = (h - newh) / 2; h = newh; } } if (isfullscreen && !this.isNWjs) { offx = 0; offy = 0; } } else if (this.isNWjs && this.isNodeFullscreen && this.fullscreen_mode_set === 0) { offx = Math.floor((w - this.original_width) / 2); offy = Math.floor((h - this.original_height) / 2); w = this.original_width; h = this.original_height; } if (mode < 2) this.aspect_scale = dpr; this.cssWidth = Math.round(w); this.cssHeight = Math.round(h); this.width = Math.round(w * dpr); this.height = Math.round(h * dpr); this.redraw = true; if (this.wantFullscreenScalingQuality) { this.draw_width = this.width; this.draw_height = this.height; this.fullscreenScalingQuality = true; } else { if ((this.width < this.original_width && this.height < this.original_height) || mode === 1) { this.draw_width = this.width; this.draw_height = this.height; this.fullscreenScalingQuality = true; } else { this.draw_width = this.original_width; this.draw_height = this.original_height; this.fullscreenScalingQuality = false; /*var orig_aspect = this.original_width / this.original_height; var cur_aspect = this.width / this.height; if ((this.fullscreen_mode !== 2 && cur_aspect > orig_aspect) || (this.fullscreen_mode === 2 && cur_aspect < orig_aspect)) this.aspect_scale = this.height / this.original_height; else this.aspect_scale = this.width / this.original_width;*/ if (mode === 2) // scale inner { orig_aspect = this.original_width / this.original_height; cur_aspect = this.lastWindowWidth / this.lastWindowHeight; if (cur_aspect < orig_aspect) this.draw_width = this.draw_height * cur_aspect; else if (cur_aspect > orig_aspect) this.draw_height = this.draw_width / cur_aspect; } else if (mode === 3) { orig_aspect = this.original_width / this.original_height; cur_aspect = this.lastWindowWidth / this.lastWindowHeight; if (cur_aspect > orig_aspect) this.draw_width = this.draw_height * cur_aspect; else if (cur_aspect < orig_aspect) this.draw_height = this.draw_width / cur_aspect; } } } if (this.canvasdiv && !this.isDomFree) { jQuery(this.canvasdiv).css({"width": Math.round(w) + "px", "height": Math.round(h) + "px", "margin-left": Math.floor(offx) + "px", "margin-top": Math.floor(offy) + "px"}); if (typeof cr_is_preview !== "undefined") { jQuery("#borderwrap").css({"width": Math.round(w) + "px", "height": Math.round(h) + "px"}); } } if (this.canvas) { this.canvas.width = Math.round(w * dpr); this.canvas.height = Math.round(h * dpr); if (this.isEjecta) { this.canvas.style.left = Math.floor(offx) + "px"; this.canvas.style.top = Math.floor(offy) + "px"; this.canvas.style.width = Math.round(w) + "px"; this.canvas.style.height = Math.round(h) + "px"; } else if (this.isRetina && !this.isDomFree) { this.canvas.style.width = Math.round(w) + "px"; this.canvas.style.height = Math.round(h) + "px"; } } if (this.overlay_canvas) { this.overlay_canvas.width = Math.round(w * dpr); this.overlay_canvas.height = Math.round(h * dpr); this.overlay_canvas.style.width = this.cssWidth + "px"; this.overlay_canvas.style.height = this.cssHeight + "px"; } if (this.glwrap) { this.glwrap.setSize(Math.round(w * dpr), Math.round(h * dpr)); } if (this.isDirectCanvas && this.ctx) { this.ctx.width = Math.round(w); this.ctx.height = Math.round(h); } if (this.ctx) { this.setCtxImageSmoothingEnabled(this.ctx, this.linearSampling); } this.tryLockOrientation(); if (this.isiPhone && !this.isCordova) { window.scrollTo(0, 0); } }; Runtime.prototype.tryLockOrientation = function () { if (!this.autoLockOrientation || this.orientations === 0) return; var orientation = "portrait"; if (this.orientations === 2) orientation = "landscape"; try { if (screen["orientation"] && screen["orientation"]["lock"]) screen["orientation"]["lock"](orientation).catch(function(){}); else if (screen["lockOrientation"]) screen["lockOrientation"](orientation); else if (screen["webkitLockOrientation"]) screen["webkitLockOrientation"](orientation); else if (screen["mozLockOrientation"]) screen["mozLockOrientation"](orientation); else if (screen["msLockOrientation"]) screen["msLockOrientation"](orientation); } catch (e) { if (console && console.warn) console.warn("Failed to lock orientation: ", e); } }; Runtime.prototype.onContextLost = function () { this.glwrap.contextLost(); this.is_WebGL_context_lost = true; var i, len, t; for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; if (t.onLostWebGLContext) t.onLostWebGLContext(); } }; Runtime.prototype.onContextRestored = function () { this.is_WebGL_context_lost = false; var i, len, t; for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; if (t.onRestoreWebGLContext) t.onRestoreWebGLContext(); } }; Runtime.prototype.positionOverlayCanvas = function() { if (this.isDomFree) return; var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || document["fullScreen"] || !!document["msFullscreenElement"] || this.isNodeFullscreen) && !this.isCordova; var overlay_position = isfullscreen ? jQuery(this.canvas).offset() : jQuery(this.canvas).position(); overlay_position.position = "absolute"; jQuery(this.overlay_canvas).css(overlay_position); }; var caf = window["cancelAnimationFrame"] || window["mozCancelAnimationFrame"] || window["webkitCancelAnimationFrame"] || window["msCancelAnimationFrame"] || window["oCancelAnimationFrame"]; Runtime.prototype["setSuspended"] = function (s) { var i, len; var self = this; if (s && !this.isSuspended) { cr.logexport("[Construct 2] Suspending"); this.isSuspended = true; // next tick will be last if (this.raf_id !== -1 && caf) // note: CocoonJS does not implement cancelAnimationFrame caf(this.raf_id); if (this.timeout_id !== -1) clearTimeout(this.timeout_id); for (i = 0, len = this.suspend_events.length; i < len; i++) this.suspend_events[i](true); } else if (!s && this.isSuspended) { cr.logexport("[Construct 2] Resuming"); this.isSuspended = false; this.last_tick_time = cr.performance_now(); // ensure first tick is a zero-dt one this.last_fps_time = cr.performance_now(); // reset FPS counter this.framecount = 0; this.logictime = 0; for (i = 0, len = this.suspend_events.length; i < len; i++) this.suspend_events[i](false); this.tick(false); // kick off runtime again } }; Runtime.prototype.addSuspendCallback = function (f) { this.suspend_events.push(f); }; Runtime.prototype.GetObjectReference = function (i) { ; return this.objectRefTable[i]; }; Runtime.prototype.loadProject = function (data_response) { ; if (!data_response || !data_response["project"]) cr.logerror("Project model unavailable"); var pm = data_response["project"]; this.name = pm[0]; this.first_layout = pm[1]; this.fullscreen_mode = pm[12]; // 0 = off, 1 = crop, 2 = scale inner, 3 = scale outer, 4 = letterbox scale, 5 = integer letterbox scale this.fullscreen_mode_set = pm[12]; this.original_width = pm[10]; this.original_height = pm[11]; this.parallax_x_origin = this.original_width / 2; this.parallax_y_origin = this.original_height / 2; if (this.isDomFree && !this.isEjecta && (pm[12] >= 4 || pm[12] === 0)) { cr.logexport("[Construct 2] Letterbox scale fullscreen modes are not supported on this platform - falling back to 'Scale outer'"); this.fullscreen_mode = 3; this.fullscreen_mode_set = 3; } this.uses_loader_layout = pm[18]; this.loaderstyle = pm[19]; if (this.loaderstyle === 0) { var loaderImage = new Image(); loaderImage.crossOrigin = "anonymous"; this.setImageSrc(loaderImage, "loading-logo.png"); this.loaderlogos = { logo: loaderImage }; } else if (this.loaderstyle === 4) // c2 splash { var loaderC2logo_1024 = new Image(); loaderC2logo_1024.src = ""; var loaderC2logo_512 = new Image(); loaderC2logo_512.src = ""; var loaderC2logo_256 = new Image(); loaderC2logo_256.src = ""; var loaderC2logo_128 = new Image(); loaderC2logo_128.src = ""; var loaderPowered_1024 = new Image(); loaderPowered_1024.src = ""; var loaderPowered_512 = new Image(); loaderPowered_512.src = ""; var loaderPowered_256 = new Image(); loaderPowered_256.src = ""; var loaderPowered_128 = new Image(); loaderPowered_128.src = ""; var loaderWebsite_1024 = new Image(); loaderWebsite_1024.src = ""; var loaderWebsite_512 = new Image(); loaderWebsite_512.src = ""; var loaderWebsite_256 = new Image(); loaderWebsite_256.src = ""; var loaderWebsite_128 = new Image(); loaderWebsite_128.src = ""; this.loaderlogos = { logo: [loaderC2logo_1024, loaderC2logo_512, loaderC2logo_256, loaderC2logo_128], powered: [loaderPowered_1024, loaderPowered_512, loaderPowered_256, loaderPowered_128], website: [loaderWebsite_1024, loaderWebsite_512, loaderWebsite_256, loaderWebsite_128] }; } this.next_uid = pm[21]; this.objectRefTable = cr.getObjectRefTable(); this.system = new cr.system_object(this); var i, len, j, lenj, k, lenk, idstr, m, b, t, f, p; var plugin, plugin_ctor; for (i = 0, len = pm[2].length; i < len; i++) { m = pm[2][i]; p = this.GetObjectReference(m[0]); ; cr.add_common_aces(m, p.prototype); plugin = new p(this); plugin.singleglobal = m[1]; plugin.is_world = m[2]; plugin.is_rotatable = m[5]; plugin.must_predraw = m[9]; if (plugin.onCreate) plugin.onCreate(); // opportunity to override default ACEs cr.seal(plugin); this.plugins.push(plugin); } this.objectRefTable = cr.getObjectRefTable(); for (i = 0, len = pm[3].length; i < len; i++) { m = pm[3][i]; plugin_ctor = this.GetObjectReference(m[1]); ; plugin = null; for (j = 0, lenj = this.plugins.length; j < lenj; j++) { if (this.plugins[j] instanceof plugin_ctor) { plugin = this.plugins[j]; break; } } ; ; var type_inst = new plugin.Type(plugin); ; type_inst.name = m[0]; type_inst.is_family = m[2]; type_inst.instvar_sids = m[3].slice(0); type_inst.vars_count = m[3].length; type_inst.behs_count = m[4]; type_inst.fx_count = m[5]; type_inst.sid = m[11]; if (type_inst.is_family) { type_inst.members = []; // types in this family type_inst.family_index = this.family_count++; type_inst.families = null; } else { type_inst.members = null; type_inst.family_index = -1; type_inst.families = []; // families this type belongs to } type_inst.family_var_map = null; type_inst.family_beh_map = null; type_inst.family_fx_map = null; type_inst.is_contained = false; type_inst.container = null; if (m[6]) { type_inst.texture_file = m[6][0]; type_inst.texture_filesize = m[6][1]; type_inst.texture_pixelformat = m[6][2]; } else { type_inst.texture_file = null; type_inst.texture_filesize = 0; type_inst.texture_pixelformat = 0; // rgba8 } if (m[7]) { type_inst.animations = m[7]; } else { type_inst.animations = null; } type_inst.index = i; // save index in to types array in type type_inst.instances = []; // all instances of this type type_inst.deadCache = []; // destroyed instances to recycle next create type_inst.solstack = [new cr.selection(type_inst)]; // initialise SOL stack with one empty SOL type_inst.cur_sol = 0; type_inst.default_instance = null; type_inst.default_layerindex = 0; type_inst.stale_iids = true; type_inst.updateIIDs = cr.type_updateIIDs; type_inst.getFirstPicked = cr.type_getFirstPicked; type_inst.getPairedInstance = cr.type_getPairedInstance; type_inst.getCurrentSol = cr.type_getCurrentSol; type_inst.pushCleanSol = cr.type_pushCleanSol; type_inst.pushCopySol = cr.type_pushCopySol; type_inst.popSol = cr.type_popSol; type_inst.getBehaviorByName = cr.type_getBehaviorByName; type_inst.getBehaviorIndexByName = cr.type_getBehaviorIndexByName; type_inst.getEffectIndexByName = cr.type_getEffectIndexByName; type_inst.applySolToContainer = cr.type_applySolToContainer; type_inst.getInstanceByIID = cr.type_getInstanceByIID; type_inst.collision_grid = new cr.SparseGrid(this.original_width, this.original_height); type_inst.any_cell_changed = true; type_inst.any_instance_parallaxed = false; type_inst.extra = {}; type_inst.toString = cr.type_toString; type_inst.behaviors = []; for (j = 0, lenj = m[8].length; j < lenj; j++) { b = m[8][j]; var behavior_ctor = this.GetObjectReference(b[1]); var behavior_plugin = null; for (k = 0, lenk = this.behaviors.length; k < lenk; k++) { if (this.behaviors[k] instanceof behavior_ctor) { behavior_plugin = this.behaviors[k]; break; } } if (!behavior_plugin) { behavior_plugin = new behavior_ctor(this); behavior_plugin.my_types = []; // types using this behavior behavior_plugin.my_instances = new cr.ObjectSet(); // instances of this behavior if (behavior_plugin.onCreate) behavior_plugin.onCreate(); cr.seal(behavior_plugin); this.behaviors.push(behavior_plugin); if (cr.behaviors.solid && behavior_plugin instanceof cr.behaviors.solid) this.solidBehavior = behavior_plugin; if (cr.behaviors.jumpthru && behavior_plugin instanceof cr.behaviors.jumpthru) this.jumpthruBehavior = behavior_plugin; if (cr.behaviors.shadowcaster && behavior_plugin instanceof cr.behaviors.shadowcaster) this.shadowcasterBehavior = behavior_plugin; } if (behavior_plugin.my_types.indexOf(type_inst) === -1) behavior_plugin.my_types.push(type_inst); var behavior_type = new behavior_plugin.Type(behavior_plugin, type_inst); behavior_type.name = b[0]; behavior_type.sid = b[2]; behavior_type.onCreate(); cr.seal(behavior_type); type_inst.behaviors.push(behavior_type); } type_inst.global = m[9]; type_inst.isOnLoaderLayout = m[10]; type_inst.effect_types = []; for (j = 0, lenj = m[12].length; j < lenj; j++) { type_inst.effect_types.push({ id: m[12][j][0], name: m[12][j][1], shaderindex: -1, preservesOpaqueness: false, active: true, index: j }); } type_inst.tile_poly_data = m[13]; if (!this.uses_loader_layout || type_inst.is_family || type_inst.isOnLoaderLayout || !plugin.is_world) { type_inst.onCreate(); cr.seal(type_inst); } if (type_inst.name) this.types[type_inst.name] = type_inst; this.types_by_index.push(type_inst); if (plugin.singleglobal) { var instance = new plugin.Instance(type_inst); instance.uid = this.next_uid++; instance.puid = this.next_puid++; instance.iid = 0; instance.get_iid = cr.inst_get_iid; instance.toString = cr.inst_toString; instance.properties = m[14]; instance.onCreate(); cr.seal(instance); type_inst.instances.push(instance); this.objectsByUid[instance.uid.toString()] = instance; } } for (i = 0, len = pm[4].length; i < len; i++) { var familydata = pm[4][i]; var familytype = this.types_by_index[familydata[0]]; var familymember; for (j = 1, lenj = familydata.length; j < lenj; j++) { familymember = this.types_by_index[familydata[j]]; familymember.families.push(familytype); familytype.members.push(familymember); } } for (i = 0, len = pm[28].length; i < len; i++) { var containerdata = pm[28][i]; var containertypes = []; for (j = 0, lenj = containerdata.length; j < lenj; j++) containertypes.push(this.types_by_index[containerdata[j]]); for (j = 0, lenj = containertypes.length; j < lenj; j++) { containertypes[j].is_contained = true; containertypes[j].container = containertypes; } } if (this.family_count > 0) { for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; if (t.is_family || !t.families.length) continue; t.family_var_map = new Array(this.family_count); t.family_beh_map = new Array(this.family_count); t.family_fx_map = new Array(this.family_count); var all_fx = []; var varsum = 0; var behsum = 0; var fxsum = 0; for (j = 0, lenj = t.families.length; j < lenj; j++) { f = t.families[j]; t.family_var_map[f.family_index] = varsum; varsum += f.vars_count; t.family_beh_map[f.family_index] = behsum; behsum += f.behs_count; t.family_fx_map[f.family_index] = fxsum; fxsum += f.fx_count; for (k = 0, lenk = f.effect_types.length; k < lenk; k++) all_fx.push(cr.shallowCopy({}, f.effect_types[k])); } t.effect_types = all_fx.concat(t.effect_types); for (j = 0, lenj = t.effect_types.length; j < lenj; j++) t.effect_types[j].index = j; } } for (i = 0, len = pm[5].length; i < len; i++) { m = pm[5][i]; var layout = new cr.layout(this, m); cr.seal(layout); this.layouts[layout.name] = layout; this.layouts_by_index.push(layout); } for (i = 0, len = pm[6].length; i < len; i++) { m = pm[6][i]; var sheet = new cr.eventsheet(this, m); cr.seal(sheet); this.eventsheets[sheet.name] = sheet; this.eventsheets_by_index.push(sheet); } for (i = 0, len = this.eventsheets_by_index.length; i < len; i++) this.eventsheets_by_index[i].postInit(); for (i = 0, len = this.eventsheets_by_index.length; i < len; i++) this.eventsheets_by_index[i].updateDeepIncludes(); for (i = 0, len = this.triggers_to_postinit.length; i < len; i++) this.triggers_to_postinit[i].postInit(); cr.clearArray(this.triggers_to_postinit) this.audio_to_preload = pm[7]; this.files_subfolder = pm[8]; this.pixel_rounding = pm[9]; this.aspect_scale = 1.0; this.enableWebGL = pm[13]; this.linearSampling = pm[14]; this.clearBackground = pm[15]; this.versionstr = pm[16]; this.useHighDpi = pm[17]; this.orientations = pm[20]; // 0 = any, 1 = portrait, 2 = landscape this.autoLockOrientation = (this.orientations > 0); this.pauseOnBlur = pm[22]; this.wantFullscreenScalingQuality = pm[23]; // false = low quality, true = high quality this.fullscreenScalingQuality = this.wantFullscreenScalingQuality; this.downscalingQuality = pm[24]; // 0 = low (mips off), 1 = medium (mips on, dense spritesheet), 2 = high (mips on, sparse spritesheet) this.preloadSounds = pm[25]; // 0 = no, 1 = yes this.projectName = pm[26]; this.enableFrontToBack = pm[27] && !this.isIE; // front-to-back renderer disabled in IE (but not Edge) this.start_time = Date.now(); cr.clearArray(this.objectRefTable); this.initRendererAndLoader(); }; var anyImageHadError = false; Runtime.prototype.waitForImageLoad = function (img_, src_) { img_["cocoonLazyLoad"] = true; img_.onerror = function (e) { img_.c2error = true; anyImageHadError = true; if (console && console.error) console.error("Error loading image '" + img_.src + "': ", e); }; if (this.isEjecta) { img_.src = src_; } else if (!img_.src) { if (typeof XAPKReader !== "undefined") { XAPKReader.get(src_, function (expanded_url) { img_.src = expanded_url; }, function (e) { img_.c2error = true; anyImageHadError = true; if (console && console.error) console.error("Error extracting image '" + src_ + "' from expansion file: ", e); }); } else { img_.crossOrigin = "anonymous"; // required for Arcade sandbox compatibility this.setImageSrc(img_, src_); // work around WKWebView problems } } this.wait_for_textures.push(img_); }; Runtime.prototype.findWaitingTexture = function (src_) { var i, len; for (i = 0, len = this.wait_for_textures.length; i < len; i++) { if (this.wait_for_textures[i].cr_src === src_) return this.wait_for_textures[i]; } return null; }; var audio_preload_totalsize = 0; var audio_preload_started = false; Runtime.prototype.getready = function () { if (!this.audioInstance) return; audio_preload_totalsize = this.audioInstance.setPreloadList(this.audio_to_preload); }; Runtime.prototype.areAllTexturesAndSoundsLoaded = function () { var totalsize = audio_preload_totalsize; var completedsize = 0; var audiocompletedsize = 0; var ret = true; var i, len, img; for (i = 0, len = this.wait_for_textures.length; i < len; i++) { img = this.wait_for_textures[i]; var filesize = img.cr_filesize; if (!filesize || filesize <= 0) filesize = 50000; totalsize += filesize; if (!!img.src && (img.complete || img["loaded"]) && !img.c2error) completedsize += filesize; else ret = false; // not all textures loaded } if (ret && this.preloadSounds && this.audioInstance) { if (!audio_preload_started) { this.audioInstance.startPreloads(); audio_preload_started = true; } audiocompletedsize = this.audioInstance.getPreloadedSize(); completedsize += audiocompletedsize; if (audiocompletedsize < audio_preload_totalsize) ret = false; // not done yet } if (totalsize == 0) this.progress = 1; // indicate to C2 splash loader that it can finish now else this.progress = (completedsize / totalsize); return ret; }; var isC2SplashDone = false; Runtime.prototype.go = function () { if (!this.ctx && !this.glwrap) return; var ctx = this.ctx || this.overlay_ctx; if (this.overlay_canvas) this.positionOverlayCanvas(); var curwidth = window.innerWidth; var curheight = window.innerHeight; if (this.lastWindowWidth !== curwidth || this.lastWindowHeight !== curheight) { this["setSize"](curwidth, curheight); } this.progress = 0; this.last_progress = -1; var self = this; if (this.areAllTexturesAndSoundsLoaded() && (this.loaderstyle !== 4 || isC2SplashDone)) { this.go_loading_finished(); } else { var ms_elapsed = Date.now() - this.start_time; if (ctx) { var overlay_width = this.width; var overlay_height = this.height; var dpr = this.devicePixelRatio; if (this.loaderstyle < 3 && (this.isCocoonJs || (ms_elapsed >= 500 && this.last_progress != this.progress))) { ctx.clearRect(0, 0, overlay_width, overlay_height); var mx = overlay_width / 2; var my = overlay_height / 2; var haslogo = (this.loaderstyle === 0 && this.loaderlogos.logo.complete); var hlw = 40 * dpr; var hlh = 0; var logowidth = 80 * dpr; var logoheight; if (haslogo) { var loaderLogoImage = this.loaderlogos.logo; logowidth = loaderLogoImage.width * dpr; logoheight = loaderLogoImage.height * dpr; hlw = logowidth / 2; hlh = logoheight / 2; ctx.drawImage(loaderLogoImage, cr.floor(mx - hlw), cr.floor(my - hlh), logowidth, logoheight); } if (this.loaderstyle <= 1) { my += hlh + (haslogo ? 12 * dpr : 0); mx -= hlw; mx = cr.floor(mx) + 0.5; my = cr.floor(my) + 0.5; ctx.fillStyle = anyImageHadError ? "red" : "DodgerBlue"; ctx.fillRect(mx, my, Math.floor(logowidth * this.progress), 6 * dpr); ctx.strokeStyle = "black"; ctx.strokeRect(mx, my, logowidth, 6 * dpr); ctx.strokeStyle = "white"; ctx.strokeRect(mx - 1 * dpr, my - 1 * dpr, logowidth + 2 * dpr, 8 * dpr); } else if (this.loaderstyle === 2) { ctx.font = (this.isEjecta ? "12pt ArialMT" : "12pt Arial"); ctx.fillStyle = anyImageHadError ? "#f00" : "#999"; ctx.textBaseLine = "middle"; var percent_text = Math.round(this.progress * 100) + "%"; var text_dim = ctx.measureText ? ctx.measureText(percent_text) : null; var text_width = text_dim ? text_dim.width : 0; ctx.fillText(percent_text, mx - (text_width / 2), my); } this.last_progress = this.progress; } else if (this.loaderstyle === 4) { this.draw_c2_splash_loader(ctx); if (raf) raf(function() { self.go(); }); else setTimeout(function() { self.go(); }, 16); return; } } setTimeout(function() { self.go(); }, (this.isCocoonJs ? 10 : 100)); } }; var splashStartTime = -1; var splashFadeInDuration = 300; var splashFadeOutDuration = 300; var splashAfterFadeOutWait = (typeof cr_is_preview === "undefined" ? 200 : 0); var splashIsFadeIn = true; var splashIsFadeOut = false; var splashFadeInFinish = 0; var splashFadeOutStart = 0; var splashMinDisplayTime = (typeof cr_is_preview === "undefined" ? 3000 : 0); var renderViaCanvas = null; var renderViaCtx = null; var splashFrameNumber = 0; function maybeCreateRenderViaCanvas(w, h) { if (!renderViaCanvas || renderViaCanvas.width !== w || renderViaCanvas.height !== h) { renderViaCanvas = document.createElement("canvas"); renderViaCanvas.width = w; renderViaCanvas.height = h; renderViaCtx = renderViaCanvas.getContext("2d"); } }; function mipImage(arr, size) { if (size <= 128) return arr[3]; else if (size <= 256) return arr[2]; else if (size <= 512) return arr[1]; else return arr[0]; }; Runtime.prototype.draw_c2_splash_loader = function(ctx) { if (isC2SplashDone) return; var w = Math.ceil(this.width); var h = Math.ceil(this.height); var dpr = this.devicePixelRatio; var logoimages = this.loaderlogos.logo; var poweredimages = this.loaderlogos.powered; var websiteimages = this.loaderlogos.website; for (var i = 0; i < 4; ++i) { if (!logoimages[i].complete || !poweredimages[i].complete || !websiteimages[i].complete) return; } if (splashFrameNumber === 0) splashStartTime = Date.now(); var nowTime = Date.now(); var isRenderingVia = false; var renderToCtx = ctx; var drawW, drawH; if (splashIsFadeIn || splashIsFadeOut) { ctx.clearRect(0, 0, w, h); maybeCreateRenderViaCanvas(w, h); renderToCtx = renderViaCtx; isRenderingVia = true; if (splashIsFadeIn && splashFrameNumber === 1) splashStartTime = Date.now(); } else { ctx.globalAlpha = 1; } renderToCtx.fillStyle = "#333333"; renderToCtx.fillRect(0, 0, w, h); if (this.cssHeight > 256) { drawW = cr.clamp(h * 0.22, 105, w * 0.6); drawH = drawW * 0.25; renderToCtx.drawImage(mipImage(poweredimages, drawW), w * 0.5 - drawW/2, h * 0.2 - drawH/2, drawW, drawH); drawW = Math.min(h * 0.395, w * 0.95); drawH = drawW; renderToCtx.drawImage(mipImage(logoimages, drawW), w * 0.5 - drawW/2, h * 0.485 - drawH/2, drawW, drawH); drawW = cr.clamp(h * 0.22, 105, w * 0.6); drawH = drawW * 0.25; renderToCtx.drawImage(mipImage(websiteimages, drawW), w * 0.5 - drawW/2, h * 0.868 - drawH/2, drawW, drawH); renderToCtx.fillStyle = "#3C3C3C"; drawW = w; drawH = Math.max(h * 0.005, 2); renderToCtx.fillRect(0, h * 0.8 - drawH/2, drawW, drawH); renderToCtx.fillStyle = anyImageHadError ? "red" : "#E0FF65"; drawW = w * this.progress; renderToCtx.fillRect(w * 0.5 - drawW/2, h * 0.8 - drawH/2, drawW, drawH); } else { drawW = h * 0.55; drawH = drawW; renderToCtx.drawImage(mipImage(logoimages, drawW), w * 0.5 - drawW/2, h * 0.45 - drawH/2, drawW, drawH); renderToCtx.fillStyle = "#3C3C3C"; drawW = w; drawH = Math.max(h * 0.005, 2); renderToCtx.fillRect(0, h * 0.85 - drawH/2, drawW, drawH); renderToCtx.fillStyle = anyImageHadError ? "red" : "#E0FF65"; drawW = w * this.progress; renderToCtx.fillRect(w * 0.5 - drawW/2, h * 0.85 - drawH/2, drawW, drawH); } if (isRenderingVia) { if (splashIsFadeIn) { if (splashFrameNumber === 0) ctx.globalAlpha = 0; else ctx.globalAlpha = Math.min((nowTime - splashStartTime) / splashFadeInDuration, 1); } else if (splashIsFadeOut) { ctx.globalAlpha = Math.max(1 - (nowTime - splashFadeOutStart) / splashFadeOutDuration, 0); } ctx.drawImage(renderViaCanvas, 0, 0, w, h); } if (splashIsFadeIn && nowTime - splashStartTime >= splashFadeInDuration && splashFrameNumber >= 2) { splashIsFadeIn = false; splashFadeInFinish = nowTime; } if (!splashIsFadeIn && nowTime - splashFadeInFinish >= splashMinDisplayTime && !splashIsFadeOut && this.progress >= 1) { splashIsFadeOut = true; splashFadeOutStart = nowTime; } if ((splashIsFadeOut && nowTime - splashFadeOutStart >= splashFadeOutDuration + splashAfterFadeOutWait) || (typeof cr_is_preview !== "undefined" && this.progress >= 1 && Date.now() - splashStartTime < 500)) { isC2SplashDone = true; splashIsFadeIn = false; splashIsFadeOut = false; renderViaCanvas = null; renderViaCtx = null; this.loaderlogos = null; } ++splashFrameNumber; }; Runtime.prototype.go_loading_finished = function () { if (this.overlay_canvas) { this.canvas.parentNode.removeChild(this.overlay_canvas); this.overlay_ctx = null; this.overlay_canvas = null; } this.start_time = Date.now(); this.last_fps_time = cr.performance_now(); // for counting framerate var i, len, t; if (this.uses_loader_layout) { for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; if (!t.is_family && !t.isOnLoaderLayout && t.plugin.is_world) { t.onCreate(); cr.seal(t); } } } else this.isloading = false; for (i = 0, len = this.layouts_by_index.length; i < len; i++) { this.layouts_by_index[i].createGlobalNonWorlds(); } if (this.fullscreen_mode >= 2) { var orig_aspect = this.original_width / this.original_height; var cur_aspect = this.width / this.height; if ((this.fullscreen_mode !== 2 && cur_aspect > orig_aspect) || (this.fullscreen_mode === 2 && cur_aspect < orig_aspect)) this.aspect_scale = this.height / this.original_height; else this.aspect_scale = this.width / this.original_width; } if (this.first_layout) this.layouts[this.first_layout].startRunning(); else this.layouts_by_index[0].startRunning(); ; if (!this.uses_loader_layout) { this.loadingprogress = 1; this.trigger(cr.system_object.prototype.cnds.OnLoadFinished, null); if (window["C2_RegisterSW"]) // note not all platforms use SW window["C2_RegisterSW"](); } if (navigator["splashscreen"] && navigator["splashscreen"]["hide"]) navigator["splashscreen"]["hide"](); for (i = 0, len = this.types_by_index.length; i < len; i++) { t = this.types_by_index[i]; if (t.onAppBegin) t.onAppBegin(); } if (document["hidden"] || document["webkitHidden"] || document["mozHidden"] || document["msHidden"]) { window["cr_setSuspended"](true); // stop rendering } else { this.tick(false); } if (this.isDirectCanvas) AppMobi["webview"]["execute"]("onGameReady();"); }; Runtime.prototype.tick = function (background_wake, timestamp, debug_step) { if (!this.running_layout) return; var nowtime = cr.performance_now(); var logic_start = nowtime; if (!debug_step && this.isSuspended && !background_wake) return; if (!background_wake) { if (raf) this.raf_id = raf(this.tickFunc); else { this.timeout_id = setTimeout(this.tickFunc, this.isMobile ? 1 : 16); } } var raf_time = timestamp || nowtime; var fsmode = this.fullscreen_mode; var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || document["fullScreen"] || !!document["msFullscreenElement"]) && !this.isCordova; if ((isfullscreen || this.isNodeFullscreen) && this.fullscreen_scaling > 0) fsmode = this.fullscreen_scaling; if (fsmode > 0) // r222: experimentally enabling this workaround for all platforms { var curwidth = window.innerWidth; var curheight = window.innerHeight; if (this.lastWindowWidth !== curwidth || this.lastWindowHeight !== curheight) { this["setSize"](curwidth, curheight); } } if (!this.isDomFree) { if (isfullscreen) { if (!this.firstInFullscreen) { this.fullscreenOldMarginCss = jQuery(this.canvas).css("margin") || "0"; this.firstInFullscreen = true; } if (!this.isChrome && !this.isNWjs) { jQuery(this.canvas).css({ "margin-left": "" + Math.floor((screen.width - (this.width / this.devicePixelRatio)) / 2) + "px", "margin-top": "" + Math.floor((screen.height - (this.height / this.devicePixelRatio)) / 2) + "px" }); } } else { if (this.firstInFullscreen) { if (!this.isChrome && !this.isNWjs) { jQuery(this.canvas).css("margin", this.fullscreenOldMarginCss); } this.fullscreenOldMarginCss = ""; this.firstInFullscreen = false; if (this.fullscreen_mode === 0) { this["setSize"](Math.round(this.oldWidth / this.devicePixelRatio), Math.round(this.oldHeight / this.devicePixelRatio), true); } } else { this.oldWidth = this.width; this.oldHeight = this.height; } } } if (this.isloading) { var done = this.areAllTexturesAndSoundsLoaded(); // updates this.progress this.loadingprogress = this.progress; if (done) { this.isloading = false; this.progress = 1; this.trigger(cr.system_object.prototype.cnds.OnLoadFinished, null); if (window["C2_RegisterSW"]) window["C2_RegisterSW"](); } } this.logic(raf_time); if ((this.redraw || this.isCocoonJs) && !this.is_WebGL_context_lost && !this.suspendDrawing && !background_wake) { this.redraw = false; if (this.glwrap) this.drawGL(); else this.draw(); if (this.snapshotCanvas) { if (this.canvas && this.canvas.toDataURL) { this.snapshotData = this.canvas.toDataURL(this.snapshotCanvas[0], this.snapshotCanvas[1]); if (window["cr_onSnapshot"]) window["cr_onSnapshot"](this.snapshotData); this.trigger(cr.system_object.prototype.cnds.OnCanvasSnapshot, null); } this.snapshotCanvas = null; } } if (!this.hit_breakpoint) { this.tickcount++; this.execcount++; this.framecount++; } this.logictime += cr.performance_now() - logic_start; }; Runtime.prototype.logic = function (cur_time) { var i, leni, j, lenj, k, lenk, type, inst, binst; if (cur_time - this.last_fps_time >= 1000) // every 1 second { this.last_fps_time += 1000; if (cur_time - this.last_fps_time >= 1000) this.last_fps_time = cur_time; this.fps = this.framecount; this.framecount = 0; this.cpuutilisation = this.logictime; this.logictime = 0; } var wallDt = 0; if (this.last_tick_time !== 0) { var ms_diff = cur_time - this.last_tick_time; if (ms_diff < 0) ms_diff = 0; wallDt = ms_diff / 1000.0; // dt measured in seconds this.dt1 = wallDt; if (this.dt1 > 0.5) this.dt1 = 0; else if (this.dt1 > 1 / this.minimumFramerate) this.dt1 = 1 / this.minimumFramerate; } this.last_tick_time = cur_time; this.dt = this.dt1 * this.timescale; this.kahanTime.add(this.dt); this.wallTime.add(wallDt); // prevent min/max framerate affecting wall clock var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || document["fullScreen"] || !!document["msFullscreenElement"] || this.isNodeFullscreen) && !this.isCordova; if (this.fullscreen_mode >= 2 /* scale */ || (isfullscreen && this.fullscreen_scaling > 0)) { var orig_aspect = this.original_width / this.original_height; var cur_aspect = this.width / this.height; var mode = this.fullscreen_mode; if (isfullscreen && this.fullscreen_scaling > 0) mode = this.fullscreen_scaling; if ((mode !== 2 && cur_aspect > orig_aspect) || (mode === 2 && cur_aspect < orig_aspect)) { this.aspect_scale = this.height / this.original_height; } else { this.aspect_scale = this.width / this.original_width; } if (this.running_layout) { this.running_layout.scrollToX(this.running_layout.scrollX); this.running_layout.scrollToY(this.running_layout.scrollY); } } else this.aspect_scale = (this.isRetina ? this.devicePixelRatio : 1); this.ClearDeathRow(); this.isInOnDestroy++; this.system.runWaits(); // prevent instance list changing this.isInOnDestroy--; this.ClearDeathRow(); // allow instance list changing this.isInOnDestroy++; var tickarr = this.objects_to_pretick.valuesRef(); for (i = 0, leni = tickarr.length; i < leni; i++) tickarr[i].pretick(); for (i = 0, leni = this.types_by_index.length; i < leni; i++) { type = this.types_by_index[i]; if (type.is_family || (!type.behaviors.length && !type.families.length)) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { inst.behavior_insts[k].tick(); } } } for (i = 0, leni = this.types_by_index.length; i < leni; i++) { type = this.types_by_index[i]; if (type.is_family || (!type.behaviors.length && !type.families.length)) continue; // type doesn't have any behaviors for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { binst = inst.behavior_insts[k]; if (binst.posttick) binst.posttick(); } } } tickarr = this.objects_to_tick.valuesRef(); for (i = 0, leni = tickarr.length; i < leni; i++) tickarr[i].tick(); this.isInOnDestroy--; // end preventing instance lists from being changed this.handleSaveLoad(); // save/load now if queued i = 0; while (this.changelayout && i++ < 10) { this.doChangeLayout(this.changelayout); } for (i = 0, leni = this.eventsheets_by_index.length; i < leni; i++) this.eventsheets_by_index[i].hasRun = false; if (this.running_layout.event_sheet) this.running_layout.event_sheet.run(); cr.clearArray(this.registered_collisions); this.layout_first_tick = false; this.isInOnDestroy++; // prevent instance lists from being changed for (i = 0, leni = this.types_by_index.length; i < leni; i++) { type = this.types_by_index[i]; if (type.is_family || (!type.behaviors.length && !type.families.length)) continue; // type doesn't have any behaviors for (j = 0, lenj = type.instances.length; j < lenj; j++) { var inst = type.instances[j]; for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { binst = inst.behavior_insts[k]; if (binst.tick2) binst.tick2(); } } } tickarr = this.objects_to_tick2.valuesRef(); for (i = 0, leni = tickarr.length; i < leni; i++) tickarr[i].tick2(); this.isInOnDestroy--; // end preventing instance lists from being changed }; Runtime.prototype.onWindowBlur = function () { var i, leni, j, lenj, k, lenk, type, inst, binst; for (i = 0, leni = this.types_by_index.length; i < leni; i++) { type = this.types_by_index[i]; if (type.is_family) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; if (inst.onWindowBlur) inst.onWindowBlur(); if (!inst.behavior_insts) continue; // single-globals don't have behavior_insts for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { binst = inst.behavior_insts[k]; if (binst.onWindowBlur) binst.onWindowBlur(); } } } }; Runtime.prototype.doChangeLayout = function (changeToLayout) { var prev_layout = this.running_layout; this.running_layout.stopRunning(); var i, len, j, lenj, k, lenk, type, inst, binst; if (this.glwrap) { for (i = 0, len = this.types_by_index.length; i < len; i++) { type = this.types_by_index[i]; if (type.is_family) continue; if (type.unloadTextures && (!type.global || type.instances.length === 0) && changeToLayout.initial_types.indexOf(type) === -1) { type.unloadTextures(); } } } if (prev_layout == changeToLayout) cr.clearArray(this.system.waits); cr.clearArray(this.registered_collisions); this.runLayoutChangeMethods(true); changeToLayout.startRunning(); this.runLayoutChangeMethods(false); this.redraw = true; this.layout_first_tick = true; this.ClearDeathRow(); }; Runtime.prototype.runLayoutChangeMethods = function (isBeforeChange) { var i, len, beh, type, j, lenj, inst, k, lenk, binst; for (i = 0, len = this.behaviors.length; i < len; i++) { beh = this.behaviors[i]; if (isBeforeChange) { if (beh.onBeforeLayoutChange) beh.onBeforeLayoutChange(); } else { if (beh.onLayoutChange) beh.onLayoutChange(); } } for (i = 0, len = this.types_by_index.length; i < len; i++) { type = this.types_by_index[i]; if (!type.global && !type.plugin.singleglobal) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; if (isBeforeChange) { if (inst.onBeforeLayoutChange) inst.onBeforeLayoutChange(); } else { if (inst.onLayoutChange) inst.onLayoutChange(); } if (inst.behavior_insts) { for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { binst = inst.behavior_insts[k]; if (isBeforeChange) { if (binst.onBeforeLayoutChange) binst.onBeforeLayoutChange(); } else { if (binst.onLayoutChange) binst.onLayoutChange(); } } } } } }; Runtime.prototype.pretickMe = function (inst) { this.objects_to_pretick.add(inst); }; Runtime.prototype.unpretickMe = function (inst) { this.objects_to_pretick.remove(inst); }; Runtime.prototype.tickMe = function (inst) { this.objects_to_tick.add(inst); }; Runtime.prototype.untickMe = function (inst) { this.objects_to_tick.remove(inst); }; Runtime.prototype.tick2Me = function (inst) { this.objects_to_tick2.add(inst); }; Runtime.prototype.untick2Me = function (inst) { this.objects_to_tick2.remove(inst); }; Runtime.prototype.getDt = function (inst) { if (!inst || inst.my_timescale === -1.0) return this.dt; return this.dt1 * inst.my_timescale; }; Runtime.prototype.draw = function () { this.running_layout.draw(this.ctx); if (this.isDirectCanvas) this.ctx["present"](); }; Runtime.prototype.drawGL = function () { if (this.enableFrontToBack) { this.earlyz_index = 1; // start from front, 1-based to avoid exactly equalling near plane Z value this.running_layout.drawGL_earlyZPass(this.glwrap); } this.running_layout.drawGL(this.glwrap); this.glwrap.present(); }; Runtime.prototype.addDestroyCallback = function (f) { if (f) this.destroycallbacks.push(f); }; Runtime.prototype.removeDestroyCallback = function (f) { cr.arrayFindRemove(this.destroycallbacks, f); }; Runtime.prototype.getObjectByUID = function (uid_) { ; var uidstr = uid_.toString(); if (this.objectsByUid.hasOwnProperty(uidstr)) return this.objectsByUid[uidstr]; else return null; }; var objectset_cache = []; function alloc_objectset() { if (objectset_cache.length) return objectset_cache.pop(); else return new cr.ObjectSet(); }; function free_objectset(s) { s.clear(); objectset_cache.push(s); }; Runtime.prototype.DestroyInstance = function (inst) { var i, len; var type = inst.type; var typename = type.name; var has_typename = this.deathRow.hasOwnProperty(typename); var obj_set = null; if (has_typename) { obj_set = this.deathRow[typename]; if (obj_set.contains(inst)) return; // already had DestroyInstance called } else { obj_set = alloc_objectset(); this.deathRow[typename] = obj_set; } obj_set.add(inst); this.hasPendingInstances = true; if (inst.is_contained) { for (i = 0, len = inst.siblings.length; i < len; i++) { this.DestroyInstance(inst.siblings[i]); } } if (this.isInClearDeathRow) obj_set.values_cache.push(inst); if (!this.isEndingLayout) { this.isInOnDestroy++; // support recursion this.trigger(Object.getPrototypeOf(inst.type.plugin).cnds.OnDestroyed, inst); this.isInOnDestroy--; } }; Runtime.prototype.ClearDeathRow = function () { if (!this.hasPendingInstances) return; var inst, type, instances; var i, j, leni, lenj, obj_set; this.isInClearDeathRow = true; for (i = 0, leni = this.createRow.length; i < leni; ++i) { inst = this.createRow[i]; type = inst.type; type.instances.push(inst); for (j = 0, lenj = type.families.length; j < lenj; ++j) { type.families[j].instances.push(inst); type.families[j].stale_iids = true; } } cr.clearArray(this.createRow); this.IterateDeathRow(); // moved to separate function so for-in performance doesn't hobble entire function cr.wipe(this.deathRow); // all objectsets have already been recycled this.isInClearDeathRow = false; this.hasPendingInstances = false; }; Runtime.prototype.IterateDeathRow = function () { for (var p in this.deathRow) { if (this.deathRow.hasOwnProperty(p)) { this.ClearDeathRowForType(this.deathRow[p]); } } }; Runtime.prototype.ClearDeathRowForType = function (obj_set) { var arr = obj_set.valuesRef(); // get array of items from set ; var type = arr[0].type; ; ; var i, len, j, lenj, w, f, layer_instances, inst; cr.arrayRemoveAllFromObjectSet(type.instances, obj_set); type.stale_iids = true; if (type.instances.length === 0) type.any_instance_parallaxed = false; for (i = 0, len = type.families.length; i < len; ++i) { f = type.families[i]; cr.arrayRemoveAllFromObjectSet(f.instances, obj_set); f.stale_iids = true; } for (i = 0, len = this.system.waits.length; i < len; ++i) { w = this.system.waits[i]; if (w.sols.hasOwnProperty(type.index)) cr.arrayRemoveAllFromObjectSet(w.sols[type.index].insts, obj_set); if (!type.is_family) { for (j = 0, lenj = type.families.length; j < lenj; ++j) { f = type.families[j]; if (w.sols.hasOwnProperty(f.index)) cr.arrayRemoveAllFromObjectSet(w.sols[f.index].insts, obj_set); } } } var first_layer = arr[0].layer; if (first_layer) { if (first_layer.useRenderCells) { layer_instances = first_layer.instances; for (i = 0, len = layer_instances.length; i < len; ++i) { inst = layer_instances[i]; if (!obj_set.contains(inst)) continue; // not destroying this instance inst.update_bbox(); first_layer.render_grid.update(inst, inst.rendercells, null); inst.rendercells.set(0, 0, -1, -1); } } cr.arrayRemoveAllFromObjectSet(first_layer.instances, obj_set); first_layer.setZIndicesStaleFrom(0); } for (i = 0; i < arr.length; ++i) // check array length every time in case it changes { this.ClearDeathRowForSingleInstance(arr[i], type); } free_objectset(obj_set); this.redraw = true; }; Runtime.prototype.ClearDeathRowForSingleInstance = function (inst, type) { var i, len, binst; for (i = 0, len = this.destroycallbacks.length; i < len; ++i) this.destroycallbacks[i](inst); if (inst.collcells) { type.collision_grid.update(inst, inst.collcells, null); } var layer = inst.layer; if (layer) { layer.removeFromInstanceList(inst, true); // remove from both instance list and render grid } if (inst.behavior_insts) { for (i = 0, len = inst.behavior_insts.length; i < len; ++i) { binst = inst.behavior_insts[i]; if (binst.onDestroy) binst.onDestroy(); binst.behavior.my_instances.remove(inst); } } this.objects_to_pretick.remove(inst); this.objects_to_tick.remove(inst); this.objects_to_tick2.remove(inst); if (inst.onDestroy) inst.onDestroy(); if (this.objectsByUid.hasOwnProperty(inst.uid.toString())) delete this.objectsByUid[inst.uid.toString()]; this.objectcount--; if (type.deadCache.length < 100) type.deadCache.push(inst); }; Runtime.prototype.createInstance = function (type, layer, sx, sy) { if (type.is_family) { var i = cr.floor(Math.random() * type.members.length); return this.createInstance(type.members[i], layer, sx, sy); } if (!type.default_instance) { return null; } return this.createInstanceFromInit(type.default_instance, layer, false, sx, sy, false); }; var all_behaviors = []; Runtime.prototype.createInstanceFromInit = function (initial_inst, layer, is_startup_instance, sx, sy, skip_siblings) { var i, len, j, lenj, p, effect_fallback, x, y; if (!initial_inst) return null; var type = this.types_by_index[initial_inst[1]]; ; ; var is_world = type.plugin.is_world; ; if (this.isloading && is_world && !type.isOnLoaderLayout) return null; if (is_world && !this.glwrap && initial_inst[0][11] === 11) return null; var original_layer = layer; if (!is_world) layer = null; var inst; if (type.deadCache.length) { inst = type.deadCache.pop(); inst.recycled = true; type.plugin.Instance.call(inst, type); } else { inst = new type.plugin.Instance(type); inst.recycled = false; } if (is_startup_instance && !skip_siblings && !this.objectsByUid.hasOwnProperty(initial_inst[2].toString())) inst.uid = initial_inst[2]; else inst.uid = this.next_uid++; this.objectsByUid[inst.uid.toString()] = inst; inst.puid = this.next_puid++; inst.iid = type.instances.length; for (i = 0, len = this.createRow.length; i < len; ++i) { if (this.createRow[i].type === type) inst.iid++; } inst.get_iid = cr.inst_get_iid; inst.toString = cr.inst_toString; var initial_vars = initial_inst[3]; if (inst.recycled) { cr.wipe(inst.extra); } else { inst.extra = {}; if (typeof cr_is_preview !== "undefined") { inst.instance_var_names = []; inst.instance_var_names.length = initial_vars.length; for (i = 0, len = initial_vars.length; i < len; i++) inst.instance_var_names[i] = initial_vars[i][1]; } inst.instance_vars = []; inst.instance_vars.length = initial_vars.length; } for (i = 0, len = initial_vars.length; i < len; i++) inst.instance_vars[i] = initial_vars[i][0]; if (is_world) { var wm = initial_inst[0]; ; inst.x = cr.is_undefined(sx) ? wm[0] : sx; inst.y = cr.is_undefined(sy) ? wm[1] : sy; inst.z = wm[2]; inst.width = wm[3]; inst.height = wm[4]; inst.depth = wm[5]; inst.angle = wm[6]; inst.opacity = wm[7]; inst.hotspotX = wm[8]; inst.hotspotY = wm[9]; inst.blend_mode = wm[10]; effect_fallback = wm[11]; if (!this.glwrap && type.effect_types.length) // no WebGL renderer and shaders used inst.blend_mode = effect_fallback; // use fallback blend mode - destroy mode was handled above inst.compositeOp = cr.effectToCompositeOp(inst.blend_mode); if (this.gl) cr.setGLBlend(inst, inst.blend_mode, this.gl); if (inst.recycled) { for (i = 0, len = wm[12].length; i < len; i++) { for (j = 0, lenj = wm[12][i].length; j < lenj; j++) inst.effect_params[i][j] = wm[12][i][j]; } inst.bbox.set(0, 0, 0, 0); inst.collcells.set(0, 0, -1, -1); inst.rendercells.set(0, 0, -1, -1); inst.bquad.set_from_rect(inst.bbox); cr.clearArray(inst.bbox_changed_callbacks); } else { inst.effect_params = wm[12].slice(0); for (i = 0, len = inst.effect_params.length; i < len; i++) inst.effect_params[i] = wm[12][i].slice(0); inst.active_effect_types = []; inst.active_effect_flags = []; inst.active_effect_flags.length = type.effect_types.length; inst.bbox = new cr.rect(0, 0, 0, 0); inst.collcells = new cr.rect(0, 0, -1, -1); inst.rendercells = new cr.rect(0, 0, -1, -1); inst.bquad = new cr.quad(); inst.bbox_changed_callbacks = []; inst.set_bbox_changed = cr.set_bbox_changed; inst.add_bbox_changed_callback = cr.add_bbox_changed_callback; inst.contains_pt = cr.inst_contains_pt; inst.update_bbox = cr.update_bbox; inst.update_render_cell = cr.update_render_cell; inst.update_collision_cell = cr.update_collision_cell; inst.get_zindex = cr.inst_get_zindex; } inst.tilemap_exists = false; inst.tilemap_width = 0; inst.tilemap_height = 0; inst.tilemap_data = null; if (wm.length === 14) { inst.tilemap_exists = true; inst.tilemap_width = wm[13][0]; inst.tilemap_height = wm[13][1]; inst.tilemap_data = wm[13][2]; } for (i = 0, len = type.effect_types.length; i < len; i++) inst.active_effect_flags[i] = true; inst.shaders_preserve_opaqueness = true; inst.updateActiveEffects = cr.inst_updateActiveEffects; inst.updateActiveEffects(); inst.uses_shaders = !!inst.active_effect_types.length; inst.bbox_changed = true; inst.cell_changed = true; type.any_cell_changed = true; inst.visible = true; inst.my_timescale = -1.0; inst.layer = layer; inst.zindex = layer.instances.length; // will be placed at top of current layer inst.earlyz_index = 0; if (typeof inst.collision_poly === "undefined") inst.collision_poly = null; inst.collisionsEnabled = true; this.redraw = true; } var initial_props, binst; cr.clearArray(all_behaviors); for (i = 0, len = type.families.length; i < len; i++) { all_behaviors.push.apply(all_behaviors, type.families[i].behaviors); } all_behaviors.push.apply(all_behaviors, type.behaviors); if (inst.recycled) { for (i = 0, len = all_behaviors.length; i < len; i++) { var btype = all_behaviors[i]; binst = inst.behavior_insts[i]; binst.recycled = true; btype.behavior.Instance.call(binst, btype, inst); initial_props = initial_inst[4][i]; for (j = 0, lenj = initial_props.length; j < lenj; j++) binst.properties[j] = initial_props[j]; binst.onCreate(); btype.behavior.my_instances.add(inst); } } else { inst.behavior_insts = []; for (i = 0, len = all_behaviors.length; i < len; i++) { var btype = all_behaviors[i]; var binst = new btype.behavior.Instance(btype, inst); binst.recycled = false; binst.properties = initial_inst[4][i].slice(0); binst.onCreate(); cr.seal(binst); inst.behavior_insts.push(binst); btype.behavior.my_instances.add(inst); } } initial_props = initial_inst[5]; if (inst.recycled) { for (i = 0, len = initial_props.length; i < len; i++) inst.properties[i] = initial_props[i]; } else inst.properties = initial_props.slice(0); this.createRow.push(inst); this.hasPendingInstances = true; if (layer) { ; layer.appendToInstanceList(inst, true); if (layer.parallaxX !== 1 || layer.parallaxY !== 1) type.any_instance_parallaxed = true; } this.objectcount++; if (type.is_contained) { inst.is_contained = true; if (inst.recycled) cr.clearArray(inst.siblings); else inst.siblings = []; // note: should not include self in siblings if (!is_startup_instance && !skip_siblings) // layout links initial instances { for (i = 0, len = type.container.length; i < len; i++) { if (type.container[i] === type) continue; if (!type.container[i].default_instance) { return null; } inst.siblings.push(this.createInstanceFromInit(type.container[i].default_instance, original_layer, false, is_world ? inst.x : sx, is_world ? inst.y : sy, true)); } for (i = 0, len = inst.siblings.length; i < len; i++) { inst.siblings[i].siblings.push(inst); for (j = 0; j < len; j++) { if (i !== j) inst.siblings[i].siblings.push(inst.siblings[j]); } } } } else { inst.is_contained = false; inst.siblings = null; } inst.onCreate(); if (!inst.recycled) cr.seal(inst); for (i = 0, len = inst.behavior_insts.length; i < len; i++) { if (inst.behavior_insts[i].postCreate) inst.behavior_insts[i].postCreate(); } return inst; }; Runtime.prototype.getLayerByName = function (layer_name) { var i, len; for (i = 0, len = this.running_layout.layers.length; i < len; i++) { var layer = this.running_layout.layers[i]; if (cr.equals_nocase(layer.name, layer_name)) return layer; } return null; }; Runtime.prototype.getLayerByNumber = function (index) { index = cr.floor(index); if (index < 0) index = 0; if (index >= this.running_layout.layers.length) index = this.running_layout.layers.length - 1; return this.running_layout.layers[index]; }; Runtime.prototype.getLayer = function (l) { if (cr.is_number(l)) return this.getLayerByNumber(l); else return this.getLayerByName(l.toString()); }; Runtime.prototype.clearSol = function (solModifiers) { var i, len; for (i = 0, len = solModifiers.length; i < len; i++) { solModifiers[i].getCurrentSol().select_all = true; } }; Runtime.prototype.pushCleanSol = function (solModifiers) { var i, len; for (i = 0, len = solModifiers.length; i < len; i++) { solModifiers[i].pushCleanSol(); } }; Runtime.prototype.pushCopySol = function (solModifiers) { var i, len; for (i = 0, len = solModifiers.length; i < len; i++) { solModifiers[i].pushCopySol(); } }; Runtime.prototype.popSol = function (solModifiers) { var i, len; for (i = 0, len = solModifiers.length; i < len; i++) { solModifiers[i].popSol(); } }; Runtime.prototype.updateAllCells = function (type) { if (!type.any_cell_changed) return; // all instances must already be up-to-date var i, len, instances = type.instances; for (i = 0, len = instances.length; i < len; ++i) { instances[i].update_collision_cell(); } var createRow = this.createRow; for (i = 0, len = createRow.length; i < len; ++i) { if (createRow[i].type === type) createRow[i].update_collision_cell(); } type.any_cell_changed = false; }; Runtime.prototype.getCollisionCandidates = function (layer, rtype, bbox, candidates) { var i, len, t; var is_parallaxed = (layer ? (layer.parallaxX !== 1 || layer.parallaxY !== 1) : false); if (rtype.is_family) { for (i = 0, len = rtype.members.length; i < len; ++i) { t = rtype.members[i]; if (is_parallaxed || t.any_instance_parallaxed) { cr.appendArray(candidates, t.instances); } else { this.updateAllCells(t); t.collision_grid.queryRange(bbox, candidates); } } } else { if (is_parallaxed || rtype.any_instance_parallaxed) { cr.appendArray(candidates, rtype.instances); } else { this.updateAllCells(rtype); rtype.collision_grid.queryRange(bbox, candidates); } } }; Runtime.prototype.getTypesCollisionCandidates = function (layer, types, bbox, candidates) { var i, len; for (i = 0, len = types.length; i < len; ++i) { this.getCollisionCandidates(layer, types[i], bbox, candidates); } }; Runtime.prototype.getSolidCollisionCandidates = function (layer, bbox, candidates) { var solid = this.getSolidBehavior(); if (!solid) return null; this.getTypesCollisionCandidates(layer, solid.my_types, bbox, candidates); }; Runtime.prototype.getJumpthruCollisionCandidates = function (layer, bbox, candidates) { var jumpthru = this.getJumpthruBehavior(); if (!jumpthru) return null; this.getTypesCollisionCandidates(layer, jumpthru.my_types, bbox, candidates); }; Runtime.prototype.testAndSelectCanvasPointOverlap = function (type, ptx, pty, inverted) { var sol = type.getCurrentSol(); var i, j, inst, len; var orblock = this.getCurrentEventStack().current_event.orblock; var lx, ly, arr; if (sol.select_all) { if (!inverted) { sol.select_all = false; cr.clearArray(sol.instances); // clear contents } for (i = 0, len = type.instances.length; i < len; i++) { inst = type.instances[i]; inst.update_bbox(); lx = inst.layer.canvasToLayer(ptx, pty, true); ly = inst.layer.canvasToLayer(ptx, pty, false); if (inst.contains_pt(lx, ly)) { if (inverted) return false; else sol.instances.push(inst); } else if (orblock) sol.else_instances.push(inst); } } else { j = 0; arr = (orblock ? sol.else_instances : sol.instances); for (i = 0, len = arr.length; i < len; i++) { inst = arr[i]; inst.update_bbox(); lx = inst.layer.canvasToLayer(ptx, pty, true); ly = inst.layer.canvasToLayer(ptx, pty, false); if (inst.contains_pt(lx, ly)) { if (inverted) return false; else if (orblock) sol.instances.push(inst); else { sol.instances[j] = sol.instances[i]; j++; } } } if (!inverted) arr.length = j; } type.applySolToContainer(); if (inverted) return true; // did not find anything overlapping else return sol.hasObjects(); }; Runtime.prototype.testOverlap = function (a, b) { if (!a || !b || a === b || !a.collisionsEnabled || !b.collisionsEnabled) return false; a.update_bbox(); b.update_bbox(); var layera = a.layer; var layerb = b.layer; var different_layers = (layera !== layerb && (layera.parallaxX !== layerb.parallaxX || layerb.parallaxY !== layerb.parallaxY || layera.scale !== layerb.scale || layera.angle !== layerb.angle || layera.zoomRate !== layerb.zoomRate)); var i, len, i2, i21, x, y, haspolya, haspolyb, polya, polyb; if (!different_layers) // same layers: easy check { if (!a.bbox.intersects_rect(b.bbox)) return false; if (!a.bquad.intersects_quad(b.bquad)) return false; if (a.tilemap_exists && b.tilemap_exists) return false; if (a.tilemap_exists) return this.testTilemapOverlap(a, b); if (b.tilemap_exists) return this.testTilemapOverlap(b, a); haspolya = (a.collision_poly && !a.collision_poly.is_empty()); haspolyb = (b.collision_poly && !b.collision_poly.is_empty()); if (!haspolya && !haspolyb) return true; if (haspolya) { a.collision_poly.cache_poly(a.width, a.height, a.angle); polya = a.collision_poly; } else { this.temp_poly.set_from_quad(a.bquad, a.x, a.y, a.width, a.height); polya = this.temp_poly; } if (haspolyb) { b.collision_poly.cache_poly(b.width, b.height, b.angle); polyb = b.collision_poly; } else { this.temp_poly.set_from_quad(b.bquad, b.x, b.y, b.width, b.height); polyb = this.temp_poly; } return polya.intersects_poly(polyb, b.x - a.x, b.y - a.y); } else // different layers: need to do full translated check { haspolya = (a.collision_poly && !a.collision_poly.is_empty()); haspolyb = (b.collision_poly && !b.collision_poly.is_empty()); if (haspolya) { a.collision_poly.cache_poly(a.width, a.height, a.angle); this.temp_poly.set_from_poly(a.collision_poly); } else { this.temp_poly.set_from_quad(a.bquad, a.x, a.y, a.width, a.height); } polya = this.temp_poly; if (haspolyb) { b.collision_poly.cache_poly(b.width, b.height, b.angle); this.temp_poly2.set_from_poly(b.collision_poly); } else { this.temp_poly2.set_from_quad(b.bquad, b.x, b.y, b.width, b.height); } polyb = this.temp_poly2; for (i = 0, len = polya.pts_count; i < len; i++) { i2 = i * 2; i21 = i2 + 1; x = polya.pts_cache[i2]; y = polya.pts_cache[i21]; polya.pts_cache[i2] = layera.layerToCanvas(x + a.x, y + a.y, true); polya.pts_cache[i21] = layera.layerToCanvas(x + a.x, y + a.y, false); } polya.update_bbox(); for (i = 0, len = polyb.pts_count; i < len; i++) { i2 = i * 2; i21 = i2 + 1; x = polyb.pts_cache[i2]; y = polyb.pts_cache[i21]; polyb.pts_cache[i2] = layerb.layerToCanvas(x + b.x, y + b.y, true); polyb.pts_cache[i21] = layerb.layerToCanvas(x + b.x, y + b.y, false); } polyb.update_bbox(); return polya.intersects_poly(polyb, 0, 0); } }; var tmpQuad = new cr.quad(); var tmpRect = new cr.rect(0, 0, 0, 0); var collrect_candidates = []; Runtime.prototype.testTilemapOverlap = function (tm, a) { var i, len, c, rc; var bbox = a.bbox; var tmx = tm.x; var tmy = tm.y; tm.getCollisionRectCandidates(bbox, collrect_candidates); var collrects = collrect_candidates; var haspolya = (a.collision_poly && !a.collision_poly.is_empty()); for (i = 0, len = collrects.length; i < len; ++i) { c = collrects[i]; rc = c.rc; if (bbox.intersects_rect_off(rc, tmx, tmy)) { tmpQuad.set_from_rect(rc); tmpQuad.offset(tmx, tmy); if (tmpQuad.intersects_quad(a.bquad)) { if (haspolya) { a.collision_poly.cache_poly(a.width, a.height, a.angle); if (c.poly) { if (c.poly.intersects_poly(a.collision_poly, a.x - (tmx + rc.left), a.y - (tmy + rc.top))) { cr.clearArray(collrect_candidates); return true; } } else { this.temp_poly.set_from_quad(tmpQuad, 0, 0, rc.right - rc.left, rc.bottom - rc.top); if (this.temp_poly.intersects_poly(a.collision_poly, a.x, a.y)) { cr.clearArray(collrect_candidates); return true; } } } else { if (c.poly) { this.temp_poly.set_from_quad(a.bquad, 0, 0, a.width, a.height); if (c.poly.intersects_poly(this.temp_poly, -(tmx + rc.left), -(tmy + rc.top))) { cr.clearArray(collrect_candidates); return true; } } else { cr.clearArray(collrect_candidates); return true; } } } } } cr.clearArray(collrect_candidates); return false; }; Runtime.prototype.testRectOverlap = function (r, b) { if (!b || !b.collisionsEnabled) return false; b.update_bbox(); var layerb = b.layer; var haspolyb, polyb; if (!b.bbox.intersects_rect(r)) return false; if (b.tilemap_exists) { b.getCollisionRectCandidates(r, collrect_candidates); var collrects = collrect_candidates; var i, len, c, tilerc; var tmx = b.x; var tmy = b.y; for (i = 0, len = collrects.length; i < len; ++i) { c = collrects[i]; tilerc = c.rc; if (r.intersects_rect_off(tilerc, tmx, tmy)) { if (c.poly) { this.temp_poly.set_from_rect(r, 0, 0); if (c.poly.intersects_poly(this.temp_poly, -(tmx + tilerc.left), -(tmy + tilerc.top))) { cr.clearArray(collrect_candidates); return true; } } else { cr.clearArray(collrect_candidates); return true; } } } cr.clearArray(collrect_candidates); return false; } else { tmpQuad.set_from_rect(r); if (!b.bquad.intersects_quad(tmpQuad)) return false; haspolyb = (b.collision_poly && !b.collision_poly.is_empty()); if (!haspolyb) return true; b.collision_poly.cache_poly(b.width, b.height, b.angle); tmpQuad.offset(-r.left, -r.top); this.temp_poly.set_from_quad(tmpQuad, 0, 0, 1, 1); return b.collision_poly.intersects_poly(this.temp_poly, r.left - b.x, r.top - b.y); } }; Runtime.prototype.testSegmentOverlap = function (x1, y1, x2, y2, b) { if (!b || !b.collisionsEnabled) return false; b.update_bbox(); var layerb = b.layer; var haspolyb, polyb; tmpRect.set(cr.min(x1, x2), cr.min(y1, y2), cr.max(x1, x2), cr.max(y1, y2)); if (!b.bbox.intersects_rect(tmpRect)) return false; if (b.tilemap_exists) { b.getCollisionRectCandidates(tmpRect, collrect_candidates); var collrects = collrect_candidates; var i, len, c, tilerc; var tmx = b.x; var tmy = b.y; for (i = 0, len = collrects.length; i < len; ++i) { c = collrects[i]; tilerc = c.rc; if (tmpRect.intersects_rect_off(tilerc, tmx, tmy)) { tmpQuad.set_from_rect(tilerc); tmpQuad.offset(tmx, tmy); if (tmpQuad.intersects_segment(x1, y1, x2, y2)) { if (c.poly) { if (c.poly.intersects_segment(tmx + tilerc.left, tmy + tilerc.top, x1, y1, x2, y2)) { cr.clearArray(collrect_candidates); return true; } } else { cr.clearArray(collrect_candidates); return true; } } } } cr.clearArray(collrect_candidates); return false; } else { if (!b.bquad.intersects_segment(x1, y1, x2, y2)) return false; haspolyb = (b.collision_poly && !b.collision_poly.is_empty()); if (!haspolyb) return true; b.collision_poly.cache_poly(b.width, b.height, b.angle); return b.collision_poly.intersects_segment(b.x, b.y, x1, y1, x2, y2); } }; Runtime.prototype.typeHasBehavior = function (t, b) { if (!b) return false; var i, len, j, lenj, f; for (i = 0, len = t.behaviors.length; i < len; i++) { if (t.behaviors[i].behavior instanceof b) return true; } if (!t.is_family) { for (i = 0, len = t.families.length; i < len; i++) { f = t.families[i]; for (j = 0, lenj = f.behaviors.length; j < lenj; j++) { if (f.behaviors[j].behavior instanceof b) return true; } } } return false; }; Runtime.prototype.typeHasNoSaveBehavior = function (t) { return this.typeHasBehavior(t, cr.behaviors.NoSave); }; Runtime.prototype.typeHasPersistBehavior = function (t) { return this.typeHasBehavior(t, cr.behaviors.Persist); }; Runtime.prototype.getSolidBehavior = function () { return this.solidBehavior; }; Runtime.prototype.getJumpthruBehavior = function () { return this.jumpthruBehavior; }; var candidates = []; Runtime.prototype.testOverlapSolid = function (inst) { var i, len, s; inst.update_bbox(); this.getSolidCollisionCandidates(inst.layer, inst.bbox, candidates); for (i = 0, len = candidates.length; i < len; ++i) { s = candidates[i]; if (!s.extra["solidEnabled"]) continue; if (this.testOverlap(inst, s)) { cr.clearArray(candidates); return s; } } cr.clearArray(candidates); return null; }; Runtime.prototype.testRectOverlapSolid = function (r) { var i, len, s; this.getSolidCollisionCandidates(null, r, candidates); for (i = 0, len = candidates.length; i < len; ++i) { s = candidates[i]; if (!s.extra["solidEnabled"]) continue; if (this.testRectOverlap(r, s)) { cr.clearArray(candidates); return s; } } cr.clearArray(candidates); return null; }; var jumpthru_array_ret = []; Runtime.prototype.testOverlapJumpThru = function (inst, all) { var ret = null; if (all) { ret = jumpthru_array_ret; cr.clearArray(ret); } inst.update_bbox(); this.getJumpthruCollisionCandidates(inst.layer, inst.bbox, candidates); var i, len, j; for (i = 0, len = candidates.length; i < len; ++i) { j = candidates[i]; if (!j.extra["jumpthruEnabled"]) continue; if (this.testOverlap(inst, j)) { if (all) ret.push(j); else { cr.clearArray(candidates); return j; } } } cr.clearArray(candidates); return ret; }; Runtime.prototype.pushOutSolid = function (inst, xdir, ydir, dist, include_jumpthrus, specific_jumpthru) { var push_dist = dist || 50; var oldx = inst.x var oldy = inst.y; var i; var last_overlapped = null, secondlast_overlapped = null; for (i = 0; i < push_dist; i++) { inst.x = (oldx + (xdir * i)); inst.y = (oldy + (ydir * i)); inst.set_bbox_changed(); if (!this.testOverlap(inst, last_overlapped)) { last_overlapped = this.testOverlapSolid(inst); if (last_overlapped) secondlast_overlapped = last_overlapped; if (!last_overlapped) { if (include_jumpthrus) { if (specific_jumpthru) last_overlapped = (this.testOverlap(inst, specific_jumpthru) ? specific_jumpthru : null); else last_overlapped = this.testOverlapJumpThru(inst); if (last_overlapped) secondlast_overlapped = last_overlapped; } if (!last_overlapped) { if (secondlast_overlapped) this.pushInFractional(inst, xdir, ydir, secondlast_overlapped, 16); return true; } } } } inst.x = oldx; inst.y = oldy; inst.set_bbox_changed(); return false; }; Runtime.prototype.pushOut = function (inst, xdir, ydir, dist, otherinst) { var push_dist = dist || 50; var oldx = inst.x var oldy = inst.y; var i; for (i = 0; i < push_dist; i++) { inst.x = (oldx + (xdir * i)); inst.y = (oldy + (ydir * i)); inst.set_bbox_changed(); if (!this.testOverlap(inst, otherinst)) return true; } inst.x = oldx; inst.y = oldy; inst.set_bbox_changed(); return false; }; Runtime.prototype.pushInFractional = function (inst, xdir, ydir, obj, limit) { var divisor = 2; var frac; var forward = false; var overlapping = false; var bestx = inst.x; var besty = inst.y; while (divisor <= limit) { frac = 1 / divisor; divisor *= 2; inst.x += xdir * frac * (forward ? 1 : -1); inst.y += ydir * frac * (forward ? 1 : -1); inst.set_bbox_changed(); if (this.testOverlap(inst, obj)) { forward = true; overlapping = true; } else { forward = false; overlapping = false; bestx = inst.x; besty = inst.y; } } if (overlapping) { inst.x = bestx; inst.y = besty; inst.set_bbox_changed(); } }; Runtime.prototype.pushOutSolidNearest = function (inst, max_dist_) { var max_dist = (cr.is_undefined(max_dist_) ? 100 : max_dist_); var dist = 0; var oldx = inst.x var oldy = inst.y; var dir = 0; var dx = 0, dy = 0; var last_overlapped = this.testOverlapSolid(inst); if (!last_overlapped) return true; // already clear of solids while (dist <= max_dist) { switch (dir) { case 0: dx = 0; dy = -1; dist++; break; case 1: dx = 1; dy = -1; break; case 2: dx = 1; dy = 0; break; case 3: dx = 1; dy = 1; break; case 4: dx = 0; dy = 1; break; case 5: dx = -1; dy = 1; break; case 6: dx = -1; dy = 0; break; case 7: dx = -1; dy = -1; break; } dir = (dir + 1) % 8; inst.x = cr.floor(oldx + (dx * dist)); inst.y = cr.floor(oldy + (dy * dist)); inst.set_bbox_changed(); if (!this.testOverlap(inst, last_overlapped)) { last_overlapped = this.testOverlapSolid(inst); if (!last_overlapped) return true; } } inst.x = oldx; inst.y = oldy; inst.set_bbox_changed(); return false; }; Runtime.prototype.registerCollision = function (a, b) { if (!a.collisionsEnabled || !b.collisionsEnabled) return; this.registered_collisions.push([a, b]); }; Runtime.prototype.checkRegisteredCollision = function (a, b) { var i, len, x; for (i = 0, len = this.registered_collisions.length; i < len; i++) { x = this.registered_collisions[i]; if ((x[0] == a && x[1] == b) || (x[0] == b && x[1] == a)) return true; } return false; }; Runtime.prototype.calculateSolidBounceAngle = function(inst, startx, starty, obj) { var objx = inst.x; var objy = inst.y; var radius = cr.max(10, cr.distanceTo(startx, starty, objx, objy)); var startangle = cr.angleTo(startx, starty, objx, objy); var firstsolid = obj || this.testOverlapSolid(inst); if (!firstsolid) return cr.clamp_angle(startangle + cr.PI); var cursolid = firstsolid; var i, curangle, anticlockwise_free_angle, clockwise_free_angle; var increment = cr.to_radians(5); // 5 degree increments for (i = 1; i < 36; i++) { curangle = startangle - i * increment; inst.x = startx + Math.cos(curangle) * radius; inst.y = starty + Math.sin(curangle) * radius; inst.set_bbox_changed(); if (!this.testOverlap(inst, cursolid)) { cursolid = obj ? null : this.testOverlapSolid(inst); if (!cursolid) { anticlockwise_free_angle = curangle; break; } } } if (i === 36) anticlockwise_free_angle = cr.clamp_angle(startangle + cr.PI); var cursolid = firstsolid; for (i = 1; i < 36; i++) { curangle = startangle + i * increment; inst.x = startx + Math.cos(curangle) * radius; inst.y = starty + Math.sin(curangle) * radius; inst.set_bbox_changed(); if (!this.testOverlap(inst, cursolid)) { cursolid = obj ? null : this.testOverlapSolid(inst); if (!cursolid) { clockwise_free_angle = curangle; break; } } } if (i === 36) clockwise_free_angle = cr.clamp_angle(startangle + cr.PI); inst.x = objx; inst.y = objy; inst.set_bbox_changed(); if (clockwise_free_angle === anticlockwise_free_angle) return clockwise_free_angle; var half_diff = cr.angleDiff(clockwise_free_angle, anticlockwise_free_angle) / 2; var normal; if (cr.angleClockwise(clockwise_free_angle, anticlockwise_free_angle)) { normal = cr.clamp_angle(anticlockwise_free_angle + half_diff + cr.PI); } else { normal = cr.clamp_angle(clockwise_free_angle + half_diff); } ; var vx = Math.cos(startangle); var vy = Math.sin(startangle); var nx = Math.cos(normal); var ny = Math.sin(normal); var v_dot_n = vx * nx + vy * ny; var rx = vx - 2 * v_dot_n * nx; var ry = vy - 2 * v_dot_n * ny; return cr.angleTo(0, 0, rx, ry); }; var triggerSheetIndex = -1; Runtime.prototype.trigger = function (method, inst, value /* for fast triggers */) { ; if (!this.running_layout) return false; var sheet = this.running_layout.event_sheet; if (!sheet) return false; // no event sheet active; nothing to trigger var ret = false; var r, i, len; triggerSheetIndex++; var deep_includes = sheet.deep_includes; for (i = 0, len = deep_includes.length; i < len; ++i) { r = this.triggerOnSheet(method, inst, deep_includes[i], value); ret = ret || r; } r = this.triggerOnSheet(method, inst, sheet, value); ret = ret || r; triggerSheetIndex--; return ret; }; Runtime.prototype.triggerOnSheet = function (method, inst, sheet, value) { var ret = false; var i, leni, r, families; if (!inst) { r = this.triggerOnSheetForTypeName(method, inst, "system", sheet, value); ret = ret || r; } else { r = this.triggerOnSheetForTypeName(method, inst, inst.type.name, sheet, value); ret = ret || r; families = inst.type.families; for (i = 0, leni = families.length; i < leni; ++i) { r = this.triggerOnSheetForTypeName(method, inst, families[i].name, sheet, value); ret = ret || r; } } return ret; // true if anything got triggered }; Runtime.prototype.triggerOnSheetForTypeName = function (method, inst, type_name, sheet, value) { var i, leni; var ret = false, ret2 = false; var trig, index; var fasttrigger = (typeof value !== "undefined"); var triggers = (fasttrigger ? sheet.fasttriggers : sheet.triggers); var obj_entry = triggers[type_name]; if (!obj_entry) return ret; var triggers_list = null; for (i = 0, leni = obj_entry.length; i < leni; ++i) { if (obj_entry[i].method == method) { triggers_list = obj_entry[i].evs; break; } } if (!triggers_list) return ret; var triggers_to_fire; if (fasttrigger) { triggers_to_fire = triggers_list[value]; } else { triggers_to_fire = triggers_list; } if (!triggers_to_fire) return null; for (i = 0, leni = triggers_to_fire.length; i < leni; i++) { trig = triggers_to_fire[i][0]; index = triggers_to_fire[i][1]; ret2 = this.executeSingleTrigger(inst, type_name, trig, index); ret = ret || ret2; } return ret; }; Runtime.prototype.executeSingleTrigger = function (inst, type_name, trig, index) { var i, leni; var ret = false; this.trigger_depth++; var current_event = this.getCurrentEventStack().current_event; if (current_event) this.pushCleanSol(current_event.solModifiersIncludingParents); var isrecursive = (this.trigger_depth > 1); // calling trigger from inside another trigger this.pushCleanSol(trig.solModifiersIncludingParents); if (isrecursive) this.pushLocalVarStack(); var event_stack = this.pushEventStack(trig); event_stack.current_event = trig; if (inst) { var sol = this.types[type_name].getCurrentSol(); sol.select_all = false; cr.clearArray(sol.instances); sol.instances[0] = inst; this.types[type_name].applySolToContainer(); } var ok_to_run = true; if (trig.parent) { var temp_parents_arr = event_stack.temp_parents_arr; var cur_parent = trig.parent; while (cur_parent) { temp_parents_arr.push(cur_parent); cur_parent = cur_parent.parent; } temp_parents_arr.reverse(); for (i = 0, leni = temp_parents_arr.length; i < leni; i++) { if (!temp_parents_arr[i].run_pretrigger()) // parent event failed { ok_to_run = false; break; } } } if (ok_to_run) { this.execcount++; if (trig.orblock) trig.run_orblocktrigger(index); else trig.run(); ret = ret || event_stack.last_event_true; } this.popEventStack(); if (isrecursive) this.popLocalVarStack(); this.popSol(trig.solModifiersIncludingParents); if (current_event) this.popSol(current_event.solModifiersIncludingParents); if (this.hasPendingInstances && this.isInOnDestroy === 0 && triggerSheetIndex === 0 && !this.isRunningEvents) { this.ClearDeathRow(); } this.trigger_depth--; return ret; }; Runtime.prototype.getCurrentCondition = function () { var evinfo = this.getCurrentEventStack(); return evinfo.current_event.conditions[evinfo.cndindex]; }; Runtime.prototype.getCurrentConditionObjectType = function () { var cnd = this.getCurrentCondition(); return cnd.type; }; Runtime.prototype.isCurrentConditionFirst = function () { var evinfo = this.getCurrentEventStack(); return evinfo.cndindex === 0; }; Runtime.prototype.getCurrentAction = function () { var evinfo = this.getCurrentEventStack(); return evinfo.current_event.actions[evinfo.actindex]; }; Runtime.prototype.pushLocalVarStack = function () { this.localvar_stack_index++; if (this.localvar_stack_index >= this.localvar_stack.length) this.localvar_stack.push([]); }; Runtime.prototype.popLocalVarStack = function () { ; this.localvar_stack_index--; }; Runtime.prototype.getCurrentLocalVarStack = function () { return this.localvar_stack[this.localvar_stack_index]; }; Runtime.prototype.pushEventStack = function (cur_event) { this.event_stack_index++; if (this.event_stack_index >= this.event_stack.length) this.event_stack.push(new cr.eventStackFrame()); var ret = this.getCurrentEventStack(); ret.reset(cur_event); return ret; }; Runtime.prototype.popEventStack = function () { ; this.event_stack_index--; }; Runtime.prototype.getCurrentEventStack = function () { return this.event_stack[this.event_stack_index]; }; Runtime.prototype.pushLoopStack = function (name_) { this.loop_stack_index++; if (this.loop_stack_index >= this.loop_stack.length) { this.loop_stack.push(cr.seal({ name: name_, index: 0, stopped: false })); } var ret = this.getCurrentLoop(); ret.name = name_; ret.index = 0; ret.stopped = false; return ret; }; Runtime.prototype.popLoopStack = function () { ; this.loop_stack_index--; }; Runtime.prototype.getCurrentLoop = function () { return this.loop_stack[this.loop_stack_index]; }; Runtime.prototype.getEventVariableByName = function (name, scope) { var i, leni, j, lenj, sheet, e; while (scope) { for (i = 0, leni = scope.subevents.length; i < leni; i++) { e = scope.subevents[i]; if (e instanceof cr.eventvariable && cr.equals_nocase(name, e.name)) return e; } scope = scope.parent; } for (i = 0, leni = this.eventsheets_by_index.length; i < leni; i++) { sheet = this.eventsheets_by_index[i]; for (j = 0, lenj = sheet.events.length; j < lenj; j++) { e = sheet.events[j]; if (e instanceof cr.eventvariable && cr.equals_nocase(name, e.name)) return e; } } return null; }; Runtime.prototype.getLayoutBySid = function (sid_) { var i, len; for (i = 0, len = this.layouts_by_index.length; i < len; i++) { if (this.layouts_by_index[i].sid === sid_) return this.layouts_by_index[i]; } return null; }; Runtime.prototype.getObjectTypeBySid = function (sid_) { var i, len; for (i = 0, len = this.types_by_index.length; i < len; i++) { if (this.types_by_index[i].sid === sid_) return this.types_by_index[i]; } return null; }; Runtime.prototype.getGroupBySid = function (sid_) { var i, len; for (i = 0, len = this.allGroups.length; i < len; i++) { if (this.allGroups[i].sid === sid_) return this.allGroups[i]; } return null; }; Runtime.prototype.doCanvasSnapshot = function (format_, quality_) { this.snapshotCanvas = [format_, quality_]; this.redraw = true; // force redraw so snapshot is always taken }; function IsIndexedDBAvailable() { try { return !!window.indexedDB; } catch (e) { return false; } }; function makeSaveDb(e) { var db = e.target.result; db.createObjectStore("saves", { keyPath: "slot" }); }; function IndexedDB_WriteSlot(slot_, data_, oncomplete_, onerror_) { try { var request = indexedDB.open("_C2SaveStates"); request.onupgradeneeded = makeSaveDb; request.onerror = onerror_; request.onsuccess = function (e) { var db = e.target.result; db.onerror = onerror_; var transaction = db.transaction(["saves"], "readwrite"); var objectStore = transaction.objectStore("saves"); var putReq = objectStore.put({"slot": slot_, "data": data_ }); putReq.onsuccess = oncomplete_; }; } catch (err) { onerror_(err); } }; function IndexedDB_ReadSlot(slot_, oncomplete_, onerror_) { try { var request = indexedDB.open("_C2SaveStates"); request.onupgradeneeded = makeSaveDb; request.onerror = onerror_; request.onsuccess = function (e) { var db = e.target.result; db.onerror = onerror_; var transaction = db.transaction(["saves"]); var objectStore = transaction.objectStore("saves"); var readReq = objectStore.get(slot_); readReq.onsuccess = function (e) { if (readReq.result) oncomplete_(readReq.result["data"]); else oncomplete_(null); }; }; } catch (err) { onerror_(err); } }; Runtime.prototype.signalContinuousPreview = function () { this.signalledContinuousPreview = true; }; function doContinuousPreviewReload() { cr.logexport("Reloading for continuous preview"); if (!!window["c2cocoonjs"]) { CocoonJS["App"]["reload"](); } else { if (window.location.search.indexOf("continuous") > -1) window.location.reload(true); else window.location = window.location + "?continuous"; } }; Runtime.prototype.handleSaveLoad = function () { var self = this; var savingToSlot = this.saveToSlot; var savingJson = this.lastSaveJson; var loadingFromSlot = this.loadFromSlot; var continuous = false; if (this.signalledContinuousPreview) { continuous = true; savingToSlot = "__c2_continuouspreview"; this.signalledContinuousPreview = false; } if (savingToSlot.length) { this.ClearDeathRow(); savingJson = this.saveToJSONString(); if (IsIndexedDBAvailable() && !this.isCocoonJs) { IndexedDB_WriteSlot(savingToSlot, savingJson, function () { cr.logexport("Saved state to IndexedDB storage (" + savingJson.length + " bytes)"); self.lastSaveJson = savingJson; self.trigger(cr.system_object.prototype.cnds.OnSaveComplete, null); self.lastSaveJson = ""; if (continuous) doContinuousPreviewReload(); }, function (e) { try { localStorage.setItem("__c2save_" + savingToSlot, savingJson); cr.logexport("Saved state to WebStorage (" + savingJson.length + " bytes)"); self.lastSaveJson = savingJson; self.trigger(cr.system_object.prototype.cnds.OnSaveComplete, null); self.lastSaveJson = ""; if (continuous) doContinuousPreviewReload(); } catch (f) { cr.logexport("Failed to save game state: " + e + "; " + f); self.trigger(cr.system_object.prototype.cnds.OnSaveFailed, null); } }); } else { try { localStorage.setItem("__c2save_" + savingToSlot, savingJson); cr.logexport("Saved state to WebStorage (" + savingJson.length + " bytes)"); self.lastSaveJson = savingJson; this.trigger(cr.system_object.prototype.cnds.OnSaveComplete, null); self.lastSaveJson = ""; if (continuous) doContinuousPreviewReload(); } catch (e) { cr.logexport("Error saving to WebStorage: " + e); self.trigger(cr.system_object.prototype.cnds.OnSaveFailed, null); } } this.saveToSlot = ""; this.loadFromSlot = ""; this.loadFromJson = null; } if (loadingFromSlot.length) { if (IsIndexedDBAvailable() && !this.isCocoonJs) { IndexedDB_ReadSlot(loadingFromSlot, function (result_) { if (result_) { self.loadFromJson = result_; cr.logexport("Loaded state from IndexedDB storage (" + self.loadFromJson.length + " bytes)"); } else { self.loadFromJson = localStorage.getItem("__c2save_" + loadingFromSlot) || ""; cr.logexport("Loaded state from WebStorage (" + self.loadFromJson.length + " bytes)"); } self.suspendDrawing = false; if (!self.loadFromJson) { self.loadFromJson = null; self.trigger(cr.system_object.prototype.cnds.OnLoadFailed, null); } }, function (e) { self.loadFromJson = localStorage.getItem("__c2save_" + loadingFromSlot) || ""; cr.logexport("Loaded state from WebStorage (" + self.loadFromJson.length + " bytes)"); self.suspendDrawing = false; if (!self.loadFromJson) { self.loadFromJson = null; self.trigger(cr.system_object.prototype.cnds.OnLoadFailed, null); } }); } else { try { this.loadFromJson = localStorage.getItem("__c2save_" + loadingFromSlot) || ""; cr.logexport("Loaded state from WebStorage (" + this.loadFromJson.length + " bytes)"); } catch (e) { this.loadFromJson = null; } this.suspendDrawing = false; if (!self.loadFromJson) { self.loadFromJson = null; self.trigger(cr.system_object.prototype.cnds.OnLoadFailed, null); } } this.loadFromSlot = ""; this.saveToSlot = ""; } if (this.loadFromJson !== null) { this.ClearDeathRow(); var ok = this.loadFromJSONString(this.loadFromJson); if (ok) { this.lastSaveJson = this.loadFromJson; this.trigger(cr.system_object.prototype.cnds.OnLoadComplete, null); this.lastSaveJson = ""; } else { self.trigger(cr.system_object.prototype.cnds.OnLoadFailed, null); } this.loadFromJson = null; } }; function CopyExtraObject(extra) { var p, ret = {}; for (p in extra) { if (extra.hasOwnProperty(p)) { if (extra[p] instanceof cr.ObjectSet) continue; if (extra[p] && typeof extra[p].c2userdata !== "undefined") continue; if (p === "spriteCreatedDestroyCallback") continue; ret[p] = extra[p]; } } return ret; }; Runtime.prototype.saveToJSONString = function() { var i, len, j, lenj, type, layout, typeobj, g, c, a, v, p; var o = { "c2save": true, "version": 1, "rt": { "time": this.kahanTime.sum, "walltime": this.wallTime.sum, "timescale": this.timescale, "tickcount": this.tickcount, "execcount": this.execcount, "next_uid": this.next_uid, "running_layout": this.running_layout.sid, "start_time_offset": (Date.now() - this.start_time) }, "types": {}, "layouts": {}, "events": { "groups": {}, "cnds": {}, "acts": {}, "vars": {} } }; for (i = 0, len = this.types_by_index.length; i < len; i++) { type = this.types_by_index[i]; if (type.is_family || this.typeHasNoSaveBehavior(type)) continue; typeobj = { "instances": [] }; if (cr.hasAnyOwnProperty(type.extra)) typeobj["ex"] = CopyExtraObject(type.extra); for (j = 0, lenj = type.instances.length; j < lenj; j++) { typeobj["instances"].push(this.saveInstanceToJSON(type.instances[j])); } o["types"][type.sid.toString()] = typeobj; } for (i = 0, len = this.layouts_by_index.length; i < len; i++) { layout = this.layouts_by_index[i]; o["layouts"][layout.sid.toString()] = layout.saveToJSON(); } var ogroups = o["events"]["groups"]; for (i = 0, len = this.allGroups.length; i < len; i++) { g = this.allGroups[i]; ogroups[g.sid.toString()] = this.groups_by_name[g.group_name].group_active; } var ocnds = o["events"]["cnds"]; for (p in this.cndsBySid) { if (this.cndsBySid.hasOwnProperty(p)) { c = this.cndsBySid[p]; if (cr.hasAnyOwnProperty(c.extra)) ocnds[p] = { "ex": CopyExtraObject(c.extra) }; } } var oacts = o["events"]["acts"]; for (p in this.actsBySid) { if (this.actsBySid.hasOwnProperty(p)) { a = this.actsBySid[p]; if (cr.hasAnyOwnProperty(a.extra)) oacts[p] = { "ex": CopyExtraObject(a.extra) }; } } var ovars = o["events"]["vars"]; for (p in this.varsBySid) { if (this.varsBySid.hasOwnProperty(p)) { v = this.varsBySid[p]; if (!v.is_constant && (!v.parent || v.is_static)) ovars[p] = v.data; } } o["system"] = this.system.saveToJSON(); return JSON.stringify(o); }; Runtime.prototype.refreshUidMap = function () { var i, len, type, j, lenj, inst; this.objectsByUid = {}; for (i = 0, len = this.types_by_index.length; i < len; i++) { type = this.types_by_index[i]; if (type.is_family) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; this.objectsByUid[inst.uid.toString()] = inst; } } }; Runtime.prototype.loadFromJSONString = function (str) { var o; try { o = JSON.parse(str); } catch (e) { return false; } if (!o["c2save"]) return false; // probably not a c2 save state if (o["version"] > 1) return false; // from future version of c2; assume not compatible this.isLoadingState = true; var rt = o["rt"]; this.kahanTime.reset(); this.kahanTime.sum = rt["time"]; this.wallTime.reset(); this.wallTime.sum = rt["walltime"] || 0; this.timescale = rt["timescale"]; this.tickcount = rt["tickcount"]; this.execcount = rt["execcount"]; this.start_time = Date.now() - rt["start_time_offset"]; var layout_sid = rt["running_layout"]; if (layout_sid !== this.running_layout.sid) { var changeToLayout = this.getLayoutBySid(layout_sid); if (changeToLayout) this.doChangeLayout(changeToLayout); else return; // layout that was saved on has gone missing (deleted?) } var i, len, j, lenj, k, lenk, p, type, existing_insts, load_insts, inst, binst, layout, layer, g, iid, t; var otypes = o["types"]; for (p in otypes) { if (otypes.hasOwnProperty(p)) { type = this.getObjectTypeBySid(parseInt(p, 10)); if (!type || type.is_family || this.typeHasNoSaveBehavior(type)) continue; if (otypes[p]["ex"]) type.extra = otypes[p]["ex"]; else cr.wipe(type.extra); existing_insts = type.instances; load_insts = otypes[p]["instances"]; for (i = 0, len = cr.min(existing_insts.length, load_insts.length); i < len; i++) { this.loadInstanceFromJSON(existing_insts[i], load_insts[i]); } for (i = load_insts.length, len = existing_insts.length; i < len; i++) this.DestroyInstance(existing_insts[i]); for (i = existing_insts.length, len = load_insts.length; i < len; i++) { layer = null; if (type.plugin.is_world) { layer = this.running_layout.getLayerBySid(load_insts[i]["w"]["l"]); if (!layer) continue; } inst = this.createInstanceFromInit(type.default_instance, layer, false, 0, 0, true); this.loadInstanceFromJSON(inst, load_insts[i]); } type.stale_iids = true; } } this.ClearDeathRow(); this.refreshUidMap(); var olayouts = o["layouts"]; for (p in olayouts) { if (olayouts.hasOwnProperty(p)) { layout = this.getLayoutBySid(parseInt(p, 10)); if (!layout) continue; // must've gone missing layout.loadFromJSON(olayouts[p]); } } var ogroups = o["events"]["groups"]; for (p in ogroups) { if (ogroups.hasOwnProperty(p)) { g = this.getGroupBySid(parseInt(p, 10)); if (g && this.groups_by_name[g.group_name]) this.groups_by_name[g.group_name].setGroupActive(ogroups[p]); } } var ocnds = o["events"]["cnds"]; for (p in this.cndsBySid) { if (this.cndsBySid.hasOwnProperty(p)) { if (ocnds.hasOwnProperty(p)) { this.cndsBySid[p].extra = ocnds[p]["ex"]; } else { this.cndsBySid[p].extra = {}; } } } var oacts = o["events"]["acts"]; for (p in this.actsBySid) { if (this.actsBySid.hasOwnProperty(p)) { if (oacts.hasOwnProperty(p)) { this.actsBySid[p].extra = oacts[p]["ex"]; } else { this.actsBySid[p].extra = {}; } } } var ovars = o["events"]["vars"]; for (p in ovars) { if (ovars.hasOwnProperty(p) && this.varsBySid.hasOwnProperty(p)) { this.varsBySid[p].data = ovars[p]; } } this.next_uid = rt["next_uid"]; this.isLoadingState = false; for (i = 0, len = this.fireOnCreateAfterLoad.length; i < len; ++i) { inst = this.fireOnCreateAfterLoad[i]; this.trigger(Object.getPrototypeOf(inst.type.plugin).cnds.OnCreated, inst); } cr.clearArray(this.fireOnCreateAfterLoad); this.system.loadFromJSON(o["system"]); for (i = 0, len = this.types_by_index.length; i < len; i++) { type = this.types_by_index[i]; if (type.is_family || this.typeHasNoSaveBehavior(type)) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) { inst = type.instances[j]; if (type.is_contained) { iid = inst.get_iid(); cr.clearArray(inst.siblings); for (k = 0, lenk = type.container.length; k < lenk; k++) { t = type.container[k]; if (type === t) continue; ; inst.siblings.push(t.instances[iid]); } } if (inst.afterLoad) inst.afterLoad(); if (inst.behavior_insts) { for (k = 0, lenk = inst.behavior_insts.length; k < lenk; k++) { binst = inst.behavior_insts[k]; if (binst.afterLoad) binst.afterLoad(); } } } } this.redraw = true; return true; }; Runtime.prototype.saveInstanceToJSON = function(inst, state_only) { var i, len, world, behinst, et; var type = inst.type; var plugin = type.plugin; var o = {}; if (state_only) o["c2"] = true; // mark as known json data from Construct 2 else o["uid"] = inst.uid; if (cr.hasAnyOwnProperty(inst.extra)) o["ex"] = CopyExtraObject(inst.extra); if (inst.instance_vars && inst.instance_vars.length) { o["ivs"] = {}; for (i = 0, len = inst.instance_vars.length; i < len; i++) { o["ivs"][inst.type.instvar_sids[i].toString()] = inst.instance_vars[i]; } } if (plugin.is_world) { world = { "x": inst.x, "y": inst.y, "w": inst.width, "h": inst.height, "l": inst.layer.sid, "zi": inst.get_zindex() }; if (inst.angle !== 0) world["a"] = inst.angle; if (inst.opacity !== 1) world["o"] = inst.opacity; if (inst.hotspotX !== 0.5) world["hX"] = inst.hotspotX; if (inst.hotspotY !== 0.5) world["hY"] = inst.hotspotY; if (inst.blend_mode !== 0) world["bm"] = inst.blend_mode; if (!inst.visible) world["v"] = inst.visible; if (!inst.collisionsEnabled) world["ce"] = inst.collisionsEnabled; if (inst.my_timescale !== -1) world["mts"] = inst.my_timescale; if (type.effect_types.length) { world["fx"] = []; for (i = 0, len = type.effect_types.length; i < len; i++) { et = type.effect_types[i]; world["fx"].push({"name": et.name, "active": inst.active_effect_flags[et.index], "params": inst.effect_params[et.index] }); } } o["w"] = world; } if (inst.behavior_insts && inst.behavior_insts.length) { o["behs"] = {}; for (i = 0, len = inst.behavior_insts.length; i < len; i++) { behinst = inst.behavior_insts[i]; if (behinst.saveToJSON) o["behs"][behinst.type.sid.toString()] = behinst.saveToJSON(); } } if (inst.saveToJSON) o["data"] = inst.saveToJSON(); return o; }; Runtime.prototype.getInstanceVarIndexBySid = function (type, sid_) { var i, len; for (i = 0, len = type.instvar_sids.length; i < len; i++) { if (type.instvar_sids[i] === sid_) return i; } return -1; }; Runtime.prototype.getBehaviorIndexBySid = function (inst, sid_) { var i, len; for (i = 0, len = inst.behavior_insts.length; i < len; i++) { if (inst.behavior_insts[i].type.sid === sid_) return i; } return -1; }; Runtime.prototype.loadInstanceFromJSON = function(inst, o, state_only) { var p, i, len, iv, oivs, world, fxindex, obehs, behindex; var oldlayer; var type = inst.type; var plugin = type.plugin; if (state_only) { if (!o["c2"]) return; } else inst.uid = o["uid"]; if (o["ex"]) inst.extra = o["ex"]; else cr.wipe(inst.extra); oivs = o["ivs"]; if (oivs) { for (p in oivs) { if (oivs.hasOwnProperty(p)) { iv = this.getInstanceVarIndexBySid(type, parseInt(p, 10)); if (iv < 0 || iv >= inst.instance_vars.length) continue; // must've gone missing inst.instance_vars[iv] = oivs[p]; } } } if (plugin.is_world) { world = o["w"]; if (inst.layer.sid !== world["l"]) { oldlayer = inst.layer; inst.layer = this.running_layout.getLayerBySid(world["l"]); if (inst.layer) { oldlayer.removeFromInstanceList(inst, true); inst.layer.appendToInstanceList(inst, true); inst.set_bbox_changed(); inst.layer.setZIndicesStaleFrom(0); } else { inst.layer = oldlayer; if (!state_only) this.DestroyInstance(inst); } } inst.x = world["x"]; inst.y = world["y"]; inst.width = world["w"]; inst.height = world["h"]; inst.zindex = world["zi"]; inst.angle = world.hasOwnProperty("a") ? world["a"] : 0; inst.opacity = world.hasOwnProperty("o") ? world["o"] : 1; inst.hotspotX = world.hasOwnProperty("hX") ? world["hX"] : 0.5; inst.hotspotY = world.hasOwnProperty("hY") ? world["hY"] : 0.5; inst.visible = world.hasOwnProperty("v") ? world["v"] : true; inst.collisionsEnabled = world.hasOwnProperty("ce") ? world["ce"] : true; inst.my_timescale = world.hasOwnProperty("mts") ? world["mts"] : -1; inst.blend_mode = world.hasOwnProperty("bm") ? world["bm"] : 0;; inst.compositeOp = cr.effectToCompositeOp(inst.blend_mode); if (this.gl) cr.setGLBlend(inst, inst.blend_mode, this.gl); inst.set_bbox_changed(); if (world.hasOwnProperty("fx")) { for (i = 0, len = world["fx"].length; i < len; i++) { fxindex = type.getEffectIndexByName(world["fx"][i]["name"]); if (fxindex < 0) continue; // must've gone missing inst.active_effect_flags[fxindex] = world["fx"][i]["active"]; inst.effect_params[fxindex] = world["fx"][i]["params"]; } } inst.updateActiveEffects(); } obehs = o["behs"]; if (obehs) { for (p in obehs) { if (obehs.hasOwnProperty(p)) { behindex = this.getBehaviorIndexBySid(inst, parseInt(p, 10)); if (behindex < 0) continue; // must've gone missing inst.behavior_insts[behindex].loadFromJSON(obehs[p]); } } } if (o["data"]) inst.loadFromJSON(o["data"]); }; Runtime.prototype.fetchLocalFileViaCordova = function (filename, successCallback, errorCallback) { var path = cordova["file"]["applicationDirectory"] + "www/" + filename; window["resolveLocalFileSystemURL"](path, function (entry) { entry.file(successCallback, errorCallback); }, errorCallback); }; Runtime.prototype.fetchLocalFileViaCordovaAsText = function (filename, successCallback, errorCallback) { this.fetchLocalFileViaCordova(filename, function (file) { var reader = new FileReader(); reader.onload = function (e) { successCallback(e.target.result); }; reader.onerror = errorCallback; reader.readAsText(file); }, errorCallback); }; var queuedArrayBufferReads = []; var activeArrayBufferReads = 0; var MAX_ARRAYBUFFER_READS = 8; Runtime.prototype.maybeStartNextArrayBufferRead = function() { if (!queuedArrayBufferReads.length) return; // none left if (activeArrayBufferReads >= MAX_ARRAYBUFFER_READS) return; // already got maximum number in-flight activeArrayBufferReads++; var job = queuedArrayBufferReads.shift(); this.doFetchLocalFileViaCordovaAsArrayBuffer(job.filename, job.successCallback, job.errorCallback); }; Runtime.prototype.fetchLocalFileViaCordovaAsArrayBuffer = function (filename, successCallback_, errorCallback_) { var self = this; queuedArrayBufferReads.push({ filename: filename, successCallback: function (result) { activeArrayBufferReads--; self.maybeStartNextArrayBufferRead(); successCallback_(result); }, errorCallback: function (err) { activeArrayBufferReads--; self.maybeStartNextArrayBufferRead(); errorCallback_(err); } }); this.maybeStartNextArrayBufferRead(); }; Runtime.prototype.doFetchLocalFileViaCordovaAsArrayBuffer = function (filename, successCallback, errorCallback) { this.fetchLocalFileViaCordova(filename, function (file) { var reader = new FileReader(); reader.onload = function (e) { successCallback(e.target.result); }; reader.readAsArrayBuffer(file); }, errorCallback); }; Runtime.prototype.fetchLocalFileViaCordovaAsURL = function (filename, successCallback, errorCallback) { this.fetchLocalFileViaCordovaAsArrayBuffer(filename, function (arrayBuffer) { var blob = new Blob([arrayBuffer]); var url = URL.createObjectURL(blob); successCallback(url); }, errorCallback); }; Runtime.prototype.isAbsoluteUrl = function (url) { return /^(?:[a-z]+:)?\/\//.test(url) || url.substr(0, 5) === "data:" || url.substr(0, 5) === "blob:"; }; Runtime.prototype.setImageSrc = function (img, src) { if (this.isWKWebView && !this.isAbsoluteUrl(src)) { this.fetchLocalFileViaCordovaAsURL(src, function (url) { img.src = url; }, function (err) { alert("Failed to load image: " + err); }); } else { img.src = src; } }; Runtime.prototype.setCtxImageSmoothingEnabled = function (ctx, e) { if (typeof ctx["imageSmoothingEnabled"] !== "undefined") { ctx["imageSmoothingEnabled"] = e; } else { ctx["webkitImageSmoothingEnabled"] = e; ctx["mozImageSmoothingEnabled"] = e; ctx["msImageSmoothingEnabled"] = e; } }; cr.runtime = Runtime; cr.createRuntime = function (canvasid) { return new Runtime(document.getElementById(canvasid)); }; cr.createDCRuntime = function (w, h) { return new Runtime({ "dc": true, "width": w, "height": h }); }; window["cr_createRuntime"] = cr.createRuntime; window["cr_createDCRuntime"] = cr.createDCRuntime; window["createCocoonJSRuntime"] = function () { window["c2cocoonjs"] = true; var canvas = document.createElement("screencanvas") || document.createElement("canvas"); canvas.screencanvas = true; document.body.appendChild(canvas); var rt = new Runtime(canvas); window["c2runtime"] = rt; window.addEventListener("orientationchange", function () { window["c2runtime"]["setSize"](window.innerWidth, window.innerHeight); }); window["c2runtime"]["setSize"](window.innerWidth, window.innerHeight); return rt; }; window["createEjectaRuntime"] = function () { var canvas = document.getElementById("canvas"); var rt = new Runtime(canvas); window["c2runtime"] = rt; window["c2runtime"]["setSize"](window.innerWidth, window.innerHeight); return rt; }; }()); window["cr_getC2Runtime"] = function() { var canvas = document.getElementById("c2canvas"); if (canvas) return canvas["c2runtime"]; else if (window["c2runtime"]) return window["c2runtime"]; else return null; } window["cr_getSnapshot"] = function (format_, quality_) { var runtime = window["cr_getC2Runtime"](); if (runtime) runtime.doCanvasSnapshot(format_, quality_); } window["cr_sizeCanvas"] = function(w, h) { if (w === 0 || h === 0) return; var runtime = window["cr_getC2Runtime"](); if (runtime) runtime["setSize"](w, h); } window["cr_setSuspended"] = function(s) { var runtime = window["cr_getC2Runtime"](); if (runtime) runtime["setSuspended"](s); } ; (function() { function Layout(runtime, m) { this.runtime = runtime; this.event_sheet = null; this.scrollX = (this.runtime.original_width / 2); this.scrollY = (this.runtime.original_height / 2); this.scale = 1.0; this.angle = 0; this.first_visit = true; this.name = m[0]; this.originalWidth = m[1]; this.originalHeight = m[2]; this.width = m[1]; this.height = m[2]; this.unbounded_scrolling = m[3]; this.sheetname = m[4]; this.sid = m[5]; var lm = m[6]; var i, len; this.layers = []; this.initial_types = []; for (i = 0, len = lm.length; i < len; i++) { var layer = new cr.layer(this, lm[i]); layer.number = i; cr.seal(layer); this.layers.push(layer); } var im = m[7]; this.initial_nonworld = []; for (i = 0, len = im.length; i < len; i++) { var inst = im[i]; var type = this.runtime.types_by_index[inst[1]]; ; if (!type.default_instance) type.default_instance = inst; this.initial_nonworld.push(inst); if (this.initial_types.indexOf(type) === -1) this.initial_types.push(type); } this.effect_types = []; this.active_effect_types = []; this.shaders_preserve_opaqueness = true; this.effect_params = []; for (i = 0, len = m[8].length; i < len; i++) { this.effect_types.push({ id: m[8][i][0], name: m[8][i][1], shaderindex: -1, preservesOpaqueness: false, active: true, index: i }); this.effect_params.push(m[8][i][2].slice(0)); } this.updateActiveEffects(); this.rcTex = new cr.rect(0, 0, 1, 1); this.rcTex2 = new cr.rect(0, 0, 1, 1); this.persist_data = {}; }; Layout.prototype.saveObjectToPersist = function (inst) { var sidStr = inst.type.sid.toString(); if (!this.persist_data.hasOwnProperty(sidStr)) this.persist_data[sidStr] = []; var type_persist = this.persist_data[sidStr]; type_persist.push(this.runtime.saveInstanceToJSON(inst)); }; Layout.prototype.hasOpaqueBottomLayer = function () { var layer = this.layers[0]; return !layer.transparent && layer.opacity === 1.0 && !layer.forceOwnTexture && layer.visible; }; Layout.prototype.updateActiveEffects = function () { cr.clearArray(this.active_effect_types); this.shaders_preserve_opaqueness = true; var i, len, et; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; if (et.active) { this.active_effect_types.push(et); if (!et.preservesOpaqueness) this.shaders_preserve_opaqueness = false; } } }; Layout.prototype.getEffectByName = function (name_) { var i, len, et; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; if (et.name === name_) return et; } return null; }; var created_instances = []; function sort_by_zindex(a, b) { return a.zindex - b.zindex; }; var first_layout = true; Layout.prototype.startRunning = function () { if (this.sheetname) { this.event_sheet = this.runtime.eventsheets[this.sheetname]; ; this.event_sheet.updateDeepIncludes(); } this.runtime.running_layout = this; this.width = this.originalWidth; this.height = this.originalHeight; this.scrollX = (this.runtime.original_width / 2); this.scrollY = (this.runtime.original_height / 2); var i, k, len, lenk, type, type_instances, inst, iid, t, s, p, q, type_data, layer; for (i = 0, len = this.runtime.types_by_index.length; i < len; i++) { type = this.runtime.types_by_index[i]; if (type.is_family) continue; // instances are only transferred for their real type type_instances = type.instances; for (k = 0, lenk = type_instances.length; k < lenk; k++) { inst = type_instances[k]; if (inst.layer) { var num = inst.layer.number; if (num >= this.layers.length) num = this.layers.length - 1; inst.layer = this.layers[num]; if (inst.layer.instances.indexOf(inst) === -1) inst.layer.instances.push(inst); inst.layer.zindices_stale = true; } } } if (!first_layout) { for (i = 0, len = this.layers.length; i < len; ++i) { this.layers[i].instances.sort(sort_by_zindex); } } var layer; cr.clearArray(created_instances); this.boundScrolling(); for (i = 0, len = this.layers.length; i < len; i++) { layer = this.layers[i]; layer.createInitialInstances(); // fills created_instances layer.updateViewport(null); } var uids_changed = false; if (!this.first_visit) { for (p in this.persist_data) { if (this.persist_data.hasOwnProperty(p)) { type = this.runtime.getObjectTypeBySid(parseInt(p, 10)); if (!type || type.is_family || !this.runtime.typeHasPersistBehavior(type)) continue; type_data = this.persist_data[p]; for (i = 0, len = type_data.length; i < len; i++) { layer = null; if (type.plugin.is_world) { layer = this.getLayerBySid(type_data[i]["w"]["l"]); if (!layer) continue; } inst = this.runtime.createInstanceFromInit(type.default_instance, layer, false, 0, 0, true); this.runtime.loadInstanceFromJSON(inst, type_data[i]); uids_changed = true; created_instances.push(inst); } cr.clearArray(type_data); } } for (i = 0, len = this.layers.length; i < len; i++) { this.layers[i].instances.sort(sort_by_zindex); this.layers[i].zindices_stale = true; // in case of duplicates/holes } } if (uids_changed) { this.runtime.ClearDeathRow(); this.runtime.refreshUidMap(); } for (i = 0; i < created_instances.length; i++) { inst = created_instances[i]; if (!inst.type.is_contained) continue; iid = inst.get_iid(); for (k = 0, lenk = inst.type.container.length; k < lenk; k++) { t = inst.type.container[k]; if (inst.type === t) continue; if (t.instances.length > iid) inst.siblings.push(t.instances[iid]); else { if (!t.default_instance) { } else { s = this.runtime.createInstanceFromInit(t.default_instance, inst.layer, true, inst.x, inst.y, true); this.runtime.ClearDeathRow(); t.updateIIDs(); inst.siblings.push(s); created_instances.push(s); // come back around and link up its own instances too } } } } for (i = 0, len = this.initial_nonworld.length; i < len; i++) { inst = this.runtime.createInstanceFromInit(this.initial_nonworld[i], null, true); ; } this.runtime.changelayout = null; this.runtime.ClearDeathRow(); if (this.runtime.ctx && !this.runtime.isDomFree) { for (i = 0, len = this.runtime.types_by_index.length; i < len; i++) { t = this.runtime.types_by_index[i]; if (t.is_family || !t.instances.length || !t.preloadCanvas2D) continue; t.preloadCanvas2D(this.runtime.ctx); } } /* if (this.runtime.glwrap) { console.log("Estimated VRAM at layout start: " + this.runtime.glwrap.textureCount() + " textures, approx. " + Math.round(this.runtime.glwrap.estimateVRAM() / 1024) + " kb"); } */ if (this.runtime.isLoadingState) { cr.shallowAssignArray(this.runtime.fireOnCreateAfterLoad, created_instances); } else { for (i = 0, len = created_instances.length; i < len; i++) { inst = created_instances[i]; this.runtime.trigger(Object.getPrototypeOf(inst.type.plugin).cnds.OnCreated, inst); } } cr.clearArray(created_instances); if (!this.runtime.isLoadingState) { this.runtime.trigger(cr.system_object.prototype.cnds.OnLayoutStart, null); } this.first_visit = false; }; Layout.prototype.createGlobalNonWorlds = function () { var i, k, len, initial_inst, inst, type; for (i = 0, k = 0, len = this.initial_nonworld.length; i < len; i++) { initial_inst = this.initial_nonworld[i]; type = this.runtime.types_by_index[initial_inst[1]]; if (type.global) { if (!type.is_contained) { inst = this.runtime.createInstanceFromInit(initial_inst, null, true); } } else { this.initial_nonworld[k] = initial_inst; k++; } } cr.truncateArray(this.initial_nonworld, k); }; Layout.prototype.stopRunning = function () { ; /* if (this.runtime.glwrap) { console.log("Estimated VRAM at layout end: " + this.runtime.glwrap.textureCount() + " textures, approx. " + Math.round(this.runtime.glwrap.estimateVRAM() / 1024) + " kb"); } */ if (!this.runtime.isLoadingState) { this.runtime.trigger(cr.system_object.prototype.cnds.OnLayoutEnd, null); } this.runtime.isEndingLayout = true; cr.clearArray(this.runtime.system.waits); var i, leni, j, lenj; var layer_instances, inst, type; if (!this.first_visit) { for (i = 0, leni = this.layers.length; i < leni; i++) { this.layers[i].updateZIndices(); layer_instances = this.layers[i].instances; for (j = 0, lenj = layer_instances.length; j < lenj; j++) { inst = layer_instances[j]; if (!inst.type.global) { if (this.runtime.typeHasPersistBehavior(inst.type)) this.saveObjectToPersist(inst); } } } } for (i = 0, leni = this.layers.length; i < leni; i++) { layer_instances = this.layers[i].instances; for (j = 0, lenj = layer_instances.length; j < lenj; j++) { inst = layer_instances[j]; if (!inst.type.global) { this.runtime.DestroyInstance(inst); } } this.runtime.ClearDeathRow(); cr.clearArray(layer_instances); this.layers[i].zindices_stale = true; } for (i = 0, leni = this.runtime.types_by_index.length; i < leni; i++) { type = this.runtime.types_by_index[i]; if (type.global || type.plugin.is_world || type.plugin.singleglobal || type.is_family) continue; for (j = 0, lenj = type.instances.length; j < lenj; j++) this.runtime.DestroyInstance(type.instances[j]); this.runtime.ClearDeathRow(); } first_layout = false; this.runtime.isEndingLayout = false; }; var temp_rect = new cr.rect(0, 0, 0, 0); Layout.prototype.recreateInitialObjects = function (type, x1, y1, x2, y2) { temp_rect.set(x1, y1, x2, y2); var i, len; for (i = 0, len = this.layers.length; i < len; i++) { this.layers[i].recreateInitialObjects(type, temp_rect); } }; Layout.prototype.draw = function (ctx) { var layout_canvas; var layout_ctx = ctx; var ctx_changed = false; var render_offscreen = !this.runtime.fullscreenScalingQuality; if (render_offscreen) { if (!this.runtime.layout_canvas) { this.runtime.layout_canvas = document.createElement("canvas"); layout_canvas = this.runtime.layout_canvas; layout_canvas.width = this.runtime.draw_width; layout_canvas.height = this.runtime.draw_height; this.runtime.layout_ctx = layout_canvas.getContext("2d"); ctx_changed = true; } layout_canvas = this.runtime.layout_canvas; layout_ctx = this.runtime.layout_ctx; if (layout_canvas.width !== this.runtime.draw_width) { layout_canvas.width = this.runtime.draw_width; ctx_changed = true; } if (layout_canvas.height !== this.runtime.draw_height) { layout_canvas.height = this.runtime.draw_height; ctx_changed = true; } if (ctx_changed) { this.runtime.setCtxImageSmoothingEnabled(layout_ctx, this.runtime.linearSampling); } } layout_ctx.globalAlpha = 1; layout_ctx.globalCompositeOperation = "source-over"; if (this.runtime.clearBackground && !this.hasOpaqueBottomLayer()) layout_ctx.clearRect(0, 0, this.runtime.draw_width, this.runtime.draw_height); var i, len, l; for (i = 0, len = this.layers.length; i < len; i++) { l = this.layers[i]; if (l.visible && l.opacity > 0 && l.blend_mode !== 11 && (l.instances.length || !l.transparent)) l.draw(layout_ctx); else l.updateViewport(null); // even if not drawing, keep viewport up to date } if (render_offscreen) { ctx.drawImage(layout_canvas, 0, 0, this.runtime.width, this.runtime.height); } }; Layout.prototype.drawGL_earlyZPass = function (glw) { glw.setEarlyZPass(true); if (!this.runtime.layout_tex) { this.runtime.layout_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } if (this.runtime.layout_tex.c2width !== this.runtime.draw_width || this.runtime.layout_tex.c2height !== this.runtime.draw_height) { glw.deleteTexture(this.runtime.layout_tex); this.runtime.layout_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } glw.setRenderingToTexture(this.runtime.layout_tex); if (!this.runtime.fullscreenScalingQuality) { glw.setSize(this.runtime.draw_width, this.runtime.draw_height); } var i, l; for (i = this.layers.length - 1; i >= 0; --i) { l = this.layers[i]; if (l.visible && l.opacity === 1 && l.shaders_preserve_opaqueness && l.blend_mode === 0 && (l.instances.length || !l.transparent)) { l.drawGL_earlyZPass(glw); } else { l.updateViewport(null); // even if not drawing, keep viewport up to date } } glw.setEarlyZPass(false); }; Layout.prototype.drawGL = function (glw) { var render_to_texture = (this.active_effect_types.length > 0 || this.runtime.uses_background_blending || !this.runtime.fullscreenScalingQuality || this.runtime.enableFrontToBack); if (render_to_texture) { if (!this.runtime.layout_tex) { this.runtime.layout_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } if (this.runtime.layout_tex.c2width !== this.runtime.draw_width || this.runtime.layout_tex.c2height !== this.runtime.draw_height) { glw.deleteTexture(this.runtime.layout_tex); this.runtime.layout_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } glw.setRenderingToTexture(this.runtime.layout_tex); if (!this.runtime.fullscreenScalingQuality) { glw.setSize(this.runtime.draw_width, this.runtime.draw_height); } } else { if (this.runtime.layout_tex) { glw.setRenderingToTexture(null); glw.deleteTexture(this.runtime.layout_tex); this.runtime.layout_tex = null; } } if (this.runtime.clearBackground && !this.hasOpaqueBottomLayer()) glw.clear(0, 0, 0, 0); var i, len, l; for (i = 0, len = this.layers.length; i < len; i++) { l = this.layers[i]; if (l.visible && l.opacity > 0 && (l.instances.length || !l.transparent)) l.drawGL(glw); else l.updateViewport(null); // even if not drawing, keep viewport up to date } if (render_to_texture) { if (this.active_effect_types.length === 0 || (this.active_effect_types.length === 1 && this.runtime.fullscreenScalingQuality)) { if (this.active_effect_types.length === 1) { var etindex = this.active_effect_types[0].index; glw.switchProgram(this.active_effect_types[0].shaderindex); glw.setProgramParameters(null, // backTex 1.0 / this.runtime.draw_width, // pixelWidth 1.0 / this.runtime.draw_height, // pixelHeight 0.0, 0.0, // destStart 1.0, 1.0, // destEnd this.scale, // layerScale this.angle, // layerAngle 0.0, 0.0, // viewOrigin this.runtime.draw_width / 2, this.runtime.draw_height / 2, // scrollPos this.runtime.kahanTime.sum, // seconds this.effect_params[etindex]); // fx parameters if (glw.programIsAnimated(this.active_effect_types[0].shaderindex)) this.runtime.redraw = true; } else glw.switchProgram(0); if (!this.runtime.fullscreenScalingQuality) { glw.setSize(this.runtime.width, this.runtime.height); } glw.setRenderingToTexture(null); // to backbuffer glw.setDepthTestEnabled(false); // ignore depth buffer, copy full texture glw.setOpacity(1); glw.setTexture(this.runtime.layout_tex); glw.setAlphaBlend(); glw.resetModelView(); glw.updateModelView(); var halfw = this.runtime.width / 2; var halfh = this.runtime.height / 2; glw.quad(-halfw, halfh, halfw, halfh, halfw, -halfh, -halfw, -halfh); glw.setTexture(null); glw.setDepthTestEnabled(true); // turn depth test back on } else { this.renderEffectChain(glw, null, null, null); } } }; Layout.prototype.getRenderTarget = function() { if (this.active_effect_types.length > 0 || this.runtime.uses_background_blending || !this.runtime.fullscreenScalingQuality || this.runtime.enableFrontToBack) { return this.runtime.layout_tex; } else { return null; } }; Layout.prototype.getMinLayerScale = function () { var m = this.layers[0].getScale(); var i, len, l; for (i = 1, len = this.layers.length; i < len; i++) { l = this.layers[i]; if (l.parallaxX === 0 && l.parallaxY === 0) continue; if (l.getScale() < m) m = l.getScale(); } return m; }; Layout.prototype.scrollToX = function (x) { if (!this.unbounded_scrolling) { var widthBoundary = (this.runtime.draw_width * (1 / this.getMinLayerScale()) / 2); if (x > this.width - widthBoundary) x = this.width - widthBoundary; if (x < widthBoundary) x = widthBoundary; } if (this.scrollX !== x) { this.scrollX = x; this.runtime.redraw = true; } }; Layout.prototype.scrollToY = function (y) { if (!this.unbounded_scrolling) { var heightBoundary = (this.runtime.draw_height * (1 / this.getMinLayerScale()) / 2); if (y > this.height - heightBoundary) y = this.height - heightBoundary; if (y < heightBoundary) y = heightBoundary; } if (this.scrollY !== y) { this.scrollY = y; this.runtime.redraw = true; } }; Layout.prototype.boundScrolling = function () { this.scrollToX(this.scrollX); this.scrollToY(this.scrollY); }; Layout.prototype.renderEffectChain = function (glw, layer, inst, rendertarget) { var active_effect_types = inst ? inst.active_effect_types : layer ? layer.active_effect_types : this.active_effect_types; var layerScale = 1, layerAngle = 0, viewOriginLeft = 0, viewOriginTop = 0, viewOriginRight = this.runtime.draw_width, viewOriginBottom = this.runtime.draw_height; if (inst) { layerScale = inst.layer.getScale(); layerAngle = inst.layer.getAngle(); viewOriginLeft = inst.layer.viewLeft; viewOriginTop = inst.layer.viewTop; viewOriginRight = inst.layer.viewRight; viewOriginBottom = inst.layer.viewBottom; } else if (layer) { layerScale = layer.getScale(); layerAngle = layer.getAngle(); viewOriginLeft = layer.viewLeft; viewOriginTop = layer.viewTop; viewOriginRight = layer.viewRight; viewOriginBottom = layer.viewBottom; } var fx_tex = this.runtime.fx_tex; var i, len, last, temp, fx_index = 0, other_fx_index = 1; var y, h; var windowWidth = this.runtime.draw_width; var windowHeight = this.runtime.draw_height; var halfw = windowWidth / 2; var halfh = windowHeight / 2; var rcTex = layer ? layer.rcTex : this.rcTex; var rcTex2 = layer ? layer.rcTex2 : this.rcTex2; var screenleft = 0, clearleft = 0; var screentop = 0, cleartop = 0; var screenright = windowWidth, clearright = windowWidth; var screenbottom = windowHeight, clearbottom = windowHeight; var boxExtendHorizontal = 0; var boxExtendVertical = 0; var inst_layer_angle = inst ? inst.layer.getAngle() : 0; if (inst) { for (i = 0, len = active_effect_types.length; i < len; i++) { boxExtendHorizontal += glw.getProgramBoxExtendHorizontal(active_effect_types[i].shaderindex); boxExtendVertical += glw.getProgramBoxExtendVertical(active_effect_types[i].shaderindex); } var bbox = inst.bbox; screenleft = layer.layerToCanvas(bbox.left, bbox.top, true, true); screentop = layer.layerToCanvas(bbox.left, bbox.top, false, true); screenright = layer.layerToCanvas(bbox.right, bbox.bottom, true, true); screenbottom = layer.layerToCanvas(bbox.right, bbox.bottom, false, true); if (inst_layer_angle !== 0) { var screentrx = layer.layerToCanvas(bbox.right, bbox.top, true, true); var screentry = layer.layerToCanvas(bbox.right, bbox.top, false, true); var screenblx = layer.layerToCanvas(bbox.left, bbox.bottom, true, true); var screenbly = layer.layerToCanvas(bbox.left, bbox.bottom, false, true); temp = Math.min(screenleft, screenright, screentrx, screenblx); screenright = Math.max(screenleft, screenright, screentrx, screenblx); screenleft = temp; temp = Math.min(screentop, screenbottom, screentry, screenbly); screenbottom = Math.max(screentop, screenbottom, screentry, screenbly); screentop = temp; } screenleft -= boxExtendHorizontal; screentop -= boxExtendVertical; screenright += boxExtendHorizontal; screenbottom += boxExtendVertical; rcTex2.left = screenleft / windowWidth; rcTex2.top = 1 - screentop / windowHeight; rcTex2.right = screenright / windowWidth; rcTex2.bottom = 1 - screenbottom / windowHeight; clearleft = screenleft = cr.floor(screenleft); cleartop = screentop = cr.floor(screentop); clearright = screenright = cr.ceil(screenright); clearbottom = screenbottom = cr.ceil(screenbottom); clearleft -= boxExtendHorizontal; cleartop -= boxExtendVertical; clearright += boxExtendHorizontal; clearbottom += boxExtendVertical; if (screenleft < 0) screenleft = 0; if (screentop < 0) screentop = 0; if (screenright > windowWidth) screenright = windowWidth; if (screenbottom > windowHeight) screenbottom = windowHeight; if (clearleft < 0) clearleft = 0; if (cleartop < 0) cleartop = 0; if (clearright > windowWidth) clearright = windowWidth; if (clearbottom > windowHeight) clearbottom = windowHeight; rcTex.left = screenleft / windowWidth; rcTex.top = 1 - screentop / windowHeight; rcTex.right = screenright / windowWidth; rcTex.bottom = 1 - screenbottom / windowHeight; } else { rcTex.left = rcTex2.left = 0; rcTex.top = rcTex2.top = 0; rcTex.right = rcTex2.right = 1; rcTex.bottom = rcTex2.bottom = 1; } var pre_draw = (inst && (glw.programUsesDest(active_effect_types[0].shaderindex) || boxExtendHorizontal !== 0 || boxExtendVertical !== 0 || inst.opacity !== 1 || inst.type.plugin.must_predraw)) || (layer && !inst && layer.opacity !== 1); glw.setAlphaBlend(); if (pre_draw) { if (!fx_tex[fx_index]) { fx_tex[fx_index] = glw.createEmptyTexture(windowWidth, windowHeight, this.runtime.linearSampling); } if (fx_tex[fx_index].c2width !== windowWidth || fx_tex[fx_index].c2height !== windowHeight) { glw.deleteTexture(fx_tex[fx_index]); fx_tex[fx_index] = glw.createEmptyTexture(windowWidth, windowHeight, this.runtime.linearSampling); } glw.switchProgram(0); glw.setRenderingToTexture(fx_tex[fx_index]); h = clearbottom - cleartop; y = (windowHeight - cleartop) - h; glw.clearRect(clearleft, y, clearright - clearleft, h); if (inst) { inst.drawGL(glw); } else { glw.setTexture(this.runtime.layer_tex); glw.setOpacity(layer.opacity); glw.resetModelView(); glw.translate(-halfw, -halfh); glw.updateModelView(); glw.quadTex(screenleft, screenbottom, screenright, screenbottom, screenright, screentop, screenleft, screentop, rcTex); } rcTex2.left = rcTex2.top = 0; rcTex2.right = rcTex2.bottom = 1; if (inst) { temp = rcTex.top; rcTex.top = rcTex.bottom; rcTex.bottom = temp; } fx_index = 1; other_fx_index = 0; } glw.setOpacity(1); var last = active_effect_types.length - 1; var post_draw = glw.programUsesCrossSampling(active_effect_types[last].shaderindex) || (!layer && !inst && !this.runtime.fullscreenScalingQuality); var etindex = 0; for (i = 0, len = active_effect_types.length; i < len; i++) { if (!fx_tex[fx_index]) { fx_tex[fx_index] = glw.createEmptyTexture(windowWidth, windowHeight, this.runtime.linearSampling); } if (fx_tex[fx_index].c2width !== windowWidth || fx_tex[fx_index].c2height !== windowHeight) { glw.deleteTexture(fx_tex[fx_index]); fx_tex[fx_index] = glw.createEmptyTexture(windowWidth, windowHeight, this.runtime.linearSampling); } glw.switchProgram(active_effect_types[i].shaderindex); etindex = active_effect_types[i].index; if (glw.programIsAnimated(active_effect_types[i].shaderindex)) this.runtime.redraw = true; if (i == 0 && !pre_draw) { glw.setRenderingToTexture(fx_tex[fx_index]); h = clearbottom - cleartop; y = (windowHeight - cleartop) - h; glw.clearRect(clearleft, y, clearright - clearleft, h); if (inst) { var pixelWidth; var pixelHeight; if (inst.curFrame && inst.curFrame.texture_img) { var img = inst.curFrame.texture_img; pixelWidth = 1.0 / img.width; pixelHeight = 1.0 / img.height; } else { pixelWidth = 1.0 / inst.width; pixelHeight = 1.0 / inst.height; } glw.setProgramParameters(rendertarget, // backTex pixelWidth, pixelHeight, rcTex2.left, rcTex2.top, // destStart rcTex2.right, rcTex2.bottom, // destEnd layerScale, layerAngle, viewOriginLeft, viewOriginTop, (viewOriginLeft + viewOriginRight) / 2, (viewOriginTop + viewOriginBottom) / 2, this.runtime.kahanTime.sum, inst.effect_params[etindex]); // fx params inst.drawGL(glw); } else { glw.setProgramParameters(rendertarget, // backTex 1.0 / windowWidth, // pixelWidth 1.0 / windowHeight, // pixelHeight 0.0, 0.0, // destStart 1.0, 1.0, // destEnd layerScale, layerAngle, viewOriginLeft, viewOriginTop, (viewOriginLeft + viewOriginRight) / 2, (viewOriginTop + viewOriginBottom) / 2, this.runtime.kahanTime.sum, layer ? // fx params layer.effect_params[etindex] : this.effect_params[etindex]); glw.setTexture(layer ? this.runtime.layer_tex : this.runtime.layout_tex); glw.resetModelView(); glw.translate(-halfw, -halfh); glw.updateModelView(); glw.quadTex(screenleft, screenbottom, screenright, screenbottom, screenright, screentop, screenleft, screentop, rcTex); } rcTex2.left = rcTex2.top = 0; rcTex2.right = rcTex2.bottom = 1; if (inst && !post_draw) { temp = screenbottom; screenbottom = screentop; screentop = temp; } } else { glw.setProgramParameters(rendertarget, // backTex 1.0 / windowWidth, // pixelWidth 1.0 / windowHeight, // pixelHeight rcTex2.left, rcTex2.top, // destStart rcTex2.right, rcTex2.bottom, // destEnd layerScale, layerAngle, viewOriginLeft, viewOriginTop, (viewOriginLeft + viewOriginRight) / 2, (viewOriginTop + viewOriginBottom) / 2, this.runtime.kahanTime.sum, inst ? // fx params inst.effect_params[etindex] : layer ? layer.effect_params[etindex] : this.effect_params[etindex]); glw.setTexture(null); if (i === last && !post_draw) { if (inst) glw.setBlend(inst.srcBlend, inst.destBlend); else if (layer) glw.setBlend(layer.srcBlend, layer.destBlend); glw.setRenderingToTexture(rendertarget); } else { glw.setRenderingToTexture(fx_tex[fx_index]); h = clearbottom - cleartop; y = (windowHeight - cleartop) - h; glw.clearRect(clearleft, y, clearright - clearleft, h); } glw.setTexture(fx_tex[other_fx_index]); glw.resetModelView(); glw.translate(-halfw, -halfh); glw.updateModelView(); glw.quadTex(screenleft, screenbottom, screenright, screenbottom, screenright, screentop, screenleft, screentop, rcTex); if (i === last && !post_draw) glw.setTexture(null); } fx_index = (fx_index === 0 ? 1 : 0); other_fx_index = (fx_index === 0 ? 1 : 0); // will be opposite to fx_index since it was just assigned } if (post_draw) { glw.switchProgram(0); if (inst) glw.setBlend(inst.srcBlend, inst.destBlend); else if (layer) glw.setBlend(layer.srcBlend, layer.destBlend); else { if (!this.runtime.fullscreenScalingQuality) { glw.setSize(this.runtime.width, this.runtime.height); halfw = this.runtime.width / 2; halfh = this.runtime.height / 2; screenleft = 0; screentop = 0; screenright = this.runtime.width; screenbottom = this.runtime.height; } } glw.setRenderingToTexture(rendertarget); glw.setTexture(fx_tex[other_fx_index]); glw.resetModelView(); glw.translate(-halfw, -halfh); glw.updateModelView(); if (inst && active_effect_types.length === 1 && !pre_draw) glw.quadTex(screenleft, screentop, screenright, screentop, screenright, screenbottom, screenleft, screenbottom, rcTex); else glw.quadTex(screenleft, screenbottom, screenright, screenbottom, screenright, screentop, screenleft, screentop, rcTex); glw.setTexture(null); } }; Layout.prototype.getLayerBySid = function (sid_) { var i, len; for (i = 0, len = this.layers.length; i < len; i++) { if (this.layers[i].sid === sid_) return this.layers[i]; } return null; }; Layout.prototype.saveToJSON = function () { var i, len, layer, et; var o = { "sx": this.scrollX, "sy": this.scrollY, "s": this.scale, "a": this.angle, "w": this.width, "h": this.height, "fv": this.first_visit, // added r127 "persist": this.persist_data, "fx": [], "layers": {} }; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; o["fx"].push({"name": et.name, "active": et.active, "params": this.effect_params[et.index] }); } for (i = 0, len = this.layers.length; i < len; i++) { layer = this.layers[i]; o["layers"][layer.sid.toString()] = layer.saveToJSON(); } return o; }; Layout.prototype.loadFromJSON = function (o) { var i, j, len, fx, p, layer; this.scrollX = o["sx"]; this.scrollY = o["sy"]; this.scale = o["s"]; this.angle = o["a"]; this.width = o["w"]; this.height = o["h"]; this.persist_data = o["persist"]; if (typeof o["fv"] !== "undefined") this.first_visit = o["fv"]; var ofx = o["fx"]; for (i = 0, len = ofx.length; i < len; i++) { fx = this.getEffectByName(ofx[i]["name"]); if (!fx) continue; // must've gone missing fx.active = ofx[i]["active"]; this.effect_params[fx.index] = ofx[i]["params"]; } this.updateActiveEffects(); var olayers = o["layers"]; for (p in olayers) { if (olayers.hasOwnProperty(p)) { layer = this.getLayerBySid(parseInt(p, 10)); if (!layer) continue; // must've gone missing layer.loadFromJSON(olayers[p]); } } }; cr.layout = Layout; function Layer(layout, m) { this.layout = layout; this.runtime = layout.runtime; this.instances = []; // running instances this.scale = 1.0; this.angle = 0; this.disableAngle = false; this.tmprect = new cr.rect(0, 0, 0, 0); this.tmpquad = new cr.quad(); this.viewLeft = 0; this.viewRight = 0; this.viewTop = 0; this.viewBottom = 0; this.zindices_stale = false; this.zindices_stale_from = -1; // first index that has changed, or -1 if no bound this.clear_earlyz_index = 0; this.name = m[0]; this.index = m[1]; this.sid = m[2]; this.visible = m[3]; // initially visible this.background_color = m[4]; this.transparent = m[5]; this.parallaxX = m[6]; this.parallaxY = m[7]; this.opacity = m[8]; this.forceOwnTexture = m[9]; this.useRenderCells = m[10]; this.zoomRate = m[11]; this.blend_mode = m[12]; this.effect_fallback = m[13]; this.compositeOp = "source-over"; this.srcBlend = 0; this.destBlend = 0; this.render_grid = null; this.last_render_list = alloc_arr(); this.render_list_stale = true; this.last_render_cells = new cr.rect(0, 0, -1, -1); this.cur_render_cells = new cr.rect(0, 0, -1, -1); if (this.useRenderCells) { this.render_grid = new cr.RenderGrid(this.runtime.original_width, this.runtime.original_height); } this.render_offscreen = false; var im = m[14]; var i, len; this.startup_initial_instances = []; // for restoring initial_instances after load this.initial_instances = []; this.created_globals = []; // global object UIDs already created - for save/load to avoid recreating for (i = 0, len = im.length; i < len; i++) { var inst = im[i]; var type = this.runtime.types_by_index[inst[1]]; ; if (!type.default_instance) { type.default_instance = inst; type.default_layerindex = this.index; } this.initial_instances.push(inst); if (this.layout.initial_types.indexOf(type) === -1) this.layout.initial_types.push(type); } cr.shallowAssignArray(this.startup_initial_instances, this.initial_instances); this.effect_types = []; this.active_effect_types = []; this.shaders_preserve_opaqueness = true; this.effect_params = []; for (i = 0, len = m[15].length; i < len; i++) { this.effect_types.push({ id: m[15][i][0], name: m[15][i][1], shaderindex: -1, preservesOpaqueness: false, active: true, index: i }); this.effect_params.push(m[15][i][2].slice(0)); } this.updateActiveEffects(); this.rcTex = new cr.rect(0, 0, 1, 1); this.rcTex2 = new cr.rect(0, 0, 1, 1); }; Layer.prototype.updateActiveEffects = function () { cr.clearArray(this.active_effect_types); this.shaders_preserve_opaqueness = true; var i, len, et; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; if (et.active) { this.active_effect_types.push(et); if (!et.preservesOpaqueness) this.shaders_preserve_opaqueness = false; } } }; Layer.prototype.getEffectByName = function (name_) { var i, len, et; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; if (et.name === name_) return et; } return null; }; Layer.prototype.createInitialInstances = function () { var i, k, len, inst, initial_inst, type, keep, hasPersistBehavior; for (i = 0, k = 0, len = this.initial_instances.length; i < len; i++) { initial_inst = this.initial_instances[i]; type = this.runtime.types_by_index[initial_inst[1]]; ; hasPersistBehavior = this.runtime.typeHasPersistBehavior(type); keep = true; if (!hasPersistBehavior || this.layout.first_visit) { inst = this.runtime.createInstanceFromInit(initial_inst, this, true); if (!inst) continue; // may have skipped creation due to fallback effect "destroy" created_instances.push(inst); if (inst.type.global) { keep = false; this.created_globals.push(inst.uid); } } if (keep) { this.initial_instances[k] = this.initial_instances[i]; k++; } } this.initial_instances.length = k; this.runtime.ClearDeathRow(); // flushes creation row so IIDs will be correct if (!this.runtime.glwrap && this.effect_types.length) // no WebGL renderer and shaders used this.blend_mode = this.effect_fallback; // use fallback blend mode this.compositeOp = cr.effectToCompositeOp(this.blend_mode); if (this.runtime.gl) cr.setGLBlend(this, this.blend_mode, this.runtime.gl); this.render_list_stale = true; }; Layer.prototype.recreateInitialObjects = function (only_type, rc) { var i, len, initial_inst, type, wm, x, y, inst, j, lenj, s; var types_by_index = this.runtime.types_by_index; var only_type_is_family = only_type.is_family; var only_type_members = only_type.members; for (i = 0, len = this.initial_instances.length; i < len; ++i) { initial_inst = this.initial_instances[i]; wm = initial_inst[0]; x = wm[0]; y = wm[1]; if (!rc.contains_pt(x, y)) continue; // not in the given area type = types_by_index[initial_inst[1]]; if (type !== only_type) { if (only_type_is_family) { if (only_type_members.indexOf(type) < 0) continue; } else continue; // only_type is not a family, and the initial inst type does not match } inst = this.runtime.createInstanceFromInit(initial_inst, this, false); this.runtime.isInOnDestroy++; this.runtime.trigger(Object.getPrototypeOf(type.plugin).cnds.OnCreated, inst); if (inst.is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[i]; this.runtime.trigger(Object.getPrototypeOf(s.type.plugin).cnds.OnCreated, s); } } this.runtime.isInOnDestroy--; } }; Layer.prototype.removeFromInstanceList = function (inst, remove_from_grid) { var index = cr.fastIndexOf(this.instances, inst); if (index < 0) return; // not found if (remove_from_grid && this.useRenderCells && inst.rendercells && inst.rendercells.right >= inst.rendercells.left) { inst.update_bbox(); // make sure actually in its current rendercells this.render_grid.update(inst, inst.rendercells, null); // no new range provided - remove only inst.rendercells.set(0, 0, -1, -1); // set to invalid state to indicate not inserted } if (index === this.instances.length - 1) this.instances.pop(); else { cr.arrayRemove(this.instances, index); this.setZIndicesStaleFrom(index); } this.render_list_stale = true; }; Layer.prototype.appendToInstanceList = function (inst, add_to_grid) { ; inst.zindex = this.instances.length; this.instances.push(inst); if (add_to_grid && this.useRenderCells && inst.rendercells) { inst.set_bbox_changed(); // will cause immediate update and new insertion to grid } this.render_list_stale = true; }; Layer.prototype.prependToInstanceList = function (inst, add_to_grid) { ; this.instances.unshift(inst); this.setZIndicesStaleFrom(0); if (add_to_grid && this.useRenderCells && inst.rendercells) { inst.set_bbox_changed(); // will cause immediate update and new insertion to grid } }; Layer.prototype.moveInstanceAdjacent = function (inst, other, isafter) { ; var myZ = inst.get_zindex(); var insertZ = other.get_zindex(); cr.arrayRemove(this.instances, myZ); if (myZ < insertZ) insertZ--; if (isafter) insertZ++; if (insertZ === this.instances.length) this.instances.push(inst); else this.instances.splice(insertZ, 0, inst); this.setZIndicesStaleFrom(myZ < insertZ ? myZ : insertZ); }; Layer.prototype.setZIndicesStaleFrom = function (index) { if (this.zindices_stale_from === -1) // not yet set this.zindices_stale_from = index; else if (index < this.zindices_stale_from) // determine minimum z index affected this.zindices_stale_from = index; this.zindices_stale = true; this.render_list_stale = true; }; Layer.prototype.updateZIndices = function () { if (!this.zindices_stale) return; if (this.zindices_stale_from === -1) this.zindices_stale_from = 0; var i, len, inst; if (this.useRenderCells) { for (i = this.zindices_stale_from, len = this.instances.length; i < len; ++i) { inst = this.instances[i]; inst.zindex = i; this.render_grid.markRangeChanged(inst.rendercells); } } else { for (i = this.zindices_stale_from, len = this.instances.length; i < len; ++i) { this.instances[i].zindex = i; } } this.zindices_stale = false; this.zindices_stale_from = -1; }; Layer.prototype.getScale = function (include_aspect) { return this.getNormalScale() * (this.runtime.fullscreenScalingQuality || include_aspect ? this.runtime.aspect_scale : 1); }; Layer.prototype.getNormalScale = function () { return ((this.scale * this.layout.scale) - 1) * this.zoomRate + 1; }; Layer.prototype.getAngle = function () { if (this.disableAngle) return 0; return cr.clamp_angle(this.layout.angle + this.angle); }; var arr_cache = []; function alloc_arr() { if (arr_cache.length) return arr_cache.pop(); else return []; } function free_arr(a) { cr.clearArray(a); arr_cache.push(a); }; function mergeSortedZArrays(a, b, out) { var i = 0, j = 0, k = 0, lena = a.length, lenb = b.length, ai, bj; out.length = lena + lenb; for ( ; i < lena && j < lenb; ++k) { ai = a[i]; bj = b[j]; if (ai.zindex < bj.zindex) { out[k] = ai; ++i; } else { out[k] = bj; ++j; } } for ( ; i < lena; ++i, ++k) out[k] = a[i]; for ( ; j < lenb; ++j, ++k) out[k] = b[j]; }; var next_arr = []; function mergeAllSortedZArrays_pass(arr, first_pass) { var i, len, arr1, arr2, out; for (i = 0, len = arr.length; i < len - 1; i += 2) { arr1 = arr[i]; arr2 = arr[i+1]; out = alloc_arr(); mergeSortedZArrays(arr1, arr2, out); if (!first_pass) { free_arr(arr1); free_arr(arr2); } next_arr.push(out); } if (len % 2 === 1) { if (first_pass) { arr1 = alloc_arr(); cr.shallowAssignArray(arr1, arr[len - 1]); next_arr.push(arr1); } else { next_arr.push(arr[len - 1]); } } cr.shallowAssignArray(arr, next_arr); cr.clearArray(next_arr); }; function mergeAllSortedZArrays(arr) { var first_pass = true; while (arr.length > 1) { mergeAllSortedZArrays_pass(arr, first_pass); first_pass = false; } return arr[0]; }; var render_arr = []; Layer.prototype.getRenderCellInstancesToDraw = function () { ; this.updateZIndices(); this.render_grid.queryRange(this.viewLeft, this.viewTop, this.viewRight, this.viewBottom, render_arr); if (!render_arr.length) return alloc_arr(); if (render_arr.length === 1) { var a = alloc_arr(); cr.shallowAssignArray(a, render_arr[0]); cr.clearArray(render_arr); return a; } var draw_list = mergeAllSortedZArrays(render_arr); cr.clearArray(render_arr); return draw_list; }; Layer.prototype.draw = function (ctx) { this.render_offscreen = (this.forceOwnTexture || this.opacity !== 1.0 || this.blend_mode !== 0); var layer_canvas = this.runtime.canvas; var layer_ctx = ctx; var ctx_changed = false; if (this.render_offscreen) { if (!this.runtime.layer_canvas) { this.runtime.layer_canvas = document.createElement("canvas"); ; layer_canvas = this.runtime.layer_canvas; layer_canvas.width = this.runtime.draw_width; layer_canvas.height = this.runtime.draw_height; this.runtime.layer_ctx = layer_canvas.getContext("2d"); ; ctx_changed = true; } layer_canvas = this.runtime.layer_canvas; layer_ctx = this.runtime.layer_ctx; if (layer_canvas.width !== this.runtime.draw_width) { layer_canvas.width = this.runtime.draw_width; ctx_changed = true; } if (layer_canvas.height !== this.runtime.draw_height) { layer_canvas.height = this.runtime.draw_height; ctx_changed = true; } if (ctx_changed) { this.runtime.setCtxImageSmoothingEnabled(layer_ctx, this.runtime.linearSampling); } if (this.transparent) layer_ctx.clearRect(0, 0, this.runtime.draw_width, this.runtime.draw_height); } layer_ctx.globalAlpha = 1; layer_ctx.globalCompositeOperation = "source-over"; if (!this.transparent) { layer_ctx.fillStyle = "rgb(" + this.background_color[0] + "," + this.background_color[1] + "," + this.background_color[2] + ")"; layer_ctx.fillRect(0, 0, this.runtime.draw_width, this.runtime.draw_height); } layer_ctx.save(); this.disableAngle = true; var px = this.canvasToLayer(0, 0, true, true); var py = this.canvasToLayer(0, 0, false, true); this.disableAngle = false; if (this.runtime.pixel_rounding) { px = Math.round(px); py = Math.round(py); } this.rotateViewport(px, py, layer_ctx); var myscale = this.getScale(); layer_ctx.scale(myscale, myscale); layer_ctx.translate(-px, -py); var instances_to_draw; if (this.useRenderCells) { this.cur_render_cells.left = this.render_grid.XToCell(this.viewLeft); this.cur_render_cells.top = this.render_grid.YToCell(this.viewTop); this.cur_render_cells.right = this.render_grid.XToCell(this.viewRight); this.cur_render_cells.bottom = this.render_grid.YToCell(this.viewBottom); if (this.render_list_stale || !this.cur_render_cells.equals(this.last_render_cells)) { free_arr(this.last_render_list); instances_to_draw = this.getRenderCellInstancesToDraw(); this.render_list_stale = false; this.last_render_cells.copy(this.cur_render_cells); } else instances_to_draw = this.last_render_list; } else instances_to_draw = this.instances; var i, len, inst, last_inst = null; for (i = 0, len = instances_to_draw.length; i < len; ++i) { inst = instances_to_draw[i]; if (inst === last_inst) continue; this.drawInstance(inst, layer_ctx); last_inst = inst; } if (this.useRenderCells) this.last_render_list = instances_to_draw; layer_ctx.restore(); if (this.render_offscreen) { ctx.globalCompositeOperation = this.compositeOp; ctx.globalAlpha = this.opacity; ctx.drawImage(layer_canvas, 0, 0); } }; Layer.prototype.drawInstance = function(inst, layer_ctx) { if (!inst.visible || inst.width === 0 || inst.height === 0) return; inst.update_bbox(); var bbox = inst.bbox; if (bbox.right < this.viewLeft || bbox.bottom < this.viewTop || bbox.left > this.viewRight || bbox.top > this.viewBottom) return; layer_ctx.globalCompositeOperation = inst.compositeOp; inst.draw(layer_ctx); }; Layer.prototype.updateViewport = function (ctx) { this.disableAngle = true; var px = this.canvasToLayer(0, 0, true, true); var py = this.canvasToLayer(0, 0, false, true); this.disableAngle = false; if (this.runtime.pixel_rounding) { px = Math.round(px); py = Math.round(py); } this.rotateViewport(px, py, ctx); }; Layer.prototype.rotateViewport = function (px, py, ctx) { var myscale = this.getScale(); this.viewLeft = px; this.viewTop = py; this.viewRight = px + (this.runtime.draw_width * (1 / myscale)); this.viewBottom = py + (this.runtime.draw_height * (1 / myscale)); var temp; if (this.viewLeft > this.viewRight) { temp = this.viewLeft; this.viewLeft = this.viewRight; this.viewRight = temp; } if (this.viewTop > this.viewBottom) { temp = this.viewTop; this.viewTop = this.viewBottom; this.viewBottom = temp; } var myAngle = this.getAngle(); if (myAngle !== 0) { if (ctx) { ctx.translate(this.runtime.draw_width / 2, this.runtime.draw_height / 2); ctx.rotate(-myAngle); ctx.translate(this.runtime.draw_width / -2, this.runtime.draw_height / -2); } this.tmprect.set(this.viewLeft, this.viewTop, this.viewRight, this.viewBottom); this.tmprect.offset((this.viewLeft + this.viewRight) / -2, (this.viewTop + this.viewBottom) / -2); this.tmpquad.set_from_rotated_rect(this.tmprect, myAngle); this.tmpquad.bounding_box(this.tmprect); this.tmprect.offset((this.viewLeft + this.viewRight) / 2, (this.viewTop + this.viewBottom) / 2); this.viewLeft = this.tmprect.left; this.viewTop = this.tmprect.top; this.viewRight = this.tmprect.right; this.viewBottom = this.tmprect.bottom; } } Layer.prototype.drawGL_earlyZPass = function (glw) { var windowWidth = this.runtime.draw_width; var windowHeight = this.runtime.draw_height; var shaderindex = 0; var etindex = 0; this.render_offscreen = this.forceOwnTexture; if (this.render_offscreen) { if (!this.runtime.layer_tex) { this.runtime.layer_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } if (this.runtime.layer_tex.c2width !== this.runtime.draw_width || this.runtime.layer_tex.c2height !== this.runtime.draw_height) { glw.deleteTexture(this.runtime.layer_tex); this.runtime.layer_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } glw.setRenderingToTexture(this.runtime.layer_tex); } this.disableAngle = true; var px = this.canvasToLayer(0, 0, true, true); var py = this.canvasToLayer(0, 0, false, true); this.disableAngle = false; if (this.runtime.pixel_rounding) { px = Math.round(px); py = Math.round(py); } this.rotateViewport(px, py, null); var myscale = this.getScale(); glw.resetModelView(); glw.scale(myscale, myscale); glw.rotateZ(-this.getAngle()); glw.translate((this.viewLeft + this.viewRight) / -2, (this.viewTop + this.viewBottom) / -2); glw.updateModelView(); var instances_to_draw; if (this.useRenderCells) { this.cur_render_cells.left = this.render_grid.XToCell(this.viewLeft); this.cur_render_cells.top = this.render_grid.YToCell(this.viewTop); this.cur_render_cells.right = this.render_grid.XToCell(this.viewRight); this.cur_render_cells.bottom = this.render_grid.YToCell(this.viewBottom); if (this.render_list_stale || !this.cur_render_cells.equals(this.last_render_cells)) { free_arr(this.last_render_list); instances_to_draw = this.getRenderCellInstancesToDraw(); this.render_list_stale = false; this.last_render_cells.copy(this.cur_render_cells); } else instances_to_draw = this.last_render_list; } else instances_to_draw = this.instances; var i, inst, last_inst = null; for (i = instances_to_draw.length - 1; i >= 0; --i) { inst = instances_to_draw[i]; if (inst === last_inst) continue; this.drawInstanceGL_earlyZPass(instances_to_draw[i], glw); last_inst = inst; } if (this.useRenderCells) this.last_render_list = instances_to_draw; if (!this.transparent) { this.clear_earlyz_index = this.runtime.earlyz_index++; glw.setEarlyZIndex(this.clear_earlyz_index); glw.setColorFillMode(1, 1, 1, 1); glw.fullscreenQuad(); // fill remaining space in depth buffer with current Z value glw.restoreEarlyZMode(); } }; Layer.prototype.drawGL = function (glw) { var windowWidth = this.runtime.draw_width; var windowHeight = this.runtime.draw_height; var shaderindex = 0; var etindex = 0; this.render_offscreen = (this.forceOwnTexture || this.opacity !== 1.0 || this.active_effect_types.length > 0 || this.blend_mode !== 0); if (this.render_offscreen) { if (!this.runtime.layer_tex) { this.runtime.layer_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } if (this.runtime.layer_tex.c2width !== this.runtime.draw_width || this.runtime.layer_tex.c2height !== this.runtime.draw_height) { glw.deleteTexture(this.runtime.layer_tex); this.runtime.layer_tex = glw.createEmptyTexture(this.runtime.draw_width, this.runtime.draw_height, this.runtime.linearSampling); } glw.setRenderingToTexture(this.runtime.layer_tex); if (this.transparent) glw.clear(0, 0, 0, 0); } if (!this.transparent) { if (this.runtime.enableFrontToBack) { glw.setEarlyZIndex(this.clear_earlyz_index); glw.setColorFillMode(this.background_color[0] / 255, this.background_color[1] / 255, this.background_color[2] / 255, 1); glw.fullscreenQuad(); glw.setTextureFillMode(); } else { glw.clear(this.background_color[0] / 255, this.background_color[1] / 255, this.background_color[2] / 255, 1); } } this.disableAngle = true; var px = this.canvasToLayer(0, 0, true, true); var py = this.canvasToLayer(0, 0, false, true); this.disableAngle = false; if (this.runtime.pixel_rounding) { px = Math.round(px); py = Math.round(py); } this.rotateViewport(px, py, null); var myscale = this.getScale(); glw.resetModelView(); glw.scale(myscale, myscale); glw.rotateZ(-this.getAngle()); glw.translate((this.viewLeft + this.viewRight) / -2, (this.viewTop + this.viewBottom) / -2); glw.updateModelView(); var instances_to_draw; if (this.useRenderCells) { this.cur_render_cells.left = this.render_grid.XToCell(this.viewLeft); this.cur_render_cells.top = this.render_grid.YToCell(this.viewTop); this.cur_render_cells.right = this.render_grid.XToCell(this.viewRight); this.cur_render_cells.bottom = this.render_grid.YToCell(this.viewBottom); if (this.render_list_stale || !this.cur_render_cells.equals(this.last_render_cells)) { free_arr(this.last_render_list); instances_to_draw = this.getRenderCellInstancesToDraw(); this.render_list_stale = false; this.last_render_cells.copy(this.cur_render_cells); } else instances_to_draw = this.last_render_list; } else instances_to_draw = this.instances; var i, len, inst, last_inst = null; for (i = 0, len = instances_to_draw.length; i < len; ++i) { inst = instances_to_draw[i]; if (inst === last_inst) continue; this.drawInstanceGL(instances_to_draw[i], glw); last_inst = inst; } if (this.useRenderCells) this.last_render_list = instances_to_draw; if (this.render_offscreen) { shaderindex = this.active_effect_types.length ? this.active_effect_types[0].shaderindex : 0; etindex = this.active_effect_types.length ? this.active_effect_types[0].index : 0; if (this.active_effect_types.length === 0 || (this.active_effect_types.length === 1 && !glw.programUsesCrossSampling(shaderindex) && this.opacity === 1)) { if (this.active_effect_types.length === 1) { glw.switchProgram(shaderindex); glw.setProgramParameters(this.layout.getRenderTarget(), // backTex 1.0 / this.runtime.draw_width, // pixelWidth 1.0 / this.runtime.draw_height, // pixelHeight 0.0, 0.0, // destStart 1.0, 1.0, // destEnd myscale, // layerScale this.getAngle(), this.viewLeft, this.viewTop, (this.viewLeft + this.viewRight) / 2, (this.viewTop + this.viewBottom) / 2, this.runtime.kahanTime.sum, this.effect_params[etindex]); // fx parameters if (glw.programIsAnimated(shaderindex)) this.runtime.redraw = true; } else glw.switchProgram(0); glw.setRenderingToTexture(this.layout.getRenderTarget()); glw.setOpacity(this.opacity); glw.setTexture(this.runtime.layer_tex); glw.setBlend(this.srcBlend, this.destBlend); glw.resetModelView(); glw.updateModelView(); var halfw = this.runtime.draw_width / 2; var halfh = this.runtime.draw_height / 2; glw.quad(-halfw, halfh, halfw, halfh, halfw, -halfh, -halfw, -halfh); glw.setTexture(null); } else { this.layout.renderEffectChain(glw, this, null, this.layout.getRenderTarget()); } } }; Layer.prototype.drawInstanceGL = function (inst, glw) { ; if (!inst.visible || inst.width === 0 || inst.height === 0) return; inst.update_bbox(); var bbox = inst.bbox; if (bbox.right < this.viewLeft || bbox.bottom < this.viewTop || bbox.left > this.viewRight || bbox.top > this.viewBottom) return; glw.setEarlyZIndex(inst.earlyz_index); if (inst.uses_shaders) { this.drawInstanceWithShadersGL(inst, glw); } else { glw.switchProgram(0); // un-set any previously set shader glw.setBlend(inst.srcBlend, inst.destBlend); inst.drawGL(glw); } }; Layer.prototype.drawInstanceGL_earlyZPass = function (inst, glw) { ; if (!inst.visible || inst.width === 0 || inst.height === 0) return; inst.update_bbox(); var bbox = inst.bbox; if (bbox.right < this.viewLeft || bbox.bottom < this.viewTop || bbox.left > this.viewRight || bbox.top > this.viewBottom) return; inst.earlyz_index = this.runtime.earlyz_index++; if (inst.blend_mode !== 0 || inst.opacity !== 1 || !inst.shaders_preserve_opaqueness || !inst.drawGL_earlyZPass) return; glw.setEarlyZIndex(inst.earlyz_index); inst.drawGL_earlyZPass(glw); }; Layer.prototype.drawInstanceWithShadersGL = function (inst, glw) { var shaderindex = inst.active_effect_types[0].shaderindex; var etindex = inst.active_effect_types[0].index; var myscale = this.getScale(); if (inst.active_effect_types.length === 1 && !glw.programUsesCrossSampling(shaderindex) && !glw.programExtendsBox(shaderindex) && ((!inst.angle && !inst.layer.getAngle()) || !glw.programUsesDest(shaderindex)) && inst.opacity === 1 && !inst.type.plugin.must_predraw) { glw.switchProgram(shaderindex); glw.setBlend(inst.srcBlend, inst.destBlend); if (glw.programIsAnimated(shaderindex)) this.runtime.redraw = true; var destStartX = 0, destStartY = 0, destEndX = 0, destEndY = 0; if (glw.programUsesDest(shaderindex)) { var bbox = inst.bbox; var screenleft = this.layerToCanvas(bbox.left, bbox.top, true, true); var screentop = this.layerToCanvas(bbox.left, bbox.top, false, true); var screenright = this.layerToCanvas(bbox.right, bbox.bottom, true, true); var screenbottom = this.layerToCanvas(bbox.right, bbox.bottom, false, true); destStartX = screenleft / windowWidth; destStartY = 1 - screentop / windowHeight; destEndX = screenright / windowWidth; destEndY = 1 - screenbottom / windowHeight; } var pixelWidth; var pixelHeight; if (inst.curFrame && inst.curFrame.texture_img) { var img = inst.curFrame.texture_img; pixelWidth = 1.0 / img.width; pixelHeight = 1.0 / img.height; } else { pixelWidth = 1.0 / inst.width; pixelHeight = 1.0 / inst.height; } glw.setProgramParameters(this.render_offscreen ? this.runtime.layer_tex : this.layout.getRenderTarget(), // backTex pixelWidth, pixelHeight, destStartX, destStartY, destEndX, destEndY, myscale, this.getAngle(), this.viewLeft, this.viewTop, (this.viewLeft + this.viewRight) / 2, (this.viewTop + this.viewBottom) / 2, this.runtime.kahanTime.sum, inst.effect_params[etindex]); inst.drawGL(glw); } else { this.layout.renderEffectChain(glw, this, inst, this.render_offscreen ? this.runtime.layer_tex : this.layout.getRenderTarget()); glw.resetModelView(); glw.scale(myscale, myscale); glw.rotateZ(-this.getAngle()); glw.translate((this.viewLeft + this.viewRight) / -2, (this.viewTop + this.viewBottom) / -2); glw.updateModelView(); } }; Layer.prototype.canvasToLayer = function (ptx, pty, getx, using_draw_area) { var multiplier = this.runtime.devicePixelRatio; if (this.runtime.isRetina) { ptx *= multiplier; pty *= multiplier; } var ox = this.runtime.parallax_x_origin; var oy = this.runtime.parallax_y_origin; var par_x = ((this.layout.scrollX - ox) * this.parallaxX) + ox; var par_y = ((this.layout.scrollY - oy) * this.parallaxY) + oy; var x = par_x; var y = par_y; var invScale = 1 / this.getScale(!using_draw_area); if (using_draw_area) { x -= (this.runtime.draw_width * invScale) / 2; y -= (this.runtime.draw_height * invScale) / 2; } else { x -= (this.runtime.width * invScale) / 2; y -= (this.runtime.height * invScale) / 2; } x += ptx * invScale; y += pty * invScale; var a = this.getAngle(); if (a !== 0) { x -= par_x; y -= par_y; var cosa = Math.cos(a); var sina = Math.sin(a); var x_temp = (x * cosa) - (y * sina); y = (y * cosa) + (x * sina); x = x_temp; x += par_x; y += par_y; } return getx ? x : y; }; Layer.prototype.layerToCanvas = function (ptx, pty, getx, using_draw_area) { var ox = this.runtime.parallax_x_origin; var oy = this.runtime.parallax_y_origin; var par_x = ((this.layout.scrollX - ox) * this.parallaxX) + ox; var par_y = ((this.layout.scrollY - oy) * this.parallaxY) + oy; var x = par_x; var y = par_y; var a = this.getAngle(); if (a !== 0) { ptx -= par_x; pty -= par_y; var cosa = Math.cos(-a); var sina = Math.sin(-a); var x_temp = (ptx * cosa) - (pty * sina); pty = (pty * cosa) + (ptx * sina); ptx = x_temp; ptx += par_x; pty += par_y; } var invScale = 1 / this.getScale(!using_draw_area); if (using_draw_area) { x -= (this.runtime.draw_width * invScale) / 2; y -= (this.runtime.draw_height * invScale) / 2; } else { x -= (this.runtime.width * invScale) / 2; y -= (this.runtime.height * invScale) / 2; } x = (ptx - x) / invScale; y = (pty - y) / invScale; var multiplier = this.runtime.devicePixelRatio; if (this.runtime.isRetina && !using_draw_area) { x /= multiplier; y /= multiplier; } return getx ? x : y; }; Layer.prototype.rotatePt = function (x_, y_, getx) { if (this.getAngle() === 0) return getx ? x_ : y_; var nx = this.layerToCanvas(x_, y_, true); var ny = this.layerToCanvas(x_, y_, false); this.disableAngle = true; var px = this.canvasToLayer(nx, ny, true); var py = this.canvasToLayer(nx, ny, true); this.disableAngle = false; return getx ? px : py; }; Layer.prototype.saveToJSON = function () { var i, len, et; var o = { "s": this.scale, "a": this.angle, "vl": this.viewLeft, "vt": this.viewTop, "vr": this.viewRight, "vb": this.viewBottom, "v": this.visible, "bc": this.background_color, "t": this.transparent, "px": this.parallaxX, "py": this.parallaxY, "o": this.opacity, "zr": this.zoomRate, "fx": [], "cg": this.created_globals, // added r197; list of global UIDs already created "instances": [] }; for (i = 0, len = this.effect_types.length; i < len; i++) { et = this.effect_types[i]; o["fx"].push({"name": et.name, "active": et.active, "params": this.effect_params[et.index] }); } return o; }; Layer.prototype.loadFromJSON = function (o) { var i, j, len, p, inst, fx; this.scale = o["s"]; this.angle = o["a"]; this.viewLeft = o["vl"]; this.viewTop = o["vt"]; this.viewRight = o["vr"]; this.viewBottom = o["vb"]; this.visible = o["v"]; this.background_color = o["bc"]; this.transparent = o["t"]; this.parallaxX = o["px"]; this.parallaxY = o["py"]; this.opacity = o["o"]; this.zoomRate = o["zr"]; this.created_globals = o["cg"] || []; // added r197 cr.shallowAssignArray(this.initial_instances, this.startup_initial_instances); var temp_set = new cr.ObjectSet(); for (i = 0, len = this.created_globals.length; i < len; ++i) temp_set.add(this.created_globals[i]); for (i = 0, j = 0, len = this.initial_instances.length; i < len; ++i) { if (!temp_set.contains(this.initial_instances[i][2])) // UID in element 2 { this.initial_instances[j] = this.initial_instances[i]; ++j; } } cr.truncateArray(this.initial_instances, j); var ofx = o["fx"]; for (i = 0, len = ofx.length; i < len; i++) { fx = this.getEffectByName(ofx[i]["name"]); if (!fx) continue; // must've gone missing fx.active = ofx[i]["active"]; this.effect_params[fx.index] = ofx[i]["params"]; } this.updateActiveEffects(); this.instances.sort(sort_by_zindex); this.zindices_stale = true; }; cr.layer = Layer; }()); ; (function() { var allUniqueSolModifiers = []; function testSolsMatch(arr1, arr2) { var i, len = arr1.length; switch (len) { case 0: return true; case 1: return arr1[0] === arr2[0]; case 2: return arr1[0] === arr2[0] && arr1[1] === arr2[1]; default: for (i = 0; i < len; i++) { if (arr1[i] !== arr2[i]) return false; } return true; } }; function solArraySorter(t1, t2) { return t1.index - t2.index; }; function findMatchingSolModifier(arr) { var i, len, u, temp, subarr; if (arr.length === 2) { if (arr[0].index > arr[1].index) { temp = arr[0]; arr[0] = arr[1]; arr[1] = temp; } } else if (arr.length > 2) arr.sort(solArraySorter); // so testSolsMatch compares in same order if (arr.length >= allUniqueSolModifiers.length) allUniqueSolModifiers.length = arr.length + 1; if (!allUniqueSolModifiers[arr.length]) allUniqueSolModifiers[arr.length] = []; subarr = allUniqueSolModifiers[arr.length]; for (i = 0, len = subarr.length; i < len; i++) { u = subarr[i]; if (testSolsMatch(arr, u)) return u; } subarr.push(arr); return arr; }; function EventSheet(runtime, m) { this.runtime = runtime; this.triggers = {}; this.fasttriggers = {}; this.hasRun = false; this.includes = new cr.ObjectSet(); // all event sheets included by this sheet, at first-level indirection only this.deep_includes = []; // all includes from this sheet recursively, in trigger order this.already_included_sheets = []; // used while building deep_includes this.name = m[0]; var em = m[1]; // events model this.events = []; // triggers won't make it to this array var i, len; for (i = 0, len = em.length; i < len; i++) this.init_event(em[i], null, this.events); }; EventSheet.prototype.toString = function () { return this.name; }; EventSheet.prototype.init_event = function (m, parent, nontriggers) { switch (m[0]) { case 0: // event block { var block = new cr.eventblock(this, parent, m); cr.seal(block); if (block.orblock) { nontriggers.push(block); var i, len; for (i = 0, len = block.conditions.length; i < len; i++) { if (block.conditions[i].trigger) this.init_trigger(block, i); } } else { if (block.is_trigger()) this.init_trigger(block, 0); else nontriggers.push(block); } break; } case 1: // variable { var v = new cr.eventvariable(this, parent, m); cr.seal(v); nontriggers.push(v); break; } case 2: // include { var inc = new cr.eventinclude(this, parent, m); cr.seal(inc); nontriggers.push(inc); break; } default: ; } }; EventSheet.prototype.postInit = function () { var i, len; for (i = 0, len = this.events.length; i < len; i++) { this.events[i].postInit(i < len - 1 && this.events[i + 1].is_else_block); } }; EventSheet.prototype.updateDeepIncludes = function () { cr.clearArray(this.deep_includes); cr.clearArray(this.already_included_sheets); this.addDeepIncludes(this); cr.clearArray(this.already_included_sheets); }; EventSheet.prototype.addDeepIncludes = function (root_sheet) { var i, len, inc, sheet; var deep_includes = root_sheet.deep_includes; var already_included_sheets = root_sheet.already_included_sheets; var arr = this.includes.valuesRef(); for (i = 0, len = arr.length; i < len; ++i) { inc = arr[i]; sheet = inc.include_sheet; if (!inc.isActive() || root_sheet === sheet || already_included_sheets.indexOf(sheet) > -1) continue; already_included_sheets.push(sheet); sheet.addDeepIncludes(root_sheet); deep_includes.push(sheet); } }; EventSheet.prototype.run = function (from_include) { if (!this.runtime.resuming_breakpoint) { this.hasRun = true; if (!from_include) this.runtime.isRunningEvents = true; } var i, len; for (i = 0, len = this.events.length; i < len; i++) { var ev = this.events[i]; ev.run(); this.runtime.clearSol(ev.solModifiers); if (this.runtime.hasPendingInstances) this.runtime.ClearDeathRow(); } if (!from_include) this.runtime.isRunningEvents = false; }; function isPerformanceSensitiveTrigger(method) { if (cr.plugins_.Sprite && method === cr.plugins_.Sprite.prototype.cnds.OnFrameChanged) { return true; } return false; }; EventSheet.prototype.init_trigger = function (trig, index) { if (!trig.orblock) this.runtime.triggers_to_postinit.push(trig); // needs to be postInit'd later var i, len; var cnd = trig.conditions[index]; var type_name; if (cnd.type) type_name = cnd.type.name; else type_name = "system"; var fasttrigger = cnd.fasttrigger; var triggers = (fasttrigger ? this.fasttriggers : this.triggers); if (!triggers[type_name]) triggers[type_name] = []; var obj_entry = triggers[type_name]; var method = cnd.func; if (fasttrigger) { if (!cnd.parameters.length) // no parameters return; var firstparam = cnd.parameters[0]; if (firstparam.type !== 1 || // not a string param firstparam.expression.type !== 2) // not a string literal node { return; } var fastevs; var firstvalue = firstparam.expression.value.toLowerCase(); var i, len; for (i = 0, len = obj_entry.length; i < len; i++) { if (obj_entry[i].method == method) { fastevs = obj_entry[i].evs; if (!fastevs[firstvalue]) fastevs[firstvalue] = [[trig, index]]; else fastevs[firstvalue].push([trig, index]); return; } } fastevs = {}; fastevs[firstvalue] = [[trig, index]]; obj_entry.push({ method: method, evs: fastevs }); } else { for (i = 0, len = obj_entry.length; i < len; i++) { if (obj_entry[i].method == method) { obj_entry[i].evs.push([trig, index]); return; } } if (isPerformanceSensitiveTrigger(method)) obj_entry.unshift({ method: method, evs: [[trig, index]]}); else obj_entry.push({ method: method, evs: [[trig, index]]}); } }; cr.eventsheet = EventSheet; function Selection(type) { this.type = type; this.instances = []; // subset of picked instances this.else_instances = []; // subset of unpicked instances this.select_all = true; }; Selection.prototype.hasObjects = function () { if (this.select_all) return this.type.instances.length; else return this.instances.length; }; Selection.prototype.getObjects = function () { if (this.select_all) return this.type.instances; else return this.instances; }; /* Selection.prototype.ensure_picked = function (inst, skip_siblings) { var i, len; var orblock = inst.runtime.getCurrentEventStack().current_event.orblock; if (this.select_all) { this.select_all = false; if (orblock) { cr.shallowAssignArray(this.else_instances, inst.type.instances); cr.arrayFindRemove(this.else_instances, inst); } this.instances.length = 1; this.instances[0] = inst; } else { if (orblock) { i = this.else_instances.indexOf(inst); if (i !== -1) { this.instances.push(this.else_instances[i]); this.else_instances.splice(i, 1); } } else { if (this.instances.indexOf(inst) === -1) this.instances.push(inst); } } if (!skip_siblings) { } }; */ Selection.prototype.pick_one = function (inst) { if (!inst) return; if (inst.runtime.getCurrentEventStack().current_event.orblock) { if (this.select_all) { cr.clearArray(this.instances); cr.shallowAssignArray(this.else_instances, inst.type.instances); this.select_all = false; } var i = this.else_instances.indexOf(inst); if (i !== -1) { this.instances.push(this.else_instances[i]); this.else_instances.splice(i, 1); } } else { this.select_all = false; cr.clearArray(this.instances); this.instances[0] = inst; } }; cr.selection = Selection; function EventBlock(sheet, parent, m) { this.sheet = sheet; this.parent = parent; this.runtime = sheet.runtime; this.solModifiers = []; this.solModifiersIncludingParents = []; this.solWriterAfterCnds = false; // block does not change SOL after running its conditions this.group = false; // is group of events this.initially_activated = false; // if a group, is active on startup this.toplevelevent = false; // is an event block parented only by a top-level group this.toplevelgroup = false; // is parented only by other groups or is top-level (i.e. not in a subevent) this.has_else_block = false; // is followed by else ; this.conditions = []; this.actions = []; this.subevents = []; this.group_name = ""; this.group = false; this.initially_activated = false; this.group_active = false; this.contained_includes = null; if (m[1]) { this.group_name = m[1][1].toLowerCase(); this.group = true; this.initially_activated = !!m[1][0]; this.contained_includes = []; this.group_active = this.initially_activated; this.runtime.allGroups.push(this); this.runtime.groups_by_name[this.group_name] = this; } this.orblock = m[2]; this.sid = m[4]; if (!this.group) this.runtime.blocksBySid[this.sid.toString()] = this; var i, len; var cm = m[5]; for (i = 0, len = cm.length; i < len; i++) { var cnd = new cr.condition(this, cm[i]); cnd.index = i; cr.seal(cnd); this.conditions.push(cnd); /* if (cnd.is_logical()) this.is_logical = true; if (cnd.type && !cnd.type.plugin.singleglobal && this.cndReferences.indexOf(cnd.type) === -1) this.cndReferences.push(cnd.type); */ this.addSolModifier(cnd.type); } var am = m[6]; for (i = 0, len = am.length; i < len; i++) { var act = new cr.action(this, am[i]); act.index = i; cr.seal(act); this.actions.push(act); } if (m.length === 8) { var em = m[7]; for (i = 0, len = em.length; i < len; i++) this.sheet.init_event(em[i], this, this.subevents); } this.is_else_block = false; if (this.conditions.length) { this.is_else_block = (this.conditions[0].type == null && this.conditions[0].func == cr.system_object.prototype.cnds.Else); } }; window["_c2hh_"] = "3142B1E7D97A3706CD2EEDCB7012C2BFDE9A7D25"; EventBlock.prototype.postInit = function (hasElse/*, prevBlock_*/) { var i, len; var p = this.parent; if (this.group) { this.toplevelgroup = true; while (p) { if (!p.group) { this.toplevelgroup = false; break; } p = p.parent; } } this.toplevelevent = !this.is_trigger() && (!this.parent || (this.parent.group && this.parent.toplevelgroup)); this.has_else_block = !!hasElse; this.solModifiersIncludingParents = this.solModifiers.slice(0); p = this.parent; while (p) { for (i = 0, len = p.solModifiers.length; i < len; i++) this.addParentSolModifier(p.solModifiers[i]); p = p.parent; } this.solModifiers = findMatchingSolModifier(this.solModifiers); this.solModifiersIncludingParents = findMatchingSolModifier(this.solModifiersIncludingParents); var i, len/*, s*/; for (i = 0, len = this.conditions.length; i < len; i++) this.conditions[i].postInit(); for (i = 0, len = this.actions.length; i < len; i++) this.actions[i].postInit(); for (i = 0, len = this.subevents.length; i < len; i++) { this.subevents[i].postInit(i < len - 1 && this.subevents[i + 1].is_else_block); } /* if (this.is_else_block && this.prev_block) { for (i = 0, len = this.prev_block.solModifiers.length; i < len; i++) { s = this.prev_block.solModifiers[i]; if (this.solModifiers.indexOf(s) === -1) this.solModifiers.push(s); } } */ }; EventBlock.prototype.setGroupActive = function (a) { if (this.group_active === !!a) return; // same state this.group_active = !!a; var i, len; for (i = 0, len = this.contained_includes.length; i < len; ++i) { this.contained_includes[i].updateActive(); } if (len > 0 && this.runtime.running_layout.event_sheet) this.runtime.running_layout.event_sheet.updateDeepIncludes(); }; function addSolModifierToList(type, arr) { var i, len, t; if (!type) return; if (arr.indexOf(type) === -1) arr.push(type); if (type.is_contained) { for (i = 0, len = type.container.length; i < len; i++) { t = type.container[i]; if (type === t) continue; // already handled if (arr.indexOf(t) === -1) arr.push(t); } } }; EventBlock.prototype.addSolModifier = function (type) { addSolModifierToList(type, this.solModifiers); }; EventBlock.prototype.addParentSolModifier = function (type) { addSolModifierToList(type, this.solModifiersIncludingParents); }; EventBlock.prototype.setSolWriterAfterCnds = function () { this.solWriterAfterCnds = true; if (this.parent) this.parent.setSolWriterAfterCnds(); }; EventBlock.prototype.is_trigger = function () { if (!this.conditions.length) // no conditions return false; else return this.conditions[0].trigger; }; EventBlock.prototype.run = function () { var i, len, c, any_true = false, cnd_result; var runtime = this.runtime; var evinfo = this.runtime.getCurrentEventStack(); evinfo.current_event = this; var conditions = this.conditions; if (!this.is_else_block) evinfo.else_branch_ran = false; if (this.orblock) { if (conditions.length === 0) any_true = true; // be sure to run if empty block evinfo.cndindex = 0 for (len = conditions.length; evinfo.cndindex < len; evinfo.cndindex++) { c = conditions[evinfo.cndindex]; if (c.trigger) // skip triggers when running OR block continue; cnd_result = c.run(); if (cnd_result) // make sure all conditions run and run if any were true any_true = true; } evinfo.last_event_true = any_true; if (any_true) this.run_actions_and_subevents(); } else { evinfo.cndindex = 0 for (len = conditions.length; evinfo.cndindex < len; evinfo.cndindex++) { cnd_result = conditions[evinfo.cndindex].run(); if (!cnd_result) // condition failed { evinfo.last_event_true = false; if (this.toplevelevent && runtime.hasPendingInstances) runtime.ClearDeathRow(); return; // bail out now } } evinfo.last_event_true = true; this.run_actions_and_subevents(); } this.end_run(evinfo); }; EventBlock.prototype.end_run = function (evinfo) { if (evinfo.last_event_true && this.has_else_block) evinfo.else_branch_ran = true; if (this.toplevelevent && this.runtime.hasPendingInstances) this.runtime.ClearDeathRow(); }; EventBlock.prototype.run_orblocktrigger = function (index) { var evinfo = this.runtime.getCurrentEventStack(); evinfo.current_event = this; if (this.conditions[index].run()) { this.run_actions_and_subevents(); this.runtime.getCurrentEventStack().last_event_true = true; } }; EventBlock.prototype.run_actions_and_subevents = function () { var evinfo = this.runtime.getCurrentEventStack(); var len; for (evinfo.actindex = 0, len = this.actions.length; evinfo.actindex < len; evinfo.actindex++) { if (this.actions[evinfo.actindex].run()) return; } this.run_subevents(); }; EventBlock.prototype.resume_actions_and_subevents = function () { var evinfo = this.runtime.getCurrentEventStack(); var len; for (len = this.actions.length; evinfo.actindex < len; evinfo.actindex++) { if (this.actions[evinfo.actindex].run()) return; } this.run_subevents(); }; EventBlock.prototype.run_subevents = function () { if (!this.subevents.length) return; var i, len, subev, pushpop/*, skipped_pop = false, pop_modifiers = null*/; var last = this.subevents.length - 1; this.runtime.pushEventStack(this); if (this.solWriterAfterCnds) { for (i = 0, len = this.subevents.length; i < len; i++) { subev = this.subevents[i]; pushpop = (!this.toplevelgroup || (!this.group && i < last)); if (pushpop) this.runtime.pushCopySol(subev.solModifiers); subev.run(); if (pushpop) this.runtime.popSol(subev.solModifiers); else this.runtime.clearSol(subev.solModifiers); } } else { for (i = 0, len = this.subevents.length; i < len; i++) { this.subevents[i].run(); } } this.runtime.popEventStack(); }; EventBlock.prototype.run_pretrigger = function () { var evinfo = this.runtime.getCurrentEventStack(); evinfo.current_event = this; var any_true = false; var i, len; for (evinfo.cndindex = 0, len = this.conditions.length; evinfo.cndindex < len; evinfo.cndindex++) { ; if (this.conditions[evinfo.cndindex].run()) any_true = true; else if (!this.orblock) // condition failed (let OR blocks run all conditions anyway) return false; // bail out } return this.orblock ? any_true : true; }; EventBlock.prototype.retrigger = function () { this.runtime.execcount++; var prevcndindex = this.runtime.getCurrentEventStack().cndindex; var len; var evinfo = this.runtime.pushEventStack(this); if (!this.orblock) { for (evinfo.cndindex = prevcndindex + 1, len = this.conditions.length; evinfo.cndindex < len; evinfo.cndindex++) { if (!this.conditions[evinfo.cndindex].run()) // condition failed { this.runtime.popEventStack(); // moving up level of recursion return false; // bail out } } } this.run_actions_and_subevents(); this.runtime.popEventStack(); return true; // ran an iteration }; EventBlock.prototype.isFirstConditionOfType = function (cnd) { var cndindex = cnd.index; if (cndindex === 0) return true; --cndindex; for ( ; cndindex >= 0; --cndindex) { if (this.conditions[cndindex].type === cnd.type) return false; } return true; }; cr.eventblock = EventBlock; function Condition(block, m) { this.block = block; this.sheet = block.sheet; this.runtime = block.runtime; this.parameters = []; this.results = []; this.extra = {}; // for plugins to stow away some custom info this.index = -1; this.anyParamVariesPerInstance = false; this.func = this.runtime.GetObjectReference(m[1]); ; this.trigger = (m[3] > 0); this.fasttrigger = (m[3] === 2); this.looping = m[4]; this.inverted = m[5]; this.isstatic = m[6]; this.sid = m[7]; this.runtime.cndsBySid[this.sid.toString()] = this; if (m[0] === -1) // system object { this.type = null; this.run = this.run_system; this.behaviortype = null; this.beh_index = -1; } else { this.type = this.runtime.types_by_index[m[0]]; ; if (this.isstatic) this.run = this.run_static; else this.run = this.run_object; if (m[2]) { this.behaviortype = this.type.getBehaviorByName(m[2]); ; this.beh_index = this.type.getBehaviorIndexByName(m[2]); ; } else { this.behaviortype = null; this.beh_index = -1; } if (this.block.parent) this.block.parent.setSolWriterAfterCnds(); } if (this.fasttrigger) this.run = this.run_true; if (m.length === 10) { var i, len; var em = m[9]; for (i = 0, len = em.length; i < len; i++) { var param = new cr.parameter(this, em[i]); cr.seal(param); this.parameters.push(param); } this.results.length = em.length; } }; Condition.prototype.postInit = function () { var i, len, p; for (i = 0, len = this.parameters.length; i < len; i++) { p = this.parameters[i]; p.postInit(); if (p.variesPerInstance) this.anyParamVariesPerInstance = true; } }; /* Condition.prototype.is_logical = function () { return !this.type || this.type.plugin.singleglobal; }; */ Condition.prototype.run_true = function () { return true; }; Condition.prototype.run_system = function () { var i, len; for (i = 0, len = this.parameters.length; i < len; i++) this.results[i] = this.parameters[i].get(); return cr.xor(this.func.apply(this.runtime.system, this.results), this.inverted); }; Condition.prototype.run_static = function () { var i, len; for (i = 0, len = this.parameters.length; i < len; i++) this.results[i] = this.parameters[i].get(); var ret = this.func.apply(this.behaviortype ? this.behaviortype : this.type, this.results); this.type.applySolToContainer(); return ret; }; Condition.prototype.run_object = function () { var i, j, k, leni, lenj, p, ret, met, inst, s, sol2; var type = this.type; var sol = type.getCurrentSol(); var is_orblock = this.block.orblock && !this.trigger; // triggers in OR blocks need to work normally var offset = 0; var is_contained = type.is_contained; var is_family = type.is_family; var family_index = type.family_index; var beh_index = this.beh_index; var is_beh = (beh_index > -1); var params_vary = this.anyParamVariesPerInstance; var parameters = this.parameters; var results = this.results; var inverted = this.inverted; var func = this.func; var arr, container; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; ++j) { p = parameters[j]; if (!p.variesPerInstance) results[j] = p.get(0); } } else { for (j = 0, lenj = parameters.length; j < lenj; ++j) results[j] = parameters[j].get(0); } if (sol.select_all) { cr.clearArray(sol.instances); // clear contents cr.clearArray(sol.else_instances); arr = type.instances; for (i = 0, leni = arr.length; i < leni; ++i) { inst = arr[i]; ; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; ++j) { p = parameters[j]; if (p.variesPerInstance) results[j] = p.get(i); // default SOL index is current object } } if (is_beh) { offset = 0; if (is_family) { offset = inst.type.family_beh_map[family_index]; } ret = func.apply(inst.behavior_insts[beh_index + offset], results); } else ret = func.apply(inst, results); met = cr.xor(ret, inverted); if (met) sol.instances.push(inst); else if (is_orblock) // in OR blocks, keep the instances not meeting the condition for subsequent testing sol.else_instances.push(inst); } if (type.finish) type.finish(true); sol.select_all = false; type.applySolToContainer(); return sol.hasObjects(); } else { k = 0; var using_else_instances = (is_orblock && !this.block.isFirstConditionOfType(this)); arr = (using_else_instances ? sol.else_instances : sol.instances); var any_true = false; for (i = 0, leni = arr.length; i < leni; ++i) { inst = arr[i]; ; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; ++j) { p = parameters[j]; if (p.variesPerInstance) results[j] = p.get(i); // default SOL index is current object } } if (is_beh) { offset = 0; if (is_family) { offset = inst.type.family_beh_map[family_index]; } ret = func.apply(inst.behavior_insts[beh_index + offset], results); } else ret = func.apply(inst, results); if (cr.xor(ret, inverted)) { any_true = true; if (using_else_instances) { sol.instances.push(inst); if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; s.type.getCurrentSol().instances.push(s); } } } else { arr[k] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; s.type.getCurrentSol().instances[k] = s; } } k++; } } else { if (using_else_instances) { arr[k] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; s.type.getCurrentSol().else_instances[k] = s; } } k++; } else if (is_orblock) { sol.else_instances.push(inst); if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; s.type.getCurrentSol().else_instances.push(s); } } } } } cr.truncateArray(arr, k); if (is_contained) { container = type.container; for (i = 0, leni = container.length; i < leni; i++) { sol2 = container[i].getCurrentSol(); if (using_else_instances) cr.truncateArray(sol2.else_instances, k); else cr.truncateArray(sol2.instances, k); } } var pick_in_finish = any_true; // don't pick in finish() if we're only doing the logic test below if (using_else_instances && !any_true) { for (i = 0, leni = sol.instances.length; i < leni; i++) { inst = sol.instances[i]; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; j++) { p = parameters[j]; if (p.variesPerInstance) results[j] = p.get(i); } } if (is_beh) ret = func.apply(inst.behavior_insts[beh_index], results); else ret = func.apply(inst, results); if (cr.xor(ret, inverted)) { any_true = true; break; // got our flag, don't need to test any more } } } if (type.finish) type.finish(pick_in_finish || is_orblock); return is_orblock ? any_true : sol.hasObjects(); } }; cr.condition = Condition; function Action(block, m) { this.block = block; this.sheet = block.sheet; this.runtime = block.runtime; this.parameters = []; this.results = []; this.extra = {}; // for plugins to stow away some custom info this.index = -1; this.anyParamVariesPerInstance = false; this.func = this.runtime.GetObjectReference(m[1]); ; if (m[0] === -1) // system { this.type = null; this.run = this.run_system; this.behaviortype = null; this.beh_index = -1; } else { this.type = this.runtime.types_by_index[m[0]]; ; this.run = this.run_object; if (m[2]) { this.behaviortype = this.type.getBehaviorByName(m[2]); ; this.beh_index = this.type.getBehaviorIndexByName(m[2]); ; } else { this.behaviortype = null; this.beh_index = -1; } } this.sid = m[3]; this.runtime.actsBySid[this.sid.toString()] = this; if (m.length === 6) { var i, len; var em = m[5]; for (i = 0, len = em.length; i < len; i++) { var param = new cr.parameter(this, em[i]); cr.seal(param); this.parameters.push(param); } this.results.length = em.length; } }; Action.prototype.postInit = function () { var i, len, p; for (i = 0, len = this.parameters.length; i < len; i++) { p = this.parameters[i]; p.postInit(); if (p.variesPerInstance) this.anyParamVariesPerInstance = true; } }; Action.prototype.run_system = function () { var runtime = this.runtime; var i, len; var parameters = this.parameters; var results = this.results; for (i = 0, len = parameters.length; i < len; ++i) results[i] = parameters[i].get(); return this.func.apply(runtime.system, results); }; Action.prototype.run_object = function () { var type = this.type; var beh_index = this.beh_index; var family_index = type.family_index; var params_vary = this.anyParamVariesPerInstance; var parameters = this.parameters; var results = this.results; var func = this.func; var instances = type.getCurrentSol().getObjects(); var is_family = type.is_family; var is_beh = (beh_index > -1); var i, j, leni, lenj, p, inst, offset; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; ++j) { p = parameters[j]; if (!p.variesPerInstance) results[j] = p.get(0); } } else { for (j = 0, lenj = parameters.length; j < lenj; ++j) results[j] = parameters[j].get(0); } for (i = 0, leni = instances.length; i < leni; ++i) { inst = instances[i]; if (params_vary) { for (j = 0, lenj = parameters.length; j < lenj; ++j) { p = parameters[j]; if (p.variesPerInstance) results[j] = p.get(i); // pass i to use as default SOL index } } if (is_beh) { offset = 0; if (is_family) { offset = inst.type.family_beh_map[family_index]; } func.apply(inst.behavior_insts[beh_index + offset], results); } else func.apply(inst, results); } return false; }; cr.action = Action; var tempValues = []; var tempValuesPtr = -1; function pushTempValue() { tempValuesPtr++; if (tempValues.length === tempValuesPtr) tempValues.push(new cr.expvalue()); return tempValues[tempValuesPtr]; }; function popTempValue() { tempValuesPtr--; }; function Parameter(owner, m) { this.owner = owner; this.block = owner.block; this.sheet = owner.sheet; this.runtime = owner.runtime; this.type = m[0]; this.expression = null; this.solindex = 0; this.get = null; this.combosel = 0; this.layout = null; this.key = 0; this.object = null; this.index = 0; this.varname = null; this.eventvar = null; this.fileinfo = null; this.subparams = null; this.variadicret = null; this.subparams = null; this.variadicret = null; this.variesPerInstance = false; var i, len, param; switch (m[0]) { case 0: // number case 7: // any this.expression = new cr.expNode(this, m[1]); this.solindex = 0; this.get = this.get_exp; break; case 1: // string this.expression = new cr.expNode(this, m[1]); this.solindex = 0; this.get = this.get_exp_str; break; case 5: // layer this.expression = new cr.expNode(this, m[1]); this.solindex = 0; this.get = this.get_layer; break; case 3: // combo case 8: // cmp this.combosel = m[1]; this.get = this.get_combosel; break; case 6: // layout this.layout = this.runtime.layouts[m[1]]; ; this.get = this.get_layout; break; case 9: // keyb this.key = m[1]; this.get = this.get_key; break; case 4: // object this.object = this.runtime.types_by_index[m[1]]; ; this.get = this.get_object; this.block.addSolModifier(this.object); if (this.owner instanceof cr.action) this.block.setSolWriterAfterCnds(); else if (this.block.parent) this.block.parent.setSolWriterAfterCnds(); break; case 10: // instvar this.index = m[1]; if (owner.type && owner.type.is_family) { this.get = this.get_familyvar; this.variesPerInstance = true; } else this.get = this.get_instvar; break; case 11: // eventvar this.varname = m[1]; this.eventvar = null; this.get = this.get_eventvar; break; case 2: // audiofile ["name", ismusic] case 12: // fileinfo "name" this.fileinfo = m[1]; this.get = this.get_audiofile; break; case 13: // variadic this.get = this.get_variadic; this.subparams = []; this.variadicret = []; for (i = 1, len = m.length; i < len; i++) { param = new cr.parameter(this.owner, m[i]); cr.seal(param); this.subparams.push(param); this.variadicret.push(0); } break; default: ; } }; Parameter.prototype.postInit = function () { var i, len; if (this.type === 11) // eventvar { this.eventvar = this.runtime.getEventVariableByName(this.varname, this.block.parent); ; } else if (this.type === 13) // variadic, postInit all sub-params { for (i = 0, len = this.subparams.length; i < len; i++) this.subparams[i].postInit(); } if (this.expression) this.expression.postInit(); }; Parameter.prototype.maybeVaryForType = function (t) { if (this.variesPerInstance) return; // already varies per instance, no need to check again if (!t) return; // never vary for system type if (!t.plugin.singleglobal) { this.variesPerInstance = true; return; } }; Parameter.prototype.setVaries = function () { this.variesPerInstance = true; }; Parameter.prototype.get_exp = function (solindex) { this.solindex = solindex || 0; // default SOL index to use var temp = pushTempValue(); this.expression.get(temp); popTempValue(); return temp.data; // return actual JS value, not expvalue }; Parameter.prototype.get_exp_str = function (solindex) { this.solindex = solindex || 0; // default SOL index to use var temp = pushTempValue(); this.expression.get(temp); popTempValue(); if (cr.is_string(temp.data)) return temp.data; else return ""; }; Parameter.prototype.get_object = function () { return this.object; }; Parameter.prototype.get_combosel = function () { return this.combosel; }; Parameter.prototype.get_layer = function (solindex) { this.solindex = solindex || 0; // default SOL index to use var temp = pushTempValue(); this.expression.get(temp); popTempValue(); if (temp.is_number()) return this.runtime.getLayerByNumber(temp.data); else return this.runtime.getLayerByName(temp.data); } Parameter.prototype.get_layout = function () { return this.layout; }; Parameter.prototype.get_key = function () { return this.key; }; Parameter.prototype.get_instvar = function () { return this.index; }; Parameter.prototype.get_familyvar = function (solindex_) { var solindex = solindex_ || 0; var familytype = this.owner.type; var realtype = null; var sol = familytype.getCurrentSol(); var objs = sol.getObjects(); if (objs.length) realtype = objs[solindex % objs.length].type; else if (sol.else_instances.length) realtype = sol.else_instances[solindex % sol.else_instances.length].type; else if (familytype.instances.length) realtype = familytype.instances[solindex % familytype.instances.length].type; else return 0; return this.index + realtype.family_var_map[familytype.family_index]; }; Parameter.prototype.get_eventvar = function () { return this.eventvar; }; Parameter.prototype.get_audiofile = function () { return this.fileinfo; }; Parameter.prototype.get_variadic = function () { var i, len; for (i = 0, len = this.subparams.length; i < len; i++) { this.variadicret[i] = this.subparams[i].get(); } return this.variadicret; }; cr.parameter = Parameter; function EventVariable(sheet, parent, m) { this.sheet = sheet; this.parent = parent; this.runtime = sheet.runtime; this.solModifiers = []; this.name = m[1]; this.vartype = m[2]; this.initial = m[3]; this.is_static = !!m[4]; this.is_constant = !!m[5]; this.sid = m[6]; this.runtime.varsBySid[this.sid.toString()] = this; this.data = this.initial; // note: also stored in event stack frame for local nonstatic nonconst vars if (this.parent) // local var { if (this.is_static || this.is_constant) this.localIndex = -1; else this.localIndex = this.runtime.stackLocalCount++; this.runtime.all_local_vars.push(this); } else // global var { this.localIndex = -1; this.runtime.all_global_vars.push(this); } }; EventVariable.prototype.postInit = function () { this.solModifiers = findMatchingSolModifier(this.solModifiers); }; EventVariable.prototype.setValue = function (x) { ; var lvs = this.runtime.getCurrentLocalVarStack(); if (!this.parent || this.is_static || !lvs) this.data = x; else // local nonstatic variable: use event stack to keep value at this level of recursion { if (this.localIndex >= lvs.length) lvs.length = this.localIndex + 1; lvs[this.localIndex] = x; } }; EventVariable.prototype.getValue = function () { var lvs = this.runtime.getCurrentLocalVarStack(); if (!this.parent || this.is_static || !lvs || this.is_constant) return this.data; else // local nonstatic variable { if (this.localIndex >= lvs.length) { return this.initial; } if (typeof lvs[this.localIndex] === "undefined") { return this.initial; } return lvs[this.localIndex]; } }; EventVariable.prototype.run = function () { if (this.parent && !this.is_static && !this.is_constant) this.setValue(this.initial); }; cr.eventvariable = EventVariable; function EventInclude(sheet, parent, m) { this.sheet = sheet; this.parent = parent; this.runtime = sheet.runtime; this.solModifiers = []; this.include_sheet = null; // determined in postInit this.include_sheet_name = m[1]; this.active = true; }; EventInclude.prototype.toString = function () { return "include:" + this.include_sheet.toString(); }; EventInclude.prototype.postInit = function () { this.include_sheet = this.runtime.eventsheets[this.include_sheet_name]; ; ; this.sheet.includes.add(this); this.solModifiers = findMatchingSolModifier(this.solModifiers); var p = this.parent; while (p) { if (p.group) p.contained_includes.push(this); p = p.parent; } this.updateActive(); }; EventInclude.prototype.run = function () { if (this.parent) this.runtime.pushCleanSol(this.runtime.types_by_index); if (!this.include_sheet.hasRun) this.include_sheet.run(true); // from include if (this.parent) this.runtime.popSol(this.runtime.types_by_index); }; EventInclude.prototype.updateActive = function () { var p = this.parent; while (p) { if (p.group && !p.group_active) { this.active = false; return; } p = p.parent; } this.active = true; }; EventInclude.prototype.isActive = function () { return this.active; }; cr.eventinclude = EventInclude; function EventStackFrame() { this.temp_parents_arr = []; this.reset(null); cr.seal(this); }; EventStackFrame.prototype.reset = function (cur_event) { this.current_event = cur_event; this.cndindex = 0; this.actindex = 0; cr.clearArray(this.temp_parents_arr); this.last_event_true = false; this.else_branch_ran = false; this.any_true_state = false; }; EventStackFrame.prototype.isModifierAfterCnds = function () { if (this.current_event.solWriterAfterCnds) return true; if (this.cndindex < this.current_event.conditions.length - 1) return !!this.current_event.solModifiers.length; return false; }; cr.eventStackFrame = EventStackFrame; }()); (function() { function ExpNode(owner_, m) { this.owner = owner_; this.runtime = owner_.runtime; this.type = m[0]; ; this.get = [this.eval_int, this.eval_float, this.eval_string, this.eval_unaryminus, this.eval_add, this.eval_subtract, this.eval_multiply, this.eval_divide, this.eval_mod, this.eval_power, this.eval_and, this.eval_or, this.eval_equal, this.eval_notequal, this.eval_less, this.eval_lessequal, this.eval_greater, this.eval_greaterequal, this.eval_conditional, this.eval_system_exp, this.eval_object_exp, this.eval_instvar_exp, this.eval_behavior_exp, this.eval_eventvar_exp][this.type]; var paramsModel = null; this.value = null; this.first = null; this.second = null; this.third = null; this.func = null; this.results = null; this.parameters = null; this.object_type = null; this.beh_index = -1; this.instance_expr = null; this.varindex = -1; this.behavior_type = null; this.varname = null; this.eventvar = null; this.return_string = false; switch (this.type) { case 0: // int case 1: // float case 2: // string this.value = m[1]; break; case 3: // unaryminus this.first = new cr.expNode(owner_, m[1]); break; case 18: // conditional this.first = new cr.expNode(owner_, m[1]); this.second = new cr.expNode(owner_, m[2]); this.third = new cr.expNode(owner_, m[3]); break; case 19: // system_exp this.func = this.runtime.GetObjectReference(m[1]); ; if (this.func === cr.system_object.prototype.exps.random || this.func === cr.system_object.prototype.exps.choose) { this.owner.setVaries(); } this.results = []; this.parameters = []; if (m.length === 3) { paramsModel = m[2]; this.results.length = paramsModel.length + 1; // must also fit 'ret' } else this.results.length = 1; // to fit 'ret' break; case 20: // object_exp this.object_type = this.runtime.types_by_index[m[1]]; ; this.beh_index = -1; this.func = this.runtime.GetObjectReference(m[2]); this.return_string = m[3]; if (cr.plugins_.Function && this.func === cr.plugins_.Function.prototype.exps.Call) { this.owner.setVaries(); } if (m[4]) this.instance_expr = new cr.expNode(owner_, m[4]); else this.instance_expr = null; this.results = []; this.parameters = []; if (m.length === 6) { paramsModel = m[5]; this.results.length = paramsModel.length + 1; } else this.results.length = 1; // to fit 'ret' break; case 21: // instvar_exp this.object_type = this.runtime.types_by_index[m[1]]; ; this.return_string = m[2]; if (m[3]) this.instance_expr = new cr.expNode(owner_, m[3]); else this.instance_expr = null; this.varindex = m[4]; break; case 22: // behavior_exp this.object_type = this.runtime.types_by_index[m[1]]; ; this.behavior_type = this.object_type.getBehaviorByName(m[2]); ; this.beh_index = this.object_type.getBehaviorIndexByName(m[2]); this.func = this.runtime.GetObjectReference(m[3]); this.return_string = m[4]; if (m[5]) this.instance_expr = new cr.expNode(owner_, m[5]); else this.instance_expr = null; this.results = []; this.parameters = []; if (m.length === 7) { paramsModel = m[6]; this.results.length = paramsModel.length + 1; } else this.results.length = 1; // to fit 'ret' break; case 23: // eventvar_exp this.varname = m[1]; this.eventvar = null; // assigned in postInit break; } this.owner.maybeVaryForType(this.object_type); if (this.type >= 4 && this.type <= 17) { this.first = new cr.expNode(owner_, m[1]); this.second = new cr.expNode(owner_, m[2]); } if (paramsModel) { var i, len; for (i = 0, len = paramsModel.length; i < len; i++) this.parameters.push(new cr.expNode(owner_, paramsModel[i])); } cr.seal(this); }; ExpNode.prototype.postInit = function () { if (this.type === 23) // eventvar_exp { this.eventvar = this.owner.runtime.getEventVariableByName(this.varname, this.owner.block.parent); ; } if (this.first) this.first.postInit(); if (this.second) this.second.postInit(); if (this.third) this.third.postInit(); if (this.instance_expr) this.instance_expr.postInit(); if (this.parameters) { var i, len; for (i = 0, len = this.parameters.length; i < len; i++) this.parameters[i].postInit(); } }; var tempValues = []; var tempValuesPtr = -1; function pushTempValue() { ++tempValuesPtr; if (tempValues.length === tempValuesPtr) tempValues.push(new cr.expvalue()); return tempValues[tempValuesPtr]; }; function popTempValue() { --tempValuesPtr; }; function eval_params(parameters, results, temp) { var i, len; for (i = 0, len = parameters.length; i < len; ++i) { parameters[i].get(temp); results[i + 1] = temp.data; // passing actual javascript value as argument instead of expvalue } } ExpNode.prototype.eval_system_exp = function (ret) { var parameters = this.parameters; var results = this.results; results[0] = ret; var temp = pushTempValue(); eval_params(parameters, results, temp); popTempValue(); this.func.apply(this.runtime.system, results); }; ExpNode.prototype.eval_object_exp = function (ret) { var object_type = this.object_type; var results = this.results; var parameters = this.parameters; var instance_expr = this.instance_expr; var func = this.func; var index = this.owner.solindex; // default to parameter's intended SOL index var sol = object_type.getCurrentSol(); var instances = sol.getObjects(); if (!instances.length) { if (sol.else_instances.length) instances = sol.else_instances; else { if (this.return_string) ret.set_string(""); else ret.set_int(0); return; } } results[0] = ret; ret.object_class = object_type; // so expression can access family type if need be var temp = pushTempValue(); eval_params(parameters, results, temp); if (instance_expr) { instance_expr.get(temp); if (temp.is_number()) { index = temp.data; instances = object_type.instances; // pick from all instances, not SOL } } popTempValue(); var len = instances.length; if (index >= len || index <= -len) index %= len; // wraparound if (index < 0) index += len; var returned_val = func.apply(instances[index], results); ; }; ExpNode.prototype.eval_behavior_exp = function (ret) { var object_type = this.object_type; var results = this.results; var parameters = this.parameters; var instance_expr = this.instance_expr; var beh_index = this.beh_index; var func = this.func; var index = this.owner.solindex; // default to parameter's intended SOL index var sol = object_type.getCurrentSol(); var instances = sol.getObjects(); if (!instances.length) { if (sol.else_instances.length) instances = sol.else_instances; else { if (this.return_string) ret.set_string(""); else ret.set_int(0); return; } } results[0] = ret; ret.object_class = object_type; // so expression can access family type if need be var temp = pushTempValue(); eval_params(parameters, results, temp); if (instance_expr) { instance_expr.get(temp); if (temp.is_number()) { index = temp.data; instances = object_type.instances; // pick from all instances, not SOL } } popTempValue(); var len = instances.length; if (index >= len || index <= -len) index %= len; // wraparound if (index < 0) index += len; var inst = instances[index]; var offset = 0; if (object_type.is_family) { offset = inst.type.family_beh_map[object_type.family_index]; } var returned_val = func.apply(inst.behavior_insts[beh_index + offset], results); ; }; ExpNode.prototype.eval_instvar_exp = function (ret) { var instance_expr = this.instance_expr; var object_type = this.object_type; var varindex = this.varindex; var index = this.owner.solindex; // default to parameter's intended SOL index var sol = object_type.getCurrentSol(); var instances = sol.getObjects(); var inst; if (!instances.length) { if (sol.else_instances.length) instances = sol.else_instances; else { if (this.return_string) ret.set_string(""); else ret.set_int(0); return; } } if (instance_expr) { var temp = pushTempValue(); instance_expr.get(temp); if (temp.is_number()) { index = temp.data; var type_instances = object_type.instances; if (type_instances.length !== 0) // avoid NaN result with % { index %= type_instances.length; // wraparound if (index < 0) // offset index += type_instances.length; } inst = object_type.getInstanceByIID(index); var to_ret = inst.instance_vars[varindex]; if (cr.is_string(to_ret)) ret.set_string(to_ret); else ret.set_float(to_ret); popTempValue(); return; // done } popTempValue(); } var len = instances.length; if (index >= len || index <= -len) index %= len; // wraparound if (index < 0) index += len; inst = instances[index]; var offset = 0; if (object_type.is_family) { offset = inst.type.family_var_map[object_type.family_index]; } var to_ret = inst.instance_vars[varindex + offset]; if (cr.is_string(to_ret)) ret.set_string(to_ret); else ret.set_float(to_ret); }; ExpNode.prototype.eval_int = function (ret) { ret.type = cr.exptype.Integer; ret.data = this.value; }; ExpNode.prototype.eval_float = function (ret) { ret.type = cr.exptype.Float; ret.data = this.value; }; ExpNode.prototype.eval_string = function (ret) { ret.type = cr.exptype.String; ret.data = this.value; }; ExpNode.prototype.eval_unaryminus = function (ret) { this.first.get(ret); // retrieve operand if (ret.is_number()) ret.data = -ret.data; }; ExpNode.prototype.eval_add = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data += temp.data; // both operands numbers: add if (temp.is_float()) ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_subtract = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data -= temp.data; // both operands numbers: subtract if (temp.is_float()) ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_multiply = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data *= temp.data; // both operands numbers: multiply if (temp.is_float()) ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_divide = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data /= temp.data; // both operands numbers: divide ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_mod = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data %= temp.data; // both operands numbers: modulo if (temp.is_float()) ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_power = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { ret.data = Math.pow(ret.data, temp.data); // both operands numbers: raise to power if (temp.is_float()) ret.make_float(); } popTempValue(); }; ExpNode.prototype.eval_and = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (temp.is_string() || ret.is_string()) this.eval_and_stringconcat(ret, temp); else this.eval_and_logical(ret, temp); popTempValue(); }; ExpNode.prototype.eval_and_stringconcat = function (ret, temp) { if (ret.is_string() && temp.is_string()) this.eval_and_stringconcat_str_str(ret, temp); else this.eval_and_stringconcat_num(ret, temp); }; ExpNode.prototype.eval_and_stringconcat_str_str = function (ret, temp) { ret.data += temp.data; }; ExpNode.prototype.eval_and_stringconcat_num = function (ret, temp) { if (ret.is_string()) { ret.data += (Math.round(temp.data * 1e10) / 1e10).toString(); } else { ret.set_string(ret.data.toString() + temp.data); } }; ExpNode.prototype.eval_and_logical = function (ret, temp) { ret.set_int(ret.data && temp.data ? 1 : 0); }; ExpNode.prototype.eval_or = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand if (ret.is_number() && temp.is_number()) { if (ret.data || temp.data) ret.set_int(1); else ret.set_int(0); } popTempValue(); }; ExpNode.prototype.eval_conditional = function (ret) { this.first.get(ret); // condition operand if (ret.data) // is true this.second.get(ret); // evaluate second operand to ret else this.third.get(ret); // evaluate third operand to ret }; ExpNode.prototype.eval_equal = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data === temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_notequal = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data !== temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_less = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data < temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_lessequal = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data <= temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_greater = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data > temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_greaterequal = function (ret) { this.first.get(ret); // left operand var temp = pushTempValue(); this.second.get(temp); // right operand ret.set_int(ret.data >= temp.data ? 1 : 0); popTempValue(); }; ExpNode.prototype.eval_eventvar_exp = function (ret) { var val = this.eventvar.getValue(); if (cr.is_number(val)) ret.set_float(val); else ret.set_string(val); }; cr.expNode = ExpNode; function ExpValue(type, data) { this.type = type || cr.exptype.Integer; this.data = data || 0; this.object_class = null; ; ; ; if (this.type == cr.exptype.Integer) this.data = Math.floor(this.data); cr.seal(this); }; ExpValue.prototype.is_int = function () { return this.type === cr.exptype.Integer; }; ExpValue.prototype.is_float = function () { return this.type === cr.exptype.Float; }; ExpValue.prototype.is_number = function () { return this.type === cr.exptype.Integer || this.type === cr.exptype.Float; }; ExpValue.prototype.is_string = function () { return this.type === cr.exptype.String; }; ExpValue.prototype.make_int = function () { if (!this.is_int()) { if (this.is_float()) this.data = Math.floor(this.data); // truncate float else if (this.is_string()) this.data = parseInt(this.data, 10); this.type = cr.exptype.Integer; } }; ExpValue.prototype.make_float = function () { if (!this.is_float()) { if (this.is_string()) this.data = parseFloat(this.data); this.type = cr.exptype.Float; } }; ExpValue.prototype.make_string = function () { if (!this.is_string()) { this.data = this.data.toString(); this.type = cr.exptype.String; } }; ExpValue.prototype.set_int = function (val) { ; this.type = cr.exptype.Integer; this.data = Math.floor(val); }; ExpValue.prototype.set_float = function (val) { ; this.type = cr.exptype.Float; this.data = val; }; ExpValue.prototype.set_string = function (val) { ; this.type = cr.exptype.String; this.data = val; }; ExpValue.prototype.set_any = function (val) { if (cr.is_number(val)) { this.type = cr.exptype.Float; this.data = val; } else if (cr.is_string(val)) { this.type = cr.exptype.String; this.data = val.toString(); } else { this.type = cr.exptype.Integer; this.data = 0; } }; cr.expvalue = ExpValue; cr.exptype = { Integer: 0, // emulated; no native integer support in javascript Float: 1, String: 2 }; }()); ; cr.system_object = function (runtime) { this.runtime = runtime; this.waits = []; }; cr.system_object.prototype.saveToJSON = function () { var o = {}; var i, len, j, lenj, p, w, t, sobj; o["waits"] = []; var owaits = o["waits"]; var waitobj; for (i = 0, len = this.waits.length; i < len; i++) { w = this.waits[i]; waitobj = { "t": w.time, "st": w.signaltag, "s": w.signalled, "ev": w.ev.sid, "sm": [], "sols": {} }; if (w.ev.actions[w.actindex]) waitobj["act"] = w.ev.actions[w.actindex].sid; for (j = 0, lenj = w.solModifiers.length; j < lenj; j++) waitobj["sm"].push(w.solModifiers[j].sid); for (p in w.sols) { if (w.sols.hasOwnProperty(p)) { t = this.runtime.types_by_index[parseInt(p, 10)]; ; sobj = { "sa": w.sols[p].sa, "insts": [] }; for (j = 0, lenj = w.sols[p].insts.length; j < lenj; j++) sobj["insts"].push(w.sols[p].insts[j].uid); waitobj["sols"][t.sid.toString()] = sobj; } } owaits.push(waitobj); } return o; }; cr.system_object.prototype.loadFromJSON = function (o) { var owaits = o["waits"]; var i, len, j, lenj, p, w, addWait, e, aindex, t, savedsol, nusol, inst; cr.clearArray(this.waits); for (i = 0, len = owaits.length; i < len; i++) { w = owaits[i]; e = this.runtime.blocksBySid[w["ev"].toString()]; if (!e) continue; // event must've gone missing aindex = -1; for (j = 0, lenj = e.actions.length; j < lenj; j++) { if (e.actions[j].sid === w["act"]) { aindex = j; break; } } if (aindex === -1) continue; // action must've gone missing addWait = {}; addWait.sols = {}; addWait.solModifiers = []; addWait.deleteme = false; addWait.time = w["t"]; addWait.signaltag = w["st"] || ""; addWait.signalled = !!w["s"]; addWait.ev = e; addWait.actindex = aindex; for (j = 0, lenj = w["sm"].length; j < lenj; j++) { t = this.runtime.getObjectTypeBySid(w["sm"][j]); if (t) addWait.solModifiers.push(t); } for (p in w["sols"]) { if (w["sols"].hasOwnProperty(p)) { t = this.runtime.getObjectTypeBySid(parseInt(p, 10)); if (!t) continue; // type must've been deleted savedsol = w["sols"][p]; nusol = { sa: savedsol["sa"], insts: [] }; for (j = 0, lenj = savedsol["insts"].length; j < lenj; j++) { inst = this.runtime.getObjectByUID(savedsol["insts"][j]); if (inst) nusol.insts.push(inst); } addWait.sols[t.index.toString()] = nusol; } } this.waits.push(addWait); } }; (function () { var sysProto = cr.system_object.prototype; function SysCnds() {}; SysCnds.prototype.EveryTick = function() { return true; }; SysCnds.prototype.OnLayoutStart = function() { return true; }; SysCnds.prototype.OnLayoutEnd = function() { return true; }; SysCnds.prototype.Compare = function(x, cmp, y) { return cr.do_cmp(x, cmp, y); }; SysCnds.prototype.CompareTime = function (cmp, t) { var elapsed = this.runtime.kahanTime.sum; if (cmp === 0) { var cnd = this.runtime.getCurrentCondition(); if (!cnd.extra["CompareTime_executed"]) { if (elapsed >= t) { cnd.extra["CompareTime_executed"] = true; return true; } } return false; } return cr.do_cmp(elapsed, cmp, t); }; SysCnds.prototype.LayerVisible = function (layer) { if (!layer) return false; else return layer.visible; }; SysCnds.prototype.LayerEmpty = function (layer) { if (!layer) return false; else return !layer.instances.length; }; SysCnds.prototype.LayerCmpOpacity = function (layer, cmp, opacity_) { if (!layer) return false; return cr.do_cmp(layer.opacity * 100, cmp, opacity_); }; SysCnds.prototype.Repeat = function (count) { var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var solModifierAfterCnds = current_frame.isModifierAfterCnds(); var current_loop = this.runtime.pushLoopStack(); var i; if (solModifierAfterCnds) { for (i = 0; i < count && !current_loop.stopped; i++) { this.runtime.pushCopySol(current_event.solModifiers); current_loop.index = i; current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); } } else { for (i = 0; i < count && !current_loop.stopped; i++) { current_loop.index = i; current_event.retrigger(); } } this.runtime.popLoopStack(); return false; }; SysCnds.prototype.While = function (count) { var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var solModifierAfterCnds = current_frame.isModifierAfterCnds(); var current_loop = this.runtime.pushLoopStack(); var i; if (solModifierAfterCnds) { for (i = 0; !current_loop.stopped; i++) { this.runtime.pushCopySol(current_event.solModifiers); current_loop.index = i; if (!current_event.retrigger()) // one of the other conditions returned false current_loop.stopped = true; // break this.runtime.popSol(current_event.solModifiers); } } else { for (i = 0; !current_loop.stopped; i++) { current_loop.index = i; if (!current_event.retrigger()) current_loop.stopped = true; } } this.runtime.popLoopStack(); return false; }; SysCnds.prototype.For = function (name, start, end) { var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var solModifierAfterCnds = current_frame.isModifierAfterCnds(); var current_loop = this.runtime.pushLoopStack(name); var i; if (end < start) { if (solModifierAfterCnds) { for (i = start; i >= end && !current_loop.stopped; --i) // inclusive to end { this.runtime.pushCopySol(current_event.solModifiers); current_loop.index = i; current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); } } else { for (i = start; i >= end && !current_loop.stopped; --i) // inclusive to end { current_loop.index = i; current_event.retrigger(); } } } else { if (solModifierAfterCnds) { for (i = start; i <= end && !current_loop.stopped; ++i) // inclusive to end { this.runtime.pushCopySol(current_event.solModifiers); current_loop.index = i; current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); } } else { for (i = start; i <= end && !current_loop.stopped; ++i) // inclusive to end { current_loop.index = i; current_event.retrigger(); } } } this.runtime.popLoopStack(); return false; }; var foreach_instancestack = []; var foreach_instanceptr = -1; SysCnds.prototype.ForEach = function (obj) { var sol = obj.getCurrentSol(); foreach_instanceptr++; if (foreach_instancestack.length === foreach_instanceptr) foreach_instancestack.push([]); var instances = foreach_instancestack[foreach_instanceptr]; cr.shallowAssignArray(instances, sol.getObjects()); var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var solModifierAfterCnds = current_frame.isModifierAfterCnds(); var current_loop = this.runtime.pushLoopStack(); var i, len, j, lenj, inst, s, sol2; var is_contained = obj.is_contained; if (solModifierAfterCnds) { for (i = 0, len = instances.length; i < len && !current_loop.stopped; i++) { this.runtime.pushCopySol(current_event.solModifiers); inst = instances[i]; sol = obj.getCurrentSol(); sol.select_all = false; cr.clearArray(sol.instances); sol.instances[0] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; sol2 = s.type.getCurrentSol(); sol2.select_all = false; cr.clearArray(sol2.instances); sol2.instances[0] = s; } } current_loop.index = i; current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); } } else { sol.select_all = false; cr.clearArray(sol.instances); for (i = 0, len = instances.length; i < len && !current_loop.stopped; i++) { inst = instances[i]; sol.instances[0] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; sol2 = s.type.getCurrentSol(); sol2.select_all = false; cr.clearArray(sol2.instances); sol2.instances[0] = s; } } current_loop.index = i; current_event.retrigger(); } } cr.clearArray(instances); this.runtime.popLoopStack(); foreach_instanceptr--; return false; }; function foreach_sortinstances(a, b) { var va = a.extra["c2_feo_val"]; var vb = b.extra["c2_feo_val"]; if (cr.is_number(va) && cr.is_number(vb)) return va - vb; else { va = "" + va; vb = "" + vb; if (va < vb) return -1; else if (va > vb) return 1; else return 0; } }; SysCnds.prototype.ForEachOrdered = function (obj, exp, order) { var sol = obj.getCurrentSol(); foreach_instanceptr++; if (foreach_instancestack.length === foreach_instanceptr) foreach_instancestack.push([]); var instances = foreach_instancestack[foreach_instanceptr]; cr.shallowAssignArray(instances, sol.getObjects()); var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var current_condition = this.runtime.getCurrentCondition(); var solModifierAfterCnds = current_frame.isModifierAfterCnds(); var current_loop = this.runtime.pushLoopStack(); var i, len, j, lenj, inst, s, sol2; for (i = 0, len = instances.length; i < len; i++) { instances[i].extra["c2_feo_val"] = current_condition.parameters[1].get(i); } instances.sort(foreach_sortinstances); if (order === 1) instances.reverse(); var is_contained = obj.is_contained; if (solModifierAfterCnds) { for (i = 0, len = instances.length; i < len && !current_loop.stopped; i++) { this.runtime.pushCopySol(current_event.solModifiers); inst = instances[i]; sol = obj.getCurrentSol(); sol.select_all = false; cr.clearArray(sol.instances); sol.instances[0] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; sol2 = s.type.getCurrentSol(); sol2.select_all = false; cr.clearArray(sol2.instances); sol2.instances[0] = s; } } current_loop.index = i; current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); } } else { sol.select_all = false; cr.clearArray(sol.instances); for (i = 0, len = instances.length; i < len && !current_loop.stopped; i++) { inst = instances[i]; sol.instances[0] = inst; if (is_contained) { for (j = 0, lenj = inst.siblings.length; j < lenj; j++) { s = inst.siblings[j]; sol2 = s.type.getCurrentSol(); sol2.select_all = false; cr.clearArray(sol2.instances); sol2.instances[0] = s; } } current_loop.index = i; current_event.retrigger(); } } cr.clearArray(instances); this.runtime.popLoopStack(); foreach_instanceptr--; return false; }; SysCnds.prototype.PickByComparison = function (obj_, exp_, cmp_, val_) { var i, len, k, inst; if (!obj_) return; foreach_instanceptr++; if (foreach_instancestack.length === foreach_instanceptr) foreach_instancestack.push([]); var tmp_instances = foreach_instancestack[foreach_instanceptr]; var sol = obj_.getCurrentSol(); cr.shallowAssignArray(tmp_instances, sol.getObjects()); if (sol.select_all) cr.clearArray(sol.else_instances); var current_condition = this.runtime.getCurrentCondition(); for (i = 0, k = 0, len = tmp_instances.length; i < len; i++) { inst = tmp_instances[i]; tmp_instances[k] = inst; exp_ = current_condition.parameters[1].get(i); val_ = current_condition.parameters[3].get(i); if (cr.do_cmp(exp_, cmp_, val_)) { k++; } else { sol.else_instances.push(inst); } } cr.truncateArray(tmp_instances, k); sol.select_all = false; cr.shallowAssignArray(sol.instances, tmp_instances); cr.clearArray(tmp_instances); foreach_instanceptr--; obj_.applySolToContainer(); return !!sol.instances.length; }; SysCnds.prototype.PickByEvaluate = function (obj_, exp_) { var i, len, k, inst; if (!obj_) return; foreach_instanceptr++; if (foreach_instancestack.length === foreach_instanceptr) foreach_instancestack.push([]); var tmp_instances = foreach_instancestack[foreach_instanceptr]; var sol = obj_.getCurrentSol(); cr.shallowAssignArray(tmp_instances, sol.getObjects()); if (sol.select_all) cr.clearArray(sol.else_instances); var current_condition = this.runtime.getCurrentCondition(); for (i = 0, k = 0, len = tmp_instances.length; i < len; i++) { inst = tmp_instances[i]; tmp_instances[k] = inst; exp_ = current_condition.parameters[1].get(i); if (exp_) { k++; } else { sol.else_instances.push(inst); } } cr.truncateArray(tmp_instances, k); sol.select_all = false; cr.shallowAssignArray(sol.instances, tmp_instances); cr.clearArray(tmp_instances); foreach_instanceptr--; obj_.applySolToContainer(); return !!sol.instances.length; }; SysCnds.prototype.TriggerOnce = function () { var cndextra = this.runtime.getCurrentCondition().extra; if (typeof cndextra["TriggerOnce_lastTick"] === "undefined") cndextra["TriggerOnce_lastTick"] = -1; var last_tick = cndextra["TriggerOnce_lastTick"]; var cur_tick = this.runtime.tickcount; cndextra["TriggerOnce_lastTick"] = cur_tick; return this.runtime.layout_first_tick || last_tick !== cur_tick - 1; }; SysCnds.prototype.Every = function (seconds) { var cnd = this.runtime.getCurrentCondition(); var last_time = cnd.extra["Every_lastTime"] || 0; var cur_time = this.runtime.kahanTime.sum; if (typeof cnd.extra["Every_seconds"] === "undefined") cnd.extra["Every_seconds"] = seconds; var this_seconds = cnd.extra["Every_seconds"]; if (cur_time >= last_time + this_seconds) { cnd.extra["Every_lastTime"] = last_time + this_seconds; if (cur_time >= cnd.extra["Every_lastTime"] + 0.04) { cnd.extra["Every_lastTime"] = cur_time; } cnd.extra["Every_seconds"] = seconds; return true; } else if (cur_time < last_time - 0.1) { cnd.extra["Every_lastTime"] = cur_time; } return false; }; SysCnds.prototype.PickNth = function (obj, index) { if (!obj) return false; var sol = obj.getCurrentSol(); var instances = sol.getObjects(); index = cr.floor(index); if (index < 0 || index >= instances.length) return false; var inst = instances[index]; sol.pick_one(inst); obj.applySolToContainer(); return true; }; SysCnds.prototype.PickRandom = function (obj) { if (!obj) return false; var sol = obj.getCurrentSol(); var instances = sol.getObjects(); var index = cr.floor(Math.random() * instances.length); if (index >= instances.length) return false; var inst = instances[index]; sol.pick_one(inst); obj.applySolToContainer(); return true; }; SysCnds.prototype.CompareVar = function (v, cmp, val) { return cr.do_cmp(v.getValue(), cmp, val); }; SysCnds.prototype.IsGroupActive = function (group) { var g = this.runtime.groups_by_name[group.toLowerCase()]; return g && g.group_active; }; SysCnds.prototype.IsPreview = function () { return typeof cr_is_preview !== "undefined"; }; SysCnds.prototype.PickAll = function (obj) { if (!obj) return false; if (!obj.instances.length) return false; var sol = obj.getCurrentSol(); sol.select_all = true; obj.applySolToContainer(); return true; }; SysCnds.prototype.IsMobile = function () { return this.runtime.isMobile; }; SysCnds.prototype.CompareBetween = function (x, a, b) { return x >= a && x <= b; }; SysCnds.prototype.Else = function () { var current_frame = this.runtime.getCurrentEventStack(); if (current_frame.else_branch_ran) return false; // another event in this else-if chain has run else return !current_frame.last_event_true; /* var current_frame = this.runtime.getCurrentEventStack(); var current_event = current_frame.current_event; var prev_event = current_event.prev_block; if (!prev_event) return false; if (prev_event.is_logical) return !this.runtime.last_event_true; var i, len, j, lenj, s, sol, temp, inst, any_picked = false; for (i = 0, len = prev_event.cndReferences.length; i < len; i++) { s = prev_event.cndReferences[i]; sol = s.getCurrentSol(); if (sol.select_all || sol.instances.length === s.instances.length) { sol.select_all = false; sol.instances.length = 0; } else { if (sol.instances.length === 1 && sol.else_instances.length === 0 && s.instances.length >= 2) { inst = sol.instances[0]; sol.instances.length = 0; for (j = 0, lenj = s.instances.length; j < lenj; j++) { if (s.instances[j] != inst) sol.instances.push(s.instances[j]); } any_picked = true; } else { temp = sol.instances; sol.instances = sol.else_instances; sol.else_instances = temp; any_picked = true; } } } return any_picked; */ }; SysCnds.prototype.OnLoadFinished = function () { return true; }; SysCnds.prototype.OnCanvasSnapshot = function () { return true; }; SysCnds.prototype.EffectsSupported = function () { return !!this.runtime.glwrap; }; SysCnds.prototype.OnSaveComplete = function () { return true; }; SysCnds.prototype.OnSaveFailed = function () { return true; }; SysCnds.prototype.OnLoadComplete = function () { return true; }; SysCnds.prototype.OnLoadFailed = function () { return true; }; SysCnds.prototype.ObjectUIDExists = function (u) { return !!this.runtime.getObjectByUID(u); }; SysCnds.prototype.IsOnPlatform = function (p) { var rt = this.runtime; switch (p) { case 0: // HTML5 website return !rt.isDomFree && !rt.isNodeWebkit && !rt.isCordova && !rt.isWinJS && !rt.isWindowsPhone8 && !rt.isBlackberry10 && !rt.isAmazonWebApp; case 1: // iOS return rt.isiOS; case 2: // Android return rt.isAndroid; case 3: // Windows 8 return rt.isWindows8App; case 4: // Windows Phone 8 return rt.isWindowsPhone8; case 5: // Blackberry 10 return rt.isBlackberry10; case 6: // Tizen return rt.isTizen; case 7: // CocoonJS return rt.isCocoonJs; case 8: // Cordova return rt.isCordova; case 9: // Scirra Arcade return rt.isArcade; case 10: // node-webkit return rt.isNodeWebkit; case 11: // crosswalk return rt.isCrosswalk; case 12: // amazon webapp return rt.isAmazonWebApp; case 13: // windows 10 app return rt.isWindows10; default: // should not be possible return false; } }; var cacheRegex = null; var lastRegex = ""; var lastFlags = ""; function getRegex(regex_, flags_) { if (!cacheRegex || regex_ !== lastRegex || flags_ !== lastFlags) { cacheRegex = new RegExp(regex_, flags_); lastRegex = regex_; lastFlags = flags_; } cacheRegex.lastIndex = 0; // reset return cacheRegex; }; SysCnds.prototype.RegexTest = function (str_, regex_, flags_) { var regex = getRegex(regex_, flags_); return regex.test(str_); }; var tmp_arr = []; SysCnds.prototype.PickOverlappingPoint = function (obj_, x_, y_) { if (!obj_) return false; var sol = obj_.getCurrentSol(); var instances = sol.getObjects(); var current_event = this.runtime.getCurrentEventStack().current_event; var orblock = current_event.orblock; var cnd = this.runtime.getCurrentCondition(); var i, len, inst, pick; if (sol.select_all) { cr.shallowAssignArray(tmp_arr, instances); cr.clearArray(sol.else_instances); sol.select_all = false; cr.clearArray(sol.instances); } else { if (orblock) { cr.shallowAssignArray(tmp_arr, sol.else_instances); cr.clearArray(sol.else_instances); } else { cr.shallowAssignArray(tmp_arr, instances); cr.clearArray(sol.instances); } } for (i = 0, len = tmp_arr.length; i < len; ++i) { inst = tmp_arr[i]; inst.update_bbox(); pick = cr.xor(inst.contains_pt(x_, y_), cnd.inverted); if (pick) sol.instances.push(inst); else sol.else_instances.push(inst); } obj_.applySolToContainer(); return cr.xor(!!sol.instances.length, cnd.inverted); }; SysCnds.prototype.IsNaN = function (n) { return !!isNaN(n); }; SysCnds.prototype.AngleWithin = function (a1, within, a2) { return cr.angleDiff(cr.to_radians(a1), cr.to_radians(a2)) <= cr.to_radians(within); }; SysCnds.prototype.IsClockwiseFrom = function (a1, a2) { return cr.angleClockwise(cr.to_radians(a1), cr.to_radians(a2)); }; SysCnds.prototype.IsBetweenAngles = function (a, la, ua) { var angle = cr.to_clamped_radians(a); var lower = cr.to_clamped_radians(la); var upper = cr.to_clamped_radians(ua); var obtuse = (!cr.angleClockwise(upper, lower)); if (obtuse) return !(!cr.angleClockwise(angle, lower) && cr.angleClockwise(angle, upper)); else return cr.angleClockwise(angle, lower) && !cr.angleClockwise(angle, upper); }; SysCnds.prototype.IsValueType = function (x, t) { if (typeof x === "number") return t === 0; else // string return t === 1; }; sysProto.cnds = new SysCnds(); function SysActs() {}; SysActs.prototype.GoToLayout = function (to) { if (this.runtime.isloading) return; // cannot change layout while loading on loader layout if (this.runtime.changelayout) return; // already changing to a different layout ; this.runtime.changelayout = to; }; SysActs.prototype.NextPrevLayout = function (prev) { if (this.runtime.isloading) return; // cannot change layout while loading on loader layout if (this.runtime.changelayout) return; // already changing to a different layout var index = this.runtime.layouts_by_index.indexOf(this.runtime.running_layout); if (prev && index === 0) return; // cannot go to previous layout from first layout if (!prev && index === this.runtime.layouts_by_index.length - 1) return; // cannot go to next layout from last layout var to = this.runtime.layouts_by_index[index + (prev ? -1 : 1)]; ; this.runtime.changelayout = to; }; SysActs.prototype.CreateObject = function (obj, layer, x, y) { if (!layer || !obj) return; var inst = this.runtime.createInstance(obj, layer, x, y); if (!inst) return; this.runtime.isInOnDestroy++; var i, len, s; this.runtime.trigger(Object.getPrototypeOf(obj.plugin).cnds.OnCreated, inst); if (inst.is_contained) { for (i = 0, len = inst.siblings.length; i < len; i++) { s = inst.siblings[i]; this.runtime.trigger(Object.getPrototypeOf(s.type.plugin).cnds.OnCreated, s); } } this.runtime.isInOnDestroy--; var sol = obj.getCurrentSol(); sol.select_all = false; cr.clearArray(sol.instances); sol.instances[0] = inst; if (inst.is_contained) { for (i = 0, len = inst.siblings.length; i < len; i++) { s = inst.siblings[i]; sol = s.type.getCurrentSol(); sol.select_all = false; cr.clearArray(sol.instances); sol.instances[0] = s; } } }; SysActs.prototype.SetLayerVisible = function (layer, visible_) { if (!layer) return; if (layer.visible !== visible_) { layer.visible = visible_; this.runtime.redraw = true; } }; SysActs.prototype.SetLayerOpacity = function (layer, opacity_) { if (!layer) return; opacity_ = cr.clamp(opacity_ / 100, 0, 1); if (layer.opacity !== opacity_) { layer.opacity = opacity_; this.runtime.redraw = true; } }; SysActs.prototype.SetLayerScaleRate = function (layer, sr) { if (!layer) return; if (layer.zoomRate !== sr) { layer.zoomRate = sr; this.runtime.redraw = true; } }; SysActs.prototype.SetLayerForceOwnTexture = function (layer, f) { if (!layer) return; f = !!f; if (layer.forceOwnTexture !== f) { layer.forceOwnTexture = f; this.runtime.redraw = true; } }; SysActs.prototype.SetLayoutScale = function (s) { if (!this.runtime.running_layout) return; if (this.runtime.running_layout.scale !== s) { this.runtime.running_layout.scale = s; this.runtime.running_layout.boundScrolling(); this.runtime.redraw = true; } }; SysActs.prototype.ScrollX = function(x) { this.runtime.running_layout.scrollToX(x); }; SysActs.prototype.ScrollY = function(y) { this.runtime.running_layout.scrollToY(y); }; SysActs.prototype.Scroll = function(x, y) { this.runtime.running_layout.scrollToX(x); this.runtime.running_layout.scrollToY(y); }; SysActs.prototype.ScrollToObject = function(obj) { var inst = obj.getFirstPicked(); if (inst) { this.runtime.running_layout.scrollToX(inst.x); this.runtime.running_layout.scrollToY(inst.y); } }; SysActs.prototype.SetVar = function(v, x) { ; if (v.vartype === 0) { if (cr.is_number(x)) v.setValue(x); else v.setValue(parseFloat(x)); } else if (v.vartype === 1) v.setValue(x.toString()); }; SysActs.prototype.AddVar = function(v, x) { ; if (v.vartype === 0) { if (cr.is_number(x)) v.setValue(v.getValue() + x); else v.setValue(v.getValue() + parseFloat(x)); } else if (v.vartype === 1) v.setValue(v.getValue() + x.toString()); }; SysActs.prototype.SubVar = function(v, x) { ; if (v.vartype === 0) { if (cr.is_number(x)) v.setValue(v.getValue() - x); else v.setValue(v.getValue() - parseFloat(x)); } }; SysActs.prototype.SetGroupActive = function (group, active) { var g = this.runtime.groups_by_name[group.toLowerCase()]; if (!g) return; switch (active) { case 0: g.setGroupActive(false); break; case 1: g.setGroupActive(true); break; case 2: g.setGroupActive(!g.group_active); break; } }; SysActs.prototype.SetTimescale = function (ts_) { var ts = ts_; if (ts < 0) ts = 0; this.runtime.timescale = ts; }; SysActs.prototype.SetObjectTimescale = function (obj, ts_) { var ts = ts_; if (ts < 0) ts = 0; if (!obj) return; var sol = obj.getCurrentSol(); var instances = sol.getObjects(); var i, len; for (i = 0, len = instances.length; i < len; i++) { instances[i].my_timescale = ts; } }; SysActs.prototype.RestoreObjectTimescale = function (obj) { if (!obj) return false; var sol = obj.getCurrentSol(); var instances = sol.getObjects(); var i, len; for (i = 0, len = instances.length; i < len; i++) { instances[i].my_timescale = -1.0; } }; var waitobjrecycle = []; function allocWaitObject() { var w; if (waitobjrecycle.length) w = waitobjrecycle.pop(); else { w = {}; w.sols = {}; w.solModifiers = []; } w.deleteme = false; return w; }; function freeWaitObject(w) { cr.wipe(w.sols); cr.clearArray(w.solModifiers); waitobjrecycle.push(w); }; var solstateobjects = []; function allocSolStateObject() { var s; if (solstateobjects.length) s = solstateobjects.pop(); else { s = {}; s.insts = []; } s.sa = false; return s; }; function freeSolStateObject(s) { cr.clearArray(s.insts); solstateobjects.push(s); }; SysActs.prototype.Wait = function (seconds) { if (seconds < 0) return; var i, len, s, t, ss; var evinfo = this.runtime.getCurrentEventStack(); var waitobj = allocWaitObject(); waitobj.time = this.runtime.kahanTime.sum + seconds; waitobj.signaltag = ""; waitobj.signalled = false; waitobj.ev = evinfo.current_event; waitobj.actindex = evinfo.actindex + 1; // pointing at next action for (i = 0, len = this.runtime.types_by_index.length; i < len; i++) { t = this.runtime.types_by_index[i]; s = t.getCurrentSol(); if (s.select_all && evinfo.current_event.solModifiers.indexOf(t) === -1) continue; waitobj.solModifiers.push(t); ss = allocSolStateObject(); ss.sa = s.select_all; cr.shallowAssignArray(ss.insts, s.instances); waitobj.sols[i.toString()] = ss; } this.waits.push(waitobj); return true; }; SysActs.prototype.WaitForSignal = function (tag) { var i, len, s, t, ss; var evinfo = this.runtime.getCurrentEventStack(); var waitobj = allocWaitObject(); waitobj.time = -1; waitobj.signaltag = tag.toLowerCase(); waitobj.signalled = false; waitobj.ev = evinfo.current_event; waitobj.actindex = evinfo.actindex + 1; // pointing at next action for (i = 0, len = this.runtime.types_by_index.length; i < len; i++) { t = this.runtime.types_by_index[i]; s = t.getCurrentSol(); if (s.select_all && evinfo.current_event.solModifiers.indexOf(t) === -1) continue; waitobj.solModifiers.push(t); ss = allocSolStateObject(); ss.sa = s.select_all; cr.shallowAssignArray(ss.insts, s.instances); waitobj.sols[i.toString()] = ss; } this.waits.push(waitobj); return true; }; SysActs.prototype.Signal = function (tag) { var lowertag = tag.toLowerCase(); var i, len, w; for (i = 0, len = this.waits.length; i < len; ++i) { w = this.waits[i]; if (w.time !== -1) continue; // timer wait, ignore if (w.signaltag === lowertag) // waiting for this signal w.signalled = true; // will run on next check } }; SysActs.prototype.SetLayerScale = function (layer, scale) { if (!layer) return; if (layer.scale === scale) return; layer.scale = scale; this.runtime.redraw = true; }; SysActs.prototype.ResetGlobals = function () { var i, len, g; for (i = 0, len = this.runtime.all_global_vars.length; i < len; i++) { g = this.runtime.all_global_vars[i]; g.data = g.initial; } }; SysActs.prototype.SetLayoutAngle = function (a) { a = cr.to_radians(a); a = cr.clamp_angle(a); if (this.runtime.running_layout) { if (this.runtime.running_layout.angle !== a) { this.runtime.running_layout.angle = a; this.runtime.redraw = true; } } }; SysActs.prototype.SetLayerAngle = function (layer, a) { if (!layer) return; a = cr.to_radians(a); a = cr.clamp_angle(a); if (layer.angle === a) return; layer.angle = a; this.runtime.redraw = true; }; SysActs.prototype.SetLayerParallax = function (layer, px, py) { if (!layer) return; if (layer.parallaxX === px / 100 && layer.parallaxY === py / 100) return; layer.parallaxX = px / 100; layer.parallaxY = py / 100; if (layer.parallaxX !== 1 || layer.parallaxY !== 1) { var i, len, instances = layer.instances; for (i = 0, len = instances.length; i < len; ++i) { instances[i].type.any_instance_parallaxed = true; } } this.runtime.redraw = true; }; SysActs.prototype.SetLayerBackground = function (layer, c) { if (!layer) return; var r = cr.GetRValue(c); var g = cr.GetGValue(c); var b = cr.GetBValue(c); if (layer.background_color[0] === r && layer.background_color[1] === g && layer.background_color[2] === b) return; layer.background_color[0] = r; layer.background_color[1] = g; layer.background_color[2] = b; this.runtime.redraw = true; }; SysActs.prototype.SetLayerTransparent = function (layer, t) { if (!layer) return; if (!!t === !!layer.transparent) return; layer.transparent = !!t; this.runtime.redraw = true; }; SysActs.prototype.SetLayerBlendMode = function (layer, bm) { if (!layer) return; if (layer.blend_mode === bm) return; layer.blend_mode = bm; layer.compositeOp = cr.effectToCompositeOp(layer.blend_mode); if (this.runtime.gl) cr.setGLBlend(layer, layer.blend_mode, this.runtime.gl); this.runtime.redraw = true; }; SysActs.prototype.StopLoop = function () { if (this.runtime.loop_stack_index < 0) return; // no loop currently running this.runtime.getCurrentLoop().stopped = true; }; SysActs.prototype.GoToLayoutByName = function (layoutname) { if (this.runtime.isloading) return; // cannot change layout while loading on loader layout if (this.runtime.changelayout) return; // already changing to different layout ; var l; for (l in this.runtime.layouts) { if (this.runtime.layouts.hasOwnProperty(l) && cr.equals_nocase(l, layoutname)) { this.runtime.changelayout = this.runtime.layouts[l]; return; } } }; SysActs.prototype.RestartLayout = function (layoutname) { if (this.runtime.isloading) return; // cannot restart loader layouts if (this.runtime.changelayout) return; // already changing to a different layout ; if (!this.runtime.running_layout) return; this.runtime.changelayout = this.runtime.running_layout; var i, len, g; for (i = 0, len = this.runtime.allGroups.length; i < len; i++) { g = this.runtime.allGroups[i]; g.setGroupActive(g.initially_activated); } }; SysActs.prototype.SnapshotCanvas = function (format_, quality_) { this.runtime.doCanvasSnapshot(format_ === 0 ? "image/png" : "image/jpeg", quality_ / 100); }; SysActs.prototype.SetCanvasSize = function (w, h) { if (w <= 0 || h <= 0) return; var mode = this.runtime.fullscreen_mode; var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || !!document["msFullscreenElement"] || document["fullScreen"] || this.runtime.isNodeFullscreen); if (isfullscreen && this.runtime.fullscreen_scaling > 0) mode = this.runtime.fullscreen_scaling; if (mode === 0) { this.runtime["setSize"](w, h, true); } else { this.runtime.original_width = w; this.runtime.original_height = h; this.runtime["setSize"](this.runtime.lastWindowWidth, this.runtime.lastWindowHeight, true); } }; SysActs.prototype.SetLayoutEffectEnabled = function (enable_, effectname_) { if (!this.runtime.running_layout || !this.runtime.glwrap) return; var et = this.runtime.running_layout.getEffectByName(effectname_); if (!et) return; // effect name not found var enable = (enable_ === 1); if (et.active == enable) return; // no change et.active = enable; this.runtime.running_layout.updateActiveEffects(); this.runtime.redraw = true; }; SysActs.prototype.SetLayerEffectEnabled = function (layer, enable_, effectname_) { if (!layer || !this.runtime.glwrap) return; var et = layer.getEffectByName(effectname_); if (!et) return; // effect name not found var enable = (enable_ === 1); if (et.active == enable) return; // no change et.active = enable; layer.updateActiveEffects(); this.runtime.redraw = true; }; SysActs.prototype.SetLayoutEffectParam = function (effectname_, index_, value_) { if (!this.runtime.running_layout || !this.runtime.glwrap) return; var et = this.runtime.running_layout.getEffectByName(effectname_); if (!et) return; // effect name not found var params = this.runtime.running_layout.effect_params[et.index]; index_ = Math.floor(index_); if (index_ < 0 || index_ >= params.length) return; // effect index out of bounds if (this.runtime.glwrap.getProgramParameterType(et.shaderindex, index_) === 1) value_ /= 100.0; if (params[index_] === value_) return; // no change params[index_] = value_; if (et.active) this.runtime.redraw = true; }; SysActs.prototype.SetLayerEffectParam = function (layer, effectname_, index_, value_) { if (!layer || !this.runtime.glwrap) return; var et = layer.getEffectByName(effectname_); if (!et) return; // effect name not found var params = layer.effect_params[et.index]; index_ = Math.floor(index_); if (index_ < 0 || index_ >= params.length) return; // effect index out of bounds if (this.runtime.glwrap.getProgramParameterType(et.shaderindex, index_) === 1) value_ /= 100.0; if (params[index_] === value_) return; // no change params[index_] = value_; if (et.active) this.runtime.redraw = true; }; SysActs.prototype.SaveState = function (slot_) { this.runtime.saveToSlot = slot_; }; SysActs.prototype.LoadState = function (slot_) { this.runtime.loadFromSlot = slot_; }; SysActs.prototype.LoadStateJSON = function (jsonstr_) { this.runtime.loadFromJson = jsonstr_; }; SysActs.prototype.SetHalfFramerateMode = function (set_) { this.runtime.halfFramerateMode = (set_ !== 0); }; SysActs.prototype.SetFullscreenQuality = function (q) { var isfullscreen = (document["mozFullScreen"] || document["webkitIsFullScreen"] || !!document["msFullscreenElement"] || document["fullScreen"] || this.isNodeFullscreen); if (!isfullscreen && this.runtime.fullscreen_mode === 0) return; this.runtime.wantFullscreenScalingQuality = (q !== 0); this.runtime["setSize"](this.runtime.lastWindowWidth, this.runtime.lastWindowHeight, true); }; SysActs.prototype.ResetPersisted = function () { var i, len; for (i = 0, len = this.runtime.layouts_by_index.length; i < len; ++i) { this.runtime.layouts_by_index[i].persist_data = {}; this.runtime.layouts_by_index[i].first_visit = true; } }; SysActs.prototype.RecreateInitialObjects = function (obj, x1, y1, x2, y2) { if (!obj) return; this.runtime.running_layout.recreateInitialObjects(obj, x1, y1, x2, y2); }; SysActs.prototype.SetPixelRounding = function (m) { this.runtime.pixel_rounding = (m !== 0); this.runtime.redraw = true; }; SysActs.prototype.SetMinimumFramerate = function (f) { if (f < 1) f = 1; if (f > 120) f = 120; this.runtime.minimumFramerate = f; }; function SortZOrderList(a, b) { var layerA = a[0]; var layerB = b[0]; var diff = layerA - layerB; if (diff !== 0) return diff; var indexA = a[1]; var indexB = b[1]; return indexA - indexB; }; function SortInstancesByValue(a, b) { return a[1] - b[1]; }; SysActs.prototype.SortZOrderByInstVar = function (obj, iv) { if (!obj) return; var i, len, inst, value, r, layer, toZ; var sol = obj.getCurrentSol(); var pickedInstances = sol.getObjects(); var zOrderList = []; var instValues = []; var layout = this.runtime.running_layout; var isFamily = obj.is_family; var familyIndex = obj.family_index; for (i = 0, len = pickedInstances.length; i < len; ++i) { inst = pickedInstances[i]; if (!inst.layer) continue; // not a world instance if (isFamily) value = inst.instance_vars[iv + inst.type.family_var_map[familyIndex]]; else value = inst.instance_vars[iv]; zOrderList.push([ inst.layer.index, inst.get_zindex() ]); instValues.push([ inst, value ]); } if (!zOrderList.length) return; // no instances were world instances zOrderList.sort(SortZOrderList); instValues.sort(SortInstancesByValue); for (i = 0, len = zOrderList.length; i < len; ++i) { inst = instValues[i][0]; // instance in the order we want layer = layout.layers[zOrderList[i][0]]; // layer to put it on toZ = zOrderList[i][1]; // Z index on that layer to put it if (layer.instances[toZ] !== inst) // not already got this instance there { layer.instances[toZ] = inst; // update instance inst.layer = layer; // update instance's layer reference (could have changed) layer.setZIndicesStaleFrom(toZ); // mark Z indices stale from this point since they have changed } } }; sysProto.acts = new SysActs(); function SysExps() {}; SysExps.prototype["int"] = function(ret, x) { if (cr.is_string(x)) { ret.set_int(parseInt(x, 10)); if (isNaN(ret.data)) ret.data = 0; } else ret.set_int(x); }; SysExps.prototype["float"] = function(ret, x) { if (cr.is_string(x)) { ret.set_float(parseFloat(x)); if (isNaN(ret.data)) ret.data = 0; } else ret.set_float(x); }; SysExps.prototype.str = function(ret, x) { if (cr.is_string(x)) ret.set_string(x); else ret.set_string(x.toString()); }; SysExps.prototype.len = function(ret, x) { ret.set_int(x.length || 0); }; SysExps.prototype.random = function (ret, a, b) { if (b === undefined) { ret.set_float(Math.random() * a); } else { ret.set_float(Math.random() * (b - a) + a); } }; SysExps.prototype.sqrt = function(ret, x) { ret.set_float(Math.sqrt(x)); }; SysExps.prototype.abs = function(ret, x) { ret.set_float(Math.abs(x)); }; SysExps.prototype.round = function(ret, x) { ret.set_int(Math.round(x)); }; SysExps.prototype.floor = function(ret, x) { ret.set_int(Math.floor(x)); }; SysExps.prototype.ceil = function(ret, x) { ret.set_int(Math.ceil(x)); }; SysExps.prototype.sin = function(ret, x) { ret.set_float(Math.sin(cr.to_radians(x))); }; SysExps.prototype.cos = function(ret, x) { ret.set_float(Math.cos(cr.to_radians(x))); }; SysExps.prototype.tan = function(ret, x) { ret.set_float(Math.tan(cr.to_radians(x))); }; SysExps.prototype.asin = function(ret, x) { ret.set_float(cr.to_degrees(Math.asin(x))); }; SysExps.prototype.acos = function(ret, x) { ret.set_float(cr.to_degrees(Math.acos(x))); }; SysExps.prototype.atan = function(ret, x) { ret.set_float(cr.to_degrees(Math.atan(x))); }; SysExps.prototype.exp = function(ret, x) { ret.set_float(Math.exp(x)); }; SysExps.prototype.ln = function(ret, x) { ret.set_float(Math.log(x)); }; SysExps.prototype.log10 = function(ret, x) { ret.set_float(Math.log(x) / Math.LN10); }; SysExps.prototype.max = function(ret) { var max_ = arguments[1]; if (typeof max_ !== "number") max_ = 0; var i, len, a; for (i = 2, len = arguments.length; i < len; i++) { a = arguments[i]; if (typeof a !== "number") continue; // ignore non-numeric types if (max_ < a) max_ = a; } ret.set_float(max_); }; SysExps.prototype.min = function(ret) { var min_ = arguments[1]; if (typeof min_ !== "number") min_ = 0; var i, len, a; for (i = 2, len = arguments.length; i < len; i++) { a = arguments[i]; if (typeof a !== "number") continue; // ignore non-numeric types if (min_ > a) min_ = a; } ret.set_float(min_); }; SysExps.prototype.dt = function(ret) { ret.set_float(this.runtime.dt); }; SysExps.prototype.timescale = function(ret) { ret.set_float(this.runtime.timescale); }; SysExps.prototype.wallclocktime = function(ret) { ret.set_float((Date.now() - this.runtime.start_time) / 1000.0); }; SysExps.prototype.time = function(ret) { ret.set_float(this.runtime.kahanTime.sum); }; SysExps.prototype.tickcount = function(ret) { ret.set_int(this.runtime.tickcount); }; SysExps.prototype.objectcount = function(ret) { ret.set_int(this.runtime.objectcount); }; SysExps.prototype.fps = function(ret) { ret.set_int(this.runtime.fps); }; SysExps.prototype.loopindex = function(ret, name_) { var loop, i, len; if (!this.runtime.loop_stack.length) { ret.set_int(0); return; } if (name_) { for (i = this.runtime.loop_stack_index; i >= 0; --i) { loop = this.runtime.loop_stack[i]; if (loop.name === name_) { ret.set_int(loop.index); return; } } ret.set_int(0); } else { loop = this.runtime.getCurrentLoop(); ret.set_int(loop ? loop.index : -1); } }; SysExps.prototype.distance = function(ret, x1, y1, x2, y2) { ret.set_float(cr.distanceTo(x1, y1, x2, y2)); }; SysExps.prototype.angle = function(ret, x1, y1, x2, y2) { ret.set_float(cr.to_degrees(cr.angleTo(x1, y1, x2, y2))); }; SysExps.prototype.scrollx = function(ret) { ret.set_float(this.runtime.running_layout.scrollX); }; SysExps.prototype.scrolly = function(ret) { ret.set_float(this.runtime.running_layout.scrollY); }; SysExps.prototype.newline = function(ret) { ret.set_string("\n"); }; SysExps.prototype.lerp = function(ret, a, b, x) { ret.set_float(cr.lerp(a, b, x)); }; SysExps.prototype.qarp = function(ret, a, b, c, x) { ret.set_float(cr.qarp(a, b, c, x)); }; SysExps.prototype.cubic = function(ret, a, b, c, d, x) { ret.set_float(cr.cubic(a, b, c, d, x)); }; SysExps.prototype.cosp = function(ret, a, b, x) { ret.set_float(cr.cosp(a, b, x)); }; SysExps.prototype.windowwidth = function(ret) { ret.set_int(this.runtime.width); }; SysExps.prototype.windowheight = function(ret) { ret.set_int(this.runtime.height); }; SysExps.prototype.uppercase = function(ret, str) { ret.set_string(cr.is_string(str) ? str.toUpperCase() : ""); }; SysExps.prototype.lowercase = function(ret, str) { ret.set_string(cr.is_string(str) ? str.toLowerCase() : ""); }; SysExps.prototype.clamp = function(ret, x, l, u) { if (x < l) ret.set_float(l); else if (x > u) ret.set_float(u); else ret.set_float(x); }; SysExps.prototype.layerscale = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(layer.scale); }; SysExps.prototype.layeropacity = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(layer.opacity * 100); }; SysExps.prototype.layerscalerate = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(layer.zoomRate); }; SysExps.prototype.layerparallaxx = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(layer.parallaxX * 100); }; SysExps.prototype.layerparallaxy = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(layer.parallaxY * 100); }; SysExps.prototype.layerindex = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_int(-1); else ret.set_int(layer.index); }; SysExps.prototype.layoutscale = function (ret) { if (this.runtime.running_layout) ret.set_float(this.runtime.running_layout.scale); else ret.set_float(0); }; SysExps.prototype.layoutangle = function (ret) { ret.set_float(cr.to_degrees(this.runtime.running_layout.angle)); }; SysExps.prototype.layerangle = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); if (!layer) ret.set_float(0); else ret.set_float(cr.to_degrees(layer.angle)); }; SysExps.prototype.layoutwidth = function (ret) { ret.set_int(this.runtime.running_layout.width); }; SysExps.prototype.layoutheight = function (ret) { ret.set_int(this.runtime.running_layout.height); }; SysExps.prototype.find = function (ret, text, searchstr) { if (cr.is_string(text) && cr.is_string(searchstr)) ret.set_int(text.search(new RegExp(cr.regexp_escape(searchstr), "i"))); else ret.set_int(-1); }; SysExps.prototype.findcase = function (ret, text, searchstr) { if (cr.is_string(text) && cr.is_string(searchstr)) ret.set_int(text.search(new RegExp(cr.regexp_escape(searchstr), ""))); else ret.set_int(-1); }; SysExps.prototype.left = function (ret, text, n) { ret.set_string(cr.is_string(text) ? text.substr(0, n) : ""); }; SysExps.prototype.right = function (ret, text, n) { ret.set_string(cr.is_string(text) ? text.substr(text.length - n) : ""); }; SysExps.prototype.mid = function (ret, text, index_, length_) { ret.set_string(cr.is_string(text) ? text.substr(index_, length_) : ""); }; SysExps.prototype.tokenat = function (ret, text, index_, sep) { if (cr.is_string(text) && cr.is_string(sep)) { var arr = text.split(sep); var i = cr.floor(index_); if (i < 0 || i >= arr.length) ret.set_string(""); else ret.set_string(arr[i]); } else ret.set_string(""); }; SysExps.prototype.tokencount = function (ret, text, sep) { if (cr.is_string(text) && text.length) ret.set_int(text.split(sep).length); else ret.set_int(0); }; SysExps.prototype.replace = function (ret, text, find_, replace_) { if (cr.is_string(text) && cr.is_string(find_) && cr.is_string(replace_)) ret.set_string(text.replace(new RegExp(cr.regexp_escape(find_), "gi"), replace_)); else ret.set_string(cr.is_string(text) ? text : ""); }; SysExps.prototype.trim = function (ret, text) { ret.set_string(cr.is_string(text) ? text.trim() : ""); }; SysExps.prototype.pi = function (ret) { ret.set_float(cr.PI); }; SysExps.prototype.layoutname = function (ret) { if (this.runtime.running_layout) ret.set_string(this.runtime.running_layout.name); else ret.set_string(""); }; SysExps.prototype.renderer = function (ret) { ret.set_string(this.runtime.gl ? "webgl" : "canvas2d"); }; SysExps.prototype.rendererdetail = function (ret) { ret.set_string(this.runtime.glUnmaskedRenderer); }; SysExps.prototype.anglediff = function (ret, a, b) { ret.set_float(cr.to_degrees(cr.angleDiff(cr.to_radians(a), cr.to_radians(b)))); }; SysExps.prototype.choose = function (ret) { var index = cr.floor(Math.random() * (arguments.length - 1)); ret.set_any(arguments[index + 1]); }; SysExps.prototype.rgb = function (ret, r, g, b) { ret.set_int(cr.RGB(r, g, b)); }; SysExps.prototype.projectversion = function (ret) { ret.set_string(this.runtime.versionstr); }; SysExps.prototype.projectname = function (ret) { ret.set_string(this.runtime.projectName); }; SysExps.prototype.anglelerp = function (ret, a, b, x) { a = cr.to_radians(a); b = cr.to_radians(b); var diff = cr.angleDiff(a, b); if (cr.angleClockwise(b, a)) { ret.set_float(cr.to_clamped_degrees(a + diff * x)); } else { ret.set_float(cr.to_clamped_degrees(a - diff * x)); } }; SysExps.prototype.anglerotate = function (ret, a, b, c) { a = cr.to_radians(a); b = cr.to_radians(b); c = cr.to_radians(c); ret.set_float(cr.to_clamped_degrees(cr.angleRotate(a, b, c))); }; SysExps.prototype.zeropad = function (ret, n, d) { var s = (n < 0 ? "-" : ""); if (n < 0) n = -n; var zeroes = d - n.toString().length; for (var i = 0; i < zeroes; i++) s += "0"; ret.set_string(s + n.toString()); }; SysExps.prototype.cpuutilisation = function (ret) { ret.set_float(this.runtime.cpuutilisation / 1000); }; SysExps.prototype.viewportleft = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.viewLeft : 0); }; SysExps.prototype.viewporttop = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.viewTop : 0); }; SysExps.prototype.viewportright = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.viewRight : 0); }; SysExps.prototype.viewportbottom = function (ret, layerparam) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.viewBottom : 0); }; SysExps.prototype.loadingprogress = function (ret) { ret.set_float(this.runtime.loadingprogress); }; SysExps.prototype.unlerp = function(ret, a, b, y) { ret.set_float(cr.unlerp(a, b, y)); }; SysExps.prototype.canvassnapshot = function (ret) { ret.set_string(this.runtime.snapshotData); }; SysExps.prototype.urlencode = function (ret, s) { ret.set_string(encodeURIComponent(s)); }; SysExps.prototype.urldecode = function (ret, s) { ret.set_string(decodeURIComponent(s)); }; SysExps.prototype.canvastolayerx = function (ret, layerparam, x, y) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.canvasToLayer(x, y, true) : 0); }; SysExps.prototype.canvastolayery = function (ret, layerparam, x, y) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.canvasToLayer(x, y, false) : 0); }; SysExps.prototype.layertocanvasx = function (ret, layerparam, x, y) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.layerToCanvas(x, y, true) : 0); }; SysExps.prototype.layertocanvasy = function (ret, layerparam, x, y) { var layer = this.runtime.getLayer(layerparam); ret.set_float(layer ? layer.layerToCanvas(x, y, false) : 0); }; SysExps.prototype.savestatejson = function (ret) { ret.set_string(this.runtime.lastSaveJson); }; SysExps.prototype.imagememoryusage = function (ret) { if (this.runtime.glwrap) ret.set_float(Math.round(100 * this.runtime.glwrap.estimateVRAM() / (1024 * 1024)) / 100); else ret.set_float(0); }; SysExps.prototype.regexsearch = function (ret, str_, regex_, flags_) { var regex = getRegex(regex_, flags_); ret.set_int(str_ ? str_.search(regex) : -1); }; SysExps.prototype.regexreplace = function (ret, str_, regex_, flags_, replace_) { var regex = getRegex(regex_, flags_); ret.set_string(str_ ? str_.replace(regex, replace_) : ""); }; var regexMatches = []; var lastMatchesStr = ""; var lastMatchesRegex = ""; var lastMatchesFlags = ""; function updateRegexMatches(str_, regex_, flags_) { if (str_ === lastMatchesStr && regex_ === lastMatchesRegex && flags_ === lastMatchesFlags) return; var regex = getRegex(regex_, flags_); regexMatches = str_.match(regex); lastMatchesStr = str_; lastMatchesRegex = regex_; lastMatchesFlags = flags_; }; SysExps.prototype.regexmatchcount = function (ret, str_, regex_, flags_) { var regex = getRegex(regex_, flags_); updateRegexMatches(str_, regex_, flags_); ret.set_int(regexMatches ? regexMatches.length : 0); }; SysExps.prototype.regexmatchat = function (ret, str_, regex_, flags_, index_) { index_ = Math.floor(index_); var regex = getRegex(regex_, flags_); updateRegexMatches(str_, regex_, flags_); if (!regexMatches || index_ < 0 || index_ >= regexMatches.length) ret.set_string(""); else ret.set_string(regexMatches[index_]); }; SysExps.prototype.infinity = function (ret) { ret.set_float(Infinity); }; SysExps.prototype.setbit = function (ret, n, b, v) { n = n | 0; b = b | 0; v = (v !== 0 ? 1 : 0); ret.set_int((n & ~(1 << b)) | (v << b)); }; SysExps.prototype.togglebit = function (ret, n, b) { n = n | 0; b = b | 0; ret.set_int(n ^ (1 << b)); }; SysExps.prototype.getbit = function (ret, n, b) { n = n | 0; b = b | 0; ret.set_int((n & (1 << b)) ? 1 : 0); }; SysExps.prototype.originalwindowwidth = function (ret) { ret.set_int(this.runtime.original_width); }; SysExps.prototype.originalwindowheight = function (ret) { ret.set_int(this.runtime.original_height); }; sysProto.exps = new SysExps(); sysProto.runWaits = function () { var i, j, len, w, k, s, ss; var evinfo = this.runtime.getCurrentEventStack(); for (i = 0, len = this.waits.length; i < len; i++) { w = this.waits[i]; if (w.time === -1) // signalled wait { if (!w.signalled) continue; // not yet signalled } else // timer wait { if (w.time > this.runtime.kahanTime.sum) continue; // timer not yet expired } evinfo.current_event = w.ev; evinfo.actindex = w.actindex; evinfo.cndindex = 0; for (k in w.sols) { if (w.sols.hasOwnProperty(k)) { s = this.runtime.types_by_index[parseInt(k, 10)].getCurrentSol(); ss = w.sols[k]; s.select_all = ss.sa; cr.shallowAssignArray(s.instances, ss.insts); freeSolStateObject(ss); } } w.ev.resume_actions_and_subevents(); this.runtime.clearSol(w.solModifiers); w.deleteme = true; } for (i = 0, j = 0, len = this.waits.length; i < len; i++) { w = this.waits[i]; this.waits[j] = w; if (w.deleteme) freeWaitObject(w); else j++; } cr.truncateArray(this.waits, j); }; }()); ; (function () { cr.add_common_aces = function (m, pluginProto) { var singleglobal_ = m[1]; var position_aces = m[3]; var size_aces = m[4]; var angle_aces = m[5]; var appearance_aces = m[6]; var zorder_aces = m[7]; var effects_aces = m[8]; if (!pluginProto.cnds) pluginProto.cnds = {}; if (!pluginProto.acts) pluginProto.acts = {}; if (!pluginProto.exps) pluginProto.exps = {}; var cnds = pluginProto.cnds; var acts = pluginProto.acts; var exps = pluginProto.exps; if (position_aces) { cnds.CompareX = function (cmp, x) { return cr.do_cmp(this.x, cmp, x); }; cnds.CompareY = function (cmp, y) { return cr.do_cmp(this.y, cmp, y); }; cnds.IsOnScreen = function () { var layer = this.layer; this.update_bbox(); var bbox = this.bbox; return !(bbox.right < layer.viewLeft || bbox.bottom < layer.viewTop || bbox.left > layer.viewRight || bbox.top > layer.viewBottom); }; cnds.IsOutsideLayout = function () { this.update_bbox(); var bbox = this.bbox; var layout = this.runtime.running_layout; return (bbox.right < 0 || bbox.bottom < 0 || bbox.left > layout.width || bbox.top > layout.height); }; cnds.PickDistance = function (which, x, y) { var sol = this.getCurrentSol(); var instances = sol.getObjects(); if (!instances.length) return false; var inst = instances[0]; var pickme = inst; var dist = cr.distanceTo(inst.x, inst.y, x, y); var i, len, d; for (i = 1, len = instances.length; i < len; i++) { inst = instances[i]; d = cr.distanceTo(inst.x, inst.y, x, y); if ((which === 0 && d < dist) || (which === 1 && d > dist)) { dist = d; pickme = inst; } } sol.pick_one(pickme); return true; }; acts.SetX = function (x) { if (this.x !== x) { this.x = x; this.set_bbox_changed(); } }; acts.SetY = function (y) { if (this.y !== y) { this.y = y; this.set_bbox_changed(); } }; acts.SetPos = function (x, y) { if (this.x !== x || this.y !== y) { this.x = x; this.y = y; this.set_bbox_changed(); } }; acts.SetPosToObject = function (obj, imgpt) { var inst = obj.getPairedInstance(this); if (!inst) return; var newx, newy; if (inst.getImagePoint) { newx = inst.getImagePoint(imgpt, true); newy = inst.getImagePoint(imgpt, false); } else { newx = inst.x; newy = inst.y; } if (this.x !== newx || this.y !== newy) { this.x = newx; this.y = newy; this.set_bbox_changed(); } }; acts.MoveForward = function (dist) { if (dist !== 0) { this.x += Math.cos(this.angle) * dist; this.y += Math.sin(this.angle) * dist; this.set_bbox_changed(); } }; acts.MoveAtAngle = function (a, dist) { if (dist !== 0) { this.x += Math.cos(cr.to_radians(a)) * dist; this.y += Math.sin(cr.to_radians(a)) * dist; this.set_bbox_changed(); } }; exps.X = function (ret) { ret.set_float(this.x); }; exps.Y = function (ret) { ret.set_float(this.y); }; exps.dt = function (ret) { ret.set_float(this.runtime.getDt(this)); }; } if (size_aces) { cnds.CompareWidth = function (cmp, w) { return cr.do_cmp(this.width, cmp, w); }; cnds.CompareHeight = function (cmp, h) { return cr.do_cmp(this.height, cmp, h); }; acts.SetWidth = function (w) { if (this.width !== w) { this.width = w; this.set_bbox_changed(); } }; acts.SetHeight = function (h) { if (this.height !== h) { this.height = h; this.set_bbox_changed(); } }; acts.SetSize = function (w, h) { if (this.width !== w || this.height !== h) { this.width = w; this.height = h; this.set_bbox_changed(); } }; exps.Width = function (ret) { ret.set_float(this.width); }; exps.Height = function (ret) { ret.set_float(this.height); }; exps.BBoxLeft = function (ret) { this.update_bbox(); ret.set_float(this.bbox.left); }; exps.BBoxTop = function (ret) { this.update_bbox(); ret.set_float(this.bbox.top); }; exps.BBoxRight = function (ret) { this.update_bbox(); ret.set_float(this.bbox.right); }; exps.BBoxBottom = function (ret) { this.update_bbox(); ret.set_float(this.bbox.bottom); }; } if (angle_aces) { cnds.AngleWithin = function (within, a) { return cr.angleDiff(this.angle, cr.to_radians(a)) <= cr.to_radians(within); }; cnds.IsClockwiseFrom = function (a) { return cr.angleClockwise(this.angle, cr.to_radians(a)); }; cnds.IsBetweenAngles = function (a, b) { var lower = cr.to_clamped_radians(a); var upper = cr.to_clamped_radians(b); var angle = cr.clamp_angle(this.angle); var obtuse = (!cr.angleClockwise(upper, lower)); if (obtuse) return !(!cr.angleClockwise(angle, lower) && cr.angleClockwise(angle, upper)); else return cr.angleClockwise(angle, lower) && !cr.angleClockwise(angle, upper); }; acts.SetAngle = function (a) { var newangle = cr.to_radians(cr.clamp_angle_degrees(a)); if (isNaN(newangle)) return; if (this.angle !== newangle) { this.angle = newangle; this.set_bbox_changed(); } }; acts.RotateClockwise = function (a) { if (a !== 0 && !isNaN(a)) { this.angle += cr.to_radians(a); this.angle = cr.clamp_angle(this.angle); this.set_bbox_changed(); } }; acts.RotateCounterclockwise = function (a) { if (a !== 0 && !isNaN(a)) { this.angle -= cr.to_radians(a); this.angle = cr.clamp_angle(this.angle); this.set_bbox_changed(); } }; acts.RotateTowardAngle = function (amt, target) { var newangle = cr.angleRotate(this.angle, cr.to_radians(target), cr.to_radians(amt)); if (isNaN(newangle)) return; if (this.angle !== newangle) { this.angle = newangle; this.set_bbox_changed(); } }; acts.RotateTowardPosition = function (amt, x, y) { var dx = x - this.x; var dy = y - this.y; var target = Math.atan2(dy, dx); var newangle = cr.angleRotate(this.angle, target, cr.to_radians(amt)); if (isNaN(newangle)) return; if (this.angle !== newangle) { this.angle = newangle; this.set_bbox_changed(); } }; acts.SetTowardPosition = function (x, y) { var dx = x - this.x; var dy = y - this.y; var newangle = Math.atan2(dy, dx); if (isNaN(newangle)) return; if (this.angle !== newangle) { this.angle = newangle; this.set_bbox_changed(); } }; exps.Angle = function (ret) { ret.set_float(cr.to_clamped_degrees(this.angle)); }; } if (!singleglobal_) { cnds.CompareInstanceVar = function (iv, cmp, val) { return cr.do_cmp(this.instance_vars[iv], cmp, val); }; cnds.IsBoolInstanceVarSet = function (iv) { return this.instance_vars[iv]; }; cnds.PickInstVarHiLow = function (which, iv) { var sol = this.getCurrentSol(); var instances = sol.getObjects(); if (!instances.length) return false; var inst = instances[0]; var pickme = inst; var val = inst.instance_vars[iv]; var i, len, v; for (i = 1, len = instances.length; i < len; i++) { inst = instances[i]; v = inst.instance_vars[iv]; if ((which === 0 && v < val) || (which === 1 && v > val)) { val = v; pickme = inst; } } sol.pick_one(pickme); return true; }; cnds.PickByUID = function (u) { var i, len, j, inst, families, instances, sol; var cnd = this.runtime.getCurrentCondition(); if (cnd.inverted) { sol = this.getCurrentSol(); if (sol.select_all) { sol.select_all = false; cr.clearArray(sol.instances); cr.clearArray(sol.else_instances); instances = this.instances; for (i = 0, len = instances.length; i < len; i++) { inst = instances[i]; if (inst.uid === u) sol.else_instances.push(inst); else sol.instances.push(inst); } this.applySolToContainer(); return !!sol.instances.length; } else { for (i = 0, j = 0, len = sol.instances.length; i < len; i++) { inst = sol.instances[i]; sol.instances[j] = inst; if (inst.uid === u) { sol.else_instances.push(inst); } else j++; } cr.truncateArray(sol.instances, j); this.applySolToContainer(); return !!sol.instances.length; } } else { inst = this.runtime.getObjectByUID(u); if (!inst) return false; sol = this.getCurrentSol(); if (!sol.select_all && sol.instances.indexOf(inst) === -1) return false; // not picked if (this.is_family) { families = inst.type.families; for (i = 0, len = families.length; i < len; i++) { if (families[i] === this) { sol.pick_one(inst); this.applySolToContainer(); return true; } } } else if (inst.type === this) { sol.pick_one(inst); this.applySolToContainer(); return true; } return false; } }; cnds.OnCreated = function () { return true; }; cnds.OnDestroyed = function () { return true; }; acts.SetInstanceVar = function (iv, val) { var myinstvars = this.instance_vars; if (cr.is_number(myinstvars[iv])) { if (cr.is_number(val)) myinstvars[iv] = val; else myinstvars[iv] = parseFloat(val); } else if (cr.is_string(myinstvars[iv])) { if (cr.is_string(val)) myinstvars[iv] = val; else myinstvars[iv] = val.toString(); } else ; }; acts.AddInstanceVar = function (iv, val) { var myinstvars = this.instance_vars; if (cr.is_number(myinstvars[iv])) { if (cr.is_number(val)) myinstvars[iv] += val; else myinstvars[iv] += parseFloat(val); } else if (cr.is_string(myinstvars[iv])) { if (cr.is_string(val)) myinstvars[iv] += val; else myinstvars[iv] += val.toString(); } else ; }; acts.SubInstanceVar = function (iv, val) { var myinstvars = this.instance_vars; if (cr.is_number(myinstvars[iv])) { if (cr.is_number(val)) myinstvars[iv] -= val; else myinstvars[iv] -= parseFloat(val); } else ; }; acts.SetBoolInstanceVar = function (iv, val) { this.instance_vars[iv] = val ? 1 : 0; }; acts.ToggleBoolInstanceVar = function (iv) { this.instance_vars[iv] = 1 - this.instance_vars[iv]; }; acts.Destroy = function () { this.runtime.DestroyInstance(this); }; if (!acts.LoadFromJsonString) { acts.LoadFromJsonString = function (str_) { var o, i, len, binst; try { o = JSON.parse(str_); } catch (e) { return; } this.runtime.loadInstanceFromJSON(this, o, true); if (this.afterLoad) this.afterLoad(); if (this.behavior_insts) { for (i = 0, len = this.behavior_insts.length; i < len; ++i) { binst = this.behavior_insts[i]; if (binst.afterLoad) binst.afterLoad(); } } }; } exps.Count = function (ret) { var count = ret.object_class.instances.length; var i, len, inst; for (i = 0, len = this.runtime.createRow.length; i < len; i++) { inst = this.runtime.createRow[i]; if (ret.object_class.is_family) { if (inst.type.families.indexOf(ret.object_class) >= 0) count++; } else { if (inst.type === ret.object_class) count++; } } ret.set_int(count); }; exps.PickedCount = function (ret) { ret.set_int(ret.object_class.getCurrentSol().getObjects().length); }; exps.UID = function (ret) { ret.set_int(this.uid); }; exps.IID = function (ret) { ret.set_int(this.get_iid()); }; if (!exps.AsJSON) { exps.AsJSON = function (ret) { ret.set_string(JSON.stringify(this.runtime.saveInstanceToJSON(this, true))); }; } } if (appearance_aces) { cnds.IsVisible = function () { return this.visible; }; acts.SetVisible = function (v) { if (!v !== !this.visible) { this.visible = !!v; this.runtime.redraw = true; } }; cnds.CompareOpacity = function (cmp, x) { return cr.do_cmp(cr.round6dp(this.opacity * 100), cmp, x); }; acts.SetOpacity = function (x) { var new_opacity = x / 100.0; if (new_opacity < 0) new_opacity = 0; else if (new_opacity > 1) new_opacity = 1; if (new_opacity !== this.opacity) { this.opacity = new_opacity; this.runtime.redraw = true; } }; exps.Opacity = function (ret) { ret.set_float(cr.round6dp(this.opacity * 100.0)); }; } if (zorder_aces) { cnds.IsOnLayer = function (layer_) { if (!layer_) return false; return this.layer === layer_; }; cnds.PickTopBottom = function (which_) { var sol = this.getCurrentSol(); var instances = sol.getObjects(); if (!instances.length) return false; var inst = instances[0]; var pickme = inst; var i, len; for (i = 1, len = instances.length; i < len; i++) { inst = instances[i]; if (which_ === 0) { if (inst.layer.index > pickme.layer.index || (inst.layer.index === pickme.layer.index && inst.get_zindex() > pickme.get_zindex())) { pickme = inst; } } else { if (inst.layer.index < pickme.layer.index || (inst.layer.index === pickme.layer.index && inst.get_zindex() < pickme.get_zindex())) { pickme = inst; } } } sol.pick_one(pickme); return true; }; acts.MoveToTop = function () { var layer = this.layer; var layer_instances = layer.instances; if (layer_instances.length && layer_instances[layer_instances.length - 1] === this) return; // is already at top layer.removeFromInstanceList(this, false); layer.appendToInstanceList(this, false); this.runtime.redraw = true; }; acts.MoveToBottom = function () { var layer = this.layer; var layer_instances = layer.instances; if (layer_instances.length && layer_instances[0] === this) return; // is already at bottom layer.removeFromInstanceList(this, false); layer.prependToInstanceList(this, false); this.runtime.redraw = true; }; acts.MoveToLayer = function (layerMove) { if (!layerMove || layerMove == this.layer) return; this.layer.removeFromInstanceList(this, true); this.layer = layerMove; layerMove.appendToInstanceList(this, true); this.runtime.redraw = true; }; acts.ZMoveToObject = function (where_, obj_) { var isafter = (where_ === 0); if (!obj_) return; var other = obj_.getFirstPicked(this); if (!other || other.uid === this.uid) return; if (this.layer.index !== other.layer.index) { this.layer.removeFromInstanceList(this, true); this.layer = other.layer; other.layer.appendToInstanceList(this, true); } this.layer.moveInstanceAdjacent(this, other, isafter); this.runtime.redraw = true; }; exps.LayerNumber = function (ret) { ret.set_int(this.layer.number); }; exps.LayerName = function (ret) { ret.set_string(this.layer.name); }; exps.ZIndex = function (ret) { ret.set_int(this.get_zindex()); }; } if (effects_aces) { acts.SetEffectEnabled = function (enable_, effectname_) { if (!this.runtime.glwrap) return; var i = this.type.getEffectIndexByName(effectname_); if (i < 0) return; // effect name not found var enable = (enable_ === 1); if (this.active_effect_flags[i] === enable) return; // no change this.active_effect_flags[i] = enable; this.updateActiveEffects(); this.runtime.redraw = true; }; acts.SetEffectParam = function (effectname_, index_, value_) { if (!this.runtime.glwrap) return; var i = this.type.getEffectIndexByName(effectname_); if (i < 0) return; // effect name not found var et = this.type.effect_types[i]; var params = this.effect_params[i]; index_ = Math.floor(index_); if (index_ < 0 || index_ >= params.length) return; // effect index out of bounds if (this.runtime.glwrap.getProgramParameterType(et.shaderindex, index_) === 1) value_ /= 100.0; if (params[index_] === value_) return; // no change params[index_] = value_; if (et.active) this.runtime.redraw = true; }; } }; cr.set_bbox_changed = function () { this.bbox_changed = true; // will recreate next time box requested this.cell_changed = true; this.type.any_cell_changed = true; // avoid unnecessary updateAllBBox() calls this.runtime.redraw = true; // assume runtime needs to redraw var i, len, callbacks = this.bbox_changed_callbacks; for (i = 0, len = callbacks.length; i < len; ++i) { callbacks[i](this); } if (this.layer.useRenderCells) this.update_bbox(); }; cr.add_bbox_changed_callback = function (f) { if (f) { this.bbox_changed_callbacks.push(f); } }; cr.update_bbox = function () { if (!this.bbox_changed) return; // bounding box not changed var bbox = this.bbox; var bquad = this.bquad; bbox.set(this.x, this.y, this.x + this.width, this.y + this.height); bbox.offset(-this.hotspotX * this.width, -this.hotspotY * this.height); if (!this.angle) { bquad.set_from_rect(bbox); // make bounding quad from box } else { bbox.offset(-this.x, -this.y); // translate to origin bquad.set_from_rotated_rect(bbox, this.angle); // rotate around origin bquad.offset(this.x, this.y); // translate back to original position bquad.bounding_box(bbox); } bbox.normalize(); this.bbox_changed = false; // bounding box up to date this.update_render_cell(); }; var tmprc = new cr.rect(0, 0, 0, 0); cr.update_render_cell = function () { if (!this.layer.useRenderCells) return; var mygrid = this.layer.render_grid; var bbox = this.bbox; tmprc.set(mygrid.XToCell(bbox.left), mygrid.YToCell(bbox.top), mygrid.XToCell(bbox.right), mygrid.YToCell(bbox.bottom)); if (this.rendercells.equals(tmprc)) return; if (this.rendercells.right < this.rendercells.left) mygrid.update(this, null, tmprc); // first insertion with invalid rect: don't provide old range else mygrid.update(this, this.rendercells, tmprc); this.rendercells.copy(tmprc); this.layer.render_list_stale = true; }; cr.update_collision_cell = function () { if (!this.cell_changed || !this.collisionsEnabled) return; this.update_bbox(); var mygrid = this.type.collision_grid; var bbox = this.bbox; tmprc.set(mygrid.XToCell(bbox.left), mygrid.YToCell(bbox.top), mygrid.XToCell(bbox.right), mygrid.YToCell(bbox.bottom)); if (this.collcells.equals(tmprc)) return; if (this.collcells.right < this.collcells.left) mygrid.update(this, null, tmprc); // first insertion with invalid rect: don't provide old range else mygrid.update(this, this.collcells, tmprc); this.collcells.copy(tmprc); this.cell_changed = false; }; cr.inst_contains_pt = function (x, y) { if (!this.bbox.contains_pt(x, y)) return false; if (!this.bquad.contains_pt(x, y)) return false; if (this.collision_poly && !this.collision_poly.is_empty()) { this.collision_poly.cache_poly(this.width, this.height, this.angle); return this.collision_poly.contains_pt(x - this.x, y - this.y); } else return true; }; cr.inst_get_iid = function () { this.type.updateIIDs(); return this.iid; }; cr.inst_get_zindex = function () { this.layer.updateZIndices(); return this.zindex; }; cr.inst_updateActiveEffects = function () { cr.clearArray(this.active_effect_types); var i, len, et; var preserves_opaqueness = true; for (i = 0, len = this.active_effect_flags.length; i < len; i++) { if (this.active_effect_flags[i]) { et = this.type.effect_types[i]; this.active_effect_types.push(et); if (!et.preservesOpaqueness) preserves_opaqueness = false; } } this.uses_shaders = !!this.active_effect_types.length; this.shaders_preserve_opaqueness = preserves_opaqueness; }; cr.inst_toString = function () { return "Inst" + this.puid; }; cr.type_getFirstPicked = function (frominst) { if (frominst && frominst.is_contained && frominst.type != this) { var i, len, s; for (i = 0, len = frominst.siblings.length; i < len; i++) { s = frominst.siblings[i]; if (s.type == this) return s; } } var instances = this.getCurrentSol().getObjects(); if (instances.length) return instances[0]; else return null; }; cr.type_getPairedInstance = function (inst) { var instances = this.getCurrentSol().getObjects(); if (instances.length) return instances[inst.get_iid() % instances.length]; else return null; }; cr.type_updateIIDs = function () { if (!this.stale_iids || this.is_family) return; // up to date or is family - don't want family to overwrite IIDs var i, len; for (i = 0, len = this.instances.length; i < len; i++) this.instances[i].iid = i; var next_iid = i; var createRow = this.runtime.createRow; for (i = 0, len = createRow.length; i < len; ++i) { if (createRow[i].type === this) createRow[i].iid = next_iid++; } this.stale_iids = false; }; cr.type_getInstanceByIID = function (i) { if (i < this.instances.length) return this.instances[i]; i -= this.instances.length; var createRow = this.runtime.createRow; var j, lenj; for (j = 0, lenj = createRow.length; j < lenj; ++j) { if (createRow[j].type === this) { if (i === 0) return createRow[j]; --i; } } ; return null; }; cr.type_getCurrentSol = function () { return this.solstack[this.cur_sol]; }; cr.type_pushCleanSol = function () { this.cur_sol++; if (this.cur_sol === this.solstack.length) { this.solstack.push(new cr.selection(this)); } else { this.solstack[this.cur_sol].select_all = true; // else clear next SOL cr.clearArray(this.solstack[this.cur_sol].else_instances); } }; cr.type_pushCopySol = function () { this.cur_sol++; if (this.cur_sol === this.solstack.length) this.solstack.push(new cr.selection(this)); var clonesol = this.solstack[this.cur_sol]; var prevsol = this.solstack[this.cur_sol - 1]; if (prevsol.select_all) { clonesol.select_all = true; cr.clearArray(clonesol.else_instances); } else { clonesol.select_all = false; cr.shallowAssignArray(clonesol.instances, prevsol.instances); cr.shallowAssignArray(clonesol.else_instances, prevsol.else_instances); } }; cr.type_popSol = function () { ; this.cur_sol--; }; cr.type_getBehaviorByName = function (behname) { var i, len, j, lenj, f, index = 0; if (!this.is_family) { for (i = 0, len = this.families.length; i < len; i++) { f = this.families[i]; for (j = 0, lenj = f.behaviors.length; j < lenj; j++) { if (behname === f.behaviors[j].name) { this.extra["lastBehIndex"] = index; return f.behaviors[j]; } index++; } } } for (i = 0, len = this.behaviors.length; i < len; i++) { if (behname === this.behaviors[i].name) { this.extra["lastBehIndex"] = index; return this.behaviors[i]; } index++; } return null; }; cr.type_getBehaviorIndexByName = function (behname) { var b = this.getBehaviorByName(behname); if (b) return this.extra["lastBehIndex"]; else return -1; }; cr.type_getEffectIndexByName = function (name_) { var i, len; for (i = 0, len = this.effect_types.length; i < len; i++) { if (this.effect_types[i].name === name_) return i; } return -1; }; cr.type_applySolToContainer = function () { if (!this.is_contained || this.is_family) return; var i, len, j, lenj, t, sol, sol2; this.updateIIDs(); sol = this.getCurrentSol(); var select_all = sol.select_all; var es = this.runtime.getCurrentEventStack(); var orblock = es && es.current_event && es.current_event.orblock; for (i = 0, len = this.container.length; i < len; i++) { t = this.container[i]; if (t === this) continue; t.updateIIDs(); sol2 = t.getCurrentSol(); sol2.select_all = select_all; if (!select_all) { cr.clearArray(sol2.instances); for (j = 0, lenj = sol.instances.length; j < lenj; ++j) sol2.instances[j] = t.getInstanceByIID(sol.instances[j].iid); if (orblock) { cr.clearArray(sol2.else_instances); for (j = 0, lenj = sol.else_instances.length; j < lenj; ++j) sol2.else_instances[j] = t.getInstanceByIID(sol.else_instances[j].iid); } } } }; cr.type_toString = function () { return "Type" + this.sid; }; cr.do_cmp = function (x, cmp, y) { if (typeof x === "undefined" || typeof y === "undefined") return false; switch (cmp) { case 0: // equal return x === y; case 1: // not equal return x !== y; case 2: // less return x < y; case 3: // less/equal return x <= y; case 4: // greater return x > y; case 5: // greater/equal return x >= y; default: ; return false; } }; })(); cr.shaders = {}; cr.shaders["blurhorizontal"] = {src: ["varying mediump vec2 vTex;", "uniform mediump sampler2D samplerFront;", "uniform mediump float pixelWidth;", "uniform mediump float intensity;", "void main(void)", "{", "mediump vec4 sum = vec4(0.0);", "mediump float halfPixelWidth = pixelWidth / 2.0;", "sum += texture2D(samplerFront, vTex - vec2(pixelWidth * 7.0 + halfPixelWidth, 0.0)) * 0.06;", "sum += texture2D(samplerFront, vTex - vec2(pixelWidth * 5.0 + halfPixelWidth, 0.0)) * 0.10;", "sum += texture2D(samplerFront, vTex - vec2(pixelWidth * 3.0 + halfPixelWidth, 0.0)) * 0.13;", "sum += texture2D(samplerFront, vTex - vec2(pixelWidth * 1.0 + halfPixelWidth, 0.0)) * 0.16;", "mediump vec4 front = texture2D(samplerFront, vTex);", "sum += front * 0.10;", "sum += texture2D(samplerFront, vTex + vec2(pixelWidth * 1.0 + halfPixelWidth, 0.0)) * 0.16;", "sum += texture2D(samplerFront, vTex + vec2(pixelWidth * 3.0 + halfPixelWidth, 0.0)) * 0.13;", "sum += texture2D(samplerFront, vTex + vec2(pixelWidth * 5.0 + halfPixelWidth, 0.0)) * 0.10;", "sum += texture2D(samplerFront, vTex + vec2(pixelWidth * 7.0 + halfPixelWidth, 0.0)) * 0.06;", "gl_FragColor = mix(front, sum, intensity);", "}" ].join("\n"), extendBoxHorizontal: 8, extendBoxVertical: 0, crossSampling: false, preservesOpaqueness: false, animated: false, parameters: [["intensity", 0, 1]] } cr.shaders["blurvertical"] = {src: ["varying mediump vec2 vTex;", "uniform mediump sampler2D samplerFront;", "uniform mediump float pixelHeight;", "uniform mediump float intensity;", "void main(void)", "{", "mediump vec4 sum = vec4(0.0);", "mediump float halfPixelHeight = pixelHeight / 2.0;", "sum += texture2D(samplerFront, vTex - vec2(0.0, pixelHeight * 7.0 + halfPixelHeight)) * 0.06;", "sum += texture2D(samplerFront, vTex - vec2(0.0, pixelHeight * 5.0 + halfPixelHeight)) * 0.10;", "sum += texture2D(samplerFront, vTex - vec2(0.0, pixelHeight * 3.0 + halfPixelHeight)) * 0.13;", "sum += texture2D(samplerFront, vTex - vec2(0.0, pixelHeight * 1.0 + halfPixelHeight)) * 0.16;", "mediump vec4 front = texture2D(samplerFront, vTex);", "sum += front * 0.10;", "sum += texture2D(samplerFront, vTex + vec2(0.0, pixelHeight * 1.0 + halfPixelHeight)) * 0.16;", "sum += texture2D(samplerFront, vTex + vec2(0.0, pixelHeight * 3.0 + halfPixelHeight)) * 0.13;", "sum += texture2D(samplerFront, vTex + vec2(0.0, pixelHeight * 5.0 + halfPixelHeight)) * 0.10;", "sum += texture2D(samplerFront, vTex + vec2(0.0, pixelHeight * 7.0 + halfPixelHeight)) * 0.06;", "gl_FragColor = mix(front, sum, intensity);", "}" ].join("\n"), extendBoxHorizontal: 0, extendBoxVertical: 8, crossSampling: false, preservesOpaqueness: false, animated: false, parameters: [["intensity", 0, 1]] } cr.shaders["brightness"] = {src: ["varying mediump vec2 vTex;", "uniform lowp sampler2D samplerFront;", "uniform lowp float brightness;", "void main(void)", "{", "lowp vec4 front = texture2D(samplerFront, vTex);", "lowp float a = front.a;", "if (a != 0.0)", "front.rgb /= front.a;", "front.rgb += (brightness - 1.0);", "front.rgb *= a;", "gl_FragColor = front;", "}" ].join("\n"), extendBoxHorizontal: 0, extendBoxVertical: 0, crossSampling: false, preservesOpaqueness: true, animated: false, parameters: [["brightness", 0, 1]] } cr.shaders["setcolor"] = {src: ["varying mediump vec2 vTex;", "uniform lowp sampler2D samplerFront;", "uniform lowp float red;", "uniform lowp float green;", "uniform lowp float blue;", "void main(void)", "{", "lowp float a = texture2D(samplerFront, vTex).a;", "gl_FragColor = vec4(red * a, green * a, blue * a, a);", "}" ].join("\n"), extendBoxHorizontal: 0, extendBoxVertical: 0, crossSampling: false, preservesOpaqueness: true, animated: false, parameters: [["red", 0, 1], ["green", 0, 1], ["blue", 0, 1]] } cr.shaders["tint"] = {src: ["varying mediump vec2 vTex;", "uniform lowp sampler2D samplerFront;", "uniform lowp float red;", "uniform lowp float green;", "uniform lowp float blue;", "void main(void)", "{", "lowp vec4 front = texture2D(samplerFront, vTex);", "gl_FragColor = front * vec4(red, green, blue, 1.0);", "}" ].join("\n"), extendBoxHorizontal: 0, extendBoxVertical: 0, crossSampling: false, preservesOpaqueness: true, animated: false, parameters: [["red", 0, 1], ["green", 0, 1], ["blue", 0, 1]] } cr.shaders["waterbg"] = {src: ["varying mediump vec2 vTex;", "uniform lowp sampler2D samplerFront;", "uniform lowp sampler2D samplerBack;", "uniform mediump vec2 destStart;", "uniform mediump vec2 destEnd;", "precision mediump float;", "uniform float seconds;", "uniform float pixelWidth;", "uniform float pixelHeight;", "const float PI = 3.1415926535897932;", "uniform float speed;", "uniform float speed_x;", "uniform float speed_y;", "uniform float intensity;", "const int steps = 8;", "uniform float frequency;", "uniform float angle; // better when a prime", "uniform float delta;", "uniform float intence;", "uniform float emboss;", "float col(vec2 coord)", "{", "float delta_theta = 2.0 * PI / angle;", "float col = 0.0;", "float theta = 0.0;", "for (int i = 0; i < steps; i++)", "{", "vec2 adjc = coord;", "theta = delta_theta*float(i);", "adjc.x += cos(theta)*seconds*speed + seconds * speed_x;", "adjc.y -= sin(theta)*seconds*speed - seconds * speed_y;", "col = col + cos( (adjc.x*cos(theta) - adjc.y*sin(theta))*frequency)*intensity;", "}", "return cos(col);", "}", "void main(void)", "{", "vec2 p = vTex, c1 = p, c2 = p;", "float cc1 = col(c1);", "c2.x += (1.0 / pixelWidth) / delta;", "float dx = emboss*(cc1-col(c2))/delta;", "c2.x = p.x;", "c2.y += (1.0 / pixelHeight) / delta;", "float dy = emboss*(cc1-col(c2))/delta;", "c1.x += dx;", "c1.y = -(c1.y+dy);", "float alpha = 1.+dot(dx,dy)*intence;", "c1.y = -c1.y;", "lowp vec4 front = texture2D(samplerFront,c1) * alpha;", "lowp vec4 result;", "if (front.a == 0.0)", "result = front + texture2D(samplerBack, mix(destStart, destEnd, vTex)) * (1.0 - front.a);", "else", "result = front + texture2D(samplerBack, mix(destStart, destEnd, c1)) * (1.0 - front.a);", "gl_FragColor = result;", "}" ].join("\n"), extendBoxHorizontal: 25, extendBoxVertical: 25, crossSampling: true, preservesOpaqueness: false, animated: true, parameters: [["speed", 0, 1], ["speed_x", 0, 1], ["speed_y", 0, 1], ["intensity", 0, 0], ["frequency", 0, 0], ["angle", 0, 0], ["delta", 0, 0], ["intence", 0, 0], ["emboss", 0, 1]] } ; ; cr.plugins_.Arr = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Arr.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; var arrCache = []; function allocArray() { if (arrCache.length) return arrCache.pop(); else return []; }; if (!Array.isArray) { Array.isArray = function (vArg) { return Object.prototype.toString.call(vArg) === "[object Array]"; }; } function freeArray(a) { var i, len; for (i = 0, len = a.length; i < len; i++) { if (Array.isArray(a[i])) freeArray(a[i]); } cr.clearArray(a); arrCache.push(a); }; instanceProto.onCreate = function() { this.cx = this.properties[0]; this.cy = this.properties[1]; this.cz = this.properties[2]; if (!this.recycled) this.arr = allocArray(); var a = this.arr; a.length = this.cx; var x, y, z; for (x = 0; x < this.cx; x++) { if (!a[x]) a[x] = allocArray(); a[x].length = this.cy; for (y = 0; y < this.cy; y++) { if (!a[x][y]) a[x][y] = allocArray(); a[x][y].length = this.cz; for (z = 0; z < this.cz; z++) a[x][y][z] = 0; } } this.forX = []; this.forY = []; this.forZ = []; this.forDepth = -1; }; instanceProto.onDestroy = function () { var x; for (x = 0; x < this.cx; x++) freeArray(this.arr[x]); // will recurse down and recycle other arrays cr.clearArray(this.arr); }; instanceProto.at = function (x, y, z) { x = Math.floor(x); y = Math.floor(y); z = Math.floor(z); if (isNaN(x) || x < 0 || x > this.cx - 1) return 0; if (isNaN(y) || y < 0 || y > this.cy - 1) return 0; if (isNaN(z) || z < 0 || z > this.cz - 1) return 0; return this.arr[x][y][z]; }; instanceProto.set = function (x, y, z, val) { x = Math.floor(x); y = Math.floor(y); z = Math.floor(z); if (isNaN(x) || x < 0 || x > this.cx - 1) return; if (isNaN(y) || y < 0 || y > this.cy - 1) return; if (isNaN(z) || z < 0 || z > this.cz - 1) return; this.arr[x][y][z] = val; }; instanceProto.getAsJSON = function () { return JSON.stringify({ "c2array": true, "size": [this.cx, this.cy, this.cz], "data": this.arr }); }; instanceProto.saveToJSON = function () { return { "size": [this.cx, this.cy, this.cz], "data": this.arr }; }; instanceProto.loadFromJSON = function (o) { var sz = o["size"]; this.cx = sz[0]; this.cy = sz[1]; this.cz = sz[2]; this.arr = o["data"]; }; instanceProto.setSize = function (w, h, d) { if (w < 0) w = 0; if (h < 0) h = 0; if (d < 0) d = 0; if (this.cx === w && this.cy === h && this.cz === d) return; // no change this.cx = w; this.cy = h; this.cz = d; var x, y, z; var a = this.arr; a.length = w; for (x = 0; x < this.cx; x++) { if (cr.is_undefined(a[x])) a[x] = allocArray(); a[x].length = h; for (y = 0; y < this.cy; y++) { if (cr.is_undefined(a[x][y])) a[x][y] = allocArray(); a[x][y].length = d; for (z = 0; z < this.cz; z++) { if (cr.is_undefined(a[x][y][z])) a[x][y][z] = 0; } } } }; instanceProto.getForX = function () { if (this.forDepth >= 0 && this.forDepth < this.forX.length) return this.forX[this.forDepth]; else return 0; }; instanceProto.getForY = function () { if (this.forDepth >= 0 && this.forDepth < this.forY.length) return this.forY[this.forDepth]; else return 0; }; instanceProto.getForZ = function () { if (this.forDepth >= 0 && this.forDepth < this.forZ.length) return this.forZ[this.forDepth]; else return 0; }; function Cnds() {}; Cnds.prototype.CompareX = function (x, cmp, val) { return cr.do_cmp(this.at(x, 0, 0), cmp, val); }; Cnds.prototype.CompareXY = function (x, y, cmp, val) { return cr.do_cmp(this.at(x, y, 0), cmp, val); }; Cnds.prototype.CompareXYZ = function (x, y, z, cmp, val) { return cr.do_cmp(this.at(x, y, z), cmp, val); }; instanceProto.doForEachTrigger = function (current_event) { this.runtime.pushCopySol(current_event.solModifiers); current_event.retrigger(); this.runtime.popSol(current_event.solModifiers); }; Cnds.prototype.ArrForEach = function (dims) { var current_event = this.runtime.getCurrentEventStack().current_event; this.forDepth++; var forDepth = this.forDepth; if (forDepth === this.forX.length) { this.forX.push(0); this.forY.push(0); this.forZ.push(0); } else { this.forX[forDepth] = 0; this.forY[forDepth] = 0; this.forZ[forDepth] = 0; } switch (dims) { case 0: for (this.forX[forDepth] = 0; this.forX[forDepth] < this.cx; this.forX[forDepth]++) { for (this.forY[forDepth] = 0; this.forY[forDepth] < this.cy; this.forY[forDepth]++) { for (this.forZ[forDepth] = 0; this.forZ[forDepth] < this.cz; this.forZ[forDepth]++) { this.doForEachTrigger(current_event); } } } break; case 1: for (this.forX[forDepth] = 0; this.forX[forDepth] < this.cx; this.forX[forDepth]++) { for (this.forY[forDepth] = 0; this.forY[forDepth] < this.cy; this.forY[forDepth]++) { this.doForEachTrigger(current_event); } } break; case 2: for (this.forX[forDepth] = 0; this.forX[forDepth] < this.cx; this.forX[forDepth]++) { this.doForEachTrigger(current_event); } break; } this.forDepth--; return false; }; Cnds.prototype.CompareCurrent = function (cmp, val) { return cr.do_cmp(this.at(this.getForX(), this.getForY(), this.getForZ()), cmp, val); }; Cnds.prototype.Contains = function(val) { var x, y, z; for (x = 0; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { for (z = 0; z < this.cz; z++) { if (this.arr[x][y][z] === val) return true; } } } return false; }; Cnds.prototype.IsEmpty = function () { return this.cx === 0 || this.cy === 0 || this.cz === 0; }; Cnds.prototype.CompareSize = function (axis, cmp, value) { var s = 0; switch (axis) { case 0: s = this.cx; break; case 1: s = this.cy; break; case 2: s = this.cz; break; } return cr.do_cmp(s, cmp, value); }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Clear = function () { var x, y, z; for (x = 0; x < this.cx; x++) for (y = 0; y < this.cy; y++) for (z = 0; z < this.cz; z++) this.arr[x][y][z] = 0; }; Acts.prototype.SetSize = function (w, h, d) { this.setSize(w, h, d); }; Acts.prototype.SetX = function (x, val) { this.set(x, 0, 0, val); }; Acts.prototype.SetXY = function (x, y, val) { this.set(x, y, 0, val); }; Acts.prototype.SetXYZ = function (x, y, z, val) { this.set(x, y, z, val); }; Acts.prototype.Push = function (where, value, axis) { var x = 0, y = 0, z = 0; var a = this.arr; switch (axis) { case 0: // X axis if (where === 0) // back { x = a.length; a.push(allocArray()); } else // front { x = 0; a.unshift(allocArray()); } a[x].length = this.cy; for ( ; y < this.cy; y++) { a[x][y] = allocArray(); a[x][y].length = this.cz; for (z = 0; z < this.cz; z++) a[x][y][z] = value; } this.cx++; break; case 1: // Y axis for ( ; x < this.cx; x++) { if (where === 0) // back { y = a[x].length; a[x].push(allocArray()); } else // front { y = 0; a[x].unshift(allocArray()); } a[x][y].length = this.cz; for (z = 0; z < this.cz; z++) a[x][y][z] = value; } this.cy++; break; case 2: // Z axis for ( ; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { if (where === 0) // back { a[x][y].push(value); } else // front { a[x][y].unshift(value); } } } this.cz++; break; } }; Acts.prototype.Pop = function (where, axis) { var x = 0, y = 0, z = 0; var a = this.arr; switch (axis) { case 0: // X axis if (this.cx === 0) break; if (where === 0) // back { freeArray(a.pop()); } else // front { freeArray(a.shift()); } this.cx--; break; case 1: // Y axis if (this.cy === 0) break; for ( ; x < this.cx; x++) { if (where === 0) // back { freeArray(a[x].pop()); } else // front { freeArray(a[x].shift()); } } this.cy--; break; case 2: // Z axis if (this.cz === 0) break; for ( ; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { if (where === 0) // back { a[x][y].pop(); } else // front { a[x][y].shift(); } } } this.cz--; break; } }; Acts.prototype.Reverse = function (axis) { var x = 0, y = 0, z = 0; var a = this.arr; if (this.cx === 0 || this.cy === 0 || this.cz === 0) return; // no point reversing empty array switch (axis) { case 0: // X axis a.reverse(); break; case 1: // Y axis for ( ; x < this.cx; x++) a[x].reverse(); break; case 2: // Z axis for ( ; x < this.cx; x++) for (y = 0; y < this.cy; y++) a[x][y].reverse(); this.cz--; break; } }; function compareValues(va, vb) { if (cr.is_number(va) && cr.is_number(vb)) return va - vb; else { var sa = "" + va; var sb = "" + vb; if (sa < sb) return -1; else if (sa > sb) return 1; else return 0; } } Acts.prototype.Sort = function (axis) { var x = 0, y = 0, z = 0; var a = this.arr; if (this.cx === 0 || this.cy === 0 || this.cz === 0) return; // no point sorting empty array switch (axis) { case 0: // X axis a.sort(function (a, b) { return compareValues(a[0][0], b[0][0]); }); break; case 1: // Y axis for ( ; x < this.cx; x++) { a[x].sort(function (a, b) { return compareValues(a[0], b[0]); }); } break; case 2: // Z axis for ( ; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { a[x][y].sort(compareValues); } } break; } }; Acts.prototype.Delete = function (index, axis) { var x = 0, y = 0, z = 0; index = Math.floor(index); var a = this.arr; if (index < 0) return; switch (axis) { case 0: // X axis if (index >= this.cx) break; freeArray(a[index]); a.splice(index, 1); this.cx--; break; case 1: // Y axis if (index >= this.cy) break; for ( ; x < this.cx; x++) { freeArray(a[x][index]); a[x].splice(index, 1); } this.cy--; break; case 2: // Z axis if (index >= this.cz) break; for ( ; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { a[x][y].splice(index, 1); } } this.cz--; break; } }; Acts.prototype.Insert = function (value, index, axis) { var x = 0, y = 0, z = 0; index = Math.floor(index); var a = this.arr; if (index < 0) return; switch (axis) { case 0: // X axis if (index > this.cx) return; x = index; a.splice(x, 0, allocArray()); a[x].length = this.cy; for ( ; y < this.cy; y++) { a[x][y] = allocArray(); a[x][y].length = this.cz; for (z = 0; z < this.cz; z++) a[x][y][z] = value; } this.cx++; break; case 1: // Y axis if (index > this.cy) return; for ( ; x < this.cx; x++) { y = index; a[x].splice(y, 0, allocArray()); a[x][y].length = this.cz; for (z = 0; z < this.cz; z++) a[x][y][z] = value; } this.cy++; break; case 2: // Z axis if (index > this.cz) return; for ( ; x < this.cx; x++) { for (y = 0; y < this.cy; y++) { a[x][y].splice(index, 0, value); } } this.cz++; break; } }; Acts.prototype.JSONLoad = function (json_) { var o; try { o = JSON.parse(json_); } catch(e) { return; } if (!o["c2array"]) // presumably not a c2array object return; var sz = o["size"]; this.cx = sz[0]; this.cy = sz[1]; this.cz = sz[2]; this.arr = o["data"]; }; Acts.prototype.JSONDownload = function (filename) { }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.At = function (ret, x, y_, z_) { var y = y_ || 0; var z = z_ || 0; ret.set_any(this.at(x, y, z)); }; Exps.prototype.Width = function (ret) { ret.set_int(this.cx); }; Exps.prototype.Height = function (ret) { ret.set_int(this.cy); }; Exps.prototype.Depth = function (ret) { ret.set_int(this.cz); }; Exps.prototype.CurX = function (ret) { ret.set_int(this.getForX()); }; Exps.prototype.CurY = function (ret) { ret.set_int(this.getForY()); }; Exps.prototype.CurZ = function (ret) { ret.set_int(this.getForZ()); }; Exps.prototype.CurValue = function (ret) { ret.set_any(this.at(this.getForX(), this.getForY(), this.getForZ())); }; Exps.prototype.Front = function (ret) { ret.set_any(this.at(0, 0, 0)); }; Exps.prototype.Back = function (ret) { ret.set_any(this.at(this.cx - 1, 0, 0)); }; Exps.prototype.IndexOf = function (ret, v) { for (var i = 0; i < this.cx; i++) { if (this.arr[i][0][0] === v) { ret.set_int(i); return; } } ret.set_int(-1); }; Exps.prototype.LastIndexOf = function (ret, v) { for (var i = this.cx - 1; i >= 0; i--) { if (this.arr[i][0][0] === v) { ret.set_int(i); return; } } ret.set_int(-1); }; Exps.prototype.AsJSON = function (ret) { ret.set_string(this.getAsJSON()); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Audio = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Audio.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; var audRuntime = null; var audInst = null; var audTag = ""; var appPath = ""; // for Cordova only var API_HTML5 = 0; var API_WEBAUDIO = 1; var API_CORDOVA = 2; var API_APPMOBI = 3; var api = API_HTML5; var context = null; var audioBuffers = []; // cache of buffers var audioInstances = []; // cache of instances var lastAudio = null; var useOgg = false; // determined at create time var timescale_mode = 0; var silent = false; var masterVolume = 1; var listenerX = 0; var listenerY = 0; var isContextSuspended = false; var panningModel = 1; // HRTF var distanceModel = 1; // Inverse var refDistance = 10; var maxDistance = 10000; var rolloffFactor = 1; var micSource = null; var micTag = ""; var isMusicWorkaround = false; var musicPlayNextTouch = []; var playMusicAsSoundWorkaround = false; // play music tracks with Web Audio API function dbToLinear(x) { var v = dbToLinear_nocap(x); if (!isFinite(v)) // accidentally passing a string can result in NaN; set volume to 0 if so v = 0; if (v < 0) v = 0; if (v > 1) v = 1; return v; }; function linearToDb(x) { if (x < 0) x = 0; if (x > 1) x = 1; return linearToDb_nocap(x); }; function dbToLinear_nocap(x) { return Math.pow(10, x / 20); }; function linearToDb_nocap(x) { return (Math.log(x) / Math.log(10)) * 20; }; var effects = {}; function getDestinationForTag(tag) { tag = tag.toLowerCase(); if (effects.hasOwnProperty(tag)) { if (effects[tag].length) return effects[tag][0].getInputNode(); } return context["destination"]; }; function createGain() { if (context["createGain"]) return context["createGain"](); else return context["createGainNode"](); }; function createDelay(d) { if (context["createDelay"]) return context["createDelay"](d); else return context["createDelayNode"](d); }; function startSource(s, scheduledTime) { if (s["start"]) s["start"](scheduledTime || 0); else s["noteOn"](scheduledTime || 0); }; function startSourceAt(s, x, d, scheduledTime) { if (s["start"]) s["start"](scheduledTime || 0, x); else s["noteGrainOn"](scheduledTime || 0, x, d - x); }; function stopSource(s) { try { if (s["stop"]) s["stop"](0); else s["noteOff"](0); } catch (e) {} }; function setAudioParam(ap, value, ramp, time) { if (!ap) return; // iOS is missing some parameters ap["cancelScheduledValues"](0); if (time === 0) { ap["value"] = value; return; } var curTime = context["currentTime"]; time += curTime; switch (ramp) { case 0: // step ap["setValueAtTime"](value, time); break; case 1: // linear ap["setValueAtTime"](ap["value"], curTime); // to set what to ramp from ap["linearRampToValueAtTime"](value, time); break; case 2: // exponential ap["setValueAtTime"](ap["value"], curTime); // to set what to ramp from ap["exponentialRampToValueAtTime"](value, time); break; } }; var filterTypes = ["lowpass", "highpass", "bandpass", "lowshelf", "highshelf", "peaking", "notch", "allpass"]; function FilterEffect(type, freq, detune, q, gain, mix) { this.type = "filter"; this.params = [type, freq, detune, q, gain, mix]; this.inputNode = createGain(); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix; this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - mix; this.filterNode = context["createBiquadFilter"](); if (typeof this.filterNode["type"] === "number") this.filterNode["type"] = type; else this.filterNode["type"] = filterTypes[type]; this.filterNode["frequency"]["value"] = freq; if (this.filterNode["detune"]) // iOS 6 doesn't have detune yet this.filterNode["detune"]["value"] = detune; this.filterNode["Q"]["value"] = q; this.filterNode["gain"]["value"] = gain; this.inputNode["connect"](this.filterNode); this.inputNode["connect"](this.dryNode); this.filterNode["connect"](this.wetNode); }; FilterEffect.prototype.connectTo = function (node) { this.wetNode["disconnect"](); this.wetNode["connect"](node); this.dryNode["disconnect"](); this.dryNode["connect"](node); }; FilterEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.filterNode["disconnect"](); this.wetNode["disconnect"](); this.dryNode["disconnect"](); }; FilterEffect.prototype.getInputNode = function () { return this.inputNode; }; FilterEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[5] = value; setAudioParam(this.wetNode["gain"], value, ramp, time); setAudioParam(this.dryNode["gain"], 1 - value, ramp, time); break; case 1: // filter frequency this.params[1] = value; setAudioParam(this.filterNode["frequency"], value, ramp, time); break; case 2: // filter detune this.params[2] = value; setAudioParam(this.filterNode["detune"], value, ramp, time); break; case 3: // filter Q this.params[3] = value; setAudioParam(this.filterNode["Q"], value, ramp, time); break; case 4: // filter/delay gain (note value is in dB here) this.params[4] = value; setAudioParam(this.filterNode["gain"], value, ramp, time); break; } }; function DelayEffect(delayTime, delayGain, mix) { this.type = "delay"; this.params = [delayTime, delayGain, mix]; this.inputNode = createGain(); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix; this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - mix; this.mainNode = createGain(); this.delayNode = createDelay(delayTime); this.delayNode["delayTime"]["value"] = delayTime; this.delayGainNode = createGain(); this.delayGainNode["gain"]["value"] = delayGain; this.inputNode["connect"](this.mainNode); this.inputNode["connect"](this.dryNode); this.mainNode["connect"](this.wetNode); this.mainNode["connect"](this.delayNode); this.delayNode["connect"](this.delayGainNode); this.delayGainNode["connect"](this.mainNode); }; DelayEffect.prototype.connectTo = function (node) { this.wetNode["disconnect"](); this.wetNode["connect"](node); this.dryNode["disconnect"](); this.dryNode["connect"](node); }; DelayEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.mainNode["disconnect"](); this.delayNode["disconnect"](); this.delayGainNode["disconnect"](); this.wetNode["disconnect"](); this.dryNode["disconnect"](); }; DelayEffect.prototype.getInputNode = function () { return this.inputNode; }; DelayEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[2] = value; setAudioParam(this.wetNode["gain"], value, ramp, time); setAudioParam(this.dryNode["gain"], 1 - value, ramp, time); break; case 4: // filter/delay gain (note value is passed in dB but needs to be linear here) this.params[1] = dbToLinear(value); setAudioParam(this.delayGainNode["gain"], dbToLinear(value), ramp, time); break; case 5: // delay time this.params[0] = value; setAudioParam(this.delayNode["delayTime"], value, ramp, time); break; } }; function ConvolveEffect(buffer, normalize, mix, src) { this.type = "convolve"; this.params = [normalize, mix, src]; this.inputNode = createGain(); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix; this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - mix; this.convolveNode = context["createConvolver"](); if (buffer) { this.convolveNode["normalize"] = normalize; this.convolveNode["buffer"] = buffer; } this.inputNode["connect"](this.convolveNode); this.inputNode["connect"](this.dryNode); this.convolveNode["connect"](this.wetNode); }; ConvolveEffect.prototype.connectTo = function (node) { this.wetNode["disconnect"](); this.wetNode["connect"](node); this.dryNode["disconnect"](); this.dryNode["connect"](node); }; ConvolveEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.convolveNode["disconnect"](); this.wetNode["disconnect"](); this.dryNode["disconnect"](); }; ConvolveEffect.prototype.getInputNode = function () { return this.inputNode; }; ConvolveEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[1] = value; setAudioParam(this.wetNode["gain"], value, ramp, time); setAudioParam(this.dryNode["gain"], 1 - value, ramp, time); break; } }; function FlangerEffect(delay, modulation, freq, feedback, mix) { this.type = "flanger"; this.params = [delay, modulation, freq, feedback, mix]; this.inputNode = createGain(); this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - (mix / 2); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix / 2; this.feedbackNode = createGain(); this.feedbackNode["gain"]["value"] = feedback; this.delayNode = createDelay(delay + modulation); this.delayNode["delayTime"]["value"] = delay; this.oscNode = context["createOscillator"](); this.oscNode["frequency"]["value"] = freq; this.oscGainNode = createGain(); this.oscGainNode["gain"]["value"] = modulation; this.inputNode["connect"](this.delayNode); this.inputNode["connect"](this.dryNode); this.delayNode["connect"](this.wetNode); this.delayNode["connect"](this.feedbackNode); this.feedbackNode["connect"](this.delayNode); this.oscNode["connect"](this.oscGainNode); this.oscGainNode["connect"](this.delayNode["delayTime"]); startSource(this.oscNode); }; FlangerEffect.prototype.connectTo = function (node) { this.dryNode["disconnect"](); this.dryNode["connect"](node); this.wetNode["disconnect"](); this.wetNode["connect"](node); }; FlangerEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.delayNode["disconnect"](); this.oscNode["disconnect"](); this.oscGainNode["disconnect"](); this.dryNode["disconnect"](); this.wetNode["disconnect"](); this.feedbackNode["disconnect"](); }; FlangerEffect.prototype.getInputNode = function () { return this.inputNode; }; FlangerEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[4] = value; setAudioParam(this.wetNode["gain"], value / 2, ramp, time); setAudioParam(this.dryNode["gain"], 1 - (value / 2), ramp, time); break; case 6: // modulation this.params[1] = value / 1000; setAudioParam(this.oscGainNode["gain"], value / 1000, ramp, time); break; case 7: // modulation frequency this.params[2] = value; setAudioParam(this.oscNode["frequency"], value, ramp, time); break; case 8: // feedback this.params[3] = value / 100; setAudioParam(this.feedbackNode["gain"], value / 100, ramp, time); break; } }; function PhaserEffect(freq, detune, q, modulation, modfreq, mix) { this.type = "phaser"; this.params = [freq, detune, q, modulation, modfreq, mix]; this.inputNode = createGain(); this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - (mix / 2); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix / 2; this.filterNode = context["createBiquadFilter"](); if (typeof this.filterNode["type"] === "number") this.filterNode["type"] = 7; // all-pass else this.filterNode["type"] = "allpass"; this.filterNode["frequency"]["value"] = freq; if (this.filterNode["detune"]) // iOS 6 doesn't have detune yet this.filterNode["detune"]["value"] = detune; this.filterNode["Q"]["value"] = q; this.oscNode = context["createOscillator"](); this.oscNode["frequency"]["value"] = modfreq; this.oscGainNode = createGain(); this.oscGainNode["gain"]["value"] = modulation; this.inputNode["connect"](this.filterNode); this.inputNode["connect"](this.dryNode); this.filterNode["connect"](this.wetNode); this.oscNode["connect"](this.oscGainNode); this.oscGainNode["connect"](this.filterNode["frequency"]); startSource(this.oscNode); }; PhaserEffect.prototype.connectTo = function (node) { this.dryNode["disconnect"](); this.dryNode["connect"](node); this.wetNode["disconnect"](); this.wetNode["connect"](node); }; PhaserEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.filterNode["disconnect"](); this.oscNode["disconnect"](); this.oscGainNode["disconnect"](); this.dryNode["disconnect"](); this.wetNode["disconnect"](); }; PhaserEffect.prototype.getInputNode = function () { return this.inputNode; }; PhaserEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[5] = value; setAudioParam(this.wetNode["gain"], value / 2, ramp, time); setAudioParam(this.dryNode["gain"], 1 - (value / 2), ramp, time); break; case 1: // filter frequency this.params[0] = value; setAudioParam(this.filterNode["frequency"], value, ramp, time); break; case 2: // filter detune this.params[1] = value; setAudioParam(this.filterNode["detune"], value, ramp, time); break; case 3: // filter Q this.params[2] = value; setAudioParam(this.filterNode["Q"], value, ramp, time); break; case 6: // modulation this.params[3] = value; setAudioParam(this.oscGainNode["gain"], value, ramp, time); break; case 7: // modulation frequency this.params[4] = value; setAudioParam(this.oscNode["frequency"], value, ramp, time); break; } }; function GainEffect(g) { this.type = "gain"; this.params = [g]; this.node = createGain(); this.node["gain"]["value"] = g; }; GainEffect.prototype.connectTo = function (node_) { this.node["disconnect"](); this.node["connect"](node_); }; GainEffect.prototype.remove = function () { this.node["disconnect"](); }; GainEffect.prototype.getInputNode = function () { return this.node; }; GainEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 4: // gain this.params[0] = dbToLinear(value); setAudioParam(this.node["gain"], dbToLinear(value), ramp, time); break; } }; function TremoloEffect(freq, mix) { this.type = "tremolo"; this.params = [freq, mix]; this.node = createGain(); this.node["gain"]["value"] = 1 - (mix / 2); this.oscNode = context["createOscillator"](); this.oscNode["frequency"]["value"] = freq; this.oscGainNode = createGain(); this.oscGainNode["gain"]["value"] = mix / 2; this.oscNode["connect"](this.oscGainNode); this.oscGainNode["connect"](this.node["gain"]); startSource(this.oscNode); }; TremoloEffect.prototype.connectTo = function (node_) { this.node["disconnect"](); this.node["connect"](node_); }; TremoloEffect.prototype.remove = function () { this.oscNode["disconnect"](); this.oscGainNode["disconnect"](); this.node["disconnect"](); }; TremoloEffect.prototype.getInputNode = function () { return this.node; }; TremoloEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[1] = value; setAudioParam(this.node["gain"]["value"], 1 - (value / 2), ramp, time); setAudioParam(this.oscGainNode["gain"]["value"], value / 2, ramp, time); break; case 7: // modulation frequency this.params[0] = value; setAudioParam(this.oscNode["frequency"], value, ramp, time); break; } }; function RingModulatorEffect(freq, mix) { this.type = "ringmod"; this.params = [freq, mix]; this.inputNode = createGain(); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix; this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - mix; this.ringNode = createGain(); this.ringNode["gain"]["value"] = 0; this.oscNode = context["createOscillator"](); this.oscNode["frequency"]["value"] = freq; this.oscNode["connect"](this.ringNode["gain"]); startSource(this.oscNode); this.inputNode["connect"](this.ringNode); this.inputNode["connect"](this.dryNode); this.ringNode["connect"](this.wetNode); }; RingModulatorEffect.prototype.connectTo = function (node_) { this.wetNode["disconnect"](); this.wetNode["connect"](node_); this.dryNode["disconnect"](); this.dryNode["connect"](node_); }; RingModulatorEffect.prototype.remove = function () { this.oscNode["disconnect"](); this.ringNode["disconnect"](); this.inputNode["disconnect"](); this.wetNode["disconnect"](); this.dryNode["disconnect"](); }; RingModulatorEffect.prototype.getInputNode = function () { return this.inputNode; }; RingModulatorEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[1] = value; setAudioParam(this.wetNode["gain"], value, ramp, time); setAudioParam(this.dryNode["gain"], 1 - value, ramp, time); break; case 7: // modulation frequency this.params[0] = value; setAudioParam(this.oscNode["frequency"], value, ramp, time); break; } }; function DistortionEffect(threshold, headroom, drive, makeupgain, mix) { this.type = "distortion"; this.params = [threshold, headroom, drive, makeupgain, mix]; this.inputNode = createGain(); this.preGain = createGain(); this.postGain = createGain(); this.setDrive(drive, dbToLinear_nocap(makeupgain)); this.wetNode = createGain(); this.wetNode["gain"]["value"] = mix; this.dryNode = createGain(); this.dryNode["gain"]["value"] = 1 - mix; this.waveShaper = context["createWaveShaper"](); this.curve = new Float32Array(65536); this.generateColortouchCurve(threshold, headroom); this.waveShaper.curve = this.curve; this.inputNode["connect"](this.preGain); this.inputNode["connect"](this.dryNode); this.preGain["connect"](this.waveShaper); this.waveShaper["connect"](this.postGain); this.postGain["connect"](this.wetNode); }; DistortionEffect.prototype.setDrive = function (drive, makeupgain) { if (drive < 0.01) drive = 0.01; this.preGain["gain"]["value"] = drive; this.postGain["gain"]["value"] = Math.pow(1 / drive, 0.6) * makeupgain; }; function e4(x, k) { return 1.0 - Math.exp(-k * x); } DistortionEffect.prototype.shape = function (x, linearThreshold, linearHeadroom) { var maximum = 1.05 * linearHeadroom * linearThreshold; var kk = (maximum - linearThreshold); var sign = x < 0 ? -1 : +1; var absx = x < 0 ? -x : x; var shapedInput = absx < linearThreshold ? absx : linearThreshold + kk * e4(absx - linearThreshold, 1.0 / kk); shapedInput *= sign; return shapedInput; }; DistortionEffect.prototype.generateColortouchCurve = function (threshold, headroom) { var linearThreshold = dbToLinear_nocap(threshold); var linearHeadroom = dbToLinear_nocap(headroom); var n = 65536; var n2 = n / 2; var x = 0; for (var i = 0; i < n2; ++i) { x = i / n2; x = this.shape(x, linearThreshold, linearHeadroom); this.curve[n2 + i] = x; this.curve[n2 - i - 1] = -x; } }; DistortionEffect.prototype.connectTo = function (node) { this.wetNode["disconnect"](); this.wetNode["connect"](node); this.dryNode["disconnect"](); this.dryNode["connect"](node); }; DistortionEffect.prototype.remove = function () { this.inputNode["disconnect"](); this.preGain["disconnect"](); this.waveShaper["disconnect"](); this.postGain["disconnect"](); this.wetNode["disconnect"](); this.dryNode["disconnect"](); }; DistortionEffect.prototype.getInputNode = function () { return this.inputNode; }; DistortionEffect.prototype.setParam = function(param, value, ramp, time) { switch (param) { case 0: // mix value = value / 100; if (value < 0) value = 0; if (value > 1) value = 1; this.params[4] = value; setAudioParam(this.wetNode["gain"], value, ramp, time); setAudioParam(this.dryNode["gain"], 1 - value, ramp, time); break; } }; function CompressorEffect(threshold, knee, ratio, attack, release) { this.type = "compressor"; this.params = [threshold, knee, ratio, attack, release]; this.node = context["createDynamicsCompressor"](); try { this.node["threshold"]["value"] = threshold; this.node["knee"]["value"] = knee; this.node["ratio"]["value"] = ratio; this.node["attack"]["value"] = attack; this.node["release"]["value"] = release; } catch (e) {} }; CompressorEffect.prototype.connectTo = function (node_) { this.node["disconnect"](); this.node["connect"](node_); }; CompressorEffect.prototype.remove = function () { this.node["disconnect"](); }; CompressorEffect.prototype.getInputNode = function () { return this.node; }; CompressorEffect.prototype.setParam = function(param, value, ramp, time) { }; function AnalyserEffect(fftSize, smoothing) { this.type = "analyser"; this.params = [fftSize, smoothing]; this.node = context["createAnalyser"](); this.node["fftSize"] = fftSize; this.node["smoothingTimeConstant"] = smoothing; this.freqBins = new Float32Array(this.node["frequencyBinCount"]); this.signal = new Uint8Array(fftSize); this.peak = 0; this.rms = 0; }; AnalyserEffect.prototype.tick = function () { this.node["getFloatFrequencyData"](this.freqBins); this.node["getByteTimeDomainData"](this.signal); var fftSize = this.node["fftSize"]; var i = 0; this.peak = 0; var rmsSquaredSum = 0; var s = 0; for ( ; i < fftSize; i++) { s = (this.signal[i] - 128) / 128; if (s < 0) s = -s; if (this.peak < s) this.peak = s; rmsSquaredSum += s * s; } this.peak = linearToDb(this.peak); this.rms = linearToDb(Math.sqrt(rmsSquaredSum / fftSize)); }; AnalyserEffect.prototype.connectTo = function (node_) { this.node["disconnect"](); this.node["connect"](node_); }; AnalyserEffect.prototype.remove = function () { this.node["disconnect"](); }; AnalyserEffect.prototype.getInputNode = function () { return this.node; }; AnalyserEffect.prototype.setParam = function(param, value, ramp, time) { }; function ObjectTracker() { this.obj = null; this.loadUid = 0; }; ObjectTracker.prototype.setObject = function (obj_) { this.obj = obj_; }; ObjectTracker.prototype.hasObject = function () { return !!this.obj; }; ObjectTracker.prototype.tick = function (dt) { }; var iOShadtouchstart = false; // has had touch start input on iOS <=8 to work around web audio API muting var iOShadtouchend = false; // has had touch end input on iOS 9+ to work around web audio API muting function C2AudioBuffer(src_, is_music) { this.src = src_; this.myapi = api; this.is_music = is_music; this.added_end_listener = false; var self = this; this.outNode = null; this.mediaSourceNode = null; this.panWhenReady = []; // for web audio API positioned sounds this.seekWhenReady = 0; this.pauseWhenReady = false; this.supportWebAudioAPI = false; this.failedToLoad = false; this.wasEverReady = false; // if a buffer is ever marked as ready, it's permanently considered ready after then. if (api === API_WEBAUDIO && is_music && !playMusicAsSoundWorkaround) { this.myapi = API_HTML5; this.outNode = createGain(); } this.bufferObject = null; // actual audio object this.audioData = null; // web audio api: ajax request result (compressed audio that needs decoding) var request; switch (this.myapi) { case API_HTML5: this.bufferObject = new Audio(); this.bufferObject.crossOrigin = "anonymous"; this.bufferObject.addEventListener("canplaythrough", function () { self.wasEverReady = true; // update loaded state so preload is considered complete }); if (api === API_WEBAUDIO && context["createMediaElementSource"] && !/wiiu/i.test(navigator.userAgent)) { this.supportWebAudioAPI = true; // can be routed through web audio api this.bufferObject.addEventListener("canplay", function () { if (!self.mediaSourceNode) // protect against this event firing twice { self.mediaSourceNode = context["createMediaElementSource"](self.bufferObject); self.mediaSourceNode["connect"](self.outNode); } }); } this.bufferObject.autoplay = false; // this is only a source buffer, not an instance this.bufferObject.preload = "auto"; this.bufferObject.src = src_; break; case API_WEBAUDIO: if (audRuntime.isWKWebView) { audRuntime.fetchLocalFileViaCordovaAsArrayBuffer(src_, function (arrayBuffer) { self.audioData = arrayBuffer; self.decodeAudioBuffer(); }, function (err) { self.failedToLoad = true; }); } else { request = new XMLHttpRequest(); request.open("GET", src_, true); request.responseType = "arraybuffer"; request.onload = function () { self.audioData = request.response; self.decodeAudioBuffer(); }; request.onerror = function () { self.failedToLoad = true; }; request.send(); } break; case API_CORDOVA: this.bufferObject = true; break; case API_APPMOBI: this.bufferObject = true; break; } }; C2AudioBuffer.prototype.release = function () { var i, len, j, a; for (i = 0, j = 0, len = audioInstances.length; i < len; ++i) { a = audioInstances[i]; audioInstances[j] = a; if (a.buffer === this) a.stop(); else ++j; // keep } audioInstances.length = j; this.bufferObject = null; this.audioData = null; }; C2AudioBuffer.prototype.decodeAudioBuffer = function () { if (this.bufferObject || !this.audioData) return; // audio already decoded or AJAX request not yet complete var self = this; if (context["decodeAudioData"]) { context["decodeAudioData"](this.audioData, function (buffer) { self.bufferObject = buffer; self.audioData = null; // clear AJAX response to allow GC and save memory, only need the bufferObject now var p, i, len, a; if (!cr.is_undefined(self.playTagWhenReady) && !silent) { if (self.panWhenReady.length) { for (i = 0, len = self.panWhenReady.length; i < len; i++) { p = self.panWhenReady[i]; a = new C2AudioInstance(self, p.thistag); a.setPannerEnabled(true); if (typeof p.objUid !== "undefined") { p.obj = audRuntime.getObjectByUID(p.objUid); if (!p.obj) continue; } if (p.obj) { var px = cr.rotatePtAround(p.obj.x, p.obj.y, -p.obj.layer.getAngle(), listenerX, listenerY, true); var py = cr.rotatePtAround(p.obj.x, p.obj.y, -p.obj.layer.getAngle(), listenerX, listenerY, false); a.setPan(px, py, cr.to_degrees(p.obj.angle - p.obj.layer.getAngle()), p.ia, p.oa, p.og); a.setObject(p.obj); } else { a.setPan(p.x, p.y, p.a, p.ia, p.oa, p.og); } a.play(self.loopWhenReady, self.volumeWhenReady, self.seekWhenReady); if (self.pauseWhenReady) a.pause(); audioInstances.push(a); } cr.clearArray(self.panWhenReady); } else { a = new C2AudioInstance(self, self.playTagWhenReady || ""); // sometimes playTagWhenReady is not set - TODO: why? a.play(self.loopWhenReady, self.volumeWhenReady, self.seekWhenReady); if (self.pauseWhenReady) a.pause(); audioInstances.push(a); } } else if (!cr.is_undefined(self.convolveWhenReady)) { var convolveNode = self.convolveWhenReady.convolveNode; convolveNode["normalize"] = self.normalizeWhenReady; convolveNode["buffer"] = buffer; } }, function (e) { self.failedToLoad = true; }); } else { this.bufferObject = context["createBuffer"](this.audioData, false); this.audioData = null; // clear AJAX response to allow GC and save memory, only need the bufferObject now if (!cr.is_undefined(this.playTagWhenReady) && !silent) { var a = new C2AudioInstance(this, this.playTagWhenReady); a.play(this.loopWhenReady, this.volumeWhenReady, this.seekWhenReady); if (this.pauseWhenReady) a.pause(); audioInstances.push(a); } else if (!cr.is_undefined(this.convolveWhenReady)) { var convolveNode = this.convolveWhenReady.convolveNode; convolveNode["normalize"] = this.normalizeWhenReady; convolveNode["buffer"] = this.bufferObject; } } }; C2AudioBuffer.prototype.isLoaded = function () { switch (this.myapi) { case API_HTML5: var ret = this.bufferObject["readyState"] >= 4; // HAVE_ENOUGH_DATA if (ret) this.wasEverReady = true; return ret || this.wasEverReady; case API_WEBAUDIO: return !!this.audioData || !!this.bufferObject; case API_CORDOVA: return true; case API_APPMOBI: return true; } return false; }; C2AudioBuffer.prototype.isLoadedAndDecoded = function () { switch (this.myapi) { case API_HTML5: return this.isLoaded(); // no distinction between loaded and decoded in HTML5 audio, just rely on ready state case API_WEBAUDIO: return !!this.bufferObject; case API_CORDOVA: return true; case API_APPMOBI: return true; } return false; }; C2AudioBuffer.prototype.hasFailedToLoad = function () { switch (this.myapi) { case API_HTML5: return !!this.bufferObject["error"]; case API_WEBAUDIO: return this.failedToLoad; } return false; }; function C2AudioInstance(buffer_, tag_) { var self = this; this.tag = tag_; this.fresh = true; this.stopped = true; this.src = buffer_.src; this.buffer = buffer_; this.myapi = api; this.is_music = buffer_.is_music; this.playbackRate = 1; this.hasPlaybackEnded = true; // ended flag this.resume_me = false; // make sure resumes when leaving suspend this.is_paused = false; this.resume_position = 0; // for web audio api to resume from correct playback position this.looping = false; this.is_muted = false; this.is_silent = false; this.volume = 1; this.onended_handler = function (e) { if (self.is_paused || self.resume_me) return; var bufferThatEnded = this; if (!bufferThatEnded) bufferThatEnded = e.target; if (bufferThatEnded !== self.active_buffer) return; self.hasPlaybackEnded = true; self.stopped = true; audTag = self.tag; audRuntime.trigger(cr.plugins_.Audio.prototype.cnds.OnEnded, audInst); }; this.active_buffer = null; this.isTimescaled = ((timescale_mode === 1 && !this.is_music) || timescale_mode === 2); this.mutevol = 1; this.startTime = (this.isTimescaled ? audRuntime.kahanTime.sum : audRuntime.wallTime.sum); this.gainNode = null; this.pannerNode = null; this.pannerEnabled = false; this.objectTracker = null; this.panX = 0; this.panY = 0; this.panAngle = 0; this.panConeInner = 0; this.panConeOuter = 0; this.panConeOuterGain = 0; this.instanceObject = null; var add_end_listener = false; if (this.myapi === API_WEBAUDIO && this.buffer.myapi === API_HTML5 && !this.buffer.supportWebAudioAPI) this.myapi = API_HTML5; switch (this.myapi) { case API_HTML5: if (this.is_music) { this.instanceObject = buffer_.bufferObject; add_end_listener = !buffer_.added_end_listener; buffer_.added_end_listener = true; } else { this.instanceObject = new Audio(); this.instanceObject.crossOrigin = "anonymous"; this.instanceObject.autoplay = false; this.instanceObject.src = buffer_.bufferObject.src; add_end_listener = true; } if (add_end_listener) { this.instanceObject.addEventListener('ended', function () { audTag = self.tag; self.stopped = true; audRuntime.trigger(cr.plugins_.Audio.prototype.cnds.OnEnded, audInst); }); } break; case API_WEBAUDIO: this.gainNode = createGain(); this.gainNode["connect"](getDestinationForTag(tag_)); if (this.buffer.myapi === API_WEBAUDIO) { if (buffer_.bufferObject) { this.instanceObject = context["createBufferSource"](); this.instanceObject["buffer"] = buffer_.bufferObject; this.instanceObject["connect"](this.gainNode); } } else { this.instanceObject = this.buffer.bufferObject; // reference the audio element this.buffer.outNode["connect"](this.gainNode); if (!this.buffer.added_end_listener) { this.buffer.added_end_listener = true; this.buffer.bufferObject.addEventListener('ended', function () { audTag = self.tag; self.stopped = true; audRuntime.trigger(cr.plugins_.Audio.prototype.cnds.OnEnded, audInst); }); } } break; case API_CORDOVA: this.instanceObject = new window["Media"](appPath + this.src, null, null, function (status) { if (status === window["Media"]["MEDIA_STOPPED"]) { self.hasPlaybackEnded = true; self.stopped = true; audTag = self.tag; audRuntime.trigger(cr.plugins_.Audio.prototype.cnds.OnEnded, audInst); } }); break; case API_APPMOBI: this.instanceObject = true; break; } }; C2AudioInstance.prototype.hasEnded = function () { var time; switch (this.myapi) { case API_HTML5: return this.instanceObject.ended; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { if (!this.fresh && !this.stopped && this.instanceObject["loop"]) return false; if (this.is_paused) return false; return this.hasPlaybackEnded; } else return this.instanceObject.ended; case API_CORDOVA: return this.hasPlaybackEnded; case API_APPMOBI: true; // recycling an AppMobi sound does not matter because it will just do another throwaway playSound } return true; }; C2AudioInstance.prototype.canBeRecycled = function () { if (this.fresh || this.stopped) return true; // not yet used or is not playing return this.hasEnded(); }; C2AudioInstance.prototype.setPannerEnabled = function (enable_) { if (api !== API_WEBAUDIO) return; if (!this.pannerEnabled && enable_) { if (!this.gainNode) return; if (!this.pannerNode) { this.pannerNode = context["createPanner"](); if (typeof this.pannerNode["panningModel"] === "number") this.pannerNode["panningModel"] = panningModel; else this.pannerNode["panningModel"] = ["equalpower", "HRTF", "soundfield"][panningModel]; if (typeof this.pannerNode["distanceModel"] === "number") this.pannerNode["distanceModel"] = distanceModel; else this.pannerNode["distanceModel"] = ["linear", "inverse", "exponential"][distanceModel]; this.pannerNode["refDistance"] = refDistance; this.pannerNode["maxDistance"] = maxDistance; this.pannerNode["rolloffFactor"] = rolloffFactor; } this.gainNode["disconnect"](); this.gainNode["connect"](this.pannerNode); this.pannerNode["connect"](getDestinationForTag(this.tag)); this.pannerEnabled = true; } else if (this.pannerEnabled && !enable_) { if (!this.gainNode) return; this.pannerNode["disconnect"](); this.gainNode["disconnect"](); this.gainNode["connect"](getDestinationForTag(this.tag)); this.pannerEnabled = false; } }; C2AudioInstance.prototype.setPan = function (x, y, angle, innerangle, outerangle, outergain) { if (!this.pannerEnabled || api !== API_WEBAUDIO) return; this.pannerNode["setPosition"](x, y, 0); this.pannerNode["setOrientation"](Math.cos(cr.to_radians(angle)), Math.sin(cr.to_radians(angle)), 0); this.pannerNode["coneInnerAngle"] = innerangle; this.pannerNode["coneOuterAngle"] = outerangle; this.pannerNode["coneOuterGain"] = outergain; this.panX = x; this.panY = y; this.panAngle = angle; this.panConeInner = innerangle; this.panConeOuter = outerangle; this.panConeOuterGain = outergain; }; C2AudioInstance.prototype.setObject = function (o) { if (!this.pannerEnabled || api !== API_WEBAUDIO) return; if (!this.objectTracker) this.objectTracker = new ObjectTracker(); this.objectTracker.setObject(o); }; C2AudioInstance.prototype.tick = function (dt) { if (!this.pannerEnabled || api !== API_WEBAUDIO || !this.objectTracker || !this.objectTracker.hasObject() || !this.isPlaying()) { return; } this.objectTracker.tick(dt); var inst = this.objectTracker.obj; var px = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, true); var py = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, false); this.pannerNode["setPosition"](px, py, 0); var a = 0; if (typeof this.objectTracker.obj.angle !== "undefined") { a = inst.angle - inst.layer.getAngle(); this.pannerNode["setOrientation"](Math.cos(a), Math.sin(a), 0); } }; C2AudioInstance.prototype.play = function (looping, vol, fromPosition, scheduledTime) { var instobj = this.instanceObject; this.looping = looping; this.volume = vol; var seekPos = fromPosition || 0; scheduledTime = scheduledTime || 0; switch (this.myapi) { case API_HTML5: if (instobj.playbackRate !== 1.0) instobj.playbackRate = 1.0; if (instobj.volume !== vol * masterVolume) instobj.volume = vol * masterVolume; if (instobj.loop !== looping) instobj.loop = looping; if (instobj.muted) instobj.muted = false; if (instobj.currentTime !== seekPos) { try { instobj.currentTime = seekPos; } catch (err) { ; } } if (this.is_music && isMusicWorkaround && !audRuntime.isInUserInputEvent) musicPlayNextTouch.push(this); else { try { this.instanceObject.play(); } catch (e) { // sometimes throws on WP8.1... try not to kill the app if (console && console.log) console.log("[C2] WARNING: exception trying to play audio '" + this.buffer.src + "': ", e); } } break; case API_WEBAUDIO: this.muted = false; this.mutevol = 1; if (this.buffer.myapi === API_WEBAUDIO) { this.gainNode["gain"]["value"] = vol * masterVolume; if (!this.fresh) { this.instanceObject = context["createBufferSource"](); this.instanceObject["buffer"] = this.buffer.bufferObject; this.instanceObject["connect"](this.gainNode); } this.instanceObject["onended"] = this.onended_handler; this.active_buffer = this.instanceObject; this.instanceObject.loop = looping; this.hasPlaybackEnded = false; if (seekPos === 0) startSource(this.instanceObject, scheduledTime); else startSourceAt(this.instanceObject, seekPos, this.getDuration(), scheduledTime); } else { if (instobj.playbackRate !== 1.0) instobj.playbackRate = 1.0; if (instobj.loop !== looping) instobj.loop = looping; instobj.volume = vol * masterVolume; if (instobj.currentTime !== seekPos) { try { instobj.currentTime = seekPos; } catch (err) { ; } } if (this.is_music && isMusicWorkaround && !audRuntime.isInUserInputEvent) musicPlayNextTouch.push(this); else instobj.play(); } break; case API_CORDOVA: if ((!this.fresh && this.stopped) || seekPos !== 0) instobj["seekTo"](seekPos); instobj["play"](); this.hasPlaybackEnded = false; break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["playSound"](this.src, looping); else AppMobi["player"]["playSound"](this.src, looping); break; } this.playbackRate = 1; this.startTime = (this.isTimescaled ? audRuntime.kahanTime.sum : audRuntime.wallTime.sum) - seekPos; this.fresh = false; this.stopped = false; this.is_paused = false; }; C2AudioInstance.prototype.stop = function () { switch (this.myapi) { case API_HTML5: if (!this.instanceObject.paused) this.instanceObject.pause(); break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) stopSource(this.instanceObject); else { if (!this.instanceObject.paused) this.instanceObject.pause(); } break; case API_CORDOVA: this.instanceObject["stop"](); break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["stopSound"](this.src); break; } this.stopped = true; this.is_paused = false; }; C2AudioInstance.prototype.pause = function () { if (this.fresh || this.stopped || this.hasEnded() || this.is_paused) return; switch (this.myapi) { case API_HTML5: if (!this.instanceObject.paused) this.instanceObject.pause(); break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { this.resume_position = this.getPlaybackTime(true); if (this.looping) this.resume_position = this.resume_position % this.getDuration(); this.is_paused = true; stopSource(this.instanceObject); } else { if (!this.instanceObject.paused) this.instanceObject.pause(); } break; case API_CORDOVA: this.instanceObject["pause"](); break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["stopSound"](this.src); break; } this.is_paused = true; }; C2AudioInstance.prototype.resume = function () { if (this.fresh || this.stopped || this.hasEnded() || !this.is_paused) return; switch (this.myapi) { case API_HTML5: this.instanceObject.play(); break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { this.instanceObject = context["createBufferSource"](); this.instanceObject["buffer"] = this.buffer.bufferObject; this.instanceObject["connect"](this.gainNode); this.instanceObject["onended"] = this.onended_handler; this.active_buffer = this.instanceObject; this.instanceObject.loop = this.looping; this.gainNode["gain"]["value"] = masterVolume * this.volume * this.mutevol; this.updatePlaybackRate(); this.startTime = (this.isTimescaled ? audRuntime.kahanTime.sum : audRuntime.wallTime.sum) - (this.resume_position / (this.playbackRate || 0.001)); startSourceAt(this.instanceObject, this.resume_position, this.getDuration()); } else { this.instanceObject.play(); } break; case API_CORDOVA: this.instanceObject["play"](); break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["resumeSound"](this.src); break; } this.is_paused = false; }; C2AudioInstance.prototype.seek = function (pos) { if (this.fresh || this.stopped || this.hasEnded()) return; switch (this.myapi) { case API_HTML5: try { this.instanceObject.currentTime = pos; } catch (e) {} break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { if (this.is_paused) this.resume_position = pos; else { this.pause(); this.resume_position = pos; this.resume(); } } else { try { this.instanceObject.currentTime = pos; } catch (e) {} } break; case API_CORDOVA: break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["seekSound"](this.src, pos); break; } }; C2AudioInstance.prototype.reconnect = function (toNode) { if (this.myapi !== API_WEBAUDIO) return; if (this.pannerEnabled) { this.pannerNode["disconnect"](); this.pannerNode["connect"](toNode); } else { this.gainNode["disconnect"](); this.gainNode["connect"](toNode); } }; C2AudioInstance.prototype.getDuration = function (applyPlaybackRate) { var ret = 0; switch (this.myapi) { case API_HTML5: if (typeof this.instanceObject.duration !== "undefined") ret = this.instanceObject.duration; break; case API_WEBAUDIO: ret = this.buffer.bufferObject["duration"]; break; case API_CORDOVA: ret = this.instanceObject["getDuration"](); break; case API_APPMOBI: if (audRuntime.isDirectCanvas) ret = AppMobi["context"]["getDurationSound"](this.src); break; } if (applyPlaybackRate) ret /= (this.playbackRate || 0.001); // avoid divide-by-zero return ret; }; C2AudioInstance.prototype.getPlaybackTime = function (applyPlaybackRate) { var duration = this.getDuration(); var ret = 0; switch (this.myapi) { case API_HTML5: if (typeof this.instanceObject.currentTime !== "undefined") ret = this.instanceObject.currentTime; break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { if (this.is_paused) return this.resume_position; else ret = (this.isTimescaled ? audRuntime.kahanTime.sum : audRuntime.wallTime.sum) - this.startTime; } else if (typeof this.instanceObject.currentTime !== "undefined") ret = this.instanceObject.currentTime; break; case API_CORDOVA: break; case API_APPMOBI: if (audRuntime.isDirectCanvas) ret = AppMobi["context"]["getPlaybackTimeSound"](this.src); break; } if (applyPlaybackRate) ret *= this.playbackRate; if (!this.looping && ret > duration) ret = duration; return ret; }; C2AudioInstance.prototype.isPlaying = function () { return !this.is_paused && !this.fresh && !this.stopped && !this.hasEnded(); }; C2AudioInstance.prototype.shouldSave = function () { return !this.fresh && !this.stopped && !this.hasEnded(); }; C2AudioInstance.prototype.setVolume = function (v) { this.volume = v; this.updateVolume(); }; C2AudioInstance.prototype.updateVolume = function () { var volToSet = this.volume * masterVolume; if (!isFinite(volToSet)) volToSet = 0; // HTMLMediaElement throws if setting non-finite volume switch (this.myapi) { case API_HTML5: if (typeof this.instanceObject.volume !== "undefined" && this.instanceObject.volume !== volToSet) this.instanceObject.volume = volToSet; break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { this.gainNode["gain"]["value"] = volToSet * this.mutevol; } else { if (typeof this.instanceObject.volume !== "undefined" && this.instanceObject.volume !== volToSet) this.instanceObject.volume = volToSet; } break; case API_CORDOVA: break; case API_APPMOBI: break; } }; C2AudioInstance.prototype.getVolume = function () { return this.volume; }; C2AudioInstance.prototype.doSetMuted = function (m) { switch (this.myapi) { case API_HTML5: if (this.instanceObject.muted !== !!m) this.instanceObject.muted = !!m; break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { this.mutevol = (m ? 0 : 1); this.gainNode["gain"]["value"] = masterVolume * this.volume * this.mutevol; } else { if (this.instanceObject.muted !== !!m) this.instanceObject.muted = !!m; } break; case API_CORDOVA: break; case API_APPMOBI: break; } }; C2AudioInstance.prototype.setMuted = function (m) { this.is_muted = !!m; this.doSetMuted(this.is_muted || this.is_silent); }; C2AudioInstance.prototype.setSilent = function (m) { this.is_silent = !!m; this.doSetMuted(this.is_muted || this.is_silent); }; C2AudioInstance.prototype.setLooping = function (l) { this.looping = l; switch (this.myapi) { case API_HTML5: if (this.instanceObject.loop !== !!l) this.instanceObject.loop = !!l; break; case API_WEBAUDIO: if (this.instanceObject.loop !== !!l) this.instanceObject.loop = !!l; break; case API_CORDOVA: break; case API_APPMOBI: if (audRuntime.isDirectCanvas) AppMobi["context"]["setLoopingSound"](this.src, l); break; } }; C2AudioInstance.prototype.setPlaybackRate = function (r) { this.playbackRate = r; this.updatePlaybackRate(); }; C2AudioInstance.prototype.updatePlaybackRate = function () { var r = this.playbackRate; if (this.isTimescaled) r *= audRuntime.timescale; switch (this.myapi) { case API_HTML5: if (this.instanceObject.playbackRate !== r) this.instanceObject.playbackRate = r; break; case API_WEBAUDIO: if (this.buffer.myapi === API_WEBAUDIO) { if (this.instanceObject["playbackRate"]["value"] !== r) this.instanceObject["playbackRate"]["value"] = r; } else { if (this.instanceObject.playbackRate !== r) this.instanceObject.playbackRate = r; } break; case API_CORDOVA: break; case API_APPMOBI: break; } }; C2AudioInstance.prototype.setSuspended = function (s) { switch (this.myapi) { case API_HTML5: if (s) { if (this.isPlaying()) { this.resume_me = true; this.instanceObject["pause"](); } else this.resume_me = false; } else { if (this.resume_me) { this.instanceObject["play"](); this.resume_me = false; } } break; case API_WEBAUDIO: if (s) { if (this.isPlaying()) { this.resume_me = true; if (this.buffer.myapi === API_WEBAUDIO) { this.resume_position = this.getPlaybackTime(true); if (this.looping) this.resume_position = this.resume_position % this.getDuration(); stopSource(this.instanceObject); } else this.instanceObject["pause"](); } else this.resume_me = false; } else { if (this.resume_me) { if (this.buffer.myapi === API_WEBAUDIO) { this.instanceObject = context["createBufferSource"](); this.instanceObject["buffer"] = this.buffer.bufferObject; this.instanceObject["connect"](this.gainNode); this.instanceObject["onended"] = this.onended_handler; this.active_buffer = this.instanceObject; this.instanceObject.loop = this.looping; this.gainNode["gain"]["value"] = masterVolume * this.volume * this.mutevol; this.updatePlaybackRate(); this.startTime = (this.isTimescaled ? audRuntime.kahanTime.sum : audRuntime.wallTime.sum) - (this.resume_position / (this.playbackRate || 0.001)); startSourceAt(this.instanceObject, this.resume_position, this.getDuration()); } else { this.instanceObject["play"](); } this.resume_me = false; } } break; case API_CORDOVA: if (s) { if (this.isPlaying()) { this.instanceObject["pause"](); this.resume_me = true; } else this.resume_me = false; } else { if (this.resume_me) { this.resume_me = false; this.instanceObject["play"](); } } break; case API_APPMOBI: break; } }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; audRuntime = this.runtime; audInst = this; this.listenerTracker = null; this.listenerZ = -600; if (this.runtime.isWKWebView) playMusicAsSoundWorkaround = true; if ((this.runtime.isiOS || (this.runtime.isAndroid && (this.runtime.isChrome || this.runtime.isAndroidStockBrowser))) && !this.runtime.isCrosswalk && !this.runtime.isDomFree && !this.runtime.isAmazonWebApp && !playMusicAsSoundWorkaround) { isMusicWorkaround = true; } context = null; if (typeof AudioContext !== "undefined") { api = API_WEBAUDIO; context = new AudioContext(); } else if (typeof webkitAudioContext !== "undefined") { api = API_WEBAUDIO; context = new webkitAudioContext(); } if (this.runtime.isiOS && context) { if (context.close) context.close(); if (typeof AudioContext !== "undefined") context = new AudioContext(); else if (typeof webkitAudioContext !== "undefined") context = new webkitAudioContext(); } var isAndroid = this.runtime.isAndroid; var playDummyBuffer = function () { if (isContextSuspended || !context["createBuffer"]) return; var buffer = context["createBuffer"](1, 220, 22050); var source = context["createBufferSource"](); source["buffer"] = buffer; source["connect"](context["destination"]); startSource(source); }; if (isMusicWorkaround) { var playQueuedMusic = function () { var i, len, m; if (isMusicWorkaround) { if (!silent) { for (i = 0, len = musicPlayNextTouch.length; i < len; ++i) { m = musicPlayNextTouch[i]; if (!m.stopped && !m.is_paused) m.instanceObject.play(); } } cr.clearArray(musicPlayNextTouch); } }; document.addEventListener("touchend", function () { if (!iOShadtouchend && context) { playDummyBuffer(); iOShadtouchend = true; } playQueuedMusic(); }, true); } else if (playMusicAsSoundWorkaround) { document.addEventListener("touchend", function () { if (!iOShadtouchend && context) { playDummyBuffer(); iOShadtouchend = true; } }, true); } if (api !== API_WEBAUDIO) { if (this.runtime.isCordova && typeof window["Media"] !== "undefined") api = API_CORDOVA; else if (this.runtime.isAppMobi) api = API_APPMOBI; } if (api === API_CORDOVA) { appPath = location.href; var i = appPath.lastIndexOf("/"); if (i > -1) appPath = appPath.substr(0, i + 1); appPath = appPath.replace("file://", ""); } if (this.runtime.isSafari && this.runtime.isWindows && typeof Audio === "undefined") { alert("It looks like you're using Safari for Windows without Quicktime. Audio cannot be played until Quicktime is installed."); this.runtime.DestroyInstance(this); } else { if (this.runtime.isDirectCanvas) useOgg = this.runtime.isAndroid; // AAC on iOS, OGG on Android else { try { useOgg = !!(new Audio().canPlayType('audio/ogg; codecs="vorbis"')); } catch (e) { useOgg = false; } } switch (api) { case API_HTML5: ; break; case API_WEBAUDIO: ; break; case API_CORDOVA: ; break; case API_APPMOBI: ; break; default: ; } this.runtime.tickMe(this); } }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function () { this.runtime.audioInstance = this; timescale_mode = this.properties[0]; // 0 = off, 1 = sounds only, 2 = all this.saveload = this.properties[1]; // 0 = all, 1 = sounds only, 2 = music only, 3 = none this.playinbackground = (this.properties[2] !== 0); this.nextPlayTime = 0; panningModel = this.properties[3]; // 0 = equalpower, 1 = hrtf, 3 = soundfield distanceModel = this.properties[4]; // 0 = linear, 1 = inverse, 2 = exponential this.listenerZ = -this.properties[5]; refDistance = this.properties[6]; maxDistance = this.properties[7]; rolloffFactor = this.properties[8]; this.listenerTracker = new ObjectTracker(); var draw_width = (this.runtime.draw_width || this.runtime.width); var draw_height = (this.runtime.draw_height || this.runtime.height); if (api === API_WEBAUDIO) { context["listener"]["setPosition"](draw_width / 2, draw_height / 2, this.listenerZ); context["listener"]["setOrientation"](0, 0, 1, 0, -1, 0); window["c2OnAudioMicStream"] = function (localMediaStream, tag) { if (micSource) micSource["disconnect"](); micTag = tag.toLowerCase(); micSource = context["createMediaStreamSource"](localMediaStream); micSource["connect"](getDestinationForTag(micTag)); }; } this.runtime.addSuspendCallback(function(s) { audInst.onSuspend(s); }); var self = this; this.runtime.addDestroyCallback(function (inst) { self.onInstanceDestroyed(inst); }); }; instanceProto.onInstanceDestroyed = function (inst) { var i, len, a; for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; if (a.objectTracker) { if (a.objectTracker.obj === inst) { a.objectTracker.obj = null; if (a.pannerEnabled && a.isPlaying() && a.looping) a.stop(); } } } if (this.listenerTracker.obj === inst) this.listenerTracker.obj = null; }; instanceProto.saveToJSON = function () { var o = { "silent": silent, "masterVolume": masterVolume, "listenerZ": this.listenerZ, "listenerUid": this.listenerTracker.hasObject() ? this.listenerTracker.obj.uid : -1, "playing": [], "effects": {} }; var playingarr = o["playing"]; var i, len, a, d, p, panobj, playbackTime; for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; if (!a.shouldSave()) continue; // no need to save stopped sounds if (this.saveload === 3) // not saving/loading any sounds/music continue; if (a.is_music && this.saveload === 1) // not saving/loading music continue; if (!a.is_music && this.saveload === 2) // not saving/loading sound continue; playbackTime = a.getPlaybackTime(); if (a.looping) playbackTime = playbackTime % a.getDuration(); d = { "tag": a.tag, "buffersrc": a.buffer.src, "is_music": a.is_music, "playbackTime": playbackTime, "volume": a.volume, "looping": a.looping, "muted": a.is_muted, "playbackRate": a.playbackRate, "paused": a.is_paused, "resume_position": a.resume_position }; if (a.pannerEnabled) { d["pan"] = {}; panobj = d["pan"]; if (a.objectTracker && a.objectTracker.hasObject()) { panobj["objUid"] = a.objectTracker.obj.uid; } else { panobj["x"] = a.panX; panobj["y"] = a.panY; panobj["a"] = a.panAngle; } panobj["ia"] = a.panConeInner; panobj["oa"] = a.panConeOuter; panobj["og"] = a.panConeOuterGain; } playingarr.push(d); } var fxobj = o["effects"]; var fxarr; for (p in effects) { if (effects.hasOwnProperty(p)) { fxarr = []; for (i = 0, len = effects[p].length; i < len; i++) { fxarr.push({ "type": effects[p][i].type, "params": effects[p][i].params }); } fxobj[p] = fxarr; } } return o; }; var objectTrackerUidsToLoad = []; instanceProto.loadFromJSON = function (o) { var setSilent = o["silent"]; masterVolume = o["masterVolume"]; this.listenerZ = o["listenerZ"]; this.listenerTracker.setObject(null); var listenerUid = o["listenerUid"]; if (listenerUid !== -1) { this.listenerTracker.loadUid = listenerUid; objectTrackerUidsToLoad.push(this.listenerTracker); } var playingarr = o["playing"]; var i, len, d, src, is_music, tag, playbackTime, looping, vol, b, a, p, pan, panObjUid; if (this.saveload !== 3) { for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; if (a.is_music && this.saveload === 1) continue; // only saving/loading sound: leave music playing if (!a.is_music && this.saveload === 2) continue; // only saving/loading music: leave sound playing a.stop(); } } var fxarr, fxtype, fxparams, fx; for (p in effects) { if (effects.hasOwnProperty(p)) { for (i = 0, len = effects[p].length; i < len; i++) effects[p][i].remove(); } } cr.wipe(effects); for (p in o["effects"]) { if (o["effects"].hasOwnProperty(p)) { fxarr = o["effects"][p]; for (i = 0, len = fxarr.length; i < len; i++) { fxtype = fxarr[i]["type"]; fxparams = fxarr[i]["params"]; switch (fxtype) { case "filter": addEffectForTag(p, new FilterEffect(fxparams[0], fxparams[1], fxparams[2], fxparams[3], fxparams[4], fxparams[5])); break; case "delay": addEffectForTag(p, new DelayEffect(fxparams[0], fxparams[1], fxparams[2])); break; case "convolve": src = fxparams[2]; b = this.getAudioBuffer(src, false); if (b.bufferObject) { fx = new ConvolveEffect(b.bufferObject, fxparams[0], fxparams[1], src); } else { fx = new ConvolveEffect(null, fxparams[0], fxparams[1], src); b.normalizeWhenReady = fxparams[0]; b.convolveWhenReady = fx; } addEffectForTag(p, fx); break; case "flanger": addEffectForTag(p, new FlangerEffect(fxparams[0], fxparams[1], fxparams[2], fxparams[3], fxparams[4])); break; case "phaser": addEffectForTag(p, new PhaserEffect(fxparams[0], fxparams[1], fxparams[2], fxparams[3], fxparams[4], fxparams[5])); break; case "gain": addEffectForTag(p, new GainEffect(fxparams[0])); break; case "tremolo": addEffectForTag(p, new TremoloEffect(fxparams[0], fxparams[1])); break; case "ringmod": addEffectForTag(p, new RingModulatorEffect(fxparams[0], fxparams[1])); break; case "distortion": addEffectForTag(p, new DistortionEffect(fxparams[0], fxparams[1], fxparams[2], fxparams[3], fxparams[4])); break; case "compressor": addEffectForTag(p, new CompressorEffect(fxparams[0], fxparams[1], fxparams[2], fxparams[3], fxparams[4])); break; case "analyser": addEffectForTag(p, new AnalyserEffect(fxparams[0], fxparams[1])); break; } } } } for (i = 0, len = playingarr.length; i < len; i++) { if (this.saveload === 3) // not saving/loading any sounds/music continue; d = playingarr[i]; src = d["buffersrc"]; is_music = d["is_music"]; tag = d["tag"]; playbackTime = d["playbackTime"]; looping = d["looping"]; vol = d["volume"]; pan = d["pan"]; panObjUid = (pan && pan.hasOwnProperty("objUid")) ? pan["objUid"] : -1; if (is_music && this.saveload === 1) // not saving/loading music continue; if (!is_music && this.saveload === 2) // not saving/loading sound continue; a = this.getAudioInstance(src, tag, is_music, looping, vol); if (!a) { b = this.getAudioBuffer(src, is_music); b.seekWhenReady = playbackTime; b.pauseWhenReady = d["paused"]; if (pan) { if (panObjUid !== -1) { b.panWhenReady.push({ objUid: panObjUid, ia: pan["ia"], oa: pan["oa"], og: pan["og"], thistag: tag }); } else { b.panWhenReady.push({ x: pan["x"], y: pan["y"], a: pan["a"], ia: pan["ia"], oa: pan["oa"], og: pan["og"], thistag: tag }); } } continue; } a.resume_position = d["resume_position"]; a.setPannerEnabled(!!pan); a.play(looping, vol, playbackTime); a.updatePlaybackRate(); a.updateVolume(); a.doSetMuted(a.is_muted || a.is_silent); if (d["paused"]) a.pause(); if (d["muted"]) a.setMuted(true); a.doSetMuted(a.is_muted || a.is_silent); if (pan) { if (panObjUid !== -1) { a.objectTracker = a.objectTracker || new ObjectTracker(); a.objectTracker.loadUid = panObjUid; objectTrackerUidsToLoad.push(a.objectTracker); } else { a.setPan(pan["x"], pan["y"], pan["a"], pan["ia"], pan["oa"], pan["og"]); } } } if (setSilent && !silent) // setting silent { for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].setSilent(true); silent = true; } else if (!setSilent && silent) // setting not silent { for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].setSilent(false); silent = false; } }; instanceProto.afterLoad = function () { var i, len, ot, inst; for (i = 0, len = objectTrackerUidsToLoad.length; i < len; i++) { ot = objectTrackerUidsToLoad[i]; inst = this.runtime.getObjectByUID(ot.loadUid); ot.setObject(inst); ot.loadUid = -1; if (inst) { listenerX = inst.x; listenerY = inst.y; } } cr.clearArray(objectTrackerUidsToLoad); }; instanceProto.onSuspend = function (s) { if (this.playinbackground) return; if (!s && context && context["resume"]) { context["resume"](); isContextSuspended = false; } var i, len; for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].setSuspended(s); if (s && context && context["suspend"]) { context["suspend"](); isContextSuspended = true; } }; instanceProto.tick = function () { var dt = this.runtime.dt; var i, len, a; for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; a.tick(dt); if (timescale_mode !== 0) a.updatePlaybackRate(); } var p, arr, f; for (p in effects) { if (effects.hasOwnProperty(p)) { arr = effects[p]; for (i = 0, len = arr.length; i < len; i++) { f = arr[i]; if (f.tick) f.tick(); } } } if (api === API_WEBAUDIO && this.listenerTracker.hasObject()) { this.listenerTracker.tick(dt); listenerX = this.listenerTracker.obj.x; listenerY = this.listenerTracker.obj.y; context["listener"]["setPosition"](this.listenerTracker.obj.x, this.listenerTracker.obj.y, this.listenerZ); } }; var preload_list = []; instanceProto.setPreloadList = function (arr) { var i, len, p, filename, size, isOgg; var total_size = 0; for (i = 0, len = arr.length; i < len; ++i) { p = arr[i]; filename = p[0]; size = p[1] * 2; isOgg = (filename.length > 4 && filename.substr(filename.length - 4) === ".ogg"); if ((isOgg && useOgg) || (!isOgg && !useOgg)) { preload_list.push({ filename: filename, size: size, obj: null }); total_size += size; } } return total_size; }; instanceProto.startPreloads = function () { var i, len, p, src; for (i = 0, len = preload_list.length; i < len; ++i) { p = preload_list[i]; src = this.runtime.files_subfolder + p.filename; p.obj = this.getAudioBuffer(src, false); } }; instanceProto.getPreloadedSize = function () { var completed = 0; var i, len, p; for (i = 0, len = preload_list.length; i < len; ++i) { p = preload_list[i]; if (p.obj.isLoadedAndDecoded() || p.obj.hasFailedToLoad() || this.runtime.isDomFree || this.runtime.isAndroidStockBrowser) { completed += p.size; } else if (p.obj.isLoaded()) // downloaded but not decoded: only happens in Web Audio API, count as half-way progress { completed += Math.floor(p.size / 2); } }; return completed; }; instanceProto.releaseAllMusicBuffers = function () { var i, len, j, b; for (i = 0, j = 0, len = audioBuffers.length; i < len; ++i) { b = audioBuffers[i]; audioBuffers[j] = b; if (b.is_music) b.release(); else ++j; // keep } audioBuffers.length = j; }; instanceProto.getAudioBuffer = function (src_, is_music, dont_create) { var i, len, a, ret = null, j, k, lenj, ai; for (i = 0, len = audioBuffers.length; i < len; i++) { a = audioBuffers[i]; if (a.src === src_) { ret = a; break; } } if (!ret && !dont_create) { if (playMusicAsSoundWorkaround && is_music) this.releaseAllMusicBuffers(); ret = new C2AudioBuffer(src_, is_music); audioBuffers.push(ret); } return ret; }; instanceProto.getAudioInstance = function (src_, tag, is_music, looping, vol) { var i, len, a; for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; if (a.src === src_ && (a.canBeRecycled() || is_music)) { a.tag = tag; return a; } } var b = this.getAudioBuffer(src_, is_music); if (!b.bufferObject) { if (tag !== "") { b.playTagWhenReady = tag; b.loopWhenReady = looping; b.volumeWhenReady = vol; } return null; } a = new C2AudioInstance(b, tag); audioInstances.push(a); return a; }; var taggedAudio = []; function SortByIsPlaying(a, b) { var an = a.isPlaying() ? 1 : 0; var bn = b.isPlaying() ? 1 : 0; if (an === bn) return 0; else if (an < bn) return 1; else return -1; }; function getAudioByTag(tag, sort_by_playing) { cr.clearArray(taggedAudio); if (!tag.length) { if (!lastAudio || lastAudio.hasEnded()) return; else { cr.clearArray(taggedAudio); taggedAudio[0] = lastAudio; return; } } var i, len, a; for (i = 0, len = audioInstances.length; i < len; i++) { a = audioInstances[i]; if (cr.equals_nocase(tag, a.tag)) taggedAudio.push(a); } if (sort_by_playing) taggedAudio.sort(SortByIsPlaying); }; function reconnectEffects(tag) { var i, len, arr, n, toNode = context["destination"]; if (effects.hasOwnProperty(tag)) { arr = effects[tag]; if (arr.length) { toNode = arr[0].getInputNode(); for (i = 0, len = arr.length; i < len; i++) { n = arr[i]; if (i + 1 === len) n.connectTo(context["destination"]); else n.connectTo(arr[i + 1].getInputNode()); } } } getAudioByTag(tag); for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].reconnect(toNode); if (micSource && micTag === tag) { micSource["disconnect"](); micSource["connect"](toNode); } }; function addEffectForTag(tag, fx) { if (!effects.hasOwnProperty(tag)) effects[tag] = [fx]; else effects[tag].push(fx); reconnectEffects(tag); }; function Cnds() {}; Cnds.prototype.OnEnded = function (t) { return cr.equals_nocase(audTag, t); }; Cnds.prototype.PreloadsComplete = function () { var i, len; for (i = 0, len = audioBuffers.length; i < len; i++) { if (!audioBuffers[i].isLoadedAndDecoded() && !audioBuffers[i].hasFailedToLoad()) return false; } return true; }; Cnds.prototype.AdvancedAudioSupported = function () { return api === API_WEBAUDIO; }; Cnds.prototype.IsSilent = function () { return silent; }; Cnds.prototype.IsAnyPlaying = function () { var i, len; for (i = 0, len = audioInstances.length; i < len; i++) { if (audioInstances[i].isPlaying()) return true; } return false; }; Cnds.prototype.IsTagPlaying = function (tag) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) { if (taggedAudio[i].isPlaying()) return true; } return false; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Play = function (file, looping, vol, tag) { if (silent) return; var v = dbToLinear(vol); var is_music = file[1]; var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) return; lastAudio.setPannerEnabled(false); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.PlayAtPosition = function (file, looping, vol, x_, y_, angle_, innerangle_, outerangle_, outergain_, tag) { if (silent) return; var v = dbToLinear(vol); var is_music = file[1]; var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) { var b = this.getAudioBuffer(src, is_music); b.panWhenReady.push({ x: x_, y: y_, a: angle_, ia: innerangle_, oa: outerangle_, og: dbToLinear(outergain_), thistag: tag }); return; } lastAudio.setPannerEnabled(true); lastAudio.setPan(x_, y_, angle_, innerangle_, outerangle_, dbToLinear(outergain_)); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.PlayAtObject = function (file, looping, vol, obj, innerangle, outerangle, outergain, tag) { if (silent || !obj) return; var inst = obj.getFirstPicked(); if (!inst) return; var v = dbToLinear(vol); var is_music = file[1]; var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) { var b = this.getAudioBuffer(src, is_music); b.panWhenReady.push({ obj: inst, ia: innerangle, oa: outerangle, og: dbToLinear(outergain), thistag: tag }); return; } lastAudio.setPannerEnabled(true); var px = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, true); var py = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, false); lastAudio.setPan(px, py, cr.to_degrees(inst.angle - inst.layer.getAngle()), innerangle, outerangle, dbToLinear(outergain)); lastAudio.setObject(inst); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.PlayByName = function (folder, filename, looping, vol, tag) { if (silent) return; var v = dbToLinear(vol); var is_music = (folder === 1); var src = this.runtime.files_subfolder + filename.toLowerCase() + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) return; lastAudio.setPannerEnabled(false); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.PlayAtPositionByName = function (folder, filename, looping, vol, x_, y_, angle_, innerangle_, outerangle_, outergain_, tag) { if (silent) return; var v = dbToLinear(vol); var is_music = (folder === 1); var src = this.runtime.files_subfolder + filename.toLowerCase() + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) { var b = this.getAudioBuffer(src, is_music); b.panWhenReady.push({ x: x_, y: y_, a: angle_, ia: innerangle_, oa: outerangle_, og: dbToLinear(outergain_), thistag: tag }); return; } lastAudio.setPannerEnabled(true); lastAudio.setPan(x_, y_, angle_, innerangle_, outerangle_, dbToLinear(outergain_)); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.PlayAtObjectByName = function (folder, filename, looping, vol, obj, innerangle, outerangle, outergain, tag) { if (silent || !obj) return; var inst = obj.getFirstPicked(); if (!inst) return; var v = dbToLinear(vol); var is_music = (folder === 1); var src = this.runtime.files_subfolder + filename.toLowerCase() + (useOgg ? ".ogg" : ".m4a"); lastAudio = this.getAudioInstance(src, tag, is_music, looping!==0, v); if (!lastAudio) { var b = this.getAudioBuffer(src, is_music); b.panWhenReady.push({ obj: inst, ia: innerangle, oa: outerangle, og: dbToLinear(outergain), thistag: tag }); return; } lastAudio.setPannerEnabled(true); var px = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, true); var py = cr.rotatePtAround(inst.x, inst.y, -inst.layer.getAngle(), listenerX, listenerY, false); lastAudio.setPan(px, py, cr.to_degrees(inst.angle - inst.layer.getAngle()), innerangle, outerangle, dbToLinear(outergain)); lastAudio.setObject(inst); lastAudio.play(looping!==0, v, 0, this.nextPlayTime); this.nextPlayTime = 0; }; Acts.prototype.SetLooping = function (tag, looping) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].setLooping(looping === 0); }; Acts.prototype.SetMuted = function (tag, muted) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].setMuted(muted === 0); }; Acts.prototype.SetVolume = function (tag, vol) { getAudioByTag(tag); var v = dbToLinear(vol); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].setVolume(v); }; Acts.prototype.Preload = function (file) { if (silent) return; var is_music = file[1]; var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); if (api === API_APPMOBI) { if (this.runtime.isDirectCanvas) AppMobi["context"]["loadSound"](src); else AppMobi["player"]["loadSound"](src); return; } else if (api === API_CORDOVA) { return; } this.getAudioInstance(src, "", is_music, false); }; Acts.prototype.PreloadByName = function (folder, filename) { if (silent) return; var is_music = (folder === 1); var src = this.runtime.files_subfolder + filename.toLowerCase() + (useOgg ? ".ogg" : ".m4a"); if (api === API_APPMOBI) { if (this.runtime.isDirectCanvas) AppMobi["context"]["loadSound"](src); else AppMobi["player"]["loadSound"](src); return; } else if (api === API_CORDOVA) { return; } this.getAudioInstance(src, "", is_music, false); }; Acts.prototype.SetPlaybackRate = function (tag, rate) { getAudioByTag(tag); if (rate < 0.0) rate = 0; var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].setPlaybackRate(rate); }; Acts.prototype.Stop = function (tag) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) taggedAudio[i].stop(); }; Acts.prototype.StopAll = function () { var i, len; for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].stop(); }; Acts.prototype.SetPaused = function (tag, state) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) { if (state === 0) taggedAudio[i].pause(); else taggedAudio[i].resume(); } }; Acts.prototype.Seek = function (tag, pos) { getAudioByTag(tag); var i, len; for (i = 0, len = taggedAudio.length; i < len; i++) { taggedAudio[i].seek(pos); } }; Acts.prototype.SetSilent = function (s) { var i, len; if (s === 2) // toggling s = (silent ? 1 : 0); // choose opposite state if (s === 0 && !silent) // setting silent { for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].setSilent(true); silent = true; } else if (s === 1 && silent) // setting not silent { for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].setSilent(false); silent = false; } }; Acts.prototype.SetMasterVolume = function (vol) { masterVolume = dbToLinear(vol); var i, len; for (i = 0, len = audioInstances.length; i < len; i++) audioInstances[i].updateVolume(); }; Acts.prototype.AddFilterEffect = function (tag, type, freq, detune, q, gain, mix) { if (api !== API_WEBAUDIO || type < 0 || type >= filterTypes.length || !context["createBiquadFilter"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new FilterEffect(type, freq, detune, q, gain, mix)); }; Acts.prototype.AddDelayEffect = function (tag, delay, gain, mix) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new DelayEffect(delay, dbToLinear(gain), mix)); }; Acts.prototype.AddFlangerEffect = function (tag, delay, modulation, freq, feedback, mix) { if (api !== API_WEBAUDIO || !context["createOscillator"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new FlangerEffect(delay / 1000, modulation / 1000, freq, feedback / 100, mix)); }; Acts.prototype.AddPhaserEffect = function (tag, freq, detune, q, mod, modfreq, mix) { if (api !== API_WEBAUDIO || !context["createOscillator"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new PhaserEffect(freq, detune, q, mod, modfreq, mix)); }; Acts.prototype.AddConvolutionEffect = function (tag, file, norm, mix) { if (api !== API_WEBAUDIO || !context["createConvolver"]) return; var doNormalize = (norm === 0); var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); var b = this.getAudioBuffer(src, false); tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; var fx; if (b.bufferObject) { fx = new ConvolveEffect(b.bufferObject, doNormalize, mix, src); } else { fx = new ConvolveEffect(null, doNormalize, mix, src); b.normalizeWhenReady = doNormalize; b.convolveWhenReady = fx; } addEffectForTag(tag, fx); }; Acts.prototype.AddGainEffect = function (tag, g) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); addEffectForTag(tag, new GainEffect(dbToLinear(g))); }; Acts.prototype.AddMuteEffect = function (tag) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); addEffectForTag(tag, new GainEffect(0)); // re-use gain effect with 0 gain }; Acts.prototype.AddTremoloEffect = function (tag, freq, mix) { if (api !== API_WEBAUDIO || !context["createOscillator"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new TremoloEffect(freq, mix)); }; Acts.prototype.AddRingModEffect = function (tag, freq, mix) { if (api !== API_WEBAUDIO || !context["createOscillator"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new RingModulatorEffect(freq, mix)); }; Acts.prototype.AddDistortionEffect = function (tag, threshold, headroom, drive, makeupgain, mix) { if (api !== API_WEBAUDIO || !context["createWaveShaper"]) return; tag = tag.toLowerCase(); mix = mix / 100; if (mix < 0) mix = 0; if (mix > 1) mix = 1; addEffectForTag(tag, new DistortionEffect(threshold, headroom, drive, makeupgain, mix)); }; Acts.prototype.AddCompressorEffect = function (tag, threshold, knee, ratio, attack, release) { if (api !== API_WEBAUDIO || !context["createDynamicsCompressor"]) return; tag = tag.toLowerCase(); addEffectForTag(tag, new CompressorEffect(threshold, knee, ratio, attack / 1000, release / 1000)); }; Acts.prototype.AddAnalyserEffect = function (tag, fftSize, smoothing) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); addEffectForTag(tag, new AnalyserEffect(fftSize, smoothing)); }; Acts.prototype.RemoveEffects = function (tag) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); var i, len, arr; if (effects.hasOwnProperty(tag)) { arr = effects[tag]; if (arr.length) { for (i = 0, len = arr.length; i < len; i++) arr[i].remove(); cr.clearArray(arr); reconnectEffects(tag); } } }; Acts.prototype.SetEffectParameter = function (tag, index, param, value, ramp, time) { if (api !== API_WEBAUDIO) return; tag = tag.toLowerCase(); index = Math.floor(index); var arr; if (!effects.hasOwnProperty(tag)) return; arr = effects[tag]; if (index < 0 || index >= arr.length) return; arr[index].setParam(param, value, ramp, time); }; Acts.prototype.SetListenerObject = function (obj_) { if (!obj_ || api !== API_WEBAUDIO) return; var inst = obj_.getFirstPicked(); if (!inst) return; this.listenerTracker.setObject(inst); listenerX = inst.x; listenerY = inst.y; }; Acts.prototype.SetListenerZ = function (z) { this.listenerZ = z; }; Acts.prototype.ScheduleNextPlay = function (t) { if (!context) return; // needs Web Audio API this.nextPlayTime = t; }; Acts.prototype.UnloadAudio = function (file) { var is_music = file[1]; var src = this.runtime.files_subfolder + file[0] + (useOgg ? ".ogg" : ".m4a"); var b = this.getAudioBuffer(src, is_music, true /* don't create if missing */); if (!b) return; // not loaded b.release(); cr.arrayFindRemove(audioBuffers, b); }; Acts.prototype.UnloadAudioByName = function (folder, filename) { var is_music = (folder === 1); var src = this.runtime.files_subfolder + filename.toLowerCase() + (useOgg ? ".ogg" : ".m4a"); var b = this.getAudioBuffer(src, is_music, true /* don't create if missing */); if (!b) return; // not loaded b.release(); cr.arrayFindRemove(audioBuffers, b); }; Acts.prototype.UnloadAll = function () { var i, len; for (i = 0, len = audioBuffers.length; i < len; ++i) { audioBuffers[i].release(); }; cr.clearArray(audioBuffers); }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.Duration = function (ret, tag) { getAudioByTag(tag, true); if (taggedAudio.length) ret.set_float(taggedAudio[0].getDuration()); else ret.set_float(0); }; Exps.prototype.PlaybackTime = function (ret, tag) { getAudioByTag(tag, true); if (taggedAudio.length) ret.set_float(taggedAudio[0].getPlaybackTime(true)); else ret.set_float(0); }; Exps.prototype.Volume = function (ret, tag) { getAudioByTag(tag, true); if (taggedAudio.length) { var v = taggedAudio[0].getVolume(); ret.set_float(linearToDb(v)); } else ret.set_float(0); }; Exps.prototype.MasterVolume = function (ret) { ret.set_float(linearToDb(masterVolume)); }; Exps.prototype.EffectCount = function (ret, tag) { tag = tag.toLowerCase(); var arr = null; if (effects.hasOwnProperty(tag)) arr = effects[tag]; ret.set_int(arr ? arr.length : 0); }; function getAnalyser(tag, index) { var arr = null; if (effects.hasOwnProperty(tag)) arr = effects[tag]; if (arr && index >= 0 && index < arr.length && arr[index].freqBins) return arr[index]; else return null; }; Exps.prototype.AnalyserFreqBinCount = function (ret, tag, index) { tag = tag.toLowerCase(); index = Math.floor(index); var analyser = getAnalyser(tag, index); ret.set_int(analyser ? analyser.node["frequencyBinCount"] : 0); }; Exps.prototype.AnalyserFreqBinAt = function (ret, tag, index, bin) { tag = tag.toLowerCase(); index = Math.floor(index); bin = Math.floor(bin); var analyser = getAnalyser(tag, index); if (!analyser) ret.set_float(0); else if (bin < 0 || bin >= analyser.node["frequencyBinCount"]) ret.set_float(0); else ret.set_float(analyser.freqBins[bin]); }; Exps.prototype.AnalyserPeakLevel = function (ret, tag, index) { tag = tag.toLowerCase(); index = Math.floor(index); var analyser = getAnalyser(tag, index); if (analyser) ret.set_float(analyser.peak); else ret.set_float(0); }; Exps.prototype.AnalyserRMSLevel = function (ret, tag, index) { tag = tag.toLowerCase(); index = Math.floor(index); var analyser = getAnalyser(tag, index); if (analyser) ret.set_float(analyser.rms); else ret.set_float(0); }; Exps.prototype.SampleRate = function (ret) { ret.set_int(context ? context.sampleRate : 0); }; Exps.prototype.CurrentTime = function (ret) { ret.set_float(context ? context.currentTime : cr.performance_now()); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Browser = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Browser.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; var offlineScriptReady = false; var browserPluginReady = false; document.addEventListener("DOMContentLoaded", function () { if (window["C2_RegisterSW"] && navigator.serviceWorker) { var offlineClientScript = document.createElement("script"); offlineClientScript.onload = function () { offlineScriptReady = true; checkReady() }; offlineClientScript.src = "offlineClient.js"; document.head.appendChild(offlineClientScript); } }); var browserInstance = null; typeProto.onAppBegin = function () { browserPluginReady = true; checkReady(); }; function checkReady() { if (offlineScriptReady && browserPluginReady && window["OfflineClientInfo"]) { window["OfflineClientInfo"]["SetMessageCallback"](function (e) { browserInstance.onSWMessage(e); }); } }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { var self = this; window.addEventListener("resize", function () { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnResize, self); }); browserInstance = this; if (typeof navigator.onLine !== "undefined") { window.addEventListener("online", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnOnline, self); }); window.addEventListener("offline", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnOffline, self); }); } if (typeof window.applicationCache !== "undefined") { window.applicationCache.addEventListener('updateready', function() { self.runtime.loadingprogress = 1; self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateReady, self); }); window.applicationCache.addEventListener('progress', function(e) { self.runtime.loadingprogress = (e["loaded"] / e["total"]) || 0; }); } if (!this.runtime.isDirectCanvas) { document.addEventListener("appMobi.device.update.available", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateReady, self); }); document.addEventListener("backbutton", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnBackButton, self); }); document.addEventListener("menubutton", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnMenuButton, self); }); document.addEventListener("searchbutton", function() { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnSearchButton, self); }); document.addEventListener("tizenhwkey", function (e) { var ret; switch (e["keyName"]) { case "back": ret = self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnBackButton, self); if (!ret) { if (window["tizen"]) window["tizen"]["application"]["getCurrentApplication"]()["exit"](); } break; case "menu": ret = self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnMenuButton, self); if (!ret) e.preventDefault(); break; } }); } if (this.runtime.isWindows10 && typeof Windows !== "undefined") { Windows["UI"]["Core"]["SystemNavigationManager"]["getForCurrentView"]().addEventListener("backrequested", function (e) { var ret = self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnBackButton, self); if (ret) e.handled = true; }); } else if (this.runtime.isWinJS && WinJS["Application"]) { WinJS["Application"]["onbackclick"] = function (e) { return !!self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnBackButton, self); }; } this.runtime.addSuspendCallback(function(s) { if (s) { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnPageHidden, self); } else { self.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnPageVisible, self); } }); this.is_arcade = (typeof window["is_scirra_arcade"] !== "undefined"); }; instanceProto.onSWMessage = function (e) { var messageType = e.data.type; if (messageType === "downloading-update") this.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateFound, this); else if (messageType === "update-ready" || messageType === "update-pending") this.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnUpdateReady, this); else if (messageType === "offline-ready") this.runtime.trigger(cr.plugins_.Browser.prototype.cnds.OnOfflineReady, this); }; var batteryManager = null; var loadedBatteryManager = false; function maybeLoadBatteryManager() { if (loadedBatteryManager) return; if (!navigator["getBattery"]) return; var promise = navigator["getBattery"](); loadedBatteryManager = true; if (promise) { promise.then(function (manager) { batteryManager = manager; }); } }; function Cnds() {}; Cnds.prototype.CookiesEnabled = function() { return navigator ? navigator.cookieEnabled : false; }; Cnds.prototype.IsOnline = function() { return navigator ? navigator.onLine : false; }; Cnds.prototype.HasJava = function() { return navigator ? navigator.javaEnabled() : false; }; Cnds.prototype.OnOnline = function() { return true; }; Cnds.prototype.OnOffline = function() { return true; }; Cnds.prototype.IsDownloadingUpdate = function () { if (typeof window["applicationCache"] === "undefined") return false; else return window["applicationCache"]["status"] === window["applicationCache"]["DOWNLOADING"]; }; Cnds.prototype.OnUpdateReady = function () { return true; }; Cnds.prototype.PageVisible = function () { return !this.runtime.isSuspended; }; Cnds.prototype.OnPageVisible = function () { return true; }; Cnds.prototype.OnPageHidden = function () { return true; }; Cnds.prototype.OnResize = function () { return true; }; Cnds.prototype.IsFullscreen = function () { return !!(document["mozFullScreen"] || document["webkitIsFullScreen"] || document["fullScreen"] || this.runtime.isNodeFullscreen); }; Cnds.prototype.OnBackButton = function () { return true; }; Cnds.prototype.OnMenuButton = function () { return true; }; Cnds.prototype.OnSearchButton = function () { return true; }; Cnds.prototype.IsMetered = function () { var connection = navigator["connection"] || navigator["mozConnection"] || navigator["webkitConnection"]; if (!connection) return false; return !!connection["metered"]; }; Cnds.prototype.IsCharging = function () { var battery = navigator["battery"] || navigator["mozBattery"] || navigator["webkitBattery"]; if (battery) { return !!battery["charging"] } else { maybeLoadBatteryManager(); if (batteryManager) { return !!batteryManager["charging"]; } else { return true; // if unknown, default to charging (powered) } } }; Cnds.prototype.IsPortraitLandscape = function (p) { var current = (window.innerWidth <= window.innerHeight ? 0 : 1); return current === p; }; Cnds.prototype.SupportsFullscreen = function () { if (this.runtime.isNodeWebkit) return true; var elem = this.runtime.canvasdiv || this.runtime.canvas; return !!(elem["requestFullscreen"] || elem["mozRequestFullScreen"] || elem["msRequestFullscreen"] || elem["webkitRequestFullScreen"]); }; Cnds.prototype.OnUpdateFound = function () { return true; }; Cnds.prototype.OnUpdateReady = function () { return true; }; Cnds.prototype.OnOfflineReady = function () { return true; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Alert = function (msg) { if (!this.runtime.isDomFree) alert(msg.toString()); }; Acts.prototype.Close = function () { if (this.runtime.isCocoonJs) CocoonJS["App"]["forceToFinish"](); else if (window["tizen"]) window["tizen"]["application"]["getCurrentApplication"]()["exit"](); else if (navigator["app"] && navigator["app"]["exitApp"]) navigator["app"]["exitApp"](); else if (navigator["device"] && navigator["device"]["exitApp"]) navigator["device"]["exitApp"](); else if (!this.is_arcade && !this.runtime.isDomFree) window.close(); }; Acts.prototype.Focus = function () { if (this.runtime.isNodeWebkit) { var win = window["nwgui"]["Window"]["get"](); win["focus"](); } else if (!this.is_arcade && !this.runtime.isDomFree) window.focus(); }; Acts.prototype.Blur = function () { if (this.runtime.isNodeWebkit) { var win = window["nwgui"]["Window"]["get"](); win["blur"](); } else if (!this.is_arcade && !this.runtime.isDomFree) window.blur(); }; Acts.prototype.GoBack = function () { if (navigator["app"] && navigator["app"]["backHistory"]) navigator["app"]["backHistory"](); else if (!this.is_arcade && !this.runtime.isDomFree && window.back) window.back(); }; Acts.prototype.GoForward = function () { if (!this.is_arcade && !this.runtime.isDomFree && window.forward) window.forward(); }; Acts.prototype.GoHome = function () { if (!this.is_arcade && !this.runtime.isDomFree && window.home) window.home(); }; Acts.prototype.GoToURL = function (url, target) { }; Acts.prototype.GoToURLWindow = function (url, tag) { }; Acts.prototype.Reload = function () { if (!this.is_arcade && !this.runtime.isDomFree) window.location.reload(); }; var firstRequestFullscreen = true; var crruntime = null; function onFullscreenError(e) { if (console && console.warn) console.warn("Fullscreen request failed: ", e); crruntime["setSize"](window.innerWidth, window.innerHeight); }; Acts.prototype.RequestFullScreen = function (stretchmode) { if (this.runtime.isDomFree) { cr.logexport("[Construct 2] Requesting fullscreen is not supported on this platform - the request has been ignored"); return; } if (stretchmode >= 2) stretchmode += 1; if (stretchmode === 6) stretchmode = 2; if (this.runtime.isNodeWebkit) { if (this.runtime.isDebug) { debuggerFullscreen(true); } else if (!this.runtime.isNodeFullscreen && window["nwgui"]) { window["nwgui"]["Window"]["get"]()["enterFullscreen"](); this.runtime.isNodeFullscreen = true; this.runtime.fullscreen_scaling = (stretchmode >= 2 ? stretchmode : 0); } } else { if (document["mozFullScreen"] || document["webkitIsFullScreen"] || !!document["msFullscreenElement"] || document["fullScreen"] || document["fullScreenElement"]) { return; } this.runtime.fullscreen_scaling = (stretchmode >= 2 ? stretchmode : 0); var elem = this.runtime.canvasdiv || this.runtime.canvas; if (firstRequestFullscreen) { firstRequestFullscreen = false; crruntime = this.runtime; elem.addEventListener("mozfullscreenerror", onFullscreenError); elem.addEventListener("webkitfullscreenerror", onFullscreenError); elem.addEventListener("MSFullscreenError", onFullscreenError); elem.addEventListener("fullscreenerror", onFullscreenError); } if (elem["requestFullscreen"]) elem["requestFullscreen"](); else if (elem["mozRequestFullScreen"]) elem["mozRequestFullScreen"](); else if (elem["msRequestFullscreen"]) elem["msRequestFullscreen"](); else if (elem["webkitRequestFullScreen"]) { if (typeof Element !== "undefined" && typeof Element["ALLOW_KEYBOARD_INPUT"] !== "undefined") elem["webkitRequestFullScreen"](Element["ALLOW_KEYBOARD_INPUT"]); else elem["webkitRequestFullScreen"](); } } }; Acts.prototype.CancelFullScreen = function () { if (this.runtime.isDomFree) { cr.logexport("[Construct 2] Exiting fullscreen is not supported on this platform - the request has been ignored"); return; } if (this.runtime.isNodeWebkit) { if (this.runtime.isDebug) { debuggerFullscreen(false); } else if (this.runtime.isNodeFullscreen && window["nwgui"]) { window["nwgui"]["Window"]["get"]()["leaveFullscreen"](); this.runtime.isNodeFullscreen = false; } } else { if (document["exitFullscreen"]) document["exitFullscreen"](); else if (document["mozCancelFullScreen"]) document["mozCancelFullScreen"](); else if (document["msExitFullscreen"]) document["msExitFullscreen"](); else if (document["webkitCancelFullScreen"]) document["webkitCancelFullScreen"](); } }; Acts.prototype.Vibrate = function (pattern_) { try { var arr = pattern_.split(","); var i, len; for (i = 0, len = arr.length; i < len; i++) { arr[i] = parseInt(arr[i], 10); } if (navigator["vibrate"]) navigator["vibrate"](arr); else if (navigator["mozVibrate"]) navigator["mozVibrate"](arr); else if (navigator["webkitVibrate"]) navigator["webkitVibrate"](arr); else if (navigator["msVibrate"]) navigator["msVibrate"](arr); } catch (e) {} }; Acts.prototype.InvokeDownload = function (url_, filename_) { }; Acts.prototype.InvokeDownloadString = function (str_, mimetype_, filename_) { }; Acts.prototype.ConsoleLog = function (type_, msg_) { if (typeof console === "undefined") return; if (type_ === 0 && console.log) console.log(msg_.toString()); if (type_ === 1 && console.warn) console.warn(msg_.toString()); if (type_ === 2 && console.error) console.error(msg_.toString()); }; Acts.prototype.ConsoleGroup = function (name_) { if (console && console.group) console.group(name_); }; Acts.prototype.ConsoleGroupEnd = function () { if (console && console.groupEnd) console.groupEnd(); }; Acts.prototype.ExecJs = function (js_) { try { if (eval) eval(js_); } catch (e) { if (console && console.error) console.error("Error executing Javascript: ", e); } }; var orientations = [ "portrait", "landscape", "portrait-primary", "portrait-secondary", "landscape-primary", "landscape-secondary" ]; Acts.prototype.LockOrientation = function (o) { o = Math.floor(o); if (o < 0 || o >= orientations.length) return; this.runtime.autoLockOrientation = false; var orientation = orientations[o]; if (screen["orientation"] && screen["orientation"]["lock"]) screen["orientation"]["lock"](orientation); else if (screen["lockOrientation"]) screen["lockOrientation"](orientation); else if (screen["webkitLockOrientation"]) screen["webkitLockOrientation"](orientation); else if (screen["mozLockOrientation"]) screen["mozLockOrientation"](orientation); else if (screen["msLockOrientation"]) screen["msLockOrientation"](orientation); }; Acts.prototype.UnlockOrientation = function () { this.runtime.autoLockOrientation = false; if (screen["orientation"] && screen["orientation"]["unlock"]) screen["orientation"]["unlock"](); else if (screen["unlockOrientation"]) screen["unlockOrientation"](); else if (screen["webkitUnlockOrientation"]) screen["webkitUnlockOrientation"](); else if (screen["mozUnlockOrientation"]) screen["mozUnlockOrientation"](); else if (screen["msUnlockOrientation"]) screen["msUnlockOrientation"](); }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.URL = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.toString()); }; Exps.prototype.Protocol = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.protocol); }; Exps.prototype.Domain = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.hostname); }; Exps.prototype.PathName = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.pathname); }; Exps.prototype.Hash = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.hash); }; Exps.prototype.Referrer = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : document.referrer); }; Exps.prototype.Title = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : document.title); }; Exps.prototype.Name = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : navigator.appName); }; Exps.prototype.Version = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : navigator.appVersion); }; Exps.prototype.Language = function (ret) { if (navigator && navigator.language) ret.set_string(navigator.language); else ret.set_string(""); }; Exps.prototype.Platform = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : navigator.platform); }; Exps.prototype.Product = function (ret) { if (navigator && navigator.product) ret.set_string(navigator.product); else ret.set_string(""); }; Exps.prototype.Vendor = function (ret) { if (navigator && navigator.vendor) ret.set_string(navigator.vendor); else ret.set_string(""); }; Exps.prototype.UserAgent = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : navigator.userAgent); }; Exps.prototype.QueryString = function (ret) { ret.set_string(this.runtime.isDomFree ? "" : window.location.search); }; Exps.prototype.QueryParam = function (ret, paramname) { if (this.runtime.isDomFree) { ret.set_string(""); return; } var match = RegExp('[?&]' + paramname + '=([^&]*)').exec(window.location.search); if (match) ret.set_string(decodeURIComponent(match[1].replace(/\+/g, ' '))); else ret.set_string(""); }; Exps.prototype.Bandwidth = function (ret) { var connection = navigator["connection"] || navigator["mozConnection"] || navigator["webkitConnection"]; if (!connection) ret.set_float(Number.POSITIVE_INFINITY); else { if (typeof connection["bandwidth"] !== "undefined") ret.set_float(connection["bandwidth"]); else if (typeof connection["downlinkMax"] !== "undefined") ret.set_float(connection["downlinkMax"]); else ret.set_float(Number.POSITIVE_INFINITY); } }; Exps.prototype.ConnectionType = function (ret) { var connection = navigator["connection"] || navigator["mozConnection"] || navigator["webkitConnection"]; if (!connection) ret.set_string("unknown"); else { ret.set_string(connection["type"] || "unknown"); } }; Exps.prototype.BatteryLevel = function (ret) { var battery = navigator["battery"] || navigator["mozBattery"] || navigator["webkitBattery"]; if (battery) { ret.set_float(battery["level"]); } else { maybeLoadBatteryManager(); if (batteryManager) { ret.set_float(batteryManager["level"]); } else { ret.set_float(1); // not supported/unknown: assume charged } } }; Exps.prototype.BatteryTimeLeft = function (ret) { var battery = navigator["battery"] || navigator["mozBattery"] || navigator["webkitBattery"]; if (battery) { ret.set_float(battery["dischargingTime"]); } else { maybeLoadBatteryManager(); if (batteryManager) { ret.set_float(batteryManager["dischargingTime"]); } else { ret.set_float(Number.POSITIVE_INFINITY); // not supported/unknown: assume infinite time left } } }; Exps.prototype.ExecJS = function (ret, js_) { if (!eval) { ret.set_any(0); return; } var result = 0; try { result = eval(js_); } catch (e) { if (console && console.error) console.error("Error executing Javascript: ", e); } if (typeof result === "number") ret.set_any(result); else if (typeof result === "string") ret.set_any(result); else if (typeof result === "boolean") ret.set_any(result ? 1 : 0); else ret.set_any(0); }; Exps.prototype.ScreenWidth = function (ret) { ret.set_int(screen.width); }; Exps.prototype.ScreenHeight = function (ret) { ret.set_int(screen.height); }; Exps.prototype.DevicePixelRatio = function (ret) { ret.set_float(this.runtime.devicePixelRatio); }; Exps.prototype.WindowInnerWidth = function (ret) { ret.set_int(window.innerWidth); }; Exps.prototype.WindowInnerHeight = function (ret) { ret.set_int(window.innerHeight); }; Exps.prototype.WindowOuterWidth = function (ret) { ret.set_int(window.outerWidth); }; Exps.prototype.WindowOuterHeight = function (ret) { ret.set_int(window.outerHeight); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Function = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Function.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; var funcStack = []; var funcStackPtr = -1; var isInPreview = false; // set in onCreate function FuncStackEntry() { this.name = ""; this.retVal = 0; this.params = []; }; function pushFuncStack() { funcStackPtr++; if (funcStackPtr === funcStack.length) funcStack.push(new FuncStackEntry()); return funcStack[funcStackPtr]; }; function getCurrentFuncStack() { if (funcStackPtr < 0) return null; return funcStack[funcStackPtr]; }; function getOneAboveFuncStack() { if (!funcStack.length) return null; var i = funcStackPtr + 1; if (i >= funcStack.length) i = funcStack.length - 1; return funcStack[i]; }; function popFuncStack() { ; funcStackPtr--; }; instanceProto.onCreate = function() { isInPreview = (typeof cr_is_preview !== "undefined"); var self = this; window["c2_callFunction"] = function (name_, params_) { var i, len, v; var fs = pushFuncStack(); fs.name = name_.toLowerCase(); fs.retVal = 0; if (params_) { fs.params.length = params_.length; for (i = 0, len = params_.length; i < len; ++i) { v = params_[i]; if (typeof v === "number" || typeof v === "string") fs.params[i] = v; else if (typeof v === "boolean") fs.params[i] = (v ? 1 : 0); else fs.params[i] = 0; } } else { cr.clearArray(fs.params); } self.runtime.trigger(cr.plugins_.Function.prototype.cnds.OnFunction, self, fs.name); popFuncStack(); return fs.retVal; }; }; function Cnds() {}; Cnds.prototype.OnFunction = function (name_) { var fs = getCurrentFuncStack(); if (!fs) return false; return cr.equals_nocase(name_, fs.name); }; Cnds.prototype.CompareParam = function (index_, cmp_, value_) { var fs = getCurrentFuncStack(); if (!fs) return false; index_ = cr.floor(index_); if (index_ < 0 || index_ >= fs.params.length) return false; return cr.do_cmp(fs.params[index_], cmp_, value_); }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.CallFunction = function (name_, params_) { var fs = pushFuncStack(); fs.name = name_.toLowerCase(); fs.retVal = 0; cr.shallowAssignArray(fs.params, params_); var ran = this.runtime.trigger(cr.plugins_.Function.prototype.cnds.OnFunction, this, fs.name); if (isInPreview && !ran) { ; } popFuncStack(); }; Acts.prototype.SetReturnValue = function (value_) { var fs = getCurrentFuncStack(); if (fs) fs.retVal = value_; else ; }; Acts.prototype.CallExpression = function (unused) { }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.ReturnValue = function (ret) { var fs = getOneAboveFuncStack(); if (fs) ret.set_any(fs.retVal); else ret.set_int(0); }; Exps.prototype.ParamCount = function (ret) { var fs = getCurrentFuncStack(); if (fs) ret.set_int(fs.params.length); else { ; ret.set_int(0); } }; Exps.prototype.Param = function (ret, index_) { index_ = cr.floor(index_); var fs = getCurrentFuncStack(); if (fs) { if (index_ >= 0 && index_ < fs.params.length) { ret.set_any(fs.params[index_]); } else { ; ret.set_int(0); } } else { ; ret.set_int(0); } }; Exps.prototype.Call = function (ret, name_) { var fs = pushFuncStack(); fs.name = name_.toLowerCase(); fs.retVal = 0; cr.clearArray(fs.params); var i, len; for (i = 2, len = arguments.length; i < len; i++) fs.params.push(arguments[i]); var ran = this.runtime.trigger(cr.plugins_.Function.prototype.cnds.OnFunction, this, fs.name); if (isInPreview && !ran) { ; } popFuncStack(); ret.set_any(fs.retVal); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.IDNet = function(runtime) { this.runtime = runtime; }; (function() { var pluginProto = cr.plugins_.IDNet.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; var _document = null; var _unsafeWindow = null; var idNetRuntime = null; var idNetInst = null; var idnetUserName = "Guest"; var authorized = false; var userAuthorized = false; var idnetSessionKey = ""; var onlineSavesData = ""; var isBlacklisted = 0; var isSponsor = 0; var gotSaveData = 0; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; idNetRuntime = this.runtime; idNetInst = this; this._document = window.document; this._unsafeWindow = this._document.defaultView; }; function ShowLeaderBoardCallback(response) { } var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { }; instanceProto.onDestroy = function () { }; instanceProto.draw = function(ctx) { }; instanceProto.drawGL = function (glw) { }; function Cnds() {}; Cnds.prototype.isAuthorized = function () { return idNetInst.authorized; }; Cnds.prototype.isNotAuthorized = function () { return !idNetInst.authorized; }; Cnds.prototype.UserIsAuthorized = function () { return idNetInst.userAuthorized; }; Cnds.prototype.UserIsNotAuthorized = function () { return !idNetInst.userAuthorized; }; Cnds.prototype.blacklisted = function () { return idNetInst.isBlacklisted; }; Cnds.prototype.sponsored = function () { return idNetInst.isSponsor; }; Cnds.prototype.dataReady = function () { if (idNetInst.gotSaveData === 1) { idNetInst.gotSaveData = 0; return 1; } }; Cnds.prototype.menuVisible = function() { if (window.ID && ID.isVisible()) { return 1; } }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Inititalize = function(appid_) {/* console.log('init with appid ' + appid_); (function(d, s, id){ var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) {return;} js = d.createElement(s); js.id = id; js.src = document.location.protocol == 'https:' ? "https://scdn.id.net/api/sdk.js" : "http://cdn.id.net/api/sdk.js"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'id-jssdk')); window.idAsyncInit = function() { ID.Event.subscribe("id.init",function() { window.idnet_autologin = function(response) { if (response != null && response.user != null) { console.log("id.net autologin"); idNetInst.idnetUserName = response.user.nickname; idNetInst.userAuthorized = true; ID.login(function(response) {}); } } var fjs = document.head.getElementsByTagName('meta')[0]; if (document.getElementById('id-autologin')) { var js_auto = document.getElementById('id-autologin'); } else { var js_auto = document.createElement('script'); js_auto.id = 'id-autologin'; js_auto.src = "https://www.id.net/api/user_data/autologin?app_id=" + appid_ + "&callback=window.idnet_autologin"; fjs.parentNode.insertBefore(js_auto, fjs); } console.log("id.net initialized"); ID.Protection.isBlacklisted(function(blacklisted) { idNetInst.isBlacklisted = blacklisted; }); ID.Protection.isSponsor(function(sponsor) { idNetInst.isSponsor = sponsor; }); }); ID.init({ appId : appid_ }); idNetInst.authorized = true; }*/ }; Acts.prototype.RegisterPopup = function() {/* console.log("open registration menu"); if (idNetInst.authorized) ID.register(function (response) { if(response == null) { } else { console.log("Registration complete"); idNetInst.idnetUserName = response.authResponse.details.nickname; idNetInst.userAuthorized = true; } });*/ }; Acts.prototype.LoginPopup = function() {/* console.log("open login menu"); if (idNetInst.authorized){ ID.login(function (response) { if(response == null) { } else { console.log("Login complete"); idNetInst.idnetUserName = response.authResponse.details.nickname; idNetInst.userAuthorized = true; } }); }*/ }; Acts.prototype.ShowLeaderBoard = function(table, mode, highest, allowduplicates) {/* if (idNetInst.authorized) { console.log('oi') var options = { table: table, mode: mode, highest: !!highest, allowduplicates: !!allowduplicates }; ID.GameAPI.Leaderboards.list(options); }*/ }; Acts.prototype.SubmitScore = function(score, table, allowduplicates, highest, playername) {/* if (idNetInst.authorized) { var score = { table: table, points: score, allowduplicates: !!allowduplicates, highest: !!highest, playername: playername || idNetInst.idnetUserName }; ID.GameAPI.Leaderboards.save(score, function(response) { console.log("score submitted", response); }); }*/ }; Acts.prototype.SubmitProfileImage = function(image_) {/* if (idNetInst.authorized) ID.submit_image(image_, function(response){ console.log("screenshot submitted", response); });*/ }; Acts.prototype.AchievementSave = function(achievementTitle_, achievementKey_, overwrite_, allowduplicates_) {/* if (idNetInst.authorized) { var achievementData = { achievement: achievementTitle_, achievementkey: achievementKey_, overwrite: overwrite_, allowduplicates: allowduplicates_ }; ID.GameAPI.Achievements.save(achievementData, function(response) { console.log("achievement saved", response); }); }*/ }; Acts.prototype.ShowAchievements = function() {/* if (idNetInst.authorized) { ID.GameAPI.Achievements.list(); }*/ }; Acts.prototype.OnlineSavesSave = function(key_, value_) {/* if (idNetInst.authorized) { ID.api('user_data/submit', 'POST', {key: key_, value: value_}, function(response) { console.log("save submitted", response); }); }*/ }; Acts.prototype.OnlineSavesRemove = function(key_) {/* if (idNetInst.authorized) { ID.api('user_data/remove', 'POST', {key: key_}, function(response) { console.log("save deleted", response); }); }*/ }; Acts.prototype.OnlineSavesLoad = function(key_) {/* if (idNetInst.authorized) { ID.api('user_data/retrieve', 'POST', {key: key_}, function(response) { if(response) { idNetInst.onlineSavesData = response.jsondata; idNetInst.gotSaveData = 1; console.log("save loaded", response); } }); }*/ }; Acts.prototype.CheckIsBlacklisted = function () { ID.Protection.isBlacklisted(function(blacklisted){ console.log("check blacklist called", blacklisted); idNetInst.isBlacklisted = blacklisted; }); }; Acts.prototype.CheckIsSponsor = function () { ID.Protection.isSponsor(function(sponsor){ console.log("check sponser called", sponser); idNetInst.isSponsor = sponsor; }); }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.UserName = function(ret) { if(idNetInst.idnetUserName != undefined) { ret.set_string(idNetInst.idnetUserName); } }; Exps.prototype.SessionKey = function(ret) { if(idnetSessionKey != undefined) { ret.set_string(idnetSessionKey); } }; Exps.prototype.GateOnlineSavesData = function (ret) { ret.set_string(String(idNetInst.onlineSavesData)); }; Exps.prototype.GetIsBlacklisted = function(ret) { // if(idNetInst.isBlacklisted) { // ret.set_int(1); // } else { // ret.set_int(0); // } }; Exps.prototype.GetIsSponsor = function(ret) { if(idNetInst.isSponsor) { ret.set_int(1); } else { ret.set_int(0); } }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Keyboard = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Keyboard.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; this.keyMap = new Array(256); // stores key up/down state this.usedKeys = new Array(256); this.triggerKey = 0; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { var self = this; if (!this.runtime.isDomFree) { jQuery(document).keydown( function(info) { self.onKeyDown(info); } ); jQuery(document).keyup( function(info) { self.onKeyUp(info); } ); } }; var keysToBlockWhenFramed = [32, 33, 34, 35, 36, 37, 38, 39, 40, 44]; instanceProto.onKeyDown = function (info) { var alreadyPreventedDefault = false; if (window != window.top && keysToBlockWhenFramed.indexOf(info.which) > -1) { info.preventDefault(); alreadyPreventedDefault = true; info.stopPropagation(); } if (this.keyMap[info.which]) { if (this.usedKeys[info.which] && !alreadyPreventedDefault) info.preventDefault(); return; } this.keyMap[info.which] = true; this.triggerKey = info.which; this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnAnyKey, this); var eventRan = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKey, this); var eventRan2 = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKeyCode, this); this.runtime.isInUserInputEvent = false; if (eventRan || eventRan2) { this.usedKeys[info.which] = true; if (!alreadyPreventedDefault) info.preventDefault(); } }; instanceProto.onKeyUp = function (info) { this.keyMap[info.which] = false; this.triggerKey = info.which; this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnAnyKeyReleased, this); var eventRan = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKeyReleased, this); var eventRan2 = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKeyCodeReleased, this); this.runtime.isInUserInputEvent = false; if (eventRan || eventRan2 || this.usedKeys[info.which]) { this.usedKeys[info.which] = true; info.preventDefault(); } }; instanceProto.onWindowBlur = function () { var i; for (i = 0; i < 256; ++i) { if (!this.keyMap[i]) continue; // key already up this.keyMap[i] = false; this.triggerKey = i; this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnAnyKeyReleased, this); var eventRan = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKeyReleased, this); var eventRan2 = this.runtime.trigger(cr.plugins_.Keyboard.prototype.cnds.OnKeyCodeReleased, this); if (eventRan || eventRan2) this.usedKeys[i] = true; } }; instanceProto.saveToJSON = function () { return { "triggerKey": this.triggerKey }; }; instanceProto.loadFromJSON = function (o) { this.triggerKey = o["triggerKey"]; }; function Cnds() {}; Cnds.prototype.IsKeyDown = function(key) { return this.keyMap[key]; }; Cnds.prototype.OnKey = function(key) { return (key === this.triggerKey); }; Cnds.prototype.OnAnyKey = function(key) { return true; }; Cnds.prototype.OnAnyKeyReleased = function(key) { return true; }; Cnds.prototype.OnKeyReleased = function(key) { return (key === this.triggerKey); }; Cnds.prototype.IsKeyCodeDown = function(key) { key = Math.floor(key); if (key < 0 || key >= this.keyMap.length) return false; return this.keyMap[key]; }; Cnds.prototype.OnKeyCode = function(key) { return (key === this.triggerKey); }; Cnds.prototype.OnKeyCodeReleased = function(key) { return (key === this.triggerKey); }; pluginProto.cnds = new Cnds(); function Acts() {}; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.LastKeyCode = function (ret) { ret.set_int(this.triggerKey); }; function fixedStringFromCharCode(kc) { kc = Math.floor(kc); switch (kc) { case 8: return "backspace"; case 9: return "tab"; case 13: return "enter"; case 16: return "shift"; case 17: return "control"; case 18: return "alt"; case 19: return "pause"; case 20: return "capslock"; case 27: return "esc"; case 33: return "pageup"; case 34: return "pagedown"; case 35: return "end"; case 36: return "home"; case 37: return "โ†"; case 38: return "โ†‘"; case 39: return "โ†’"; case 40: return "โ†“"; case 45: return "insert"; case 46: return "del"; case 91: return "left window key"; case 92: return "right window key"; case 93: return "select"; case 96: return "numpad 0"; case 97: return "numpad 1"; case 98: return "numpad 2"; case 99: return "numpad 3"; case 100: return "numpad 4"; case 101: return "numpad 5"; case 102: return "numpad 6"; case 103: return "numpad 7"; case 104: return "numpad 8"; case 105: return "numpad 9"; case 106: return "numpad *"; case 107: return "numpad +"; case 109: return "numpad -"; case 110: return "numpad ."; case 111: return "numpad /"; case 112: return "F1"; case 113: return "F2"; case 114: return "F3"; case 115: return "F4"; case 116: return "F5"; case 117: return "F6"; case 118: return "F7"; case 119: return "F8"; case 120: return "F9"; case 121: return "F10"; case 122: return "F11"; case 123: return "F12"; case 144: return "numlock"; case 145: return "scroll lock"; case 186: return ";"; case 187: return "="; case 188: return ","; case 189: return "-"; case 190: return "."; case 191: return "/"; case 192: return "'"; case 219: return "["; case 220: return "\\"; case 221: return "]"; case 222: return "#"; case 223: return "`"; default: return String.fromCharCode(kc); } }; Exps.prototype.StringFromKeyCode = function (ret, kc) { ret.set_string(fixedStringFromCharCode(kc)); }; pluginProto.exps = new Exps(); }()); ; ; var localForageInitFailed = false; try { /*! localForage -- Offline Storage, Improved Version 1.4.0 https://mozilla.github.io/localForage (c) 2013-2015 Mozilla, Apache License 2.0 */ !function(){var a,b,c,d;!function(){var e={},f={};a=function(a,b,c){e[a]={deps:b,callback:c}},d=c=b=function(a){function c(b){if("."!==b.charAt(0))return b;for(var c=b.split("/"),d=a.split("/").slice(0,-1),e=0,f=c.length;f>e;e++){var g=c[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(d._eak_seen=e,f[a])return f[a];if(f[a]={},!e[a])throw new Error("Could not find module "+a);for(var g,h=e[a],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)"exports"===i[l]?k.push(g={}):k.push(b(c(i[l])));var n=j.apply(this,k);return f[a]=g||n}}(),a("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;jc;c++){var e=a[c];this.supports(e)&&b.push(e)}return b},a.prototype._wrapLibraryMethodsWithReady=function(){for(var a=0;ae;e++)d[e]=a.charCodeAt(e);return c}function d(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.withCredentials=!0,d.responseType="arraybuffer",d.onreadystatechange=function(){return 4===d.readyState?200===d.status?b({response:d.response,type:d.getResponseHeader("Content-Type")}):void c({status:d.status,response:d.response}):void 0},d.send()})}function e(a){return new Promise(function(c,e){var f=b([""],{type:"image/png"}),g=a.transaction([D],"readwrite");g.objectStore(D).put(f,"key"),g.oncomplete=function(){var b=a.transaction([D],"readwrite"),f=b.objectStore(D).get("key");f.onerror=e,f.onsuccess=function(a){var b=a.target.result,e=URL.createObjectURL(b);d(e).then(function(a){c(!(!a||"image/png"!==a.type))},function(){c(!1)}).then(function(){URL.revokeObjectURL(e)})}},g.onerror=g.onabort=e})["catch"](function(){return!1})}function f(a){return"boolean"==typeof B?Promise.resolve(B):e(a).then(function(a){return B=a})}function g(a){return new Promise(function(b,c){var d=new FileReader;d.onerror=c,d.onloadend=function(c){var d=btoa(c.target.result||"");b({__local_forage_encoded_blob:!0,data:d,type:a.type})},d.readAsBinaryString(a)})}function h(a){var d=c(atob(a.data));return b([d],{type:a.type})}function i(a){return a&&a.__local_forage_encoded_blob}function j(a){var b=this,c=b._initReady().then(function(){var a=C[b._dbInfo.name];return a&&a.dbReady?a.dbReady:void 0});return c.then(a,a),c}function k(a){var b=C[a.name],c={};c.promise=new Promise(function(a){c.resolve=a}),b.deferredOperations.push(c),b.dbReady?b.dbReady=b.dbReady.then(function(){return c.promise}):b.dbReady=c.promise}function l(a){var b=C[a.name],c=b.deferredOperations.pop();c&&c.resolve()}function m(a){function b(){return Promise.resolve()}var c=this,d={db:null};if(a)for(var e in a)d[e]=a[e];C||(C={});var f=C[d.name];f||(f={forages:[],db:null,dbReady:null,deferredOperations:[]},C[d.name]=f),f.forages.push(c),c._initReady||(c._initReady=c.ready,c.ready=j);for(var g=[],h=0;hb.db.version;if(e&&(b.version!==c&&a.console.warn('The database "'+b.name+"\" can't be downgraded from version "+b.db.version+" to version "+b.version+"."),b.version=b.db.version),f||d){if(d){var g=b.db.version+1;g>b.version&&(b.version=g)}return!0}return!1}function r(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=new Promise(function(a,c){d.ready().then(function(){var e=d._dbInfo,f=e.db.transaction(e.storeName,"readonly").objectStore(e.storeName),g=f.get(b);g.onsuccess=function(){var b=g.result;void 0===b&&(b=null),i(b)&&(b=h(b)),a(b)},g.onerror=function(){c(g.error)}})["catch"](c)});return z(e,c),e}function s(a,b){var c=this,d=new Promise(function(b,d){c.ready().then(function(){var e=c._dbInfo,f=e.db.transaction(e.storeName,"readonly").objectStore(e.storeName),g=f.openCursor(),j=1;g.onsuccess=function(){var c=g.result;if(c){var d=c.value;i(d)&&(d=h(d));var e=a(d,c.key,j++);void 0!==e?b(e):c["continue"]()}else b()},g.onerror=function(){d(g.error)}})["catch"](d)});return z(d,b),d}function t(b,c,d){var e=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var h=new Promise(function(a,d){var h;e.ready().then(function(){return h=e._dbInfo,c instanceof Blob?f(h.db).then(function(a){return a?c:g(c)}):c}).then(function(c){var e=h.db.transaction(h.storeName,"readwrite"),f=e.objectStore(h.storeName);null===c&&(c=void 0),e.oncomplete=function(){void 0===c&&(c=null),a(c)},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;d(a)};var g=f.put(c,b)})["catch"](d)});return z(h,d),h}function u(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=new Promise(function(a,c){d.ready().then(function(){var e=d._dbInfo,f=e.db.transaction(e.storeName,"readwrite"),g=f.objectStore(e.storeName),h=g["delete"](b);f.oncomplete=function(){a()},f.onerror=function(){c(h.error)},f.onabort=function(){var a=h.error?h.error:h.transaction.error;c(a)}})["catch"](c)});return z(e,c),e}function v(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo,e=d.db.transaction(d.storeName,"readwrite"),f=e.objectStore(d.storeName),g=f.clear();e.oncomplete=function(){a()},e.onabort=e.onerror=function(){var a=g.error?g.error:g.transaction.error;c(a)}})["catch"](c)});return z(c,a),c}function w(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo,e=d.db.transaction(d.storeName,"readonly").objectStore(d.storeName),f=e.count();f.onsuccess=function(){a(f.result)},f.onerror=function(){c(f.error)}})["catch"](c)});return z(c,a),c}function x(a,b){var c=this,d=new Promise(function(b,d){return 0>a?void b(null):void c.ready().then(function(){var e=c._dbInfo,f=e.db.transaction(e.storeName,"readonly").objectStore(e.storeName),g=!1,h=f.openCursor();h.onsuccess=function(){var c=h.result;return c?void(0===a?b(c.key):g?b(c.key):(g=!0,c.advance(a))):void b(null)},h.onerror=function(){d(h.error)}})["catch"](d)});return z(d,b),d}function y(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo,e=d.db.transaction(d.storeName,"readonly").objectStore(d.storeName),f=e.openCursor(),g=[];f.onsuccess=function(){var b=f.result;return b?(g.push(b.key),void b["continue"]()):void a(g)},f.onerror=function(){c(f.error)}})["catch"](c)});return z(c,a),c}function z(a,b){b&&a.then(function(a){b(null,a)},function(a){b(a)})}var A=A||a.indexedDB||a.webkitIndexedDB||a.mozIndexedDB||a.OIndexedDB||a.msIndexedDB;if(A){var B,C,D="local-forage-detect-blob-support",E={_driver:"asyncStorage",_initStorage:m,iterate:s,getItem:r,setItem:t,removeItem:u,clear:v,length:w,key:x,keys:y};return E}}("undefined"!=typeof window?window:self);b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";b.__esModule=!0;var d=function(a){function b(a){var b=this,d={};if(a)for(var e in a)d[e]=a[e];return d.keyPrefix=d.name+"/",d.storeName!==b._defaultConfig.storeName&&(d.keyPrefix+=d.storeName+"/"),b._dbInfo=d,new Promise(function(a,b){a(c(3))}).then(function(a){return d.serializer=a,Promise.resolve()})}function d(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo.keyPrefix,c=m.length-1;c>=0;c--){var d=m.key(c);0===d.indexOf(a)&&m.removeItem(d)}});return l(c,a),c}function e(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=d.ready().then(function(){var a=d._dbInfo,c=m.getItem(a.keyPrefix+b);return c&&(c=a.serializer.deserialize(c)),c});return l(e,c),e}function f(a,b){var c=this,d=c.ready().then(function(){for(var b=c._dbInfo,d=b.keyPrefix,e=d.length,f=m.length,g=1,h=0;f>h;h++){var i=m.key(h);if(0===i.indexOf(d)){var j=m.getItem(i);if(j&&(j=b.serializer.deserialize(j)),j=a(j,i.substring(e),g++),void 0!==j)return j}}});return l(d,b),d}function g(a,b){var c=this,d=c.ready().then(function(){var b,d=c._dbInfo;try{b=m.key(a)}catch(e){b=null}return b&&(b=b.substring(d.keyPrefix.length)),b});return l(d,b),d}function h(a){var b=this,c=b.ready().then(function(){for(var a=b._dbInfo,c=m.length,d=[],e=0;c>e;e++)0===m.key(e).indexOf(a.keyPrefix)&&d.push(m.key(e).substring(a.keyPrefix.length));return d});return l(c,a),c}function i(a){var b=this,c=b.keys().then(function(a){return a.length});return l(c,a),c}function j(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=d.ready().then(function(){var a=d._dbInfo;m.removeItem(a.keyPrefix+b)});return l(e,c),e}function k(b,c,d){var e=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var f=e.ready().then(function(){void 0===c&&(c=null);var a=c;return new Promise(function(d,f){var g=e._dbInfo;g.serializer.serialize(c,function(c,e){if(e)f(e);else try{m.setItem(g.keyPrefix+b,c),d(a)}catch(h){("QuotaExceededError"===h.name||"NS_ERROR_DOM_QUOTA_REACHED"===h.name)&&f(h),f(h)}})})});return l(f,d),f}function l(a,b){b&&a.then(function(a){b(null,a)},function(a){b(a)})}var m=null;try{if(!(a.localStorage&&"setItem"in a.localStorage))return;m=a.localStorage}catch(n){return}var o={_driver:"localStorageWrapper",_initStorage:b,iterate:f,getItem:e,setItem:k,removeItem:j,clear:d,length:i,key:g,keys:h};return o}("undefined"!=typeof window?window:self);b["default"]=d,a.exports=b["default"]},function(a,b){"use strict";b.__esModule=!0;var c=function(a){function b(b,c){b=b||[],c=c||{};try{return new Blob(b,c)}catch(d){if("TypeError"!==d.name)throw d;for(var e=a.BlobBuilder||a.MSBlobBuilder||a.MozBlobBuilder||a.WebKitBlobBuilder,f=new e,g=0;gb;b+=4)c=g.indexOf(a[b]),d=g.indexOf(a[b+1]),e=g.indexOf(a[b+2]),f=g.indexOf(a[b+3]),l[j++]=c<<2|d>>4,l[j++]=(15&d)<<4|e>>2,l[j++]=(3&e)<<6|63&f;return k}function f(a){var b,c=new Uint8Array(a),d="";for(b=0;b>2],d+=g[(3&c[b])<<4|c[b+1]>>4],d+=g[(15&c[b+1])<<2|c[b+2]>>6],d+=g[63&c[b+2]];return c.length%3===2?d=d.substring(0,d.length-1)+"=":c.length%3===1&&(d=d.substring(0,d.length-2)+"=="),d}var g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",h="~~local_forage_type~",i=/^~~local_forage_type~([^~]+)~/,j="__lfsc__:",k=j.length,l="arbf",m="blob",n="si08",o="ui08",p="uic8",q="si16",r="si32",s="ur16",t="ui32",u="fl32",v="fl64",w=k+l.length,x={serialize:c,deserialize:d,stringToBuffer:e,bufferToString:f};return x}("undefined"!=typeof window?window:self);b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";b.__esModule=!0;var d=function(a){function b(a){var b=this,d={db:null};if(a)for(var e in a)d[e]="string"!=typeof a[e]?a[e].toString():a[e];var f=new Promise(function(a,c){try{d.db=m(d.name,String(d.version),d.description,d.size)}catch(e){return c(e)}d.db.transaction(function(e){e.executeSql("CREATE TABLE IF NOT EXISTS "+d.storeName+" (id INTEGER PRIMARY KEY, key unique, value)",[],function(){b._dbInfo=d,a()},function(a,b){c(b)})})});return new Promise(function(a,b){a(c(3))}).then(function(a){return d.serializer=a,f})}function d(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=new Promise(function(a,c){d.ready().then(function(){var e=d._dbInfo;e.db.transaction(function(d){d.executeSql("SELECT * FROM "+e.storeName+" WHERE key = ? LIMIT 1",[b],function(b,c){var d=c.rows.length?c.rows.item(0).value:null;d&&(d=e.serializer.deserialize(d)),a(d)},function(a,b){c(b)})})})["catch"](c)});return l(e,c),e}function e(a,b){var c=this,d=new Promise(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){c.executeSql("SELECT * FROM "+e.storeName,[],function(c,d){for(var f=d.rows,g=f.length,h=0;g>h;h++){var i=f.item(h),j=i.value;if(j&&(j=e.serializer.deserialize(j)),j=a(j,i.key,h+1),void 0!==j)return void b(j)}b()},function(a,b){d(b)})})})["catch"](d)});return l(d,b),d}function f(b,c,d){var e=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var f=new Promise(function(a,d){e.ready().then(function(){void 0===c&&(c=null);var f=c,g=e._dbInfo;g.serializer.serialize(c,function(c,e){e?d(e):g.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO "+g.storeName+" (key, value) VALUES (?, ?)",[b,c],function(){a(f)},function(a,b){d(b)})},function(a){a.code===a.QUOTA_ERR&&d(a)})})})["catch"](d)});return l(f,d),f}function g(b,c){var d=this;"string"!=typeof b&&(a.console.warn(b+" used as a key, but it is not a string."),b=String(b));var e=new Promise(function(a,c){d.ready().then(function(){var e=d._dbInfo;e.db.transaction(function(d){d.executeSql("DELETE FROM "+e.storeName+" WHERE key = ?",[b],function(){a()},function(a,b){c(b)})})})["catch"](c)});return l(e,c),e}function h(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){b.executeSql("DELETE FROM "+d.storeName,[],function(){a()},function(a,b){c(b)})})})["catch"](c)});return l(c,a),c}function i(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){b.executeSql("SELECT COUNT(key) as c FROM "+d.storeName,[],function(b,c){var d=c.rows.item(0).c;a(d)},function(a,b){c(b)})})})["catch"](c)});return l(c,a),c}function j(a,b){var c=this,d=new Promise(function(b,d){c.ready().then(function(){var e=c._dbInfo;e.db.transaction(function(c){c.executeSql("SELECT key FROM "+e.storeName+" WHERE id = ? LIMIT 1",[a+1],function(a,c){var d=c.rows.length?c.rows.item(0).key:null;b(d)},function(a,b){d(b)})})})["catch"](d)});return l(d,b),d}function k(a){var b=this,c=new Promise(function(a,c){b.ready().then(function(){var d=b._dbInfo;d.db.transaction(function(b){b.executeSql("SELECT key FROM "+d.storeName,[],function(b,c){for(var d=[],e=0;e 0; }; Cnds.prototype.IsProcessingGets = function () { return this.pendingGets > 0; }; Cnds.prototype.OnAllSetsComplete = function () { return true; }; Cnds.prototype.OnAllGetsComplete = function () { return true; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetItem = function (keyNoPrefix, value) { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } var keyPrefix = prefix + keyNoPrefix; this.pendingSets++; var self = this; localforage["setItem"](keyPrefix, value, function (err, valueSet) { debugDataChanged = true; self.pendingSets--; if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { currentKey = keyNoPrefix; lastValue = valueSet; self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAnyItemSet, self); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnItemSet, self); currentKey = ""; lastValue = ""; } if (self.pendingSets === 0) { self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAllSetsComplete, self); } }); }; Acts.prototype.GetItem = function (keyNoPrefix) { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } var keyPrefix = prefix + keyNoPrefix; this.pendingGets++; var self = this; localforage["getItem"](keyPrefix, function (err, value) { self.pendingGets--; if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { currentKey = keyNoPrefix; lastValue = value; if (typeof lastValue === "undefined" || lastValue === null) lastValue = ""; self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAnyItemGet, self); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnItemGet, self); currentKey = ""; lastValue = ""; } if (self.pendingGets === 0) { self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAllGetsComplete, self); } }); }; Acts.prototype.CheckItemExists = function (keyNoPrefix) { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } var keyPrefix = prefix + keyNoPrefix; var self = this; localforage["getItem"](keyPrefix, function (err, value) { if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { currentKey = keyNoPrefix; if (value === null) // null value indicates key missing { lastValue = ""; // prevent ItemValue meaning anything self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnItemMissing, self); } else { lastValue = value; // make available to ItemValue expression self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnItemExists, self); } currentKey = ""; lastValue = ""; } }); }; Acts.prototype.RemoveItem = function (keyNoPrefix) { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } var keyPrefix = prefix + keyNoPrefix; var self = this; localforage["removeItem"](keyPrefix, function (err) { debugDataChanged = true; if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { currentKey = keyNoPrefix; lastValue = ""; self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAnyItemRemoved, self); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnItemRemoved, self); currentKey = ""; } }); }; Acts.prototype.ClearStorage = function () { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } if (is_arcade) return; var self = this; localforage["clear"](function (err) { debugDataChanged = true; if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { currentKey = ""; lastValue = ""; cr.clearArray(keyNamesList); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnCleared, self); } }); }; Acts.prototype.GetAllKeyNames = function () { if (localForageInitFailed) { TriggerStorageError(this, "storage failed to initialise - may be disabled in browser settings"); return; } var self = this; localforage["keys"](function (err, keyList) { var i, len, k; if (err) { errorMessage = getErrorString(err); self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnError, self); } else { cr.clearArray(keyNamesList); for (i = 0, len = keyList.length; i < len; ++i) { k = keyList[i]; if (!hasRequiredPrefix(k)) continue; keyNamesList.push(removePrefix(k)); } self.runtime.trigger(cr.plugins_.LocalStorage.prototype.cnds.OnAllKeyNamesLoaded, self); } }); }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.ItemValue = function (ret) { ret.set_any(lastValue); }; Exps.prototype.Key = function (ret) { ret.set_string(currentKey); }; Exps.prototype.KeyCount = function (ret) { ret.set_int(keyNamesList.length); }; Exps.prototype.KeyAt = function (ret, i) { i = Math.floor(i); if (i < 0 || i >= keyNamesList.length) { ret.set_string(""); return; } ret.set_string(keyNamesList[i]); }; Exps.prototype.ErrorMessage = function (ret) { ret.set_string(errorMessage); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Mouse = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Mouse.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; this.buttonMap = new Array(4); // mouse down states this.mouseXcanvas = 0; // mouse position relative to canvas this.mouseYcanvas = 0; this.triggerButton = 0; this.triggerType = 0; this.triggerDir = 0; this.handled = false; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { var self = this; if (!this.runtime.isDomFree) { jQuery(document).mousemove( function(info) { self.onMouseMove(info); } ); jQuery(document).mousedown( function(info) { self.onMouseDown(info); } ); jQuery(document).mouseup( function(info) { self.onMouseUp(info); } ); jQuery(document).dblclick( function(info) { self.onDoubleClick(info); } ); var wheelevent = function(info) { self.onWheel(info); }; document.addEventListener("mousewheel", wheelevent, false); document.addEventListener("DOMMouseScroll", wheelevent, false); } }; var dummyoffset = {left: 0, top: 0}; instanceProto.onMouseMove = function(info) { var offset = this.runtime.isDomFree ? dummyoffset : jQuery(this.runtime.canvas).offset(); this.mouseXcanvas = info.pageX - offset.left; this.mouseYcanvas = info.pageY - offset.top; }; instanceProto.mouseInGame = function () { if (this.runtime.fullscreen_mode > 0) return true; return this.mouseXcanvas >= 0 && this.mouseYcanvas >= 0 && this.mouseXcanvas < this.runtime.width && this.mouseYcanvas < this.runtime.height; }; instanceProto.onMouseDown = function(info) { if (!this.mouseInGame()) return; this.buttonMap[info.which] = true; this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnAnyClick, this); this.triggerButton = info.which - 1; // 1-based this.triggerType = 0; // single click this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnClick, this); this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnObjectClicked, this); this.runtime.isInUserInputEvent = false; }; instanceProto.onMouseUp = function(info) { if (!this.buttonMap[info.which]) return; if (this.runtime.had_a_click && !this.runtime.isMobile) info.preventDefault(); this.runtime.had_a_click = true; this.buttonMap[info.which] = false; this.runtime.isInUserInputEvent = true; this.triggerButton = info.which - 1; // 1-based this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnRelease, this); this.runtime.isInUserInputEvent = false; }; instanceProto.onDoubleClick = function(info) { if (!this.mouseInGame()) return; info.preventDefault(); this.runtime.isInUserInputEvent = true; this.triggerButton = info.which - 1; // 1-based this.triggerType = 1; // double click this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnClick, this); this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnObjectClicked, this); this.runtime.isInUserInputEvent = false; }; instanceProto.onWheel = function (info) { var delta = info.wheelDelta ? info.wheelDelta : info.detail ? -info.detail : 0; this.triggerDir = (delta < 0 ? 0 : 1); this.handled = false; this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnWheel, this); this.runtime.isInUserInputEvent = false; if (this.handled && cr.isCanvasInputEvent(info)) info.preventDefault(); }; instanceProto.onWindowBlur = function () { var i, len; for (i = 0, len = this.buttonMap.length; i < len; ++i) { if (!this.buttonMap[i]) continue; this.buttonMap[i] = false; this.triggerButton = i - 1; this.runtime.trigger(cr.plugins_.Mouse.prototype.cnds.OnRelease, this); } }; function Cnds() {}; Cnds.prototype.OnClick = function (button, type) { return button === this.triggerButton && type === this.triggerType; }; Cnds.prototype.OnAnyClick = function () { return true; }; Cnds.prototype.IsButtonDown = function (button) { return this.buttonMap[button + 1]; // jQuery uses 1-based buttons for some reason }; Cnds.prototype.OnRelease = function (button) { return button === this.triggerButton; }; Cnds.prototype.IsOverObject = function (obj) { var cnd = this.runtime.getCurrentCondition(); var mx = this.mouseXcanvas; var my = this.mouseYcanvas; return cr.xor(this.runtime.testAndSelectCanvasPointOverlap(obj, mx, my, cnd.inverted), cnd.inverted); }; Cnds.prototype.OnObjectClicked = function (button, type, obj) { if (button !== this.triggerButton || type !== this.triggerType) return false; // wrong click type return this.runtime.testAndSelectCanvasPointOverlap(obj, this.mouseXcanvas, this.mouseYcanvas, false); }; Cnds.prototype.OnWheel = function (dir) { this.handled = true; return dir === this.triggerDir; }; pluginProto.cnds = new Cnds(); function Acts() {}; var lastSetCursor = null; Acts.prototype.SetCursor = function (c) { if (this.runtime.isDomFree) return; var cursor_style = ["auto", "pointer", "text", "crosshair", "move", "help", "wait", "none"][c]; if (lastSetCursor === cursor_style) return; // redundant lastSetCursor = cursor_style; document.body.style.cursor = cursor_style; }; Acts.prototype.SetCursorSprite = function (obj) { if (this.runtime.isDomFree || this.runtime.isMobile || !obj) return; var inst = obj.getFirstPicked(); if (!inst || !inst.curFrame) return; var frame = inst.curFrame; if (lastSetCursor === frame) return; // already set this frame lastSetCursor = frame; var datauri = frame.getDataUri(); var cursor_style = "url(" + datauri + ") " + Math.round(frame.hotspotX * frame.width) + " " + Math.round(frame.hotspotY * frame.height) + ", auto"; document.body.style.cursor = ""; document.body.style.cursor = cursor_style; }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.X = function (ret, layerparam) { var layer, oldScale, oldZoomRate, oldParallaxX, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxX = layer.parallaxX; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxX = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.mouseXcanvas, this.mouseYcanvas, true)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxX = oldParallaxX; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.mouseXcanvas, this.mouseYcanvas, true)); else ret.set_float(0); } }; Exps.prototype.Y = function (ret, layerparam) { var layer, oldScale, oldZoomRate, oldParallaxY, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxY = layer.parallaxY; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxY = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.mouseXcanvas, this.mouseYcanvas, false)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxY = oldParallaxY; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.mouseXcanvas, this.mouseYcanvas, false)); else ret.set_float(0); } }; Exps.prototype.AbsoluteX = function (ret) { ret.set_float(this.mouseXcanvas); }; Exps.prototype.AbsoluteY = function (ret) { ret.set_float(this.mouseYcanvas); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Particles = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Particles.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { if (this.is_family) return; this.texture_img = new Image(); this.texture_img.cr_filesize = this.texture_filesize; this.webGL_texture = null; this.runtime.waitForImageLoad(this.texture_img, this.texture_file); }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; this.webGL_texture = null; }; typeProto.onRestoreWebGLContext = function () { if (this.is_family || !this.instances.length) return; if (!this.webGL_texture) { this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, true, this.runtime.linearSampling, this.texture_pixelformat); } }; typeProto.loadTextures = function () { if (this.is_family || this.webGL_texture || !this.runtime.glwrap) return; this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, true, this.runtime.linearSampling, this.texture_pixelformat); }; typeProto.unloadTextures = function () { if (this.is_family || this.instances.length || !this.webGL_texture) return; this.runtime.glwrap.deleteTexture(this.webGL_texture); this.webGL_texture = null; }; typeProto.preloadCanvas2D = function (ctx) { ctx.drawImage(this.texture_img, 0, 0); }; function Particle(owner) { this.owner = owner; this.active = false; this.x = 0; this.y = 0; this.speed = 0; this.angle = 0; this.opacity = 1; this.grow = 0; this.size = 0; this.gs = 0; // gravity speed this.age = 0; cr.seal(this); }; Particle.prototype.init = function () { var owner = this.owner; this.x = owner.x - (owner.xrandom / 2) + (Math.random() * owner.xrandom); this.y = owner.y - (owner.yrandom / 2) + (Math.random() * owner.yrandom); this.speed = owner.initspeed - (owner.speedrandom / 2) + (Math.random() * owner.speedrandom); this.angle = owner.angle - (owner.spraycone / 2) + (Math.random() * owner.spraycone); this.opacity = owner.initopacity; this.size = owner.initsize - (owner.sizerandom / 2) + (Math.random() * owner.sizerandom); this.grow = owner.growrate - (owner.growrandom / 2) + (Math.random() * owner.growrandom); this.gs = 0; this.age = 0; }; Particle.prototype.tick = function (dt) { var owner = this.owner; this.x += Math.cos(this.angle) * this.speed * dt; this.y += Math.sin(this.angle) * this.speed * dt; this.y += this.gs * dt; this.speed += owner.acc * dt; this.size += this.grow * dt; this.gs += owner.g * dt; this.age += dt; if (this.size < 1) { this.active = false; return; } if (owner.lifeanglerandom !== 0) this.angle += (Math.random() * owner.lifeanglerandom * dt) - (owner.lifeanglerandom * dt / 2); if (owner.lifespeedrandom !== 0) this.speed += (Math.random() * owner.lifespeedrandom * dt) - (owner.lifespeedrandom * dt / 2); if (owner.lifeopacityrandom !== 0) { this.opacity += (Math.random() * owner.lifeopacityrandom * dt) - (owner.lifeopacityrandom * dt / 2); if (this.opacity < 0) this.opacity = 0; else if (this.opacity > 1) this.opacity = 1; } if (owner.destroymode <= 1 && this.age >= owner.timeout) { this.active = false; } if (owner.destroymode === 2 && this.speed <= 0) { this.active = false; } }; Particle.prototype.draw = function (ctx) { var curopacity = this.owner.opacity * this.opacity; if (curopacity === 0) return; if (this.owner.destroymode === 0) curopacity *= 1 - (this.age / this.owner.timeout); ctx.globalAlpha = curopacity; var drawx = this.x - this.size / 2; var drawy = this.y - this.size / 2; if (this.owner.runtime.pixel_rounding) { drawx = (drawx + 0.5) | 0; drawy = (drawy + 0.5) | 0; } ctx.drawImage(this.owner.type.texture_img, drawx, drawy, this.size, this.size); }; Particle.prototype.drawGL = function (glw) { var curopacity = this.owner.opacity * this.opacity; if (this.owner.destroymode === 0) curopacity *= 1 - (this.age / this.owner.timeout); var drawsize = this.size; var scaleddrawsize = drawsize * this.owner.particlescale; var drawx = this.x - drawsize / 2; var drawy = this.y - drawsize / 2; if (this.owner.runtime.pixel_rounding) { drawx = (drawx + 0.5) | 0; drawy = (drawy + 0.5) | 0; } if (scaleddrawsize < 1 || curopacity === 0) return; if (scaleddrawsize < glw.minPointSize || scaleddrawsize > glw.maxPointSize) { glw.setOpacity(curopacity); glw.quad(drawx, drawy, drawx + drawsize, drawy, drawx + drawsize, drawy + drawsize, drawx, drawy + drawsize); } else glw.point(this.x, this.y, scaleddrawsize, curopacity); }; Particle.prototype.left = function () { return this.x - this.size / 2; }; Particle.prototype.right = function () { return this.x + this.size / 2; }; Particle.prototype.top = function () { return this.y - this.size / 2; }; Particle.prototype.bottom = function () { return this.y + this.size / 2; }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; var deadparticles = []; instanceProto.onCreate = function() { var props = this.properties; this.rate = props[0]; this.spraycone = cr.to_radians(props[1]); this.spraytype = props[2]; // 0 = continuous, 1 = one-shot this.spraying = true; // for continuous mode only this.initspeed = props[3]; this.initsize = props[4]; this.initopacity = props[5] / 100.0; this.growrate = props[6]; this.xrandom = props[7]; this.yrandom = props[8]; this.speedrandom = props[9]; this.sizerandom = props[10]; this.growrandom = props[11]; this.acc = props[12]; this.g = props[13]; this.lifeanglerandom = props[14]; this.lifespeedrandom = props[15]; this.lifeopacityrandom = props[16]; this.destroymode = props[17]; // 0 = fade, 1 = timeout, 2 = stopped this.timeout = props[18]; this.particleCreateCounter = 0; this.particlescale = 1; this.particleBoxLeft = this.x; this.particleBoxTop = this.y; this.particleBoxRight = this.x; this.particleBoxBottom = this.y; this.add_bbox_changed_callback(function (self) { self.bbox.set(self.particleBoxLeft, self.particleBoxTop, self.particleBoxRight, self.particleBoxBottom); self.bquad.set_from_rect(self.bbox); self.bbox_changed = false; self.update_collision_cell(); self.update_render_cell(); }); if (!this.recycled) this.particles = []; this.runtime.tickMe(this); this.type.loadTextures(); if (this.spraytype === 1) { for (var i = 0; i < this.rate; i++) this.allocateParticle().opacity = 0; } this.first_tick = true; // for re-init'ing one-shot particles on first tick so they assume any new angle/position }; instanceProto.saveToJSON = function () { var o = { "r": this.rate, "sc": this.spraycone, "st": this.spraytype, "s": this.spraying, "isp": this.initspeed, "isz": this.initsize, "io": this.initopacity, "gr": this.growrate, "xr": this.xrandom, "yr": this.yrandom, "spr": this.speedrandom, "szr": this.sizerandom, "grnd": this.growrandom, "acc": this.acc, "g": this.g, "lar": this.lifeanglerandom, "lsr": this.lifespeedrandom, "lor": this.lifeopacityrandom, "dm": this.destroymode, "to": this.timeout, "pcc": this.particleCreateCounter, "ft": this.first_tick, "p": [] }; var i, len, p; var arr = o["p"]; for (i = 0, len = this.particles.length; i < len; i++) { p = this.particles[i]; arr.push([p.x, p.y, p.speed, p.angle, p.opacity, p.grow, p.size, p.gs, p.age]); } return o; }; instanceProto.loadFromJSON = function (o) { this.rate = o["r"]; this.spraycone = o["sc"]; this.spraytype = o["st"]; this.spraying = o["s"]; this.initspeed = o["isp"]; this.initsize = o["isz"]; this.initopacity = o["io"]; this.growrate = o["gr"]; this.xrandom = o["xr"]; this.yrandom = o["yr"]; this.speedrandom = o["spr"]; this.sizerandom = o["szr"]; this.growrandom = o["grnd"]; this.acc = o["acc"]; this.g = o["g"]; this.lifeanglerandom = o["lar"]; this.lifespeedrandom = o["lsr"]; this.lifeopacityrandom = o["lor"]; this.destroymode = o["dm"]; this.timeout = o["to"]; this.particleCreateCounter = o["pcc"]; this.first_tick = o["ft"]; deadparticles.push.apply(deadparticles, this.particles); cr.clearArray(this.particles); var i, len, p, d; var arr = o["p"]; for (i = 0, len = arr.length; i < len; i++) { p = this.allocateParticle(); d = arr[i]; p.x = d[0]; p.y = d[1]; p.speed = d[2]; p.angle = d[3]; p.opacity = d[4]; p.grow = d[5]; p.size = d[6]; p.gs = d[7]; p.age = d[8]; } }; instanceProto.onDestroy = function () { deadparticles.push.apply(deadparticles, this.particles); cr.clearArray(this.particles); }; instanceProto.allocateParticle = function () { var p; if (deadparticles.length) { p = deadparticles.pop(); p.owner = this; } else p = new Particle(this); this.particles.push(p); p.active = true; return p; }; instanceProto.tick = function() { var dt = this.runtime.getDt(this); var i, len, p, n, j; if (this.spraytype === 0 && this.spraying) { this.particleCreateCounter += dt * this.rate; n = cr.floor(this.particleCreateCounter); this.particleCreateCounter -= n; for (i = 0; i < n; i++) { p = this.allocateParticle(); p.init(); } } this.particleBoxLeft = this.x; this.particleBoxTop = this.y; this.particleBoxRight = this.x; this.particleBoxBottom = this.y; for (i = 0, j = 0, len = this.particles.length; i < len; i++) { p = this.particles[i]; this.particles[j] = p; this.runtime.redraw = true; if (this.spraytype === 1 && this.first_tick) p.init(); p.tick(dt); if (!p.active) { deadparticles.push(p); continue; } if (p.left() < this.particleBoxLeft) this.particleBoxLeft = p.left(); if (p.right() > this.particleBoxRight) this.particleBoxRight = p.right(); if (p.top() < this.particleBoxTop) this.particleBoxTop = p.top(); if (p.bottom() > this.particleBoxBottom) this.particleBoxBottom = p.bottom(); j++; } cr.truncateArray(this.particles, j); this.set_bbox_changed(); this.first_tick = false; if (this.spraytype === 1 && this.particles.length === 0) this.runtime.DestroyInstance(this); }; instanceProto.draw = function (ctx) { var i, len, p, layer = this.layer; for (i = 0, len = this.particles.length; i < len; i++) { p = this.particles[i]; if (p.right() >= layer.viewLeft && p.bottom() >= layer.viewTop && p.left() <= layer.viewRight && p.top() <= layer.viewBottom) { p.draw(ctx); } } }; instanceProto.drawGL = function (glw) { this.particlescale = this.layer.getScale(); glw.setTexture(this.type.webGL_texture); var i, len, p, layer = this.layer; for (i = 0, len = this.particles.length; i < len; i++) { p = this.particles[i]; if (p.right() >= layer.viewLeft && p.bottom() >= layer.viewTop && p.left() <= layer.viewRight && p.top() <= layer.viewBottom) { p.drawGL(glw); } } }; function Cnds() {}; Cnds.prototype.IsSpraying = function () { return this.spraying; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetSpraying = function (set_) { this.spraying = (set_ !== 0); }; Acts.prototype.SetEffect = function (effect) { this.blend_mode = effect; this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; Acts.prototype.SetRate = function (x) { this.rate = x; var diff, i; if (this.spraytype === 1 && this.first_tick) { if (x < this.particles.length) { diff = this.particles.length - x; for (i = 0; i < diff; i++) deadparticles.push(this.particles.pop()); } else if (x > this.particles.length) { diff = x - this.particles.length; for (i = 0; i < diff; i++) this.allocateParticle().opacity = 0; } } }; Acts.prototype.SetSprayCone = function (x) { this.spraycone = cr.to_radians(x); }; Acts.prototype.SetInitSpeed = function (x) { this.initspeed = x; }; Acts.prototype.SetInitSize = function (x) { this.initsize = x; }; Acts.prototype.SetInitOpacity = function (x) { this.initopacity = x / 100; }; Acts.prototype.SetGrowRate = function (x) { this.growrate = x; }; Acts.prototype.SetXRandomiser = function (x) { this.xrandom = x; }; Acts.prototype.SetYRandomiser = function (x) { this.yrandom = x; }; Acts.prototype.SetSpeedRandomiser = function (x) { this.speedrandom = x; }; Acts.prototype.SetSizeRandomiser = function (x) { this.sizerandom = x; }; Acts.prototype.SetGrowRateRandomiser = function (x) { this.growrandom = x; }; Acts.prototype.SetParticleAcc = function (x) { this.acc = x; }; Acts.prototype.SetGravity = function (x) { this.g = x; }; Acts.prototype.SetAngleRandomiser = function (x) { this.lifeanglerandom = x; }; Acts.prototype.SetLifeSpeedRandomiser = function (x) { this.lifespeedrandom = x; }; Acts.prototype.SetOpacityRandomiser = function (x) { this.lifeopacityrandom = x; }; Acts.prototype.SetTimeout = function (x) { this.timeout = x; }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.ParticleCount = function (ret) { ret.set_int(this.particles.length); }; Exps.prototype.Rate = function (ret) { ret.set_float(this.rate); }; Exps.prototype.SprayCone = function (ret) { ret.set_float(cr.to_degrees(this.spraycone)); }; Exps.prototype.InitSpeed = function (ret) { ret.set_float(this.initspeed); }; Exps.prototype.InitSize = function (ret) { ret.set_float(this.initsize); }; Exps.prototype.InitOpacity = function (ret) { ret.set_float(this.initopacity * 100); }; Exps.prototype.InitGrowRate = function (ret) { ret.set_float(this.growrate); }; Exps.prototype.XRandom = function (ret) { ret.set_float(this.xrandom); }; Exps.prototype.YRandom = function (ret) { ret.set_float(this.yrandom); }; Exps.prototype.InitSpeedRandom = function (ret) { ret.set_float(this.speedrandom); }; Exps.prototype.InitSizeRandom = function (ret) { ret.set_float(this.sizerandom); }; Exps.prototype.InitGrowRandom = function (ret) { ret.set_float(this.growrandom); }; Exps.prototype.ParticleAcceleration = function (ret) { ret.set_float(this.acc); }; Exps.prototype.Gravity = function (ret) { ret.set_float(this.g); }; Exps.prototype.ParticleAngleRandom = function (ret) { ret.set_float(this.lifeanglerandom); }; Exps.prototype.ParticleSpeedRandom = function (ret) { ret.set_float(this.lifespeedrandom); }; Exps.prototype.ParticleOpacityRandom = function (ret) { ret.set_float(this.lifeopacityrandom); }; Exps.prototype.Timeout = function (ret) { ret.set_float(this.timeout); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Sprite = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Sprite.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; function frame_getDataUri() { if (this.datauri.length === 0) { var tmpcanvas = document.createElement("canvas"); tmpcanvas.width = this.width; tmpcanvas.height = this.height; var tmpctx = tmpcanvas.getContext("2d"); if (this.spritesheeted) { tmpctx.drawImage(this.texture_img, this.offx, this.offy, this.width, this.height, 0, 0, this.width, this.height); } else { tmpctx.drawImage(this.texture_img, 0, 0, this.width, this.height); } this.datauri = tmpcanvas.toDataURL("image/png"); } return this.datauri; }; typeProto.onCreate = function() { if (this.is_family) return; var i, leni, j, lenj; var anim, frame, animobj, frameobj, wt, uv; this.all_frames = []; this.has_loaded_textures = false; for (i = 0, leni = this.animations.length; i < leni; i++) { anim = this.animations[i]; animobj = {}; animobj.name = anim[0]; animobj.speed = anim[1]; animobj.loop = anim[2]; animobj.repeatcount = anim[3]; animobj.repeatto = anim[4]; animobj.pingpong = anim[5]; animobj.sid = anim[6]; animobj.frames = []; for (j = 0, lenj = anim[7].length; j < lenj; j++) { frame = anim[7][j]; frameobj = {}; frameobj.texture_file = frame[0]; frameobj.texture_filesize = frame[1]; frameobj.offx = frame[2]; frameobj.offy = frame[3]; frameobj.width = frame[4]; frameobj.height = frame[5]; frameobj.duration = frame[6]; frameobj.hotspotX = frame[7]; frameobj.hotspotY = frame[8]; frameobj.image_points = frame[9]; frameobj.poly_pts = frame[10]; frameobj.pixelformat = frame[11]; frameobj.spritesheeted = (frameobj.width !== 0); frameobj.datauri = ""; // generated on demand and cached frameobj.getDataUri = frame_getDataUri; uv = {}; uv.left = 0; uv.top = 0; uv.right = 1; uv.bottom = 1; frameobj.sheetTex = uv; frameobj.webGL_texture = null; wt = this.runtime.findWaitingTexture(frame[0]); if (wt) { frameobj.texture_img = wt; } else { frameobj.texture_img = new Image(); frameobj.texture_img.cr_src = frame[0]; frameobj.texture_img.cr_filesize = frame[1]; frameobj.texture_img.c2webGL_texture = null; this.runtime.waitForImageLoad(frameobj.texture_img, frame[0]); } cr.seal(frameobj); animobj.frames.push(frameobj); this.all_frames.push(frameobj); } cr.seal(animobj); this.animations[i] = animobj; // swap array data for object } }; typeProto.updateAllCurrentTexture = function () { var i, len, inst; for (i = 0, len = this.instances.length; i < len; i++) { inst = this.instances[i]; inst.curWebGLTexture = inst.curFrame.webGL_texture; } }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; var i, len, frame; for (i = 0, len = this.all_frames.length; i < len; ++i) { frame = this.all_frames[i]; frame.texture_img.c2webGL_texture = null; frame.webGL_texture = null; } this.has_loaded_textures = false; this.updateAllCurrentTexture(); }; typeProto.onRestoreWebGLContext = function () { if (this.is_family || !this.instances.length) return; var i, len, frame; for (i = 0, len = this.all_frames.length; i < len; ++i) { frame = this.all_frames[i]; frame.webGL_texture = this.runtime.glwrap.loadTexture(frame.texture_img, false, this.runtime.linearSampling, frame.pixelformat); } this.updateAllCurrentTexture(); }; typeProto.loadTextures = function () { if (this.is_family || this.has_loaded_textures || !this.runtime.glwrap) return; var i, len, frame; for (i = 0, len = this.all_frames.length; i < len; ++i) { frame = this.all_frames[i]; frame.webGL_texture = this.runtime.glwrap.loadTexture(frame.texture_img, false, this.runtime.linearSampling, frame.pixelformat); } this.has_loaded_textures = true; }; typeProto.unloadTextures = function () { if (this.is_family || this.instances.length || !this.has_loaded_textures) return; var i, len, frame; for (i = 0, len = this.all_frames.length; i < len; ++i) { frame = this.all_frames[i]; this.runtime.glwrap.deleteTexture(frame.webGL_texture); frame.webGL_texture = null; } this.has_loaded_textures = false; }; var already_drawn_images = []; typeProto.preloadCanvas2D = function (ctx) { var i, len, frameimg; cr.clearArray(already_drawn_images); for (i = 0, len = this.all_frames.length; i < len; ++i) { frameimg = this.all_frames[i].texture_img; if (already_drawn_images.indexOf(frameimg) !== -1) continue; ctx.drawImage(frameimg, 0, 0); already_drawn_images.push(frameimg); } }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; var poly_pts = this.type.animations[0].frames[0].poly_pts; if (this.recycled) this.collision_poly.set_pts(poly_pts); else this.collision_poly = new cr.CollisionPoly(poly_pts); }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { this.visible = (this.properties[0] === 0); // 0=visible, 1=invisible this.isTicking = false; this.inAnimTrigger = false; this.collisionsEnabled = (this.properties[3] !== 0); this.cur_animation = this.getAnimationByName(this.properties[1]) || this.type.animations[0]; this.cur_frame = this.properties[2]; if (this.cur_frame < 0) this.cur_frame = 0; if (this.cur_frame >= this.cur_animation.frames.length) this.cur_frame = this.cur_animation.frames.length - 1; var curanimframe = this.cur_animation.frames[this.cur_frame]; this.collision_poly.set_pts(curanimframe.poly_pts); this.hotspotX = curanimframe.hotspotX; this.hotspotY = curanimframe.hotspotY; this.cur_anim_speed = this.cur_animation.speed; this.cur_anim_repeatto = this.cur_animation.repeatto; if (!(this.type.animations.length === 1 && this.type.animations[0].frames.length === 1) && this.cur_anim_speed !== 0) { this.runtime.tickMe(this); this.isTicking = true; } if (this.recycled) this.animTimer.reset(); else this.animTimer = new cr.KahanAdder(); this.frameStart = this.getNowTime(); this.animPlaying = true; this.animRepeats = 0; this.animForwards = true; this.animTriggerName = ""; this.changeAnimName = ""; this.changeAnimFrom = 0; this.changeAnimFrame = -1; this.type.loadTextures(); var i, leni, j, lenj; var anim, frame, uv, maintex; for (i = 0, leni = this.type.animations.length; i < leni; i++) { anim = this.type.animations[i]; for (j = 0, lenj = anim.frames.length; j < lenj; j++) { frame = anim.frames[j]; if (frame.width === 0) { frame.width = frame.texture_img.width; frame.height = frame.texture_img.height; } if (frame.spritesheeted) { maintex = frame.texture_img; uv = frame.sheetTex; uv.left = frame.offx / maintex.width; uv.top = frame.offy / maintex.height; uv.right = (frame.offx + frame.width) / maintex.width; uv.bottom = (frame.offy + frame.height) / maintex.height; if (frame.offx === 0 && frame.offy === 0 && frame.width === maintex.width && frame.height === maintex.height) { frame.spritesheeted = false; } } } } this.curFrame = this.cur_animation.frames[this.cur_frame]; this.curWebGLTexture = this.curFrame.webGL_texture; }; instanceProto.saveToJSON = function () { var o = { "a": this.cur_animation.sid, "f": this.cur_frame, "cas": this.cur_anim_speed, "fs": this.frameStart, "ar": this.animRepeats, "at": this.animTimer.sum, "rt": this.cur_anim_repeatto }; if (!this.animPlaying) o["ap"] = this.animPlaying; if (!this.animForwards) o["af"] = this.animForwards; return o; }; instanceProto.loadFromJSON = function (o) { var anim = this.getAnimationBySid(o["a"]); if (anim) this.cur_animation = anim; this.cur_frame = o["f"]; if (this.cur_frame < 0) this.cur_frame = 0; if (this.cur_frame >= this.cur_animation.frames.length) this.cur_frame = this.cur_animation.frames.length - 1; this.cur_anim_speed = o["cas"]; this.frameStart = o["fs"]; this.animRepeats = o["ar"]; this.animTimer.reset(); this.animTimer.sum = o["at"]; this.animPlaying = o.hasOwnProperty("ap") ? o["ap"] : true; this.animForwards = o.hasOwnProperty("af") ? o["af"] : true; if (o.hasOwnProperty("rt")) this.cur_anim_repeatto = o["rt"]; else this.cur_anim_repeatto = this.cur_animation.repeatto; this.curFrame = this.cur_animation.frames[this.cur_frame]; this.curWebGLTexture = this.curFrame.webGL_texture; this.collision_poly.set_pts(this.curFrame.poly_pts); this.hotspotX = this.curFrame.hotspotX; this.hotspotY = this.curFrame.hotspotY; }; instanceProto.animationFinish = function (reverse) { this.cur_frame = reverse ? 0 : this.cur_animation.frames.length - 1; this.animPlaying = false; this.animTriggerName = this.cur_animation.name; this.inAnimTrigger = true; this.runtime.trigger(cr.plugins_.Sprite.prototype.cnds.OnAnyAnimFinished, this); this.runtime.trigger(cr.plugins_.Sprite.prototype.cnds.OnAnimFinished, this); this.inAnimTrigger = false; this.animRepeats = 0; }; instanceProto.getNowTime = function() { return this.animTimer.sum; }; instanceProto.tick = function() { this.animTimer.add(this.runtime.getDt(this)); if (this.changeAnimName.length) this.doChangeAnim(); if (this.changeAnimFrame >= 0) this.doChangeAnimFrame(); var now = this.getNowTime(); var cur_animation = this.cur_animation; var prev_frame = cur_animation.frames[this.cur_frame]; var next_frame; var cur_frame_time = prev_frame.duration / this.cur_anim_speed; if (this.animPlaying && now >= this.frameStart + cur_frame_time) { if (this.animForwards) { this.cur_frame++; } else { this.cur_frame--; } this.frameStart += cur_frame_time; if (this.cur_frame >= cur_animation.frames.length) { if (cur_animation.pingpong) { this.animForwards = false; this.cur_frame = cur_animation.frames.length - 2; } else if (cur_animation.loop) { this.cur_frame = this.cur_anim_repeatto; } else { this.animRepeats++; if (this.animRepeats >= cur_animation.repeatcount) { this.animationFinish(false); } else { this.cur_frame = this.cur_anim_repeatto; } } } if (this.cur_frame < 0) { if (cur_animation.pingpong) { this.cur_frame = 1; this.animForwards = true; if (!cur_animation.loop) { this.animRepeats++; if (this.animRepeats >= cur_animation.repeatcount) { this.animationFinish(true); } } } else { if (cur_animation.loop) { this.cur_frame = this.cur_anim_repeatto; } else { this.animRepeats++; if (this.animRepeats >= cur_animation.repeatcount) { this.animationFinish(true); } else { this.cur_frame = this.cur_anim_repeatto; } } } } if (this.cur_frame < 0) this.cur_frame = 0; else if (this.cur_frame >= cur_animation.frames.length) this.cur_frame = cur_animation.frames.length - 1; if (now > this.frameStart + (cur_animation.frames[this.cur_frame].duration / this.cur_anim_speed)) { this.frameStart = now; } next_frame = cur_animation.frames[this.cur_frame]; this.OnFrameChanged(prev_frame, next_frame); this.runtime.redraw = true; } }; instanceProto.getAnimationByName = function (name_) { var i, len, a; for (i = 0, len = this.type.animations.length; i < len; i++) { a = this.type.animations[i]; if (cr.equals_nocase(a.name, name_)) return a; } return null; }; instanceProto.getAnimationBySid = function (sid_) { var i, len, a; for (i = 0, len = this.type.animations.length; i < len; i++) { a = this.type.animations[i]; if (a.sid === sid_) return a; } return null; }; instanceProto.doChangeAnim = function () { var prev_frame = this.cur_animation.frames[this.cur_frame]; var anim = this.getAnimationByName(this.changeAnimName); this.changeAnimName = ""; if (!anim) return; if (cr.equals_nocase(anim.name, this.cur_animation.name) && this.animPlaying) return; this.cur_animation = anim; this.cur_anim_speed = anim.speed; this.cur_anim_repeatto = anim.repeatto; if (this.cur_frame < 0) this.cur_frame = 0; if (this.cur_frame >= this.cur_animation.frames.length) this.cur_frame = this.cur_animation.frames.length - 1; if (this.changeAnimFrom === 1) this.cur_frame = 0; this.animPlaying = true; this.frameStart = this.getNowTime(); this.animForwards = true; this.OnFrameChanged(prev_frame, this.cur_animation.frames[this.cur_frame]); this.runtime.redraw = true; }; instanceProto.doChangeAnimFrame = function () { var prev_frame = this.cur_animation.frames[this.cur_frame]; var prev_frame_number = this.cur_frame; this.cur_frame = cr.floor(this.changeAnimFrame); if (this.cur_frame < 0) this.cur_frame = 0; if (this.cur_frame >= this.cur_animation.frames.length) this.cur_frame = this.cur_animation.frames.length - 1; if (prev_frame_number !== this.cur_frame) { this.OnFrameChanged(prev_frame, this.cur_animation.frames[this.cur_frame]); this.frameStart = this.getNowTime(); this.runtime.redraw = true; } this.changeAnimFrame = -1; }; instanceProto.OnFrameChanged = function (prev_frame, next_frame) { var oldw = prev_frame.width; var oldh = prev_frame.height; var neww = next_frame.width; var newh = next_frame.height; if (oldw != neww) this.width *= (neww / oldw); if (oldh != newh) this.height *= (newh / oldh); this.hotspotX = next_frame.hotspotX; this.hotspotY = next_frame.hotspotY; this.collision_poly.set_pts(next_frame.poly_pts); this.set_bbox_changed(); this.curFrame = next_frame; this.curWebGLTexture = next_frame.webGL_texture; var i, len, b; for (i = 0, len = this.behavior_insts.length; i < len; i++) { b = this.behavior_insts[i]; if (b.onSpriteFrameChanged) b.onSpriteFrameChanged(prev_frame, next_frame); } this.runtime.trigger(cr.plugins_.Sprite.prototype.cnds.OnFrameChanged, this); }; instanceProto.draw = function(ctx) { ctx.globalAlpha = this.opacity; var cur_frame = this.curFrame; var spritesheeted = cur_frame.spritesheeted; var cur_image = cur_frame.texture_img; var myx = this.x; var myy = this.y; var w = this.width; var h = this.height; if (this.angle === 0 && w >= 0 && h >= 0) { myx -= this.hotspotX * w; myy -= this.hotspotY * h; if (this.runtime.pixel_rounding) { myx = Math.round(myx); myy = Math.round(myy); } if (spritesheeted) { ctx.drawImage(cur_image, cur_frame.offx, cur_frame.offy, cur_frame.width, cur_frame.height, myx, myy, w, h); } else { ctx.drawImage(cur_image, myx, myy, w, h); } } else { if (this.runtime.pixel_rounding) { myx = Math.round(myx); myy = Math.round(myy); } ctx.save(); var widthfactor = w > 0 ? 1 : -1; var heightfactor = h > 0 ? 1 : -1; ctx.translate(myx, myy); if (widthfactor !== 1 || heightfactor !== 1) ctx.scale(widthfactor, heightfactor); ctx.rotate(this.angle * widthfactor * heightfactor); var drawx = 0 - (this.hotspotX * cr.abs(w)) var drawy = 0 - (this.hotspotY * cr.abs(h)); if (spritesheeted) { ctx.drawImage(cur_image, cur_frame.offx, cur_frame.offy, cur_frame.width, cur_frame.height, drawx, drawy, cr.abs(w), cr.abs(h)); } else { ctx.drawImage(cur_image, drawx, drawy, cr.abs(w), cr.abs(h)); } ctx.restore(); } /* ctx.strokeStyle = "#f00"; ctx.lineWidth = 3; ctx.beginPath(); this.collision_poly.cache_poly(this.width, this.height, this.angle); var i, len, ax, ay, bx, by; for (i = 0, len = this.collision_poly.pts_count; i < len; i++) { ax = this.collision_poly.pts_cache[i*2] + this.x; ay = this.collision_poly.pts_cache[i*2+1] + this.y; bx = this.collision_poly.pts_cache[((i+1)%len)*2] + this.x; by = this.collision_poly.pts_cache[((i+1)%len)*2+1] + this.y; ctx.moveTo(ax, ay); ctx.lineTo(bx, by); } ctx.stroke(); ctx.closePath(); */ /* if (this.behavior_insts.length >= 1 && this.behavior_insts[0].draw) { this.behavior_insts[0].draw(ctx); } */ }; instanceProto.drawGL_earlyZPass = function(glw) { this.drawGL(glw); }; instanceProto.drawGL = function(glw) { glw.setTexture(this.curWebGLTexture); glw.setOpacity(this.opacity); var cur_frame = this.curFrame; var q = this.bquad; if (this.runtime.pixel_rounding) { var ox = Math.round(this.x) - this.x; var oy = Math.round(this.y) - this.y; if (cur_frame.spritesheeted) glw.quadTex(q.tlx + ox, q.tly + oy, q.trx + ox, q.try_ + oy, q.brx + ox, q.bry + oy, q.blx + ox, q.bly + oy, cur_frame.sheetTex); else glw.quad(q.tlx + ox, q.tly + oy, q.trx + ox, q.try_ + oy, q.brx + ox, q.bry + oy, q.blx + ox, q.bly + oy); } else { if (cur_frame.spritesheeted) glw.quadTex(q.tlx, q.tly, q.trx, q.try_, q.brx, q.bry, q.blx, q.bly, cur_frame.sheetTex); else glw.quad(q.tlx, q.tly, q.trx, q.try_, q.brx, q.bry, q.blx, q.bly); } }; instanceProto.getImagePointIndexByName = function(name_) { var cur_frame = this.curFrame; var i, len; for (i = 0, len = cur_frame.image_points.length; i < len; i++) { if (cr.equals_nocase(name_, cur_frame.image_points[i][0])) return i; } return -1; }; instanceProto.getImagePoint = function(imgpt, getX) { var cur_frame = this.curFrame; var image_points = cur_frame.image_points; var index; if (cr.is_string(imgpt)) index = this.getImagePointIndexByName(imgpt); else index = imgpt - 1; // 0 is origin index = cr.floor(index); if (index < 0 || index >= image_points.length) return getX ? this.x : this.y; // return origin var x = (image_points[index][1] - cur_frame.hotspotX) * this.width; var y = image_points[index][2]; y = (y - cur_frame.hotspotY) * this.height; var cosa = Math.cos(this.angle); var sina = Math.sin(this.angle); var x_temp = (x * cosa) - (y * sina); y = (y * cosa) + (x * sina); x = x_temp; x += this.x; y += this.y; return getX ? x : y; }; function Cnds() {}; var arrCache = []; function allocArr() { if (arrCache.length) return arrCache.pop(); else return [0, 0, 0]; }; function freeArr(a) { a[0] = 0; a[1] = 0; a[2] = 0; arrCache.push(a); }; function makeCollKey(a, b) { if (a < b) return "" + a + "," + b; else return "" + b + "," + a; }; function collmemory_add(collmemory, a, b, tickcount) { var a_uid = a.uid; var b_uid = b.uid; var key = makeCollKey(a_uid, b_uid); if (collmemory.hasOwnProperty(key)) { collmemory[key][2] = tickcount; return; } var arr = allocArr(); arr[0] = a_uid; arr[1] = b_uid; arr[2] = tickcount; collmemory[key] = arr; }; function collmemory_remove(collmemory, a, b) { var key = makeCollKey(a.uid, b.uid); if (collmemory.hasOwnProperty(key)) { freeArr(collmemory[key]); delete collmemory[key]; } }; function collmemory_removeInstance(collmemory, inst) { var uid = inst.uid; var p, entry; for (p in collmemory) { if (collmemory.hasOwnProperty(p)) { entry = collmemory[p]; if (entry[0] === uid || entry[1] === uid) { freeArr(collmemory[p]); delete collmemory[p]; } } } }; var last_coll_tickcount = -2; function collmemory_has(collmemory, a, b) { var key = makeCollKey(a.uid, b.uid); if (collmemory.hasOwnProperty(key)) { last_coll_tickcount = collmemory[key][2]; return true; } else { last_coll_tickcount = -2; return false; } }; var candidates1 = []; Cnds.prototype.OnCollision = function (rtype) { if (!rtype) return false; var runtime = this.runtime; var cnd = runtime.getCurrentCondition(); var ltype = cnd.type; var collmemory = null; if (cnd.extra["collmemory"]) { collmemory = cnd.extra["collmemory"]; } else { collmemory = {}; cnd.extra["collmemory"] = collmemory; } if (!cnd.extra["spriteCreatedDestroyCallback"]) { cnd.extra["spriteCreatedDestroyCallback"] = true; runtime.addDestroyCallback(function(inst) { collmemory_removeInstance(cnd.extra["collmemory"], inst); }); } var lsol = ltype.getCurrentSol(); var rsol = rtype.getCurrentSol(); var linstances = lsol.getObjects(); var rinstances; var l, linst, r, rinst; var curlsol, currsol; var tickcount = this.runtime.tickcount; var lasttickcount = tickcount - 1; var exists, run; var current_event = runtime.getCurrentEventStack().current_event; var orblock = current_event.orblock; for (l = 0; l < linstances.length; l++) { linst = linstances[l]; if (rsol.select_all) { linst.update_bbox(); this.runtime.getCollisionCandidates(linst.layer, rtype, linst.bbox, candidates1); rinstances = candidates1; } else rinstances = rsol.getObjects(); for (r = 0; r < rinstances.length; r++) { rinst = rinstances[r]; if (runtime.testOverlap(linst, rinst) || runtime.checkRegisteredCollision(linst, rinst)) { exists = collmemory_has(collmemory, linst, rinst); run = (!exists || (last_coll_tickcount < lasttickcount)); collmemory_add(collmemory, linst, rinst, tickcount); if (run) { runtime.pushCopySol(current_event.solModifiers); curlsol = ltype.getCurrentSol(); currsol = rtype.getCurrentSol(); curlsol.select_all = false; currsol.select_all = false; if (ltype === rtype) { curlsol.instances.length = 2; // just use lsol, is same reference as rsol curlsol.instances[0] = linst; curlsol.instances[1] = rinst; ltype.applySolToContainer(); } else { curlsol.instances.length = 1; currsol.instances.length = 1; curlsol.instances[0] = linst; currsol.instances[0] = rinst; ltype.applySolToContainer(); rtype.applySolToContainer(); } current_event.retrigger(); runtime.popSol(current_event.solModifiers); } } else { collmemory_remove(collmemory, linst, rinst); } } cr.clearArray(candidates1); } return false; }; var rpicktype = null; var rtopick = new cr.ObjectSet(); var needscollisionfinish = false; var candidates2 = []; var temp_bbox = new cr.rect(0, 0, 0, 0); function DoOverlapCondition(rtype, offx, offy) { if (!rtype) return false; var do_offset = (offx !== 0 || offy !== 0); var oldx, oldy, ret = false, r, lenr, rinst; var cnd = this.runtime.getCurrentCondition(); var ltype = cnd.type; var inverted = cnd.inverted; var rsol = rtype.getCurrentSol(); var orblock = this.runtime.getCurrentEventStack().current_event.orblock; var rinstances; if (rsol.select_all) { this.update_bbox(); temp_bbox.copy(this.bbox); temp_bbox.offset(offx, offy); this.runtime.getCollisionCandidates(this.layer, rtype, temp_bbox, candidates2); rinstances = candidates2; } else if (orblock) { if (this.runtime.isCurrentConditionFirst() && !rsol.else_instances.length && rsol.instances.length) rinstances = rsol.instances; else rinstances = rsol.else_instances; } else { rinstances = rsol.instances; } rpicktype = rtype; needscollisionfinish = (ltype !== rtype && !inverted); if (do_offset) { oldx = this.x; oldy = this.y; this.x += offx; this.y += offy; this.set_bbox_changed(); } for (r = 0, lenr = rinstances.length; r < lenr; r++) { rinst = rinstances[r]; if (this.runtime.testOverlap(this, rinst)) { ret = true; if (inverted) break; if (ltype !== rtype) rtopick.add(rinst); } } if (do_offset) { this.x = oldx; this.y = oldy; this.set_bbox_changed(); } cr.clearArray(candidates2); return ret; }; typeProto.finish = function (do_pick) { if (!needscollisionfinish) return; if (do_pick) { var orblock = this.runtime.getCurrentEventStack().current_event.orblock; var sol = rpicktype.getCurrentSol(); var topick = rtopick.valuesRef(); var i, len, inst; if (sol.select_all) { sol.select_all = false; cr.clearArray(sol.instances); for (i = 0, len = topick.length; i < len; ++i) { sol.instances[i] = topick[i]; } if (orblock) { cr.clearArray(sol.else_instances); for (i = 0, len = rpicktype.instances.length; i < len; ++i) { inst = rpicktype.instances[i]; if (!rtopick.contains(inst)) sol.else_instances.push(inst); } } } else { if (orblock) { var initsize = sol.instances.length; for (i = 0, len = topick.length; i < len; ++i) { sol.instances[initsize + i] = topick[i]; cr.arrayFindRemove(sol.else_instances, topick[i]); } } else { cr.shallowAssignArray(sol.instances, topick); } } rpicktype.applySolToContainer(); } rtopick.clear(); needscollisionfinish = false; }; Cnds.prototype.IsOverlapping = function (rtype) { return DoOverlapCondition.call(this, rtype, 0, 0); }; Cnds.prototype.IsOverlappingOffset = function (rtype, offx, offy) { return DoOverlapCondition.call(this, rtype, offx, offy); }; Cnds.prototype.IsAnimPlaying = function (animname) { if (this.changeAnimName.length) return cr.equals_nocase(this.changeAnimName, animname); else return cr.equals_nocase(this.cur_animation.name, animname); }; Cnds.prototype.CompareFrame = function (cmp, framenum) { return cr.do_cmp(this.cur_frame, cmp, framenum); }; Cnds.prototype.CompareAnimSpeed = function (cmp, x) { var s = (this.animForwards ? this.cur_anim_speed : -this.cur_anim_speed); return cr.do_cmp(s, cmp, x); }; Cnds.prototype.OnAnimFinished = function (animname) { return cr.equals_nocase(this.animTriggerName, animname); }; Cnds.prototype.OnAnyAnimFinished = function () { return true; }; Cnds.prototype.OnFrameChanged = function () { return true; }; Cnds.prototype.IsMirrored = function () { return this.width < 0; }; Cnds.prototype.IsFlipped = function () { return this.height < 0; }; Cnds.prototype.OnURLLoaded = function () { return true; }; Cnds.prototype.IsCollisionEnabled = function () { return this.collisionsEnabled; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Spawn = function (obj, layer, imgpt) { if (!obj || !layer) return; var inst = this.runtime.createInstance(obj, layer, this.getImagePoint(imgpt, true), this.getImagePoint(imgpt, false)); if (!inst) return; if (typeof inst.angle !== "undefined") { inst.angle = this.angle; inst.set_bbox_changed(); } this.runtime.isInOnDestroy++; var i, len, s; this.runtime.trigger(Object.getPrototypeOf(obj.plugin).cnds.OnCreated, inst); if (inst.is_contained) { for (i = 0, len = inst.siblings.length; i < len; i++) { s = inst.siblings[i]; this.runtime.trigger(Object.getPrototypeOf(s.type.plugin).cnds.OnCreated, s); } } this.runtime.isInOnDestroy--; var cur_act = this.runtime.getCurrentAction(); var reset_sol = false; if (cr.is_undefined(cur_act.extra["Spawn_LastExec"]) || cur_act.extra["Spawn_LastExec"] < this.runtime.execcount) { reset_sol = true; cur_act.extra["Spawn_LastExec"] = this.runtime.execcount; } var sol; if (obj != this.type) { sol = obj.getCurrentSol(); sol.select_all = false; if (reset_sol) { cr.clearArray(sol.instances); sol.instances[0] = inst; } else sol.instances.push(inst); if (inst.is_contained) { for (i = 0, len = inst.siblings.length; i < len; i++) { s = inst.siblings[i]; sol = s.type.getCurrentSol(); sol.select_all = false; if (reset_sol) { cr.clearArray(sol.instances); sol.instances[0] = s; } else sol.instances.push(s); } } } }; Acts.prototype.SetEffect = function (effect) { this.blend_mode = effect; this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; Acts.prototype.StopAnim = function () { this.animPlaying = false; }; Acts.prototype.StartAnim = function (from) { this.animPlaying = true; this.frameStart = this.getNowTime(); if (from === 1 && this.cur_frame !== 0) { this.changeAnimFrame = 0; if (!this.inAnimTrigger) this.doChangeAnimFrame(); } if (!this.isTicking) { this.runtime.tickMe(this); this.isTicking = true; } }; Acts.prototype.SetAnim = function (animname, from) { this.changeAnimName = animname; this.changeAnimFrom = from; if (!this.isTicking) { this.runtime.tickMe(this); this.isTicking = true; } if (!this.inAnimTrigger) this.doChangeAnim(); }; Acts.prototype.SetAnimFrame = function (framenumber) { this.changeAnimFrame = framenumber; if (!this.isTicking) { this.runtime.tickMe(this); this.isTicking = true; } if (!this.inAnimTrigger) this.doChangeAnimFrame(); }; Acts.prototype.SetAnimSpeed = function (s) { this.cur_anim_speed = cr.abs(s); this.animForwards = (s >= 0); if (!this.isTicking) { this.runtime.tickMe(this); this.isTicking = true; } }; Acts.prototype.SetAnimRepeatToFrame = function (s) { s = Math.floor(s); if (s < 0) s = 0; if (s >= this.cur_animation.frames.length) s = this.cur_animation.frames.length - 1; this.cur_anim_repeatto = s; }; Acts.prototype.SetMirrored = function (m) { var neww = cr.abs(this.width) * (m === 0 ? -1 : 1); if (this.width === neww) return; this.width = neww; this.set_bbox_changed(); }; Acts.prototype.SetFlipped = function (f) { var newh = cr.abs(this.height) * (f === 0 ? -1 : 1); if (this.height === newh) return; this.height = newh; this.set_bbox_changed(); }; Acts.prototype.SetScale = function (s) { var cur_frame = this.curFrame; var mirror_factor = (this.width < 0 ? -1 : 1); var flip_factor = (this.height < 0 ? -1 : 1); var new_width = cur_frame.width * s * mirror_factor; var new_height = cur_frame.height * s * flip_factor; if (this.width !== new_width || this.height !== new_height) { this.width = new_width; this.height = new_height; this.set_bbox_changed(); } }; Acts.prototype.LoadURL = function (url_, resize_, crossOrigin_) { var img = new Image(); var self = this; var curFrame_ = this.curFrame; img.onload = function () { if (curFrame_.texture_img.src === img.src) { if (self.runtime.glwrap && self.curFrame === curFrame_) self.curWebGLTexture = curFrame_.webGL_texture; if (resize_ === 0) // resize to image size { self.width = img.width; self.height = img.height; self.set_bbox_changed(); } self.runtime.redraw = true; self.runtime.trigger(cr.plugins_.Sprite.prototype.cnds.OnURLLoaded, self); return; } curFrame_.texture_img = img; curFrame_.offx = 0; curFrame_.offy = 0; curFrame_.width = img.width; curFrame_.height = img.height; curFrame_.spritesheeted = false; curFrame_.datauri = ""; curFrame_.pixelformat = 0; // reset to RGBA, since we don't know what type of image will have come in if (self.runtime.glwrap) { if (curFrame_.webGL_texture) self.runtime.glwrap.deleteTexture(curFrame_.webGL_texture); curFrame_.webGL_texture = self.runtime.glwrap.loadTexture(img, false, self.runtime.linearSampling); if (self.curFrame === curFrame_) self.curWebGLTexture = curFrame_.webGL_texture; self.type.updateAllCurrentTexture(); } if (resize_ === 0) // resize to image size { self.width = img.width; self.height = img.height; self.set_bbox_changed(); } self.runtime.redraw = true; self.runtime.trigger(cr.plugins_.Sprite.prototype.cnds.OnURLLoaded, self); }; if (url_.substr(0, 5) !== "data:" && crossOrigin_ === 0) img["crossOrigin"] = "anonymous"; this.runtime.setImageSrc(img, url_); }; Acts.prototype.SetCollisions = function (set_) { if (this.collisionsEnabled === (set_ !== 0)) return; // no change this.collisionsEnabled = (set_ !== 0); if (this.collisionsEnabled) this.set_bbox_changed(); // needs to be added back to cells else { if (this.collcells.right >= this.collcells.left) this.type.collision_grid.update(this, this.collcells, null); this.collcells.set(0, 0, -1, -1); } }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.AnimationFrame = function (ret) { ret.set_int(this.cur_frame); }; Exps.prototype.AnimationFrameCount = function (ret) { ret.set_int(this.cur_animation.frames.length); }; Exps.prototype.AnimationName = function (ret) { ret.set_string(this.cur_animation.name); }; Exps.prototype.AnimationSpeed = function (ret) { ret.set_float(this.animForwards ? this.cur_anim_speed : -this.cur_anim_speed); }; Exps.prototype.ImagePointX = function (ret, imgpt) { ret.set_float(this.getImagePoint(imgpt, true)); }; Exps.prototype.ImagePointY = function (ret, imgpt) { ret.set_float(this.getImagePoint(imgpt, false)); }; Exps.prototype.ImagePointCount = function (ret) { ret.set_int(this.curFrame.image_points.length); }; Exps.prototype.ImageWidth = function (ret) { ret.set_float(this.curFrame.width); }; Exps.prototype.ImageHeight = function (ret) { ret.set_float(this.curFrame.height); }; pluginProto.exps = new Exps(); }()); /* global cr,log,assert2 */ /* jshint globalstrict: true */ /* jshint strict: true */ ; ; var jText = ''; cr.plugins_.SpriteFontPlus = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.SpriteFontPlus.prototype; pluginProto.onCreate = function () { }; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { if (this.is_family) return; this.texture_img = new Image(); this.texture_img["idtkLoadDisposed"] = true; this.texture_img.src = this.texture_file; this.runtime.wait_for_textures.push(this.texture_img); this.webGL_texture = null; }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; this.webGL_texture = null; }; typeProto.onRestoreWebGLContext = function () { if (this.is_family || !this.instances.length) return; if (!this.webGL_texture) { this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, false, this.runtime.linearSampling, this.texture_pixelformat); } var i, len; for (i = 0, len = this.instances.length; i < len; i++) this.instances[i].webGL_texture = this.webGL_texture; }; typeProto.unloadTextures = function () { if (this.is_family || this.instances.length || !this.webGL_texture) return; this.runtime.glwrap.deleteTexture(this.webGL_texture); this.webGL_texture = null; }; typeProto.preloadCanvas2D = function (ctx) { ctx.drawImage(this.texture_img, 0, 0); }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onDestroy = function() { freeAllLines (this.lines); freeAllClip (this.clipList); freeAllClipUV(this.clipUV); cr.wipe(this.characterWidthList); }; instanceProto.onCreate = function() { this.texture_img = this.type.texture_img; this.characterWidth = this.properties[0]; this.characterHeight = this.properties[1]; this.characterSet = this.properties[2]; this.text = this.properties[3]; this.characterScale = this.properties[4]; this.visible = (this.properties[5] === 0); // 0=visible, 1=invisible this.halign = this.properties[6]/2.0; // 0=left, 1=center, 2=right this.valign = this.properties[7]/2.0; // 0=top, 1=center, 2=bottom this.wrapbyword = (this.properties[9] === 0); // 0=word, 1=character this.characterSpacing = this.properties[10]; this.lineHeight = this.properties[11]; this.textWidth = 0; this.textHeight = 0; this.charWidthJSON = this.properties[12]; this.spaceWidth = this.properties[13]; console.log(this.charWidthJSON); jText = this.charWidthJSON; if (this.recycled) { this.lines.length = 0; cr.wipe(this.clipList); cr.wipe(this.clipUV); cr.wipe(this.characterWidthList); } else { this.lines = []; this.clipList = {}; this.clipUV = {}; this.characterWidthList = {}; } try{ if(this.charWidthJSON){ if(this.charWidthJSON.indexOf('""c2array""') !== -1) { var jStr = jQuery.parseJSON(this.charWidthJSON.replace(/""/g,'"')); var l = jStr.size[1]; for(var s = 0; s < l; s++) { var cs = jStr.data[1][s][0]; var w = jStr.data[0][s][0]; for(var c = 0; c < cs.length; c++) { this.characterWidthList[cs.charAt(c)] = w } } } else { var jStr = jQuery.parseJSON(this.charWidthJSON); var l = jStr.length; for(var s = 0; s < l; s++) { var cs = jStr[s][1]; var w = jStr[s][0]; for(var c = 0; c < cs.length; c++) { this.characterWidthList[cs.charAt(c)] = w } } } } if(this.spaceWidth !== -1) { this.characterWidthList[' '] = this.spaceWidth; } } catch(e){ if(window.console && window.console.log) { window.console.log('SpriteFont+ Failure: ' + e); } } this.text_changed = true; this.lastwrapwidth = this.width; if (this.runtime.glwrap) { if (!this.type.webGL_texture) { this.type.webGL_texture = this.runtime.glwrap.loadTexture(this.type.texture_img, false, this.runtime.linearSampling, this.type.texture_pixelformat); } this.webGL_texture = this.type.webGL_texture; } this.SplitSheet(); }; instanceProto.saveToJSON = function () { var save = { "t": this.text, "csc": this.characterScale, "csp": this.characterSpacing, "lh": this.lineHeight, "tw": this.textWidth, "th": this.textHeight, "lrt": this.last_render_tick, "cw": {} }; for (var ch in this.characterWidthList) save["cw"][ch] = this.characterWidthList[ch]; return save; }; instanceProto.loadFromJSON = function (o) { this.text = o["t"]; this.characterScale = o["csc"]; this.characterSpacing = o["csp"]; this.lineHeight = o["lh"]; this.textWidth = o["tw"]; this.textHeight = o["th"]; this.last_render_tick = o["lrt"]; for(var ch in o["cw"]) this.characterWidthList[ch] = o["cw"][ch]; this.text_changed = true; this.lastwrapwidth = this.width; }; function trimRight(text) { return text.replace(/\s\s*$/, ''); } var MAX_CACHE_SIZE = 1000; function alloc(cache,Constructor) { if (cache.length) return cache.pop(); else return new Constructor(); } function free(cache,data) { if (cache.length < MAX_CACHE_SIZE) { cache.push(data); } } function freeAll(cache,dataList,isArray) { if (isArray) { var i, len; for (i = 0, len = dataList.length; i < len; i++) { free(cache,dataList[i]); } dataList.length = 0; } else { var prop; for(prop in dataList) { if(Object.prototype.hasOwnProperty.call(dataList,prop)) { free(cache,dataList[prop]); delete dataList[prop]; } } } } function addLine(inst,lineIndex,cur_line) { var lines = inst.lines; var line; cur_line = trimRight(cur_line); if (lineIndex >= lines.length) lines.push(allocLine()); line = lines[lineIndex]; line.text = cur_line; line.width = inst.measureWidth(cur_line); inst.textWidth = cr.max(inst.textWidth,line.width); } var linesCache = []; function allocLine() { return alloc(linesCache,Object); } function freeLine(l) { free(linesCache,l); } function freeAllLines(arr) { freeAll(linesCache,arr,true); } function addClip(obj,property,x,y,w,h) { if (obj[property] === undefined) { obj[property] = alloc(clipCache,Object); } obj[property].x = x; obj[property].y = y; obj[property].w = w; obj[property].h = h; } var clipCache = []; function allocClip() { return alloc(clipCache,Object); } function freeAllClip(obj) { freeAll(clipCache,obj,false);} function addClipUV(obj,property,left,top,right,bottom) { if (obj[property] === undefined) { obj[property] = alloc(clipUVCache,cr.rect); } obj[property].left = left; obj[property].top = top; obj[property].right = right; obj[property].bottom = bottom; } var clipUVCache = []; function allocClipUV() { return alloc(clipUVCache,cr.rect);} function freeAllClipUV(obj) { freeAll(clipUVCache,obj,false);} instanceProto.SplitSheet = function() { var texture = this.texture_img; var texWidth = texture.width; var texHeight = texture.height; var charWidth = this.characterWidth; var charHeight = this.characterHeight; var charU = charWidth /texWidth; var charV = charHeight/texHeight; var charSet = this.characterSet ; var cols = Math.floor(texWidth/charWidth); var rows = Math.floor(texHeight/charHeight); for ( var c = 0; c < charSet.length; c++) { if (c >= cols * rows) break; var x = c%cols; var y = Math.floor(c/cols); var letter = charSet.charAt(c); if (this.runtime.glwrap) { addClipUV( this.clipUV, letter, x * charU , y * charV , (x+1) * charU , (y+1) * charV ); } else { addClip( this.clipList, letter, x * charWidth, y * charHeight, charWidth, charHeight ); } } }; /* * Word-Wrapping */ var wordsCache = []; pluginProto.TokeniseWords = function (text) { wordsCache.length = 0; var cur_word = ""; var ch; var i = 0; while (i < text.length) { ch = text.charAt(i); if (ch === "\n") { if (cur_word.length) { wordsCache.push(cur_word); cur_word = ""; } wordsCache.push("\n"); ++i; } else if (ch === " " || ch === "\t" || ch === "-") { do { cur_word += text.charAt(i); i++; } while (i < text.length && (text.charAt(i) === " " || text.charAt(i) === "\t")); wordsCache.push(cur_word); cur_word = ""; } else if (i < text.length) { cur_word += ch; i++; } } if (cur_word.length) wordsCache.push(cur_word); }; pluginProto.WordWrap = function (inst) { var text = inst.text; var lines = inst.lines; if (!text || !text.length) { freeAllLines(lines); return; } var width = inst.width; if (width <= 2.0) { freeAllLines(lines); return; } var charWidth = inst.characterWidth; var charScale = inst.characterScale; var charSpacing = inst.characterSpacing; if ( (text.length * (charWidth * charScale + charSpacing) - charSpacing) <= width && text.indexOf("\n") === -1) { var all_width = inst.measureWidth(text); if (all_width <= width) { freeAllLines(lines); lines.push(allocLine()); lines[0].text = text; lines[0].width = all_width; inst.textWidth = all_width; inst.textHeight = inst.characterHeight * charScale + inst.lineHeight; return; } } var wrapbyword = inst.wrapbyword; this.WrapText(inst); inst.textHeight = lines.length * (inst.characterHeight * charScale + inst.lineHeight); }; pluginProto.WrapText = function (inst) { var wrapbyword = inst.wrapbyword; var text = inst.text; var lines = inst.lines; var width = inst.width; var wordArray; if (wrapbyword) { this.TokeniseWords(text); // writes to wordsCache wordArray = wordsCache; } else { wordArray = text; } var cur_line = ""; var prev_line; var line_width; var i; var lineIndex = 0; var line; var ignore_newline = false; for (i = 0; i < wordArray.length; i++) { if (wordArray[i] === "\n") { if (ignore_newline === true) { ignore_newline = false; } else { addLine(inst,lineIndex,cur_line); lineIndex++; } cur_line = ""; continue; } ignore_newline = false; prev_line = cur_line; cur_line += wordArray[i]; line_width = inst.measureWidth(trimRight(cur_line)); if (line_width > width) { if (prev_line === "") { addLine(inst,lineIndex,cur_line); cur_line = ""; ignore_newline = true; } else { addLine(inst,lineIndex,prev_line); cur_line = wordArray[i]; } lineIndex++; if (!wrapbyword && cur_line === " ") cur_line = ""; } } if (trimRight(cur_line).length) { addLine(inst,lineIndex,cur_line); lineIndex++; } for (i = lineIndex; i < lines.length; i++) freeLine(lines[i]); lines.length = lineIndex; }; instanceProto.measureWidth = function(text) { var spacing = this.characterSpacing; var len = text.length; var width = 0; for (var i = 0; i < len; i++) { width += this.getCharacterWidth(text.charAt(i)) * this.characterScale + spacing; } width -= (width > 0) ? spacing : 0; return width; }; /***/ instanceProto.getCharacterWidth = function(character) { var widthList = this.characterWidthList; if (widthList[character] !== undefined) { return widthList[character]; } else { return this.characterWidth; } }; instanceProto.rebuildText = function() { if (this.text_changed || this.width !== this.lastwrapwidth) { this.textWidth = 0; this.textHeight = 0; this.type.plugin.WordWrap(this); this.text_changed = false; this.lastwrapwidth = this.width; } }; var EPSILON = 0.00001; instanceProto.draw = function(ctx, glmode) { var texture = this.texture_img; if (this.text !== "" && texture != null) { this.rebuildText(); if (this.height < this.characterHeight*this.characterScale + this.lineHeight) { return; } ctx.globalAlpha = this.opacity; var myx = this.x; var myy = this.y; if (this.runtime.pixel_rounding) { myx = (myx + 0.5) | 0; myy = (myy + 0.5) | 0; } ctx.save(); ctx.translate(myx, myy); ctx.rotate(this.angle); var ha = this.halign; var va = this.valign; var scale = this.characterScale; var charHeight = this.characterHeight * scale; var lineHeight = this.lineHeight; var charSpace = this.characterSpacing; var lines = this.lines; var textHeight = this.textHeight; var halign; var valign = va * cr.max(0,(this.height - textHeight)); var offx = -(this.hotspotX * this.width); var offy = -(this.hotspotY * this.height); offy += valign; var drawX ; var drawY = offy; for(var i = 0; i < lines.length; i++) { var line = lines[i].text; var len = lines[i].width; halign = ha * cr.max(0,this.width - len); drawX = offx + halign; drawY += lineHeight; for(var j = 0; j < line.length; j++) { var letter = line.charAt(j); var clip = this.clipList[letter]; if ( drawX + this.getCharacterWidth(letter) * scale > this.width + EPSILON ) { break; } if (clip !== undefined) { ctx.drawImage( this.texture_img, clip.x, clip.y, clip.w, clip.h, Math.round(drawX),Math.round(drawY),clip.w*scale,clip.h*scale); } drawX += this.getCharacterWidth(letter) * scale + charSpace; } drawY += charHeight; if ( drawY + charHeight + lineHeight > this.height) { break; } } ctx.restore(); } }; var dQuad = new cr.quad(); function rotateQuad(quad,cosa,sina) { var x_temp; x_temp = (quad.tlx * cosa) - (quad.tly * sina); quad.tly = (quad.tly * cosa) + (quad.tlx * sina); quad.tlx = x_temp; x_temp = (quad.trx * cosa) - (quad.try_ * sina); quad.try_ = (quad.try_ * cosa) + (quad.trx * sina); quad.trx = x_temp; x_temp = (quad.blx * cosa) - (quad.bly * sina); quad.bly = (quad.bly * cosa) + (quad.blx * sina); quad.blx = x_temp; x_temp = (quad.brx * cosa) - (quad.bry * sina); quad.bry = (quad.bry * cosa) + (quad.brx * sina); quad.brx = x_temp; } instanceProto.drawGL = function(glw) { glw.setTexture(this.webGL_texture); glw.setOpacity(this.opacity); if (this.text !== "") { this.rebuildText(); if (this.height < this.characterHeight*this.characterScale + this.lineHeight) { return; } this.update_bbox(); var q = this.bquad; var ox = 0; var oy = 0; if (this.runtime.pixel_rounding) { ox = ((this.x + 0.5) | 0) - this.x; oy = ((this.y + 0.5) | 0) - this.y; } var angle = this.angle; var ha = this.halign; var va = this.valign; var scale = this.characterScale; var charHeight = this.characterHeight * scale; // to precalculate in onCreate or on change var lineHeight = this.lineHeight; var charSpace = this.characterSpacing; var lines = this.lines; var textHeight = this.textHeight; var cosa,sina; if (angle !== 0) { cosa = Math.cos(angle); sina = Math.sin(angle); } var halign; var valign = va * cr.max(0,(this.height - textHeight)); var offx = q.tlx + ox; var offy = q.tly + oy; var drawX ; var drawY = valign; for(var i = 0; i < lines.length; i++) { var line = lines[i].text; var lineWidth = lines[i].width; halign = ha * cr.max(0,this.width - lineWidth); drawX = halign; drawY += lineHeight; for(var j = 0; j < line.length; j++) { var letter = line.charAt(j); var clipUV = this.clipUV[letter]; if ( drawX + this.getCharacterWidth(letter) * scale > this.width + EPSILON) { break; } if (clipUV !== undefined) { var clipWidth = this.characterWidth*scale; var clipHeight = this.characterHeight*scale; dQuad.tlx = drawX; dQuad.tly = drawY; dQuad.trx = drawX + clipWidth; dQuad.try_ = drawY ; dQuad.blx = drawX; dQuad.bly = drawY + clipHeight; dQuad.brx = drawX + clipWidth; dQuad.bry = drawY + clipHeight; if(angle !== 0) { rotateQuad(dQuad,cosa,sina); } dQuad.offset(offx,offy); glw.quadTex( dQuad.tlx, dQuad.tly, dQuad.trx, dQuad.try_, dQuad.brx, dQuad.bry, dQuad.blx, dQuad.bly, clipUV ); } drawX += this.getCharacterWidth(letter) * scale + charSpace; } drawY += charHeight; if ( drawY + charHeight + lineHeight > this.height) { break; } } } }; function Cnds() {} Cnds.prototype.CompareText = function(text_to_compare, case_sensitive) { if (case_sensitive) return this.text == text_to_compare; else return cr.equals_nocase(this.text, text_to_compare); }; pluginProto.cnds = new Cnds(); function Acts() {} Acts.prototype.SetText = function(param) { if (cr.is_number(param) && param < 1e9) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_set = param.toString(); if (this.text !== text_to_set) { this.text = text_to_set; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.AppendText = function(param) { if (cr.is_number(param)) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_append = param.toString(); if (text_to_append) // not empty { this.text += text_to_append; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetScale = function(param) { if (param !== this.characterScale) { this.characterScale = param; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetCharacterSpacing = function(param) { if (param !== this.CharacterSpacing) { this.characterSpacing = param; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetLineHeight = function(param) { if (param !== this.lineHeight) { this.lineHeight = param; this.text_changed = true; this.runtime.redraw = true; } }; instanceProto.SetCharWidth = function(character,width) { var w = parseInt(width,10); if (this.characterWidthList[character] !== w) { this.characterWidthList[character] = w; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetCharacterWidth = function(characterSet,width) { if (characterSet !== "") { for(var c = 0; c < characterSet.length; c++) { this.SetCharWidth(characterSet.charAt(c),width); } } }; Acts.prototype.SetEffect = function (effect) { this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; pluginProto.acts = new Acts(); function Exps() {} Exps.prototype.CharacterWidth = function(ret,character) { ret.set_int(this.getCharacterWidth(character)); }; Exps.prototype.CharacterHeight = function(ret) { ret.set_int(this.characterHeight); }; Exps.prototype.CharacterScale = function(ret) { ret.set_float(this.characterScale); }; Exps.prototype.CharacterSpacing = function(ret) { ret.set_int(this.characterSpacing); }; Exps.prototype.LineHeight = function(ret) { ret.set_int(this.lineHeight); }; Exps.prototype.Text = function(ret) { ret.set_string(this.text); }; Exps.prototype.TextWidth = function (ret) { this.rebuildText(); ret.set_float(this.textWidth); }; Exps.prototype.TextHeight = function (ret) { this.rebuildText(); ret.set_float(this.textHeight); }; pluginProto.exps = new Exps(); }()); /* global cr,log,assert2 */ /* jshint globalstrict: true */ /* jshint strict: true */ ; ; cr.plugins_.Spritefont2 = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Spritefont2.prototype; pluginProto.onCreate = function () { }; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { if (this.is_family) return; this.texture_img = new Image(); this.runtime.waitForImageLoad(this.texture_img, this.texture_file); this.webGL_texture = null; }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; this.webGL_texture = null; }; typeProto.onRestoreWebGLContext = function () { if (this.is_family || !this.instances.length) return; if (!this.webGL_texture) { this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, false, this.runtime.linearSampling, this.texture_pixelformat); } var i, len; for (i = 0, len = this.instances.length; i < len; i++) this.instances[i].webGL_texture = this.webGL_texture; }; typeProto.unloadTextures = function () { if (this.is_family || this.instances.length || !this.webGL_texture) return; this.runtime.glwrap.deleteTexture(this.webGL_texture); this.webGL_texture = null; }; typeProto.preloadCanvas2D = function (ctx) { ctx.drawImage(this.texture_img, 0, 0); }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onDestroy = function() { freeAllLines (this.lines); freeAllClip (this.clipList); freeAllClipUV(this.clipUV); cr.wipe(this.characterWidthList); }; instanceProto.onCreate = function() { this.texture_img = this.type.texture_img; this.characterWidth = this.properties[0]; this.characterHeight = this.properties[1]; this.characterSet = this.properties[2]; this.text = this.properties[3]; this.characterScale = this.properties[4]; this.visible = (this.properties[5] === 0); // 0=visible, 1=invisible this.halign = this.properties[6]/2.0; // 0=left, 1=center, 2=right this.valign = this.properties[7]/2.0; // 0=top, 1=center, 2=bottom this.wrapbyword = (this.properties[9] === 0); // 0=word, 1=character this.characterSpacing = this.properties[10]; this.lineHeight = this.properties[11]; this.textWidth = 0; this.textHeight = 0; if (this.recycled) { cr.clearArray(this.lines); cr.wipe(this.clipList); cr.wipe(this.clipUV); cr.wipe(this.characterWidthList); } else { this.lines = []; this.clipList = {}; this.clipUV = {}; this.characterWidthList = {}; } this.text_changed = true; this.lastwrapwidth = this.width; if (this.runtime.glwrap) { if (!this.type.webGL_texture) { this.type.webGL_texture = this.runtime.glwrap.loadTexture(this.type.texture_img, false, this.runtime.linearSampling, this.type.texture_pixelformat); } this.webGL_texture = this.type.webGL_texture; } this.SplitSheet(); }; instanceProto.saveToJSON = function () { var save = { "t": this.text, "csc": this.characterScale, "csp": this.characterSpacing, "lh": this.lineHeight, "tw": this.textWidth, "th": this.textHeight, "lrt": this.last_render_tick, "ha": this.halign, "va": this.valign, "cw": {} }; for (var ch in this.characterWidthList) save["cw"][ch] = this.characterWidthList[ch]; return save; }; instanceProto.loadFromJSON = function (o) { this.text = o["t"]; this.characterScale = o["csc"]; this.characterSpacing = o["csp"]; this.lineHeight = o["lh"]; this.textWidth = o["tw"]; this.textHeight = o["th"]; this.last_render_tick = o["lrt"]; if (o.hasOwnProperty("ha")) this.halign = o["ha"]; if (o.hasOwnProperty("va")) this.valign = o["va"]; for(var ch in o["cw"]) this.characterWidthList[ch] = o["cw"][ch]; this.text_changed = true; this.lastwrapwidth = this.width; }; function trimRight(text) { return text.replace(/\s\s*$/, ''); } var MAX_CACHE_SIZE = 1000; function alloc(cache,Constructor) { if (cache.length) return cache.pop(); else return new Constructor(); } function free(cache,data) { if (cache.length < MAX_CACHE_SIZE) { cache.push(data); } } function freeAll(cache,dataList,isArray) { if (isArray) { var i, len; for (i = 0, len = dataList.length; i < len; i++) { free(cache,dataList[i]); } cr.clearArray(dataList); } else { var prop; for(prop in dataList) { if(Object.prototype.hasOwnProperty.call(dataList,prop)) { free(cache,dataList[prop]); delete dataList[prop]; } } } } function addLine(inst,lineIndex,cur_line) { var lines = inst.lines; var line; cur_line = trimRight(cur_line); if (lineIndex >= lines.length) lines.push(allocLine()); line = lines[lineIndex]; line.text = cur_line; line.width = inst.measureWidth(cur_line); inst.textWidth = cr.max(inst.textWidth,line.width); } var linesCache = []; function allocLine() { return alloc(linesCache,Object); } function freeLine(l) { free(linesCache,l); } function freeAllLines(arr) { freeAll(linesCache,arr,true); } function addClip(obj,property,x,y,w,h) { if (obj[property] === undefined) { obj[property] = alloc(clipCache,Object); } obj[property].x = x; obj[property].y = y; obj[property].w = w; obj[property].h = h; } var clipCache = []; function allocClip() { return alloc(clipCache,Object); } function freeAllClip(obj) { freeAll(clipCache,obj,false);} function addClipUV(obj,property,left,top,right,bottom) { if (obj[property] === undefined) { obj[property] = alloc(clipUVCache,cr.rect); } obj[property].left = left; obj[property].top = top; obj[property].right = right; obj[property].bottom = bottom; } var clipUVCache = []; function allocClipUV() { return alloc(clipUVCache,cr.rect);} function freeAllClipUV(obj) { freeAll(clipUVCache,obj,false);} instanceProto.SplitSheet = function() { var texture = this.texture_img; var texWidth = texture.width; var texHeight = texture.height; var charWidth = this.characterWidth; var charHeight = this.characterHeight; var charU = charWidth /texWidth; var charV = charHeight/texHeight; var charSet = this.characterSet ; var cols = Math.floor(texWidth/charWidth); var rows = Math.floor(texHeight/charHeight); for ( var c = 0; c < charSet.length; c++) { if (c >= cols * rows) break; var x = c%cols; var y = Math.floor(c/cols); var letter = charSet.charAt(c); if (this.runtime.glwrap) { addClipUV( this.clipUV, letter, x * charU , y * charV , (x+1) * charU , (y+1) * charV ); } else { addClip( this.clipList, letter, x * charWidth, y * charHeight, charWidth, charHeight ); } } }; /* * Word-Wrapping */ var wordsCache = []; pluginProto.TokeniseWords = function (text) { cr.clearArray(wordsCache); var cur_word = ""; var ch; var i = 0; while (i < text.length) { ch = text.charAt(i); if (ch === "\n") { if (cur_word.length) { wordsCache.push(cur_word); cur_word = ""; } wordsCache.push("\n"); ++i; } else if (ch === " " || ch === "\t" || ch === "-") { do { cur_word += text.charAt(i); i++; } while (i < text.length && (text.charAt(i) === " " || text.charAt(i) === "\t")); wordsCache.push(cur_word); cur_word = ""; } else if (i < text.length) { cur_word += ch; i++; } } if (cur_word.length) wordsCache.push(cur_word); }; pluginProto.WordWrap = function (inst) { var text = inst.text; var lines = inst.lines; if (!text || !text.length) { freeAllLines(lines); return; } var width = inst.width; if (width <= 2.0) { freeAllLines(lines); return; } var charWidth = inst.characterWidth; var charScale = inst.characterScale; var charSpacing = inst.characterSpacing; if ( (text.length * (charWidth * charScale + charSpacing) - charSpacing) <= width && text.indexOf("\n") === -1) { var all_width = inst.measureWidth(text); if (all_width <= width) { freeAllLines(lines); lines.push(allocLine()); lines[0].text = text; lines[0].width = all_width; inst.textWidth = all_width; inst.textHeight = inst.characterHeight * charScale + inst.lineHeight; return; } } var wrapbyword = inst.wrapbyword; this.WrapText(inst); inst.textHeight = lines.length * (inst.characterHeight * charScale + inst.lineHeight); }; pluginProto.WrapText = function (inst) { var wrapbyword = inst.wrapbyword; var text = inst.text; var lines = inst.lines; var width = inst.width; var wordArray; if (wrapbyword) { this.TokeniseWords(text); // writes to wordsCache wordArray = wordsCache; } else { wordArray = text; } var cur_line = ""; var prev_line; var line_width; var i; var lineIndex = 0; var line; var ignore_newline = false; for (i = 0; i < wordArray.length; i++) { if (wordArray[i] === "\n") { if (ignore_newline === true) { ignore_newline = false; } else { addLine(inst,lineIndex,cur_line); lineIndex++; } cur_line = ""; continue; } ignore_newline = false; prev_line = cur_line; cur_line += wordArray[i]; line_width = inst.measureWidth(trimRight(cur_line)); if (line_width > width) { if (prev_line === "") { addLine(inst,lineIndex,cur_line); cur_line = ""; ignore_newline = true; } else { addLine(inst,lineIndex,prev_line); cur_line = wordArray[i]; } lineIndex++; if (!wrapbyword && cur_line === " ") cur_line = ""; } } if (trimRight(cur_line).length) { addLine(inst,lineIndex,cur_line); lineIndex++; } for (i = lineIndex; i < lines.length; i++) freeLine(lines[i]); lines.length = lineIndex; }; instanceProto.measureWidth = function(text) { var spacing = this.characterSpacing; var len = text.length; var width = 0; for (var i = 0; i < len; i++) { width += this.getCharacterWidth(text.charAt(i)) * this.characterScale + spacing; } width -= (width > 0) ? spacing : 0; return width; }; /***/ instanceProto.getCharacterWidth = function(character) { var widthList = this.characterWidthList; if (widthList[character] !== undefined) { return widthList[character]; } else { return this.characterWidth; } }; instanceProto.rebuildText = function() { if (this.text_changed || this.width !== this.lastwrapwidth) { this.textWidth = 0; this.textHeight = 0; this.type.plugin.WordWrap(this); this.text_changed = false; this.lastwrapwidth = this.width; } }; var EPSILON = 0.00001; instanceProto.draw = function(ctx, glmode) { var texture = this.texture_img; if (this.text !== "" && texture != null) { this.rebuildText(); if (this.height < this.characterHeight*this.characterScale + this.lineHeight) { return; } ctx.globalAlpha = this.opacity; var myx = this.x; var myy = this.y; if (this.runtime.pixel_rounding) { myx = Math.round(myx); myy = Math.round(myy); } var viewLeft = this.layer.viewLeft; var viewTop = this.layer.viewTop; var viewRight = this.layer.viewRight; var viewBottom = this.layer.viewBottom; ctx.save(); ctx.translate(myx, myy); ctx.rotate(this.angle); var angle = this.angle; var ha = this.halign; var va = this.valign; var scale = this.characterScale; var charHeight = this.characterHeight * scale; var lineHeight = this.lineHeight; var charSpace = this.characterSpacing; var lines = this.lines; var textHeight = this.textHeight; var letterWidth; var halign; var valign = va * cr.max(0,(this.height - textHeight)); var offx = -(this.hotspotX * this.width); var offy = -(this.hotspotY * this.height); offy += valign; var drawX ; var drawY = offy; var roundX, roundY; for(var i = 0; i < lines.length; i++) { var line = lines[i].text; var len = lines[i].width; halign = ha * cr.max(0,this.width - len); drawX = offx + halign; drawY += lineHeight; if (angle === 0 && myy + drawY + charHeight < viewTop) { drawY += charHeight; continue; } for(var j = 0; j < line.length; j++) { var letter = line.charAt(j); letterWidth = this.getCharacterWidth(letter); var clip = this.clipList[letter]; if (angle === 0 && myx + drawX + letterWidth * scale + charSpace < viewLeft) { drawX += letterWidth * scale + charSpace; continue; } if ( drawX + letterWidth * scale > this.width + EPSILON ) { break; } if (clip !== undefined) { roundX = drawX; roundY = drawY; if (angle === 0 && scale === 1) { roundX = Math.round(roundX); roundY = Math.round(roundY); } ctx.drawImage( this.texture_img, clip.x, clip.y, clip.w, clip.h, roundX,roundY,clip.w*scale,clip.h*scale); } drawX += letterWidth * scale + charSpace; if (angle === 0 && myx + drawX > viewRight) break; } drawY += charHeight; if (angle === 0 && (drawY + charHeight + lineHeight > this.height || myy + drawY > viewBottom)) { break; } } ctx.restore(); } }; var dQuad = new cr.quad(); function rotateQuad(quad,cosa,sina) { var x_temp; x_temp = (quad.tlx * cosa) - (quad.tly * sina); quad.tly = (quad.tly * cosa) + (quad.tlx * sina); quad.tlx = x_temp; x_temp = (quad.trx * cosa) - (quad.try_ * sina); quad.try_ = (quad.try_ * cosa) + (quad.trx * sina); quad.trx = x_temp; x_temp = (quad.blx * cosa) - (quad.bly * sina); quad.bly = (quad.bly * cosa) + (quad.blx * sina); quad.blx = x_temp; x_temp = (quad.brx * cosa) - (quad.bry * sina); quad.bry = (quad.bry * cosa) + (quad.brx * sina); quad.brx = x_temp; } instanceProto.drawGL = function(glw) { glw.setTexture(this.webGL_texture); glw.setOpacity(this.opacity); if (!this.text) return; this.rebuildText(); if (this.height < this.characterHeight*this.characterScale + this.lineHeight) { return; } this.update_bbox(); var q = this.bquad; var ox = 0; var oy = 0; if (this.runtime.pixel_rounding) { ox = Math.round(this.x) - this.x; oy = Math.round(this.y) - this.y; } var viewLeft = this.layer.viewLeft; var viewTop = this.layer.viewTop; var viewRight = this.layer.viewRight; var viewBottom = this.layer.viewBottom; var angle = this.angle; var ha = this.halign; var va = this.valign; var scale = this.characterScale; var charHeight = this.characterHeight * scale; // to precalculate in onCreate or on change var lineHeight = this.lineHeight; var charSpace = this.characterSpacing; var lines = this.lines; var textHeight = this.textHeight; var letterWidth; var cosa,sina; if (angle !== 0) { cosa = Math.cos(angle); sina = Math.sin(angle); } var halign; var valign = va * cr.max(0,(this.height - textHeight)); var offx = q.tlx + ox; var offy = q.tly + oy; var drawX ; var drawY = valign; var roundX, roundY; for(var i = 0; i < lines.length; i++) { var line = lines[i].text; var lineWidth = lines[i].width; halign = ha * cr.max(0,this.width - lineWidth); drawX = halign; drawY += lineHeight; if (angle === 0 && offy + drawY + charHeight < viewTop) { drawY += charHeight; continue; } for(var j = 0; j < line.length; j++) { var letter = line.charAt(j); letterWidth = this.getCharacterWidth(letter); var clipUV = this.clipUV[letter]; if (angle === 0 && offx + drawX + letterWidth * scale + charSpace < viewLeft) { drawX += letterWidth * scale + charSpace; continue; } if (drawX + letterWidth * scale > this.width + EPSILON) { break; } if (clipUV !== undefined) { var clipWidth = this.characterWidth*scale; var clipHeight = this.characterHeight*scale; roundX = drawX; roundY = drawY; if (angle === 0 && scale === 1) { roundX = Math.round(roundX); roundY = Math.round(roundY); } dQuad.tlx = roundX; dQuad.tly = roundY; dQuad.trx = roundX + clipWidth; dQuad.try_ = roundY ; dQuad.blx = roundX; dQuad.bly = roundY + clipHeight; dQuad.brx = roundX + clipWidth; dQuad.bry = roundY + clipHeight; if(angle !== 0) { rotateQuad(dQuad,cosa,sina); } dQuad.offset(offx,offy); glw.quadTex( dQuad.tlx, dQuad.tly, dQuad.trx, dQuad.try_, dQuad.brx, dQuad.bry, dQuad.blx, dQuad.bly, clipUV ); } drawX += letterWidth * scale + charSpace; if (angle === 0 && offx + drawX > viewRight) break; } drawY += charHeight; if (angle === 0 && (drawY + charHeight + lineHeight > this.height || offy + drawY > viewBottom)) { break; } } }; function Cnds() {} Cnds.prototype.CompareText = function(text_to_compare, case_sensitive) { if (case_sensitive) return this.text == text_to_compare; else return cr.equals_nocase(this.text, text_to_compare); }; pluginProto.cnds = new Cnds(); function Acts() {} Acts.prototype.SetText = function(param) { if (cr.is_number(param) && param < 1e9) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_set = param.toString(); if (this.text !== text_to_set) { this.text = text_to_set; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.AppendText = function(param) { if (cr.is_number(param)) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_append = param.toString(); if (text_to_append) // not empty { this.text += text_to_append; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetScale = function(param) { if (param !== this.characterScale) { this.characterScale = param; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetCharacterSpacing = function(param) { if (param !== this.CharacterSpacing) { this.characterSpacing = param; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetLineHeight = function(param) { if (param !== this.lineHeight) { this.lineHeight = param; this.text_changed = true; this.runtime.redraw = true; } }; instanceProto.SetCharWidth = function(character,width) { var w = parseInt(width,10); if (this.characterWidthList[character] !== w) { this.characterWidthList[character] = w; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetCharacterWidth = function(characterSet,width) { if (characterSet !== "") { for(var c = 0; c < characterSet.length; c++) { this.SetCharWidth(characterSet.charAt(c),width); } } }; Acts.prototype.SetEffect = function (effect) { this.blend_mode = effect; this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; Acts.prototype.SetHAlign = function (a) { this.halign = a / 2.0; this.text_changed = true; this.runtime.redraw = true; }; Acts.prototype.SetVAlign = function (a) { this.valign = a / 2.0; this.text_changed = true; this.runtime.redraw = true; }; pluginProto.acts = new Acts(); function Exps() {} Exps.prototype.CharacterWidth = function(ret,character) { ret.set_int(this.getCharacterWidth(character)); }; Exps.prototype.CharacterHeight = function(ret) { ret.set_int(this.characterHeight); }; Exps.prototype.CharacterScale = function(ret) { ret.set_float(this.characterScale); }; Exps.prototype.CharacterSpacing = function(ret) { ret.set_int(this.characterSpacing); }; Exps.prototype.LineHeight = function(ret) { ret.set_int(this.lineHeight); }; Exps.prototype.Text = function(ret) { ret.set_string(this.text); }; Exps.prototype.TextWidth = function (ret) { this.rebuildText(); ret.set_float(this.textWidth); }; Exps.prototype.TextHeight = function (ret) { this.rebuildText(); ret.set_float(this.textHeight); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Text = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Text.prototype; pluginProto.onCreate = function () { pluginProto.acts.SetWidth = function (w) { if (this.width !== w) { this.width = w; this.text_changed = true; // also recalculate text wrapping this.set_bbox_changed(); } }; }; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; var i, len, inst; for (i = 0, len = this.instances.length; i < len; i++) { inst = this.instances[i]; inst.mycanvas = null; inst.myctx = null; inst.mytex = null; } }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; if (this.recycled) cr.clearArray(this.lines); else this.lines = []; // for word wrapping this.text_changed = true; }; var instanceProto = pluginProto.Instance.prototype; var requestedWebFonts = {}; // already requested web fonts have an entry here instanceProto.onCreate = function() { this.text = this.properties[0]; this.visible = (this.properties[1] === 0); // 0=visible, 1=invisible this.font = this.properties[2]; this.color = this.properties[3]; this.halign = this.properties[4]; // 0=left, 1=center, 2=right this.valign = this.properties[5]; // 0=top, 1=center, 2=bottom this.wrapbyword = (this.properties[7] === 0); // 0=word, 1=character this.lastwidth = this.width; this.lastwrapwidth = this.width; this.lastheight = this.height; this.line_height_offset = this.properties[8]; this.facename = ""; this.fontstyle = ""; this.ptSize = 0; this.textWidth = 0; this.textHeight = 0; this.parseFont(); this.mycanvas = null; this.myctx = null; this.mytex = null; this.need_text_redraw = false; this.last_render_tick = this.runtime.tickcount; if (this.recycled) this.rcTex.set(0, 0, 1, 1); else this.rcTex = new cr.rect(0, 0, 1, 1); if (this.runtime.glwrap) this.runtime.tickMe(this); ; }; instanceProto.parseFont = function () { var arr = this.font.split(" "); var i; for (i = 0; i < arr.length; i++) { if (arr[i].substr(arr[i].length - 2, 2) === "pt") { this.ptSize = parseInt(arr[i].substr(0, arr[i].length - 2)); this.pxHeight = Math.ceil((this.ptSize / 72.0) * 96.0) + 4; // assume 96dpi... if (i > 0) this.fontstyle = arr[i - 1]; this.facename = arr[i + 1]; for (i = i + 2; i < arr.length; i++) this.facename += " " + arr[i]; break; } } }; instanceProto.saveToJSON = function () { return { "t": this.text, "f": this.font, "c": this.color, "ha": this.halign, "va": this.valign, "wr": this.wrapbyword, "lho": this.line_height_offset, "fn": this.facename, "fs": this.fontstyle, "ps": this.ptSize, "pxh": this.pxHeight, "tw": this.textWidth, "th": this.textHeight, "lrt": this.last_render_tick }; }; instanceProto.loadFromJSON = function (o) { this.text = o["t"]; this.font = o["f"]; this.color = o["c"]; this.halign = o["ha"]; this.valign = o["va"]; this.wrapbyword = o["wr"]; this.line_height_offset = o["lho"]; this.facename = o["fn"]; this.fontstyle = o["fs"]; this.ptSize = o["ps"]; this.pxHeight = o["pxh"]; this.textWidth = o["tw"]; this.textHeight = o["th"]; this.last_render_tick = o["lrt"]; this.text_changed = true; this.lastwidth = this.width; this.lastwrapwidth = this.width; this.lastheight = this.height; }; instanceProto.tick = function () { if (this.runtime.glwrap && this.mytex && (this.runtime.tickcount - this.last_render_tick >= 300)) { var layer = this.layer; this.update_bbox(); var bbox = this.bbox; if (bbox.right < layer.viewLeft || bbox.bottom < layer.viewTop || bbox.left > layer.viewRight || bbox.top > layer.viewBottom) { this.runtime.glwrap.deleteTexture(this.mytex); this.mytex = null; this.myctx = null; this.mycanvas = null; } } }; instanceProto.onDestroy = function () { this.myctx = null; this.mycanvas = null; if (this.runtime.glwrap && this.mytex) this.runtime.glwrap.deleteTexture(this.mytex); this.mytex = null; }; instanceProto.updateFont = function () { this.font = this.fontstyle + " " + this.ptSize.toString() + "pt " + this.facename; this.text_changed = true; this.runtime.redraw = true; }; instanceProto.draw = function(ctx, glmode) { ctx.font = this.font; ctx.textBaseline = "top"; ctx.fillStyle = this.color; ctx.globalAlpha = glmode ? 1 : this.opacity; var myscale = 1; if (glmode) { myscale = Math.abs(this.layer.getScale()); ctx.save(); ctx.scale(myscale, myscale); } if (this.text_changed || this.width !== this.lastwrapwidth) { this.type.plugin.WordWrap(this.text, this.lines, ctx, this.width, this.wrapbyword); this.text_changed = false; this.lastwrapwidth = this.width; } this.update_bbox(); var penX = glmode ? 0 : this.bquad.tlx; var penY = glmode ? 0 : this.bquad.tly; if (this.runtime.pixel_rounding) { penX = (penX + 0.5) | 0; penY = (penY + 0.5) | 0; } if (this.angle !== 0 && !glmode) { ctx.save(); ctx.translate(penX, penY); ctx.rotate(this.angle); penX = 0; penY = 0; } var endY = penY + this.height; var line_height = this.pxHeight; line_height += this.line_height_offset; var drawX; var i; if (this.valign === 1) // center penY += Math.max(this.height / 2 - (this.lines.length * line_height) / 2, 0); else if (this.valign === 2) // bottom penY += Math.max(this.height - (this.lines.length * line_height) - 2, 0); for (i = 0; i < this.lines.length; i++) { drawX = penX; if (this.halign === 1) // center drawX = penX + (this.width - this.lines[i].width) / 2; else if (this.halign === 2) // right drawX = penX + (this.width - this.lines[i].width); ctx.fillText(this.lines[i].text, drawX, penY); penY += line_height; if (penY >= endY - line_height) break; } if (this.angle !== 0 || glmode) ctx.restore(); this.last_render_tick = this.runtime.tickcount; }; instanceProto.drawGL = function(glw) { if (this.width < 1 || this.height < 1) return; var need_redraw = this.text_changed || this.need_text_redraw; this.need_text_redraw = false; var layer_scale = this.layer.getScale(); var layer_angle = this.layer.getAngle(); var rcTex = this.rcTex; var floatscaledwidth = layer_scale * this.width; var floatscaledheight = layer_scale * this.height; var scaledwidth = Math.ceil(floatscaledwidth); var scaledheight = Math.ceil(floatscaledheight); var absscaledwidth = Math.abs(scaledwidth); var absscaledheight = Math.abs(scaledheight); var halfw = this.runtime.draw_width / 2; var halfh = this.runtime.draw_height / 2; if (!this.myctx) { this.mycanvas = document.createElement("canvas"); this.mycanvas.width = absscaledwidth; this.mycanvas.height = absscaledheight; this.lastwidth = absscaledwidth; this.lastheight = absscaledheight; need_redraw = true; this.myctx = this.mycanvas.getContext("2d"); } if (absscaledwidth !== this.lastwidth || absscaledheight !== this.lastheight) { this.mycanvas.width = absscaledwidth; this.mycanvas.height = absscaledheight; if (this.mytex) { glw.deleteTexture(this.mytex); this.mytex = null; } need_redraw = true; } if (need_redraw) { this.myctx.clearRect(0, 0, absscaledwidth, absscaledheight); this.draw(this.myctx, true); if (!this.mytex) this.mytex = glw.createEmptyTexture(absscaledwidth, absscaledheight, this.runtime.linearSampling, this.runtime.isMobile); glw.videoToTexture(this.mycanvas, this.mytex, this.runtime.isMobile); } this.lastwidth = absscaledwidth; this.lastheight = absscaledheight; glw.setTexture(this.mytex); glw.setOpacity(this.opacity); glw.resetModelView(); glw.translate(-halfw, -halfh); glw.updateModelView(); var q = this.bquad; var tlx = this.layer.layerToCanvas(q.tlx, q.tly, true, true); var tly = this.layer.layerToCanvas(q.tlx, q.tly, false, true); var trx = this.layer.layerToCanvas(q.trx, q.try_, true, true); var try_ = this.layer.layerToCanvas(q.trx, q.try_, false, true); var brx = this.layer.layerToCanvas(q.brx, q.bry, true, true); var bry = this.layer.layerToCanvas(q.brx, q.bry, false, true); var blx = this.layer.layerToCanvas(q.blx, q.bly, true, true); var bly = this.layer.layerToCanvas(q.blx, q.bly, false, true); if (this.runtime.pixel_rounding || (this.angle === 0 && layer_angle === 0)) { var ox = ((tlx + 0.5) | 0) - tlx; var oy = ((tly + 0.5) | 0) - tly tlx += ox; tly += oy; trx += ox; try_ += oy; brx += ox; bry += oy; blx += ox; bly += oy; } if (this.angle === 0 && layer_angle === 0) { trx = tlx + scaledwidth; try_ = tly; brx = trx; bry = tly + scaledheight; blx = tlx; bly = bry; rcTex.right = 1; rcTex.bottom = 1; } else { rcTex.right = floatscaledwidth / scaledwidth; rcTex.bottom = floatscaledheight / scaledheight; } glw.quadTex(tlx, tly, trx, try_, brx, bry, blx, bly, rcTex); glw.resetModelView(); glw.scale(layer_scale, layer_scale); glw.rotateZ(-this.layer.getAngle()); glw.translate((this.layer.viewLeft + this.layer.viewRight) / -2, (this.layer.viewTop + this.layer.viewBottom) / -2); glw.updateModelView(); this.last_render_tick = this.runtime.tickcount; }; var wordsCache = []; pluginProto.TokeniseWords = function (text) { cr.clearArray(wordsCache); var cur_word = ""; var ch; var i = 0; while (i < text.length) { ch = text.charAt(i); if (ch === "\n") { if (cur_word.length) { wordsCache.push(cur_word); cur_word = ""; } wordsCache.push("\n"); ++i; } else if (ch === " " || ch === "\t" || ch === "-") { do { cur_word += text.charAt(i); i++; } while (i < text.length && (text.charAt(i) === " " || text.charAt(i) === "\t")); wordsCache.push(cur_word); cur_word = ""; } else if (i < text.length) { cur_word += ch; i++; } } if (cur_word.length) wordsCache.push(cur_word); }; var linesCache = []; function allocLine() { if (linesCache.length) return linesCache.pop(); else return {}; }; function freeLine(l) { linesCache.push(l); }; function freeAllLines(arr) { var i, len; for (i = 0, len = arr.length; i < len; i++) { freeLine(arr[i]); } cr.clearArray(arr); }; pluginProto.WordWrap = function (text, lines, ctx, width, wrapbyword) { if (!text || !text.length) { freeAllLines(lines); return; } if (width <= 2.0) { freeAllLines(lines); return; } if (text.length <= 100 && text.indexOf("\n") === -1) { var all_width = ctx.measureText(text).width; if (all_width <= width) { freeAllLines(lines); lines.push(allocLine()); lines[0].text = text; lines[0].width = all_width; return; } } this.WrapText(text, lines, ctx, width, wrapbyword); }; function trimSingleSpaceRight(str) { if (!str.length || str.charAt(str.length - 1) !== " ") return str; return str.substring(0, str.length - 1); }; pluginProto.WrapText = function (text, lines, ctx, width, wrapbyword) { var wordArray; if (wrapbyword) { this.TokeniseWords(text); // writes to wordsCache wordArray = wordsCache; } else wordArray = text; var cur_line = ""; var prev_line; var line_width; var i; var lineIndex = 0; var line; for (i = 0; i < wordArray.length; i++) { if (wordArray[i] === "\n") { if (lineIndex >= lines.length) lines.push(allocLine()); cur_line = trimSingleSpaceRight(cur_line); // for correct center/right alignment line = lines[lineIndex]; line.text = cur_line; line.width = ctx.measureText(cur_line).width; lineIndex++; cur_line = ""; continue; } prev_line = cur_line; cur_line += wordArray[i]; line_width = ctx.measureText(cur_line).width; if (line_width >= width) { if (lineIndex >= lines.length) lines.push(allocLine()); prev_line = trimSingleSpaceRight(prev_line); line = lines[lineIndex]; line.text = prev_line; line.width = ctx.measureText(prev_line).width; lineIndex++; cur_line = wordArray[i]; if (!wrapbyword && cur_line === " ") cur_line = ""; } } if (cur_line.length) { if (lineIndex >= lines.length) lines.push(allocLine()); cur_line = trimSingleSpaceRight(cur_line); line = lines[lineIndex]; line.text = cur_line; line.width = ctx.measureText(cur_line).width; lineIndex++; } for (i = lineIndex; i < lines.length; i++) freeLine(lines[i]); lines.length = lineIndex; }; function Cnds() {}; Cnds.prototype.CompareText = function(text_to_compare, case_sensitive) { if (case_sensitive) return this.text == text_to_compare; else return cr.equals_nocase(this.text, text_to_compare); }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetText = function(param) { if (cr.is_number(param) && param < 1e9) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_set = param.toString(); if (this.text !== text_to_set) { this.text = text_to_set; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.AppendText = function(param) { if (cr.is_number(param)) param = Math.round(param * 1e10) / 1e10; // round to nearest ten billionth - hides floating point errors var text_to_append = param.toString(); if (text_to_append) // not empty { this.text += text_to_append; this.text_changed = true; this.runtime.redraw = true; } }; Acts.prototype.SetFontFace = function (face_, style_) { var newstyle = ""; switch (style_) { case 1: newstyle = "bold"; break; case 2: newstyle = "italic"; break; case 3: newstyle = "bold italic"; break; } if (face_ === this.facename && newstyle === this.fontstyle) return; // no change this.facename = face_; this.fontstyle = newstyle; this.updateFont(); }; Acts.prototype.SetFontSize = function (size_) { if (this.ptSize === size_) return; this.ptSize = size_; this.pxHeight = Math.ceil((this.ptSize / 72.0) * 96.0) + 4; // assume 96dpi... this.updateFont(); }; Acts.prototype.SetFontColor = function (rgb) { var newcolor = "rgb(" + cr.GetRValue(rgb).toString() + "," + cr.GetGValue(rgb).toString() + "," + cr.GetBValue(rgb).toString() + ")"; if (newcolor === this.color) return; this.color = newcolor; this.need_text_redraw = true; this.runtime.redraw = true; }; Acts.prototype.SetWebFont = function (familyname_, cssurl_) { if (this.runtime.isDomFree) { cr.logexport("[Construct 2] Text plugin: 'Set web font' not supported on this platform - the action has been ignored"); return; // DC todo } var self = this; var refreshFunc = (function () { self.runtime.redraw = true; self.text_changed = true; }); if (requestedWebFonts.hasOwnProperty(cssurl_)) { var newfacename = "'" + familyname_ + "'"; if (this.facename === newfacename) return; // no change this.facename = newfacename; this.updateFont(); for (var i = 1; i < 10; i++) { setTimeout(refreshFunc, i * 100); setTimeout(refreshFunc, i * 1000); } return; } var wf = document.createElement("link"); wf.href = cssurl_; wf.rel = "stylesheet"; wf.type = "text/css"; wf.onload = refreshFunc; document.getElementsByTagName('head')[0].appendChild(wf); requestedWebFonts[cssurl_] = true; this.facename = "'" + familyname_ + "'"; this.updateFont(); for (var i = 1; i < 10; i++) { setTimeout(refreshFunc, i * 100); setTimeout(refreshFunc, i * 1000); } ; }; Acts.prototype.SetEffect = function (effect) { this.blend_mode = effect; this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.Text = function(ret) { ret.set_string(this.text); }; Exps.prototype.FaceName = function (ret) { ret.set_string(this.facename); }; Exps.prototype.FaceSize = function (ret) { ret.set_int(this.ptSize); }; Exps.prototype.TextWidth = function (ret) { var w = 0; var i, len, x; for (i = 0, len = this.lines.length; i < len; i++) { x = this.lines[i].width; if (w < x) w = x; } ret.set_int(w); }; Exps.prototype.TextHeight = function (ret) { ret.set_int(this.lines.length * (this.pxHeight + this.line_height_offset) - this.line_height_offset); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.TextBox = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.TextBox.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; var elemTypes = ["text", "password", "email", "number", "tel", "url"]; if (navigator.userAgent.indexOf("MSIE 9") > -1) { elemTypes[2] = "text"; elemTypes[3] = "text"; elemTypes[4] = "text"; elemTypes[5] = "text"; } instanceProto.onCreate = function() { if (this.runtime.isDomFree) { cr.logexport("[Construct 2] Textbox plugin not supported on this platform - the object will not be created"); return; } if (this.properties[7] === 6) // textarea { this.elem = document.createElement("textarea"); jQuery(this.elem).css("resize", "none"); } else { this.elem = document.createElement("input"); this.elem.type = elemTypes[this.properties[7]]; } this.elem.id = this.properties[9]; jQuery(this.elem).appendTo(this.runtime.canvasdiv ? this.runtime.canvasdiv : "body"); this.elem["autocomplete"] = "off"; this.elem.value = this.properties[0]; this.elem["placeholder"] = this.properties[1]; this.elem.title = this.properties[2]; this.elem.disabled = (this.properties[4] === 0); this.elem["readOnly"] = (this.properties[5] === 1); this.elem["spellcheck"] = (this.properties[6] === 1); this.autoFontSize = (this.properties[8] !== 0); this.element_hidden = false; if (this.properties[3] === 0) { jQuery(this.elem).hide(); this.visible = false; this.element_hidden = true; } var onchangetrigger = (function (self) { return function() { self.runtime.trigger(cr.plugins_.TextBox.prototype.cnds.OnTextChanged, self); }; })(this); this.elem["oninput"] = onchangetrigger; if (navigator.userAgent.indexOf("MSIE") !== -1) this.elem["oncut"] = onchangetrigger; this.elem.onclick = (function (self) { return function(e) { e.stopPropagation(); self.runtime.isInUserInputEvent = true; self.runtime.trigger(cr.plugins_.TextBox.prototype.cnds.OnClicked, self); self.runtime.isInUserInputEvent = false; }; })(this); this.elem.ondblclick = (function (self) { return function(e) { e.stopPropagation(); self.runtime.isInUserInputEvent = true; self.runtime.trigger(cr.plugins_.TextBox.prototype.cnds.OnDoubleClicked, self); self.runtime.isInUserInputEvent = false; }; })(this); this.elem.addEventListener("touchstart", function (e) { e.stopPropagation(); }, false); this.elem.addEventListener("touchmove", function (e) { e.stopPropagation(); }, false); this.elem.addEventListener("touchend", function (e) { e.stopPropagation(); }, false); jQuery(this.elem).mousedown(function (e) { e.stopPropagation(); }); jQuery(this.elem).mouseup(function (e) { e.stopPropagation(); }); jQuery(this.elem).keydown(function (e) { if (e.which !== 13 && e.which != 27) // allow enter and escape e.stopPropagation(); }); jQuery(this.elem).keyup(function (e) { if (e.which !== 13 && e.which != 27) // allow enter and escape e.stopPropagation(); }); this.lastLeft = 0; this.lastTop = 0; this.lastRight = 0; this.lastBottom = 0; this.lastWinWidth = 0; this.lastWinHeight = 0; this.updatePosition(true); this.runtime.tickMe(this); }; instanceProto.saveToJSON = function () { return { "text": this.elem.value, "placeholder": this.elem.placeholder, "tooltip": this.elem.title, "disabled": !!this.elem.disabled, "readonly": !!this.elem.readOnly, "spellcheck": !!this.elem["spellcheck"] }; }; instanceProto.loadFromJSON = function (o) { this.elem.value = o["text"]; this.elem.placeholder = o["placeholder"]; this.elem.title = o["tooltip"]; this.elem.disabled = o["disabled"]; this.elem.readOnly = o["readonly"]; this.elem["spellcheck"] = o["spellcheck"]; }; instanceProto.onDestroy = function () { if (this.runtime.isDomFree) return; jQuery(this.elem).remove(); this.elem = null; }; instanceProto.tick = function () { this.updatePosition(); }; instanceProto.updatePosition = function (first) { if (this.runtime.isDomFree) return; var left = this.layer.layerToCanvas(this.x, this.y, true); var top = this.layer.layerToCanvas(this.x, this.y, false); var right = this.layer.layerToCanvas(this.x + this.width, this.y + this.height, true); var bottom = this.layer.layerToCanvas(this.x + this.width, this.y + this.height, false); var rightEdge = this.runtime.width / this.runtime.devicePixelRatio; var bottomEdge = this.runtime.height / this.runtime.devicePixelRatio; if (!this.visible || !this.layer.visible || right <= 0 || bottom <= 0 || left >= rightEdge || top >= bottomEdge) { if (!this.element_hidden) jQuery(this.elem).hide(); this.element_hidden = true; return; } if (left < 1) left = 1; if (top < 1) top = 1; if (right >= rightEdge) right = rightEdge - 1; if (bottom >= bottomEdge) bottom = bottomEdge - 1; var curWinWidth = window.innerWidth; var curWinHeight = window.innerHeight; if (!first && this.lastLeft === left && this.lastTop === top && this.lastRight === right && this.lastBottom === bottom && this.lastWinWidth === curWinWidth && this.lastWinHeight === curWinHeight) { if (this.element_hidden) { jQuery(this.elem).show(); this.element_hidden = false; } return; } this.lastLeft = left; this.lastTop = top; this.lastRight = right; this.lastBottom = bottom; this.lastWinWidth = curWinWidth; this.lastWinHeight = curWinHeight; if (this.element_hidden) { jQuery(this.elem).show(); this.element_hidden = false; } var offx = Math.round(left) + jQuery(this.runtime.canvas).offset().left; var offy = Math.round(top) + jQuery(this.runtime.canvas).offset().top; jQuery(this.elem).css("position", "absolute"); jQuery(this.elem).offset({left: offx, top: offy}); jQuery(this.elem).width(Math.round(right - left)); jQuery(this.elem).height(Math.round(bottom - top)); if (this.autoFontSize) jQuery(this.elem).css("font-size", ((this.layer.getScale(true) / this.runtime.devicePixelRatio) - 0.2) + "em"); }; instanceProto.draw = function(ctx) { }; instanceProto.drawGL = function(glw) { }; function Cnds() {}; Cnds.prototype.CompareText = function (text, case_) { if (this.runtime.isDomFree) return false; if (case_ === 0) // insensitive return cr.equals_nocase(this.elem.value, text); else return this.elem.value === text; }; Cnds.prototype.OnTextChanged = function () { return true; }; Cnds.prototype.OnClicked = function () { return true; }; Cnds.prototype.OnDoubleClicked = function () { return true; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetText = function (text) { if (this.runtime.isDomFree) return; this.elem.value = text; }; Acts.prototype.SetPlaceholder = function (text) { if (this.runtime.isDomFree) return; this.elem.placeholder = text; }; Acts.prototype.SetTooltip = function (text) { if (this.runtime.isDomFree) return; this.elem.title = text; }; Acts.prototype.SetVisible = function (vis) { if (this.runtime.isDomFree) return; this.visible = (vis !== 0); }; Acts.prototype.SetEnabled = function (en) { if (this.runtime.isDomFree) return; this.elem.disabled = (en === 0); }; Acts.prototype.SetReadOnly = function (ro) { if (this.runtime.isDomFree) return; this.elem.readOnly = (ro === 0); }; Acts.prototype.SetFocus = function () { if (this.runtime.isDomFree) return; this.elem.focus(); }; Acts.prototype.SetBlur = function () { if (this.runtime.isDomFree) return; this.elem.blur(); }; Acts.prototype.SetCSSStyle = function (p, v) { if (this.runtime.isDomFree) return; jQuery(this.elem).css(p, v); }; Acts.prototype.ScrollToBottom = function () { if (this.runtime.isDomFree) return; this.elem.scrollTop = this.elem.scrollHeight; }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.Text = function (ret) { if (this.runtime.isDomFree) { ret.set_string(""); return; } ret.set_string(this.elem.value); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.TiledBg = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.TiledBg.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { if (this.is_family) return; this.texture_img = new Image(); this.texture_img.cr_filesize = this.texture_filesize; this.runtime.waitForImageLoad(this.texture_img, this.texture_file); this.pattern = null; this.webGL_texture = null; }; typeProto.onLostWebGLContext = function () { if (this.is_family) return; this.webGL_texture = null; }; typeProto.onRestoreWebGLContext = function () { if (this.is_family || !this.instances.length) return; if (!this.webGL_texture) { this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, true, this.runtime.linearSampling, this.texture_pixelformat); } var i, len; for (i = 0, len = this.instances.length; i < len; i++) this.instances[i].webGL_texture = this.webGL_texture; }; typeProto.loadTextures = function () { if (this.is_family || this.webGL_texture || !this.runtime.glwrap) return; this.webGL_texture = this.runtime.glwrap.loadTexture(this.texture_img, true, this.runtime.linearSampling, this.texture_pixelformat); }; typeProto.unloadTextures = function () { if (this.is_family || this.instances.length || !this.webGL_texture) return; this.runtime.glwrap.deleteTexture(this.webGL_texture); this.webGL_texture = null; }; typeProto.preloadCanvas2D = function (ctx) { ctx.drawImage(this.texture_img, 0, 0); }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { this.visible = (this.properties[0] === 0); // 0=visible, 1=invisible this.rcTex = new cr.rect(0, 0, 0, 0); this.has_own_texture = false; // true if a texture loaded in from URL this.texture_img = this.type.texture_img; if (this.runtime.glwrap) { this.type.loadTextures(); this.webGL_texture = this.type.webGL_texture; } else { if (!this.type.pattern) this.type.pattern = this.runtime.ctx.createPattern(this.type.texture_img, "repeat"); this.pattern = this.type.pattern; } }; instanceProto.afterLoad = function () { this.has_own_texture = false; this.texture_img = this.type.texture_img; }; instanceProto.onDestroy = function () { if (this.runtime.glwrap && this.has_own_texture && this.webGL_texture) { this.runtime.glwrap.deleteTexture(this.webGL_texture); this.webGL_texture = null; } }; instanceProto.draw = function(ctx) { ctx.globalAlpha = this.opacity; ctx.save(); ctx.fillStyle = this.pattern; var myx = this.x; var myy = this.y; if (this.runtime.pixel_rounding) { myx = Math.round(myx); myy = Math.round(myy); } var drawX = -(this.hotspotX * this.width); var drawY = -(this.hotspotY * this.height); var offX = drawX % this.texture_img.width; var offY = drawY % this.texture_img.height; if (offX < 0) offX += this.texture_img.width; if (offY < 0) offY += this.texture_img.height; ctx.translate(myx, myy); ctx.rotate(this.angle); ctx.translate(offX, offY); ctx.fillRect(drawX - offX, drawY - offY, this.width, this.height); ctx.restore(); }; instanceProto.drawGL_earlyZPass = function(glw) { this.drawGL(glw); }; instanceProto.drawGL = function(glw) { glw.setTexture(this.webGL_texture); glw.setOpacity(this.opacity); var rcTex = this.rcTex; rcTex.right = this.width / this.texture_img.width; rcTex.bottom = this.height / this.texture_img.height; var q = this.bquad; if (this.runtime.pixel_rounding) { var ox = Math.round(this.x) - this.x; var oy = Math.round(this.y) - this.y; glw.quadTex(q.tlx + ox, q.tly + oy, q.trx + ox, q.try_ + oy, q.brx + ox, q.bry + oy, q.blx + ox, q.bly + oy, rcTex); } else glw.quadTex(q.tlx, q.tly, q.trx, q.try_, q.brx, q.bry, q.blx, q.bly, rcTex); }; function Cnds() {}; Cnds.prototype.OnURLLoaded = function () { return true; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetEffect = function (effect) { this.blend_mode = effect; this.compositeOp = cr.effectToCompositeOp(effect); cr.setGLBlend(this, effect, this.runtime.gl); this.runtime.redraw = true; }; Acts.prototype.LoadURL = function (url_, crossOrigin_) { var img = new Image(); var self = this; img.onload = function () { self.texture_img = img; if (self.runtime.glwrap) { if (self.has_own_texture && self.webGL_texture) self.runtime.glwrap.deleteTexture(self.webGL_texture); self.webGL_texture = self.runtime.glwrap.loadTexture(img, true, self.runtime.linearSampling); } else { self.pattern = self.runtime.ctx.createPattern(img, "repeat"); } self.has_own_texture = true; self.runtime.redraw = true; self.runtime.trigger(cr.plugins_.TiledBg.prototype.cnds.OnURLLoaded, self); }; if (url_.substr(0, 5) !== "data:" && crossOrigin_ === 0) img.crossOrigin = "anonymous"; this.runtime.setImageSrc(img, url_); }; pluginProto.acts = new Acts(); function Exps() {}; Exps.prototype.ImageWidth = function (ret) { ret.set_float(this.texture_img.width); }; Exps.prototype.ImageHeight = function (ret) { ret.set_float(this.texture_img.height); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.Touch = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.Touch.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; this.touches = []; this.mouseDown = false; }; var instanceProto = pluginProto.Instance.prototype; var dummyoffset = {left: 0, top: 0}; instanceProto.findTouch = function (id) { var i, len; for (i = 0, len = this.touches.length; i < len; i++) { if (this.touches[i]["id"] === id) return i; } return -1; }; var appmobi_accx = 0; var appmobi_accy = 0; var appmobi_accz = 0; function AppMobiGetAcceleration(evt) { appmobi_accx = evt.x; appmobi_accy = evt.y; appmobi_accz = evt.z; }; var pg_accx = 0; var pg_accy = 0; var pg_accz = 0; function PhoneGapGetAcceleration(evt) { pg_accx = evt.x; pg_accy = evt.y; pg_accz = evt.z; }; var theInstance = null; var touchinfo_cache = []; function AllocTouchInfo(x, y, id, index) { var ret; if (touchinfo_cache.length) ret = touchinfo_cache.pop(); else ret = new TouchInfo(); ret.init(x, y, id, index); return ret; }; function ReleaseTouchInfo(ti) { if (touchinfo_cache.length < 100) touchinfo_cache.push(ti); }; var GESTURE_HOLD_THRESHOLD = 15; // max px motion for hold gesture to register var GESTURE_HOLD_TIMEOUT = 500; // time for hold gesture to register var GESTURE_TAP_TIMEOUT = 333; // time for tap gesture to register var GESTURE_DOUBLETAP_THRESHOLD = 25; // max distance apart for taps to be function TouchInfo() { this.starttime = 0; this.time = 0; this.lasttime = 0; this.startx = 0; this.starty = 0; this.x = 0; this.y = 0; this.lastx = 0; this.lasty = 0; this["id"] = 0; this.startindex = 0; this.triggeredHold = false; this.tooFarForHold = false; }; TouchInfo.prototype.init = function (x, y, id, index) { var nowtime = cr.performance_now(); this.time = nowtime; this.lasttime = nowtime; this.starttime = nowtime; this.startx = x; this.starty = y; this.x = x; this.y = y; this.lastx = x; this.lasty = y; this.width = 0; this.height = 0; this.pressure = 0; this["id"] = id; this.startindex = index; this.triggeredHold = false; this.tooFarForHold = false; }; TouchInfo.prototype.update = function (nowtime, x, y, width, height, pressure) { this.lasttime = this.time; this.time = nowtime; this.lastx = this.x; this.lasty = this.y; this.x = x; this.y = y; this.width = width; this.height = height; this.pressure = pressure; if (!this.tooFarForHold && cr.distanceTo(this.startx, this.starty, this.x, this.y) >= GESTURE_HOLD_THRESHOLD) { this.tooFarForHold = true; } }; TouchInfo.prototype.maybeTriggerHold = function (inst, index) { if (this.triggeredHold) return; // already triggered this gesture var nowtime = cr.performance_now(); if (nowtime - this.starttime >= GESTURE_HOLD_TIMEOUT && !this.tooFarForHold && cr.distanceTo(this.startx, this.starty, this.x, this.y) < GESTURE_HOLD_THRESHOLD) { this.triggeredHold = true; inst.trigger_index = this.startindex; inst.trigger_id = this["id"]; inst.getTouchIndex = index; inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnHoldGesture, inst); inst.curTouchX = this.x; inst.curTouchY = this.y; inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnHoldGestureObject, inst); inst.getTouchIndex = 0; } }; var lastTapX = -1000; var lastTapY = -1000; var lastTapTime = -10000; TouchInfo.prototype.maybeTriggerTap = function (inst, index) { if (this.triggeredHold) return; var nowtime = cr.performance_now(); if (nowtime - this.starttime <= GESTURE_TAP_TIMEOUT && !this.tooFarForHold && cr.distanceTo(this.startx, this.starty, this.x, this.y) < GESTURE_HOLD_THRESHOLD) { inst.trigger_index = this.startindex; inst.trigger_id = this["id"]; inst.getTouchIndex = index; if ((nowtime - lastTapTime <= GESTURE_TAP_TIMEOUT * 2) && cr.distanceTo(lastTapX, lastTapY, this.x, this.y) < GESTURE_DOUBLETAP_THRESHOLD) { inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnDoubleTapGesture, inst); inst.curTouchX = this.x; inst.curTouchY = this.y; inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnDoubleTapGestureObject, inst); lastTapX = -1000; lastTapY = -1000; lastTapTime = -10000; } else { inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTapGesture, inst); inst.curTouchX = this.x; inst.curTouchY = this.y; inst.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTapGestureObject, inst); lastTapX = this.x; lastTapY = this.y; lastTapTime = nowtime; } inst.getTouchIndex = 0; } }; instanceProto.onCreate = function() { theInstance = this; this.isWindows8 = !!(typeof window["c2isWindows8"] !== "undefined" && window["c2isWindows8"]); this.orient_alpha = 0; this.orient_beta = 0; this.orient_gamma = 0; this.acc_g_x = 0; this.acc_g_y = 0; this.acc_g_z = 0; this.acc_x = 0; this.acc_y = 0; this.acc_z = 0; this.curTouchX = 0; this.curTouchY = 0; this.trigger_index = 0; this.trigger_id = 0; this.getTouchIndex = 0; this.useMouseInput = (this.properties[0] !== 0); var elem = (this.runtime.fullscreen_mode > 0) ? document : this.runtime.canvas; var elem2 = document; if (this.runtime.isDirectCanvas) elem2 = elem = window["Canvas"]; else if (this.runtime.isCocoonJs) elem2 = elem = window; var self = this; if (typeof PointerEvent !== "undefined") { elem.addEventListener("pointerdown", function(info) { self.onPointerStart(info); }, false ); elem.addEventListener("pointermove", function(info) { self.onPointerMove(info); }, false ); elem2.addEventListener("pointerup", function(info) { self.onPointerEnd(info, false); }, false ); elem2.addEventListener("pointercancel", function(info) { self.onPointerEnd(info, true); }, false ); if (this.runtime.canvas) { this.runtime.canvas.addEventListener("MSGestureHold", function(e) { e.preventDefault(); }, false); document.addEventListener("MSGestureHold", function(e) { e.preventDefault(); }, false); this.runtime.canvas.addEventListener("gesturehold", function(e) { e.preventDefault(); }, false); document.addEventListener("gesturehold", function(e) { e.preventDefault(); }, false); } } else if (window.navigator["msPointerEnabled"]) { elem.addEventListener("MSPointerDown", function(info) { self.onPointerStart(info); }, false ); elem.addEventListener("MSPointerMove", function(info) { self.onPointerMove(info); }, false ); elem2.addEventListener("MSPointerUp", function(info) { self.onPointerEnd(info, false); }, false ); elem2.addEventListener("MSPointerCancel", function(info) { self.onPointerEnd(info, true); }, false ); if (this.runtime.canvas) { this.runtime.canvas.addEventListener("MSGestureHold", function(e) { e.preventDefault(); }, false); document.addEventListener("MSGestureHold", function(e) { e.preventDefault(); }, false); } } else { elem.addEventListener("touchstart", function(info) { self.onTouchStart(info); }, false ); elem.addEventListener("touchmove", function(info) { self.onTouchMove(info); }, false ); elem2.addEventListener("touchend", function(info) { self.onTouchEnd(info, false); }, false ); elem2.addEventListener("touchcancel", function(info) { self.onTouchEnd(info, true); }, false ); } if (this.isWindows8) { var win8accelerometerFn = function(e) { var reading = e["reading"]; self.acc_x = reading["accelerationX"]; self.acc_y = reading["accelerationY"]; self.acc_z = reading["accelerationZ"]; }; var win8inclinometerFn = function(e) { var reading = e["reading"]; self.orient_alpha = reading["yawDegrees"]; self.orient_beta = reading["pitchDegrees"]; self.orient_gamma = reading["rollDegrees"]; }; var accelerometer = Windows["Devices"]["Sensors"]["Accelerometer"]["getDefault"](); if (accelerometer) { accelerometer["reportInterval"] = Math.max(accelerometer["minimumReportInterval"], 16); accelerometer.addEventListener("readingchanged", win8accelerometerFn); } var inclinometer = Windows["Devices"]["Sensors"]["Inclinometer"]["getDefault"](); if (inclinometer) { inclinometer["reportInterval"] = Math.max(inclinometer["minimumReportInterval"], 16); inclinometer.addEventListener("readingchanged", win8inclinometerFn); } document.addEventListener("visibilitychange", function(e) { if (document["hidden"] || document["msHidden"]) { if (accelerometer) accelerometer.removeEventListener("readingchanged", win8accelerometerFn); if (inclinometer) inclinometer.removeEventListener("readingchanged", win8inclinometerFn); } else { if (accelerometer) accelerometer.addEventListener("readingchanged", win8accelerometerFn); if (inclinometer) inclinometer.addEventListener("readingchanged", win8inclinometerFn); } }, false); } else { window.addEventListener("deviceorientation", function (eventData) { self.orient_alpha = eventData["alpha"] || 0; self.orient_beta = eventData["beta"] || 0; self.orient_gamma = eventData["gamma"] || 0; }, false); window.addEventListener("devicemotion", function (eventData) { if (eventData["accelerationIncludingGravity"]) { self.acc_g_x = eventData["accelerationIncludingGravity"]["x"] || 0; self.acc_g_y = eventData["accelerationIncludingGravity"]["y"] || 0; self.acc_g_z = eventData["accelerationIncludingGravity"]["z"] || 0; } if (eventData["acceleration"]) { self.acc_x = eventData["acceleration"]["x"] || 0; self.acc_y = eventData["acceleration"]["y"] || 0; self.acc_z = eventData["acceleration"]["z"] || 0; } }, false); } if (this.useMouseInput && !this.runtime.isDomFree) { jQuery(document).mousemove( function(info) { self.onMouseMove(info); } ); jQuery(document).mousedown( function(info) { self.onMouseDown(info); } ); jQuery(document).mouseup( function(info) { self.onMouseUp(info); } ); } if (!this.runtime.isiOS && this.runtime.isCordova && navigator["accelerometer"] && navigator["accelerometer"]["watchAcceleration"]) { navigator["accelerometer"]["watchAcceleration"](PhoneGapGetAcceleration, null, { "frequency": 40 }); } this.runtime.tick2Me(this); }; instanceProto.onPointerMove = function (info) { if (info["pointerType"] === info["MSPOINTER_TYPE_MOUSE"] || info["pointerType"] === "mouse") return; if (info.preventDefault) info.preventDefault(); var i = this.findTouch(info["pointerId"]); var nowtime = cr.performance_now(); if (i >= 0) { var offset = this.runtime.isDomFree ? dummyoffset : jQuery(this.runtime.canvas).offset(); var t = this.touches[i]; if (nowtime - t.time < 2) return; t.update(nowtime, info.pageX - offset.left, info.pageY - offset.top, info.width || 0, info.height || 0, info.pressure || 0); } }; instanceProto.onPointerStart = function (info) { if (info["pointerType"] === info["MSPOINTER_TYPE_MOUSE"] || info["pointerType"] === "mouse") return; if (info.preventDefault && cr.isCanvasInputEvent(info)) info.preventDefault(); var offset = this.runtime.isDomFree ? dummyoffset : jQuery(this.runtime.canvas).offset(); var touchx = info.pageX - offset.left; var touchy = info.pageY - offset.top; var nowtime = cr.performance_now(); this.trigger_index = this.touches.length; this.trigger_id = info["pointerId"]; this.touches.push(AllocTouchInfo(touchx, touchy, info["pointerId"], this.trigger_index)); this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchStart, this); this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchStart, this); this.curTouchX = touchx; this.curTouchY = touchy; this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchObject, this); this.runtime.isInUserInputEvent = false; }; instanceProto.onPointerEnd = function (info, isCancel) { if (info["pointerType"] === info["MSPOINTER_TYPE_MOUSE"] || info["pointerType"] === "mouse") return; if (info.preventDefault && cr.isCanvasInputEvent(info)) info.preventDefault(); var i = this.findTouch(info["pointerId"]); this.trigger_index = (i >= 0 ? this.touches[i].startindex : -1); this.trigger_id = (i >= 0 ? this.touches[i]["id"] : -1); this.runtime.isInUserInputEvent = true; this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchEnd, this); this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchEnd, this); if (i >= 0) { if (!isCancel) this.touches[i].maybeTriggerTap(this, i); ReleaseTouchInfo(this.touches[i]); this.touches.splice(i, 1); } this.runtime.isInUserInputEvent = false; }; instanceProto.onTouchMove = function (info) { if (info.preventDefault) info.preventDefault(); var nowtime = cr.performance_now(); var i, len, t, u; for (i = 0, len = info.changedTouches.length; i < len; i++) { t = info.changedTouches[i]; var j = this.findTouch(t["identifier"]); if (j >= 0) { var offset = this.runtime.isDomFree ? dummyoffset : jQuery(this.runtime.canvas).offset(); u = this.touches[j]; if (nowtime - u.time < 2) continue; var touchWidth = (t.radiusX || t.webkitRadiusX || t.mozRadiusX || t.msRadiusX || 0) * 2; var touchHeight = (t.radiusY || t.webkitRadiusY || t.mozRadiusY || t.msRadiusY || 0) * 2; var touchForce = t.force || t.webkitForce || t.mozForce || t.msForce || 0; u.update(nowtime, t.pageX - offset.left, t.pageY - offset.top, touchWidth, touchHeight, touchForce); } } }; instanceProto.onTouchStart = function (info) { if (info.preventDefault && cr.isCanvasInputEvent(info)) info.preventDefault(); var offset = this.runtime.isDomFree ? dummyoffset : jQuery(this.runtime.canvas).offset(); var nowtime = cr.performance_now(); this.runtime.isInUserInputEvent = true; var i, len, t, j; for (i = 0, len = info.changedTouches.length; i < len; i++) { t = info.changedTouches[i]; j = this.findTouch(t["identifier"]); if (j !== -1) continue; var touchx = t.pageX - offset.left; var touchy = t.pageY - offset.top; this.trigger_index = this.touches.length; this.trigger_id = t["identifier"]; this.touches.push(AllocTouchInfo(touchx, touchy, t["identifier"], this.trigger_index)); this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchStart, this); this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchStart, this); this.curTouchX = touchx; this.curTouchY = touchy; this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchObject, this); } this.runtime.isInUserInputEvent = false; }; instanceProto.onTouchEnd = function (info, isCancel) { if (info.preventDefault && cr.isCanvasInputEvent(info)) info.preventDefault(); this.runtime.isInUserInputEvent = true; var i, len, t, j; for (i = 0, len = info.changedTouches.length; i < len; i++) { t = info.changedTouches[i]; j = this.findTouch(t["identifier"]); if (j >= 0) { this.trigger_index = this.touches[j].startindex; this.trigger_id = this.touches[j]["id"]; this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnNthTouchEnd, this); this.runtime.trigger(cr.plugins_.Touch.prototype.cnds.OnTouchEnd, this); if (!isCancel) this.touches[j].maybeTriggerTap(this, j); ReleaseTouchInfo(this.touches[j]); this.touches.splice(j, 1); } } this.runtime.isInUserInputEvent = false; }; instanceProto.getAlpha = function () { if (this.runtime.isCordova && this.orient_alpha === 0 && pg_accz !== 0) return pg_accz * 90; else return this.orient_alpha; }; instanceProto.getBeta = function () { if (this.runtime.isCordova && this.orient_beta === 0 && pg_accy !== 0) return pg_accy * 90; else return this.orient_beta; }; instanceProto.getGamma = function () { if (this.runtime.isCordova && this.orient_gamma === 0 && pg_accx !== 0) return pg_accx * 90; else return this.orient_gamma; }; var noop_func = function(){}; instanceProto.onMouseDown = function(info) { var t = { pageX: info.pageX, pageY: info.pageY, "identifier": 0 }; var fakeinfo = { changedTouches: [t] }; this.onTouchStart(fakeinfo); this.mouseDown = true; }; instanceProto.onMouseMove = function(info) { if (!this.mouseDown) return; var t = { pageX: info.pageX, pageY: info.pageY, "identifier": 0 }; var fakeinfo = { changedTouches: [t] }; this.onTouchMove(fakeinfo); }; instanceProto.onMouseUp = function(info) { if (info.preventDefault && this.runtime.had_a_click && !this.runtime.isMobile) info.preventDefault(); this.runtime.had_a_click = true; var t = { pageX: info.pageX, pageY: info.pageY, "identifier": 0 }; var fakeinfo = { changedTouches: [t] }; this.onTouchEnd(fakeinfo); this.mouseDown = false; }; instanceProto.tick2 = function() { var i, len, t; var nowtime = cr.performance_now(); for (i = 0, len = this.touches.length; i < len; ++i) { t = this.touches[i]; if (t.time <= nowtime - 50) t.lasttime = nowtime; t.maybeTriggerHold(this, i); } }; function Cnds() {}; Cnds.prototype.OnTouchStart = function () { return true; }; Cnds.prototype.OnTouchEnd = function () { return true; }; Cnds.prototype.IsInTouch = function () { return this.touches.length; }; Cnds.prototype.OnTouchObject = function (type) { if (!type) return false; return this.runtime.testAndSelectCanvasPointOverlap(type, this.curTouchX, this.curTouchY, false); }; var touching = []; Cnds.prototype.IsTouchingObject = function (type) { if (!type) return false; var sol = type.getCurrentSol(); var instances = sol.getObjects(); var px, py; var i, leni, j, lenj; for (i = 0, leni = instances.length; i < leni; i++) { var inst = instances[i]; inst.update_bbox(); for (j = 0, lenj = this.touches.length; j < lenj; j++) { var touch = this.touches[j]; px = inst.layer.canvasToLayer(touch.x, touch.y, true); py = inst.layer.canvasToLayer(touch.x, touch.y, false); if (inst.contains_pt(px, py)) { touching.push(inst); break; } } } if (touching.length) { sol.select_all = false; cr.shallowAssignArray(sol.instances, touching); type.applySolToContainer(); cr.clearArray(touching); return true; } else return false; }; Cnds.prototype.CompareTouchSpeed = function (index, cmp, s) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) return false; var t = this.touches[index]; var dist = cr.distanceTo(t.x, t.y, t.lastx, t.lasty); var timediff = (t.time - t.lasttime) / 1000; var speed = 0; if (timediff > 0) speed = dist / timediff; return cr.do_cmp(speed, cmp, s); }; Cnds.prototype.OrientationSupported = function () { return typeof window["DeviceOrientationEvent"] !== "undefined"; }; Cnds.prototype.MotionSupported = function () { return typeof window["DeviceMotionEvent"] !== "undefined"; }; Cnds.prototype.CompareOrientation = function (orientation_, cmp_, angle_) { var v = 0; if (orientation_ === 0) v = this.getAlpha(); else if (orientation_ === 1) v = this.getBeta(); else v = this.getGamma(); return cr.do_cmp(v, cmp_, angle_); }; Cnds.prototype.CompareAcceleration = function (acceleration_, cmp_, angle_) { var v = 0; if (acceleration_ === 0) v = this.acc_g_x; else if (acceleration_ === 1) v = this.acc_g_y; else if (acceleration_ === 2) v = this.acc_g_z; else if (acceleration_ === 3) v = this.acc_x; else if (acceleration_ === 4) v = this.acc_y; else if (acceleration_ === 5) v = this.acc_z; return cr.do_cmp(v, cmp_, angle_); }; Cnds.prototype.OnNthTouchStart = function (touch_) { touch_ = Math.floor(touch_); return touch_ === this.trigger_index; }; Cnds.prototype.OnNthTouchEnd = function (touch_) { touch_ = Math.floor(touch_); return touch_ === this.trigger_index; }; Cnds.prototype.HasNthTouch = function (touch_) { touch_ = Math.floor(touch_); return this.touches.length >= touch_ + 1; }; Cnds.prototype.OnHoldGesture = function () { return true; }; Cnds.prototype.OnTapGesture = function () { return true; }; Cnds.prototype.OnDoubleTapGesture = function () { return true; }; Cnds.prototype.OnHoldGestureObject = function (type) { if (!type) return false; return this.runtime.testAndSelectCanvasPointOverlap(type, this.curTouchX, this.curTouchY, false); }; Cnds.prototype.OnTapGestureObject = function (type) { if (!type) return false; return this.runtime.testAndSelectCanvasPointOverlap(type, this.curTouchX, this.curTouchY, false); }; Cnds.prototype.OnDoubleTapGestureObject = function (type) { if (!type) return false; return this.runtime.testAndSelectCanvasPointOverlap(type, this.curTouchX, this.curTouchY, false); }; pluginProto.cnds = new Cnds(); function Exps() {}; Exps.prototype.TouchCount = function (ret) { ret.set_int(this.touches.length); }; Exps.prototype.X = function (ret, layerparam) { var index = this.getTouchIndex; if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var layer, oldScale, oldZoomRate, oldParallaxX, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxX = layer.parallaxX; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxX = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, true)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxX = oldParallaxX; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, true)); else ret.set_float(0); } }; Exps.prototype.XAt = function (ret, index, layerparam) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var layer, oldScale, oldZoomRate, oldParallaxX, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxX = layer.parallaxX; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxX = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, true)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxX = oldParallaxX; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, true)); else ret.set_float(0); } }; Exps.prototype.XForID = function (ret, id, layerparam) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; var layer, oldScale, oldZoomRate, oldParallaxX, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxX = layer.parallaxX; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxX = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(touch.x, touch.y, true)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxX = oldParallaxX; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(touch.x, touch.y, true)); else ret.set_float(0); } }; Exps.prototype.Y = function (ret, layerparam) { var index = this.getTouchIndex; if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var layer, oldScale, oldZoomRate, oldParallaxY, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxY = layer.parallaxY; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxY = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, false)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxY = oldParallaxY; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, false)); else ret.set_float(0); } }; Exps.prototype.YAt = function (ret, index, layerparam) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var layer, oldScale, oldZoomRate, oldParallaxY, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxY = layer.parallaxY; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxY = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, false)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxY = oldParallaxY; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(this.touches[index].x, this.touches[index].y, false)); else ret.set_float(0); } }; Exps.prototype.YForID = function (ret, id, layerparam) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; var layer, oldScale, oldZoomRate, oldParallaxY, oldAngle; if (cr.is_undefined(layerparam)) { layer = this.runtime.getLayerByNumber(0); oldScale = layer.scale; oldZoomRate = layer.zoomRate; oldParallaxY = layer.parallaxY; oldAngle = layer.angle; layer.scale = 1; layer.zoomRate = 1.0; layer.parallaxY = 1.0; layer.angle = 0; ret.set_float(layer.canvasToLayer(touch.x, touch.y, false)); layer.scale = oldScale; layer.zoomRate = oldZoomRate; layer.parallaxY = oldParallaxY; layer.angle = oldAngle; } else { if (cr.is_number(layerparam)) layer = this.runtime.getLayerByNumber(layerparam); else layer = this.runtime.getLayerByName(layerparam); if (layer) ret.set_float(layer.canvasToLayer(touch.x, touch.y, false)); else ret.set_float(0); } }; Exps.prototype.AbsoluteX = function (ret) { if (this.touches.length) ret.set_float(this.touches[0].x); else ret.set_float(0); }; Exps.prototype.AbsoluteXAt = function (ret, index) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } ret.set_float(this.touches[index].x); }; Exps.prototype.AbsoluteXForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(touch.x); }; Exps.prototype.AbsoluteY = function (ret) { if (this.touches.length) ret.set_float(this.touches[0].y); else ret.set_float(0); }; Exps.prototype.AbsoluteYAt = function (ret, index) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } ret.set_float(this.touches[index].y); }; Exps.prototype.AbsoluteYForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(touch.y); }; Exps.prototype.SpeedAt = function (ret, index) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var t = this.touches[index]; var dist = cr.distanceTo(t.x, t.y, t.lastx, t.lasty); var timediff = (t.time - t.lasttime) / 1000; if (timediff === 0) ret.set_float(0); else ret.set_float(dist / timediff); }; Exps.prototype.SpeedForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; var dist = cr.distanceTo(touch.x, touch.y, touch.lastx, touch.lasty); var timediff = (touch.time - touch.lasttime) / 1000; if (timediff === 0) ret.set_float(0); else ret.set_float(dist / timediff); }; Exps.prototype.AngleAt = function (ret, index) { index = Math.floor(index); if (index < 0 || index >= this.touches.length) { ret.set_float(0); return; } var t = this.touches[index]; ret.set_float(cr.to_degrees(cr.angleTo(t.lastx, t.lasty, t.x, t.y))); }; Exps.prototype.AngleForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(cr.to_degrees(cr.angleTo(touch.lastx, touch.lasty, touch.x, touch.y))); }; Exps.prototype.Alpha = function (ret) { ret.set_float(this.getAlpha()); }; Exps.prototype.Beta = function (ret) { ret.set_float(this.getBeta()); }; Exps.prototype.Gamma = function (ret) { ret.set_float(this.getGamma()); }; Exps.prototype.AccelerationXWithG = function (ret) { ret.set_float(this.acc_g_x); }; Exps.prototype.AccelerationYWithG = function (ret) { ret.set_float(this.acc_g_y); }; Exps.prototype.AccelerationZWithG = function (ret) { ret.set_float(this.acc_g_z); }; Exps.prototype.AccelerationX = function (ret) { ret.set_float(this.acc_x); }; Exps.prototype.AccelerationY = function (ret) { ret.set_float(this.acc_y); }; Exps.prototype.AccelerationZ = function (ret) { ret.set_float(this.acc_z); }; Exps.prototype.TouchIndex = function (ret) { ret.set_int(this.trigger_index); }; Exps.prototype.TouchID = function (ret) { ret.set_float(this.trigger_id); }; Exps.prototype.WidthForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(touch.width); }; Exps.prototype.HeightForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(touch.height); }; Exps.prototype.PressureForID = function (ret, id) { var index = this.findTouch(id); if (index < 0) { ret.set_float(0); return; } var touch = this.touches[index]; ret.set_float(touch.pressure); }; pluginProto.exps = new Exps(); }()); ; ; cr.plugins_.hmmg_layoutTransition_v2 = function(runtime) { this.runtime = runtime; }; (function () { var pluginProto = cr.plugins_.hmmg_layoutTransition_v2.prototype; pluginProto.Type = function(plugin) { this.plugin = plugin; this.runtime = plugin.runtime; }; var typeProto = pluginProto.Type.prototype; typeProto.onCreate = function() { }; pluginProto.Instance = function(type) { this.type = type; this.runtime = type.runtime; }; var instanceProto = pluginProto.Instance.prototype; instanceProto.onCreate = function() { var time = this.properties[0] || null; if(time != null) if(time >0) $("head").append(""); }; instanceProto.onDestroy = function () { }; instanceProto.saveToJSON = function () { return { }; }; instanceProto.loadFromJSON = function (o) { }; instanceProto.draw = function(ctx) { }; instanceProto.drawGL = function (glw) { }; function Cnds() {}; Cnds.prototype.isTransitionReady = function () { return true; }; Cnds.prototype.didTransitionStart = function () { return true; }; Cnds.prototype.didTransitionFinish = function () { return true; }; pluginProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.prepareTransition = function () { var self = this ; function prepareCanvas(elem,callback1) { self.runtime.doCanvasSnapshot("image/jpeg", 100/100); setTimeout(function() { callback1(self.runtime.snapshotData); },50); } function isCanvasReady(callback) { prepareCanvas(self,function(returnedPic) { if($("#fakeCanvas")[0] == undefined) { var c2canvasdiv = $("#c2canvasdiv") ; var fakeCanvas = $("
"); var fakeBody = $("
"); var marginLeft = parseFloat(c2canvasdiv.css("margin-left")); fakeBody.css( { "top":c2canvasdiv.offset().top, "left":c2canvasdiv.offset().left, "width":c2canvasdiv.width(), "height":c2canvasdiv.height() }); c2canvasdiv.addClass("prepared").find(" > :not(canvas)").each(function() { $(this).css("left",($(this).offset().left-marginLeft)+"px"); }); fakeBody.appendTo(document.body).append(c2canvasdiv).append(fakeCanvas); if(callback) callback(); } }); } isCanvasReady(function() { self.runtime.trigger(cr.plugins_.hmmg_layoutTransition_v2.prototype.cnds.isTransitionReady, self); }); }; Acts.prototype.startTransition = function (transID) { var fakeBody = $("#fakeBody"); var c2canvasdiv = fakeBody.find("#c2canvasdiv") ; var fakeCanvas = fakeBody.find("#fakeCanvas"); var self = this ; function darkTheFakeCanvas(callback) { setTimeout(function() { fakeCanvas.find("div").addClass("darker"); if(callback) callback(); },1); } function removeChanges() { c2canvasdiv.appendTo(document.body).removeClass("prepared"); fakeBody.remove(); self.runtime.trigger(cr.plugins_.hmmg_layoutTransition_v2.prototype.cnds.didTransitionFinish, self) } self.runtime.trigger(cr.plugins_.hmmg_layoutTransition_v2.prototype.cnds.didTransitionStart, self) if(transID == 14) { c2canvasdiv.addClass("hidden"); fakeCanvas.addClass('animated rotateOut').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); }); c2canvasdiv.removeClass("hidden").addClass('animated rotateIn').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated rotateIn"); removeChanges(); }); } else if(transID == 13) { fakeCanvas.addClass('animated rollOut').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); }); c2canvasdiv.addClass('animated rollIn').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated rollIn"); removeChanges(); }); } else if(transID == 12) { fakeCanvas.addClass('animated zoomOut').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); }); c2canvasdiv.addClass('animated zoomIn').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated zoomIn"); removeChanges(); }); } else if(transID == 11) { c2canvasdiv.addClass("hidden"); fakeCanvas.addClass('animated fadeOut').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); c2canvasdiv.removeClass("hidden").addClass('animated fadeIn').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated fadeIn"); removeChanges(); }); }); } else if(transID == 10) { c2canvasdiv.addClass("hidden"); fakeCanvas.addClass('animated fadeOut').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); }); c2canvasdiv.removeClass("hidden").addClass('animated fadeIn').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated fadeIn"); removeChanges(); }); } else if(transID == 9) { c2canvasdiv.addClass("hidden"); fakeCanvas.addClass('animated flipOutYY').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); c2canvasdiv.removeClass("hidden").addClass('animated flipInYY').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated flipInYY"); removeChanges(); }); }); } else if(transID == 8) { c2canvasdiv.addClass("hidden"); fakeCanvas.addClass('animated flipOutXX').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { fakeCanvas.addClass("hidden"); c2canvasdiv.removeClass("hidden").addClass('animated flipInXX').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { c2canvasdiv.removeClass("animated flipInXX"); removeChanges(); }); }); } else if(transID == 7) { c2canvasdiv.addClass('animated slideInRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); c2canvasdiv.removeClass("animated slideInRight"); }); } else if(transID == 6) { c2canvasdiv.addClass('animated slideInLeft').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); c2canvasdiv.removeClass('animated slideInLeft'); }); } else if(transID == 5) { c2canvasdiv.addClass('animated slideInDown').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); c2canvasdiv.removeClass('animated slideInDown'); }); } else if(transID == 4) { c2canvasdiv.addClass('animated slideInUp').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); c2canvasdiv.removeClass('animated slideInUp'); }); } else if(transID == 3) { c2canvasdiv.addClass('animated slideInRight').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); c2canvasdiv.removeClass("animated slideInRight"); fakeCanvas.removeClass('animated slideOutLeft'); }); fakeCanvas.addClass('animated slideOutLeft'); } else if(transID == 2) { c2canvasdiv.addClass('animated slideInLeft').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); fakeCanvas.removeClass("animated slideOutRight"); c2canvasdiv.removeClass('animated slideInLeft'); }); fakeCanvas.addClass('animated slideOutRight'); } else if(transID == 1) { c2canvasdiv.addClass('animated slideInDown').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); fakeCanvas.removeClass("animated slideOutDown"); c2canvasdiv.removeClass('animated slideInDown'); }); fakeCanvas.addClass('animated slideOutDown'); } else if(transID == 0) { c2canvasdiv.addClass('animated slideInUp').on('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function() { removeChanges(); fakeCanvas.removeClass("animated slideOutUp"); c2canvasdiv.removeClass('animated slideInUp'); }); fakeCanvas.addClass('animated slideOutUp'); } }; pluginProto.acts = new Acts(); function Exps() {}; pluginProto.exps = new Exps(); }()); ; ; cr.behaviors.Bullet = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Bullet.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { var speed = this.properties[0]; this.acc = this.properties[1]; this.g = this.properties[2]; this.bounceOffSolid = (this.properties[3] !== 0); this.setAngle = (this.properties[4] !== 0); this.dx = Math.cos(this.inst.angle) * speed; this.dy = Math.sin(this.inst.angle) * speed; this.lastx = this.inst.x; this.lasty = this.inst.y; this.lastKnownAngle = this.inst.angle; this.travelled = 0; this.enabled = (this.properties[5] !== 0); }; behinstProto.saveToJSON = function () { return { "acc": this.acc, "g": this.g, "dx": this.dx, "dy": this.dy, "lx": this.lastx, "ly": this.lasty, "lka": this.lastKnownAngle, "t": this.travelled, "e": this.enabled }; }; behinstProto.loadFromJSON = function (o) { this.acc = o["acc"]; this.g = o["g"]; this.dx = o["dx"]; this.dy = o["dy"]; this.lastx = o["lx"]; this.lasty = o["ly"]; this.lastKnownAngle = o["lka"]; this.travelled = o["t"]; this.enabled = o["e"]; }; behinstProto.tick = function () { if (!this.enabled) return; var dt = this.runtime.getDt(this.inst); var s, a; var bounceSolid, bounceAngle; if (this.inst.angle !== this.lastKnownAngle) { if (this.setAngle) { s = cr.distanceTo(0, 0, this.dx, this.dy); this.dx = Math.cos(this.inst.angle) * s; this.dy = Math.sin(this.inst.angle) * s; } this.lastKnownAngle = this.inst.angle; } if (this.acc !== 0) { s = cr.distanceTo(0, 0, this.dx, this.dy); if (this.dx === 0 && this.dy === 0) a = this.inst.angle; else a = cr.angleTo(0, 0, this.dx, this.dy); s += this.acc * dt; if (s < 0) s = 0; this.dx = Math.cos(a) * s; this.dy = Math.sin(a) * s; } if (this.g !== 0) this.dy += this.g * dt; this.lastx = this.inst.x; this.lasty = this.inst.y; if (this.dx !== 0 || this.dy !== 0) { this.inst.x += this.dx * dt; this.inst.y += this.dy * dt; this.travelled += cr.distanceTo(0, 0, this.dx * dt, this.dy * dt) if (this.setAngle) { this.inst.angle = cr.angleTo(0, 0, this.dx, this.dy); this.inst.set_bbox_changed(); this.lastKnownAngle = this.inst.angle; } this.inst.set_bbox_changed(); if (this.bounceOffSolid) { bounceSolid = this.runtime.testOverlapSolid(this.inst); if (bounceSolid) { this.runtime.registerCollision(this.inst, bounceSolid); s = cr.distanceTo(0, 0, this.dx, this.dy); bounceAngle = this.runtime.calculateSolidBounceAngle(this.inst, this.lastx, this.lasty); this.dx = Math.cos(bounceAngle) * s; this.dy = Math.sin(bounceAngle) * s; this.inst.x += this.dx * dt; // move out for one tick since the object can't have spent a tick in the solid this.inst.y += this.dy * dt; this.inst.set_bbox_changed(); if (this.setAngle) { this.inst.angle = bounceAngle; this.lastKnownAngle = bounceAngle; this.inst.set_bbox_changed(); } if (!this.runtime.pushOutSolid(this.inst, this.dx / s, this.dy / s, Math.max(s * 2.5 * dt, 30))) this.runtime.pushOutSolidNearest(this.inst, 100); } } } }; function Cnds() {}; Cnds.prototype.CompareSpeed = function (cmp, s) { return cr.do_cmp(cr.distanceTo(0, 0, this.dx, this.dy), cmp, s); }; Cnds.prototype.CompareTravelled = function (cmp, d) { return cr.do_cmp(this.travelled, cmp, d); }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetSpeed = function (s) { var a = cr.angleTo(0, 0, this.dx, this.dy); this.dx = Math.cos(a) * s; this.dy = Math.sin(a) * s; }; Acts.prototype.SetAcceleration = function (a) { this.acc = a; }; Acts.prototype.SetGravity = function (g) { this.g = g; }; Acts.prototype.SetAngleOfMotion = function (a) { a = cr.to_radians(a); var s = cr.distanceTo(0, 0, this.dx, this.dy) this.dx = Math.cos(a) * s; this.dy = Math.sin(a) * s; }; Acts.prototype.Bounce = function (objtype) { if (!objtype) return; var otherinst = objtype.getFirstPicked(this.inst); if (!otherinst) return; var dt = this.runtime.getDt(this.inst); var s = cr.distanceTo(0, 0, this.dx, this.dy); var bounceAngle = this.runtime.calculateSolidBounceAngle(this.inst, this.lastx, this.lasty, otherinst); this.dx = Math.cos(bounceAngle) * s; this.dy = Math.sin(bounceAngle) * s; this.inst.x += this.dx * dt; // move out for one tick since the object can't have spent a tick in the solid this.inst.y += this.dy * dt; this.inst.set_bbox_changed(); if (this.setAngle) { this.inst.angle = bounceAngle; this.lastKnownAngle = bounceAngle; this.inst.set_bbox_changed(); } if (this.bounceOffSolid) { if (!this.runtime.pushOutSolid(this.inst, this.dx / s, this.dy / s, Math.max(s * 2.5 * dt, 30))) this.runtime.pushOutSolidNearest(this.inst, 100); } else if (s !== 0) { this.runtime.pushOut(this.inst, this.dx / s, this.dy / s, Math.max(s * 2.5 * dt, 30), otherinst) } }; Acts.prototype.SetDistanceTravelled = function (d) { this.travelled = d; }; Acts.prototype.SetEnabled = function (en) { this.enabled = (en === 1); }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.Speed = function (ret) { var s = cr.distanceTo(0, 0, this.dx, this.dy); s = cr.round6dp(s); ret.set_float(s); }; Exps.prototype.Acceleration = function (ret) { ret.set_float(this.acc); }; Exps.prototype.AngleOfMotion = function (ret) { ret.set_float(cr.to_degrees(cr.angleTo(0, 0, this.dx, this.dy))); }; Exps.prototype.DistanceTravelled = function (ret) { ret.set_float(this.travelled); }; Exps.prototype.Gravity = function (ret) { ret.set_float(this.g); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Car = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Car.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; this.upkey = false; this.downkey = false; this.leftkey = false; this.rightkey = false; this.ignoreInput = false; this.simup = false; this.simdown = false; this.simleft = false; this.simright = false; this.s = 0; this.a = this.inst.angle; this.m = this.inst.angle; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.maxspeed = this.properties[0]; this.acc = this.properties[1]; this.dec = this.properties[2]; this.steerSpeed = cr.to_radians(this.properties[3]); this.driftRecover = cr.to_radians(this.properties[4]); this.friction = this.properties[5]; this.setAngle = (this.properties[6] === 1); // 0=no, 1=yes this.defaultControls = (this.properties[7] === 1); // 0=no, 1=yes this.enabled = (this.properties[8] !== 0); this.lastx = this.inst.x; this.lasty = this.inst.y; this.lastAngle = this.inst.angle; if (this.defaultControls && !this.runtime.isDomFree) { jQuery(document).keydown( (function (self) { return function(info) { self.onKeyDown(info); }; })(this) ); jQuery(document).keyup( (function (self) { return function(info) { self.onKeyUp(info); }; })(this) ); } }; behinstProto.saveToJSON = function () { return { "ignoreInput": this.ignoreInput, "enabled": this.enabled, "s": this.s, "a": this.a, "m": this.m, "maxspeed": this.maxspeed, "acc": this.acc, "dec": this.dec, "steerSpeed": this.steerSpeed, "driftRecover": this.driftRecover, "friction": this.friction, "lastx": this.lastx, "lasty": this.lasty, "lastAngle": this.lastAngle }; }; behinstProto.loadFromJSON = function (o) { this.ignoreInput = o["ignoreInput"]; this.enabled = o["enabled"]; this.s = o["s"]; this.a = o["a"]; this.m = o["m"]; this.maxspeed = o["maxspeed"]; this.acc = o["acc"]; this.dec = o["dec"]; this.steerSpeed = o["steerSpeed"]; this.driftRecover = o["driftRecover"]; this.friction = o["friction"]; this.lastx = o["lastx"]; this.lasty = o["lasty"]; this.lastAngle = o["lastAngle"]; this.upkey = false; this.downkey = false; this.leftkey = false; this.rightkey = false; this.simup = false; this.simdown = false; this.simleft = false; this.simright = false; }; behinstProto.onKeyDown = function (info) { switch (info.which) { case 37: // left info.preventDefault(); this.leftkey = true; break; case 38: // up info.preventDefault(); this.upkey = true; break; case 39: // right info.preventDefault(); this.rightkey = true; break; case 40: // down info.preventDefault(); this.downkey = true; break; } }; behinstProto.onKeyUp = function (info) { switch (info.which) { case 37: // left info.preventDefault(); this.leftkey = false; break; case 38: // up info.preventDefault(); this.upkey = false; break; case 39: // right info.preventDefault(); this.rightkey = false; break; case 40: // down info.preventDefault(); this.downkey = false; break; } }; behinstProto.onWindowBlur = function () { this.upkey = false; this.downkey = false; this.leftkey = false; this.rightkey = false; }; behinstProto.tick = function () { var dt = this.runtime.getDt(this.inst); var left = this.leftkey || this.simleft; var right = this.rightkey || this.simright; var up = this.upkey || this.simup; var down = this.downkey || this.simdown; this.simleft = false; this.simright = false; this.simup = false; this.simdown = false; if (!this.enabled) return; if (this.setAngle && this.inst.angle !== this.lastAngle) { this.a = this.inst.angle; this.m = this.inst.angle; this.lastAngle = this.inst.angle; } var collobj = this.runtime.testOverlapSolid(this.inst); if (collobj) { this.runtime.registerCollision(this.inst, collobj); if (!this.runtime.pushOutSolidNearest(this.inst)) return; // must be stuck in solid } if (this.ignoreInput) { left = false; right = false; up = false; down = false; } if (up && !down) { this.s += this.acc * dt; if (this.s > this.maxspeed) this.s = this.maxspeed; } if (down && !up) { this.s -= this.dec * dt; if (this.s < -this.maxspeed) this.s = -this.maxspeed; } if (down === up) { if (this.s > 0) { this.s -= this.dec * dt * 0.1; if (this.s < 0) this.s = 0; } else if (this.s < 0) { this.s += this.dec * dt * 0.1; if (this.s > 0) this.s = 0; } } if (this.s < 0) { var temp = left; left = right; right = temp; } if (left && !right) { this.a = cr.clamp_angle(this.a - this.steerSpeed * dt * (Math.abs(this.s) / this.maxspeed)); } if (right && !left) { this.a = cr.clamp_angle(this.a + this.steerSpeed * dt * (Math.abs(this.s) / this.maxspeed)); } var recover = this.driftRecover * dt; var diff = cr.angleDiff(this.a, this.m); if (diff > cr.to_radians(90)) recover += (diff - cr.to_radians(90)); if (diff <= recover) this.m = cr.clamp_angle(this.a); else if (cr.angleClockwise(this.a, this.m)) this.m = cr.clamp_angle(this.m + recover); else this.m = cr.clamp_angle(this.m - recover); this.lastx = this.inst.x; this.lasty = this.inst.y; if (this.s !== 0 && dt !== 0) { this.inst.x += Math.cos(this.m) * this.s * dt; this.inst.y += Math.sin(this.m) * this.s * dt; if (this.setAngle) { this.inst.angle = this.a; this.lastAngle = this.a; } this.inst.set_bbox_changed(); var hitsolid = this.runtime.testOverlapSolid(this.inst); if (hitsolid) { this.runtime.registerCollision(this.inst, hitsolid); this.s = Math.abs(this.s); this.m = this.runtime.calculateSolidBounceAngle(this.inst, this.lastx, this.lasty); this.inst.x += Math.cos(this.m) * this.s * dt; // move out for another tick to try and avoid solid this.inst.y += Math.sin(this.m) * this.s * dt; this.inst.set_bbox_changed(); this.s *= (1 - this.friction); if (!this.runtime.pushOutSolid(this.inst, Math.cos(this.m), Math.sin(this.m), Math.max(this.s * 2.5 * dt, 30))) this.runtime.pushOutSolidNearest(this.inst, 100); } } else if (this.setAngle && this.inst.angle !== this.a) { this.inst.angle = this.a; this.lastAngle = this.a; this.inst.set_bbox_changed(); if (this.runtime.testOverlapSolid(this.inst)) this.runtime.pushOutSolidNearest(this.inst, 100); } }; function Cnds() {}; Cnds.prototype.IsMoving = function () { return this.s !== 0; }; Cnds.prototype.CompareSpeed = function (cmp, s) { return cr.do_cmp(this.s, cmp, s); }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Stop = function () { this.s = 0; }; Acts.prototype.SetIgnoreInput = function (ignoring) { this.ignoreInput = ignoring; }; Acts.prototype.SetSpeed = function (speed) { if (speed < -this.maxspeed) speed = -this.maxspeed; if (speed > this.maxspeed) speed = this.maxspeed; this.s = speed; }; Acts.prototype.SetMaxSpeed = function (maxspeed) { this.maxspeed = maxspeed; if (this.maxspeed < 0) this.maxspeed = 0; }; Acts.prototype.SetAcceleration = function (acc) { this.acc = acc; if (this.acc < 0) this.acc = 0; }; Acts.prototype.SetDeceleration = function (dec) { this.dec = dec; if (this.dec < 0) this.dec = 0; }; Acts.prototype.SimulateControl = function (ctrl) { switch (ctrl) { case 0: this.simleft = true; break; case 1: this.simright = true; break; case 2: this.simup = true; break; case 3: this.simdown = true; break; } }; Acts.prototype.SetEnabled = function (en) { this.enabled = (en === 1); }; Acts.prototype.SetSteerSpeed = function (x) { this.steerSpeed = cr.to_radians(x); }; Acts.prototype.SetDriftRecover = function (x) { this.driftRecover = cr.to_radians(x); }; Acts.prototype.SetFriction = function (x) { this.friction = x; }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.Speed = function (ret) { ret.set_float(this.s); }; Exps.prototype.MaxSpeed = function (ret) { ret.set_float(this.maxspeed); }; Exps.prototype.Acceleration = function (ret) { ret.set_float(this.acc); }; Exps.prototype.Deceleration = function (ret) { ret.set_float(this.dec); }; Exps.prototype.MovingAngle = function (ret) { ret.set_float(cr.to_degrees(this.m)); }; Exps.prototype.VectorX = function (ret) { ret.set_float(Math.cos(this.m) * this.s); }; Exps.prototype.VectorY = function (ret) { ret.set_float(Math.sin(this.m) * this.s); }; Exps.prototype.SteerSpeed = function (ret) { ret.set_float(cr.to_degrees(this.steerSpeed)); }; Exps.prototype.DriftRecover = function (ret) { ret.set_float(cr.to_degrees(this.driftRecover)); }; Exps.prototype.Friction = function (ret) { ret.set_float(this.friction); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Fade = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Fade.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.activeAtStart = this.properties[0] === 1; this.setMaxOpacity = false; // used to retrieve maxOpacity once in first 'Start fade' action if initially inactive this.fadeInTime = this.properties[1]; this.waitTime = this.properties[2]; this.fadeOutTime = this.properties[3]; this.destroy = this.properties[4]; // 0 = no, 1 = after fade out this.stage = this.activeAtStart ? 0 : 3; // 0 = fade in, 1 = wait, 2 = fade out, 3 = done if (this.recycled) this.stageTime.reset(); else this.stageTime = new cr.KahanAdder(); this.maxOpacity = (this.inst.opacity ? this.inst.opacity : 1.0); if (this.activeAtStart) { if (this.fadeInTime === 0) { this.stage = 1; if (this.waitTime === 0) this.stage = 2; } else { this.inst.opacity = 0; this.runtime.redraw = true; } } }; behinstProto.saveToJSON = function () { return { "fit": this.fadeInTime, "wt": this.waitTime, "fot": this.fadeOutTime, "s": this.stage, "st": this.stageTime.sum, "mo": this.maxOpacity, }; }; behinstProto.loadFromJSON = function (o) { this.fadeInTime = o["fit"]; this.waitTime = o["wt"]; this.fadeOutTime = o["fot"]; this.stage = o["s"]; this.stageTime.reset(); this.stageTime.sum = o["st"]; this.maxOpacity = o["mo"]; }; behinstProto.tick = function () { this.stageTime.add(this.runtime.getDt(this.inst)); if (this.stage === 0) { this.inst.opacity = (this.stageTime.sum / this.fadeInTime) * this.maxOpacity; this.runtime.redraw = true; if (this.inst.opacity >= this.maxOpacity) { this.inst.opacity = this.maxOpacity; this.stage = 1; // wait stage this.stageTime.reset(); this.runtime.trigger(cr.behaviors.Fade.prototype.cnds.OnFadeInEnd, this.inst); } } if (this.stage === 1) { if (this.stageTime.sum >= this.waitTime) { this.stage = 2; // fade out stage this.stageTime.reset(); this.runtime.trigger(cr.behaviors.Fade.prototype.cnds.OnWaitEnd, this.inst); } } if (this.stage === 2) { if (this.fadeOutTime !== 0) { this.inst.opacity = this.maxOpacity - ((this.stageTime.sum / this.fadeOutTime) * this.maxOpacity); this.runtime.redraw = true; if (this.inst.opacity < 0) { this.inst.opacity = 0; this.stage = 3; // done this.stageTime.reset(); this.runtime.trigger(cr.behaviors.Fade.prototype.cnds.OnFadeOutEnd, this.inst); if (this.destroy === 1) this.runtime.DestroyInstance(this.inst); } } } }; behinstProto.doStart = function () { this.stage = 0; this.stageTime.reset(); if (this.fadeInTime === 0) { this.stage = 1; if (this.waitTime === 0) this.stage = 2; } else { this.inst.opacity = 0; this.runtime.redraw = true; } }; function Cnds() {}; Cnds.prototype.OnFadeOutEnd = function () { return true; }; Cnds.prototype.OnFadeInEnd = function () { return true; }; Cnds.prototype.OnWaitEnd = function () { return true; }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.StartFade = function () { if (!this.activeAtStart && !this.setMaxOpacity) { this.maxOpacity = (this.inst.opacity ? this.inst.opacity : 1.0); this.setMaxOpacity = true; } if (this.stage === 3) this.doStart(); }; Acts.prototype.RestartFade = function () { this.doStart(); }; Acts.prototype.SetFadeInTime = function (t) { if (t < 0) t = 0; this.fadeInTime = t; }; Acts.prototype.SetWaitTime = function (t) { if (t < 0) t = 0; this.waitTime = t; }; Acts.prototype.SetFadeOutTime = function (t) { if (t < 0) t = 0; this.fadeOutTime = t; }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.FadeInTime = function (ret) { ret.set_float(this.fadeInTime); }; Exps.prototype.WaitTime = function (ret) { ret.set_float(this.waitTime); }; Exps.prototype.FadeOutTime = function (ret) { ret.set_float(this.fadeOutTime); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Pin = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Pin.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.pinObject = null; this.pinObjectUid = -1; // for loading this.pinAngle = 0; this.pinDist = 0; this.myStartAngle = 0; this.theirStartAngle = 0; this.lastKnownAngle = 0; this.mode = 0; // 0 = position & angle; 1 = position; 2 = angle; 3 = rope; 4 = bar var self = this; if (!this.recycled) { this.myDestroyCallback = (function(inst) { self.onInstanceDestroyed(inst); }); } this.runtime.addDestroyCallback(this.myDestroyCallback); }; behinstProto.saveToJSON = function () { return { "uid": this.pinObject ? this.pinObject.uid : -1, "pa": this.pinAngle, "pd": this.pinDist, "msa": this.myStartAngle, "tsa": this.theirStartAngle, "lka": this.lastKnownAngle, "m": this.mode }; }; behinstProto.loadFromJSON = function (o) { this.pinObjectUid = o["uid"]; // wait until afterLoad to look up this.pinAngle = o["pa"]; this.pinDist = o["pd"]; this.myStartAngle = o["msa"]; this.theirStartAngle = o["tsa"]; this.lastKnownAngle = o["lka"]; this.mode = o["m"]; }; behinstProto.afterLoad = function () { if (this.pinObjectUid === -1) this.pinObject = null; else { this.pinObject = this.runtime.getObjectByUID(this.pinObjectUid); ; } this.pinObjectUid = -1; }; behinstProto.onInstanceDestroyed = function (inst) { if (this.pinObject == inst) this.pinObject = null; }; behinstProto.onDestroy = function() { this.pinObject = null; this.runtime.removeDestroyCallback(this.myDestroyCallback); }; behinstProto.tick = function () { }; behinstProto.tick2 = function () { if (!this.pinObject) return; if (this.lastKnownAngle !== this.inst.angle) this.myStartAngle = cr.clamp_angle(this.myStartAngle + (this.inst.angle - this.lastKnownAngle)); var newx = this.inst.x; var newy = this.inst.y; if (this.mode === 3 || this.mode === 4) // rope mode or bar mode { var dist = cr.distanceTo(this.inst.x, this.inst.y, this.pinObject.x, this.pinObject.y); if ((dist > this.pinDist) || (this.mode === 4 && dist < this.pinDist)) { var a = cr.angleTo(this.pinObject.x, this.pinObject.y, this.inst.x, this.inst.y); newx = this.pinObject.x + Math.cos(a) * this.pinDist; newy = this.pinObject.y + Math.sin(a) * this.pinDist; } } else { newx = this.pinObject.x + Math.cos(this.pinObject.angle + this.pinAngle) * this.pinDist; newy = this.pinObject.y + Math.sin(this.pinObject.angle + this.pinAngle) * this.pinDist; } var newangle = cr.clamp_angle(this.myStartAngle + (this.pinObject.angle - this.theirStartAngle)); this.lastKnownAngle = newangle; if ((this.mode === 0 || this.mode === 1 || this.mode === 3 || this.mode === 4) && (this.inst.x !== newx || this.inst.y !== newy)) { this.inst.x = newx; this.inst.y = newy; this.inst.set_bbox_changed(); } if ((this.mode === 0 || this.mode === 2) && (this.inst.angle !== newangle)) { this.inst.angle = newangle; this.inst.set_bbox_changed(); } }; function Cnds() {}; Cnds.prototype.IsPinned = function () { return !!this.pinObject; }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.Pin = function (obj, mode_) { if (!obj) return; var otherinst = obj.getFirstPicked(this.inst); if (!otherinst) return; this.pinObject = otherinst; this.pinAngle = cr.angleTo(otherinst.x, otherinst.y, this.inst.x, this.inst.y) - otherinst.angle; this.pinDist = cr.distanceTo(otherinst.x, otherinst.y, this.inst.x, this.inst.y); this.myStartAngle = this.inst.angle; this.lastKnownAngle = this.inst.angle; this.theirStartAngle = otherinst.angle; this.mode = mode_; }; Acts.prototype.Unpin = function () { this.pinObject = null; }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.PinnedUID = function (ret) { ret.set_int(this.pinObject ? this.pinObject.uid : -1); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Platform = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Platform.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; var ANIMMODE_STOPPED = 0; var ANIMMODE_MOVING = 1; var ANIMMODE_JUMPING = 2; var ANIMMODE_FALLING = 3; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; this.leftkey = false; this.rightkey = false; this.jumpkey = false; this.jumped = false; // prevent bunnyhopping this.doubleJumped = false; this.canDoubleJump = false; this.ignoreInput = false; this.simleft = false; this.simright = false; this.simjump = false; this.lastFloorObject = null; this.loadFloorObject = -1; this.lastFloorX = 0; this.lastFloorY = 0; this.floorIsJumpthru = false; this.animMode = ANIMMODE_STOPPED; this.fallthrough = 0; // fall through jump-thru. >0 to disable, lasts a few ticks this.firstTick = true; this.dx = 0; this.dy = 0; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.updateGravity = function() { this.downx = Math.cos(this.ga); this.downy = Math.sin(this.ga); this.rightx = Math.cos(this.ga - Math.PI / 2); this.righty = Math.sin(this.ga - Math.PI / 2); this.downx = cr.round6dp(this.downx); this.downy = cr.round6dp(this.downy); this.rightx = cr.round6dp(this.rightx); this.righty = cr.round6dp(this.righty); this.g1 = this.g; if (this.g < 0) { this.downx *= -1; this.downy *= -1; this.g = Math.abs(this.g); } }; behinstProto.onCreate = function() { this.maxspeed = this.properties[0]; this.acc = this.properties[1]; this.dec = this.properties[2]; this.jumpStrength = this.properties[3]; this.g = this.properties[4]; this.g1 = this.g; this.maxFall = this.properties[5]; this.enableDoubleJump = (this.properties[6] !== 0); // 0=disabled, 1=enabled this.jumpSustain = (this.properties[7] / 1000); // convert ms to s this.defaultControls = (this.properties[8] === 1); // 0=no, 1=yes this.enabled = (this.properties[9] !== 0); this.wasOnFloor = false; this.wasOverJumpthru = this.runtime.testOverlapJumpThru(this.inst); this.loadOverJumpthru = -1; this.sustainTime = 0; // time of jump sustain remaining this.ga = cr.to_radians(90); this.updateGravity(); var self = this; if (this.defaultControls && !this.runtime.isDomFree) { jQuery(document).keydown(function(info) { self.onKeyDown(info); }); jQuery(document).keyup(function(info) { self.onKeyUp(info); }); } if (!this.recycled) { this.myDestroyCallback = function(inst) { self.onInstanceDestroyed(inst); }; } this.runtime.addDestroyCallback(this.myDestroyCallback); this.inst.extra["isPlatformBehavior"] = true; }; behinstProto.saveToJSON = function () { return { "ii": this.ignoreInput, "lfx": this.lastFloorX, "lfy": this.lastFloorY, "lfo": (this.lastFloorObject ? this.lastFloorObject.uid : -1), "am": this.animMode, "en": this.enabled, "fall": this.fallthrough, "ft": this.firstTick, "dx": this.dx, "dy": this.dy, "ms": this.maxspeed, "acc": this.acc, "dec": this.dec, "js": this.jumpStrength, "g": this.g, "g1": this.g1, "mf": this.maxFall, "wof": this.wasOnFloor, "woj": (this.wasOverJumpthru ? this.wasOverJumpthru.uid : -1), "ga": this.ga, "edj": this.enableDoubleJump, "cdj": this.canDoubleJump, "dj": this.doubleJumped, "sus": this.jumpSustain }; }; behinstProto.loadFromJSON = function (o) { this.ignoreInput = o["ii"]; this.lastFloorX = o["lfx"]; this.lastFloorY = o["lfy"]; this.loadFloorObject = o["lfo"]; this.animMode = o["am"]; this.enabled = o["en"]; this.fallthrough = o["fall"]; this.firstTick = o["ft"]; this.dx = o["dx"]; this.dy = o["dy"]; this.maxspeed = o["ms"]; this.acc = o["acc"]; this.dec = o["dec"]; this.jumpStrength = o["js"]; this.g = o["g"]; this.g1 = o["g1"]; this.maxFall = o["mf"]; this.wasOnFloor = o["wof"]; this.loadOverJumpthru = o["woj"]; this.ga = o["ga"]; this.enableDoubleJump = o["edj"]; this.canDoubleJump = o["cdj"]; this.doubleJumped = o["dj"]; this.jumpSustain = o["sus"]; this.leftkey = false; this.rightkey = false; this.jumpkey = false; this.jumped = false; this.simleft = false; this.simright = false; this.simjump = false; this.sustainTime = 0; this.updateGravity(); }; behinstProto.afterLoad = function () { if (this.loadFloorObject === -1) this.lastFloorObject = null; else this.lastFloorObject = this.runtime.getObjectByUID(this.loadFloorObject); if (this.loadOverJumpthru === -1) this.wasOverJumpthru = null; else this.wasOverJumpthru = this.runtime.getObjectByUID(this.loadOverJumpthru); }; behinstProto.onInstanceDestroyed = function (inst) { if (this.lastFloorObject == inst) this.lastFloorObject = null; }; behinstProto.onDestroy = function () { this.lastFloorObject = null; this.runtime.removeDestroyCallback(this.myDestroyCallback); }; behinstProto.onKeyDown = function (info) { switch (info.which) { case 38: // up info.preventDefault(); this.jumpkey = true; break; case 37: // left info.preventDefault(); this.leftkey = true; break; case 39: // right info.preventDefault(); this.rightkey = true; break; } }; behinstProto.onKeyUp = function (info) { switch (info.which) { case 38: // up info.preventDefault(); this.jumpkey = false; this.jumped = false; break; case 37: // left info.preventDefault(); this.leftkey = false; break; case 39: // right info.preventDefault(); this.rightkey = false; break; } }; behinstProto.onWindowBlur = function () { this.leftkey = false; this.rightkey = false; this.jumpkey = false; }; behinstProto.getGDir = function () { if (this.g < 0) return -1; else return 1; }; behinstProto.isOnFloor = function () { var ret = null; var ret2 = null; var i, len, j; var oldx = this.inst.x; var oldy = this.inst.y; this.inst.x += this.downx; this.inst.y += this.downy; this.inst.set_bbox_changed(); if (this.lastFloorObject && this.runtime.testOverlap(this.inst, this.lastFloorObject) && (!this.runtime.typeHasBehavior(this.lastFloorObject.type, cr.behaviors.solid) || this.lastFloorObject.extra["solidEnabled"])) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); return this.lastFloorObject; } else { ret = this.runtime.testOverlapSolid(this.inst); if (!ret && this.fallthrough === 0) ret2 = this.runtime.testOverlapJumpThru(this.inst, true); this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); if (ret) // was overlapping solid { if (this.runtime.testOverlap(this.inst, ret)) return null; else { this.floorIsJumpthru = false; return ret; } } if (ret2 && ret2.length) { for (i = 0, j = 0, len = ret2.length; i < len; i++) { ret2[j] = ret2[i]; if (!this.runtime.testOverlap(this.inst, ret2[i])) j++; } if (j >= 1) { this.floorIsJumpthru = true; return ret2[0]; } } return null; } }; behinstProto.tick = function () { }; behinstProto.posttick = function () { var dt = this.runtime.getDt(this.inst); var mx, my, obstacle, mag, allover, i, len, j, oldx, oldy; if (!this.jumpkey && !this.simjump) this.jumped = false; var left = this.leftkey || this.simleft; var right = this.rightkey || this.simright; var jumpkey = (this.jumpkey || this.simjump); var jump = jumpkey && !this.jumped; this.simleft = false; this.simright = false; this.simjump = false; if (!this.enabled) return; if (this.ignoreInput) { left = false; right = false; jumpkey = false; jump = false; } if (!jumpkey) this.sustainTime = 0; var lastFloor = this.lastFloorObject; var floor_moved = false; if (this.firstTick) { if (this.runtime.testOverlapSolid(this.inst) || this.runtime.testOverlapJumpThru(this.inst)) { this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy, 4, true); } this.firstTick = false; } if (lastFloor && this.dy === 0 && (lastFloor.y !== this.lastFloorY || lastFloor.x !== this.lastFloorX)) { mx = (lastFloor.x - this.lastFloorX); my = (lastFloor.y - this.lastFloorY); this.inst.x += mx; this.inst.y += my; this.inst.set_bbox_changed(); this.lastFloorX = lastFloor.x; this.lastFloorY = lastFloor.y; floor_moved = true; if (this.runtime.testOverlapSolid(this.inst)) { this.runtime.pushOutSolid(this.inst, -mx, -my, Math.sqrt(mx * mx + my * my) * 2.5); } } var floor_ = this.isOnFloor(); var collobj = this.runtime.testOverlapSolid(this.inst); if (collobj) { if (this.inst.extra["inputPredicted"]) { this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy, 10, false); } else if (this.runtime.pushOutSolidNearest(this.inst, Math.max(this.inst.width, this.inst.height) / 2)) { this.runtime.registerCollision(this.inst, collobj); } else return; } if (floor_) { this.doubleJumped = false; // reset double jump flags for next jump this.canDoubleJump = false; if (this.dy > 0) { if (!this.wasOnFloor) { this.runtime.pushInFractional(this.inst, -this.downx, -this.downy, floor_, 16); this.wasOnFloor = true; } this.dy = 0; } if (lastFloor != floor_) { this.lastFloorObject = floor_; this.lastFloorX = floor_.x; this.lastFloorY = floor_.y; this.runtime.registerCollision(this.inst, floor_); } else if (floor_moved) { collobj = this.runtime.testOverlapSolid(this.inst); if (collobj) { this.runtime.registerCollision(this.inst, collobj); if (mx !== 0) { if (mx > 0) this.runtime.pushOutSolid(this.inst, -this.rightx, -this.righty); else this.runtime.pushOutSolid(this.inst, this.rightx, this.righty); } this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy); } } } else { if (!jumpkey) this.canDoubleJump = true; } if ((floor_ && jump) || (!floor_ && this.enableDoubleJump && jumpkey && this.canDoubleJump && !this.doubleJumped)) { oldx = this.inst.x; oldy = this.inst.y; this.inst.x -= this.downx; this.inst.y -= this.downy; this.inst.set_bbox_changed(); if (!this.runtime.testOverlapSolid(this.inst)) { this.sustainTime = this.jumpSustain; this.runtime.trigger(cr.behaviors.Platform.prototype.cnds.OnJump, this.inst); this.animMode = ANIMMODE_JUMPING; this.dy = -this.jumpStrength; jump = true; // set in case is double jump if (floor_) this.jumped = true; else this.doubleJumped = true; } else jump = false; this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); } if (!floor_) { if (jumpkey && this.sustainTime > 0) { this.dy = -this.jumpStrength; this.sustainTime -= dt; } else { this.lastFloorObject = null; this.dy += this.g * dt; if (this.dy > this.maxFall) this.dy = this.maxFall; } if (jump) this.jumped = true; } this.wasOnFloor = !!floor_; if (left == right) // both up or both down { if (this.dx < 0) { this.dx += this.dec * dt; if (this.dx > 0) this.dx = 0; } else if (this.dx > 0) { this.dx -= this.dec * dt; if (this.dx < 0) this.dx = 0; } } if (left && !right) { if (this.dx > 0) this.dx -= (this.acc + this.dec) * dt; else this.dx -= this.acc * dt; } if (right && !left) { if (this.dx < 0) this.dx += (this.acc + this.dec) * dt; else this.dx += this.acc * dt; } if (this.dx > this.maxspeed) this.dx = this.maxspeed; else if (this.dx < -this.maxspeed) this.dx = -this.maxspeed; var landed = false; if (this.dx !== 0) { oldx = this.inst.x; oldy = this.inst.y; mx = this.dx * dt * this.rightx; my = this.dx * dt * this.righty; this.inst.x += this.rightx * (this.dx > 1 ? 1 : -1) - this.downx; this.inst.y += this.righty * (this.dx > 1 ? 1 : -1) - this.downy; this.inst.set_bbox_changed(); var is_jumpthru = false; var slope_too_steep = this.runtime.testOverlapSolid(this.inst); /* if (!slope_too_steep && floor_) { slope_too_steep = this.runtime.testOverlapJumpThru(this.inst); is_jumpthru = true; if (slope_too_steep) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); if (this.runtime.testOverlap(this.inst, slope_too_steep)) { slope_too_steep = null; is_jumpthru = false; } } } */ this.inst.x = oldx + mx; this.inst.y = oldy + my; this.inst.set_bbox_changed(); obstacle = this.runtime.testOverlapSolid(this.inst); if (!obstacle && floor_) { obstacle = this.runtime.testOverlapJumpThru(this.inst); if (obstacle) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); if (this.runtime.testOverlap(this.inst, obstacle)) { obstacle = null; is_jumpthru = false; } else is_jumpthru = true; this.inst.x = oldx + mx; this.inst.y = oldy + my; this.inst.set_bbox_changed(); } } if (obstacle) { var push_dist = Math.abs(this.dx * dt) + 2; if (slope_too_steep || !this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy, push_dist, is_jumpthru, obstacle)) { this.runtime.registerCollision(this.inst, obstacle); push_dist = Math.max(Math.abs(this.dx * dt * 2.5), 30); if (!this.runtime.pushOutSolid(this.inst, this.rightx * (this.dx < 0 ? 1 : -1), this.righty * (this.dx < 0 ? 1 : -1), push_dist, false)) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); } else if (floor_ && !is_jumpthru && !this.floorIsJumpthru) { oldx = this.inst.x; oldy = this.inst.y; this.inst.x += this.downx; this.inst.y += this.downy; if (this.runtime.testOverlapSolid(this.inst)) { if (!this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy, 3, false)) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); } } else { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); } } if (!is_jumpthru) this.dx = 0; // stop } else if (!slope_too_steep && !jump && (Math.abs(this.dy) < Math.abs(this.jumpStrength / 4))) { this.dy = 0; if (!floor_) landed = true; } } else { var newfloor = this.isOnFloor(); if (floor_ && !newfloor) { mag = Math.ceil(Math.abs(this.dx * dt)) + 2; oldx = this.inst.x; oldy = this.inst.y; this.inst.x += this.downx * mag; this.inst.y += this.downy * mag; this.inst.set_bbox_changed(); if (this.runtime.testOverlapSolid(this.inst) || this.runtime.testOverlapJumpThru(this.inst)) this.runtime.pushOutSolid(this.inst, -this.downx, -this.downy, mag + 2, true); else { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); } } else if (newfloor && this.dy === 0) { this.runtime.pushInFractional(this.inst, -this.downx, -this.downy, newfloor, 16); } } } if (this.dy !== 0) { oldx = this.inst.x; oldy = this.inst.y; this.inst.x += this.dy * dt * this.downx; this.inst.y += this.dy * dt * this.downy; var newx = this.inst.x; var newy = this.inst.y; this.inst.set_bbox_changed(); collobj = this.runtime.testOverlapSolid(this.inst); var fell_on_jumpthru = false; if (!collobj && (this.dy > 0) && !floor_) { allover = this.fallthrough > 0 ? null : this.runtime.testOverlapJumpThru(this.inst, true); if (allover && allover.length) { if (this.wasOverJumpthru) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); for (i = 0, j = 0, len = allover.length; i < len; i++) { allover[j] = allover[i]; if (!this.runtime.testOverlap(this.inst, allover[i])) j++; } allover.length = j; this.inst.x = newx; this.inst.y = newy; this.inst.set_bbox_changed(); } if (allover.length >= 1) collobj = allover[0]; } fell_on_jumpthru = !!collobj; } if (collobj) { this.runtime.registerCollision(this.inst, collobj); this.sustainTime = 0; var push_dist = (fell_on_jumpthru ? Math.abs(this.dy * dt * 2.5 + 10) : Math.max(Math.abs(this.dy * dt * 2.5 + 10), 30)); if (!this.runtime.pushOutSolid(this.inst, this.downx * (this.dy < 0 ? 1 : -1), this.downy * (this.dy < 0 ? 1 : -1), push_dist, fell_on_jumpthru, collobj)) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); this.wasOnFloor = true; // prevent adjustment for unexpected floor landings if (!fell_on_jumpthru) this.dy = 0; // stop } else { this.lastFloorObject = collobj; this.lastFloorX = collobj.x; this.lastFloorY = collobj.y; this.floorIsJumpthru = fell_on_jumpthru; if (fell_on_jumpthru) landed = true; this.dy = 0; // stop } } } if (this.animMode !== ANIMMODE_FALLING && this.dy > 0 && !floor_) { this.runtime.trigger(cr.behaviors.Platform.prototype.cnds.OnFall, this.inst); this.animMode = ANIMMODE_FALLING; } if (floor_ || landed) { if (this.animMode === ANIMMODE_FALLING || landed || (jump && this.dy === 0)) { this.runtime.trigger(cr.behaviors.Platform.prototype.cnds.OnLand, this.inst); if (this.dx === 0 && this.dy === 0) this.animMode = ANIMMODE_STOPPED; else this.animMode = ANIMMODE_MOVING; } else { if (this.animMode !== ANIMMODE_STOPPED && this.dx === 0 && this.dy === 0) { this.runtime.trigger(cr.behaviors.Platform.prototype.cnds.OnStop, this.inst); this.animMode = ANIMMODE_STOPPED; } if (this.animMode !== ANIMMODE_MOVING && (this.dx !== 0 || this.dy !== 0) && !jump) { this.runtime.trigger(cr.behaviors.Platform.prototype.cnds.OnMove, this.inst); this.animMode = ANIMMODE_MOVING; } } } if (this.fallthrough > 0) this.fallthrough--; this.wasOverJumpthru = this.runtime.testOverlapJumpThru(this.inst); }; function Cnds() {}; Cnds.prototype.IsMoving = function () { return this.dx !== 0 || this.dy !== 0; }; Cnds.prototype.CompareSpeed = function (cmp, s) { var speed = Math.sqrt(this.dx * this.dx + this.dy * this.dy); return cr.do_cmp(speed, cmp, s); }; Cnds.prototype.IsOnFloor = function () { if (this.dy !== 0) return false; var ret = null; var ret2 = null; var i, len, j; var oldx = this.inst.x; var oldy = this.inst.y; this.inst.x += this.downx; this.inst.y += this.downy; this.inst.set_bbox_changed(); ret = this.runtime.testOverlapSolid(this.inst); if (!ret && this.fallthrough === 0) ret2 = this.runtime.testOverlapJumpThru(this.inst, true); this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); if (ret) // was overlapping solid { return !this.runtime.testOverlap(this.inst, ret); } if (ret2 && ret2.length) { for (i = 0, j = 0, len = ret2.length; i < len; i++) { ret2[j] = ret2[i]; if (!this.runtime.testOverlap(this.inst, ret2[i])) j++; } if (j >= 1) return true; } return false; }; Cnds.prototype.IsByWall = function (side) { var ret = false; var oldx = this.inst.x; var oldy = this.inst.y; this.inst.x -= this.downx * 3; this.inst.y -= this.downy * 3; this.inst.set_bbox_changed(); if (this.runtime.testOverlapSolid(this.inst)) { this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); return false; } if (side === 0) // left { this.inst.x -= this.rightx * 2; this.inst.y -= this.righty * 2; } else { this.inst.x += this.rightx * 2; this.inst.y += this.righty * 2; } this.inst.set_bbox_changed(); ret = this.runtime.testOverlapSolid(this.inst); this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); return ret; }; Cnds.prototype.IsJumping = function () { return this.dy < 0; }; Cnds.prototype.IsFalling = function () { return this.dy > 0; }; Cnds.prototype.OnJump = function () { return true; }; Cnds.prototype.OnFall = function () { return true; }; Cnds.prototype.OnStop = function () { return true; }; Cnds.prototype.OnMove = function () { return true; }; Cnds.prototype.OnLand = function () { return true; }; Cnds.prototype.IsDoubleJumpEnabled = function () { return this.enableDoubleJump; }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetIgnoreInput = function (ignoring) { this.ignoreInput = ignoring; }; Acts.prototype.SetMaxSpeed = function (maxspeed) { this.maxspeed = maxspeed; if (this.maxspeed < 0) this.maxspeed = 0; }; Acts.prototype.SetAcceleration = function (acc) { this.acc = acc; if (this.acc < 0) this.acc = 0; }; Acts.prototype.SetDeceleration = function (dec) { this.dec = dec; if (this.dec < 0) this.dec = 0; }; Acts.prototype.SetJumpStrength = function (js) { this.jumpStrength = js; if (this.jumpStrength < 0) this.jumpStrength = 0; }; Acts.prototype.SetGravity = function (grav) { if (this.g1 === grav) return; // no change this.g = grav; this.updateGravity(); if (this.runtime.testOverlapSolid(this.inst)) { this.runtime.pushOutSolid(this.inst, this.downx, this.downy, 10); this.inst.x += this.downx * 2; this.inst.y += this.downy * 2; this.inst.set_bbox_changed(); } this.lastFloorObject = null; }; Acts.prototype.SetMaxFallSpeed = function (mfs) { this.maxFall = mfs; if (this.maxFall < 0) this.maxFall = 0; }; Acts.prototype.SimulateControl = function (ctrl) { switch (ctrl) { case 0: this.simleft = true; break; case 1: this.simright = true; break; case 2: this.simjump = true; break; } }; Acts.prototype.SetVectorX = function (vx) { this.dx = vx; }; Acts.prototype.SetVectorY = function (vy) { this.dy = vy; }; Acts.prototype.SetGravityAngle = function (a) { a = cr.to_radians(a); a = cr.clamp_angle(a); if (this.ga === a) return; // no change this.ga = a; this.updateGravity(); this.lastFloorObject = null; }; Acts.prototype.SetEnabled = function (en) { if (this.enabled !== (en === 1)) { this.enabled = (en === 1); if (!this.enabled) this.lastFloorObject = null; } }; Acts.prototype.FallThrough = function () { var oldx = this.inst.x; var oldy = this.inst.y; this.inst.x += this.downx; this.inst.y += this.downy; this.inst.set_bbox_changed(); var overlaps = this.runtime.testOverlapJumpThru(this.inst, false); this.inst.x = oldx; this.inst.y = oldy; this.inst.set_bbox_changed(); if (!overlaps) return; this.fallthrough = 3; // disable jumpthrus for 3 ticks (1 doesn't do it, 2 does, 3 to be on safe side) this.lastFloorObject = null; }; Acts.prototype.SetDoubleJumpEnabled = function (e) { this.enableDoubleJump = (e !== 0); }; Acts.prototype.SetJumpSustain = function (s) { this.jumpSustain = s / 1000; // convert to ms }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.Speed = function (ret) { ret.set_float(Math.sqrt(this.dx * this.dx + this.dy * this.dy)); }; Exps.prototype.MaxSpeed = function (ret) { ret.set_float(this.maxspeed); }; Exps.prototype.Acceleration = function (ret) { ret.set_float(this.acc); }; Exps.prototype.Deceleration = function (ret) { ret.set_float(this.dec); }; Exps.prototype.JumpStrength = function (ret) { ret.set_float(this.jumpStrength); }; Exps.prototype.Gravity = function (ret) { ret.set_float(this.g); }; Exps.prototype.GravityAngle = function (ret) { ret.set_float(cr.to_degrees(this.ga)); }; Exps.prototype.MaxFallSpeed = function (ret) { ret.set_float(this.maxFall); }; Exps.prototype.MovingAngle = function (ret) { ret.set_float(cr.to_degrees(Math.atan2(this.dy, this.dx))); }; Exps.prototype.VectorX = function (ret) { ret.set_float(this.dx); }; Exps.prototype.VectorY = function (ret) { ret.set_float(this.dy); }; Exps.prototype.JumpSustain = function (ret) { ret.set_float(this.jumpSustain * 1000); // convert back to ms }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Rotate = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Rotate.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.speed = cr.to_radians(this.properties[0]); this.acc = cr.to_radians(this.properties[1]); }; behinstProto.saveToJSON = function () { return { "speed": this.speed, "acc": this.acc }; }; behinstProto.loadFromJSON = function (o) { this.speed = o["speed"]; this.acc = o["acc"]; }; behinstProto.tick = function () { var dt = this.runtime.getDt(this.inst); if (dt === 0) return; if (this.acc !== 0) this.speed += this.acc * dt; if (this.speed !== 0) { this.inst.angle = cr.clamp_angle(this.inst.angle + this.speed * dt); this.inst.set_bbox_changed(); } }; function Cnds() {}; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetSpeed = function (s) { this.speed = cr.to_radians(s); }; Acts.prototype.SetAcceleration = function (a) { this.acc = cr.to_radians(a); }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.Speed = function (ret) { ret.set_float(cr.to_degrees(this.speed)); }; Exps.prototype.Acceleration = function (ret) { ret.set_float(cr.to_degrees(this.acc)); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Sin = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Sin.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; this.i = 0; // period offset (radians) }; var behinstProto = behaviorProto.Instance.prototype; var _2pi = 2 * Math.PI; var _pi_2 = Math.PI / 2; var _3pi_2 = (3 * Math.PI) / 2; behinstProto.onCreate = function() { this.active = (this.properties[0] === 1); this.movement = this.properties[1]; // 0=Horizontal|1=Vertical|2=Size|3=Width|4=Height|5=Angle|6=Opacity|7=Value only this.wave = this.properties[2]; // 0=Sine|1=Triangle|2=Sawtooth|3=Reverse sawtooth|4=Square this.period = this.properties[3]; this.period += Math.random() * this.properties[4]; // period random if (this.period === 0) this.i = 0; else { this.i = (this.properties[5] / this.period) * _2pi; // period offset this.i += ((Math.random() * this.properties[6]) / this.period) * _2pi; // period offset random } this.mag = this.properties[7]; // magnitude this.mag += Math.random() * this.properties[8]; // magnitude random this.initialValue = 0; this.initialValue2 = 0; this.ratio = 0; this.init(); }; behinstProto.saveToJSON = function () { return { "i": this.i, "a": this.active, "mv": this.movement, "w": this.wave, "p": this.period, "mag": this.mag, "iv": this.initialValue, "iv2": this.initialValue2, "r": this.ratio, "lkv": this.lastKnownValue, "lkv2": this.lastKnownValue2 }; }; behinstProto.loadFromJSON = function (o) { this.i = o["i"]; this.active = o["a"]; this.movement = o["mv"]; this.wave = o["w"]; this.period = o["p"]; this.mag = o["mag"]; this.initialValue = o["iv"]; this.initialValue2 = o["iv2"] || 0; this.ratio = o["r"]; this.lastKnownValue = o["lkv"]; this.lastKnownValue2 = o["lkv2"] || 0; }; behinstProto.init = function () { switch (this.movement) { case 0: // horizontal this.initialValue = this.inst.x; break; case 1: // vertical this.initialValue = this.inst.y; break; case 2: // size this.initialValue = this.inst.width; this.ratio = this.inst.height / this.inst.width; break; case 3: // width this.initialValue = this.inst.width; break; case 4: // height this.initialValue = this.inst.height; break; case 5: // angle this.initialValue = this.inst.angle; this.mag = cr.to_radians(this.mag); // convert magnitude from degrees to radians break; case 6: // opacity this.initialValue = this.inst.opacity; break; case 7: this.initialValue = 0; break; case 8: // forwards/backwards this.initialValue = this.inst.x; this.initialValue2 = this.inst.y; break; default: ; } this.lastKnownValue = this.initialValue; this.lastKnownValue2 = this.initialValue2; }; behinstProto.waveFunc = function (x) { x = x % _2pi; switch (this.wave) { case 0: // sine return Math.sin(x); case 1: // triangle if (x <= _pi_2) return x / _pi_2; else if (x <= _3pi_2) return 1 - (2 * (x - _pi_2) / Math.PI); else return (x - _3pi_2) / _pi_2 - 1; case 2: // sawtooth return 2 * x / _2pi - 1; case 3: // reverse sawtooth return -2 * x / _2pi + 1; case 4: // square return x < Math.PI ? -1 : 1; }; return 0; }; behinstProto.tick = function () { var dt = this.runtime.getDt(this.inst); if (!this.active || dt === 0) return; if (this.period === 0) this.i = 0; else { this.i += (dt / this.period) * _2pi; this.i = this.i % _2pi; } this.updateFromPhase(); }; behinstProto.updateFromPhase = function () { switch (this.movement) { case 0: // horizontal if (this.inst.x !== this.lastKnownValue) this.initialValue += this.inst.x - this.lastKnownValue; this.inst.x = this.initialValue + this.waveFunc(this.i) * this.mag; this.lastKnownValue = this.inst.x; break; case 1: // vertical if (this.inst.y !== this.lastKnownValue) this.initialValue += this.inst.y - this.lastKnownValue; this.inst.y = this.initialValue + this.waveFunc(this.i) * this.mag; this.lastKnownValue = this.inst.y; break; case 2: // size this.inst.width = this.initialValue + this.waveFunc(this.i) * this.mag; this.inst.height = this.inst.width * this.ratio; break; case 3: // width this.inst.width = this.initialValue + this.waveFunc(this.i) * this.mag; break; case 4: // height this.inst.height = this.initialValue + this.waveFunc(this.i) * this.mag; break; case 5: // angle if (this.inst.angle !== this.lastKnownValue) this.initialValue = cr.clamp_angle(this.initialValue + (this.inst.angle - this.lastKnownValue)); this.inst.angle = cr.clamp_angle(this.initialValue + this.waveFunc(this.i) * this.mag); this.lastKnownValue = this.inst.angle; break; case 6: // opacity this.inst.opacity = this.initialValue + (this.waveFunc(this.i) * this.mag) / 100; if (this.inst.opacity < 0) this.inst.opacity = 0; else if (this.inst.opacity > 1) this.inst.opacity = 1; break; case 8: // forwards/backwards if (this.inst.x !== this.lastKnownValue) this.initialValue += this.inst.x - this.lastKnownValue; if (this.inst.y !== this.lastKnownValue2) this.initialValue2 += this.inst.y - this.lastKnownValue2; this.inst.x = this.initialValue + Math.cos(this.inst.angle) * this.waveFunc(this.i) * this.mag; this.inst.y = this.initialValue2 + Math.sin(this.inst.angle) * this.waveFunc(this.i) * this.mag; this.lastKnownValue = this.inst.x; this.lastKnownValue2 = this.inst.y; break; } this.inst.set_bbox_changed(); }; behinstProto.onSpriteFrameChanged = function (prev_frame, next_frame) { switch (this.movement) { case 2: // size this.initialValue *= (next_frame.width / prev_frame.width); this.ratio = next_frame.height / next_frame.width; break; case 3: // width this.initialValue *= (next_frame.width / prev_frame.width); break; case 4: // height this.initialValue *= (next_frame.height / prev_frame.height); break; } }; function Cnds() {}; Cnds.prototype.IsActive = function () { return this.active; }; Cnds.prototype.CompareMovement = function (m) { return this.movement === m; }; Cnds.prototype.ComparePeriod = function (cmp, v) { return cr.do_cmp(this.period, cmp, v); }; Cnds.prototype.CompareMagnitude = function (cmp, v) { if (this.movement === 5) return cr.do_cmp(this.mag, cmp, cr.to_radians(v)); else return cr.do_cmp(this.mag, cmp, v); }; Cnds.prototype.CompareWave = function (w) { return this.wave === w; }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.SetActive = function (a) { this.active = (a === 1); }; Acts.prototype.SetPeriod = function (x) { this.period = x; }; Acts.prototype.SetMagnitude = function (x) { this.mag = x; if (this.movement === 5) // angle this.mag = cr.to_radians(this.mag); }; Acts.prototype.SetMovement = function (m) { if (this.movement === 5) this.mag = cr.to_degrees(this.mag); this.movement = m; this.init(); }; Acts.prototype.SetWave = function (w) { this.wave = w; }; Acts.prototype.SetPhase = function (x) { this.i = (x * _2pi) % _2pi; this.updateFromPhase(); }; Acts.prototype.UpdateInitialState = function () { this.init(); }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.CyclePosition = function (ret) { ret.set_float(this.i / _2pi); }; Exps.prototype.Period = function (ret) { ret.set_float(this.period); }; Exps.prototype.Magnitude = function (ret) { if (this.movement === 5) // angle ret.set_float(cr.to_degrees(this.mag)); else ret.set_float(this.mag); }; Exps.prototype.Value = function (ret) { ret.set_float(this.waveFunc(this.i) * this.mag); }; behaviorProto.exps = new Exps(); }()); ; ; cr.behaviors.Timer = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.Timer.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.timers = {}; }; behinstProto.onDestroy = function () { cr.wipe(this.timers); }; behinstProto.saveToJSON = function () { var o = {}; var p, t; for (p in this.timers) { if (this.timers.hasOwnProperty(p)) { t = this.timers[p]; o[p] = { "c": t.current.sum, "t": t.total.sum, "d": t.duration, "r": t.regular }; } } return o; }; behinstProto.loadFromJSON = function (o) { this.timers = {}; var p; for (p in o) { if (o.hasOwnProperty(p)) { this.timers[p] = { current: new cr.KahanAdder(), total: new cr.KahanAdder(), duration: o[p]["d"], regular: o[p]["r"] }; this.timers[p].current.sum = o[p]["c"]; this.timers[p].total.sum = o[p]["t"]; } } }; behinstProto.tick = function () { var dt = this.runtime.getDt(this.inst); var p, t; for (p in this.timers) { if (this.timers.hasOwnProperty(p)) { t = this.timers[p]; t.current.add(dt); t.total.add(dt); } } }; behinstProto.tick2 = function () { var p, t; for (p in this.timers) { if (this.timers.hasOwnProperty(p)) { t = this.timers[p]; if (t.current.sum >= t.duration) { if (t.regular) t.current.sum -= t.duration; else delete this.timers[p]; } } } }; function Cnds() {}; Cnds.prototype.OnTimer = function (tag_) { tag_ = tag_.toLowerCase(); var t = this.timers[tag_]; if (!t) return false; return t.current.sum >= t.duration; }; behaviorProto.cnds = new Cnds(); function Acts() {}; Acts.prototype.StartTimer = function (duration_, type_, tag_) { this.timers[tag_.toLowerCase()] = { current: new cr.KahanAdder(), total: new cr.KahanAdder(), duration: duration_, regular: (type_ === 1) }; }; Acts.prototype.StopTimer = function (tag_) { tag_ = tag_.toLowerCase(); if (this.timers.hasOwnProperty(tag_)) delete this.timers[tag_]; }; behaviorProto.acts = new Acts(); function Exps() {}; Exps.prototype.CurrentTime = function (ret, tag_) { var t = this.timers[tag_.toLowerCase()]; ret.set_float(t ? t.current.sum : 0); }; Exps.prototype.TotalTime = function (ret, tag_) { var t = this.timers[tag_.toLowerCase()]; ret.set_float(t ? t.total.sum : 0); }; Exps.prototype.Duration = function (ret, tag_) { var t = this.timers[tag_.toLowerCase()]; ret.set_float(t ? t.duration : 0); }; behaviorProto.exps = new Exps(); }()); var easeOutBounceArray = []; var easeInElasticArray = []; var easeOutElasticArray = []; var easeInOutElasticArray = []; var easeInCircle = []; var easeOutCircle = []; var easeInOutCircle = []; var easeInBack = []; var easeOutBack = []; var easeInOutBack = []; var litetween_precision = 10000; var updateLimit = 0; //0.0165; function easeOutBouncefunc(t) { var b=0.0; var c=1.0; var d=1.0; if ((t/=d) < (1/2.75)) { result = c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { result = c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { result = c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { result = c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } return result; } function integerize(t, d) { return Math.round(t/d*litetween_precision); } function easeFunc(easing, t, b, c, d, flip, param) { var ret_ease = 0; switch (easing) { case 0: // linear ret_ease = c*t/d + b; break; case 1: // easeInQuad ret_ease = c*(t/=d)*t + b; break; case 2: // easeOutQuad ret_ease = -c *(t/=d)*(t-2) + b; break; case 3: // easeInOutQuad if ((t/=d/2) < 1) ret_ease = c/2*t*t + b else ret_ease = -c/2 * ((--t)*(t-2) - 1) + b; break; case 4: // easeInCubic ret_ease = c*(t/=d)*t*t + b; break; case 5: // easeOutCubic ret_ease = c*((t=t/d-1)*t*t + 1) + b; break; case 6: // easeInOutCubic if ((t/=d/2) < 1) ret_ease = c/2*t*t*t + b else ret_ease = c/2*((t-=2)*t*t + 2) + b; break; case 7: // easeInQuart ret_ease = c*(t/=d)*t*t*t + b; break; case 8: // easeOutQuart ret_ease = -c * ((t=t/d-1)*t*t*t - 1) + b; break; case 9: // easeInOutQuart if ((t/=d/2) < 1) ret_ease = c/2*t*t*t*t + b else ret_ease = -c/2 * ((t-=2)*t*t*t - 2) + b; break; case 10: // easeInQuint ret_ease = c*(t/=d)*t*t*t*t + b; break; case 11: // easeOutQuint ret_ease = c*((t=t/d-1)*t*t*t*t + 1) + b; break; case 12: // easeInOutQuint if ((t/=d/2) < 1) ret_ease = c/2*t*t*t*t*t + b else ret_ease = c/2*((t-=2)*t*t*t*t + 2) + b; break; case 13: // easeInCircle if (param.optimized) { ret_ease = easeInCircle[integerize(t,d)]; } else { ret_ease = -(Math.sqrt(1-t*t) - 1); } break; case 14: // easeOutCircle if (param.optimized) { ret_ease = easeOutCircle[integerize(t,d)]; } else { ret_ease = Math.sqrt(1 - ((t-1)*(t-1))); } break; case 15: // easeInOutCircle if (param.optimized) { ret_ease = easeInOutCircle[integerize(t,d)]; } else { if ((t/=d/2) < 1) ret_ease = -c/2 * (Math.sqrt(1 - t*t) - 1) + b else ret_ease = c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; } break; case 16: // easeInBack if (param.optimized) { ret_ease = easeInBack[integerize(t,d)]; } else { var s = param.s; ret_ease = c*(t/=d)*t*((s+1)*t - s) + b; } break; case 17: // easeOutBack if (param.optimized) { ret_ease = easeOutBack[integerize(t,d)]; } else { var s = param.s; ret_ease = c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; } break; case 18: // easeInOutBack if (param.optimized) { ret_ease = easeInOutBack[integerize(t,d)]; } else { var s = param.s if ((t/=d/2) < 1) ret_ease = c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b else ret_ease = c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; } break; case 19: //easeInElastic if (param.optimized) { ret_ease = easeInElasticArray[integerize(t, d)]; } else { var a = param.a; var p = param.p; var s = 0; if (t==0) ret_ease = b; if ((t/=d)==1) ret_ease = b+c; if (p==0) p=d*.3; if (a==0 || a < Math.abs(c)) { a=c; s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); ret_ease = -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; } break; case 20: //easeOutElastic if (param.optimized) { ret_ease = easeOutElasticArray[integerize(t,d)]; } else { var a = param.a; var p = param.p; var s = 0; if (t==0) ret_ease= b; if ((t/=d)==1) ret_ease= b+c; if (p == 0) p=d*.3; if (a==0 || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); ret_ease= (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b); } break; case 21: //easeInOutElastic if (param.optimized) { ret_ease = easeInOutElasticArray[integerize(t,d)]; } else { var a = param.a; var p = param.p; var s = 0; if (t==0) ret_ease = b; if ((t/=d/2)==2) ret_ease = b+c; if (p==0) p=d*(.3*1.5); if (a==0 || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) ret_ease = -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b else ret_ease = a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; } break; case 22: //easeInBounce if (param.optimized) { ret_ease = c - easeOutBounceArray[integerize(d-t, d)] + b; } else { ret_ease = c - easeOutBouncefunc(d-t/d) + b; } break; case 23: //easeOutBounce if (param.optimized) { ret_ease = easeOutBounceArray[integerize(t, d)]; } else { ret_ease = easeOutBouncefunc(t/d); } break; case 24: //easeInOutBounce if (param.optimized) { if (t < d/2) ret_ease = (c - easeOutBounceArray[integerize(d-(t*2), d)] + b) * 0.5 +b; else ret_ease = easeOutBounceArray[integerize(t*2-d, d)] * .5 + c*.5 + b; } else { if (t < d/2) ret_ease = (c - easeOutBouncefunc(d-(t*2)) + b) * 0.5 +b; else ret_ease = easeOutBouncefunc((t*2-d)/d) * .5 + c *.5 + b; } break; case 25: //easeInSmoothstep var mt = (t/d) / 2; ret_ease = (2*(mt * mt * (3 - 2*mt))); break; case 26: //easeOutSmoothstep var mt = ((t/d) + 1) / 2; ret_ease = ((2*(mt * mt * (3 - 2*mt))) - 1); break; case 27: //easeInOutSmoothstep var mt = (t / d); ret_ease = (mt * mt * (3 - 2*mt)); break; }; if (flip) return (c - b) - ret_ease else return ret_ease; }; (function preCalculateArray() { var d = 1.0; var b = 0.0; var c = 1.0; var result = 0.0; var a = 0.0; var p = 0.0; var t = 0.0; var s = 0.0; for (var ti = 0; ti <= litetween_precision; ti++) { t = ti/litetween_precision; if ((t/=d) < (1/2.75)) { result = c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { result = c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { result = c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { result = c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } easeOutBounceArray[ti] = result; t = ti/litetween_precision; a = 0; p = 0; if (t==0) result = b; if ((t/=d)==1) result = b+c; if (p==0) p=d*.3; if (a==0 || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); result = -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; easeInElasticArray[ti] = result; t = ti/litetween_precision; a = 0; p = 0; if (t==0) result= b; if ((t/=d)==1) result= b+c; if (p == 0) p=d*.3; if (a==0 || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); result= (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b); easeOutElasticArray[ti] = result; t = ti/litetween_precision; a = 0; p = 0; if (t==0) result = b; if ((t/=d/2)==2) result = b+c; if (p==0) p=d*(.3*1.5); if (a==0 || a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) result = -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b else result = a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; easeInOutElasticArray[ti] = result; t = ti/litetween_precision; easeInCircle[ti] = -(Math.sqrt(1-t*t) - 1); t = ti/litetween_precision; easeOutCircle[ti] = Math.sqrt(1 - ((t-1)*(t-1))); t = ti/litetween_precision; if ((t/=d/2) < 1) result = -c/2 * (Math.sqrt(1 - t*t) - 1) + b else result = c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; easeInOutCircle[ti] = result; t = ti/litetween_precision; s = 0; if (s==0) s = 1.70158; result = c*(t/=d)*t*((s+1)*t - s) + b; easeInBack[ti] = result; t = ti/litetween_precision; s = 0; if (s==0) s = 1.70158; result = c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; easeOutBack[ti] = result; t = ti/litetween_precision; s = 0; if (s==0) s = 1.70158; if ((t/=d/2) < 1) result = c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b else result = c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; easeInOutBack[ti] = result; } }()); var TweenObject = function() { var constructor = function (tname, tweened, easefunc, initial, target, duration, enforce) { this.name = tname; this.value = 0; this.setInitial(initial); this.setTarget(target); this.easefunc = easefunc; this.tweened = tweened; this.duration = duration; this.progress = 0; this.state = 0; this.onStart = false; this.onEnd = false; this.onReverseStart = false; this.onReverseEnd = false; this.lastKnownValue = 0; this.lastKnownValue2 = 0; this.enforce = enforce; this.pingpong = 1.0; this.flipEase = false; this.easingparam = []; this.lastState = 1; for (var i=0; i<28; i++) { this.easingparam[i] = {}; this.easingparam[i].a = 0.0; this.easingparam[i].p = 0.0; this.easingparam[i].t = 0.0; this.easingparam[i].s = 0.0; this.easingparam[i].optimized = true; } } return constructor; }(); (function () { TweenObject.prototype = { }; TweenObject.prototype.flipTarget = function () { var x1 = this.initialparam1; var x2 = this.initialparam2; this.initialparam1 = this.targetparam1; this.initialparam2 = this.targetparam2; this.targetparam1 = x1; this.targetparam2 = x2; this.lastKnownValue = 0; this.lastKnownValue2 = 0; } TweenObject.prototype.setInitial = function (initial) { this.initialparam1 = parseFloat(initial.split(",")[0]); this.initialparam2 = parseFloat(initial.split(",")[1]); this.lastKnownValue = 0; this.lastKnownValue2 = 0; } TweenObject.prototype.setTarget = function (target) { this.targetparam1 = parseFloat(target.split(",")[0]); this.targetparam2 = parseFloat(target.split(",")[1]); if (isNaN(this.targetparam2)) this.targetparam2 = this.targetparam1; } TweenObject.prototype.OnTick = function(dt) { if (this.state === 0) return -1.0; if (this.state === 1) this.progress += dt; if (this.state === 2) this.progress -= dt; if (this.state === 3) { this.state = 0; } if ((this.state === 4) || (this.state === 6)) { this.progress += dt * this.pingpong; } if (this.state === 5) { this.progress += dt * this.pingpong; } if (this.progress < 0) { this.progress = 0; if (this.state === 4) { this.pingpong = 1; } else if (this.state === 6) { this.pingpong = 1; this.flipEase = false; } else { this.state = 0; } this.onReverseEnd = true; return 0.0; } else if (this.progress > this.duration) { this.progress = this.duration; if (this.state === 4) { this.pingpong = -1; } else if (this.state === 6) { this.pingpong = -1; this.flipEase = true; } else if (this.state === 5) { this.progress = 0.0; } else { this.state = 0; } this.onEnd = true; return 1.0; } else { if (this.flipEase) { var factor = easeFunc(this.easefunc, this.duration - this.progress, 0, 1, this.duration, this.flipEase, this.easingparam[this.easefunc]); } else { var factor = easeFunc(this.easefunc, this.progress, 0, 1, this.duration, this.flipEase, this.easingparam[this.easefunc]); } return factor; } }; }()); ; ; function trim (str) { return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); } cr.behaviors.lunarray_LiteTween = function(runtime) { this.runtime = runtime; }; (function () { var behaviorProto = cr.behaviors.lunarray_LiteTween.prototype; behaviorProto.Type = function(behavior, objtype) { this.behavior = behavior; this.objtype = objtype; this.runtime = behavior.runtime; }; var behtypeProto = behaviorProto.Type.prototype; behtypeProto.onCreate = function() { }; behaviorProto.Instance = function(type, inst) { this.type = type; this.behavior = type.behavior; this.inst = inst; // associated object instance to modify this.runtime = type.runtime; this.i = 0; // progress }; var behinstProto = behaviorProto.Instance.prototype; behinstProto.onCreate = function() { this.playmode = this.properties[0]; this.active = (this.playmode == 1) || (this.playmode == 2) || (this.playmode == 3) || (this.playmode == 4); this.tweened = this.properties[1]; // 0=Position|1=Size|2=Width|3=Height|4=Angle|5=Opacity|6=Value only|7=Horizontal|8=Vertical|9=Scale this.easing = this.properties[2]; this.target = this.properties[3]; this.targetmode = this.properties[4]; this.useCurrent = false; if (this.targetmode === 1) this.target = "relative("+this.target+")"; this.duration = this.properties[5]; this.enforce = (this.properties[6] === 1); this.value = 0; this.tween_list = {}; this.addToTweenList("default", this.tweened, this.easing, "current", this.target, this.duration, this.enforce); if (this.properties[0] === 1) this.startTween(0) if (this.properties[0] === 2) this.startTween(2) if (this.properties[0] === 3) this.startTween(3) if (this.properties[0] === 4) this.startTween(4) }; behinstProto.parseCurrent = function(tweened, parseText) { if (parseText === undefined) parseText = "current"; var parsed = trim(parseText); parseText = trim(parseText); var value = this.value; if (parseText === "current") { switch (tweened) { case 0: parsed = this.inst.x + "," + this.inst.y; break; case 1: parsed = this.inst.width + "," + this.inst.height; break; case 2: parsed = this.inst.width + "," + this.inst.height; break; case 3: parsed = this.inst.width + "," + this.inst.height; break; case 4: parsed = cr.to_degrees(this.inst.angle) + "," + cr.to_degrees(this.inst.angle); break; case 5: parsed = (this.inst.opacity*100) + "," + (this.inst.opacity*100); break; case 6: parsed = value + "," + value; break; case 7: parsed = this.inst.x + "," + this.inst.y; break; case 8: parsed = this.inst.x + "," + this.inst.y; break; case 9: if (this.inst.curFrame !== undefined) parsed = (this.inst.width/this.inst.curFrame.width) + "," +(this.inst.height/this.inst.curFrame.height) else parsed = "1,1"; break; default: break; } } if (parseText.substring(0,8) === "relative") { var param1 = parseText.match(/\((.*?)\)/); if (param1) { var relativex = parseFloat(param1[1].split(",")[0]); var relativey = parseFloat(param1[1].split(",")[1]); } if (isNaN(relativex)) relativex = 0; if (isNaN(relativey)) relativey = 0; switch (tweened) { case 0: parsed = (this.inst.x+relativex) + "," + (this.inst.y+relativey); break; case 1: parsed = (this.inst.width+relativex) + "," + (this.inst.height+relativey); break; case 2: parsed = (this.inst.width+relativex) + "," + (this.inst.height+relativey); break; case 3: parsed = (this.inst.width+relativex) + "," + (this.inst.height+relativey); break; case 4: parsed = (cr.to_degrees(this.inst.angle)+relativex) + "," + (cr.to_degrees(this.inst.angle)+relativey); break; case 5: parsed = (this.inst.opacity*100+relativex) + "," + (this.inst.opacity*100+relativey); break; case 6: parsed = value+relativex + "," + value+relativex; break; case 7: parsed = (this.inst.x+relativex) + "," + (this.inst.y); break; case 8: parsed = (this.inst.x) + "," + (this.inst.y+relativex); break; case 9: parsed = (relativex) + "," + (relativey); break; default: break; } } return parsed; }; behinstProto.addToTweenList = function(tname, tweened, easing, init, targ, duration, enforce) { init = this.parseCurrent(tweened, init); targ = this.parseCurrent(tweened, targ); if (this.tween_list[tname] !== undefined) { delete this.tween_list[tname] } this.tween_list[tname] = new TweenObject(tname, tweened, easing, init, targ, duration, enforce); this.tween_list[tname].dt = 0; }; behinstProto.saveToJSON = function () { var v = JSON.stringify(this.tween_list["default"]); return { "playmode": this.playmode, "active": this.active, "tweened": this.tweened, "easing": this.easing, "target": this.target, "targetmode": this.targetmode, "useCurrent": this.useCurrent, "duration": this.duration, "enforce": this.enforce, "value": this.value, "tweenlist": JSON.stringify(this.tween_list["default"]) }; }; TweenObject.Load = function(rawObj, tname, tweened, easing, init, targ, duration, enforce) { var obj = new TweenObject(tname, tweened, easing, init, targ, duration, enforce); for(var i in rawObj) obj[i] = rawObj[i]; return obj; }; behinstProto.loadFromJSON = function (o) { var x = JSON.parse(o["tweenlist"]); var tempObj = TweenObject.Load(x, x.name, x.tweened, x.easefunc, x.initialparam1+","+x.initialparam2, x.targetparam1+","+x.targetparam2, x.duration, x.enforce); this.tween_list["default"] = tempObj; this.playmode = o["playmode"]; this.active = o["active"]; this.movement = o["tweened"]; this.easing = o["easing"]; this.target = o["target"]; this.targetmode = o["targetmode"]; this.useCurrent = o["useCurrent"]; this.duration = o["duration"]; this.enforce = o["enforce"]; this.value = o["value"]; }; behinstProto.setProgressTo = function (mark) { if (mark > 1.0) mark = 1.0; if (mark < 0.0) mark = 0.0; for (var i in this.tween_list) { var inst = this.tween_list[i]; inst.lastKnownValue = 0; inst.lastKnownValue2 = 0; inst.state = 3; inst.progress = mark * inst.duration; var factor = inst.OnTick(0); this.updateTween(inst, factor); } } behinstProto.startTween = function (startMode) { for (var i in this.tween_list) { var inst = this.tween_list[i]; if (this.useCurrent) { var init = this.parseCurrent(inst.tweened, "current"); var target = this.parseCurrent(inst.tweened, this.target); inst.setInitial(init); inst.setTarget(target); } if (startMode === 0) { inst.progress = 0.000001; inst.lastKnownValue = 0; inst.lastKnownValue2 = 0; inst.onStart = true; inst.state = 1; } if (startMode === 1) { inst.state = inst.lastState; } if ((startMode === 2) || (startMode === 4)) { inst.progress = 0.000001; inst.lastKnownValue = 0; inst.lastKnownValue2 = 0; inst.onStart = true; if (startMode == 2) inst.state = 4; //state ping pong if (startMode == 4) inst.state = 6; //state flip flop } if (startMode === 3) { inst.progress = 0.000001; inst.lastKnownValue = 0; inst.lastKnownValue2 = 0; inst.onStart = true; inst.state = 5; } } } behinstProto.stopTween = function (stopMode) { for (var i in this.tween_list) { var inst = this.tween_list[i]; if ((inst.state != 3) && (inst.state != 0)) //don't save paused/seek state inst.lastState = inst.state; if (stopMode === 1) inst.progress = 0.0; if (stopMode === 2) inst.progress = inst.duration; inst.state = 3; var factor = inst.OnTick(0); this.updateTween(inst, factor); } } behinstProto.reverseTween = function(reverseMode) { for (var i in this.tween_list) { var inst = this.tween_list[i]; if (reverseMode === 1) { inst.progress = inst.duration; inst.lastKnownValue = 0; inst.lastKnownValue2 = 0; inst.onReverseStart = true; } inst.state = 2; } } behinstProto.updateTween = function (inst, factor) { if (inst.tweened === 0) { if (inst.enforce) { this.inst.x = inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor; this.inst.y = inst.initialparam2 + (inst.targetparam2 - inst.initialparam2) * factor; } else { this.inst.x += ((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue; this.inst.y += ((inst.targetparam2 - inst.initialparam2) * factor) - inst.lastKnownValue2; inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); inst.lastKnownValue2 = ((inst.targetparam2 - inst.initialparam2) * factor); } } else if (inst.tweened === 1) { if (inst.enforce) { this.inst.width = (inst.initialparam1 + ((inst.targetparam1 - inst.initialparam1) * (factor))); this.inst.height = (inst.initialparam2 + ((inst.targetparam2 - inst.initialparam2) * (factor))); } else { this.inst.width += ((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue; this.inst.height += ((inst.targetparam2 - inst.initialparam2) * factor) - inst.lastKnownValue2; inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); inst.lastKnownValue2 = ((inst.targetparam2 - inst.initialparam2) * factor); } } else if (inst.tweened === 2) { if (inst.enforce) { this.inst.width = (inst.initialparam1 + ((inst.targetparam1 - inst.initialparam1) * (factor))); } else { this.inst.width += ((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue; inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); } } else if (inst.tweened === 3) { if (inst.enforce) { this.inst.height = (inst.initialparam2 + ((inst.targetparam2 - inst.initialparam2) * (factor))); } else { this.inst.height += ((inst.targetparam2 - inst.initialparam2) * factor) - inst.lastKnownValue2; inst.lastKnownValue2 = ((inst.targetparam2 - inst.initialparam2) * factor); } } else if (inst.tweened === 4) { if (inst.enforce) { var tangle = inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor; this.inst.angle = cr.clamp_angle(cr.to_radians(tangle)); } else { var tangle = ((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue; this.inst.angle = cr.clamp_angle(this.inst.angle + cr.to_radians(tangle)); inst.lastKnownValue = (inst.targetparam1 - inst.initialparam1) * factor; } } else if (inst.tweened === 5) { if (inst.enforce) { this.inst.opacity = (inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor) / 100; } else { this.inst.opacity += (((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue) / 100; inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); } } else if (inst.tweened === 6) { if (inst.enforce) { this.value = (inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor); } else { this.value += (((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue); inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); } } else if (inst.tweened === 7) { if (inst.enforce) { this.inst.x = inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor; } else { this.inst.x += ((inst.targetparam1 - inst.initialparam1) * factor) - inst.lastKnownValue; inst.lastKnownValue = ((inst.targetparam1 - inst.initialparam1) * factor); } } else if (inst.tweened === 8) { if (inst.enforce) { this.inst.y = inst.initialparam2 + (inst.targetparam2 - inst.initialparam2) * factor; } else { this.inst.y += ((inst.targetparam2 - inst.initialparam2) * factor) - inst.lastKnownValue2; inst.lastKnownValue2 = ((inst.targetparam2 - inst.initialparam2) * factor); } } else if (inst.tweened === 9) { var scalex = inst.initialparam1 + (inst.targetparam1 - inst.initialparam1) * factor; var scaley = inst.initialparam2 + (inst.targetparam2 - inst.initialparam2) * factor; if (this.inst.width < 0) scalex = inst.initialparam1 + (inst.targetparam1 + inst.initialparam1) * -factor; if (this.inst.height < 0) scaley = inst.initialparam2 + (inst.targetparam2 + inst.initialparam2) * -factor; if (inst.enforce) { this.inst.width = this.inst.curFrame.width * scalex; this.inst.height = this.inst.curFrame.height * scaley; } else { if (this.inst.width < 0) { this.inst.width = scalex * (this.inst.width / (-1+inst.lastKnownValue)); inst.lastKnownValue = scalex + 1 } else { this.inst.width = scalex * (this.inst.width / (1+inst.lastKnownValue)); inst.lastKnownValue = scalex - 1; } if (this.inst.height < 0) { this.inst.height = scaley * (this.inst.height / (-1+inst.lastKnownValue2)); inst.lastKnownValue2 = scaley + 1 } else { this.inst.height = scaley * (this.inst.height / (1+inst.lastKnownValue2)); inst.lastKnownValue2 = scaley - 1; } } } this.inst.set_bbox_changed(); } behinstProto.tick = function () { var dt = this.runtime.getDt(this.inst); var inst = this.tween_list["default"]; if (inst.state !== 0) { if (inst.onStart) { this.runtime.trigger(cr.behaviors.lunarray_LiteTween.prototype.cnds.OnStart, this.inst); inst.onStart = false; } if (inst.onReverseStart) { this.runtime.trigger(cr.behaviors.lunarray_LiteTween.prototype.cnds.OnReverseStart, this.inst); inst.onReverseStart = false; } this.active = (inst.state == 1) || (inst.state == 2) || (inst.state == 4) || (inst.state == 5) || (inst.state == 6); var factor = inst.OnTick(dt); this.updateTween(inst, factor); if (inst.onEnd) { this.runtime.trigger(cr.behaviors.lunarray_LiteTween.prototype.cnds.OnEnd, this.inst); inst.onEnd = false; } if (inst.onReverseEnd) { this.runtime.trigger(cr.behaviors.lunarray_LiteTween.prototype.cnds.OnReverseEnd, this.inst); inst.onReverseEnd = false; } } }; behaviorProto.cnds = {}; var cnds = behaviorProto.cnds; cnds.IsActive = function () { return (this.tween_list["default"].state !== 0); }; cnds.IsReversing = function () { return (this.tween_list["default"].state == 2); }; cnds.CompareProgress = function (cmp, v) { var inst = this.tween_list["default"]; return cr.do_cmp((inst.progress / inst.duration), cmp, v); }; cnds.OnThreshold = function (cmp, v) { var inst = this.tween_list["default"]; this.threshold = (cr.do_cmp((inst.progress / inst.duration), cmp, v)); var ret = (this.oldthreshold != this.threshold) && (this.threshold); if (ret) { this.oldthreshold = this.threshold; } return ret; }; cnds.OnStart = function () { if (this.tween_list["default"] === undefined) return false; return this.tween_list["default"].onStart; }; cnds.OnReverseStart = function () { if (this.tween_list["default"] === undefined) return false; return this.tween_list["default"].onReverseStart; }; cnds.OnEnd = function () { if (this.tween_list["default"] === undefined) return false; return this.tween_list["default"].onEnd; }; cnds.OnReverseEnd = function () { if (this.tween_list["default"] === undefined) return false; return this.tween_list["default"].onReverseEnd; }; behaviorProto.acts = {}; var acts = behaviorProto.acts; acts.Start = function (startmode, current) { this.threshold = false; this.oldthreshold = false; this.useCurrent = (current == 1); this.startTween(startmode); }; acts.Stop = function (stopmode) { this.stopTween(stopmode); }; acts.Reverse = function (revMode) { this.threshold = false; this.oldthreshold = false; this.reverseTween(revMode); }; acts.ProgressTo = function (progress) { this.setProgressTo(progress); }; acts.SetDuration = function (x) { if (isNaN(x)) return; if (x < 0) return; if (this.tween_list["default"] === undefined) return; this.tween_list["default"].duration = x; }; acts.SetEnforce = function (x) { if (this.tween_list["default"] === undefined) return; this.tween_list["default"].enforce = (x===1); }; acts.SetInitial = function (x) { if (this.tween_list["default"] === undefined) return; var init = this.parseCurrent(this.tween_list["default"].tweened, x); this.tween_list["default"].setInitial(init); }; acts.SetTarget = function (targettype, absrel, x) { if (this.tween_list["default"] === undefined) return; if (isNaN(x)) return; var inst = this.tween_list["default"]; var parsed = x + ""; this.targetmode = absrel; var x1 = ""; var x2 = ""; if (absrel === 1) { this.target = "relative(" + parsed + ")"; switch (targettype) { case 0: x1 = (this.inst.x + x); x2 = inst.targetparam2; break; case 1: x1 = inst.targetparam1; x2 = (this.inst.y + x); break; case 2: x1 = "" + cr.to_degrees(this.inst.angle + cr.to_radians(x)); x2 = x1; break; //angle case 3: x1 = "" + (this.inst.opacity*100) + x; x2 = x1; break; //opacity case 4: x1 = (this.inst.width + x); x2 = inst.targetparam2; break; //width case 5: x1 = inst.targetparam1; x2 = (this.inst.height + x); break; //height case 6: x1 = x; x2 = x; break; //value default: break; } parsed = x1 + "," + x2; } else { switch (targettype) { case 0: x1 = x; x2 = inst.targetparam2; break; case 1: x1 = inst.targetparam1; x2 = x; break; case 2: x1 = x; x2 = x; break; //angle case 3: x1 = x; x2 = x; break; //opacity case 4: x1 = x; x2 = inst.targetparam2; break; //width case 5: x1 = inst.targetparam1; x2 = x; break; //height case 6: x1 = x; x2 = x; break; //value default: break; } parsed = x1 + "," + x2; this.target = parsed; } var init = this.parseCurrent(this.tween_list["default"].tweened, "current"); var targ = this.parseCurrent(this.tween_list["default"].tweened, parsed); inst.setInitial(init); inst.setTarget(targ); }; acts.SetTweenedProperty = function (x) { if (this.tween_list["default"] === undefined) return; this.tween_list["default"].tweened = x; }; acts.SetEasing = function (x) { if (this.tween_list["default"] === undefined) return; this.tween_list["default"].easefunc = x; }; acts.SetEasingParam = function (x, a, p, t, s) { if (this.tween_list["default"] === undefined) return; this.tween_list["default"].easingparam[x].optimized = false; this.tween_list["default"].easingparam[x].a = a; this.tween_list["default"].easingparam[x].p = p; this.tween_list["default"].easingparam[x].t = t; this.tween_list["default"].easingparam[x].s = s; }; acts.ResetEasingParam = function () { if (this.tween_list["default"] === undefined) return; this.tween_list["default"].optimized = true; }; acts.SetValue = function (x) { var inst = this.tween_list["default"]; this.value = x; if (inst.tweened === 6) inst.setInitial( this.parseCurrent(inst.tweened, "current") ); }; acts.SetParameter = function (tweened, easefunction, target, duration, enforce) { if (this.tween_list["default"] === undefined) { this.addToTweenList("default", tweened, easefunction, initial, target, duration, enforce, 0); } else { var inst = this.tween_list["default"]; inst.tweened = tweened; inst.easefunc = easefunction; inst.setInitial( this.parseCurrent(tweened, "current") ); inst.setTarget( this.parseCurrent(tweened, target) ); inst.duration = duration; inst.enforce = (enforce === 1); } }; behaviorProto.exps = {}; var exps = behaviorProto.exps; exps.State = function (ret) { var parsed = "N/A"; switch (this.tween_list["default"].state) { case 0: parsed = "paused"; break; case 1: parsed = "playing"; break; case 2: parsed = "reversing"; break; case 3: parsed = "seeking"; break; default: break; } ret.set_string(parsed); }; exps.Progress = function (ret) { var progress = this.tween_list["default"].progress/this.tween_list["default"].duration; ret.set_float(progress); }; exps.Duration = function (ret) { ret.set_float(this.tween_list["default"].duration); }; exps.Target = function (ret) { var inst = this.tween_list["default"]; var parsed = "N/A"; switch (inst.tweened) { case 0: parsed = inst.targetparam1; break; case 1: parsed = inst.targetparam2; break; case 2: parsed = inst.targetparam1; break; case 3: parsed = inst.targetparam1; break; case 4: parsed = inst.targetparam1; break; case 5: parsed = inst.targetparam2; break; case 6: parsed = inst.targetparam1; break; default: break; } ret.set_float(parsed); }; exps.Value = function (ret) { var tval = this.value; ret.set_float(tval); }; exps.Tween = function (ret, a_, b_, x_, easefunc_) { var currX = (x_>1.0?1.0:x_); var factor = easeFunc(easefunc_, currX<0.0?0.0:currX, 0.0, 1.0, 1.0, false, false); ret.set_float(a_ + factor * (b_-a_)); }; }()); (function(){ /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ Object.create = Object.create || function(o) { function F() {} F.prototype = o; return new F(); }; var cp; if(typeof exports === 'undefined'){ cp = {}; if(typeof window === 'object'){ window["cp"] = cp; } } else { cp = exports; } var assert = function(value, message) { if (!value) { throw new Error('Assertion failed: ' + message); } }; var assertSoft = function(value, message) { if(!value && console && console.warn) { console.warn("ASSERTION FAILED: " + message); if(console.trace) { console.trace(); } } }; var mymin = function(a, b) { return a < b ? a : b; }; var mymax = function(a, b) { return a > b ? a : b; }; var min, max; if (typeof window === 'object' && window.navigator.userAgent.indexOf('Firefox') > -1){ min = Math.min; max = Math.max; } else { min = mymin; max = mymax; } /* The hashpair function takes two numbers and returns a hash code for them. * Required that hashPair(a, b) === hashPair(b, a). * Chipmunk's hashPair function is defined as: * #define CP_HASH_COEF (3344921057ul) * #define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF) * But thats not suitable in javascript because multiplying by a large number will make the number * a large float. * * The result of hashPair is used as the key in objects, so it returns a string. */ var hashPair = function(a, b) { return a < b ? a + ' ' + b : b + ' ' + a; }; var deleteObjFromList = function(arr, obj) { for(var i=0; i> 1; for(var i=1; i maxx || (x == maxx && y > maxy)){ maxx = x; maxy = y; end = i; } } return [start, end]; }; var SWAP = function(arr, idx1, idx2) { var tmp = arr[idx1*2]; arr[idx1*2] = arr[idx2*2]; arr[idx2*2] = tmp; tmp = arr[idx1*2+1]; arr[idx1*2+1] = arr[idx2*2+1]; arr[idx2*2+1] = tmp; }; var QHullPartition = function(verts, offs, count, a, b, tol) { if(count === 0) return 0; var max = 0; var pivot = offs; var delta = vsub(b, a); var valueTol = tol * vlength(delta); var head = offs; for(var tail = offs+count-1; head <= tail;){ var v = new Vect(verts[head * 2], verts[head * 2 + 1]); var value = vcross(delta, vsub(v, a)); if(value > valueTol){ if(value > max){ max = value; pivot = head; } head++; } else { SWAP(verts, head, tail); tail--; } } if(pivot != offs) SWAP(verts, offs, pivot); return head - offs; }; var QHullReduce = function(tol, verts, offs, count, a, pivot, b, resultPos) { if(count < 0){ return 0; } else if(count == 0) { verts[resultPos*2] = pivot.x; verts[resultPos*2+1] = pivot.y; return 1; } else { var left_count = QHullPartition(verts, offs, count, a, pivot, tol); var left = new Vect(verts[offs*2], verts[offs*2+1]); var index = QHullReduce(tol, verts, offs + 1, left_count - 1, a, left, pivot, resultPos); var pivotPos = resultPos + index++; verts[pivotPos*2] = pivot.x; verts[pivotPos*2+1] = pivot.y; var right_count = QHullPartition(verts, offs + left_count, count - left_count, pivot, b, tol); var right = new Vect(verts[(offs+left_count)*2], verts[(offs+left_count)*2+1]); return index + QHullReduce(tol, verts, offs + left_count + 1, right_count - 1, pivot, right, b, resultPos + index); } }; cp.convexHull = function(verts, result, tolerance) { if(result){ for (var i = 0; i < verts.length; i++){ result[i] = verts[i]; } } else { result = verts; } var indexes = loopIndexes(verts); var start = indexes[0], end = indexes[1]; if(start == end){ result.length = 2; return result; } SWAP(result, 0, start); SWAP(result, 1, end == 0 ? start : end); var a = new Vect(result[0], result[1]); var b = new Vect(result[2], result[3]); var count = verts.length >> 1; var resultCount = QHullReduce(tolerance, result, 2, count - 2, a, b, a, 1) + 1; result.length = resultCount*2; assertSoft(polyValidate(result), "Internal error: cpConvexHull() and cpPolyValidate() did not agree." + "Please report this error with as much info as you can."); return result; }; var clamp = function(f, minv, maxv) { return min(max(f, minv), maxv); }; var clamp01 = function(f) { return max(0, min(f, 1)); }; var lerp = function(f1, f2, t) { return f1*(1 - t) + f2*t; }; var lerpconst = function(f1, f2, d) { return f1 + clamp(f2 - f1, -d, d); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var Vect = cp.Vect = function(x, y) { this.x = x; this.y = y; }; cp.v = function (x,y) { return new Vect(x, y) }; var vzero = cp.vzero = new Vect(0,0); var vdot = cp.v.dot = function(v1, v2) { return v1.x*v2.x + v1.y*v2.y; }; var vdot2 = function(x1, y1, x2, y2) { return x1*x2 + y1*y2; }; var vlength = cp.v.len = function(v) { return Math.sqrt(vdot(v, v)); }; var vlength2 = cp.v.len2 = function(x, y) { return Math.sqrt(x*x + y*y); }; var veql = cp.v.eql = function(v1, v2) { return (v1.x === v2.x && v1.y === v2.y); }; var vadd = cp.v.add = function(v1, v2) { return new Vect(v1.x + v2.x, v1.y + v2.y); }; Vect.prototype.add = function(v2) { this.x += v2.x; this.y += v2.y; return this; }; var vsub = cp.v.sub = function(v1, v2) { return new Vect(v1.x - v2.x, v1.y - v2.y); }; Vect.prototype.sub = function(v2) { this.x -= v2.x; this.y -= v2.y; return this; }; var vneg = cp.v.neg = function(v) { return new Vect(-v.x, -v.y); }; Vect.prototype.neg = function() { this.x = -this.x; this.y = -this.y; return this; }; var vmult = cp.v.mult = function(v, s) { return new Vect(v.x*s, v.y*s); }; Vect.prototype.mult = function(s) { this.x *= s; this.y *= s; return this; }; var vcross = cp.v.cross = function(v1, v2) { return v1.x*v2.y - v1.y*v2.x; }; var vcross2 = function(x1, y1, x2, y2) { return x1*y2 - y1*x2; }; var vperp = cp.v.perp = function(v) { return new Vect(-v.y, v.x); }; var vpvrperp = cp.v.pvrperp = function(v) { return new Vect(v.y, -v.x); }; var vproject = cp.v.project = function(v1, v2) { return vmult(v2, vdot(v1, v2)/vlengthsq(v2)); }; Vect.prototype.project = function(v2) { this.mult(vdot(this, v2) / vlengthsq(v2)); return this; }; var vrotate = cp.v.rotate = function(v1, v2) { return new Vect(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); }; Vect.prototype.rotate = function(v2) { this.x = this.x * v2.x - this.y * v2.y; this.y = this.x * v2.y + this.y * v2.x; return this; }; var vunrotate = cp.v.unrotate = function(v1, v2) { return new Vect(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); }; var vlengthsq = cp.v.lengthsq = function(v) { return vdot(v, v); }; var vlengthsq2 = cp.v.lengthsq2 = function(x, y) { return x*x + y*y; }; var vlerp = cp.v.lerp = function(v1, v2, t) { return vadd(vmult(v1, 1 - t), vmult(v2, t)); }; var vnormalize = cp.v.normalize = function(v) { return vmult(v, 1/vlength(v)); }; var vnormalize_safe = cp.v.normalize_safe = function(v) { return (v.x === 0 && v.y === 0 ? vzero : vnormalize(v)); }; var vclamp = cp.v.clamp = function(v, len) { return (vdot(v,v) > len*len) ? vmult(vnormalize(v), len) : v; }; var vlerpconst = cp.v.lerpconst = function(v1, v2, d) { return vadd(v1, vclamp(vsub(v2, v1), d)); }; var vdist = cp.v.dist = function(v1, v2) { return vlength(vsub(v1, v2)); }; var vdistsq = cp.v.distsq = function(v1, v2) { return vlengthsq(vsub(v1, v2)); }; var vnear = cp.v.near = function(v1, v2, dist) { return vdistsq(v1, v2) < dist*dist; }; var vslerp = cp.v.slerp = function(v1, v2, t) { var omega = Math.acos(vdot(v1, v2)); if(omega) { var denom = 1/Math.sin(omega); return vadd(vmult(v1, Math.sin((1 - t)*omega)*denom), vmult(v2, Math.sin(t*omega)*denom)); } else { return v1; } }; var vslerpconst = cp.v.slerpconst = function(v1, v2, a) { var angle = Math.acos(vdot(v1, v2)); return vslerp(v1, v2, min(a, angle)/angle); }; var vforangle = cp.v.forangle = function(a) { return new Vect(Math.cos(a), Math.sin(a)); }; var vtoangle = cp.v.toangle = function(v) { return Math.atan2(v.y, v.x); }; var vstr = cp.v.str = function(v) { return "(" + v.x.toFixed(3) + ", " + v.y.toFixed(3) + ")"; }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var numBB = 0; var BB = cp.BB = function(l, b, r, t) { this.l = l; this.b = b; this.r = r; this.t = t; numBB++; }; cp.bb = function(l, b, r, t) { return new BB(l, b, r, t); }; var bbNewForCircle = function(p, r) { return new BB( p.x - r, p.y - r, p.x + r, p.y + r ); }; var bbIntersects = function(a, b) { return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t); }; var bbIntersects2 = function(bb, l, b, r, t) { return (bb.l <= r && l <= bb.r && bb.b <= t && b <= bb.t); }; var bbContainsBB = function(bb, other) { return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t); }; var bbContainsVect = function(bb, v) { return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y); }; var bbContainsVect2 = function(l, b, r, t, v) { return (l <= v.x && r >= v.x && b <= v.y && t >= v.y); }; var bbMerge = function(a, b){ return new BB( min(a.l, b.l), min(a.b, b.b), max(a.r, b.r), max(a.t, b.t) ); }; var bbExpand = function(bb, v){ return new BB( min(bb.l, v.x), min(bb.b, v.y), max(bb.r, v.x), max(bb.t, v.y) ); }; var bbArea = function(bb) { return (bb.r - bb.l)*(bb.t - bb.b); }; var bbMergedArea = function(a, b) { return (max(a.r, b.r) - min(a.l, b.l))*(max(a.t, b.t) - min(a.b, b.b)); }; var bbMergedArea2 = function(bb, l, b, r, t) { return (max(bb.r, r) - min(bb.l, l))*(max(bb.t, t) - min(bb.b, b)); }; var bbIntersectsSegment = function(bb, a, b) { return (bbSegmentQuery(bb, a, b) != Infinity); }; var bbClampVect = function(bb, v) { var x = min(max(bb.l, v.x), bb.r); var y = min(max(bb.b, v.y), bb.t); return new Vect(x, y); }; var bbWrapVect = function(bb, v) { var ix = Math.abs(bb.r - bb.l); var modx = (v.x - bb.l) % ix; var x = (modx > 0) ? modx : modx + ix; var iy = Math.abs(bb.t - bb.b); var mody = (v.y - bb.b) % iy; var y = (mody > 0) ? mody : mody + iy; return new Vect(x + bb.l, y + bb.b); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* These are created using literals where needed. typedef struct cpSegmentQueryInfo { cpShape *shape; cpFloat t; cpVect n; } cpSegmentQueryInfo; */ var shapeIDCounter = 0; var CP_NO_GROUP = cp.NO_GROUP = 0; var CP_ALL_LAYERS = cp.ALL_LAYERS = ~0; cp.resetShapeIdCounter = function() { shapeIDCounter = 0; }; var Shape = cp.Shape = function(body) { this.body = body; this.bb_l = this.bb_b = this.bb_r = this.bb_t = 0; this.hashid = shapeIDCounter++; this.sensor = false; this.e = 0; this.u = 0; this.surface_v = vzero; this.collision_type = 0; this.group = 0; this.layers = CP_ALL_LAYERS; this.space = null; this.isAdded = false; this.collisionCode = this.collisionCode; }; Shape.prototype.setElasticity = function(e) { this.e = e; }; Shape.prototype.setFriction = function(u) { this.body.activate(); this.u = u; }; Shape.prototype.setLayers = function(layers) { this.body.activate(); this.layers = layers; }; Shape.prototype.setSensor = function(sensor) { this.body.activate(); this.sensor = sensor; }; Shape.prototype.setCollisionType = function(collision_type) { this.body.activate(); this.collision_type = collision_type; }; Shape.prototype.getBody = function() { return this.body; }; Shape.prototype.active = function() { return this.body && this.body.shapeList.indexOf(this) !== -1; }; Shape.prototype.setBody = function(body) { assert(!this.active(), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body."); this.body = body; }; Shape.prototype.cacheBB = function() { return this.update(this.body.p, this.body.rot); }; Shape.prototype.update = function(pos, rot) { assert(!isNaN(rot.x), 'Rotation is NaN'); assert(!isNaN(pos.x), 'Position is NaN'); this.cacheData(pos, rot); }; Shape.prototype.pointQuery = function(p) { var info = this.nearestPointQuery(p); if (info.d < 0) return info; }; Shape.prototype.getBB = function() { return new BB(this.bb_l, this.bb_b, this.bb_r, this.bb_t); }; /* Not implemented - all these getters and setters. Just edit the object directly. CP_DefineShapeStructGetter(cpBody*, body, Body); void cpShapeSetBody(cpShape *shape, cpBody *body); CP_DefineShapeStructGetter(cpBB, bb, BB); CP_DefineShapeStructProperty(cpBool, sensor, Sensor, cpTrue); CP_DefineShapeStructProperty(cpFloat, e, Elasticity, cpFalse); CP_DefineShapeStructProperty(cpFloat, u, Friction, cpTrue); CP_DefineShapeStructProperty(cpVect, surface_v, SurfaceVelocity, cpTrue); CP_DefineShapeStructProperty(cpDataPointer, data, UserData, cpFalse); CP_DefineShapeStructProperty(cpCollisionType, collision_type, CollisionType, cpTrue); CP_DefineShapeStructProperty(cpGroup, group, Group, cpTrue); CP_DefineShapeStructProperty(cpLayers, layers, Layers, cpTrue); */ var PointQueryExtendedInfo = function(shape) { this.shape = shape; this.d = Infinity; this.n = vzero; }; var NearestPointQueryInfo = cp.NearestPointQueryInfo = function(shape, p, d) { this.shape = shape; this.p = p; this.d = d; }; var SegmentQueryInfo = cp.SegmentQueryInfo = function(shape, t, n) { this.shape = shape; this.t = t; this.n = n; }; SegmentQueryInfo.prototype.hitPoint = function(start, end) { return vlerp(start, end, this.t); }; SegmentQueryInfo.prototype.hitDist = function(start, end) { return vdist(start, end) * this.t; }; var CircleShape = cp.CircleShape = function(body, radius, offset) { this.c = this.tc = offset; this.r = radius; this.type = 'circle'; Shape.call(this, body); }; CircleShape.prototype = Object.create(Shape.prototype); CircleShape.prototype.cacheData = function(p, rot) { var c = this.tc = vrotate(this.c, rot).add(p); var r = this.r; this.bb_l = c.x - r; this.bb_b = c.y - r; this.bb_r = c.x + r; this.bb_t = c.y + r; }; /*CircleShape.prototype.pointQuery = function(p) { var delta = vsub(p, this.tc); var distsq = vlengthsq(delta); var r = this.r; if(distsq < r*r){ var info = new PointQueryExtendedInfo(this); var dist = Math.sqrt(distsq); info.d = r - dist; info.n = vmult(delta, 1/dist); return info; } };*/ CircleShape.prototype.nearestPointQuery = function(p) { var deltax = p.x - this.tc.x; var deltay = p.y - this.tc.y; var d = vlength2(deltax, deltay); var r = this.r; var nearestp = new Vect(this.tc.x + deltax * r/d, this.tc.y + deltay * r/d); return new NearestPointQueryInfo(this, nearestp, d - r); }; var circleSegmentQuery = function(shape, center, r, a, b, info) { a = vsub(a, center); b = vsub(b, center); var qa = vdot(a, a) - 2*vdot(a, b) + vdot(b, b); var qb = -2*vdot(a, a) + 2*vdot(a, b); var qc = vdot(a, a) - r*r; var det = qb*qb - 4*qa*qc; if(det >= 0) { var t = (-qb - Math.sqrt(det))/(2*qa); if(0 <= t && t <= 1){ return new SegmentQueryInfo(shape, t, vnormalize(vlerp(a, b, t))); } } }; CircleShape.prototype.segmentQuery = function(a, b) { return circleSegmentQuery(this, this.tc, this.r, a, b); }; /* CircleShape.prototype.setRadius = function(radius) { this.r = radius; } CircleShape.prototype.setOffset = function(offset) { this.c = offset; }*/ var SegmentShape = cp.SegmentShape = function(body, a, b, r) { this.a = a; this.b = b; this.n = vperp(vnormalize(vsub(b, a))); this.ta = this.tb = this.tn = null; this.r = r; this.a_tangent = vzero; this.b_tangent = vzero; this.type = 'segment'; Shape.call(this, body); }; SegmentShape.prototype = Object.create(Shape.prototype); SegmentShape.prototype.cacheData = function(p, rot) { this.ta = vadd(p, vrotate(this.a, rot)); this.tb = vadd(p, vrotate(this.b, rot)); this.tn = vrotate(this.n, rot); var l,r,b,t; if(this.ta.x < this.tb.x){ l = this.ta.x; r = this.tb.x; } else { l = this.tb.x; r = this.ta.x; } if(this.ta.y < this.tb.y){ b = this.ta.y; t = this.tb.y; } else { b = this.tb.y; t = this.ta.y; } var rad = this.r; this.bb_l = l - rad; this.bb_b = b - rad; this.bb_r = r + rad; this.bb_t = t + rad; }; SegmentShape.prototype.nearestPointQuery = function(p) { var closest = closestPointOnSegment(p, this.ta, this.tb); var deltax = p.x - closest.x; var deltay = p.y - closest.y; var d = vlength2(deltax, deltay); var r = this.r; var nearestp = (d ? vadd(closest, vmult(new Vect(deltax, deltay), r/d)) : closest); return new NearestPointQueryInfo(this, nearestp, d - r); }; SegmentShape.prototype.segmentQuery = function(a, b) { var n = this.tn; var d = vdot(vsub(this.ta, a), n); var r = this.r; var flipped_n = (d > 0 ? vneg(n) : n); var n_offset = vsub(vmult(flipped_n, r), a); var seg_a = vadd(this.ta, n_offset); var seg_b = vadd(this.tb, n_offset); var delta = vsub(b, a); if(vcross(delta, seg_a)*vcross(delta, seg_b) <= 0){ var d_offset = d + (d > 0 ? -r : r); var ad = -d_offset; var bd = vdot(delta, n) - d_offset; if(ad*bd < 0){ return new SegmentQueryInfo(this, ad/(ad - bd), flipped_n); } } else if(r !== 0){ var info1 = circleSegmentQuery(this, this.ta, this.r, a, b); var info2 = circleSegmentQuery(this, this.tb, this.r, a, b); if (info1){ return info2 && info2.t < info1.t ? info2 : info1; } else { return info2; } } }; SegmentShape.prototype.setNeighbors = function(prev, next) { this.a_tangent = vsub(prev, this.a); this.b_tangent = vsub(next, this.b); }; SegmentShape.prototype.setEndpoints = function(a, b) { this.a = a; this.b = b; this.n = vperp(vnormalize(vsub(b, a))); }; /* cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius) { this.r = radius; }*/ /* CP_DeclareShapeGetter(cpSegmentShape, cpVect, A); CP_DeclareShapeGetter(cpSegmentShape, cpVect, B); CP_DeclareShapeGetter(cpSegmentShape, cpVect, Normal); CP_DeclareShapeGetter(cpSegmentShape, cpFloat, Radius); */ /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var polyValidate = cr.polyValidate = function(verts) { var len = verts.length; for(var i=0; i 0){ return false; } } return true; }; var PolyShape = cp.PolyShape = function(body, verts, offset) { this.setVerts(verts, offset); this.type = 'poly'; Shape.call(this, body); }; PolyShape.prototype = Object.create(Shape.prototype); var SplittingPlane = function(n, d) { this.n = n; this.d = d; }; SplittingPlane.prototype.compare = function(v) { return vdot(this.n, v) - this.d; }; PolyShape.prototype.setVerts = function(verts, offset) { assert(verts.length >= 4, "Polygons require some verts"); assert(typeof(verts[0]) === 'number', 'Polygon verticies should be specified in a flattened list (eg [x1,y1,x2,y2,x3,y3,...])'); assert(polyValidate(verts), "Polygon is concave or has a reversed winding. Consider using cpConvexHull()"); var len = verts.length; var numVerts = len >> 1; this.verts = new Array(len); this.tVerts = new Array(len); this.planes = new Array(numVerts); this.tPlanes = new Array(numVerts); for(var i=0; i>1] = new SplittingPlane(n, vdot2(n.x, n.y, ax, ay)); this.tPlanes[i>>1] = new SplittingPlane(new Vect(0,0), 0); } }; var BoxShape = cp.BoxShape = function(body, width, height) { var hw = width/2; var hh = height/2; return BoxShape2(body, new BB(-hw, -hh, hw, hh)); }; var BoxShape2 = cp.BoxShape2 = function(body, box) { var verts = [ box.l, box.b, box.l, box.t, box.r, box.t, box.r, box.b, ]; return new PolyShape(body, verts, vzero); }; PolyShape.prototype.transformVerts = function(p, rot) { var src = this.verts; var dst = this.tVerts; var l = Infinity, r = -Infinity; var b = Infinity, t = -Infinity; for(var i=0; i 0) outside = true; var v1x = verts[i*2]; var v1y = verts[i*2 + 1]; var closest = closestPointOnSegment2(p.x, p.y, v0x, v0y, v1x, v1y); var dist = vdist(p, closest); if(dist < minDist){ minDist = dist; closestPoint = closest; } v0x = v1x; v0y = v1y; } return new NearestPointQueryInfo(this, closestPoint, (outside ? minDist : -minDist)); }; PolyShape.prototype.segmentQuery = function(a, b) { var axes = this.tPlanes; var verts = this.tVerts; var numVerts = axes.length; var len = numVerts * 2; for(var i=0; i an) continue; var bn = vdot(b, n); var t = (axes[i].d - an)/(bn - an); if(t < 0 || 1 < t) continue; var point = vlerp(a, b, t); var dt = -vcross(n, point); var dtMin = -vcross2(n.x, n.y, verts[i*2], verts[i*2+1]); var dtMax = -vcross2(n.x, n.y, verts[(i*2+2)%len], verts[(i*2+3)%len]); if(dtMin <= dt && dt <= dtMax){ return new SegmentQueryInfo(this, t, n); } } }; PolyShape.prototype.valueOnAxis = function(n, d) { var verts = this.tVerts; var m = vdot2(n.x, n.y, verts[0], verts[1]); for(var i=2; i 0) return false; } return true; }; PolyShape.prototype.containsVertPartial = function(vx, vy, n) { var planes = this.tPlanes; for(var i=0; i 0) return false; } return true; }; PolyShape.prototype.getNumVerts = function() { return this.verts.length / 2; }; PolyShape.prototype.getVert = function(i) { return new Vect(this.verts[i * 2], this.verts[i * 2 + 1]); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var Body = cp.Body = function(m, i) { this.p = new Vect(0,0); this.vx = this.vy = 0; this.f = new Vect(0,0); this.w = 0; this.t = 0; this.v_limit = Infinity; this.w_limit = Infinity; this.v_biasx = this.v_biasy = 0; this.w_bias = 0; this.space = null; this.isAdded = false; this.shapeList = []; this.arbiterList = null; // These are both wacky linked lists. this.constraintList = null; this.nodeRoot = null; this.nodeNext = null; this.nodeIdleTime = 0; this.setMass(m); this.setMoment(i); this.rot = new Vect(0,0); this.setAngle(0); }; var createStaticBody = function() { var body = new Body(Infinity, Infinity); body.nodeIdleTime = Infinity; return body; }; if (typeof DEBUG !== 'undefined' && DEBUG) { var v_assert_nan = function(v, message){assert(v.x == v.x && v.y == v.y, message); }; var v_assert_infinite = function(v, message){assert(Math.abs(v.x) !== Infinity && Math.abs(v.y) !== Infinity, message);}; var v_assert_sane = function(v, message){v_assert_nan(v, message); v_assert_infinite(v, message);}; Body.prototype.sanityCheck = function() { assert(this.m === this.m && this.m_inv === this.m_inv, "Body's mass is invalid."); assert(this.i === this.i && this.i_inv === this.i_inv, "Body's moment is invalid."); v_assert_sane(this.p, "Body's position is invalid."); v_assert_sane(this.f, "Body's force is invalid."); assert(this.vx === this.vx && Math.abs(this.vx) !== Infinity, "Body's velocity is invalid."); assert(this.vy === this.vy && Math.abs(this.vy) !== Infinity, "Body's velocity is invalid."); assert(this.a === this.a && Math.abs(this.a) !== Infinity, "Body's angle is invalid."); assert(this.w === this.w && Math.abs(this.w) !== Infinity, "Body's angular velocity is invalid."); assert(this.t === this.t && Math.abs(this.t) !== Infinity, "Body's torque is invalid."); v_assert_sane(this.rot, "Body's rotation vector is invalid."); assert(this.v_limit === this.v_limit, "Body's velocity limit is invalid."); assert(this.w_limit === this.w_limit, "Body's angular velocity limit is invalid."); }; } else { Body.prototype.sanityCheck = function(){}; } Body.prototype.getPos = function() { return this.p; }; Body.prototype.getAngle=function(){return cp.v.toangle(this.rot)}, Body.prototype.getVel = function() { return new Vect(this.vx, this.vy); }; Body.prototype.getAngVel = function() { return this.w; }; Body.prototype.isSleeping = function() { return this.nodeRoot !== null; }; Body.prototype.isStatic = function() { return this.nodeIdleTime === Infinity; }; Body.prototype.isRogue = function() { return this.space === null; }; Body.prototype.setMass = function(mass) { assert(mass > 0, "Mass must be positive and non-zero."); this.activate(); this.m = mass; this.m_inv = 1/mass; }; Body.prototype.setMoment = function(moment) { assert(moment > 0, "Moment of Inertia must be positive and non-zero."); this.activate(); this.i = moment; this.i_inv = 1/moment; }; Body.prototype.addShape = function(shape) { this.shapeList.push(shape); }; Body.prototype.removeShape = function(shape) { deleteObjFromList(this.shapeList, shape); }; var filterConstraints = function(node, body, filter) { if(node === filter){ return node.next(body); } else if(node.a === body){ node.next_a = filterConstraints(node.next_a, body, filter); } else { node.next_b = filterConstraints(node.next_b, body, filter); } return node; }; Body.prototype.removeConstraint = function(constraint) { this.constraintList = filterConstraints(this.constraintList, this, constraint); }; Body.prototype.setPos = function(pos) { this.activate(); this.sanityCheck(); if (pos === vzero) { pos = cp.v(0,0); } this.p = pos; }; Body.prototype.setVel = function(velocity) { this.activate(); this.vx = velocity.x; this.vy = velocity.y; }; Body.prototype.setAngVel = function(w) { this.activate(); this.w = w; }; Body.prototype.setAngleInternal = function(angle) { assert(!isNaN(angle), "Internal Error: Attempting to set body's angle to NaN"); this.a = angle;//fmod(a, (cpFloat)M_PI*2.0f); this.rot.x = Math.cos(angle); this.rot.y = Math.sin(angle); }; Body.prototype.setAngle = function(angle) { this.activate(); this.sanityCheck(); this.setAngleInternal(angle); }; Body.prototype.velocity_func = function(gravity, damping, dt) { var vx = this.vx * damping + (gravity.x + this.f.x * this.m_inv) * dt; var vy = this.vy * damping + (gravity.y + this.f.y * this.m_inv) * dt; var v_limit = this.v_limit; var lensq = vx * vx + vy * vy; var scale = (lensq > v_limit*v_limit) ? v_limit / Math.sqrt(lensq) : 1; this.vx = vx * scale; this.vy = vy * scale; var w_limit = this.w_limit; this.w = clamp(this.w*damping + this.t*this.i_inv*dt, -w_limit, w_limit); this.sanityCheck(); }; Body.prototype.position_func = function(dt) { this.p.x += (this.vx + this.v_biasx) * dt; this.p.y += (this.vy + this.v_biasy) * dt; this.setAngleInternal(this.a + (this.w + this.w_bias)*dt); this.v_biasx = this.v_biasy = 0; this.w_bias = 0; this.sanityCheck(); }; Body.prototype.resetForces = function() { this.activate(); this.f = new Vect(0,0); this.t = 0; }; Body.prototype.applyForce = function(force, r) { this.activate(); this.f = vadd(this.f, force); this.t += vcross(r, force); }; Body.prototype.applyImpulse = function(j, r) { this.activate(); apply_impulse(this, j.x, j.y, r); }; Body.prototype.getVelAtPoint = function(r) { return vadd(new Vect(this.vx, this.vy), vmult(vperp(r), this.w)); }; Body.prototype.getVelAtWorldPoint = function(point) { return this.getVelAtPoint(vsub(point, this.p)); }; Body.prototype.getVelAtLocalPoint = function(point) { return this.getVelAtPoint(vrotate(point, this.rot)); }; Body.prototype.eachShape = function(func) { for(var i = 0, len = this.shapeList.length; i < len; i++) { func(this.shapeList[i]); } }; Body.prototype.eachConstraint = function(func) { var constraint = this.constraintList; while(constraint) { var next = constraint.next(this); func(constraint); constraint = next; } }; Body.prototype.eachArbiter = function(func) { var arb = this.arbiterList; while(arb){ var next = arb.next(this); arb.swappedColl = (this === arb.body_b); func.call(this, arb); arb = next; } }; Body.prototype.local2World = function(v) { return vadd(this.p, vrotate(v, this.rot)); }; Body.prototype.world2Local = function(v) { return vunrotate(vsub(v, this.p), this.rot); }; Body.prototype.kineticEnergy = function() { var vsq = this.vx*this.vx + this.vy*this.vy; var wsq = this.w * this.w; return (vsq ? vsq*this.m : 0) + (wsq ? wsq*this.i : 0); }; /* Copyright (c) 2010 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** @defgroup cpSpatialIndex cpSpatialIndex Spatial indexes are data structures that are used to accelerate collision detection and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from and they are programmed in a generic way so that you can use them for holding more than just Shapes. It works by using pointers to the objects you add and using a callback to ask your code for bounding boxes when it needs them. Several types of queries can be performed an index as well as reindexing and full collision information. All communication to the spatial indexes is performed through callback functions. Spatial indexes should be treated as opaque structs. This means you shouldn't be reading any of the fields directly. All spatial indexes define the following methods: count = 0; each(func); contains(obj, hashid); insert(obj, hashid); remove(obj, hashid); reindex(); reindexObject(obj, hashid); pointQuery(point, func); segmentQuery(vect a, vect b, t_exit, func); query(bb, func); reindexQuery(func); */ var SpatialIndex = cp.SpatialIndex = function(staticIndex) { this.staticIndex = staticIndex; if(staticIndex){ if(staticIndex.dynamicIndex){ throw new Error("This static index is already associated with a dynamic index."); } staticIndex.dynamicIndex = this; } }; SpatialIndex.prototype.collideStatic = function(staticIndex, func) { if(staticIndex.count > 0){ var query = staticIndex.query; this.each(function(obj) { query(obj, new BB(obj.bb_l, obj.bb_b, obj.bb_r, obj.bb_t), func); }); } }; /* Copyright (c) 2009 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var BBTree = cp.BBTree = function(staticIndex) { SpatialIndex.call(this, staticIndex); this.velocityFunc = null; this.leaves = {}; this.count = 0; this.root = null; this.pooledNodes = null; this.pooledPairs = null; this.stamp = 0; }; BBTree.prototype = Object.create(SpatialIndex.prototype); var numNodes = 0; var Node = function(tree, a, b) { this.obj = null; this.bb_l = min(a.bb_l, b.bb_l); this.bb_b = min(a.bb_b, b.bb_b); this.bb_r = max(a.bb_r, b.bb_r); this.bb_t = max(a.bb_t, b.bb_t); this.parent = null; this.setA(a); this.setB(b); }; BBTree.prototype.makeNode = function(a, b) { var node = this.pooledNodes; if(node){ this.pooledNodes = node.parent; node.constructor(this, a, b); return node; } else { numNodes++; return new Node(this, a, b); } }; var numLeaves = 0; var Leaf = function(tree, obj) { this.obj = obj; tree.getBB(obj, this); this.parent = null; this.stamp = 1; this.pairs = null; numLeaves++; }; BBTree.prototype.getBB = function(obj, dest) { var velocityFunc = this.velocityFunc; if(velocityFunc){ var coef = 0.1; var x = (obj.bb_r - obj.bb_l)*coef; var y = (obj.bb_t - obj.bb_b)*coef; var v = vmult(velocityFunc(obj), 0.1); dest.bb_l = obj.bb_l + min(-x, v.x); dest.bb_b = obj.bb_b + min(-y, v.y); dest.bb_r = obj.bb_r + max( x, v.x); dest.bb_t = obj.bb_t + max( y, v.y); } else { dest.bb_l = obj.bb_l; dest.bb_b = obj.bb_b; dest.bb_r = obj.bb_r; dest.bb_t = obj.bb_t; } }; BBTree.prototype.getStamp = function() { var dynamic = this.dynamicIndex; return (dynamic && dynamic.stamp ? dynamic.stamp : this.stamp); }; BBTree.prototype.incrementStamp = function() { if(this.dynamicIndex && this.dynamicIndex.stamp){ this.dynamicIndex.stamp++; } else { this.stamp++; } } var numPairs = 0; var Pair = function(leafA, nextA, leafB, nextB) { this.prevA = null; this.leafA = leafA; this.nextA = nextA; this.prevB = null; this.leafB = leafB; this.nextB = nextB; }; BBTree.prototype.makePair = function(leafA, nextA, leafB, nextB) { var pair = this.pooledPairs; if (pair) { this.pooledPairs = pair.prevA; pair.prevA = null; pair.leafA = leafA; pair.nextA = nextA; pair.prevB = null; pair.leafB = leafB; pair.nextB = nextB; return pair; } else { numPairs++; return new Pair(leafA, nextA, leafB, nextB); } }; Pair.prototype.recycle = function(tree) { this.prevA = tree.pooledPairs; tree.pooledPairs = this; }; var unlinkThread = function(prev, leaf, next) { if(next){ if(next.leafA === leaf) next.prevA = prev; else next.prevB = prev; } if(prev){ if(prev.leafA === leaf) prev.nextA = next; else prev.nextB = next; } else { leaf.pairs = next; } }; Leaf.prototype.clearPairs = function(tree) { var pair = this.pairs, next; this.pairs = null; while(pair){ if(pair.leafA === this){ next = pair.nextA; unlinkThread(pair.prevB, pair.leafB, pair.nextB); } else { next = pair.nextB; unlinkThread(pair.prevA, pair.leafA, pair.nextA); } pair.recycle(tree); pair = next; } }; var pairInsert = function(a, b, tree) { var nextA = a.pairs, nextB = b.pairs; var pair = tree.makePair(a, nextA, b, nextB); a.pairs = b.pairs = pair; if(nextA){ if(nextA.leafA === a) nextA.prevA = pair; else nextA.prevB = pair; } if(nextB){ if(nextB.leafA === b) nextB.prevA = pair; else nextB.prevB = pair; } }; Node.prototype.recycle = function(tree) { this.parent = tree.pooledNodes; tree.pooledNodes = this; }; Leaf.prototype.recycle = function(tree) { }; Node.prototype.setA = function(value) { this.A = value; value.parent = this; }; Node.prototype.setB = function(value) { this.B = value; value.parent = this; }; Leaf.prototype.isLeaf = true; Node.prototype.isLeaf = false; Node.prototype.otherChild = function(child) { return (this.A == child ? this.B : this.A); }; Node.prototype.replaceChild = function(child, value, tree) { assertSoft(child == this.A || child == this.B, "Node is not a child of parent."); if(this.A == child){ this.A.recycle(tree); this.setA(value); } else { this.B.recycle(tree); this.setB(value); } for(var node=this; node; node = node.parent){ var a = node.A; var b = node.B; node.bb_l = min(a.bb_l, b.bb_l); node.bb_b = min(a.bb_b, b.bb_b); node.bb_r = max(a.bb_r, b.bb_r); node.bb_t = max(a.bb_t, b.bb_t); } }; Node.prototype.bbArea = Leaf.prototype.bbArea = function() { return (this.bb_r - this.bb_l)*(this.bb_t - this.bb_b); }; var bbTreeMergedArea = function(a, b) { return (max(a.bb_r, b.bb_r) - min(a.bb_l, b.bb_l))*(max(a.bb_t, b.bb_t) - min(a.bb_b, b.bb_b)); }; var bbProximity = function(a, b) { return Math.abs(a.bb_l + a.bb_r - b.bb_l - b.bb_r) + Math.abs(a.bb_b + a.bb_t - b.bb_b - b.bb_t); }; var subtreeInsert = function(subtree, leaf, tree) { if(subtree == null){ return leaf; } else if(subtree.isLeaf){ return tree.makeNode(leaf, subtree); } else { var cost_a = subtree.B.bbArea() + bbTreeMergedArea(subtree.A, leaf); var cost_b = subtree.A.bbArea() + bbTreeMergedArea(subtree.B, leaf); if(cost_a === cost_b){ cost_a = bbProximity(subtree.A, leaf); cost_b = bbProximity(subtree.B, leaf); } if(cost_b < cost_a){ subtree.setB(subtreeInsert(subtree.B, leaf, tree)); } else { subtree.setA(subtreeInsert(subtree.A, leaf, tree)); } subtree.bb_l = min(subtree.bb_l, leaf.bb_l); subtree.bb_b = min(subtree.bb_b, leaf.bb_b); subtree.bb_r = max(subtree.bb_r, leaf.bb_r); subtree.bb_t = max(subtree.bb_t, leaf.bb_t); return subtree; } }; Node.prototype.intersectsBB = Leaf.prototype.intersectsBB = function(bb) { return (this.bb_l <= bb.r && bb.l <= this.bb_r && this.bb_b <= bb.t && bb.b <= this.bb_t); }; var subtreeQuery = function(subtree, bb, func) { if(subtree.intersectsBB(bb)){ if(subtree.isLeaf){ func(subtree.obj); } else { subtreeQuery(subtree.A, bb, func); subtreeQuery(subtree.B, bb, func); } } }; var nodeSegmentQuery = function(node, a, b) { var idx = 1/(b.x - a.x); var tx1 = (/*node.bb_l == a.x ? -Infinity :*/ (node.bb_l - a.x)*idx); var tx2 = (/*node.bb_r == a.x ? Infinity :*/ (node.bb_r - a.x)*idx); var txmin = min(tx1, tx2); var txmax = max(tx1, tx2); var idy = 1/(b.y - a.y); var ty1 = (/*node.bb_b == a.y ? -Infinity :*/ (node.bb_b - a.y)*idy); var ty2 = (/*node.bb_t == a.y ? Infinity :*/ (node.bb_t - a.y)*idy); var tymin = min(ty1, ty2); var tymax = max(ty1, ty2); if(tymin <= txmax && txmin <= tymax){ var min_ = max(txmin, tymin); var max_ = min(txmax, tymax); if(0.0 <= max_ && min_ <= 1.0) return max(min_, 0.0); } return Infinity; }; var subtreeSegmentQuery = function(subtree, a, b, t_exit, func) { if(subtree.isLeaf){ return func(subtree.obj); } else { var t_a = nodeSegmentQuery(subtree.A, a, b); var t_b = nodeSegmentQuery(subtree.B, a, b); if(t_a < t_b){ if(t_a < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.A, a, b, t_exit, func)); if(t_b < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.B, a, b, t_exit, func)); } else { if(t_b < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.B, a, b, t_exit, func)); if(t_a < t_exit) t_exit = min(t_exit, subtreeSegmentQuery(subtree.A, a, b, t_exit, func)); } return t_exit; } }; BBTree.prototype.subtreeRecycle = function(node) { if(node.isLeaf){ this.subtreeRecycle(node.A); this.subtreeRecycle(node.B); node.recycle(this); } }; var subtreeRemove = function(subtree, leaf, tree) { if(leaf == subtree){ return null; } else { var parent = leaf.parent; if(parent == subtree){ var other = subtree.otherChild(leaf); other.parent = subtree.parent; subtree.recycle(tree); return other; } else { parent.parent.replaceChild(parent, parent.otherChild(leaf), tree); return subtree; } } }; /* typedef struct MarkContext { bbTree *tree; Node *staticRoot; cpSpatialIndexQueryFunc func; } MarkContext; */ var bbTreeIntersectsNode = function(a, b) { return (a.bb_l <= b.bb_r && b.bb_l <= a.bb_r && a.bb_b <= b.bb_t && b.bb_b <= a.bb_t); }; Leaf.prototype.markLeafQuery = function(leaf, left, tree, func) { if(bbTreeIntersectsNode(leaf, this)){ if(left){ pairInsert(leaf, this, tree); } else { if(this.stamp < leaf.stamp) pairInsert(this, leaf, tree); if(func) func(leaf.obj, this.obj); } } }; Node.prototype.markLeafQuery = function(leaf, left, tree, func) { if(bbTreeIntersectsNode(leaf, this)){ this.A.markLeafQuery(leaf, left, tree, func); this.B.markLeafQuery(leaf, left, tree, func); } }; Leaf.prototype.markSubtree = function(tree, staticRoot, func) { if(this.stamp == tree.getStamp()){ if(staticRoot) staticRoot.markLeafQuery(this, false, tree, func); for(var node = this; node.parent; node = node.parent){ if(node == node.parent.A){ node.parent.B.markLeafQuery(this, true, tree, func); } else { node.parent.A.markLeafQuery(this, false, tree, func); } } } else { var pair = this.pairs; while(pair){ if(this === pair.leafB){ if(func) func(pair.leafA.obj, this.obj); pair = pair.nextB; } else { pair = pair.nextA; } } } }; Node.prototype.markSubtree = function(tree, staticRoot, func) { this.A.markSubtree(tree, staticRoot, func); this.B.markSubtree(tree, staticRoot, func); }; Leaf.prototype.containsObj = function(obj) { return (this.bb_l <= obj.bb_l && this.bb_r >= obj.bb_r && this.bb_b <= obj.bb_b && this.bb_t >= obj.bb_t); }; Leaf.prototype.update = function(tree) { var root = tree.root; var obj = this.obj; if(!this.containsObj(obj)){ tree.getBB(this.obj, this); root = subtreeRemove(root, this, tree); tree.root = subtreeInsert(root, this, tree); this.clearPairs(tree); this.stamp = tree.getStamp(); return true; } return false; }; Leaf.prototype.addPairs = function(tree) { var dynamicIndex = tree.dynamicIndex; if(dynamicIndex){ var dynamicRoot = dynamicIndex.root; if(dynamicRoot){ dynamicRoot.markLeafQuery(this, true, dynamicIndex, null); } } else { var staticRoot = tree.staticIndex.root; this.markSubtree(tree, staticRoot, null); } }; BBTree.prototype.insert = function(obj, hashid) { var leaf = new Leaf(this, obj); this.leaves[hashid] = leaf; this.root = subtreeInsert(this.root, leaf, this); this.count++; leaf.stamp = this.getStamp(); leaf.addPairs(this); this.incrementStamp(); }; BBTree.prototype.remove = function(obj, hashid) { var leaf = this.leaves[hashid]; delete this.leaves[hashid]; this.root = subtreeRemove(this.root, leaf, this); this.count--; leaf.clearPairs(this); leaf.recycle(this); }; BBTree.prototype.contains = function(obj, hashid) { return this.leaves[hashid] != null; }; var voidQueryFunc = function(obj1, obj2){}; BBTree.prototype.reindexQuery = function(func) { if(/*!this ||*/ !this.root) return; var hashid, leaves = this.leaves; for (hashid in leaves) { leaves[hashid].update(this); } var staticIndex = this.staticIndex; var staticRoot = staticIndex && staticIndex.root; this.root.markSubtree(this, staticRoot, func); if(staticIndex && !staticRoot) this.collideStatic(this, staticIndex, func); this.incrementStamp(); }; BBTree.prototype.reindex = function() { this.reindexQuery(voidQueryFunc); }; BBTree.prototype.reindexObject = function(obj, hashid) { var leaf = this.leaves[hashid]; if(leaf){ if(leaf.update(this)) leaf.addPairs(this); this.incrementStamp(); } }; BBTree.prototype.pointQuery = function(point, func) { this.query(new BB(point.x, point.y, point.x, point.y), func); }; BBTree.prototype.segmentQuery = function(a, b, t_exit, func) { if(this.root) subtreeSegmentQuery(this.root, a, b, t_exit, func); }; BBTree.prototype.query = function(bb, func) { if(this && this.root && this instanceof cp.BBTree) subtreeQuery(this.root, bb, func); }; BBTree.prototype.count = function() { return this.count; }; BBTree.prototype.each = function(func) { var hashid; for(hashid in this.leaves) { func(this.leaves[hashid].obj); } }; var bbTreeMergedArea2 = function(node, l, b, r, t) { return (max(node.bb_r, r) - min(node.bb_l, l))*(max(node.bb_t, t) - min(node.bb_b, b)); }; var partitionNodes = function(tree, nodes, offset, count) { if(count == 1){ return nodes[offset]; } else if(count == 2) { return tree.makeNode(nodes[offset], nodes[offset + 1]); } var node = nodes[offset]; var bb_l = node.bb_l, bb_b = node.bb_b, bb_r = node.bb_r, bb_t = node.bb_t; var end = offset + count; for(var i=offset + 1; i bb_t - bb_b); var bounds = new Array(count*2); if(splitWidth){ for(var i=offset; i= mindist*mindist) return; var dist = Math.sqrt(distsq); return new Contact( vadd(p1, vmult(delta, 0.5 + (r1 - 0.5*mindist)/(dist ? dist : Infinity))), (dist ? vmult(delta, 1/dist) : new Vect(1, 0)), dist - mindist, 0 ); }; var circle2circle = function(circ1, circ2) { var contact = circle2circleQuery(circ1.tc, circ2.tc, circ1.r, circ2.r); return contact ? [contact] : NONE; }; var circle2segment = function(circleShape, segmentShape) { var seg_a = segmentShape.ta; var seg_b = segmentShape.tb; var center = circleShape.tc; var seg_delta = vsub(seg_b, seg_a); var closest_t = clamp01(vdot(seg_delta, vsub(center, seg_a))/vlengthsq(seg_delta)); var closest = vadd(seg_a, vmult(seg_delta, closest_t)); var contact = circle2circleQuery(center, closest, circleShape.r, segmentShape.r); if(contact){ var n = contact.n; return ( (closest_t === 0 && vdot(n, segmentShape.a_tangent) < 0) || (closest_t === 1 && vdot(n, segmentShape.b_tangent) < 0) ) ? NONE : [contact]; } else { return NONE; } } var last_MSA_min = 0; var findMSA = function(poly, planes) { var min_index = 0; var min = poly.valueOnAxis(planes[0].n, planes[0].d); if(min > 0) return -1; for(var i=1; i 0) { return -1; } else if(dist > min){ min = dist; min_index = i; } } last_MSA_min = min; return min_index; }; var findVertsFallback = function(poly1, poly2, n, dist) { var arr = []; var verts1 = poly1.tVerts; for(var i=0; i>1))); } } var verts2 = poly2.tVerts; for(var i=0; i>1))); } } return (arr.length ? arr : findVertsFallback(poly1, poly2, n, dist)); }; var poly2poly = function(poly1, poly2) { var mini1 = findMSA(poly2, poly1.tPlanes); if(mini1 == -1) return NONE; var min1 = last_MSA_min; var mini2 = findMSA(poly1, poly2.tPlanes); if(mini2 == -1) return NONE; var min2 = last_MSA_min; if(min1 > min2) return findVerts(poly1, poly2, poly1.tPlanes[mini1].n, min1); else return findVerts(poly1, poly2, vneg(poly2.tPlanes[mini2].n), min2); }; var segValueOnAxis = function(seg, n, d) { var a = vdot(n, seg.ta) - seg.r; var b = vdot(n, seg.tb) - seg.r; return min(a, b) - d; }; var findPointsBehindSeg = function(arr, seg, poly, pDist, coef) { var dta = vcross(seg.tn, seg.ta); var dtb = vcross(seg.tn, seg.tb); var n = vmult(seg.tn, coef); var verts = poly.tVerts; for(var i=0; i= dt && dt >= dtb){ arr.push(new Contact(new Vect(vx, vy), n, pDist, hashPair(poly.hashid, i))); } } } }; var seg2poly = function(seg, poly) { var arr = []; var planes = poly.tPlanes; var numVerts = planes.length; var segD = vdot(seg.tn, seg.ta); var minNorm = poly.valueOnAxis(seg.tn, segD) - seg.r; var minNeg = poly.valueOnAxis(vneg(seg.tn), -segD) - seg.r; if(minNeg > 0 || minNorm > 0) return NONE; var mini = 0; var poly_min = segValueOnAxis(seg, planes[0].n, planes[0].d); if(poly_min > 0) return NONE; for(var i=0; i 0){ return NONE; } else if(dist > poly_min){ poly_min = dist; mini = i; } } var poly_n = vneg(planes[mini].n); var va = vadd(seg.ta, vmult(poly_n, seg.r)); var vb = vadd(seg.tb, vmult(poly_n, seg.r)); if(poly.containsVert(va.x, va.y)) arr.push(new Contact(va, poly_n, poly_min, hashPair(seg.hashid, 0))); if(poly.containsVert(vb.x, vb.y)) arr.push(new Contact(vb, poly_n, poly_min, hashPair(seg.hashid, 1))); if(minNorm >= poly_min || minNeg >= poly_min) { if(minNorm > minNeg) findPointsBehindSeg(arr, seg, poly, minNorm, 1); else findPointsBehindSeg(arr, seg, poly, minNeg, -1); } if(arr.length === 0){ var mini2 = mini * 2; var verts = poly.tVerts; var poly_a = new Vect(verts[mini2], verts[mini2+1]); var con; if((con = circle2circleQuery(seg.ta, poly_a, seg.r, 0, arr))) return [con]; if((con = circle2circleQuery(seg.tb, poly_a, seg.r, 0, arr))) return [con]; var len = numVerts * 2; var poly_b = new Vect(verts[(mini2+2)%len], verts[(mini2+3)%len]); if((con = circle2circleQuery(seg.ta, poly_b, seg.r, 0, arr))) return [con]; if((con = circle2circleQuery(seg.tb, poly_b, seg.r, 0, arr))) return [con]; } return arr; }; var circle2poly = function(circ, poly) { var planes = poly.tPlanes; var mini = 0; var min = vdot(planes[0].n, circ.tc) - planes[0].d - circ.r; for(var i=0; i 0){ return NONE; } else if(dist > min) { min = dist; mini = i; } } var n = planes[mini].n; var verts = poly.tVerts; var len = verts.length; var mini2 = mini<<1; var ax = verts[mini2]; var ay = verts[mini2+1]; var bx = verts[(mini2+2)%len]; var by = verts[(mini2+3)%len]; var dta = vcross2(n.x, n.y, ax, ay); var dtb = vcross2(n.x, n.y, bx, by); var dt = vcross(n, circ.tc); if(dt < dtb){ var con = circle2circleQuery(circ.tc, new Vect(bx, by), circ.r, 0, con); return con ? [con] : NONE; } else if(dt < dta) { return [new Contact( vsub(circ.tc, vmult(n, circ.r + min/2)), vneg(n), min, 0 )]; } else { var con = circle2circleQuery(circ.tc, new Vect(ax, ay), circ.r, 0, con); return con ? [con] : NONE; } }; CircleShape.prototype.collisionCode = 0; SegmentShape.prototype.collisionCode = 1; PolyShape.prototype.collisionCode = 2; CircleShape.prototype.collisionTable = [ circle2circle, circle2segment, circle2poly ]; SegmentShape.prototype.collisionTable = [ null, function(segA, segB) { return NONE; }, // seg2seg seg2poly ]; PolyShape.prototype.collisionTable = [ null, null, poly2poly ]; var collideShapes = cp.collideShapes = function(a, b) { assert(a.collisionCode <= b.collisionCode, 'Collided shapes must be sorted by type'); return a.collisionTable[b.collisionCode](a, b); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var defaultCollisionHandler = new CollisionHandler(); var Space = cp.Space = function() { this.stamp = 0; this.curr_dt = 0; this.bodies = []; this.rousedBodies = []; this.sleepingComponents = []; this.staticShapes = new BBTree(null); this.activeShapes = new BBTree(this.staticShapes); this.arbiters = []; this.contactBuffersHead = null; this.cachedArbiters = {}; this.constraints = []; this.locked = 0; this.collisionHandlers = {}; this.defaultHandler = defaultCollisionHandler; this.postStepCallbacks = []; this.delayedAddRemove = []; this.iterations = 10; this.gravity = vzero; this.damping = 1; this.idleSpeedThreshold = 0; this.sleepTimeThreshold = Infinity; this.collisionSlop = 0.1; this.collisionBias = Math.pow(1 - 0.1, 60); this.collisionPersistence = 3; this.enableContactGraph = false; this.staticBody = new Body(Infinity, Infinity); this.staticBody.nodeIdleTime = Infinity; this.collideShapes = this.makeCollideShapes(); }; Space.prototype.getCurrentTimeStep = function() { return this.curr_dt; }; Space.prototype.setIterations = function(iter) { this.iterations = iter; }; Space.prototype.isLocked = function() { return this.locked; }; var assertSpaceUnlocked = function(space) { assert(!space.locked, "This addition/removal cannot be done safely during a call to cpSpaceStep() \ or during a query. Put these calls into a post-step callback."); }; Space.prototype.addCollisionHandler = function(a, b, begin, preSolve, postSolve, separate) { assertSpaceUnlocked(this); this.removeCollisionHandler(a, b); var handler = new CollisionHandler(); handler.a = a; handler.b = b; if(begin) handler.begin = begin; if(preSolve) handler.preSolve = preSolve; if(postSolve) handler.postSolve = postSolve; if(separate) handler.separate = separate; this.collisionHandlers[hashPair(a, b)] = handler; }; Space.prototype.removeCollisionHandler = function(a, b) { assertSpaceUnlocked(this); delete this.collisionHandlers[hashPair(a, b)]; }; Space.prototype.setDefaultCollisionHandler = function(begin, preSolve, postSolve, separate) { assertSpaceUnlocked(this); var handler = new CollisionHandler(); if(begin) handler.begin = begin; if(preSolve) handler.preSolve = preSolve; if(postSolve) handler.postSolve = postSolve; if(separate) handler.separate = separate; this.defaultHandler = handler; }; Space.prototype.lookupHandler = function(a, b) { return this.collisionHandlers[hashPair(a, b)] || this.defaultHandler; }; Space.prototype.addShape = function(shape) { var body = shape.body; if(body.isStatic()) return this.addStaticShape(shape); if(this.locked) { if(!shape.isAdded) { shape.isAdded = true; this.delayedAddRemove.push(this.addShape); this.delayedAddRemove.push(shape); } return shape; } shape.isAdded = true; assert(!shape.space, "This shape is already added to a space and cannot be added to another."); assertSpaceUnlocked(this); body.activate(); body.addShape(shape); shape.update(body.p, body.rot); this.activeShapes.insert(shape, shape.hashid); shape.space = this; return shape; }; Space.prototype.addStaticShape = function(shape) { if(this.locked) { if(!shape.isAdded) { shape.isAdded = true; this.delayedAddRemove.push(this.addStaticShape); this.delayedAddRemove.push(shape); } return shape; } shape.isAdded = true; assert(!shape.space, "This shape is already added to a space and cannot be added to another."); assertSpaceUnlocked(this); var body = shape.body; body.addShape(shape); shape.update(body.p, body.rot); this.staticShapes.insert(shape, shape.hashid); shape.space = this; return shape; }; Space.prototype.addBody = function(body) { assert(!body.isStatic(), "Static bodies cannot be added to a space as they are not meant to be simulated."); if(this.locked) { if(!body.isAdded) { body.isAdded = true; this.delayedAddRemove.push(this.addBody); this.delayedAddRemove.push(body); } return body; } body.isAdded = true; assert(!body.space, "This body is already added to a space and cannot be added to another."); assertSpaceUnlocked(this); this.bodies.push(body); body.space = this; return body; }; Space.prototype.addConstraint = function(constraint) { if(this.locked) { if(!constraint.isAdded) { constraint.isAdded = true; this.delayedAddRemove.push(this.addConstraint); this.delayedAddRemove.push(constraint); } return constraint; } constraint.isAdded = true; assert(!constraint.space, "This shape is already added to a space and cannot be added to another."); assertSpaceUnlocked(this); var a = constraint.a, b = constraint.b; a.activate(); b.activate(); this.constraints.push(constraint); constraint.next_a = a.constraintList; a.constraintList = constraint; constraint.next_b = b.constraintList; b.constraintList = constraint; constraint.space = this; return constraint; }; Space.prototype.filterArbiters = function(body, filter) { for (var hash in this.cachedArbiters) { var arb = this.cachedArbiters[hash]; if( (body === arb.body_a && (filter === arb.a || filter === null)) || (body === arb.body_b && (filter === arb.b || filter === null)) ){ if(filter && arb.state !== 'cached') arb.callSeparate(this); arb.unthread(); deleteObjFromList(this.arbiters, arb); delete this.cachedArbiters[hash]; } } }; Space.prototype.removeShape = function(shape) { var body = shape.body; if(body.isStatic()){ this.removeStaticShape(shape); } else { if(this.locked) { if(shape.isAdded) { shape.isAdded = false; this.delayedAddRemove.push(this.removeShape); this.delayedAddRemove.push(shape); } return; } shape.isAdded = false; assert(this.containsShape(shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)"); assertSpaceUnlocked(this); body.activate(); body.removeShape(shape); this.filterArbiters(body, shape); this.activeShapes.remove(shape, shape.hashid); shape.space = null; } }; Space.prototype.removeStaticShape = function(shape) { if(this.locked) { if(shape.isAdded) { shape.isAdded = false; this.delayedAddRemove.push(this.removeStaticShape); this.delayedAddRemove.push(shape); } return; } shape.isAdded = false; assert(this.containsShape(shape), "Cannot remove a static or sleeping shape that was not added to the space. (Removed twice maybe?)"); assertSpaceUnlocked(this); var body = shape.body; if(body.isStatic()) body.activateStatic(shape); body.removeShape(shape); this.filterArbiters(body, shape); this.staticShapes.remove(shape, shape.hashid); shape.space = null; }; Space.prototype.removeBody = function(body) { if(this.locked) { if(body.isAdded) { body.isAdded = false; this.delayedAddRemove.push(this.removeBody); this.delayedAddRemove.push(body); } return; } body.isAdded = false; assert(this.containsBody(body), "Cannot remove a body that was not added to the space. (Removed twice maybe?)"); assertSpaceUnlocked(this); body.activate(); deleteObjFromList(this.bodies, body); body.space = null; }; Space.prototype.removeConstraint = function(constraint) { if(this.locked) { if(constraint.isAdded) { constraint.isAdded = false; this.delayedAddRemove.push(this.removeConstraint); this.delayedAddRemove.push(constraint); } return; } constraint.isAdded = false; assert(this.containsConstraint(constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)"); assertSpaceUnlocked(this); constraint.a.activate(); constraint.b.activate(); deleteObjFromList(this.constraints, constraint); constraint.a.removeConstraint(constraint); constraint.b.removeConstraint(constraint); constraint.space = null; }; Space.prototype.containsShape = function(shape) { return (shape.space === this); }; Space.prototype.containsBody = function(body) { return (body.space == this); }; Space.prototype.containsConstraint = function(constraint) { return (constraint.space == this); }; Space.prototype.uncacheArbiter = function(arb) { delete this.cachedArbiters[hashPair(arb.a.hashid, arb.b.hashid)]; deleteObjFromList(this.arbiters, arb); }; Space.prototype.eachBody = function(func, mythis) { this.lock(); { var bodies = this.bodies; for(var i=0; i keThreshold ? 0 : body.nodeIdleTime + dt); } else body.nodeIdleTime = Infinity; } } var arbiters = this.arbiters; for(var i=0, count=arbiters.length; i= 0, "Internal Error: Space lock underflow."); if(this.locked === 0 && runPostStep){ var waking = this.rousedBodies; for(var i=0; i this.collisionPersistence){ var tail = head.next; tail.stamp = stamp; tail.contacts.length = 0; this.contactBuffersHead = tail; } else { var buffer = new ContactBuffer(stamp, head); this.contactBuffersHead = head.next = buffer; } }; cpContact * cpContactBufferGetArray(cpSpace *space) { if(space.contactBuffersHead.numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){ space.pushFreshContactBuffer(); } cpContactBufferHeader *head = space.contactBuffersHead; return ((cpContactBuffer *)head)->contacts + head.numContacts; } void cpSpacePushContacts(cpSpace *space, int count) { cpAssertHard(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal Error: Contact buffer overflow!"); space.contactBuffersHead.numContacts += count; } static void cpSpacePopContacts(cpSpace *space, int count){ space.contactBuffersHead.numContacts -= count; } */ /* Use this to re-enable object pools. static void * cpSpaceArbiterSetTrans(cpShape **shapes, cpSpace *space) { if(space.pooledArbiters.num == 0){ int count = CP_BUFFER_BYTES/sizeof(cpArbiter); cpAssertHard(count, "Internal Error: Buffer size too small."); cpArbiter *buffer = (cpArbiter *)cpcalloc(1, CP_BUFFER_BYTES); cpArrayPush(space.allocatedBuffers, buffer); for(int i=0; i b.collisionCode){ var temp = a; a = b; b = temp; } var contacts = collideShapes(a, b); if(contacts.length === 0) return; // Shapes are not colliding. var arbHash = hashPair(a.hashid, b.hashid); var arb = space.cachedArbiters[arbHash]; if (!arb){ arb = space.cachedArbiters[arbHash] = new Arbiter(a, b); } arb.update(contacts, handler, a, b); if(arb.state == 'first coll' && !handler.begin(arb, space)){ arb.ignore(); // permanently ignore the collision until separation } if( (arb.state !== 'ignore') && handler.preSolve(arb, space) && !sensor ){ space.arbiters.push(arb); } else { arb.contacts = null; if(arb.state !== 'ignore') arb.state = 'normal'; } arb.stamp = space.stamp; }; Space.prototype.makeCollideShapes = function() { space_ = this; return anon_func; }; Space.prototype.arbiterSetFilter = function(arb) { var ticks = this.stamp - arb.stamp; var a = arb.body_a, b = arb.body_b; if( (a.isStatic() || a.isSleeping()) && (b.isStatic() || b.isSleeping()) ){ return true; } if(ticks >= 1 && arb.state != 'cached'){ arb.callSeparate(this); arb.state = 'cached'; } if(ticks >= this.collisionPersistence){ arb.contacts = null; return false; } return true; }; var updateFunc = function(shape) { var body = shape.body; shape.update(body.p, body.rot); }; Space.prototype.step = function(dt) { if(dt === 0) return; assert(vzero.x === 0 && vzero.y === 0, "vzero is invalid"); this.stamp++; var prev_dt = this.curr_dt; this.curr_dt = dt; var i; var j; var hash; var bodies = this.bodies; var constraints = this.constraints; var arbiters = this.arbiters; for(i=0; i 0, "You created a 0 length pin joint. A pivot joint will be much more stable."); this.r1 = this.r2 = null; this.n = null; this.nMass = 0; this.jnAcc = this.jnMax = 0; this.bias = 0; }; PinJoint.prototype = Object.create(Constraint.prototype); PinJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; this.r1 = vrotate(this.anchr1, a.rot); this.r2 = vrotate(this.anchr2, b.rot); var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); var dist = vlength(delta); this.n = vmult(delta, 1/(dist ? dist : Infinity)); this.nMass = 1/k_scalar(a, b, this.r1, this.r2, this.n); var maxBias = this.maxBias; this.bias = clamp(-bias_coef(this.errorBias, dt)*(dist - this.dist)/dt, -maxBias, maxBias); this.jnMax = this.maxForce * dt; }; PinJoint.prototype.applyCachedImpulse = function(dt_coef) { var j = vmult(this.n, this.jnAcc*dt_coef); apply_impulses(this.a, this.b, this.r1, this.r2, j.x, j.y); }; PinJoint.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var n = this.n; var vrn = normal_relative_velocity(a, b, this.r1, this.r2, n); var jn = (this.bias - vrn)*this.nMass; var jnOld = this.jnAcc; this.jnAcc = clamp(jnOld + jn, -this.jnMax, this.jnMax); jn = this.jnAcc - jnOld; apply_impulses(a, b, this.r1, this.r2, n.x*jn, n.y*jn); }; PinJoint.prototype.getImpulse = function() { return Math.abs(this.jnAcc); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var SlideJoint = cp.SlideJoint = function(a, b, anchr1, anchr2, min, max) { Constraint.call(this, a, b); this.anchr1 = anchr1; this.anchr2 = anchr2; this.min = min; this.max = max; this.r1 = this.r2 = this.n = null; this.nMass = 0; this.jnAcc = this.jnMax = 0; this.bias = 0; }; SlideJoint.prototype = Object.create(Constraint.prototype); SlideJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; this.r1 = vrotate(this.anchr1, a.rot); this.r2 = vrotate(this.anchr2, b.rot); var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); var dist = vlength(delta); var pdist = 0; if(dist > this.max) { pdist = dist - this.max; this.n = vnormalize_safe(delta); } else if(dist < this.min) { pdist = this.min - dist; this.n = vneg(vnormalize_safe(delta)); } else { this.n = vzero; this.jnAcc = 0; } this.nMass = 1/k_scalar(a, b, this.r1, this.r2, this.n); var maxBias = this.maxBias; this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); this.jnMax = this.maxForce * dt; }; SlideJoint.prototype.applyCachedImpulse = function(dt_coef) { var jn = this.jnAcc * dt_coef; apply_impulses(this.a, this.b, this.r1, this.r2, this.n.x * jn, this.n.y * jn); }; SlideJoint.prototype.applyImpulse = function() { if(this.n.x === 0 && this.n.y === 0) return; // early exit var a = this.a; var b = this.b; var n = this.n; var r1 = this.r1; var r2 = this.r2; var vr = relative_velocity(a, b, r1, r2); var vrn = vdot(vr, n); var jn = (this.bias - vrn)*this.nMass; var jnOld = this.jnAcc; this.jnAcc = clamp(jnOld + jn, -this.jnMax, 0); jn = this.jnAcc - jnOld; apply_impulses(a, b, this.r1, this.r2, n.x * jn, n.y * jn); }; SlideJoint.prototype.getImpulse = function() { return Math.abs(this.jnAcc); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var PivotJoint = cp.PivotJoint = function(a, b, anchr1, anchr2) { Constraint.call(this, a, b); if(typeof anchr2 === 'undefined') { var pivot = anchr1; anchr1 = (a ? a.world2Local(pivot) : pivot); anchr2 = (b ? b.world2Local(pivot) : pivot); } this.anchr1 = anchr1; this.anchr2 = anchr2; this.r1 = this.r2 = vzero; this.k1 = new Vect(0,0); this.k2 = new Vect(0,0); this.jAcc = vzero; this.jMaxLen = 0; this.bias = vzero; }; PivotJoint.prototype = Object.create(Constraint.prototype); PivotJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; this.r1 = vrotate(this.anchr1, a.rot); this.r2 = vrotate(this.anchr2, b.rot); k_tensor(a, b, this.r1, this.r2, this.k1, this.k2); this.jMaxLen = this.maxForce * dt; var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); this.bias = vclamp(vmult(delta, -bias_coef(this.errorBias, dt)/dt), this.maxBias); }; PivotJoint.prototype.applyCachedImpulse = function(dt_coef) { apply_impulses(this.a, this.b, this.r1, this.r2, this.jAcc.x * dt_coef, this.jAcc.y * dt_coef); }; PivotJoint.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var r1 = this.r1; var r2 = this.r2; var vr = relative_velocity(a, b, r1, r2); var j = mult_k(vsub(this.bias, vr), this.k1, this.k2); var jOld = this.jAcc; this.jAcc = vclamp(vadd(this.jAcc, j), this.jMaxLen); apply_impulses(a, b, this.r1, this.r2, this.jAcc.x - jOld.x, this.jAcc.y - jOld.y); }; PivotJoint.prototype.getImpulse = function() { return vlength(this.jAcc); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var GrooveJoint = cp.GrooveJoint = function(a, b, groove_a, groove_b, anchr2) { Constraint.call(this, a, b); this.grv_a = groove_a; this.grv_b = groove_b; this.grv_n = vperp(vnormalize(vsub(groove_b, groove_a))); this.anchr2 = anchr2; this.grv_tn = null; this.clamp = 0; this.r1 = this.r2 = null; this.k1 = new Vect(0,0); this.k2 = new Vect(0,0); this.jAcc = vzero; this.jMaxLen = 0; this.bias = null; }; GrooveJoint.prototype = Object.create(Constraint.prototype); GrooveJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; var ta = a.local2World(this.grv_a); var tb = a.local2World(this.grv_b); var n = vrotate(this.grv_n, a.rot); var d = vdot(ta, n); this.grv_tn = n; this.r2 = vrotate(this.anchr2, b.rot); var td = vcross(vadd(b.p, this.r2), n); if(td <= vcross(ta, n)){ this.clamp = 1; this.r1 = vsub(ta, a.p); } else if(td >= vcross(tb, n)){ this.clamp = -1; this.r1 = vsub(tb, a.p); } else { this.clamp = 0; this.r1 = vsub(vadd(vmult(vperp(n), -td), vmult(n, d)), a.p); } k_tensor(a, b, this.r1, this.r2, this.k1, this.k2); this.jMaxLen = this.maxForce * dt; var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); this.bias = vclamp(vmult(delta, -bias_coef(this.errorBias, dt)/dt), this.maxBias); }; GrooveJoint.prototype.applyCachedImpulse = function(dt_coef) { apply_impulses(this.a, this.b, this.r1, this.r2, this.jAcc.x * dt_coef, this.jAcc.y * dt_coef); }; GrooveJoint.prototype.grooveConstrain = function(j){ var n = this.grv_tn; var jClamp = (this.clamp*vcross(j, n) > 0) ? j : vproject(j, n); return vclamp(jClamp, this.jMaxLen); }; GrooveJoint.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var r1 = this.r1; var r2 = this.r2; var vr = relative_velocity(a, b, r1, r2); var j = mult_k(vsub(this.bias, vr), this.k1, this.k2); var jOld = this.jAcc; this.jAcc = this.grooveConstrain(vadd(jOld, j)); apply_impulses(a, b, this.r1, this.r2, this.jAcc.x - jOld.x, this.jAcc.y - jOld.y); }; GrooveJoint.prototype.getImpulse = function() { return vlength(this.jAcc); }; GrooveJoint.prototype.setGrooveA = function(value) { this.grv_a = value; this.grv_n = vperp(vnormalize(vsub(this.grv_b, value))); this.activateBodies(); }; GrooveJoint.prototype.setGrooveB = function(value) { this.grv_b = value; this.grv_n = vperp(vnormalize(vsub(value, this.grv_a))); this.activateBodies(); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var defaultSpringForce = function(spring, dist){ return (spring.restLength - dist)*spring.stiffness; }; var DampedSpring = cp.DampedSpring = function(a, b, anchr1, anchr2, restLength, stiffness, damping) { Constraint.call(this, a, b); this.anchr1 = anchr1; this.anchr2 = anchr2; this.restLength = restLength; this.stiffness = stiffness; this.damping = damping; this.springForceFunc = defaultSpringForce; this.target_vrn = this.v_coef = 0; this.r1 = this.r2 = null; this.nMass = 0; this.n = null; }; DampedSpring.prototype = Object.create(Constraint.prototype); DampedSpring.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; this.r1 = vrotate(this.anchr1, a.rot); this.r2 = vrotate(this.anchr2, b.rot); var delta = vsub(vadd(b.p, this.r2), vadd(a.p, this.r1)); var dist = vlength(delta); this.n = vmult(delta, 1/(dist ? dist : Infinity)); var k = k_scalar(a, b, this.r1, this.r2, this.n); assertSoft(k !== 0, "Unsolvable this."); this.nMass = 1/k; this.target_vrn = 0; this.v_coef = 1 - Math.exp(-this.damping*dt*k); var f_spring = this.springForceFunc(this, dist); apply_impulses(a, b, this.r1, this.r2, this.n.x * f_spring * dt, this.n.y * f_spring * dt); }; DampedSpring.prototype.applyCachedImpulse = function(dt_coef){}; DampedSpring.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var n = this.n; var r1 = this.r1; var r2 = this.r2; var vrn = normal_relative_velocity(a, b, r1, r2, n); var v_damp = (this.target_vrn - vrn)*this.v_coef; this.target_vrn = vrn + v_damp; v_damp *= this.nMass; apply_impulses(a, b, this.r1, this.r2, this.n.x * v_damp, this.n.y * v_damp); }; DampedSpring.prototype.getImpulse = function() { return 0; }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var defaultSpringTorque = function(spring, relativeAngle){ return (relativeAngle - spring.restAngle)*spring.stiffness; } var DampedRotarySpring = cp.DampedRotarySpring = function(a, b, restAngle, stiffness, damping) { Constraint.call(this, a, b); this.restAngle = restAngle; this.stiffness = stiffness; this.damping = damping; this.springTorqueFunc = defaultSpringTorque; this.target_wrn = 0; this.w_coef = 0; this.iSum = 0; }; DampedRotarySpring.prototype = Object.create(Constraint.prototype); DampedRotarySpring.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; var moment = a.i_inv + b.i_inv; assertSoft(moment !== 0, "Unsolvable spring."); this.iSum = 1/moment; this.w_coef = 1 - Math.exp(-this.damping*dt*moment); this.target_wrn = 0; var j_spring = this.springTorqueFunc(this, a.a - b.a)*dt; a.w -= j_spring*a.i_inv; b.w += j_spring*b.i_inv; }; DampedRotarySpring.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var wrn = a.w - b.w;//normal_relative_velocity(a, b, r1, r2, n) - this.target_vrn; var w_damp = (this.target_wrn - wrn)*this.w_coef; this.target_wrn = wrn + w_damp; var j_damp = w_damp*this.iSum; a.w += j_damp*a.i_inv; b.w -= j_damp*b.i_inv; }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var RotaryLimitJoint = cp.RotaryLimitJoint = function(a, b, min, max) { Constraint.call(this, a, b); this.min = min; this.max = max; this.jAcc = 0; this.iSum = this.bias = this.jMax = 0; }; RotaryLimitJoint.prototype = Object.create(Constraint.prototype); RotaryLimitJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; var dist = b.a - a.a; var pdist = 0; if(dist > this.max) { pdist = this.max - dist; } else if(dist < this.min) { pdist = this.min - dist; } this.iSum = 1/(1/a.i + 1/b.i); var maxBias = this.maxBias; this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); this.jMax = this.maxForce * dt; if(!this.bias) this.jAcc = 0; }; RotaryLimitJoint.prototype.applyCachedImpulse = function(dt_coef) { var a = this.a; var b = this.b; var j = this.jAcc*dt_coef; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; RotaryLimitJoint.prototype.applyImpulse = function() { if(!this.bias) return; // early exit var a = this.a; var b = this.b; var wr = b.w - a.w; var j = -(this.bias + wr)*this.iSum; var jOld = this.jAcc; if(this.bias < 0){ this.jAcc = clamp(jOld + j, 0, this.jMax); } else { this.jAcc = clamp(jOld + j, -this.jMax, 0); } j = this.jAcc - jOld; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; RotaryLimitJoint.prototype.getImpulse = function() { return Math.abs(joint.jAcc); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var RatchetJoint = cp.RatchetJoint = function(a, b, phase, ratchet) { Constraint.call(this, a, b); this.angle = 0; this.phase = phase; this.ratchet = ratchet; this.angle = (b ? b.a : 0) - (a ? a.a : 0); this.iSum = this.bias = this.jAcc = this.jMax = 0; }; RatchetJoint.prototype = Object.create(Constraint.prototype); RatchetJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; var angle = this.angle; var phase = this.phase; var ratchet = this.ratchet; var delta = b.a - a.a; var diff = angle - delta; var pdist = 0; if(diff*ratchet > 0){ pdist = diff; } else { this.angle = Math.floor((delta - phase)/ratchet)*ratchet + phase; } this.iSum = 1/(a.i_inv + b.i_inv); var maxBias = this.maxBias; this.bias = clamp(-bias_coef(this.errorBias, dt)*pdist/dt, -maxBias, maxBias); this.jMax = this.maxForce * dt; if(!this.bias) this.jAcc = 0; }; RatchetJoint.prototype.applyCachedImpulse = function(dt_coef) { var a = this.a; var b = this.b; var j = this.jAcc*dt_coef; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; RatchetJoint.prototype.applyImpulse = function() { if(!this.bias) return; // early exit var a = this.a; var b = this.b; var wr = b.w - a.w; var ratchet = this.ratchet; var j = -(this.bias + wr)*this.iSum; var jOld = this.jAcc; this.jAcc = clamp((jOld + j)*ratchet, 0, this.jMax*Math.abs(ratchet))/ratchet; j = this.jAcc - jOld; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; RatchetJoint.prototype.getImpulse = function(joint) { return Math.abs(joint.jAcc); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var GearJoint = cp.GearJoint = function(a, b, phase, ratio) { Constraint.call(this, a, b); this.phase = phase; this.ratio = ratio; this.ratio_inv = 1/ratio; this.jAcc = 0; this.iSum = this.bias = this.jMax = 0; }; GearJoint.prototype = Object.create(Constraint.prototype); GearJoint.prototype.preStep = function(dt) { var a = this.a; var b = this.b; if(a.isRogue() && b.isRogue()) return; this.iSum = 1/(a.i_inv*this.ratio_inv + this.ratio*b.i_inv); var maxBias = this.maxBias; this.bias = clamp(-bias_coef(this.errorBias, dt)*(b.a*this.ratio - a.a - this.phase)/dt, -maxBias, maxBias); this.jMax = this.maxForce * dt; }; GearJoint.prototype.applyCachedImpulse = function(dt_coef) { var a = this.a; var b = this.b; var j = this.jAcc*dt_coef; a.w -= j*a.i_inv*this.ratio_inv; b.w += j*b.i_inv; }; GearJoint.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var wr = b.w*this.ratio - a.w; var j = (this.bias - wr)*this.iSum; var jOld = this.jAcc; this.jAcc = clamp(jOld + j, -this.jMax, this.jMax); j = this.jAcc - jOld; a.w -= j*a.i_inv*this.ratio_inv; b.w += j*b.i_inv; }; GearJoint.prototype.getImpulse = function() { return Math.abs(this.jAcc); }; GearJoint.prototype.setRatio = function(value) { this.ratio = value; this.ratio_inv = 1/value; this.activateBodies(); }; /* Copyright (c) 2007 Scott Lembcke * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ var SimpleMotor = cp.SimpleMotor = function(a, b, rate) { Constraint.call(this, a, b); this.rate = rate; this.jAcc = 0; this.iSum = this.jMax = 0; }; SimpleMotor.prototype = Object.create(Constraint.prototype); SimpleMotor.prototype.preStep = function(dt) { this.iSum = 1/(this.a.i_inv + this.b.i_inv); this.jMax = this.maxForce * dt; }; SimpleMotor.prototype.applyCachedImpulse = function(dt_coef) { var a = this.a; var b = this.b; var j = this.jAcc*dt_coef; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; SimpleMotor.prototype.applyImpulse = function() { var a = this.a; var b = this.b; var wr = b.w - a.w + this.rate; var j = -wr*this.iSum; var jOld = this.jAcc; this.jAcc = clamp(jOld + j, -this.jMax, this.jMax); j = this.jAcc - jOld; a.w -= j*a.i_inv; b.w += j*b.i_inv; }; SimpleMotor.prototype.getImpulse = function() { return Math.abs(this.jAcc); }; })(); function isReversed(tri) { var A = tri[0]; var B = tri[1]; var C = tri[2]; var v1 = cp.v.sub(B, A); var v2 = cp.v.sub(C, B); return cp.v.cross(v1, v2) <0; } function pointsInTriangle (poly, tri) { var pnt, result; for(var i=0; i= 0) && (v >= 0) && (u + v < 1); } function triangulate (source_poly) { var poly = source_poly.slice(0); var triangles = []; var i = 0, tri; while (poly.length >= 3) { tri = [poly[i], poly[(i+1)%poly.length], poly[(i+2)%poly.length] ]; if (!isReversed(tri) && !pointsInTriangle(poly, tri)) { poly.splice((i+1)%poly.length, 1); //remove point triangles.push(tri); } else i++; i = i%poly.length; } return triangles; } function add_tri_2_poly (poly, tri) { var i, found_cnt=0; var not_found; var after_not_found; for(i=0; i<3; i++) { if( poly.indexOf(tri[i]) == -1) { not_found = tri[i]; after_not_found = tri[(i+1)%3]; } else found_cnt++; } if ( found_cnt != 2) return false; i = poly.indexOf(after_not_found); var len = poly.length; if ( isReversed([poly[(i-2+len)%len], poly[(i-1+len)%len], not_found]) || isReversed([not_found, poly[i], poly[(i+1)%len]]) ) { return false; } poly.splice(i, 0, not_found); return true; } function triangles2convex (source_triangles) { var triangles = source_triangles;//.slice(0); var convex_polys = []; while (triangles.length>0) { var convex = triangles[0]; triangles.shift(); for(var i=0; i this.behavior.lastUpdateTick && timescale > 0) { this.space.eachBody(eachBodyPrestep, this); this.behavior.rogues.forEach(this.updateCOM); this.space.eachBody(this.updateCOM); this.behavior.rogues.forEach(this.changeShapeCheck); this.space.bodies.forEach(this.changeShapeCheck); //dosen't cover sleeping bodies if (!this.space.stepping_mode) this.space.step(this.space.fixed_step * timescale); else this.space.step(this.runtime.getDt(this.inst)); var delayedAddRemove = this.space.delayedAddRemove; for(var i=0, len=delayedAddRemove.length/2; i=0 && index=0 && index=0 && index=0 && index=0 && index