Merge pull request #13 from BKcore/leapmotion

Merge dev and leapmotion changes to master.
This commit is contained in:
Thibaut Despoulain
2014-03-29 14:32:20 -07:00
140 changed files with 8675 additions and 332 deletions
+4
View File
@@ -0,0 +1,4 @@
AddType application/x-web-app-manifest+json .webapp
ExpiresByType application/x-web-app-manifest+json "access"
AddType text/cache-manifest .appcache
ExpiresByType text/cache-manifest "access"
+12 -2
View File
@@ -9,12 +9,22 @@ Source code of [HexGL](http://hexgl.bkcore.com), the futuristic HTML5 racing gam
## License
Unless specified in the file, HexGL's code and resources are licensed under the *Creative Commons Attribution-NonCommercial 3.0 Unported License*.
Unless specified in the file, HexGL's code and resources are licensed under the *Creative Commons Attribution-NonCommercial 3.0 Unported License*.
To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
If you feel like you deserve another license for a special case, [drop me a note](http://bkcore.com/contact.html), and we'll talk about it.
## Installation
cd ~/
git clone git://github.com/BKcore/HexGL.git
cd HexGL
python -m SimpleHTTPServer
chromium index.html
To use full size textures, swap the two textures/ and textures.full/ directories.
## Regarding the code
As of now the code is pretty much raw and undocumented. I'll be commenting it someday, but that won't be until I've finished the next content update and code refactoring sorry!
@@ -24,4 +34,4 @@ There will also be some refactoring, when I get some time :P
After much thinking, I've decided to release HexGL's source code under CC 3.0 BY-NC until further notice. This is not a definite choice, and the game may end up under the MIT license someday.
Feel free to post issues, patch or anything to make the game better.
Feel free to post issues, patch or anything to make the game better.
+141
View File
@@ -0,0 +1,141 @@
###
Loads an image and gives access to pixel data.
@class bkcore.ImageData
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class ImageData
###
Creates a new ImageData object
@param path string The path of the image
@param callback function A callback function to be called
once th eimage is loaded
###
constructor: (path, callback)->
@image = new Image
@pixels = null
@canvas = null
@loaded = false
@image.onload = ()=>
@canvas = document.createElement('canvas')
@canvas.width = @image.width
@canvas.height = @image.height
context = @canvas.getContext('2d')
context.drawImage(@image, 0, 0)
@pixels = context.getImageData(0,0, @canvas.width, @canvas.height)
@loaded = true
context = null
@canvas = null
@image = null
callback?.call(@)
@image.crossOrigin = "anonymous"
@image.src = path
###
Gets pixel RGBA data at given index
@param x int In pixels
@param y int In pixels
@return Object{r,g,b,a}
###
getPixel: (x, y)->
if !@pixels? or x < 0 or y < 0 or x >= @pixels.width or y >= @pixels.height
return {r: 0, g: 0, b:0, a:0}
i = (y*@pixels.width + x) * 4
return {
r: @pixels.data[i]
g: @pixels.data[i+1]
b: @pixels.data[i+2]
a: @pixels.data[i+3]
}
###
Gets pixel RGBA data at given float index using bilinear interpolation
@param x float In subpixels
@param y float In subpixels
@return Object{r,g,b,a}
###
getPixelBilinear: (fx, fy)->
x = Math.floor(fx)
y = Math.floor(fy)
rx = fx - x - .5
ry = fy - y - .5
ax = Math.abs(rx)
ay = Math.abs(ry)
dx = if rx < 0 then -1 else 1
dy = if ry < 0 then -1 else 1
c = @getPixel(x, y)
cx = @getPixel(x+dx, y)
cy = @getPixel(x, y+dy)
cxy = @getPixel(x+dx, y+dy)
cf1 = [
(1-ax) * c.r + ax * cx.r
(1-ax) * c.g + ax * cx.g
(1-ax) * c.b + ax * cx.b
(1-ax) * c.a + ax * cx.a
]
cf2 = [
(1-ax) * cy.r + ax * cxy.r
(1-ax) * cy.g + ax * cxy.g
(1-ax) * cy.b + ax * cxy.b
(1-ax) * cy.a + ax * cxy.a
]
return {
r: (1-ay) * cf1[0] + ay * cf2[0]
g: (1-ay) * cf1[1] + ay * cf2[1]
b: (1-ay) * cf1[2] + ay * cf2[2]
a: (1-ay) * cf1[3] + ay * cf2[3]
}
###
Gets pixel data at given index
as 3-bytes integer (for floating-point textures erzats, from RGB values)
@param x int In pixels
@param y int In pixels
@return int (R + G*255 + B*255*255)
###
getPixelF: (x, y)->
c = @getPixel(x, y)
return c.r + c.g * 255 + c.b * 255 * 255
###
Gets pixel data at given float index using bilinear interpolationas
as 3-bytes integer (for floating-point textures erzats, from RGB values)
@param x float In subpixels
@param y float In subpixels
@return Object{r,g,b,a}
###
getPixelFBilinear: (fx, fy)->
c = @getPixelBilinear(fx, fy)
return c.r + c.g * 255 + c.b * 255 * 255
###
Exports
@package bkcore
###
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.ImageData = ImageData
+156
View File
@@ -0,0 +1,156 @@
// Generated by CoffeeScript 1.4.0
/*
Loads an image and gives access to pixel data.
@class bkcore.ImageData
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var ImageData, exports;
ImageData = (function() {
/*
Creates a new ImageData object
@param path string The path of the image
@param callback function A callback function to be called
once th eimage is loaded
*/
function ImageData(path, callback) {
var _this = this;
this.image = new Image;
this.pixels = null;
this.canvas = null;
this.loaded = false;
this.image.onload = function() {
var context;
_this.canvas = document.createElement('canvas');
_this.canvas.width = _this.image.width;
_this.canvas.height = _this.image.height;
context = _this.canvas.getContext('2d');
context.drawImage(_this.image, 0, 0);
_this.pixels = context.getImageData(0, 0, _this.canvas.width, _this.canvas.height);
_this.loaded = true;
context = null;
_this.canvas = null;
_this.image = null;
return callback != null ? callback.call(_this) : void 0;
};
this.image.crossOrigin = "anonymous";
this.image.src = path;
}
/*
Gets pixel RGBA data at given index
@param x int In pixels
@param y int In pixels
@return Object{r,g,b,a}
*/
ImageData.prototype.getPixel = function(x, y) {
var i;
if (!(this.pixels != null) || x < 0 || y < 0 || x >= this.pixels.width || y >= this.pixels.height) {
return {
r: 0,
g: 0,
b: 0,
a: 0
};
}
i = (y * this.pixels.width + x) * 4;
return {
r: this.pixels.data[i],
g: this.pixels.data[i + 1],
b: this.pixels.data[i + 2],
a: this.pixels.data[i + 3]
};
};
/*
Gets pixel RGBA data at given float index using bilinear interpolation
@param x float In subpixels
@param y float In subpixels
@return Object{r,g,b,a}
*/
ImageData.prototype.getPixelBilinear = function(fx, fy) {
var ax, ay, c, cf1, cf2, cx, cxy, cy, dx, dy, rx, ry, x, y;
x = Math.floor(fx);
y = Math.floor(fy);
rx = fx - x - .5;
ry = fy - y - .5;
ax = Math.abs(rx);
ay = Math.abs(ry);
dx = rx < 0 ? -1 : 1;
dy = ry < 0 ? -1 : 1;
c = this.getPixel(x, y);
cx = this.getPixel(x + dx, y);
cy = this.getPixel(x, y + dy);
cxy = this.getPixel(x + dx, y + dy);
cf1 = [(1 - ax) * c.r + ax * cx.r, (1 - ax) * c.g + ax * cx.g, (1 - ax) * c.b + ax * cx.b, (1 - ax) * c.a + ax * cx.a];
cf2 = [(1 - ax) * cy.r + ax * cxy.r, (1 - ax) * cy.g + ax * cxy.g, (1 - ax) * cy.b + ax * cxy.b, (1 - ax) * cy.a + ax * cxy.a];
return {
r: (1 - ay) * cf1[0] + ay * cf2[0],
g: (1 - ay) * cf1[1] + ay * cf2[1],
b: (1 - ay) * cf1[2] + ay * cf2[2],
a: (1 - ay) * cf1[3] + ay * cf2[3]
};
};
/*
Gets pixel data at given index
as 3-bytes integer (for floating-point textures erzats, from RGB values)
@param x int In pixels
@param y int In pixels
@return int (R + G*255 + B*255*255)
*/
ImageData.prototype.getPixelF = function(x, y) {
var c;
c = this.getPixel(x, y);
return c.r + c.g * 255 + c.b * 255 * 255;
};
/*
Gets pixel data at given float index using bilinear interpolationas
as 3-bytes integer (for floating-point textures erzats, from RGB values)
@param x float In subpixels
@param y float In subpixels
@return Object{r,g,b,a}
*/
ImageData.prototype.getPixelFBilinear = function(fx, fy) {
var c;
c = this.getPixelBilinear(fx, fy);
return c.r + c.g * 255 + c.b * 255 * 255;
};
return ImageData;
})();
/*
Exports
@package bkcore
*/
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
exports.bkcore.ImageData = ImageData;
}).call(this);
+118
View File
@@ -0,0 +1,118 @@
###
new Date().getTime() wrapper to use as timer.
@class bkcore.Timer
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class Timer
###
Creates a new timer, inactive by default.
Call Timer.start() to activate.
###
constructor: ()->
@time =
start: 0
current: 0
previous: 0
elapsed: 0
delta: 0
@active = false
###
Starts/restarts the timer.
###
start: ()->
now = (new Date).getTime()
@time.start = now
@time.current = now
@time.previous = now
@time.elapsed = 0
@time.delta = 0
@active = true
###
Pauses(true)/Unpauses(false) the timer.
@param bool Do pause
###
pause: (doPause)->
@active = not doPause
###
Update method to be called inside a RAF loop
###
update: ()->
if not @active then return
now = (new Date).getTime()
@time.current = now
@time.elapsed = @time.current - @time.start
@time.delta = now - @time.previous
@time.previous = now
###
Returns a formatted version of the current elapsed time using msToTime().
###
getElapsedTime: ()->
return @constructor.msToTime(@time.elapsed)
###
Formats a millisecond integer into a h/m/s/ms object
@param x int In milliseconds
@return Object{h,m,s,ms}
###
@msToTime: (t)->
ms = t%1000
s = Math.floor((t/1000)%60)
m = Math.floor((t/60000)%60)
h = Math.floor(t/3600000)
return {h:h, m:m, s:s, ms,ms}
###
Formats a millisecond integer into a h/m/s/ms object with prefix zeros
@param x int In milliseconds
@return Object<string>{h,m,s,ms}
###
@msToTimeString: (t)->
time = @msToTime(t)
time.h = @zfill(time.h, 2)
time.m = @zfill(time.m, 2)
time.s = @zfill(time.s, 2)
time.ms = @zfill(time.ms, 4)
return time
###
Convert given integer to string and fill with heading zeros
@param num int Number to convert/fill
@param size int Desired string size
###
@zfill: (num, size)->
len = size - num.toString().length
return if len > 0 then new Array(len+1).join('0') + num else num.toString()
###
Exports
@package bkcore
###
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.Timer = Timer
+159
View File
@@ -0,0 +1,159 @@
// Generated by CoffeeScript 1.4.0
/*
new Date().getTime() wrapper to use as timer.
@class bkcore.Timer
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var Timer, exports;
Timer = (function() {
/*
Creates a new timer, inactive by default.
Call Timer.start() to activate.
*/
function Timer() {
this.time = {
start: 0,
current: 0,
previous: 0,
elapsed: 0,
delta: 0
};
this.active = false;
}
/*
Starts/restarts the timer.
*/
Timer.prototype.start = function() {
var now;
now = (new Date).getTime();
this.time.start = now;
this.time.current = now;
this.time.previous = now;
this.time.elapsed = 0;
this.time.delta = 0;
return this.active = true;
};
/*
Pauses(true)/Unpauses(false) the timer.
@param bool Do pause
*/
Timer.prototype.pause = function(doPause) {
return this.active = !doPause;
};
/*
Update method to be called inside a RAF loop
*/
Timer.prototype.update = function() {
var now;
if (!this.active) {
return;
}
now = (new Date).getTime();
this.time.current = now;
this.time.elapsed = this.time.current - this.time.start;
this.time.delta = now - this.time.previous;
return this.time.previous = now;
};
/*
Returns a formatted version of the current elapsed time using msToTime().
*/
Timer.prototype.getElapsedTime = function() {
return this.constructor.msToTime(this.time.elapsed);
};
/*
Formats a millisecond integer into a h/m/s/ms object
@param x int In milliseconds
@return Object{h,m,s,ms}
*/
Timer.msToTime = function(t) {
var h, m, ms, s;
ms = t % 1000;
s = Math.floor((t / 1000) % 60);
m = Math.floor((t / 60000) % 60);
h = Math.floor(t / 3600000);
return {
h: h,
m: m,
s: s,
ms: ms,
ms: ms
};
};
/*
Formats a millisecond integer into a h/m/s/ms object with prefix zeros
@param x int In milliseconds
@return Object<string>{h,m,s,ms}
*/
Timer.msToTimeString = function(t) {
var time;
time = this.msToTime(t);
time.h = this.zfill(time.h, 2);
time.m = this.zfill(time.m, 2);
time.s = this.zfill(time.s, 2);
time.ms = this.zfill(time.ms, 4);
return time;
};
/*
Convert given integer to string and fill with heading zeros
@param num int Number to convert/fill
@param size int Desired string size
*/
Timer.zfill = function(num, size) {
var len;
len = size - num.toString().length;
if (len > 0) {
return new Array(len + 1).join('0') + num;
} else {
return num.toString();
}
};
return Timer;
})();
/*
Exports
@package bkcore
*/
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
exports.bkcore.Timer = Timer;
}).call(this);
+185
View File
@@ -0,0 +1,185 @@
###
Various useful methods
@class bkcore.Utils
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class Utils
###
Creates a bkcore.threejs.Shaders["normalV"|"normal"] material
with given parameters
###
@createNormalMaterial = (opts)->
opts ?= {}
opts.ambient ?= 0x444444
opts.normalScale ?= 1.0
opts.reflectivity ?= 0.9
opts.shininess ?= 42
opts.metal ?= false
shadername = if opts.perPixel then "normalV" else "normal"
shader = bkcore.threejs.Shaders[shadername]
uniforms = THREE.UniformsUtils.clone(shader.uniforms)
uniforms["enableDiffuse"].value = true
uniforms["enableSpecular"].value = true
uniforms["enableReflection"].value = !!opts.cube
uniforms["tNormal"].texture = opts.normal
uniforms["tDiffuse"].texture = opts.diffuse
uniforms["tSpecular"].texture = opts.specular
uniforms["uAmbientColor"].value.setHex(opts.ambient)
uniforms["uAmbientColor"].value.convertGammaToLinear()
uniforms["uNormalScale"].value = opts.normalScale
if opts.cube?
uniforms["tCube"].texture = opts.cube
uniforms["uReflectivity"].value = opts.reflectivity
parameters = {
fragmentShader: shader.fragmentShader
vertexShader: shader.vertexShader
uniforms: uniforms
lights: true
fog: false
}
material = new THREE.ShaderMaterial(parameters)
material.perPixel = true
material.metal = opts.metal
return material
###
Projects an object origin vector to screen using given camera
@param THREE.Object3D object The object which origin you want to project
@param THREE.Camera camera The camera of the projection
@return THEE.Vector3 Projected verctor
###
@projectOnScreen: (object, camera)->
mat = new THREE.Matrix4()
mat.multiply(camera.matrixWorldInverse, object.matrixWorld)
mat.multiply(camera.projectionMatrix , mat)
c = mat.n44
lPos = new THREE.Vector3(mat.n14/c, mat.n24/c, mat.n34/c)
return lPos.multiplyScalar(0.5).addScalar(0.5)
###
Get an url parameter
@param String name Parameter slug
@return Mixed
###
@URLParameters: null
@getURLParameter: (name)->
if !@URLParameters?
@URLParameters = {}
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, (m, key, val)=>
@URLParameters[key] = val
)
return @URLParameters[name]
###
Get top offset of an element
@param obj HTMLElement
###
@getOffsetTop: (obj)->
curtop = obj.offsetTop
if obj.offsetParent
while obj = obj.offsetParent
curtop += obj.offsetTop
return curtop
###
Scrolls page to given element id
@param string id The ID of the element
###
@scrollTo: (id)->
window.scroll(0, @getOffsetTop(document.getElementById(id)))
###
Add or remove a class from an element
@param string id [description]
@param string cssclass [description]
@param bool active [description]
###
@updateClass: (id, cssclass, active)->
e = document.getElementById(id)
return unless e?
if active
e.classList.add(cssclass)
else
e.classList.remove(cssclass)
###
Performs an XMLHttpRequest
@param string url [description]
@param bool postData true = POST, false = GET
@param {Function} callback [description]
@param {Object} data [description]
###
@request: (url, postData, callback, data)->
XMLHttpFactories = [
()-> return new XMLHttpRequest()
()-> return new ActiveXObject("Msxml2.XMLHTTP")
()-> return new ActiveXObject("Msxml3.XMLHTTP")
()-> return new ActiveXObject("Microsoft.XMLHTTP")
]
createXMLHTTPObject = () ->
xmlhttp = false
for i in [0..XMLHttpFactories.length]
try
xmlhttp = XMLHttpFactories[i]()
catch e
continue
break
return xmlhttp
req = createXMLHTTPObject()
return unless req?
method = if postData? then "POST" else "GET"
qdata = "o=bk"
if data?
for i, val of data
qdata += "&"+i+"="+val
url += "?"+qdata if postData?
req.open(method,url,true)
if postData?
req.setRequestHeader('Content-type','application/x-www-form-urlencoded')
req.onreadystatechange = () ->
return unless req.readyState is 4
return unless req.status is 200 or req.status is 304
callback?(req)
req.send(qdata)
return req
###
Exports
@package bkcore
###
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.Utils = Utils
+240
View File
@@ -0,0 +1,240 @@
// Generated by CoffeeScript 1.4.0
/*
Various useful methods
@class bkcore.Utils
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var Utils, exports;
Utils = (function() {
/*
Creates a bkcore.threejs.Shaders["normalV"|"normal"] material
with given parameters
*/
function Utils() {}
Utils.createNormalMaterial = function(opts) {
var material, parameters, shader, shadername, uniforms, _ref, _ref1, _ref2, _ref3, _ref4;
if (opts == null) {
opts = {};
}
if ((_ref = opts.ambient) == null) {
opts.ambient = 0x444444;
}
if ((_ref1 = opts.normalScale) == null) {
opts.normalScale = 1.0;
}
if ((_ref2 = opts.reflectivity) == null) {
opts.reflectivity = 0.9;
}
if ((_ref3 = opts.shininess) == null) {
opts.shininess = 42;
}
if ((_ref4 = opts.metal) == null) {
opts.metal = false;
}
shadername = opts.perPixel ? "normalV" : "normal";
shader = bkcore.threejs.Shaders[shadername];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
uniforms["enableDiffuse"].value = true;
uniforms["enableSpecular"].value = true;
uniforms["enableReflection"].value = !!opts.cube;
uniforms["tNormal"].texture = opts.normal;
uniforms["tDiffuse"].texture = opts.diffuse;
uniforms["tSpecular"].texture = opts.specular;
uniforms["uAmbientColor"].value.setHex(opts.ambient);
uniforms["uAmbientColor"].value.convertGammaToLinear();
uniforms["uNormalScale"].value = opts.normalScale;
if (opts.cube != null) {
uniforms["tCube"].texture = opts.cube;
uniforms["uReflectivity"].value = opts.reflectivity;
}
parameters = {
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: uniforms,
lights: true,
fog: false
};
material = new THREE.ShaderMaterial(parameters);
material.perPixel = true;
material.metal = opts.metal;
return material;
};
/*
Projects an object origin vector to screen using given camera
@param THREE.Object3D object The object which origin you want to project
@param THREE.Camera camera The camera of the projection
@return THEE.Vector3 Projected verctor
*/
Utils.projectOnScreen = function(object, camera) {
var c, lPos, mat;
mat = new THREE.Matrix4();
mat.multiply(camera.matrixWorldInverse, object.matrixWorld);
mat.multiply(camera.projectionMatrix, mat);
c = mat.n44;
lPos = new THREE.Vector3(mat.n14 / c, mat.n24 / c, mat.n34 / c);
return lPos.multiplyScalar(0.5).addScalar(0.5);
};
/*
Get an url parameter
@param String name Parameter slug
@return Mixed
*/
Utils.URLParameters = null;
Utils.getURLParameter = function(name) {
var _this = this;
if (!(this.URLParameters != null)) {
this.URLParameters = {};
window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, val) {
return _this.URLParameters[key] = val;
});
}
return this.URLParameters[name];
};
/*
Get top offset of an element
@param obj HTMLElement
*/
Utils.getOffsetTop = function(obj) {
var curtop;
curtop = obj.offsetTop;
if (obj.offsetParent) {
while (obj = obj.offsetParent) {
curtop += obj.offsetTop;
}
}
return curtop;
};
/*
Scrolls page to given element id
@param string id The ID of the element
*/
Utils.scrollTo = function(id) {
return window.scroll(0, this.getOffsetTop(document.getElementById(id)));
};
/*
Add or remove a class from an element
@param string id [description]
@param string cssclass [description]
@param bool active [description]
*/
Utils.updateClass = function(id, cssclass, active) {
var e;
e = document.getElementById(id);
if (e == null) {
return;
}
if (active) {
return e.classList.add(cssclass);
} else {
return e.classList.remove(cssclass);
}
};
/*
Performs an XMLHttpRequest
@param string url [description]
@param bool postData true = POST, false = GET
@param {Function} callback [description]
@param {Object} data [description]
*/
Utils.request = function(url, postData, callback, data) {
var XMLHttpFactories, createXMLHTTPObject, i, method, qdata, req, val;
XMLHttpFactories = [
function() {
return new XMLHttpRequest();
}, function() {
return new ActiveXObject("Msxml2.XMLHTTP");
}, function() {
return new ActiveXObject("Msxml3.XMLHTTP");
}, function() {
return new ActiveXObject("Microsoft.XMLHTTP");
}
];
createXMLHTTPObject = function() {
var i, xmlhttp, _i, _ref;
xmlhttp = false;
for (i = _i = 0, _ref = XMLHttpFactories.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
try {
xmlhttp = XMLHttpFactories[i]();
} catch (e) {
continue;
}
break;
}
return xmlhttp;
};
req = createXMLHTTPObject();
if (req == null) {
return;
}
method = postData != null ? "POST" : "GET";
qdata = "o=bk";
if (data != null) {
for (i in data) {
val = data[i];
qdata += "&" + i + "=" + val;
if (postData != null) {
url += "?" + qdata;
}
}
}
req.open(method, url, true);
if (postData != null) {
req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
req.onreadystatechange = function() {
if (req.readyState !== 4) {
return;
}
if (!(req.status === 200 || req.status === 304)) {
return;
}
return typeof callback === "function" ? callback(req) : void 0;
};
req.send(qdata);
return req;
};
return Utils;
})();
/*
Exports
@package bkcore
*/
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
exports.bkcore.Utils = Utils;
}).call(this);
@@ -0,0 +1,72 @@
###
OrientationController (Orientation + buttons) for touch devices
@class bkcore.OrientationController
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class OrientationController
@isCompatible: ->
return ('DeviceOrientationEvent' of window)
###
Creates a new OrientationController
@param dom DOMElement The element that will listen to touch events
@param registerTouch bool Enable touch detection
@param touchCallback function Callback for touches
###
constructor: (@dom, @registerTouch=true, @touchCallback=null) ->
@active = true
@alpha = 0.0
@beta = 0.0
@gamma = 0.0
@dalpha = null
@dbeta = null
@dgamma = null
@touches = null
window.addEventListener('deviceorientation', ((e)=> @orientationChange(e)), false)
if @registerTouch
@dom.addEventListener('touchstart', ((e)=> @touchStart(e)), false)
@dom.addEventListener('touchend', ((e)=> @touchEnd(e)), false)
###
@private
###
orientationChange: (event) ->
return if not @active
if(@dalpha == null)
console.log "calbrate", event.beta
@dalpha = event.alpha
@dbeta = event.beta
@dgamma = event.gamma
@alpha = event.alpha - @dalpha
@beta = event.beta - @dbeta
@gamma = event.gamma - @dgamma
false
###
@private
###
touchStart: (event) ->
return if not @active
for touch in event.changedTouches
@touchCallback?(on, touch, event)
@touches = event.touches
false
###
@private
###
touchEnd: (event) ->
return if not @active
for touch in event.changedTouches
@touchCallback?(on, touch, event)
@touches = event.touches
false
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.controllers ||= {}
exports.bkcore.controllers.OrientationController = OrientationController
@@ -0,0 +1,130 @@
// Generated by CoffeeScript 1.4.0
/*
OrientationController (Orientation + buttons) for touch devices
@class bkcore.OrientationController
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var OrientationController, exports, _base;
OrientationController = (function() {
OrientationController.isCompatible = function() {
return 'DeviceOrientationEvent' in window;
};
/*
Creates a new OrientationController
@param dom DOMElement The element that will listen to touch events
@param registerTouch bool Enable touch detection
@param touchCallback function Callback for touches
*/
function OrientationController(dom, registerTouch, touchCallback) {
var _this = this;
this.dom = dom;
this.registerTouch = registerTouch != null ? registerTouch : true;
this.touchCallback = touchCallback != null ? touchCallback : null;
this.active = true;
this.alpha = 0.0;
this.beta = 0.0;
this.gamma = 0.0;
this.dalpha = null;
this.dbeta = null;
this.dgamma = null;
this.touches = null;
window.addEventListener('deviceorientation', (function(e) {
return _this.orientationChange(e);
}), false);
if (this.registerTouch) {
this.dom.addEventListener('touchstart', (function(e) {
return _this.touchStart(e);
}), false);
this.dom.addEventListener('touchend', (function(e) {
return _this.touchEnd(e);
}), false);
}
}
/*
@private
*/
OrientationController.prototype.orientationChange = function(event) {
if (!this.active) {
return;
}
if (this.dalpha === null) {
console.log("calbrate", event.beta);
this.dalpha = event.alpha;
this.dbeta = event.beta;
this.dgamma = event.gamma;
}
this.alpha = event.alpha - this.dalpha;
this.beta = event.beta - this.dbeta;
this.gamma = event.gamma - this.dgamma;
return false;
};
/*
@private
*/
OrientationController.prototype.touchStart = function(event) {
var touch, _i, _len, _ref;
if (!this.active) {
return;
}
_ref = event.changedTouches;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
touch = _ref[_i];
if (typeof this.touchCallback === "function") {
this.touchCallback(true, touch, event);
}
}
this.touches = event.touches;
return false;
};
/*
@private
*/
OrientationController.prototype.touchEnd = function(event) {
var touch, _i, _len, _ref;
if (!this.active) {
return;
}
_ref = event.changedTouches;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
touch = _ref[_i];
if (typeof this.touchCallback === "function") {
this.touchCallback(true, touch, event);
}
}
this.touches = event.touches;
return false;
};
return OrientationController;
})();
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
(_base = exports.bkcore).controllers || (_base.controllers = {});
exports.bkcore.controllers.OrientationController = OrientationController;
}).call(this);
@@ -0,0 +1,109 @@
###
TouchController (stick + buttons) for touch devices
Based on the touch demo by Seb Lee-Delisle <http://seb.ly/>
@class bkcore.controllers.TouchController
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class TouchController
@isCompatible: ->
return ('ontouchstart' of document.documentElement);
###
Creates a new TouchController
@param dom DOMElement The element that will listen to touch events
@param stickMargin int The left margin in px for stick detection
@param buttonCallback function Callback for non-stick touches
###
constructor: (@dom, @stickMargin=200, @buttonCallback=null) ->
@active = true
@touches = null
@stickID = -1
@stickPos = new Vec2(0, 0)
@stickStartPos = new Vec2(0, 0)
@stickVector = new Vec2(0, 0)
@dom.addEventListener('touchstart', ((e)=> @touchStart(e)), false)
@dom.addEventListener('touchmove', ((e)=> @touchMove(e)), false)
@dom.addEventListener('touchend', ((e)=> @touchEnd(e)), false)
###
@private
###
touchStart: (event) ->
return if not @active
for touch in event.changedTouches
if @stickID < 0 and touch.clientX < @stickMargin
@stickID = touch.identifier
@stickStartPos.set(touch.clientX, touch.clientY)
@stickPos.copy(@stickStartPos)
@stickVector.set(0, 0)
continue
else
@buttonCallback?(on, touch, event)
@touches = event.touches
false
###
@private
###
touchMove: (event) ->
event.preventDefault()
return if not @active
for touch in event.changedTouches
if @stickID is touch.identifier
@stickPos.set(touch.clientX, touch.clientY)
@stickVector.copy(@stickPos).substract(@stickStartPos)
break
@touches = event.touches
false
###
@private
###
touchEnd: (event) ->
return if not @active
@touches = event.touches
for touch in event.changedTouches
if @stickID is touch.identifier
@stickID = -1
@stickVector.set(0, 0)
break
else
@buttonCallback?(off, touch, event)
false
###
Internal class used for vector2
@class Vec2
@private
###
class Vec2
constructor: (@x = 0, @y = 0) ->
substract: (vec) ->
@x -= vec.x
@y -= vec.y
@
copy: (vec) ->
@x = vec.x
@y = vec.y
@
set: (x, y) ->
@x = x
@y = y
@
###
Exports
@package bkcore
###
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.controllers ||= {}
exports.bkcore.controllers.TouchController = TouchController
@@ -0,0 +1,186 @@
// Generated by CoffeeScript 1.4.0
/*
TouchController (stick + buttons) for touch devices
Based on the touch demo by Seb Lee-Delisle <http://seb.ly/>
@class bkcore.controllers.TouchController
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var TouchController, Vec2, exports, _base;
TouchController = (function() {
TouchController.isCompatible = function() {
return 'ontouchstart' in document.documentElement;
};
/*
Creates a new TouchController
@param dom DOMElement The element that will listen to touch events
@param stickMargin int The left margin in px for stick detection
@param buttonCallback function Callback for non-stick touches
*/
function TouchController(dom, stickMargin, buttonCallback) {
var _this = this;
this.dom = dom;
this.stickMargin = stickMargin != null ? stickMargin : 200;
this.buttonCallback = buttonCallback != null ? buttonCallback : null;
this.active = true;
this.touches = null;
this.stickID = -1;
this.stickPos = new Vec2(0, 0);
this.stickStartPos = new Vec2(0, 0);
this.stickVector = new Vec2(0, 0);
this.dom.addEventListener('touchstart', (function(e) {
return _this.touchStart(e);
}), false);
this.dom.addEventListener('touchmove', (function(e) {
return _this.touchMove(e);
}), false);
this.dom.addEventListener('touchend', (function(e) {
return _this.touchEnd(e);
}), false);
}
/*
@private
*/
TouchController.prototype.touchStart = function(event) {
var touch, _i, _len, _ref;
if (!this.active) {
return;
}
_ref = event.changedTouches;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
touch = _ref[_i];
if (this.stickID < 0 && touch.clientX < this.stickMargin) {
this.stickID = touch.identifier;
this.stickStartPos.set(touch.clientX, touch.clientY);
this.stickPos.copy(this.stickStartPos);
this.stickVector.set(0, 0);
continue;
} else {
if (typeof this.buttonCallback === "function") {
this.buttonCallback(true, touch, event);
}
}
}
this.touches = event.touches;
return false;
};
/*
@private
*/
TouchController.prototype.touchMove = function(event) {
var touch, _i, _len, _ref;
event.preventDefault();
if (!this.active) {
return;
}
_ref = event.changedTouches;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
touch = _ref[_i];
if (this.stickID === touch.identifier) {
this.stickPos.set(touch.clientX, touch.clientY);
this.stickVector.copy(this.stickPos).substract(this.stickStartPos);
break;
}
}
this.touches = event.touches;
return false;
};
/*
@private
*/
TouchController.prototype.touchEnd = function(event) {
var touch, _i, _len, _ref;
if (!this.active) {
return;
}
this.touches = event.touches;
_ref = event.changedTouches;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
touch = _ref[_i];
if (this.stickID === touch.identifier) {
this.stickID = -1;
this.stickVector.set(0, 0);
break;
} else {
if (typeof this.buttonCallback === "function") {
this.buttonCallback(false, touch, event);
}
}
}
return false;
};
return TouchController;
})();
/*
Internal class used for vector2
@class Vec2
@private
*/
Vec2 = (function() {
function Vec2(x, y) {
this.x = x != null ? x : 0;
this.y = y != null ? y : 0;
}
Vec2.prototype.substract = function(vec) {
this.x -= vec.x;
this.y -= vec.y;
return this;
};
Vec2.prototype.copy = function(vec) {
this.x = vec.x;
this.y = vec.y;
return this;
};
Vec2.prototype.set = function(x, y) {
this.x = x;
this.y = y;
return this;
};
return Vec2;
})();
/*
Exports
@package bkcore
*/
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
(_base = exports.bkcore).controllers || (_base.controllers = {});
exports.bkcore.controllers.TouchController = TouchController;
}).call(this);
+10
View File
@@ -0,0 +1,10 @@
<html>
<head>
<title>CoffeeScript Tests</title>
</head>
<body>
<script src="Timer.js"></script>
<script src="ImageData.js"></script>
<script src="../libs/Three.r53.js"></script>
</body>
</html>
+199
View File
@@ -0,0 +1,199 @@
###
Particle system wrapper/helper
@class bkcore.threejs.Particles
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class Particles
###
Creates a new particle system using given parameters
@param {Object{max, spawnRate, spawn, velocity, randomness,
force, spawnRadius, life, friction, color, color2, tint,
texture, size, blending, depthTest, transparent, opacity}} opts
###
constructor: (opts)->
@black = new THREE.Color(0x000000)
@white = new THREE.Color(0xffffff)
@material = new THREE.ParticleBasicMaterial(
color: opts.tint ? 0xffffff
map: opts.texture ? null
size: opts.size ? 4
blending: opts.blending ? THREE.AdditiveBlending
depthTest: opts.depthTest ? false
transparent: opts.transparent ? true
vertexColors: true
opacity: opts.opacity ? 1.0
sizeAttenuation: true
)
@max = opts.max ? 1000
@spawnRate = opts.spawnRate ? 0
@spawn = opts.spawn ? new THREE.Vector3()
@velocity = opts.velocity ? new THREE.Vector3()
@randomness = opts.randomness ? new THREE.Vector3()
@force = opts.force ? new THREE.Vector3()
@spawnRadius = opts.spawnRadius ? new THREE.Vector3()
@life = opts.life ? 60
@ageing = 1 / @life
@friction = opts.friction ? 1.0
@color = new THREE.Color(opts.color ? 0xffffff)
@color2 = if opts.color2? then new THREE.Color(opts.color2) else null
@position = opts.position ? new THREE.Vector3()
@rotation = opts.rotation ? new THREE.Vector3()
@sort = opts.sort ? false
@pool = []
@buffer = []
@geometry = null
@system = null
@build()
###
Emits given number of particles
@param int count
###
emit: (count)->
emitable = Math.min(count, @pool.length)
for i in [0..emitable]
p = @pool.pop()
p.available = false
p.position.copy(@spawn).addSelf(
@randomVector().multiplySelf(@spawnRadius)
)
p.velocity.copy(@velocity).addSelf(
@randomVector().multiplySelf(@randomness)
)
p.force.copy(@force)
p.basecolor.copy(@color)
if @color2?
p.basecolor.lerpSelf(@color2, Math.random())
p.life = 1.0
###
@private
###
build: ()->
@geometry = new THREE.Geometry()
@geometry.dynamic = true
@pool = []
@buffer = []
for i in [0..@max]
p = new bkcore.threejs.Particle()
@pool.push(p)
@buffer.push(p)
@geometry.vertices.push(p.position)
@geometry.colors.push(p.color)
@system = new THREE.ParticleSystem(@geometry, @material)
@system.position = @position
@system.rotation = @rotation
@system.sort = @sort
###
@private
###
randomVector: ()->
return new THREE.Vector3(
Math.random()*2-1,
Math.random()*2-1,
Math.random()*2-1
)
###
Updates particles (should be call in a RAF loop)
@param float dt time delta ~1.0
###
update: (dt)->
df = new THREE.Vector3()
dv = new THREE.Vector3()
for i in [0..@buffer.length]
p = @buffer[i]
continue if p.available
p.life -= @ageing
if p.life <= 0
p.reset()
@pool.push(p)
continue
l = if p.life > 0.5 then 1.0 else p.life + 0.5
p.color.setRGB(
l * p.basecolor.r,
l * p.basecolor.g,
l * p.basecolor.b
)
if @friction != 1.0
p.velocity.multiplyScalar(@friction)
df.copy(p.force).multiplyScalar(dt)
p.velocity.addSelf(df)
dv.copy(p.velocity).multiplyScalar(dt)
p.position.addSelf(dv)
if @spawnRate > 0
@emit(@spawnRate)
@geometry.verticesNeedUpdate = true
@geometry.colorsNeedUpdate = true
###
Particle sub class
@class bkcore.threejs.Particle
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
###
class Particle
constructor: ()->
@position = new THREE.Vector3(-10000,-10000,-10000)
@velocity = new THREE.Vector3()
@force = new THREE.Vector3()
@color = new THREE.Color(0x000000)
@basecolor = new THREE.Color(0x000000)
@life = 0.0
@available = true
reset: ()->
@position.set(0,-100000,0)
@velocity.set(0,0,0)
@force.set(0,0,0)
@color.setRGB(0,0,0)
@basecolor.setRGB(0,0,0)
@life = 0.0
@available = true
###
Exports
@package bkcore.threejs
###
exports = exports ? @
exports.bkcore ||= {}
exports.bkcore.threejs ||= {}
exports.bkcore.threejs.Particle = Particle
exports.bkcore.threejs.Particles = Particles
+210
View File
@@ -0,0 +1,210 @@
// Generated by CoffeeScript 1.4.0
/*
Particle system wrapper/helper
@class bkcore.threejs.Particles
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
(function() {
var Particle, Particles, exports, _base;
Particles = (function() {
/*
Creates a new particle system using given parameters
@param {Object{max, spawnRate, spawn, velocity, randomness,
force, spawnRadius, life, friction, color, color2, tint,
texture, size, blending, depthTest, transparent, opacity}} opts
*/
function Particles(opts) {
var _ref, _ref1, _ref10, _ref11, _ref12, _ref13, _ref14, _ref15, _ref16, _ref17, _ref18, _ref19, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9;
this.black = new THREE.Color(0x000000);
this.white = new THREE.Color(0xffffff);
this.material = new THREE.ParticleBasicMaterial({
color: (_ref = opts.tint) != null ? _ref : 0xffffff,
map: (_ref1 = opts.texture) != null ? _ref1 : null,
size: (_ref2 = opts.size) != null ? _ref2 : 4,
blending: (_ref3 = opts.blending) != null ? _ref3 : THREE.AdditiveBlending,
depthTest: (_ref4 = opts.depthTest) != null ? _ref4 : false,
transparent: (_ref5 = opts.transparent) != null ? _ref5 : true,
vertexColors: true,
opacity: (_ref6 = opts.opacity) != null ? _ref6 : 1.0,
sizeAttenuation: true
});
this.max = (_ref7 = opts.max) != null ? _ref7 : 1000;
this.spawnRate = (_ref8 = opts.spawnRate) != null ? _ref8 : 0;
this.spawn = (_ref9 = opts.spawn) != null ? _ref9 : new THREE.Vector3();
this.velocity = (_ref10 = opts.velocity) != null ? _ref10 : new THREE.Vector3();
this.randomness = (_ref11 = opts.randomness) != null ? _ref11 : new THREE.Vector3();
this.force = (_ref12 = opts.force) != null ? _ref12 : new THREE.Vector3();
this.spawnRadius = (_ref13 = opts.spawnRadius) != null ? _ref13 : new THREE.Vector3();
this.life = (_ref14 = opts.life) != null ? _ref14 : 60;
this.ageing = 1 / this.life;
this.friction = (_ref15 = opts.friction) != null ? _ref15 : 1.0;
this.color = new THREE.Color((_ref16 = opts.color) != null ? _ref16 : 0xffffff);
this.color2 = opts.color2 != null ? new THREE.Color(opts.color2) : null;
this.position = (_ref17 = opts.position) != null ? _ref17 : new THREE.Vector3();
this.rotation = (_ref18 = opts.rotation) != null ? _ref18 : new THREE.Vector3();
this.sort = (_ref19 = opts.sort) != null ? _ref19 : false;
this.pool = [];
this.buffer = [];
this.geometry = null;
this.system = null;
this.build();
}
/*
Emits given number of particles
@param int count
*/
Particles.prototype.emit = function(count) {
var emitable, i, p, _i, _results;
emitable = Math.min(count, this.pool.length);
_results = [];
for (i = _i = 0; 0 <= emitable ? _i <= emitable : _i >= emitable; i = 0 <= emitable ? ++_i : --_i) {
p = this.pool.pop();
p.available = false;
p.position.copy(this.spawn).addSelf(this.randomVector().multiplySelf(this.spawnRadius));
p.velocity.copy(this.velocity).addSelf(this.randomVector().multiplySelf(this.randomness));
p.force.copy(this.force);
p.basecolor.copy(this.color);
if (this.color2 != null) {
p.basecolor.lerpSelf(this.color2, Math.random());
}
_results.push(p.life = 1.0);
}
return _results;
};
/*
@private
*/
Particles.prototype.build = function() {
var i, p, _i, _ref;
this.geometry = new THREE.Geometry();
this.geometry.dynamic = true;
this.pool = [];
this.buffer = [];
for (i = _i = 0, _ref = this.max; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
p = new bkcore.threejs.Particle();
this.pool.push(p);
this.buffer.push(p);
this.geometry.vertices.push(p.position);
this.geometry.colors.push(p.color);
}
this.system = new THREE.ParticleSystem(this.geometry, this.material);
this.system.position = this.position;
this.system.rotation = this.rotation;
return this.system.sort = this.sort;
};
/*
@private
*/
Particles.prototype.randomVector = function() {
return new THREE.Vector3(Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1);
};
/*
Updates particles (should be call in a RAF loop)
@param float dt time delta ~1.0
*/
Particles.prototype.update = function(dt) {
var df, dv, i, l, p, _i, _ref;
df = new THREE.Vector3();
dv = new THREE.Vector3();
for (i = _i = 0, _ref = this.buffer.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
p = this.buffer[i];
if (p.available) {
continue;
}
p.life -= this.ageing;
if (p.life <= 0) {
p.reset();
this.pool.push(p);
continue;
}
l = p.life > 0.5 ? 1.0 : p.life + 0.5;
p.color.setRGB(l * p.basecolor.r, l * p.basecolor.g, l * p.basecolor.b);
if (this.friction !== 1.0) {
p.velocity.multiplyScalar(this.friction);
}
df.copy(p.force).multiplyScalar(dt);
p.velocity.addSelf(df);
dv.copy(p.velocity).multiplyScalar(dt);
p.position.addSelf(dv);
}
if (this.spawnRate > 0) {
this.emit(this.spawnRate);
}
this.geometry.verticesNeedUpdate = true;
return this.geometry.colorsNeedUpdate = true;
};
return Particles;
})();
/*
Particle sub class
@class bkcore.threejs.Particle
@author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
Particle = (function() {
function Particle() {
this.position = new THREE.Vector3(-10000, -10000, -10000);
this.velocity = new THREE.Vector3();
this.force = new THREE.Vector3();
this.color = new THREE.Color(0x000000);
this.basecolor = new THREE.Color(0x000000);
this.life = 0.0;
this.available = true;
}
Particle.prototype.reset = function() {
this.position.set(0, -100000, 0);
this.velocity.set(0, 0, 0);
this.force.set(0, 0, 0);
this.color.setRGB(0, 0, 0);
this.basecolor.setRGB(0, 0, 0);
this.life = 0.0;
return this.available = true;
};
return Particle;
})();
/*
Exports
@package bkcore.threejs
*/
exports = exports != null ? exports : this;
exports.bkcore || (exports.bkcore = {});
(_base = exports.bkcore).threejs || (_base.threejs = {});
exports.bkcore.threejs.Particle = Particle;
exports.bkcore.threejs.Particles = Particles;
}).call(this);
-2
View File
@@ -71,8 +71,6 @@ bkcore.Timer.prototype.update = function()
/*!
* Returns a formatted version of the current elapsed time using msToTime().
*
*
*/
bkcore.Timer.prototype.getElapsedTime = function()
{
+35 -12
View File
@@ -17,6 +17,12 @@ bkcore.hexgl.CameraChase = function(opts)
this.speedOffsetMax = 10;
this.speedOffsetStep = 0.05;
this.modes = {
CHASE: 0,
ORBIT: 1
}
this.mode = this.modes.CHASE;
this.camera = opts.camera;
this.targetObject = opts.target;
this.cameraCube = opts.cameraCube == undefined ? null : opts.cameraCube;
@@ -24,26 +30,43 @@ bkcore.hexgl.CameraChase = function(opts)
this.yoffset = opts.yoffest == undefined ? 8.0 : opts.yoffest;
this.zoffset = opts.zoffset == undefined ? 10.0 : opts.zoffset;
this.viewOffset = opts.viewOffset == undefined ? 10.0 : opts.viewOffset;
this.orbitOffset = 12;
this.lerp = opts.lerp == undefined ? 0.5 : opts.lerp;
this.time = 0.0;
}
bkcore.hexgl.CameraChase.prototype.update = function(dt, ratio)
{
this.dir.set(0,0,1);
this.up.set(0,1,0);
if(this.mode == this.modes.CHASE)
{
this.dir.set(0,0,1);
this.up.set(0,1,0);
this.targetObject.matrix.rotateAxis(this.up);
this.targetObject.matrix.rotateAxis(this.dir);
this.targetObject.matrix.rotateAxis(this.up);
this.targetObject.matrix.rotateAxis(this.dir);
this.speedOffset += (this.speedOffsetMax*ratio - this.speedOffset) * Math.min(1, 0.3*dt);
this.speedOffset += (this.speedOffsetMax*ratio - this.speedOffset) * Math.min(1, 0.3*dt);
this.target.copy(this.targetObject.position);
this.target.subSelf(this.dir.multiplyScalar(this.zoffset + this.speedOffset));
this.target.addSelf(this.up.multiplyScalar(this.yoffset));
this.target.y += -this.up.y + this.yoffset;
this.camera.position.copy(this.target, this.lerp);
this.camera.lookAt(this.dir.normalize().multiplyScalar(this.viewOffset).addSelf(this.targetObject.position));
this.target.copy(this.targetObject.position);
this.target.subSelf(this.dir.multiplyScalar(this.zoffset + this.speedOffset));
this.target.addSelf(this.up.multiplyScalar(this.yoffset));
this.target.y += -this.up.y + this.yoffset;
this.camera.position.copy(this.target);
this.camera.lookAt(this.dir.normalize().multiplyScalar(this.viewOffset).addSelf(this.targetObject.position));
}
else if(this.mode == this.modes.ORBIT)
{
this.time += dt*.008;
this.dir.set(
Math.cos(this.time)*this.orbitOffset,
this.yoffset/2,
Math.sin(this.time)*this.orbitOffset
);
this.target.copy(this.targetObject.position).addSelf(this.dir);
this.camera.position.copy(this.target);
this.camera.lookAt(this.targetObject.position);
}
if(this.cameraCube != null)
this.cameraCube.rotation.copy(this.camera.rotation);
+59 -18
View File
@@ -12,8 +12,8 @@ bkcore.hexgl.Gameplay = function(opts)
{
var self = this;
this.startDelay = 1000;
this.countDownDelay = 1500;
this.startDelay = opts.hud == null ? 0 : 1000;
this.countDownDelay = opts.hud == null ? 1000 : 1500;
this.active = false;
this.timer = new bkcore.Timer();
@@ -27,6 +27,7 @@ bkcore.hexgl.Gameplay = function(opts)
this.hud = opts.hud;
this.shipControls = opts.shipControls;
this.cameraControls = opts.cameraControls;
this.track = opts.track;
this.analyser = opts.analyser;
this.pixelRatio = opts.pixelRatio;
@@ -37,6 +38,7 @@ bkcore.hexgl.Gameplay = function(opts)
FINISH: 1,
DESTROYED: 2,
WRONGWAY: 3,
REPLAY: 4,
NONE: -1
};
this.result = this.results.NONE;
@@ -49,9 +51,13 @@ bkcore.hexgl.Gameplay = function(opts)
this.finishTime = null;
this.onFinish = opts.onFinish == undefined ? function(){console.log("FINISH");} : opts.onFinish;
this.raceData = null;
this.modes.timeattack = function()
{
self.hud.updateTime(self.timer.getElapsedTime());
self.raceData.tick(this.timer.time.elapsed);
self.hud != null && self.hud.updateTime(self.timer.getElapsedTime());
var cp = self.checkPoint();
if(cp == self.track.checkpoints.start && self.previousCheckPoint == self.track.checkpoints.last)
@@ -68,10 +74,10 @@ bkcore.hexgl.Gameplay = function(opts)
else
{
self.lap++;
self.hud.updateLap(self.lap, self.maxLaps);
self.hud != null && self.hud.updateLap(self.lap, self.maxLaps);
if(self.lap == self.maxLaps)
self.hud.display("Final lap", 0.5);
self.hud != null && self.hud.display("Final lap", 0.5);
}
}
else if(cp != -1 && cp != self.previousCheckPoint)
@@ -84,20 +90,30 @@ bkcore.hexgl.Gameplay = function(opts)
{
self.end(self.results.DESTROYED);
}
}
};
this.modes.replay = function()
{
self.raceData.applyInterpolated(this.timer.time.elapsed);
if(self.raceData.seek == self.raceData.last)
{
self.end(self.result.REPLAY);
}
};
}
bkcore.hexgl.Gameplay.prototype.simu = function()
{
this.lapTimes = [92300, 91250, 90365];
this.finishTime = this.lapTimes[0]+this.lapTimes[1]+this.lapTimes[2];
this.hud.display("Finish");
if(this.hud != null) this.hud.display("Finish");
this.step = 100;
this.result = this.results.FINISH;
this.shipControls.active = false;
}
bkcore.hexgl.Gameplay.prototype.start = function()
bkcore.hexgl.Gameplay.prototype.start = function(opts)
{
this.finishTime = null;
this.score = null;
@@ -108,12 +124,35 @@ bkcore.hexgl.Gameplay.prototype.start = function()
this.previousCheckPoint = this.track.checkpoints.start;
this.raceData = new bkcore.hexgl.RaceData(this.track.name, this.mode, this.shipControls);
if(this.mode == 'replay')
{
this.cameraControls.mode = this.cameraControls.modes.ORBIT;
if(this.hud != null) this.hud.messageOnly = true;
try {
var d = localStorage['race-'+this.track.name+'-replay'];
if(d == undefined)
{
console.error('No replay data for '+'race-'+this.track.name+'-replay'+'.');
return false;
}
this.raceData.import(
JSON.parse(d)
);
}
catch(e) { console.error('Bad replay format : '+e); return false; }
}
this.active = true;
this.step = 0;
this.timer.start();
this.hud.resetTime();
this.hud.display("Get ready", 1);
this.hud.updateLap(this.lap, this.maxLaps);
if(this.hud != null)
{
this.hud.resetTime();
this.hud.display("Get ready", 1);
this.hud.updateLap(this.lap, this.maxLaps);
}
}
bkcore.hexgl.Gameplay.prototype.end = function(result)
@@ -127,12 +166,12 @@ bkcore.hexgl.Gameplay.prototype.end = function(result)
if(result == this.results.FINISH)
{
this.hud.display("Finish");
if(this.hud != null) this.hud.display("Finish");
this.step = 100;
}
else if(result == this.results.DESTROYED)
{
this.hud.display("Destroyed");
if(this.hud != null) this.hud.display("Destroyed");
this.step = 100;
}
}
@@ -145,25 +184,27 @@ bkcore.hexgl.Gameplay.prototype.update = function()
if(this.step == 0 && this.timer.time.elapsed >= this.countDownDelay+this.startDelay)
{
this.hud.display("3");
if(this.hud != null) this.hud.display("3");
this.step = 1;
}
else if(this.step == 1 && this.timer.time.elapsed >= 2*this.countDownDelay+this.startDelay)
{
this.hud.display("2");
if(this.hud != null) this.hud.display("2");
this.step = 2;
}
else if(this.step == 2 && this.timer.time.elapsed >= 3*this.countDownDelay+this.startDelay)
{
this.hud.display("1");
if(this.hud != null) this.hud.display("1");
this.step = 3;
}
else if(this.step == 3 && this.timer.time.elapsed >= 4*this.countDownDelay+this.startDelay)
{
this.hud.display("Go", 0.5);
if(this.hud != null) this.hud.display("Go", 0.5);
this.step = 4;
this.timer.start();
this.shipControls.active = true;
if(this.mode != "replay")
this.shipControls.active = true;
}
else if(this.step == 4)
{
+40 -30
View File
@@ -12,6 +12,9 @@ bkcore.hexgl.HUD = function(opts)
{
var self = this;
this.visible = true;
this.messageOnly = false;
this.width = opts.width;
this.height = opts.height;
@@ -124,7 +127,11 @@ bkcore.hexgl.HUD.prototype.update = function(speed, speedRatio, shield, shieldRa
var SCREEN_HW = SCREEN_WIDTH / 2;
var SCREEN_HH = SCREEN_HEIGHT / 2;
//this.ctx.clearRect(0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT);
if(!this.visible)
{
this.ctx.clearRect(0 , 0 , SCREEN_WIDTH , SCREEN_HEIGHT);
return;
}
var w = this.bg.width;
var h = this.bg.height;
@@ -148,39 +155,42 @@ bkcore.hexgl.HUD.prototype.update = function(speed, speedRatio, shield, shieldRa
{
this.ctx.clearRect(0 , oh , SCREEN_WIDTH , nh);
this.ctx.drawImage(this.bg, o, oh, nw, nh);
if(!this.messageOnly)
{
this.ctx.drawImage(this.bg, o, oh, nw, nh);
this.ctx.save();
this.ctx.beginPath();
this.ctx.moveTo(bw+ba+SCREEN_HW, oh);
this.ctx.lineTo(-(bw+ba)+SCREEN_HW, oh);
this.ctx.lineTo(-bw+SCREEN_HW, SCREEN_HEIGHT);
this.ctx.lineTo(bw+SCREEN_HW, SCREEN_HEIGHT);
this.ctx.lineTo(bw+ba+SCREEN_HW, oh);
this.ctx.clip();
this.ctx.drawImage(this.fgspeed, o, oh, nw, nh);
this.ctx.restore();
this.ctx.save();
this.ctx.beginPath();
this.ctx.moveTo(bw+ba+SCREEN_HW, oh);
this.ctx.lineTo(-(bw+ba)+SCREEN_HW, oh);
this.ctx.lineTo(-bw+SCREEN_HW, SCREEN_HEIGHT);
this.ctx.lineTo(bw+SCREEN_HW, SCREEN_HEIGHT);
this.ctx.lineTo(bw+ba+SCREEN_HW, oh);
this.ctx.clip();
this.ctx.drawImage(this.fgspeed, o, oh, nw, nh);
this.ctx.restore();
this.ctx.save();
this.ctx.beginPath();
this.ctx.moveTo(-sw+SCREEN_HW, oh+sy);
this.ctx.lineTo(sw+SCREEN_HW, oh+sy);
this.ctx.lineTo(sw+SCREEN_HW, oh+sh+sy);
this.ctx.lineTo(-sw+SCREEN_HW, oh+sh+sy);
this.ctx.lineTo(-sw+SCREEN_HW, oh+sh);
this.ctx.clip();
this.ctx.drawImage(this.fgshield, o, oh, nw, nh);
this.ctx.restore();
this.ctx.save();
this.ctx.beginPath();
this.ctx.moveTo(-sw+SCREEN_HW, oh+sy);
this.ctx.lineTo(sw+SCREEN_HW, oh+sy);
this.ctx.lineTo(sw+SCREEN_HW, oh+sh+sy);
this.ctx.lineTo(-sw+SCREEN_HW, oh+sh+sy);
this.ctx.lineTo(-sw+SCREEN_HW, oh+sh);
this.ctx.clip();
this.ctx.drawImage(this.fgshield, o, oh, nw, nh);
this.ctx.restore();
// SPEED
this.ctx.font = (SCREEN_WIDTH/this.speedFontRatio)+"px "+this.font;
this.ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
this.ctx.fillText(speed, SCREEN_HW, SCREEN_HEIGHT - nh*0.57);
// SPEED
this.ctx.font = (SCREEN_WIDTH/this.speedFontRatio)+"px "+this.font;
this.ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
this.ctx.fillText(speed, SCREEN_HW, SCREEN_HEIGHT - nh*0.57);
// SHIELD
this.ctx.font = (SCREEN_WIDTH/this.shieldFontRatio)+"px "+this.font;
this.ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
this.ctx.fillText(shield, SCREEN_HW, SCREEN_HEIGHT - nh*0.44);
// SHIELD
this.ctx.font = (SCREEN_WIDTH/this.shieldFontRatio)+"px "+this.font;
this.ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
this.ctx.fillText(shield, SCREEN_HW, SCREEN_HEIGHT - nh*0.44);
}
}
else if(this.step == 1)
{
+80 -46
View File
@@ -1,7 +1,7 @@
/*
* HexGL
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
*/
@@ -19,8 +19,9 @@ bkcore.hexgl.HexGL = function(opts)
this.a = window.location.href;
this.mobile = opts.mobile == undefined ? false : opts.mobile;
this.active = true;
this.displayHUD = opts.hud == undefined ? true : opts.hud;
this.width = opts.width == undefined ? window.innerWidth : opts.width;
this.height = opts.height == undefined ? window.innerHeight : opts.height;
@@ -32,6 +33,10 @@ bkcore.hexgl.HexGL = function(opts)
this.track = bkcore.hexgl.tracks[ opts.track == undefined ? 'Cityscape' : opts.track ];
this.mode = opts.mode == undefined ? 'timeattack' : opts.mode;
this.controlType = opts.controlType == undefined ? 1 : opts.controlType;
if(this.half)
{
this.width /= 2;
@@ -54,6 +59,10 @@ bkcore.hexgl.HexGL = function(opts)
this.containers.main = opts.container == undefined ? document.body : opts.container;
this.containers.overlay = opts.overlay == undefined ? document.body : opts.overlay;
this.gameover = opts.gameover == undefined ? null : opts.gameover;
this.godmode = opts.godmode == undefined ? false : opts.godmode;
this.hud = null;
this.gameplay = null;
@@ -64,9 +73,9 @@ bkcore.hexgl.HexGL = function(opts)
this.initRenderer();
function onKeyPress(event)
function onKeyPress(event)
{
if(event.keyCode == 27/*escape*/)
if(event.keyCode == 27/*escape*/)
{
self.reset();
}
@@ -84,7 +93,7 @@ bkcore.hexgl.HexGL.prototype.start = function()
function raf()
{
requestAnimationFrame( raf );
if(self && self.active) requestAnimationFrame( raf );
self.update();
}
@@ -102,7 +111,8 @@ bkcore.hexgl.HexGL.prototype.reset = function()
bkcore.hexgl.HexGL.prototype.restart = function()
{
this.document.getElementById('finish').style.display = 'none';
try{ this.document.getElementById('finish').style.display = 'none'; }
catch(e){};
this.reset();
}
@@ -120,16 +130,16 @@ bkcore.hexgl.HexGL.prototype.init = function()
{
this.initHUD();
this.track.buildMaterials(this.quality);
this.track.buildMaterials(this.quality, this.mobile);
this.track.buildScenes(this, this.quality);
this.track.buildScenes(this, this.quality, this.mobile);
this.initGameComposer();
}
bkcore.hexgl.HexGL.prototype.load = function(opts)
{
this.track.load(opts, this.quality);
this.track.load(opts, this.quality, this.mobile);
}
bkcore.hexgl.HexGL.prototype.initGameplay = function()
@@ -137,17 +147,15 @@ bkcore.hexgl.HexGL.prototype.initGameplay = function()
var self = this;
this.gameplay = new bkcore.hexgl.Gameplay({
mode: "timeattack",
mode: this.mode,
hud: this.hud,
shipControls: this.components.shipControls,
cameraControls: this.components.cameraChase,
analyser: this.track.analyser,
pixelRatio: this.track.pixelRatio,
track: {
checkpoints: this.track.checkpoints,
spawn: this.track.spawn,
spawnRotation: this.track.spawnRotation
},
track: this.track,
onFinish: function() {
self.components.shipControls.terminate();
self.displayScore(this.finishTime, this.lapTimes);
}
});
@@ -157,7 +165,24 @@ bkcore.hexgl.HexGL.prototype.initGameplay = function()
bkcore.hexgl.HexGL.prototype.displayScore = function(f, l)
{
var t = 'cityscape';
this.active = false;
var tf = bkcore.Timer.msToTimeString(f);
var tl = [
bkcore.Timer.msToTimeString(l[0]),
bkcore.Timer.msToTimeString(l[1]),
bkcore.Timer.msToTimeString(l[2])
];
if(this.gameover !== null)
{
this.gameover.style.display = "block";
this.gameover.children[0].innerHTML = tf.m + "'" + tf.s + "''" + tf.ms;
this.containers.main.parentElement.style.display = "none";
return;
}
var t = this.track;
var dc = this.document.getElementById("finish");
var ds = this.document.getElementById("finish-state");
var dh = this.document.getElementById("finish-hallmsg");
@@ -172,12 +197,6 @@ bkcore.hexgl.HexGL.prototype.displayScore = function(f, l)
var sl = this.document.getElementById("lowfps-msg");
var d = this.difficulty == 0 ? 'casual' : 'hard';
var ts = this.hud.timeSeparators;
var tf = bkcore.Timer.msToTimeString(f);
var tl = [
bkcore.Timer.msToTimeString(l[0]),
bkcore.Timer.msToTimeString(l[1]),
bkcore.Timer.msToTimeString(l[2])
];
if(this.gameplay.result == this.gameplay.results.FINISH)
{
@@ -189,6 +208,9 @@ bkcore.hexgl.HexGL.prototype.displayScore = function(f, l)
{
dr != undefined && (dr.innerHTML = "New local record!");
localStorage['score-'+t+'-'+d] = f;
// Export race data
localStorage['race-'+t+'-replay'] = JSON.Stringify(this.gameplay.raceData.export());
}
else
{
@@ -250,25 +272,30 @@ bkcore.hexgl.HexGL.prototype.initRenderer = function()
clearColor: 0x000000
});
renderer.physicallyBasedShading = true;
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
if(this.quality > 0 && !this.mobile)
{
renderer.physicallyBasedShading = true;
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
}
renderer.autoClear = false;
renderer.sortObjects = false;
renderer.setSize( this.width, this.height );
renderer.domElement.style.position = "relative";
this.containers.main.appendChild( renderer.domElement );
this.containers.main.appendChild( renderer.domElement );
this.canvas = renderer.domElement;
this.renderer = renderer;
this.manager = new bkcore.threejs.RenderManager(renderer);
}
bkcore.hexgl.HexGL.prototype.initHUD = function()
{
if(!this.displayHUD) return;
this.hud = new bkcore.hexgl.HUD({
width: this.width,
height: this.height,
@@ -276,7 +303,7 @@ bkcore.hexgl.HexGL.prototype.initHUD = function()
bg: this.track.lib.get("images", "hud.bg"),
speed: this.track.lib.get("images", "hud.speed"),
shield: this.track.lib.get("images", "hud.shield")
});
});
this.containers.overlay.appendChild(this.hud.canvas);
}
@@ -285,7 +312,7 @@ bkcore.hexgl.HexGL.prototype.initGameComposer = function()
var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
var renderTarget = new THREE.WebGLRenderTarget( this.width, this.height, renderTargetParameters );
// GAME COMPOSER
// GAME COMPOSER
var renderSky = new THREE.RenderPass( this.manager.get("sky").scene, this.manager.get("sky").camera );
var renderModel = new THREE.RenderPass( this.manager.get("game").scene, this.manager.get("game").camera );
@@ -293,7 +320,8 @@ bkcore.hexgl.HexGL.prototype.initGameComposer = function()
this.composers.game = new THREE.EffectComposer( this.renderer, renderTarget );
var effectScreen = new THREE.ShaderPass( THREE.ShaderExtras[ "screen" ] );
var effectScreen = new THREE.ShaderPass( THREE.ShaderExtras[ "screen" ] );
effectScreen.renderToScreen = true;
var effectVignette = new THREE.ShaderPass( THREE.ShaderExtras[ "vignette" ] );
var effectHex = new THREE.ShaderPass( bkcore.threejs.Shaders[ "hexvignette" ] );
@@ -308,17 +336,17 @@ bkcore.hexgl.HexGL.prototype.initGameComposer = function()
this.composers.game.addPass( renderSky );
this.composers.game.addPass( renderModel );
if(this.quality > 0)
{
var effectFXAA = new THREE.ShaderPass( THREE.ShaderExtras[ "fxaa" ] );
effectFXAA.uniforms[ 'resolution' ].value.set( 1 / this.width, 1 / this.height );
// if(this.quality > 0 && !this.mobile)
// {
// var effectFXAA = new THREE.ShaderPass( THREE.ShaderExtras[ "fxaa" ] );
// effectFXAA.uniforms[ 'resolution' ].value.set( 1 / this.width, 1 / this.height );
this.composers.game.addPass( effectFXAA );
this.extras.fxaa = effectFXAA;
}
if(this.quality > 1)
{
// this.composers.game.addPass( effectFXAA );
// this.extras.fxaa = effectFXAA;
// }
if(this.quality > 1 && !this.mobile)
{
var effectBloom = new THREE.BloomPass( 0.8, 25, 4 , 256);
this.composers.game.addPass( effectBloom );
@@ -326,12 +354,15 @@ bkcore.hexgl.HexGL.prototype.initGameComposer = function()
this.extras.bloom = effectBloom;
}
this.composers.game.addPass( effectHex );
if(!this.mobile || this.quality > 0)
this.composers.game.addPass( effectHex );
else
this.composers.game.addPass( effectScreen );
}
bkcore.hexgl.HexGL.prototype.createMesh = function(parent, geometry, x, y, z, mat)
bkcore.hexgl.HexGL.prototype.createMesh = function(parent, geometry, x, y, z, mat)
{
geometry.computeTangents();
@@ -339,7 +370,7 @@ bkcore.hexgl.HexGL.prototype.createMesh = function(parent, geometry, x, y, z, ma
mesh.position.set( x, y, z );
parent.add(mesh);
if(this.quality > 0)
if(this.quality > 0 && !this.mobile)
{
mesh.castShadow = true;
mesh.receiveShadow = true;
@@ -389,4 +420,7 @@ bkcore.hexgl.HexGL.prototype.tweakShipControls = function()
c.driftLerp = 0.3;
c.angularLerp = 0.4;
}
}
if(this.godmode)
c.shieldDamage = 0.0;
}
+99
View File
@@ -0,0 +1,99 @@
/*
* HexGL
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
*/
var bkcore = bkcore || {};
bkcore.hexgl = bkcore.hexgl || {};
bkcore.hexgl.RaceData = function(track, mode, shipControls)
{
this.track = track;
this.mode = mode;
this.shipControls = shipControls;
this.rate = 2; // 1 / rate
this.rateState = 1;
this.data = [];
this.last = -1;
this.seek = 0;
this._p = new THREE.Vector3();
this._pp = new THREE.Vector3();
this._np = new THREE.Vector3();
this._q = new THREE.Quaternion();
this._pq = new THREE.Quaternion();
this._nq = new THREE.Quaternion();
}
bkcore.hexgl.RaceData.prototype.tick = function(time)
{
if(this.rateState == 1)
{
var p = this.shipControls.getPosition();
var q = this.shipControls.getQuaternion();
this.data.push([
time,
p.x, p.y, p.z,
q.x, q.y, q.z, q.w
]);
++this.last;
}
else if(this.rateState == this.rate)
{
this.rateState = 0;
}
this.rate++;
}
bkcore.hexgl.RaceData.prototype.applyInterpolated = function(time)
{
while(this.seek < this.last && this.data[this.seek+1][0] < time)
++this.seek;
var prev = this.data[this.seek];
this._pp.set(prev[1], prev[2], prev[3]);
this._pq.set(prev[4], prev[5], prev[6], prev[7]);
if(this.seek < 0)
{
console.warn('Bad race data.');
return;
}
// no interpolation
if(this.seek == this.last || this.seek == 0)
this.shipControls.teleport(this._pp, this._pq);
// interpolation
var next = this.data[this.seek+1];
this._np.set(next[1], next[2], next[3]);
this._nq.set(next[4], next[5], next[6], next[7]);
var t = (time-prev[0]) / (next[0]-prev[0]);
this._p.copy(this._pp).lerpSelf(this._np, t);
this._q.copy(this._pq).slerpSelf(this._nq, t);
this.shipControls.teleport(this._p, this._q);
}
bkcore.hexgl.RaceData.prototype.reset = function()
{
this.seek = 0;
}
bkcore.hexgl.RaceData.prototype.export = function()
{
return this.data;
}
bkcore.hexgl.RaceData.prototype.import = function(imp)
{
this.data = imp;
this.last = this.data.length-1;
console.log(this.data);
}
+282 -51
View File
@@ -1,19 +1,21 @@
/*
* HexGL
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
*/
var bkcore = bkcore || {};
bkcore.hexgl = bkcore.hexgl || {};
bkcore.hexgl.ShipControls = function(domElement)
bkcore.hexgl.ShipControls = function(ctx)
{
var self = this;
var domElement = ctx.document;
this.active = true;
this.destroyed = false;
this.falling = false;
this.dom = domElement;
this.mesh = null;
@@ -25,7 +27,7 @@ bkcore.hexgl.ShipControls = function(domElement)
this.thrust = 0.02;
this.airBrake = 0.02;
this.maxSpeed = 7.0;
this.boosterSpeed = this.maxSpeed * 0.4;
this.boosterSpeed = this.maxSpeed * 0.2;
this.boosterDecay = 0.01;
this.angularSpeed = 0.005;
this.airAngularSpeed = 0.0065;
@@ -73,7 +75,7 @@ bkcore.hexgl.ShipControls = function(domElement)
this.rollAngle = 0.6;
this.rollLerp = 0.08;
this.rollDirection = new THREE.Vector3(0,0,1);
this.gradient = 0.0;
this.gradientTarget = 0.0;
this.gradientLerp = 0.05;
@@ -95,6 +97,8 @@ bkcore.hexgl.ShipControls = function(domElement)
this.repulsionAmount = 0.0;
this.repulsionForce = new THREE.Vector3();
this.fallVector = new THREE.Vector3(0,-20,0);
this.resetPos = null;
this.resetRot = null;
@@ -114,9 +118,121 @@ bkcore.hexgl.ShipControls = function(domElement)
right: false
};
function onKeyDown(event)
this.touchController = null;
this.orientationController = null;
if(ctx.controlType == 1 && bkcore.controllers.TouchController.isCompatible())
{
switch(event.keyCode)
this.touchController = new bkcore.controllers.TouchController(
domElement, ctx.width/2,
function(state, touch, event){
if(event.touches.length >= 4)
window.location.reload(false);
else if(event.touches.length == 3)
ctx.restart();
else if(event.touches.length <= 1)
self.key.forward = false;
else
self.key.forward = true;
});
}
else if(ctx.controlType == 3 && bkcore.controllers.OrientationController.isCompatible())
{
this.orientationController = new bkcore.controllers.OrientationController(
domElement, true,
function(state, touch, event){
if(event.touches.length >= 4)
window.location.reload(false);
else if(event.touches.length == 3)
ctx.restart();
else if(event.touches.length < 1)
self.key.forward = false;
else
self.key.forward = true;
});
}
else if(ctx.controlType == 2)
{
if(Leap == null)
throw new Error("Unable to reach LeapJS!");
var leapInfo = this.leapInfo = document.getElementById('leapinfo');
isServerConnected = false;
var lb = this.leapBridge = {
isConnected: true,
hasHands: false,
palmNormal: [0, 0, 0]
};
function updateInfo()
{
if(!isServerConnected)
{
leapInfo.innerHTML = 'Waiting for the Leap Motion Controller server...'
leapInfo.style.display = 'block';
}
else if(lb.isConnected && lb.hasHands)
{
leapInfo.style.display = 'none';
}
else if(!lb.isConnected)
{
leapInfo.innerHTML = 'Please connect your Leap Motion Controller.'
leapInfo.style.display = 'block';
}
else if(!lb.hasHands)
{
leapInfo.innerHTML = 'Put your hand over the Leap Motion Controller to play.'
leapInfo.style.display = 'block';
}
}
updateInfo();
var lc = this.leapController = new Leap.Controller({enableGestures: false});
lc.on('connect', function()
{
isServerConnected = true;
updateInfo();
});
lc.on('deviceConnected', function()
{
lb.isConnected = true;
updateInfo();
});
lc.on('deviceDisconnected', function()
{
lb.isConnected = false;
updateInfo();
});
lc.on('frame', function(frame)
{
if(!lb.isConnected) return;
hand = frame.hands[0];
if(typeof hand === 'undefined')
{
if(lb.hasHands)
{
lb.hasHands = false;
updateInfo();
}
lb.palmNormal = [0, 0, 0];
}
else
{
if(!lb.hasHands)
{
lb.hasHands = true;
updateInfo();
}
lb.palmNormal = hand.palmNormal;
}
});
lc.connect();
}
function onKeyDown(event)
{
switch(event.keyCode)
{
case 38: /*up*/ self.key.forward = true; break;
@@ -134,9 +250,9 @@ bkcore.hexgl.ShipControls = function(domElement)
}
};
function onKeyUp(event)
function onKeyUp(event)
{
switch(event.keyCode)
switch(event.keyCode)
{
case 38: /*up*/ self.key.forward = false; break;
@@ -186,11 +302,22 @@ bkcore.hexgl.ShipControls.prototype.reset = function(position, rotation)
this.dummy.matrix.setPosition(this.dummy.position);
this.dummy.matrix.setRotationFromQuaternion(this.dummy.quaternion);
this.mesh.matrix.identity();
this.mesh.applyMatrix(this.dummy.matrix);
}
bkcore.hexgl.ShipControls.prototype.terminate = function()
{
this.destroy();
if(this.leapController != null)
{
this.leapController.disconnect();
this.leapInfo.style.display = 'none';
}
}
bkcore.hexgl.ShipControls.prototype.destroy = function()
{
this.active = false;
@@ -200,9 +327,26 @@ bkcore.hexgl.ShipControls.prototype.destroy = function()
this.collision.right = false;
}
bkcore.hexgl.ShipControls.prototype.fall = function()
{
this.active = false;
this.collision.front = false;
this.collision.left = false;
this.collision.right = false;
this.falling = true;
_this = this;
setTimeout(function(){
_this.destroyed = true;
}, 1500);
}
bkcore.hexgl.ShipControls.prototype.update = function(dt)
{
if(!this.active) return;
if(this.falling)
{
this.mesh.position.addSelf(this.fallVector);
return;
}
this.rotation.y = 0;
this.movement.set(0,0,0);
@@ -211,46 +355,73 @@ bkcore.hexgl.ShipControls.prototype.update = function(dt)
var rollAmount = 0.0;
var angularAmount = 0.0;
var yawLeap = 0.0;
if(this.key.forward)
this.speed += this.thrust * dt;
else
this.speed -= this.airResist * dt;
if(this.key.left)
if(this.leapBridge != null && this.leapBridge.hasHands)
{
angularAmount += this.angularSpeed * dt;
rollAmount -= this.rollAngle;
rollAmount -= this.leapBridge.palmNormal[0] * 3.5 * this.rollAngle;
yawLeap = -this.leapBridge.palmNormal[2] * 0.6;
}
if(this.key.right)
{
angularAmount -= this.angularSpeed * dt;
rollAmount += this.rollAngle;
}
if(this.key.ltrigger)
if(this.active)
{
if(this.touchController != null)
{
angularAmount -= this.touchController.stickVector.x/100 * this.angularSpeed * dt;
rollAmount += this.touchController.stickVector.x/100 * this.rollAngle;
}
if(this.orientationController != null)
{
angularAmount += this.orientationController.beta/45 * this.angularSpeed * dt;
rollAmount -= this.orientationController.beta/45 * this.rollAngle;
}
if(this.leapBridge != null && this.leapBridge.hasHands)
{
angularAmount += this.leapBridge.palmNormal[0] * 2 * this.angularSpeed * dt;
this.speed += Math.max(0.0, (0.5 + this.leapBridge.palmNormal[2])) * 3 * this.thrust * dt;
}
if(this.key.forward)
this.speed += this.thrust * dt;
else
this.speed -= this.airResist * dt;
if(this.key.left)
angularAmount += this.airAngularSpeed * dt;
else
angularAmount += this.airAngularSpeed * 0.5 * dt;
this.speed -= this.airBrake * dt;
this.drift += (this.airDrift - this.drift) * this.driftLerp;
this.movement.x += this.speed * this.drift * dt;
if(this.drift > 0.0)
this.movement.z -= this.speed * this.drift * dt;
rollAmount -= this.rollAngle * 0.7;
}
if(this.key.rtrigger)
{
{
angularAmount += this.angularSpeed * dt;
rollAmount -= this.rollAngle;
}
if(this.key.right)
angularAmount -= this.airAngularSpeed * dt;
else
angularAmount -= this.airAngularSpeed * 0.5 * dt;
this.speed -= this.airBrake * dt;
this.drift += (-this.airDrift - this.drift) * this.driftLerp;
this.movement.x += this.speed * this.drift * dt;
if(this.drift < 0.0)
this.movement.z += this.speed * this.drift * dt;
rollAmount += this.rollAngle * 0.7;
{
angularAmount -= this.angularSpeed * dt;
rollAmount += this.rollAngle;
}
if(this.key.ltrigger)
{
if(this.key.left)
angularAmount += this.airAngularSpeed * dt;
else
angularAmount += this.airAngularSpeed * 0.5 * dt;
this.speed -= this.airBrake * dt;
this.drift += (this.airDrift - this.drift) * this.driftLerp;
this.movement.x += this.speed * this.drift * dt;
if(this.drift > 0.0)
this.movement.z -= this.speed * this.drift * dt;
rollAmount -= this.rollAngle * 0.7;
}
if(this.key.rtrigger)
{
if(this.key.right)
angularAmount -= this.airAngularSpeed * dt;
else
angularAmount -= this.airAngularSpeed * 0.5 * dt;
this.speed -= this.airBrake * dt;
this.drift += (-this.airDrift - this.drift) * this.driftLerp;
this.movement.x += this.speed * this.drift * dt;
if(this.drift < 0.0)
this.movement.z += this.speed * this.drift * dt;
rollAmount += this.rollAngle * 0.7;
}
}
this.angular += (angularAmount - this.angular) * this.angularLerp;
@@ -306,7 +477,7 @@ bkcore.hexgl.ShipControls.prototype.update = function(dt)
this.mesh.matrix.identity();
// Gradient (Mesh only, no dummy physics impact)
var gradientDelta = (this.gradientTarget - this.gradient) * this.gradientLerp;
var gradientDelta = (this.gradientTarget - (yawLeap + this.gradient)) * this.gradientLerp;
if(Math.abs(gradientDelta) > this.epsilon) this.gradient += gradientDelta;
if(Math.abs(this.gradient) > this.epsilon)
{
@@ -337,6 +508,45 @@ bkcore.hexgl.ShipControls.prototype.update = function(dt)
}
};
bkcore.hexgl.ShipControls.prototype.teleport = function(pos, quat)
{
this.quaternion.copy(quat);
this.dummy.quaternion.copy(this.quaternion);
this.dummy.position.copy(pos);
this.dummy.matrix.setPosition(this.dummy.position);
//console.log(pos.x, pos.y, pos.z);
this.dummy.matrix.setRotationFromQuaternion(this.dummy.quaternion);
if(this.mesh != null)
{
this.mesh.matrix.identity();
/*
// Gradient (Mesh only, no dummy physics impact)
var gradientDelta = (this.gradientTarget - this.gradient) * this.gradientLerp;
if(Math.abs(gradientDelta) > this.epsilon) this.gradient += gradientDelta;
if(Math.abs(this.gradient) > this.epsilon)
{
this.gradientAxis.set(1,0,0);
this.mesh.matrix.rotateByAxis(this.gradientAxis, this.gradient);
}
// Tilting (Idem)
var tiltDelta = (this.tiltTarget - this.tilt) * this.tiltLerp;
if(Math.abs(tiltDelta) > this.epsilon) this.tilt += tiltDelta;
if(Math.abs(this.tilt) > this.epsilon)
{
this.tiltAxis.set(0,0,1);
this.mesh.matrix.rotateByAxis(this.tiltAxis, this.tilt);
}
*/
this.mesh.applyMatrix(this.dummy.matrix);
this.mesh.updateMatrixWorld(true);
}
}
bkcore.hexgl.ShipControls.prototype.boosterCheck = function(dt)
{
if(!this.collisionMap || !this.collisionMap.loaded)
@@ -396,9 +606,9 @@ bkcore.hexgl.ShipControls.prototype.collisionCheck = function(dt)
var rPos = this.repulsionVRight.addSelf(pos);
var lCol = this.collisionMap.getPixel(Math.round(lPos.x), Math.round(lPos.z)).r;
var rCol = this.collisionMap.getPixel(Math.round(rPos.x), Math.round(rPos.z)).r;
this.repulsionAmount = Math.max(0.8,
Math.min(this.repulsionCap,
this.repulsionAmount = Math.max(0.8,
Math.min(this.repulsionCap,
this.speed * this.repulsionRatio
)
);
@@ -421,6 +631,17 @@ bkcore.hexgl.ShipControls.prototype.collisionCheck = function(dt)
this.speed = 0;
}
// DIRTY GAMEOVER
if(rCol < 128 && lCol < 128)
{
var fCol = this.collisionMap.getPixel(Math.round(pos.x+2), Math.round(pos.z+2)).r;
if(fCol < 128)
{
console.log('GAMEOVER');
this.fall();
}
}
this.speed *= this.collisionSpeedDecrease;
this.speed *= (1-this.collisionSpeedDecreaseCoef*(1-collision.r/255));
this.boost = 0;
@@ -488,7 +709,7 @@ bkcore.hexgl.ShipControls.prototype.heightCheck = function(dt)
x = this.heightMap.pixels.width/2 + this.tiltVector.x * this.heightPixelRatio;
z = this.heightMap.pixels.height/2 + this.tiltVector.z * this.heightPixelRatio;
nheight = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;
nheight = this.heightMap.getPixelFBilinear(x, z) / this.heightScale + this.heightBias;
}
if(nheight < 16777)
@@ -506,7 +727,7 @@ bkcore.hexgl.ShipControls.prototype.getRealSpeed = function(scale)
bkcore.hexgl.ShipControls.prototype.getRealSpeedRatio = function()
{
return Math.min(
this.maxSpeed,
this.maxSpeed,
this.speed+this.boost
) / this.maxSpeed;
};
@@ -532,4 +753,14 @@ bkcore.hexgl.ShipControls.prototype.getShield = function(scale)
this.shield
* (scale == undefined ? 1 : scale)
);
};
};
bkcore.hexgl.ShipControls.prototype.getPosition = function()
{
return this.dummy.position;
}
bkcore.hexgl.ShipControls.prototype.getQuaternion = function()
{
return this.dummy.quaternion;
}
+13 -13
View File
@@ -1,7 +1,7 @@
/*
* HexGL
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
*/
@@ -46,7 +46,7 @@ bkcore.hexgl.ShipEffects = function(opts)
texture: opts.textureSpark,
size: 2,
life: 60,
max: 500
max: 200
}),
leftClouds: new bkcore.threejs.Particles(
@@ -59,7 +59,7 @@ bkcore.hexgl.ShipEffects = function(opts)
size: 6,
blending: THREE.NormalBlending,
life: 60,
max: 500,
max: 200,
spawn: new THREE.Vector3(3,-0.3,0),
spawnRadius: new THREE.Vector3(1,1,2),
velocity: new THREE.Vector3(0,0,-0.4),
@@ -75,7 +75,7 @@ bkcore.hexgl.ShipEffects = function(opts)
texture: opts.textureSpark,
size: 2,
life: 60,
max: 500
max: 200
}),
rightClouds: new bkcore.threejs.Particles(
@@ -88,7 +88,7 @@ bkcore.hexgl.ShipEffects = function(opts)
size: 6,
blending: THREE.NormalBlending,
life: 60,
max: 500,
max: 200,
spawn: new THREE.Vector3(-3,-0.3,0),
spawnRadius: new THREE.Vector3(1,1,2),
velocity: new THREE.Vector3(0,0,-0.4),
@@ -105,7 +105,7 @@ bkcore.hexgl.ShipEffects = function(opts)
bkcore.hexgl.ShipEffects.prototype.update = function(dt)
{
var boostRatio, opacity, scale, intensity, random;
var boostRatio, opacity, scale, intensity, random;
if(this.shipControls.destroyed)
{
@@ -144,8 +144,8 @@ bkcore.hexgl.ShipEffects.prototype.update = function(dt)
this.particles.leftSparks.velocity.copy(this.pVel).x *= -1;
this.particles.leftSparks.spawn.copy(this.pOffset).x *= -1;
if(this.shipControls.mesh)
{
if(this.shipControls.mesh)
{
// RIGHT
this.shipControls.mesh.matrix.rotateAxis(this.particles.rightSparks.spawn);
this.particles.rightSparks.spawn.multiplyScalar(this.pOffsetS).addSelf(this.shipControls.dummy.position);
@@ -168,14 +168,14 @@ bkcore.hexgl.ShipEffects.prototype.update = function(dt)
if(this.shipControls.collision.right)
{
this.particles.rightSparks.emit(Math.round(30*dt));
this.particles.rightClouds.emit(Math.round(10*dt));
this.particles.rightSparks.emit(10);
this.particles.rightClouds.emit(5);
}
if(this.shipControls.collision.left)
{
this.particles.leftSparks.emit(Math.round(30*dt));
this.particles.leftClouds.emit(Math.round(10*dt));
this.particles.leftSparks.emit(10);
this.particles.leftClouds.emit(5);
}
this.particles.rightSparks.update(dt);
@@ -183,4 +183,4 @@ bkcore.hexgl.ShipEffects.prototype.update = function(dt)
this.particles.leftSparks.update(dt);
this.particles.leftClouds.update(dt);
}
}
}
+65 -61
View File
@@ -1,7 +1,7 @@
/*
* HexGL
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* @license This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License.
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/3.0/.
*/
@@ -14,6 +14,8 @@ bkcore.hexgl.tracks.Cityscape = {
lib: null,
materials: {},
name: "Cityscape",
checkpoints: {
list: [0,1,2],
start: 0,
@@ -21,8 +23,8 @@ bkcore.hexgl.tracks.Cityscape = {
},
spawn: {
x: -1134*2,
y: 387,
x: -1134*2,
y: 387,
z: -443*2
},
@@ -35,15 +37,17 @@ bkcore.hexgl.tracks.Cityscape = {
analyser: null,
pixelRatio: 2048.0 / 6000.0,
load: function(opts, quality)
load: function(opts, quality, mobile)
{
this.lib = new bkcore.threejs.Loader(opts);
if(quality < 1) // LOW
if((quality < 1 && !mobile) || quality < 2) // LOW
{
this.lib.load({
textures: {
'hex' : "textures/hud/hex.jpg",
'spark' : "textures/particles/spark.png",
'cloud' : "textures/particles/cloud.png",
'ship.feisar.diffuse' : "textures/ships/feisar/diffuse.jpg",
'booster.diffuse' : "textures/ships/feisar/booster/booster.png",
'booster.sprite' : "textures/ships/feisar/booster/boostersprite.jpg",
@@ -80,36 +84,36 @@ bkcore.hexgl.tracks.Cityscape = {
});
}
else // HIGH
{
{console.log('HIGH');
this.lib.load({
textures: {
'hex' : "textures/hud/hex.jpg",
'spark' : "textures/particles/spark.png",
'cloud' : "textures/particles/cloud.png",
'ship.feisar.diffuse' : "textures/ships/feisar/diffuse.jpg",
'ship.feisar.specular' : "textures/ships/feisar/specular.jpg",
'ship.feisar.normal' : "textures/ships/feisar/normal.jpg",
'booster.diffuse' : "textures/ships/feisar/booster/booster.png",
'booster.sprite' : "textures/ships/feisar/booster/boostersprite.jpg",
'track.cityscape.diffuse' : "textures/tracks/cityscape/diffuse.jpg",
'track.cityscape.specular' : "textures/tracks/cityscape/specular.jpg",
'track.cityscape.normal' : "textures/tracks/cityscape/normal.jpg",
'track.cityscape.scrapers1.diffuse' : "textures/tracks/cityscape/scrapers1/diffuse.jpg",
'track.cityscape.scrapers1.specular': "textures/tracks/cityscape/scrapers1/specular.jpg",
'track.cityscape.scrapers1.normal' : "textures/tracks/cityscape/scrapers1/normal.jpg",
'track.cityscape.scrapers2.diffuse' : "textures/tracks/cityscape/scrapers2/diffuse.jpg",
'track.cityscape.scrapers2.specular': "textures/tracks/cityscape/scrapers2/specular.jpg",
'track.cityscape.scrapers2.normal' : "textures/tracks/cityscape/scrapers2/normal.jpg",
'track.cityscape.start.diffuse' : "textures/tracks/cityscape/start/diffuse.jpg",
'track.cityscape.start.specular' : "textures/tracks/cityscape/start/specular.jpg",
'track.cityscape.start.normal' : "textures/tracks/cityscape/start/normal.jpg",
'track.cityscape.start.banner' : "textures/tracks/cityscape/start/start.jpg",
'bonus.base.diffuse' : "textures/bonus/base/diffuse.jpg",
'bonus.base.normal' : "textures/bonus/base/normal.jpg",
'bonus.base.specular' : "textures/bonus/base/specular.jpg"
'hex' : "textures.full/hud/hex.jpg",
'spark' : "textures.full/particles/spark.png",
'cloud' : "textures.full/particles/cloud.png",
'ship.feisar.diffuse' : "textures.full/ships/feisar/diffuse.jpg",
'ship.feisar.specular' : "textures.full/ships/feisar/specular.jpg",
'ship.feisar.normal' : "textures.full/ships/feisar/normal.jpg",
'booster.diffuse' : "textures.full/ships/feisar/booster/booster.png",
'booster.sprite' : "textures.full/ships/feisar/booster/boostersprite.jpg",
'track.cityscape.diffuse' : "textures.full/tracks/cityscape/diffuse.jpg",
'track.cityscape.specular' : "textures.full/tracks/cityscape/specular.jpg",
'track.cityscape.normal' : "textures.full/tracks/cityscape/normal.jpg",
'track.cityscape.scrapers1.diffuse' : "textures.full/tracks/cityscape/scrapers1/diffuse.jpg",
'track.cityscape.scrapers1.specular': "textures.full/tracks/cityscape/scrapers1/specular.jpg",
'track.cityscape.scrapers1.normal' : "textures.full/tracks/cityscape/scrapers1/normal.jpg",
'track.cityscape.scrapers2.diffuse' : "textures.full/tracks/cityscape/scrapers2/diffuse.jpg",
'track.cityscape.scrapers2.specular': "textures.full/tracks/cityscape/scrapers2/specular.jpg",
'track.cityscape.scrapers2.normal' : "textures.full/tracks/cityscape/scrapers2/normal.jpg",
'track.cityscape.start.diffuse' : "textures.full/tracks/cityscape/start/diffuse.jpg",
'track.cityscape.start.specular' : "textures.full/tracks/cityscape/start/specular.jpg",
'track.cityscape.start.normal' : "textures.full/tracks/cityscape/start/normal.jpg",
'track.cityscape.start.banner' : "textures.full/tracks/cityscape/start/start.jpg",
'bonus.base.diffuse' : "textures.full/bonus/base/diffuse.jpg",
'bonus.base.normal' : "textures.full/bonus/base/normal.jpg",
'bonus.base.specular' : "textures.full/bonus/base/specular.jpg"
},
texturesCube: {
'skybox.dawnclouds' : "textures/skybox/dawnclouds/%1.jpg"
'skybox.dawnclouds' : "textures.full/skybox/dawnclouds/%1.jpg"
},
geometries: {
'bonus.base' : "geometries/bonus/base/base.js",
@@ -123,21 +127,21 @@ bkcore.hexgl.tracks.Cityscape = {
'track.cityscape.bonus.speed' : "geometries/tracks/cityscape/bonus/speed.js"
},
analysers: {
'track.cityscape.collision' : "textures/tracks/cityscape/collision.png",
'track.cityscape.height' : "textures/tracks/cityscape/height.png"
'track.cityscape.collision' : "textures.full/tracks/cityscape/collision.png",
'track.cityscape.height' : "textures.full/tracks/cityscape/height.png"
},
images: {
'hud.bg' : "textures/hud/hud-bg.png",
'hud.speed' : "textures/hud/hud-fg-speed.png",
'hud.shield' : "textures/hud/hud-fg-shield.png"
'hud.bg' : "textures.full/hud/hud-bg.png",
'hud.speed' : "textures.full/hud/hud-fg-speed.png",
'hud.shield' : "textures.full/hud/hud-fg-shield.png"
}
});
}
},
buildMaterials: function(quality)
buildMaterials: function(quality, mobile)
{
if(this.quality < 1) // LOW
if((quality < 1 && !mobile) || quality < 2) // LOW
{
this.materials.track = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "track.cityscape.diffuse"),
@@ -159,7 +163,7 @@ bkcore.hexgl.tracks.Cityscape = {
});
this.materials.booster = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "booster.diffuse"),
map: this.lib.get("textures", "booster.diffuse"),
transparent: true
});
@@ -175,11 +179,11 @@ bkcore.hexgl.tracks.Cityscape = {
this.materials.start = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "track.cityscape.start.diffuse"),
ambient: 0xcccccc
ambient: 0xcccccc
});
this.materials.startBanner = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "track.cityscape.start.banner"),
map: this.lib.get("textures", "track.cityscape.start.banner"),
transparent: false
});
}
@@ -221,7 +225,7 @@ bkcore.hexgl.tracks.Cityscape = {
});
this.materials.booster = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "booster.diffuse"),
map: this.lib.get("textures", "booster.diffuse"),
transparent: true
});
@@ -260,13 +264,13 @@ bkcore.hexgl.tracks.Cityscape = {
});
this.materials.startBanner = new THREE.MeshBasicMaterial({
map: this.lib.get("textures", "track.cityscape.start.banner"),
map: this.lib.get("textures", "track.cityscape.start.banner"),
transparent: false
});
}
},
buildScenes: function(ctx, quality)
buildScenes: function(ctx, quality, mobile)
{
// IMPORTANT
this.analyser = this.lib.get("analysers", "track.cityscape.collision");
@@ -297,9 +301,9 @@ bkcore.hexgl.tracks.Cityscape = {
var ambient = 0xbbbbbb, diffuse = 0xffffff, specular = 0xffffff, shininess = 42, scale = 23;
// MAIN SCENE
// MAIN SCENE
var camera = new THREE.PerspectiveCamera( 70, ctx.width / ctx.height, 1, 60000 );
var scene = new THREE.Scene();
scene.add( camera );
scene.add( new THREE.AmbientLight( ambient ) );
@@ -309,7 +313,7 @@ bkcore.hexgl.tracks.Cityscape = {
sun.position.set( -4000, 1200, 1800 );
sun.lookAt(new THREE.Vector3());
if(quality > 0)
if(quality > 0 && !mobile)
{
sun.castShadow = true;
sun.shadowCameraNear = 50;
@@ -328,15 +332,15 @@ bkcore.hexgl.tracks.Cityscape = {
// SHIP
var ship = ctx.createMesh(scene, this.lib.get("geometries", "ship.feisar"), -1134*2, 10, -443*2, this.materials.ship);
var booster = ctx.createMesh(ship, this.lib.get("geometries", "booster"), 0, 0.665, -3.8, this.materials.booster);
booster.depthWrite = false;
var boosterSprite = new THREE.Sprite({
map: this.lib.get("textures", "booster.sprite"),
blending: THREE.AdditiveBlending,
useScreenCoordinates: false,
color: 0xffffff
var boosterSprite = new THREE.Sprite({
map: this.lib.get("textures", "booster.sprite"),
blending: THREE.AdditiveBlending,
useScreenCoordinates: false,
color: 0xffffff
});
boosterSprite.scale.set(0.02, 0.02, 0.02);
boosterSprite.mergeWith3D = false;
@@ -348,7 +352,7 @@ bkcore.hexgl.tracks.Cityscape = {
ship.add(boosterLight);
// SHIP CONTROLS
var shipControls = new bkcore.hexgl.ShipControls(ctx.document);
var shipControls = new bkcore.hexgl.ShipControls(ctx);
shipControls.collisionMap = this.lib.get("analysers", "track.cityscape.collision");
shipControls.collisionPixelRatio = 2048.0 / 6000.0;
shipControls.collisionDetection = true;
@@ -369,7 +373,7 @@ bkcore.hexgl.tracks.Cityscape = {
boosterLight: boosterLight,
useParticles: false
};
if(quality > 0)
if(quality > 0 && !mobile)
{
fxParams.textureCloud = this.lib.get("textures", "cloud");
fxParams.textureSpark = this.lib.get("textures", "spark");
@@ -406,7 +410,7 @@ bkcore.hexgl.tracks.Cityscape = {
var dt = delta/16.6;
this.objects.components.shipControls.update(dt);
this.objects.components.shipEffects.update(dt);
this.objects.components.cameraChase.update(dt, this.objects.components.shipControls.getSpeedRatio());
@@ -421,10 +425,10 @@ bkcore.hexgl.tracks.Cityscape = {
this.objects.components.cameraChase.cameraCube.rotation.copy(c.rotation);*/
this.objects.composers.game.render(dt);
this.objects.hud.update(
this.objects.components.shipControls.getRealSpeed(100),
this.objects.components.shipControls.getRealSpeedRatio(),
this.objects.components.shipControls.getShield(100),
if(this.objects.hud) this.objects.hud.update(
this.objects.components.shipControls.getRealSpeed(100),
this.objects.components.shipControls.getRealSpeedRatio(),
this.objects.components.shipControls.getShield(100),
this.objects.components.shipControls.getShieldRatio()
);
if(this.objects.components.shipControls.getShieldRatio() < 0.2)
@@ -442,4 +446,4 @@ bkcore.hexgl.tracks.Cityscape = {
lowFPS: 0
});
}
}
}
+74
View File
@@ -0,0 +1,74 @@
CACHE MANIFEST
# v1.0.1-ffos-201302192207
# HexGL by Thibaut Despoulain (BKcore.com)
index.html
css/BebasNeue-webfont.eot
css/BebasNeue-webfont.ttf
css/fonts.css
css/mobile-controls-2.jpg
css/mobile-over.jpg
css/BebasNeue-webfont.svg
css/BebasNeue-webfont.woff
css/mobile-controls-1.jpg
css/mobile.jpg
css/touchcontroller.css
libs/Three.dev.js
libs/ShaderExtras.js
libs/postprocessing/EffectComposer.js
libs/postprocessing/RenderPass.js
libs/postprocessing/BloomPass.js
libs/postprocessing/ShaderPass.js
libs/postprocessing/MaskPass.js
libs/Detector.js
libs/Stats.js
libs/DAT.GUI.min.js
bkcore.coffee/controllers/TouchController.js
bkcore.coffee/controllers/OrientationController.js
bkcore/Timer.js
bkcore/ImageData.js
bkcore/Utils.js
bkcore/threejs/RenderManager.js
bkcore/threejs/Shaders.js
bkcore/threejs/Particles.js
bkcore/threejs/Loader.js
bkcore/hexgl/HUD.js
bkcore/hexgl/RaceData.js
bkcore/hexgl/ShipControls.js
bkcore/hexgl/ShipEffects.js
bkcore/hexgl/CameraChase.js
bkcore/hexgl/Gameplay.js
bkcore/hexgl/tracks/Cityscape.js
bkcore/hexgl/HexGL.js
geometries/bonus/base/base.js
geometries/booster/booster.js
geometries/ships/feisar/feisar.js
geometries/tracks/cityscape/scrapers1.js
geometries/tracks/cityscape/scrapers2.js
geometries/tracks/cityscape/startbanner.js
geometries/tracks/cityscape/start.js
geometries/tracks/cityscape/track.js
geometries/tracks/cityscape/bonus/speed.js
textures/tracks/cityscape/collision.png
textures/tracks/cityscape/diffuse.jpg
textures/tracks/cityscape/height.png
textures/tracks/cityscape/start/diffuse.jpg
textures/tracks/cityscape/start/start.jpg
textures/tracks/cityscape/scrapers2/diffuse.jpg
textures/tracks/cityscape/scrapers1/diffuse.jpg
textures/skybox/dawnclouds/nx.jpg
textures/skybox/dawnclouds/ny.jpg
textures/skybox/dawnclouds/nz.jpg
textures/skybox/dawnclouds/px.jpg
textures/skybox/dawnclouds/pz.jpg
textures/skybox/dawnclouds/py.jpg
textures/ships/feisar/diffuse.jpg
textures/ships/feisar/booster/booster.png
textures/ships/feisar/booster/boostersprite.jpg
textures/bonus/base/diffuse.jpg
textures/particles/cloud.png
textures/particles/damage.png
textures/particles/spark.png
textures/hud/hud-bg.png
textures/hud/hex.jpg
textures/hud/hud-fg-speed.png
textures/hud/hud-fg-shield.png
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

+234
View File
@@ -0,0 +1,234 @@
@-webkit-keyframes anim {
0% { -webkit-transform: translateY(10px) }
50% { -webkit-transform: translateY(-10px) }
100% { -webkit-transform: translateY(10px) }
}
@-moz-keyframes anim {
0% { -moz-transform: translateY(10px) }
50% { -moz-transform: translateY(-10px) }
100% { -moz-transform: translateY(10px) }
}
@-o-keyframes anim {
0% { -o-transform: translateY(10px) }
50% { -o-transform: translateY(-10px) }
100% { -o-transform: translateY(10px) }
}
@keyframes anim {
0% { transform: translateY(10px) }
50% { transform: translateY(-10px) }
100% { transform: translateY(10px) }
}
@-webkit-keyframes animbg {
0% { -webkit-transform: translateY(5px) }
50% { -webkit-transform: translateY(-5px) }
100% { -webkit-transform: translateY(5px) }
}
@-moz-keyframes animbg {
0% { -moz-transform: translateY(5px) }
50% { -moz-transform: translateY(-5px) }
100% { -moz-transform: translateY(5px) }
}
@-o-keyframes animbg {
0% { -o-transform: translateY(5px) }
50% { -o-transform: translateY(-5px) }
100% { -o-transform: translateY(5px) }
}
@keyframes animbg {
0% { transform: translateY(5px) }
50% { transform: translateY(-5px) }
100% { transform: translateY(5px) }
}
* {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
html, body {
overflow: hidden;
font-family: 'BebasNeueRegular', arial, sans-serif;
background: white;
}
#global {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
background: url('bg.jpg') no-repeat center center fixed;
overflow: hidden;
text-align: center;
-moz-background-size: cover;
-webkit-background-size: cover;
-ms-background-size: cover;
-o-background-size: cover;
background-size: cover;
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation: animbg 3s ease-in-out infinite;
-moz-animation: animbg 3s ease-in-out infinite;
-o-animation: animbg 3s ease-in-out infinite;
animation: animbg 3s ease-in-out infinite;
}
#title {
position: absolute;
top: 5%;
right: 0;
bottom: 5%;
left: 0;
z-index: 1;
background: url('title.png') no-repeat center top;
-moz-background-size: auto 50%;
-webkit-background-size: auto 50%;
-ms-background-size: auto 50%;
-o-background-size: auto 50%;
background-size: auto 50%;
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation: anim 3s ease-in-out infinite;
-moz-animation: anim 3s ease-in-out infinite;
-o-animation: anim 3s ease-in-out infinite;
animation: anim 3s ease-in-out infinite;
}
#menucontainer {
position: absolute;
top: auto;
right: 0;
bottom: 10%;
left: 0;
z-index: 2;
}
#menu {
position: relative;
display: block;
width: 100%;
max-width: 600px;
margin: auto;
text-align: center;
font-size: 2em;
font-family: 'BebasNeueRegular', arial, sans-serif;
text-transform: uppercase;
color: #4190bb;
}
#menu div {
padding: 0.1em 0 0 0;
margin: 0 0;
cursor: pointer;
-moz-text-shadow: 0 0 10px rgb(255,255,255);
-webkit-text-shadow: 0 0 10px rgb(255,255,255);
text-shadow: 0 0 10px rgb(255,255,255);
-webkit-transform-origin-y: 50%;
-webkit-transition: -webkit-transform 0.2s;
-webkit-transform: translateZ(0px) scaleY(0.8);
transform-origin-y: 50%;
transition: transform 0.2s ease;
transform: translateZ(0px) scaleY(0.8);
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjQwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIwLjUiLz4KICAgIDxzdG9wIG9mZnNldD0iNjAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIiBzdG9wLW9wYWNpdHk9IjAuNSIvPgogICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZmZmZmZmIiBzdG9wLW9wYWNpdHk9IjAiLz4KICA8L2xpbmVhckdyYWRpZW50PgogIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+Cjwvc3ZnPg==);
background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.5) 40%, rgba(255,255,255,0.5) 60%, rgba(255,255,255,0) 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(40%,rgba(255,255,255,0.5)), color-stop(60%,rgba(255,255,255,0.5)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.5) 40%,rgba(255,255,255,0.5) 60%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.5) 40%,rgba(255,255,255,0.5) 60%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.5) 40%,rgba(255,255,255,0.5) 60%,rgba(255,255,255,0) 100%); /* IE10+ */
background: linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.5) 40%,rgba(255,255,255,0.5) 60%,rgba(255,255,255,0) 100%); /* W3C */
}
#menu div:hover {
color: #f66439;
-webkit-transform: scaleY(1.0);
transform: scaleY(1.1);
}
#start {
font-size: 1.8em;
color: #f66439;
}
#start:hover {
color: #f8854b !important;
}
#step-2 {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: black;
vertical-align: bottom;
text-align: center;
padding: 20px;
color: white;
font-size: 2em;
background-repeat: no-repeat;
background-position: center center;
-moz-background-size: 60% auto;
-webkit-background-size: 60% auto;
-ms-background-size: 60% auto;
-o-background-size: 60% auto;
background-size: 60% auto;
}
#step-3 {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: black;
}
#step-3 #progressbar {
position: absolute;
top: 0;
right: auto;
bottom: 0;
left: 0;
width: 0%;
background: white;
}
#step-5 {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: black;
text-align: center;
padding: 100px 20px;
color: white;
font-size: 2em;
}
#step-5 #time {
font-size: 4em
}
#leapinfo {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0,0,0,0.8);
color: white;
text-align: center;
font-size: 3em;
padding-top: 200px;
z-index: 999999;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

+8
View File
@@ -0,0 +1,8 @@
* {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
/* make transparent link selection, adjust last value opacity 0 to 1.0 */
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

+101 -97
View File
@@ -1,111 +1,115 @@
<!doctype html>
<html lang="en">
<head>
<title>HexGL dev page</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="css/fonts.css" type="text/css" charset="utf-8">
<style>
body {
background:#ccc;
padding:0;
margin:0;
overflow:hidden;
font-family:georgia;
text-align:center;
color: #666;
}
h1 {color: #666 ; }
a { color:skyblue }
canvas { pointer-events:none; }
#overlay{
position: absolute;
z-index: 9999;
top: 0;
left: 0;
width: 100%;
}
</style>
</head>
<head>
<title>HexGL by BKcore</title>
<meta charset="utf-8">
<meta name="description" content="HexGL is a futuristic racing game built by Thibaut Despoulain (BKcore) using HTML5, Javascript and WebGL. Come challenge your friends on this fast-paced 3D game!">
<meta property="og:title" content="HexGL, the HTML5 futuristic racing game." />
<meta property="og:type" content="game" />
<meta property="og:url" content="http://hexgl.bkcore.com/" />
<meta property="og:image" content="http://hexgl.bkcore.com/image.png" />
<meta property="og:site_name" content="HexGL by BKcore" />
<meta property="fb:admins" content="1482017639" />
<link rel="icon" href="http://hexgl.bkcore.com/favicon.png" type="image/png">
<link rel="shortcut icon" href="http://hexgl.bkcore.com/favicon.png" type="image/png">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" href="css/multi.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="css/fonts.css" type="text/css" charset="utf-8">
<style>
body {
padding:0;
margin:0;
}
canvas { pointer-events:none; width: 100%;}
#overlay{
position: absolute;
z-index: 9999;
top: 0;
left: 0;
width: 100%;
}
</style>
<script type="text/javascript">
//analytics
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-26274524-4']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<div id="overlay"></div>
<div id="main"></div>
<body>
<div id="step-1">
<div id="global"></div>
<div id="title">
</div>
<div id="menucontainer">
<div id="menu">
<div id="start">Start</div>
<div id="s-controlType">Controls: LeapMotion</div>
<div id="s-quality">Quality: High</div>
<div id="s-platform">Platform: Desktop</div>
<div id="s-godmode">Godmode: On</div>
</div>
</div>
</div>
<div id="step-2" style="display: none">
<div id="ctrl-help">Click/Touch to continue.</div>
</div>
<div id="step-3" style="display: none">
<div id="progressbar"></div>
</div>
<div id="step-4" style="display: none">
<div id="overlay"></div>
<div id="main"></div>
</div>
<div id="step-5" style="display: none">
<div id="time"></div>
<div id="ctrl-help">Click/Touch to continue.</div>
</div>
<script src="js/Three.dev.js"></script>
<script src="js/ShaderExtras.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/BloomPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/Detector.js"></script>
<script src="js/Stats.js"></script>
<script src="js/DAT.GUI.min.js"></script>
<div id="leapinfo" style="display: none"></div>
<script src="bkcore/Timer.js"></script>
<script src="bkcore/ImageData.js"></script>
<script src="bkcore/Utils.js"></script>
<script src="libs/leap-0.4.1.min.js"></script>
<script src="libs/Three.dev.js"></script>
<script src="libs/ShaderExtras.js"></script>
<script src="libs/postprocessing/EffectComposer.js"></script>
<script src="libs/postprocessing/RenderPass.js"></script>
<script src="libs/postprocessing/BloomPass.js"></script>
<script src="libs/postprocessing/ShaderPass.js"></script>
<script src="libs/postprocessing/MaskPass.js"></script>
<script src="libs/Detector.js"></script>
<script src="libs/Stats.js"></script>
<script src="libs/DAT.GUI.min.js"></script>
<script src="bkcore/threejs/RenderManager.js"></script>
<script src="bkcore/threejs/Shaders.js"></script>
<script src="bkcore/threejs/Particles.js"></script>
<script src="bkcore/threejs/Loader.js"></script>
<script src="bkcore.coffee/controllers/TouchController.js"></script>
<script src="bkcore.coffee/controllers/OrientationController.js"></script>
<script src="bkcore/hexgl/HUD.js"></script>
<script src="bkcore/hexgl/ShipControls.js"></script>
<script src="bkcore/hexgl/ShipEffects.js"></script>
<script src="bkcore/hexgl/CameraChase.js"></script>
<script src="bkcore/hexgl/Gameplay.js"></script>
<script src="bkcore/Timer.js"></script>
<script src="bkcore/ImageData.js"></script>
<script src="bkcore/Utils.js"></script>
<script src="bkcore/hexgl/tracks/Cityscape.js"></script>
<script src="bkcore/threejs/RenderManager.js"></script>
<script src="bkcore/threejs/Shaders.js"></script>
<script src="bkcore/threejs/Particles.js"></script>
<script src="bkcore/threejs/Loader.js"></script>
<script src="bkcore/hexgl/HexGL.js"></script>
<script src="bkcore/hexgl/HUD.js"></script>
<script src="bkcore/hexgl/RaceData.js"></script>
<script src="bkcore/hexgl/ShipControls.js"></script>
<script src="bkcore/hexgl/ShipEffects.js"></script>
<script src="bkcore/hexgl/CameraChase.js"></script>
<script src="bkcore/hexgl/Gameplay.js"></script>
<script>
<script src="bkcore/hexgl/tracks/Cityscape.js"></script>
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
<script src="bkcore/hexgl/HexGL.js"></script>
var container, hudcontainer;
<script src="launch.js"></script>
var hexGL;
function init() {
hudcontainer = document.getElementById("overlay");
container = document.getElementById("main");
hexGL = new bkcore.hexgl.HexGL({
document: document,
width: SCREEN_WIDTH,
height: SCREEN_HEIGHT,
container: container,
overlay: overlay,
quality: bkcore.Utils.getURLParameter('quality'),
difficulty: bkcore.Utils.getURLParameter('difficulty'),
half: bkcore.Utils.getURLParameter('half'),
track: 'Cityscape'
});
hexGL.load({
onLoad: function(){
console.log("ALL LOADED.");
hexGL.init();
hexGL.start();
},
onError: function(s){
console.log("ERROR ON "+s+".");
},
onProgress: function(p, t, n)
{
console.log("LOADED "+t+" : "+n+" ( "+p.loaded+" / "+p.total+" ).");
}
});
}
init();
</script>
</body>
</body>
</html>
+59
View File
@@ -0,0 +1,59 @@
$ = (_) -> document.getElementById _
init = (controlType, quality, platform, godmode) ->
hexGL = new bkcore.hexgl.HexGL(
document: document
width: window.innerWidth
height: window.innerHeight
container: $ 'main'
overlay: $ 'overlay'
gameover: $ 'step-5'
quality: quality
difficulty: 0,
half: (platform is 1 and quality < 1)
mobile: platform is 1
hud: platform is 0
controlType: controlType
godmode: godmode
track: 'Cityscape'
)
window.hexGL=hexGL
progressbar = $ 'progressbar'
hexGL.load(
onLoad: ->
console.log 'LOADED.'
hexGL.init()
$('step-3').style.display = 'none'
$('step-4').style.display = 'block'
hexGL.start()
onError: (s) ->
console.error "Error loading #{ s }."
onProgress: (p, t, n) ->
console.log("LOADED "+t+" : "+n+" ( "+p.loaded+" / "+p.total+" ).")
progressbar.style.width = "#{ p.loaded / p.total * 100 }%"
)
u = bkcore.Utils.getURLParameter
s = [
['controlType', ['KEYBOARD', 'TOUCH', 'LEAP MOTION CONTROLLER'], 0, 0, 'Controls: ']
['quality', ['LOW', 'MID', 'HIGH'], 2, 2, 'Quality: ']
['platform', ['DESKTOP', 'MOBILE'], 0, 0, 'Platform: ']
['godmode', ['OFF', 'ON'], 0, 1, 'Godmode: ']
]
for a in s
do(a)->
a[3] = u(a[0]) ? a[2]
e = $ "s-#{a[0]}"
(f = -> e.innerHTML = a[4]+a[1][a[3]])()
e.onclick = -> f(a[3] = (a[3]+1)%a[1].length)
$('start').onclick = ->
$('step-1').style.display = 'none'
$('step-2').style.display = 'block'
$('step-2').style.backgroundImage = "url(css/help-#{s[0][3]}.png)"
$('step-2').onclick = ->
$('step-2').style.display = 'none'
$('step-3').style.display = 'block'
init s[0][3], s[1][3], s[2][3], s[3][3]
$('step-5').onclick = ->
window.location.reload()
+83
View File
@@ -0,0 +1,83 @@
// Generated by CoffeeScript 1.6.3
(function() {
var $, a, init, s, u, _fn, _i, _len;
$ = function(_) {
return document.getElementById(_);
};
init = function(controlType, quality, platform, godmode) {
var hexGL, progressbar;
hexGL = new bkcore.hexgl.HexGL({
document: document,
width: window.innerWidth,
height: window.innerHeight,
container: $('main'),
overlay: $('overlay'),
gameover: $('step-5'),
quality: quality,
difficulty: 0,
half: platform === 1 && quality < 1,
mobile: platform === 1,
hud: platform === 0,
controlType: controlType,
godmode: godmode,
track: 'Cityscape'
});
window.hexGL = hexGL;
progressbar = $('progressbar');
return hexGL.load({
onLoad: function() {
console.log('LOADED.');
hexGL.init();
$('step-3').style.display = 'none';
$('step-4').style.display = 'block';
return hexGL.start();
},
onError: function(s) {
return console.error("Error loading " + s + ".");
},
onProgress: function(p, t, n) {
console.log("LOADED " + t + " : " + n + " ( " + p.loaded + " / " + p.total + " ).");
return progressbar.style.width = "" + (p.loaded / p.total * 100) + "%";
}
});
};
u = bkcore.Utils.getURLParameter;
s = [['controlType', ['KEYBOARD', 'TOUCH', 'LEAP MOTION CONTROLLER'], 0, 0, 'Controls: '], ['quality', ['LOW', 'MID', 'HIGH'], 2, 2, 'Quality: '], ['platform', ['DESKTOP', 'MOBILE'], 0, 0, 'Platform: '], ['godmode', ['OFF', 'ON'], 0, 1, 'Godmode: ']];
_fn = function(a) {
var e, f, _ref;
a[3] = (_ref = u(a[0])) != null ? _ref : a[2];
e = $("s-" + a[0]);
(f = function() {
return e.innerHTML = a[4] + a[1][a[3]];
})();
return e.onclick = function() {
return f(a[3] = (a[3] + 1) % a[1].length);
};
};
for (_i = 0, _len = s.length; _i < _len; _i++) {
a = s[_i];
_fn(a);
}
$('start').onclick = function() {
$('step-1').style.display = 'none';
$('step-2').style.display = 'block';
return $('step-2').style.backgroundImage = "url(css/help-" + s[0][3] + ".png)";
};
$('step-2').onclick = function() {
$('step-2').style.display = 'none';
$('step-3').style.display = 'block';
return init(s[0][3], s[1][3], s[2][3], s[3][3]);
};
$('step-5').onclick = function() {
return window.location.reload();
};
}).call(this);
View File
View File
+717
View File
File diff suppressed because one or more lines are too long
+212
View File
@@ -0,0 +1,212 @@
var pgli = pgli || {};
pgli.App = gamecore.Base.extend("App",
{ // static
},
{ // instance
project: null,
moduleList: null,
editor: null,
diagram: null,
preview: null,
nodeCount: 0,
console: null,
debug: 2, // 0:none, 1:inapp, 2:console, 3:both
init: function(domDiagram, domModuleList, domEditor, domPreview)
{
var self = this;
this.moduleList = new pgli.ui.ModuleList(domModuleList);
this.editor = ace.edit(domEditor);
this.editor.setFontSize("16px");
this.editor.setTheme("ace/theme/monokai");
this.editor.getSession().setMode("ace/mode/json");
this.diagram = new pgli.diagram.Diagram(domDiagram, 30);
this.preview = new pgli.render.CanvasRenderer(domPreview);
this.console = $('#console-text');
this.bindEvents();
pgli.lang.Parser.debug = self.debug;
window.trace = function(args)
{
if(!self.debug) return;
for(var i=0, len=arguments.length; i<len; ++i)
{
if(self.debug < 2) console.log(arguments[i]);
if(self.debug == 1 || self.debug == 3) return;
self.console.append(arguments[i].toString()+"\n");
self.console.scrollTop(
self.console[0].scrollHeight - self.console.height()
);
};
}
window.clearTrace = function()
{
self.console.text("");
}
},
bindEvents: function()
{
var self = this;
$(window).on('resize', function(){ return self.resize.call(self); });
$(document).bind('keydown', function(e){ return self.onKeyDown.call(self,e); });
$('#modules').on('drop', function(e) { return self.onDropEvent.call(self,e); });
//window.addEventListener("drop",function(e){ return self.onDropEvent.call(self,e); }) ;
},
bindProject: function(project)
{
this.nodeCount = 0;
this.project = project;
this.project.setAppInstance(this);
this.moduleList.bindProject(project);
this.preview.bindProject(project);
this.draw();
},
draw: function()
{
this.moduleList != undefined && this.moduleList.draw();
//this.preview.draw();
},
showInEditor: function(module)
{
this.project.setActiveFile(module);
this.editor.getSession().setValue(this.project.files.get(module));
},
getEditorContent: function()
{
return this.editor.getSession().getValue();
},
addDiagramNode: function(key, module)
{
this.diagram.addNode(new pgli.diagram.Node(key, module, 50 + 160 * this.nodeCount++, 50));
},
resize: function()
{
this.diagram.resize();
this.preview.resize();
},
saveModule: function()
{
for(var i=0; i<this.project.keys.length;i++)
{
var name = this.project.keys[i]
var fileToSave = this.project.getModule(name);
var jsonData = {file :'files/'+name ,obj: fileToSave};
$.ajax({
url:"/",
type:"POST",
data: JSON.stringify(jsonData),
contentType: "application/json; charset=utf-8",
dataType: "text",
success:function(a)
{
console.log("AJAX POST OK: ", a);
},
error: function(a)
{
console.log("AJAX POST ERROR: ", a);
}
});
console.log("STARTED AJAX REQUEST");
}
},
onKeyDown: function(e)
{
if(e.keyCode==117)
{
this.updateDiagram();
e.preventDefault();
return false;
}
else if(e.keyCode==118)
{
this.preview.draw();
e.preventDefault();
return false;
}
else if(e.keyCode==119)
{
this.saveModule();
e.preventDefault();
return false;
}
},
updateDiagram:function()
{
this.project.rememberActiveFile();
for(var i = 0, len = this.project.keys.length; i<len; i++)
{
var object = pgli.lang.Parser.parseModule(this.project.files.get(this.project.keys[i]));
this.project.modules.put(this.project.keys[i], object);
this.diagram.getNode(this.project.keys[i]).module = object;
//this.getNode.updateModule()...
}
this.diagram.draw();
},
onDropEvent: function(e)
{
trace("#Parsing dropped file(s)...");
e.preventDefault();
var self = this;
var length = e.originalEvent.dataTransfer.files.length;
for (var i = 0; i < length; i++)
{
var file = e.originalEvent.dataTransfer.files[i];
console.log(file);
fileName = file.name;
if(i == 0 && this.project == null)
{
var path = window.prompt("Please prove project's root path (with trailing slash).", "../files/");
trace("#Opening new project from ["+fileName+"]...");
this.bindProject(new pgli.Project(path+fileName, function(){
self.draw();
self.showInEditor(self.project.root);
}));
}
else
this.project.loadFile(self.project.path+fileName,fileName,true,true);
}
this.draw();
return false;
}
});
+389
View File
@@ -0,0 +1,389 @@
var pgli = pgli || {};
pgli.render = pgli.render || {};
pgli.render.CanvasRenderer = gamecore.Base.extend('CanvasRenderer',
{ // static
SCOPE_KEYWORDS: {
'use': 0,
'repeat': 1,
'comment': 2
},
SCOPE_XFORMS: {
'x': 1,
'y': 2,
'width': 3,
'height': 4,
'scale': 0
},
REPEAT_TYPES: {
'none': 'no-repeat',
'x': 'repeat-x',
'y': 'repeat-y',
'xy': 'repeat'
}
},
{ // instance
dom: null,
container: null,
canvas: null,
context: null,
width: 1024,
height: 768,
project: null,
xform: [],
xseek: -1,
scope: [],
sseek: -1,
resources: {
images: []
},
variables: null,
/**
* Bind a new renderer to given canvas
* @param jQuery.DOMElement domElement [description]
* @param Integer width [description]
* @param Integer height [description]
*/
init: function(domElement)
{
this.dom = domElement;
this.container = $('#'+this.dom);
this.canvas = document.createElement("Canvas");
this.container.append(this.canvas);
this.resize();
this.variables = new gamecore.Hashtable();
},
draw: function()
{
clearTrace();
if(!this.project)
{
trace("#Render failed: No project loaded");
return;
}
trace("#Starting renderer...");
this.context.clearRect(0, 0, this.width, this.height);
var ratio = this.getRenderRatio();
var root = this.project.getRootModule();
var w = root.width*ratio;
var h = root.height*ratio;
var x = Math.round((this.width - w)/2);
var y = Math.round((this.height - h)/2);
this.sreset();
this.xreset(ratio, x, y, w, h);
this.walkModule(root);
},
walkModule: function(module)
{
if(!module) return;
trace("#Render: "+module.name);
if("params" in module) for(p in module.params)
this.sset(p, module.params[p]);
if("vars" in module)
this.actionVars(module.vars);
if("fill" in module)
this.actionFill(module.fill);
if("layers" in module) for(var i=0, len=module.layers.length; i<len; ++i)
{
var layer = module.layers[i];
if("use" in layer)
{
if("repeat" in layer)
{
var iterator;
try {
iterator = pgli.lang.Parser.parseRepeat(layer.repeat, this.sget());
trace("# Found repeat layer ["+iterator.varname+" from "+iterator.start+" to "+iterator.end+"]");
} catch(e) {
trace("(!) Syntax error in repeat expression ["+layer.repeat+"]");
continue;
}
while(iterator.loop())
{
this.sset(iterator.varname, iterator.step);
this.walkLayer(layer);
iterator.next();
}
}
else
{
this.walkLayer(layer);
}
}
}
},
walkLayer: function(layer)
{
var static = pgli.render.CanvasRenderer;
this.xpush();
this.spush();
for(k in layer)
{
if(k in static.SCOPE_KEYWORDS)
continue;
else if(k in static.SCOPE_XFORMS)
{
var val = pgli.lang.Parser.parseExpression(layer[k], this.sget(), this.xget());
if(typeof(val) == "number")
{
val = Number(val)*this.xgetr();
}
else
{
var p = val.search("%");
if(p > 0)
val = (Number(val.substr(O, p))/100)*this.xget()[static.SCOPE_XFORMS[k]];
else continue;
}
if(k == "x" || k == "y")
{
val += this.xget()[static.SCOPE_XFORMS[k]];
}
this.xset(static.SCOPE_XFORMS[k], val);
}
else
this.sset(k, pgli.lang.Parser.parseExpression(layer[k], this.sget()), this.xget());
}
this.walkModule(this.project.getModule(layer.use));
this.spop();
this.xpop();
},
actionVars: function(opts)
{
var static = pgli.render.CanvasRenderer;
for(k in opts)
{
if(k in static.SCOPE_KEYWORDS || k in static.SCOPE_XFORMS)
continue;
else
this.sset(k, pgli.lang.Parser.parseExpression(opts[k], this.sget(), this.xget()));
}
},
actionFill: function(opts)
{
var static = pgli.render.CanvasRenderer;
if(opts.type == "image")
{
//trace("# Fill(image)...")
var image = this.loadImage(
pgli.lang.Parser.parseExpression(opts.value, this.sget(), this.xget())
);
if("repeat" in opts && opts.repeat in static.REPEAT_TYPES)
{
var pattern = this.context.createPattern(image, static.REPEAT_TYPES[opts.repeat]);
this.context.rect(this.xgetx(), this.xgety(), this.xgetw(), this.xgeth());
this.context.fillStyle = pattern;
this.context.fill();
//pattern = null;
}
else
{
this.context.drawImage(image, this.xgetx(), this.xgety(), this.xgetw(), this.xgeth());
}
}
else if(opts.type == "color")
{
//trace("# Fill(color)...")
this.context.beginPath();
this.context.rect(this.xgetx(), this.xgety(), this.xgetw(), this.xgeth());
this.context.fillStyle = pgli.lang.Parser.parseExpression(opts.value, this.sget(), this.xget());
this.context.fill();
}
else if(opts.type == "line")
{
//trace("# Fill(line)...")
this.context.beginPath();
this.context.rect(this.xgetx(), this.xgety(), this.xgetw(), this.xgeth());
this.context.lineWidth = 1;
this.context.strokeStyle = pgli.lang.Parser.parseExpression(opts.value, this.sget(), this.xget());
this.context.stroke();
}
},
loadImage: function(url)
{
var self = this;
if(url in this.resources.images)
{
return this.resources.images[url];
}
else
{
trace("/!\\ Image is loading, will redraw onload.");
var img = new Image();
img.onload = function(){
trace("#Image loaded, redrawing...");
self.draw();
};
img.src = url;
this.resources.images[url] = img;
return img;
}
},
spush: function()
{
this.scope.push(this.cloneScope(this.sget()));
this.sseek++;
},
spop: function()
{
this.sseek--;
return this.scope.pop();
},
sreset: function()
{
this.scope = [{}];
this.sseek = 0;
},
sget: function()
{
return this.scope[this.sseek];
},
sset: function(key, value)
{
this.scope[this.sseek][key] = value;
},
xpush: function(ratio, x, y, h, w)
{
if(ratio == undefined)
if(this.xseek == -1)
this.xform.push([1.0, 0, 0, this.width, this.height]);
else
this.xform.push(this.xget().slice(0))
else
this.xform.push([ratio, x, y, h, w]);
this.xseek++;
},
xpop: function()
{
this.xseek--;
return this.xform.pop();
},
xget: function()
{
return this.xform[this.xseek];
},
xset: function(key, value)
{
this.xform[this.xseek][key] = value;
},
xreset: function(ratio, x, y, h, w)
{
this.xseek = -1;
this.xform = [];
this.xpush(ratio, x, y, h, w);
},
xgetr: function()
{
return this.xform[this.xseek][0];
},
xgetx: function()
{
return this.xform[this.xseek][1];
},
xgety: function()
{
return this.xform[this.xseek][2];
},
xgetw: function()
{
return this.xform[this.xseek][3];
},
xgeth: function()
{
return this.xform[this.xseek][4];
},
cloneScope: (function()
{
return function (obj) { Clone.prototype=obj; return new Clone() };
function Clone(){}
}()),
bindProject: function(project)
{
this.project = project;
},
resize: function()
{
var w = this.container.width();
var h = this.container.height();
var size = 0.9*Math.min(w, h);
this.width = size;
this.height = size;
var $canvas = $(this.canvas);
$canvas.width(size);
$canvas.height(size);
this.canvas.width = size;
this.canvas.height = size;
$canvas.css('marginTop', this.container.height() / 2 - size / 2);
$canvas.css('marginLeft', this.container.width() / 2 - size / 2);
this.context = this.canvas.getContext("2d");
/*var ctx = this.canvas.getContext("2d");
ctx.clearRect(0, 0, size, size);
ctx.canvas.width = ctx.canvas.height = size;
ctx.font = 'bold 40px Arial';
ctx.textAlign = 'center';
ctx.fillStyle = '#444';
ctx.fillText("Preview", size/2, size/2);*/
},
getRenderRatio: function()
{
var root = this.project.getRootModule();
if(!root) return;
return Math.min(this.width/root.width, this.height/root.height);
}
});
+148
View File
@@ -0,0 +1,148 @@
var pgli = pgli || {};
pgli.diagram = pgli.diagram || {};
pgli.diagram.Diagram = gamecore.Base.extend('Diagram',
{ // static
},
{ // instance
dom: null,
container: null,
width: 1024,
height: 768,
timer: null,
redrawDelay: 1000/30,
stage: null,
layers: {
background: null,
nodes: null,
links: null
},
background: null,
links: null,
project: null,
nodes: [],
/**
* Bind a new renderer to given canvas
* @param HTMLElement domContainer [description]
* @param Integer width [description]
* @param Integer height [description]
* @param Integer autoRedraw If defined, sets redraw rate to given FPS
*/
init: function(domContainer, autoRedraw)
{
var self = this;
this.dom = domContainer;
this.container = $('#'+domContainer);
this.width = this.container.width();
this.height = this.container.height();
this.stage = new Kinetic.Stage({
container: this.dom,
width: this.width,
height: this.height
});
this.layers.background = new Kinetic.Layer();
this.layers.nodes = new Kinetic.Layer();
this.layers.links = new Kinetic.Layer();
this.background = new Kinetic.Rect({
width: this.width,
height: this.height,
fill: "#272822"
});
this.layers.background.add(this.background);
this.links = new pgli.diagram.Links(this);
this.layers.links.add(this.links.shape);
this.stage.add(this.layers.background);
this.stage.add(this.layers.nodes);
this.stage.add(this.layers.links);
// Stage drag hack to trigger only on background drag
var self = this;
this.background.on("mousedown", function(){
self.stage.setDraggable(true);
self.stage.on("dragmove", function(){
self.layers.background.setX(-this.getX());
self.layers.background.setY(-this.getY());
});
self.stage.on("dragend", function(){
this.off("dragend");
this.off("dragmove");
this.setDraggable(false);
});
});
if(autoRedraw != undefined && autoRedraw != false && autoRedraw > 0)
{
this.redrawDelay = 1000 / autoRedraw;
this.timer = new bkcore.Timer();
this.timer.start();
this.autoRedraw(true);
}
},
addNode: function(node)
{
this.nodes.push(node);
this.layers.nodes.add(node.shape);
this.layers.nodes.draw();
},
getNode: function(nodeKey)
{
var i = 0, len = this.nodes.length;
while(i < len)
{
if(this.nodes[i].key == nodeKey)
return this.nodes[i];
i++;
}
console.warn("Error in Diagram: Unable to find node["+nodeKey+"]");
return false;
},
draw: function()
{
this.layers.background.draw();
this.layers.nodes.draw();
this.layers.links.draw();
},
autoRedraw: function(keep)
{
var self = this;
if(this.timer.update() > this.redrawDelay)
{
this.timer.start();
this.draw();
}
if(keep)
requestAnimFrame(function(){
self.autoRedraw(true);
});
},
resize: function()
{
this.width = this.container.width();
this.height = this.container.height();
this.stage.setSize(this.width, this.height);
this.background.setSize(this.width, this.height);
this.draw();
}
});
+92
View File
@@ -0,0 +1,92 @@
var pgli = pgli || {};
pgli.lang = pgli.lang || {};
pgli.lang.Iterator = gamecore.Base.extend('Iterator',
// Static
{
MAX_ITERATIONS: 1000,
COMPARATORS: {
"<": 0,
">": 1,
"<=": 2,
">=": 3
},
genComparatorMethod: function(type)
{
switch(type)
{
case 0:
return function(a, b){ return a < b };
case 1:
return function(a, b){ return a > b };
case 2:
return function(a, b){ return a <= b };
case 3:
return function(a, b){ return a >= b };
default:
return function(a, b){ return false };
}
},
genStepMethod: function(type, scope, attr)
{
switch(type)
{
case 1:
case 3:
return function(){ return --scope[attr] };
case 0:
case 2:
default:
return function(){ return ++scope[attr] };
}
}
},
// Instance
{
varname: "i",
start: 0,
end: 1,
comparator: 0,
compMethod: null,
stepMethod: null,
step: 0,
iter: 0,
init: function(name, start, comparator, end)
{
var static = pgli.lang.Iterator;
if(comparator in static.COMPARATORS)
this.comparator = static.COMPARATORS[comparator];
else
this.comparator = static.COMPARATORS["<"];
this.start = start;
this.end = end;
this.step = start;
this.varname = name;
this.compMethod = static.genComparatorMethod(this.comparator);
this.stepMethod = static.genStepMethod(this.comparator, this, "step");
},
loop: function()
{
return (this.iter < pgli.lang.Iterator.MAX_ITERATIONS && this.compMethod(this.step, this.end));
},
next: function()
{
++this.iter;
return this.stepMethod();
},
toString: function()
{
return "Iterator("+this.varname+") "+this.start+" - "+this.step+" - "+this.end;
}
});
+56
View File
@@ -0,0 +1,56 @@
var pgli = pgli || {};
pgli.diagram = pgli.diagram || {};
pgli.diagram.Links = gamecore.Base.extend('Links',
{ // static
bezierOffset: 50
},
{ // instance
diagram: null,
shape: null,
init: function(diagram)
{
var static = pgli.diagram.Links;
var self = this;
this.diagram = diagram;
this.shape = new Kinetic.Shape({
drawFunc: function(ctx){
ctx.beginPath();
for(var i = 0, len = self.diagram.nodes.length; i < len; i++)
{
var node = self.diagram.nodes[i];
if(! ("layers" in node.module)) continue;
for(var j = 0, _len = node.module.layers.length; j < _len; j++)
{
if(! ("use" in node.module.layers[j])) continue;
var start = node.getLayerSlot(j);
var tNode = self.diagram.getNode(node.module.layers[j].use);
if(!tNode) continue;
var end = tNode.getSlot();
ctx.moveTo(start[0], start[1]);
ctx.bezierCurveTo(
start[0]+static.bezierOffset, start[1],
end[0]-static.bezierOffset, end[1],
end[0], end[1]);
}
}
this.stroke(ctx);
},
x: 0,
y: 0,
stroke: "#999",
strokeWidth: 3,
lineCap: "round"
});
}
});
+47
View File
@@ -0,0 +1,47 @@
var pgli = pgli || {};
pgli.ui = pgli.ui || {};
pgli.ui.ModuleList = gamecore.Base.extend('ModuleList',
{ // static
tplModuleItem: "<li data-path='$path'>$name</li>",
tplModuleList: "<ul data-path='$path'>$list</ul>"
},
{ // instance
project: null,
container: null,
init: function(domContainer)
{
this.container = $('#'+domContainer);
this.container.on("click", "li", {object: this}, this.onModuleClick);
},
bindProject: function(project)
{
this.project = project;
},
draw: function()
{
var static = pgli.ui.ModuleList;
var modules = "";
var module = null;
if(!this.project.isEmpty()) for(var i = 0, len = this.project.getModulesCount(); i < len; ++i)
{
key = this.project.getModuleKey(i);
module = this.project.getModule(key);
modules += static.tplModuleItem.replace("$path", key).replace("$name", key);
}
this.container.html(static.tplModuleList.replace("$list", modules));
},
onModuleClick: function(event)
{
event.data.object.project.rememberActiveFile();
event.data.object.project.getAppInstance().showInEditor($(this).attr("data-path"));
event.data.object.container.find('li').removeClass('active');
$(this).addClass('active');
}
});
+122
View File
@@ -0,0 +1,122 @@
var pgli = pgli || {};
pgli.diagram = pgli.diagram || {};
pgli.diagram.Node = gamecore.Base.extend('Node',
{ // static
layersWidth: 20,
layersMargin: 20,
layersHeight: 16,
headerHeight: 40,
slotX: 10,
slotY: 14,
slotRadius: 6
},
{ // instance
module: null,
key: null,
shape: null,
background: null,
name: null,
layers: null,
slot: null,
sockets: [],
width: 150,
height: 200,
init: function(key, module, x, y)
{
var static = pgli.diagram.Node;
this.key = key;
this.module = module;
this.shape = new Kinetic.Group({
x: x == undefined ? 0 : x,
y: y == undefined ? 0 : y,
draggable: true
});
var layerCount = (module.layers != undefined ? module.layers.length : 0) + 1;
this.height = static.headerHeight + layerCount * static.layersHeight;
this.background = new Kinetic.Rect({
x: 0,
y: 0,
width: this.width,
height: this.height,
fill: "#222",
stroke: "#000",
strokeWidth: 0.5,
shadow: {
color: "black",
blur: 6,
offset: [0, 0],
opacity: 0.5
}
});
this.layers = new Kinetic.Shape({
drawFunc: function(ctx){
ctx.beginPath();
for(var i=0, len = this.attrs.count; i < len; ++i)
ctx.arc(10, 10+i*static.layersHeight, static.slotRadius, 0, Math.PI*2, true);
ctx.closePath();
this.fill(ctx);
},
x: this.width-static.layersWidth,
y: static.layersMargin,
count: layerCount,
fill: "#111"
});
this.slot = new Kinetic.Circle({
x: static.slotX,
y: static.slotY,
radius: static.slotRadius,
fill: "#111"
});
this.name = new Kinetic.Text({
x: 20,
y: 6,
text: module.name,
fontSize: 13,
fontFamily: "Ubuntu Mono",
textFill: "#aaa"
});
this.shape.on('mousedown', function(){
this.moveToTop();
});
this.shape.add(this.background);
this.shape.add(this.name);
this.shape.add(this.layers);
this.shape.add(this.slot);
},
updateLayers: function()
{
var layerCount = (module.layers != undefined ? module.layers.length : 0) + 1;
this.layers.attrs.count = layerCount;
},
getSlot: function()
{
var static = pgli.diagram.Node;
return [this.shape.getX()+static.slotX,
this.shape.getY()+static.slotY];
},
getLayerSlot: function(index)
{
var static = pgli.diagram.Node;
return [this.shape.getX()+this.width-static.layersWidth/2,
this.shape.getY()+10+index*static.layersHeight+static.layersMargin];
}
});
+140
View File
@@ -0,0 +1,140 @@
var pgli = pgli || {};
pgli.lang = pgli.lang || {};
pgli.lang.Parser = gamecore.Base.extend('Parser',
{
xStruct: {
"scale": 0,
"x": 1,
"y": 2,
"width": 3,
"height": 4
},
patternVar:/\@(\w+)/g,
patternMethod: /\#(\w+)(\(([^\)]+)\))/g,
debug: 1,
parseExpression: function(string, scope, xform)
{
static = pgli.lang.Parser;
var self = this;
var orig = string;
var s = (scope !== null && typeof(scope) !== "undefined")
var x = (xform !== null && typeof(xform) !== "undefined" && xform.length > 0)
if(typeof(string) != "string")
return string;
if(scope != undefined)
{
string = string.replace(this.patternVar,function(match,varName)
{
if(x && varName in static.xStruct)
return xform[static.xStruct[varName]];
else if(s && varName in scope)
return scope[varName]
else
return 0
});
}
string = string.replace(this.patternMethod,function(match,methodName,a,params)
{
if(self.debug < 2) console.log(arguments);
return self.execFunction(methodName, params);
});
if(self.debug < 2) console.log("#Parsed expr: '"+string+"' from '"+orig+"'");
try {
return eval(string);
} catch (e) {
return string;
}
},
parseRepeat: function(string, scope)
{
var items = string.split(" ");
if(items.length != 4)
throw "Syntax error in repeat expression";
if(self.debug < 2) console.warn(items[0].substr(1))
if(self.debug < 2) console.warn(Number(this.parseExpression(items[1], scope)))
if(self.debug < 2) console.warn(items[2])
if(self.debug < 2) console.warn(Number(this.parseExpression(items[3], scope)))
return new pgli.lang.Iterator(
items[0].substr(1),
Number(this.parseExpression(items[1], scope)),
items[2],
Number(this.parseExpression(items[3], scope))
);
},
parseModule: function(string)
{
try
{
return JSON.parse(string);
}
catch(e)
{
trace("(!) Syntax error in module.");
return {error:"unable to parse module"};
}
},
execFunction: function(methodName, params)
{
var hasP = params != undefined;
var p = hasP ? params.replace(" ", "").split(',') : [];
if(methodName == "random")
{
var r = Math.random();
if(hasP && p.length == 2) try
{
var min = eval(p[0]);
var max = eval(p[1]);
r = Math.round(r * (max-min) + min);
} catch(e) {
console.warn('Bad method format: '+methodName+' / '+params);
return 0;
}
return r;
}
else if(methodName == "mod")
{
if(hasP && p.length == 2) try
{
var base = eval(p[0]);
var div = eval(p[1]);
if(div == 0) throw "Divide by 0";
return Math.floor(base/div);
} catch(e) {
console.warn('Bad method format: '+methodName+' / '+params);
}
return 0;
}
else
{
console.warn("Unsupported method : "+methodName);
return 0;
}
}
},
{
});
+162
View File
@@ -0,0 +1,162 @@
var pgli = pgli || {};
pgli.Project = gamecore.Base.extend('Project',
{
patternRoot: /\/([a-z]+\.pmod)/ig,
patternPath: /([a-z\/]+\/)[a-z]+\.pmod/ig
},
{
appInstance: null,
modules: null,
activeFile: null,
files:null,
keys :[],
name: "default",
path: "/files/",
root: "default.pmod",
diagram: null,
loadingQueue: [],
onLoad: function() { console.log("Project loaded."); },
init : function(projectFile, onLoad)
{
this.onLoad = onLoad;
this.modules = new gamecore.Hashtable();
this.files = new gamecore.Hashtable();
this.path = pgli.Project.patternPath.exec(projectFile)[1];
this.root = pgli.Project.patternRoot.exec(projectFile)[1];
var self = this;
this.loadFile(projectFile,this.root,true,true);
},
loadFile: function(path,name,doDependencies,doDiagram)
{
trace("#Loading ["+name+"].");
var self = this;
var request = $.ajax({
url: path,
type: 'get',
dataType: "text",
})
.success(function(data)
{
self.files.put(name, data);
self.keys.push(name);
var object = pgli.lang.Parser.parseModule(data);
self.modules.put(name, object);
if(doDependencies == true)
self.loadDependencies(object);
if(doDiagram == true)
self.getAppInstance().addDiagramNode(name, object);
trace("#["+name+"] loaded");
self.onLoad();
})
.error(function()
{
throw "Unable to load file: " + path;
});
},
loadDependencies: function(object)
{
if(!("layers" in object))
return;
var layers = object.layers;
var self = this;
for (var i=0, len = layers.length; i<len ; i++)
{
if(!("use" in layers[i]) )
continue;
var layerName = layers[i].use;
trace("#Found dependency ["+layerName+"]");
(function(name,self)
{
self.loadFile(self.path+name,name,true,true);
})(layerName,self);
}
},
getModulesCount: function()
{
return this.keys.length;
},
getModule: function(key)
{
return this.modules.get(key);
},
getFile: function(key)
{
return this.files.get(key);
},
getModuleKey: function(index)
{
return this.keys[index];
},
getRootModule: function()
{
return this.modules.get(this.root);
},
isEmpty: function()
{
return (this.keys.length <= 0);
},
setAppInstance: function(app)
{
this.appInstance = app;
},
getAppInstance: function()
{
return this.appInstance;
},
setActiveFile: function(key)
{
this.activeFile = key;
},
rememberActiveFile: function()
{
if(!this.activeFile) return;
this.files.put(this.activeFile, this.getAppInstance().getEditorContent());
}
/*updateDiagram: function()
{
},
render: function(canvasRenderer)
{
//canvasRenderer.Render(modules, root, new Hashtable());
}*/
});
+166
View File
@@ -0,0 +1,166 @@
/*!
* @class bkcore.Timer
*
* new Date().getTime() wrapper to use as timers.
*
* @author Thibaut 'BKcore' Despoulain <http://bkcore.com>
*/
/**
* RAF shim
*/
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
/*!
* @package bkcore
*/
var bkcore = bkcore || {};
/*!
* Creates a new timer, inactive by default.
* Call Timer.start() to activate.
*/
bkcore.Timer = function()
{
this.time = {
start: 0,
current: 0,
previous: 0,
elapsed: 0,
delta: 0
}
this.active = false;
}
/*!
* Starts/restarts the timer.
*/
bkcore.Timer.prototype.start = function()
{
var now = new Date().getTime();
this.time.start = now;
this.time.current = now;
this.time.previous = now;
this.time.elapsed = 0;
this.time.delta = 0;
this.active = true;
}
/*!
* Restarts timer, returning last ms tick
*/
bkcore.Timer.prototype.restart = function()
{
var now = new Date().getTime();
var e = now - this.time.start;
this.time.start = now;
this.time.current = now;
this.time.previous = now;
this.time.elapsed = 0;
this.time.delta = 0;
this.active = true;
return e;
}
/*!
* Pauses(true)/Unpauses(false) the timer.
*
* @param bool Do pause
*/
bkcore.Timer.prototype.pause = function(bool)
{
this.active = !bool;
}
/*!
* Update method to be called inside a RAF loop
*/
bkcore.Timer.prototype.update = function()
{
if(!this.active) return;
var now = new Date().getTime();
this.time.current = now;
this.time.elapsed = this.time.current - this.time.start;
this.time.delta = now - this.time.previous;
this.time.previous = now;
return this.time.elapsed;
}
/*!
* Returns elapsed milliseconds
*/
bkcore.Timer.prototype.getElapsed = function()
{
return this.time.elapsed;
}
/*!
* Returns a formatted version of the current elapsed time using msToTime().
*
*
*/
bkcore.Timer.prototype.getElapsedTime = function()
{
return bkcore.Timer.msToTime(this.time.elapsed);
}
/*!
* Formats a millisecond integer into a h/m/s/ms object
*
* @param x int In milliseconds
* @return Object{h,m,s,ms}
*/
bkcore.Timer.msToTime = function(t)
{
var ms, s, m, h;
ms = t%1000;
s = Math.floor((t/1000)%60);
m = Math.floor((t/60000)%60);
h = Math.floor((t/3600000));
return {h:h, m:m, s:s, ms:ms};
}
/*!
* Formats a millisecond integer into a h/m/s/ms object with prefix zeros
*
* @param x int In milliseconds
* @return Object<string>{h,m,s,ms}
*/
bkcore.Timer.msToTimeString = function(t)
{
var ms, s, m, h;
ms = t%1000;
if(ms < 10) ms = "00"+ms;
else if(ms < 100) ms = "0"+ms;
s = Math.floor((t/1000)%60);
if(s < 10) s = "0"+s;
m = Math.floor((t/60000)%60);
h = Math.floor((t/3600000));
return {h:h, m:m, s:s, ms:ms};
}
File diff suppressed because one or more lines are too long
+989
View File
@@ -0,0 +1,989 @@
/**
* gamecore.js - Copyright 2012 Playcraft Labs, Inc. (see licence.txt)
* class.js
* Classes and objects
*/
/**
* @Class
* A modified version of class.js to cater to static inheritance and deep object cloning
* Based almost completely on class.js (Javascript MVC -- Justin Meyer, Brian Moschel, Michael Mayer and others)
* (http://javascriptmvc.com/contribute.html)
* Some portions adapted from Prototype JavaScript framework, version 1.6.0.1 (c) 2005-2007 Sam Stephenson
* <p>
* Class system for javascript
* <p>
* <code>
* var Fighter = gamecore.Base.extend('Fighter',
* {
* // static (this is inherited as well)
* firingSpeed: 1000
* },
* {
* // instance
*
* hp: 0,
* lastFireTime: 0,
*
* init: function(hp)
* {
* this.hp = hp;
* },
*
* fire: function()
* {
* this._super(); // super methods!
*
* // do firing!
* }
* });
*
* var gunship = new Fighter(100);
* </code>
*
* Introspection:
* <code>
* gamecore.Base.extend(Fighter.Gunship);
* Fighter.Gunship.shortName; // Gunship
* Fighter.Gunship.fullName; // Fighter.Gunship
* Fighter.Gunship.namespace; // Fighter
* </code>
* <p>
* Setup method will be called prior to any init -- nice if you want to do things without needing the
* users to call _super in the init, as well as for normalizing parameters.
* <code>
* setup: function()
* {
* this.objectId = this.Class.totalObjects++;
* this.uniqueId = this.Class.fullName + ':' + this.objectId;
* }
* </code>
*/
// compatible with jquery classing
(function ($)
{
var regs = {
undHash: /_|-/,
colons: /::/,
words: /([A-Z]+)([A-Z][a-z])/g,
lowUp: /([a-z\d])([A-Z])/g,
dash: /([a-z\d])([A-Z])/g,
replacer: /\{([^\}]+)\}/g,
dot: /\./
},
getNext = function (current, nextPart, add)
{
return current[nextPart] || ( add && (current[nextPart] = {}) );
},
isContainer = function (current)
{
var type = typeof current;
return type && ( type == 'function' || type == 'object' );
},
getObject = function (objectName, roots, add)
{
var parts = objectName ? objectName.split(regs.dot) : [],
length = parts.length,
currents = $.isArray(roots) ? roots : [roots || window],
current,
ret,
i,
c = 0,
type;
if (length == 0)
{
return currents[0];
}
while (current = currents[c++])
{
for (i = 0; i < length - 1 && isContainer(current); i++)
{
current = getNext(current, parts[i], add);
}
if (isContainer(current))
{
ret = getNext(current, parts[i], add);
if (ret !== undefined)
{
if (add === false)
{
delete current[parts[i]];
}
return ret;
}
}
}
},
/**
* @class jQuery.String
*
* A collection of useful string helpers.
*
*/
str = $.String = $.extend($.String || {}, {
/**
* @function
* Gets an object from a string.
* @param {String} name the name of the object to look for
* @param {Array} [roots] an array of root objects to look for the name
* @param {Boolean} [add] true to add missing objects to
* the path. false to remove found properties. undefined to
* not modify the root object
*/
getObject: getObject,
/**
* Capitalizes a string
* @param {String} s the string.
* @return {String} a string with the first character capitalized.
*/
capitalize: function (s, cache)
{
return s.charAt(0).toUpperCase() + s.substr(1);
},
/**
* Capitalizes a string from something undercored. Examples:
* @codestart
* jQuery.String.camelize("one_two") //-> "oneTwo"
* "three-four".camelize() //-> threeFour
* @codeend
* @param {String} s
* @return {String} a the camelized string
*/
camelize: function (s)
{
s = str.classize(s);
return s.charAt(0).toLowerCase() + s.substr(1);
},
/**
* Like camelize, but the first part is also capitalized
* @param {String} s
* @return {String} the classized string
*/
classize: function (s, join)
{
var parts = s.split(regs.undHash),
i = 0;
for (; i < parts.length; i++)
{
parts[i] = str.capitalize(parts[i]);
}
return parts.join(join || '');
},
/**
* Like [jQuery.String.classize|classize], but a space separates each 'word'
* @codestart
* jQuery.String.niceName("one_two") //-> "One Two"
* @codeend
* @param {String} s
* @return {String} the niceName
*/
niceName: function (s)
{
return str.classize(s, ' ');
},
/**
* Underscores a string.
* @codestart
* jQuery.String.underscore("OneTwo") //-> "one_two"
* @codeend
* @param {String} s
* @return {String} the underscored string
*/
underscore: function (s)
{
return s.replace(regs.colons, '/').replace(regs.words, '$1_$2').replace(regs.lowUp, '$1_$2').replace(regs.dash, '_').toLowerCase();
},
/**
* Returns a string with {param} replaced values from data.
*
* $.String.sub("foo {bar}",{bar: "far"})
* //-> "foo far"
*
* @param {String} s The string to replace
* @param {Object} data The data to be used to look for properties. If it's an array, multiple
* objects can be used.
* @param {Boolean} [remove] if a match is found, remove the property from the object
*/
sub: function (s, data, remove)
{
var obs = [];
obs.push(s.replace(regs.replacer, function (whole, inside)
{
//convert inside to type
var ob = getObject(inside, data, typeof remove == 'boolean' ? !remove : remove),
type = typeof ob;
if ((type === 'object' || type === 'function') && type !== null)
{
obs.push(ob);
return "";
} else
{
return "" + ob;
}
}));
return obs.length <= 1 ? obs[0] : obs;
}
});
})(jQuery);
(function ($)
{
// if we are initializing a new class
var initializing = false,
makeArray = $.makeArray,
isFunction = $.isFunction,
isArray = $.isArray,
extend = $.extend,
/**
*
*/
cloneObject = function(object)
{
if (!object || typeof(object) != 'object')
return object;
// special case handling of array (deep copy them)
if (object instanceof Array)
{
var clone = [];
for (var c = 0; c < object.length; c++)
clone[c] = cloneObject(object[c]);
return clone;
}
else // otherwise, it's a normal object, clone it's properties
{
var cloneObj = {};
for (var prop in object)
cloneObj[prop] = cloneObject(object[prop]);
return cloneObj;
}
},
concatArgs = function (arr, args)
{
return arr.concat(makeArray(args));
},
// tests if we can get super in .toString()
fnTest = /xyz/.test(function ()
{
xyz;
}) ? /\b_super\b/ : /.*/,
// overwrites an object with methods, sets up _super
// newProps - new properties
// oldProps - where the old properties might be
// addTo - what we are adding to
inheritProps = function (newProps, oldProps, addTo)
{
addTo = addTo || newProps
for (var name in newProps)
{
// Check if we're overwriting an existing function
addTo[name] = isFunction(newProps[name]) &&
isFunction(oldProps[name]) &&
fnTest.test(newProps[name]) ? (function (name, fn)
{
return function ()
{
var tmp = this._super, ret;
// Add a new ._super() method that is the same method but on the super-class
this._super = oldProps[name];
// The method only need to be bound temporarily, so we remove it when we're done executing
ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, newProps[name]) : newProps[name];
}
},
/**
* @class jQuery.Class
* @plugin jquery/class
* @tag core
* @download dist/jquery/jquery.class.js
* @test jquery/class/qunit.html
*
* Class provides simulated inheritance in JavaScript. Use clss to bridge the gap between
* jQuery's functional programming style and Object Oriented Programming. It
* is based off John Resig's [http://ejohn.org/blog/simple-javascript-inheritance/|Simple Class]
* Inheritance library. Besides prototypal inheritance, it includes a few important features:
*
* - Static inheritance
* - Introspection
* - Namespaces
* - Setup and initialization methods
* - Easy callback function creation
*
*
* ## Static v. Prototype
*
* Before learning about Class, it's important to
* understand the difference between
* a class's __static__ and __prototype__ properties.
*
* //STATIC
* MyClass.staticProperty //shared property
*
* //PROTOTYPE
* myclass = new MyClass()
* myclass.prototypeMethod() //instance method
*
* A static (or class) property is on the Class constructor
* function itself
* and can be thought of being shared by all instances of the
* Class. Prototype propertes are available only on instances of the Class.
*
* ## A Basic Class
*
* The following creates a Monster class with a
* name (for introspection), static, and prototype members.
* Every time a monster instance is created, the static
* count is incremented.
*
* @codestart
* $.Class.extend('Monster',
* /* @static *|
* {
* count: 0
* },
* /* @prototype *|
* {
* init: function( name ) {
*
* // saves name on the monster instance
* this.name = name;
*
* // sets the health
* this.health = 10;
*
* // increments count
* this.Class.count++;
* },
* eat: function( smallChildren ){
* this.health += smallChildren;
* },
* fight: function() {
* this.health -= 2;
* }
* });
*
* hydra = new Monster('hydra');
*
* dragon = new Monster('dragon');
*
* hydra.name // -> hydra
* Monster.count // -> 2
* Monster.shortName // -> 'Monster'
*
* hydra.eat(2); // health = 12
*
* dragon.fight(); // health = 8
*
* @codeend
*
*
* Notice that the prototype <b>init</b> function is called when a new instance of Monster is created.
*
*
* ## Inheritance
*
* When a class is extended, all static and prototype properties are available on the new class.
* If you overwrite a function, you can call the base class's function by calling
* <code>this._super</code>. Lets create a SeaMonster class. SeaMonsters are less
* efficient at eating small children, but more powerful fighters.
*
*
* Monster.extend("SeaMonster",{
* eat: function( smallChildren ) {
* this._super(smallChildren / 2);
* },
* fight: function() {
* this.health -= 1;
* }
* });
*
* lockNess = new SeaMonster('Lock Ness');
* lockNess.eat(4); //health = 12
* lockNess.fight(); //health = 11
*
* ### Static property inheritance
*
* You can also inherit static properties in the same way:
*
* $.Class.extend("First",
* {
* staticMethod: function() { return 1;}
* },{})
*
* First.extend("Second",{
* staticMethod: function() { return this._super()+1;}
* },{})
*
* Second.staticMethod() // -> 2
*
* ## Namespaces
*
* Namespaces are a good idea! We encourage you to namespace all of your code.
* It makes it possible to drop your code into another app without problems.
* Making a namespaced class is easy:
*
* @codestart
* $.Class.extend("MyNamespace.MyClass",{},{});
*
* new MyNamespace.MyClass()
* @codeend
* <h2 id='introspection'>Introspection</h2>
* Often, it's nice to create classes whose name helps determine functionality. Ruby on
* Rails's [http://api.rubyonrails.org/classes/ActiveRecord/Base.html|ActiveRecord] ORM class
* is a great example of this. Unfortunately, JavaScript doesn't have a way of determining
* an object's name, so the developer must provide a name. Class fixes this by taking a String name for the class.
* @codestart
* $.Class.extend("MyOrg.MyClass",{},{})
* MyOrg.MyClass.shortName //-> 'MyClass'
* MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
* @codeend
* The fullName (with namespaces) and the shortName (without namespaces) are added to the Class's
* static properties.
*
*
* <h2>Setup and initialization methods</h2>
* <p>
* Class provides static and prototype initialization functions.
* These come in two flavors - setup and init.
* Setup is called before init and
* can be used to 'normalize' init's arguments.
* </p>
* <div class='whisper'>PRO TIP: Typically, you don't need setup methods in your classes. Use Init instead.
* Reserve setup methods for when you need to do complex pre-processing of your class before init is called.
*
* </div>
* @codestart
* $.Class.extend("MyClass",
* {
* setup: function() {} //static setup
* init: function() {} //static constructor
* },
* {
* setup: function() {} //prototype setup
* init: function() {} //prototype constructor
* })
* @codeend
*
* <h3>Setup</h3>
* <p>Setup functions are called before init functions. Static setup functions are passed
* the base class followed by arguments passed to the extend function.
* Prototype static functions are passed the Class constructor function arguments.</p>
* <p>If a setup function returns an array, that array will be used as the arguments
* for the following init method. This provides setup functions the ability to normalize
* arguments passed to the init constructors. They are also excellent places
* to put setup code you want to almost always run.</p>
* <p>
* The following is similar to how [jQuery.Controller.prototype.setup]
* makes sure init is always called with a jQuery element and merged options
* even if it is passed a raw
* HTMLElement and no second parameter.
* </p>
* @codestart
* $.Class.extend("jQuery.Controller",{
* ...
* },{
* setup: function( el, options ) {
* ...
* return [$(el),
* $.extend(true,
* this.Class.defaults,
* options || {} ) ]
* }
* })
* @codeend
* Typically, you won't need to make or overwrite setup functions.
* <h3>Init</h3>
*
* <p>Init functions are called after setup functions.
* Typically, they receive the same arguments
* as their preceding setup function. The Foo class's <code>init</code> method
* gets called in the following example:
* </p>
* @codestart
* $.Class.Extend("Foo", {
* init: function( arg1, arg2, arg3 ) {
* this.sum = arg1+arg2+arg3;
* }
* })
* var foo = new Foo(1,2,3);
* foo.sum //-> 6
* @codeend
* <h2>Callbacks</h2>
* <p>Similar to jQuery's proxy method, Class provides a
* [jQuery.Class.static.callback callback]
* function that returns a callback to a method that will always
* have
* <code>this</code> set to the class or instance of the class.
* </p>
* The following example uses this.callback to make sure
* <code>this.name</code> is available in <code>show</code>.
* @codestart
* $.Class.extend("Todo",{
* init: function( name ) { this.name = name }
* get: function() {
* $.get("/stuff",this.callback('show'))
* },
* show: function( txt ) {
* alert(this.name+txt)
* }
* })
* new Todo("Trash").get()
* @codeend
* <p>Callback is available as a static and prototype method.</p>
*
* <h2>Typing<h2>
* Classes are automatically populating with three type related components:
*
* _types: a variable that contains an array of types of this class (extends history)
* _fullTypeName: a string representation of the extends hierarchy
* isA(string): a function you can call which will return true if the class is of a given type string.
* <p>
* Example:
* <p>
* Animal.extend('Tiger', {}, {});
* Tiger._types; // ['Animal', 'Tiger']
* Tiger._fullTypeName; // 'Animal | Tiger |"
* Tiger.isA('Animal'); // true
* </p>
* @constructor Creating a new instance of an object that has extended jQuery.Class
* calls the init prototype function and returns a new instance of the class.
*
*/
clss = $.Class = function ()
{
if (arguments.length)
{
return clss.extend.apply(clss, arguments);
}
};
/* @Static*/
extend(clss, {
/**
* @function callback
* Returns a callback function for a function on this Class.
* The callback function ensures that 'this' is set appropriately.
* @codestart
* $.Class.extend("MyClass",{
* getData: function() {
* this.showing = null;
* $.get("data.json",this.callback('gotData'),'json')
* },
* gotData: function( data ) {
* this.showing = data;
* }
* },{});
* MyClass.showData();
* @codeend
* <h2>Currying Arguments</h2>
* Additional arguments to callback will fill in arguments on the returning function.
* @codestart
* $.Class.extend("MyClass",{
* getData: function( <b>callback</b> ) {
* $.get("data.json",this.callback('process',<b>callback</b>),'json');
* },
* process: function( <b>callback</b>, jsonData ) { //callback is added as first argument
* jsonData.processed = true;
* callback(jsonData);
* }
* },{});
* MyClass.getData(showDataFunc)
* @codeend
* <h2>Nesting Functions</h2>
* Callback can take an array of functions to call as the first argument. When the returned callback function
* is called each function in the array is passed the return value of the prior function. This is often used
* to eliminate currying initial arguments.
* @codestart
* $.Class.extend("MyClass",{
* getData: function( callback ) {
* //calls process, then callback with value from process
* $.get("data.json",this.callback(['process2',callback]),'json')
* },
* process2: function( type,jsonData ) {
* jsonData.processed = true;
* return [jsonData];
* }
* },{});
* MyClass.getData(showDataFunc);
* @codeend
* @param {String|Array} fname If a string, it represents the function to be called.
* If it is an array, it will call each function in order and pass the return value of the prior function to the
* next function.
* @return {Function} the callback function.
*/
callback: function (funcs)
{
//args that should be curried
var args = makeArray(arguments),
self;
funcs = args.shift();
if (!isArray(funcs))
{
funcs = [funcs];
}
self = this;
return function class_cb()
{
var cur = concatArgs(args, arguments),
isString,
length = funcs.length,
f = 0,
func;
for (; f < length; f++)
{
func = funcs[f];
if (!func)
continue;
isString = typeof func == "string";
if (isString && self._set_called)
self.called = func;
cur = (isString ? self[func] : func).apply(self, cur || []);
if (f < length - 1)
cur = !isArray(cur) || cur._use_call ? [cur] : cur
}
return cur;
}
},
/**
* @function getObject
* Gets an object from a String.
* If the object or namespaces the string represent do not
* exist it will create them.
* @codestart
* Foo = {Bar: {Zar: {"Ted"}}}
* $.Class.getobject("Foo.Bar.Zar") //-> "Ted"
* @codeend
* @param {String} objectName the object you want to get
* @param {Object} [current=window] the object you want to look in.
* @return {Object} the object you are looking for.
*/
getObject: $.String.getObject,
/**
* @function newInstance
* Creates a new instance of the class. This method is useful for creating new instances
* with arbitrary parameters.
* <h3>Example</h3>
* @codestart
* $.Class.extend("MyClass",{},{})
* var mc = MyClass.newInstance.apply(null, new Array(parseInt(Math.random()*10,10))
* @codeend
* @return {class} instance of the class
*/
newInstance: function ()
{
var inst = this.rawInstance();
var args;
if (inst.setup)
args = inst.setup.apply(inst, arguments);
// Added by martin@playcraftlabs.com -- fix for deep cloning of properties
for (var prop in inst.__proto__)
inst[prop] = cloneObject(inst[prop]);
if (inst.init)
inst.init.apply(inst, isArray(args) ? args : arguments);
return inst;
},
/**
* Setup gets called on the inherting class with the base class followed by the
* inheriting class's raw properties.
*
* Setup will deeply extend a static defaults property on the base class with
* properties on the base class. For example:
*
* $.Class("MyBase",{
* defaults : {
* foo: 'bar'
* }
* },{})
*
* MyBase("Inheriting",{
* defaults : {
* newProp : 'newVal'
* }
* },{}
*
* Inheriting.defaults -> {foo: 'bar', 'newProp': 'newVal'}
*
* @param {Object} baseClass the base class that is being inherited from
* @param {String} fullName the name of the new class
* @param {Object} staticProps the static properties of the new class
* @param {Object} protoProps the prototype properties of the new class
*/
setup: function (baseClass, fullName)
{
this.defaults = extend(true, {}, baseClass.defaults, this.defaults);
if (this._types == undefined) this._types = [];
this._types.push(this.fullName);
if (this._fullTypeName == undefined) this._fullTypeName = '|';
this._fullTypeName += this.fullName + '|';
return arguments;
},
rawInstance: function ()
{
initializing = true;
var inst = new this();
initializing = false;
return inst;
},
/**
* Extends a class with new static and prototype functions. There are a variety of ways
* to use extend:
* @codestart
* //with className, static and prototype functions
* $.Class.extend('Task',{ STATIC },{ PROTOTYPE })
* //with just classname and prototype functions
* $.Class.extend('Task',{ PROTOTYPE })
* //With just a className
* $.Class.extend('Task')
* @codeend
* @param {String} [fullName] the classes name (used for classes w/ introspection)
* @param {Object} [klass] the new classes static/class functions
* @param {Object} [proto] the new classes prototype functions
* @return {jQuery.Class} returns the new class
*/
extend: function (fullName, klass, proto)
{
// figure out what was passed
if (typeof fullName != 'string')
{
proto = klass;
klass = fullName;
fullName = null;
}
if (!proto)
{
proto = klass;
klass = null;
}
proto = proto || {};
var _super_class = this,
_super = this.prototype,
name, shortName, namespace, prototype;
// append the isA function
this.isA = function(typeName)
{
return this._fullTypeName.indexOf('|'+typeName+'|') != -1;
};
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
inheritProps(proto, _super, prototype);
// The dummy class constructor
function Class()
{
// All construction is actually done in the init method
if (initializing) return;
if (this.constructor !== Class && arguments.length)
{ //we are being called w/o new
return arguments.callee.extend.apply(arguments.callee, arguments)
} else
{ //we are being called w/ new
// copy objects
return this.Class.newInstance.apply(this.Class, arguments)
}
}
// Copy old stuff onto class
for (name in this)
if (this.hasOwnProperty(name))
Class[name] = cloneObject(this[name]);
// copy new props on class
inheritProps(klass, this, Class);
// do namespace stuff
if (fullName)
{
var parts = fullName.split(/\./);
var shortName = parts.pop();
// Martin Wells (playcraft): bug fix. Don't add a namespace object if the class name
// has no namespace elements (i.e. it's just "MyClass", not "MyProject.MyClass")
if (parts.length > 0)
{
current = clss.getObject(parts.join('.'), window, true),
namespace = current;
}
current[shortName] = Class;
}
// set things that can't be overwritten
extend(Class, {
prototype: prototype,
namespace: namespace,
shortName: shortName,
constructor: Class,
fullName: fullName
});
//make sure our prototype looks nice
Class.prototype.Class = Class.prototype.constructor = Class;
/**
* @attribute fullName
* The full name of the class, including namespace, provided for introspection purposes.
* @codestart
* $.Class.extend("MyOrg.MyClass",{},{})
* MyOrg.MyClass.shortName //-> 'MyClass'
* MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
* @codeend
*/
var args = Class.setup.apply(Class, concatArgs([_super_class], arguments));
if (Class.init)
{
Class.init.apply(Class, args || []);
}
/* @Prototype*/
return Class;
/**
* @function setup
* If a setup method is provided, it is called when a new
* instances is created. It gets passed the same arguments that
* were given to the Class constructor function (<code> new Class( arguments ... )</code>).
*
* $.Class("MyClass",
* {
* setup: function( val ) {
* this.val = val;
* }
* })
* var mc = new MyClass("Check Check")
* mc.val //-> 'Check Check'
*
* Setup is called before [jQuery.Class.prototype.init init]. If setup
* return an array, those arguments will be used for init.
*
* $.Class("jQuery.Controller",{
* setup : function(htmlElement, rawOptions){
* return [$(htmlElement),
* $.extend({}, this.Class.defaults, rawOptions )]
* }
* })
*
* <div class='whisper'>PRO TIP:
* Setup functions are used to normalize constructor arguments and provide a place for
* setup code that extending classes don't have to remember to call _super to
* run.
* </div>
*
* Setup is not defined on $.Class itself, so calling super in inherting classes
* will break. Don't do the following:
*
* $.Class("Thing",{
* setup : function(){
* this._super(); // breaks!
* }
* })
*
* @return {Array|undefined} If an array is return, [jQuery.Class.prototype.init] is
* called with those arguments; otherwise, the original arguments are used.
*/
//break up
/**
* @function init
* If an <code>init</code> method is provided, it gets called when a new instance
* is created. Init gets called after [jQuery.Class.prototype.setup setup], typically with the
* same arguments passed to the Class
* constructor: (<code> new Class( arguments ... )</code>).
*
* $.Class("MyClass",
* {
* init: function( val ) {
* this.val = val;
* }
* })
* var mc = new MyClass(1)
* mc.val //-> 1
*
* [jQuery.Class.prototype.setup Setup] is able to modify the arguments passed to init. Read
* about it there.
*
*/
//Breaks up code
/**
* @attribute Class
* References the static properties of the instance's class.
* <h3>Quick Example</h3>
* @codestart
* // a class with a static classProperty property
* $.Class.extend("MyClass", {classProperty : true}, {});
*
* // a new instance of myClass
* var mc1 = new MyClass();
*
* //
* mc1.Class.classProperty = false;
*
* // creates a new MyClass
* var mc2 = new mc.Class();
* @codeend
* Getting static properties via the Class property, such as it's
* [jQuery.Class.static.fullName fullName] is very common.
*/
}
})
clss.prototype.
/**
* @function callback
* Returns a callback function. This does the same thing as and is described better in [jQuery.Class.static.callback].
* The only difference is this callback works
* on a instance instead of a class.
* @param {String|Array} fname If a string, it represents the function to be called.
* If it is an array, it will call each function in order and pass the return value of the prior function to the
* next function.
* @return {Function} the callback function
*/
callback = clss.callback;
})(jQuery);
+16
View File
@@ -0,0 +1,16 @@
.ace_gutter {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.ace_sb {
overflow-y: hidden !important;
}
#editor {
background: #272822;
}
+24
View File
@@ -0,0 +1,24 @@
@font-face {
font-family: 'Ubuntu Mono';
font-style: normal;
font-weight: 700;
src: local('Ubuntu Mono Bold'), local('UbuntuMono-Bold'), url(bold.woff) format('woff');
}
@font-face {
font-family: 'Ubuntu Mono';
font-style: normal;
font-weight: 400;
src: local('Ubuntu Mono'), local('UbuntuMono-Regular'), url(normal.woff) format('woff');
}
@font-face {
font-family: 'Ubuntu Mono';
font-style: italic;
font-weight: 700;
src: local('Ubuntu Mono Bold Italic'), local('UbuntuMono-BoldItalic'), url(bolditalic.woff) format('woff');
}
@font-face {
font-family: 'Ubuntu Mono';
font-style: italic;
font-weight: 400;
src: local('Ubuntu Mono Italic'), local('UbuntuMono-Italic'), url(italic.woff) format('woff');
}
+133
View File
@@ -0,0 +1,133 @@
/**
* gamecore.js - Copyright 2012 Playcraft Labs, Inc. (see licence.txt)
* gamecore.js
* Namespace wrappers and the base class
*/
window.gamecore = {};
gamecore.Class = $.Class;
/**
* @class gamecore.Base
* A base class providing logging, object counting and unique object id's
* Examples:
*
* Unique ID and total objects:
* <code>
* var Fighter = gamecore.Base.extend('Fighter', {}, {});
* var fighter1 = new Fighter();
* var fighter2 = new Fighter();
* fighter1.uniqueId; // -> 'Fighter:0'
* fighter2.uniqueId; // -> 'Fighter:1'
* Fighter.totalObjects; // -> 2
* </code>
*
* Logging: (log, info, warn, error, debug)
* <code>
* fighter1.warn('oops'); // == console.log('Fighter:0 [WARN] oops');
*/
gamecore.Base = gamecore.Class('gamecore.Base',
///
/// STATIC
///
{
totalObjects: 0,
WARN: 'WARN',
DEBUG: 'DEBUG',
ERROR: 'ERROR',
INFO: 'INFO',
log: function(id, type, message)
{
var idString = '';
if (id) idString = ':'+id;
console.log(this.fullName + idString + ' [' + type + '] ' + message);
},
warn: function (message)
{
this.log(null, this.WARN, message);
},
debug: function (message)
{
this.log(null, this.DEBUG, message);
},
error: function (message)
{
this.log(null, this.ERROR, message);
},
info: function (message)
{
this.log(null, this.INFO, message);
},
assert: function(msg, condition)
{
if (!condition)
throw msg;
}
},
///
/// INSTANCE
///
{
objectId: 0,
uniqueId: null,
init: function()
{
},
setup: function()
{
this.objectId = this.Class.totalObjects++;
this.uniqueId = this.Class.fullName + ':' + this.objectId;
},
/**
* @returns {String} A system-wide unique Id for this object instance
*/
getUniqueId: function()
{
// if you see a null error here, then likely you have forgotten to call
// this._super in a subclassed init method.
return this.uniqueId;
},
/**
* @returns {String} A hash matching this object. Override this to implement different
* kinds of object hashing in derived classes.
*/
hashCode: function()
{
return this.getUniqueId();
},
warn: function (message)
{
this.Class.log(this.objectId, this.Class.WARN, message);
},
debug: function (message)
{
this.Class.log(this.objectId, this.Class.DEBUG, message);
},
error: function (message)
{
this.Class.log(this.objectId, this.Class.ERROR, message);
},
info: function (message)
{
this.Class.log(this.objectId, this.Class.INFO, message);
},
toString: function()
{
return this.Class.fullName + ' [id: ' + this.objectId + ']';
}
});
+41
View File
@@ -0,0 +1,41 @@
/**
* A map of linked lists mapped by a string value
*/
gamecore.HashList = gamecore.Base.extend('gamecore.HashList',
{},
{
hashtable: null,
init: function()
{
this.hashtable = new gamecore.Hashtable();
},
add: function(key, object)
{
// find the list associated with this key and add the object to it
var list = this.hashtable.get(key);
if (list == null)
{
// no list associated with this key yet, so let's make one
list = new pc.LinkedList();
this.hashtable.put(key, list);
}
list.add(object);
},
remove: function(key, object)
{
var list = this.hashtable.get(key);
if (list == null) throw "No list for a key in hashlist when removing";
list.remove(object);
},
get: function(key)
{
return this.hashtable.get(key);
}
});
+468
View File
@@ -0,0 +1,468 @@
/**
* Copyright 2010 Tim Down.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 21 March 2010
* Website: http://www.timdown.co.uk/jshashtable
*
* (Slight mod to add to gamecore namespace -- martin@playcraftlabs.com)
*/
/**
* jshashtable
*
* jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable
* in the global scope.
* Example:
* <code>
* var map = new gamecore.Hashtable();
* map.put('test1', obj);
* var obj = map.get('test1');
* </code>
*/
gamecore.Hashtable = (function ()
{
var FUNCTION = "function";
var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ?
function (arr, idx)
{
arr.splice(idx, 1);
} :
function (arr, idx)
{
var itemsAfterDeleted, i, len;
if (idx === arr.length - 1)
{
arr.length = idx;
} else
{
itemsAfterDeleted = arr.slice(idx + 1);
arr.length = idx;
for (i = 0, len = itemsAfterDeleted.length; i < len; ++i)
{
arr[idx + i] = itemsAfterDeleted[i];
}
}
};
function hashObject(obj)
{
var hashCode;
if (typeof obj == "string")
{
return obj;
} else if (typeof obj.hashCode == FUNCTION)
{
// Check the hashCode method really has returned a string
hashCode = obj.hashCode();
return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
} else if (typeof obj.toString == FUNCTION)
{
return obj.toString();
} else
{
try
{
return String(obj);
}
catch (ex)
{
// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
// passed to String()
return Object.prototype.toString.call(obj);
}
}
}
function equals_fixedValueHasEquals(fixedValue, variableValue)
{
return fixedValue.equals(variableValue);
}
function equals_fixedValueNoEquals(fixedValue, variableValue)
{
return (typeof variableValue.equals == FUNCTION) ?
variableValue.equals(fixedValue) : (fixedValue === variableValue);
}
function createKeyValCheck(kvStr)
{
return function (kv)
{
if (kv === null)
{
throw new Error("null is not a valid " + kvStr);
} else if (typeof kv == "undefined")
{
throw new Error(kvStr + " must not be undefined");
}
};
}
var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");
/*----------------------------------------------------------------------------------------------------------------*/
function Bucket(hash, firstKey, firstValue, equalityFunction)
{
this[0] = hash;
this.entries = [];
this.addEntry(firstKey, firstValue);
if (equalityFunction !== null)
{
this.getEqualityFunction = function ()
{
return equalityFunction;
};
}
}
var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;
function createBucketSearcher(mode)
{
return function (key)
{
var i = this.entries.length, entry, equals = this.getEqualityFunction(key);
while (i--)
{
entry = this.entries[i];
if (equals(key, entry[0]))
{
switch (mode)
{
case EXISTENCE:
return true;
case ENTRY:
return entry;
case ENTRY_INDEX_AND_VALUE:
return [ i, entry[1] ];
}
}
}
return false;
};
}
function createBucketLister(entryProperty)
{
return function (aggregatedArr)
{
var startIndex = aggregatedArr.length;
for (var i = 0, len = this.entries.length; i < len; ++i)
{
aggregatedArr[startIndex + i] = this.entries[i][entryProperty];
}
};
}
Bucket.prototype = {
getEqualityFunction:function (searchValue)
{
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
},
getEntryForKey:createBucketSearcher(ENTRY),
getEntryAndIndexForKey:createBucketSearcher(ENTRY_INDEX_AND_VALUE),
removeEntryForKey:function (key)
{
var result = this.getEntryAndIndexForKey(key);
if (result)
{
arrayRemoveAt(this.entries, result[0]);
return result[1];
}
return null;
},
addEntry:function (key, value)
{
this.entries[this.entries.length] = [key, value];
},
keys:createBucketLister(0),
values:createBucketLister(1),
getEntries:function (entries)
{
var startIndex = entries.length;
for (var i = 0, len = this.entries.length; i < len; ++i)
{
// Clone the entry stored in the bucket before adding to array
entries[startIndex + i] = this.entries[i].slice(0);
}
},
containsKey:createBucketSearcher(EXISTENCE),
containsValue:function (value)
{
var i = this.entries.length;
while (i--)
{
if (value === this.entries[i][1])
{
return true;
}
}
return false;
}
};
/*----------------------------------------------------------------------------------------------------------------*/
// Supporting functions for searching hashtable buckets
function searchBuckets(buckets, hash)
{
var i = buckets.length, bucket;
while (i--)
{
bucket = buckets[i];
if (hash === bucket[0])
{
return i;
}
}
return null;
}
function getBucketForHash(bucketsByHash, hash)
{
var bucket = bucketsByHash[hash];
// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;
}
/*----------------------------------------------------------------------------------------------------------------*/
function Hashtable(hashingFunctionParam, equalityFunctionParam)
{
var that = this;
var buckets = [];
var bucketsByHash = {};
var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject;
var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null;
this.put = function (key, value)
{
checkKey(key);
checkValue(value);
var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null;
// Check if a bucket exists for the bucket key
bucket = getBucketForHash(bucketsByHash, hash);
if (bucket)
{
// Check this bucket to see if it already contains this key
bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry)
{
// This bucket entry is the current mapping of key to value, so replace old value and we're done.
oldValue = bucketEntry[1];
bucketEntry[1] = value;
} else
{
// The bucket does not contain an entry for this key, so add one
bucket.addEntry(key, value);
}
} else
{
// No bucket exists for the key, so create one and put our key/value mapping in
bucket = new Bucket(hash, key, value, equalityFunction);
buckets[buckets.length] = bucket;
bucketsByHash[hash] = bucket;
}
return oldValue;
};
this.get = function (key)
{
checkKey(key);
var hash = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket)
{
// Check this bucket to see if it contains this key
var bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry)
{
// This bucket entry is the current mapping of key to value, so return the value.
return bucketEntry[1];
}
}
return null;
};
this.containsKey = function (key)
{
checkKey(key);
var bucketKey = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, bucketKey);
return bucket ? bucket.containsKey(key) : false;
};
this.containsValue = function (value)
{
checkValue(value);
var i = buckets.length;
while (i--)
{
if (buckets[i].containsValue(value))
{
return true;
}
}
return false;
};
this.clear = function ()
{
buckets.length = 0;
bucketsByHash = {};
};
this.isEmpty = function ()
{
return !buckets.length;
};
var createBucketAggregator = function (bucketFuncName)
{
return function ()
{
var aggregated = [], i = buckets.length;
while (i--)
{
buckets[i][bucketFuncName](aggregated);
}
return aggregated;
};
};
this.keys = createBucketAggregator("keys");
this.values = createBucketAggregator("values");
this.entries = createBucketAggregator("getEntries");
this.remove = function (key)
{
checkKey(key);
var hash = hashingFunction(key), bucketIndex, oldValue = null;
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket)
{
// Remove entry from this bucket for this key
oldValue = bucket.removeEntryForKey(key);
if (oldValue !== null)
{
// Entry was removed, so check if bucket is empty
if (!bucket.entries.length)
{
// Bucket is empty, so remove it from the bucket collections
bucketIndex = searchBuckets(buckets, hash);
arrayRemoveAt(buckets, bucketIndex);
delete bucketsByHash[hash];
}
}
}
return oldValue;
};
this.size = function ()
{
var total = 0, i = buckets.length;
while (i--)
{
total += buckets[i].entries.length;
}
return total;
};
this.each = function (callback)
{
var entries = that.entries(), i = entries.length, entry;
while (i--)
{
entry = entries[i];
callback(entry[0], entry[1]);
}
};
this.putAll = function (hashtable, conflictCallback)
{
var entries = hashtable.entries();
var entry, key, value, thisValue, i = entries.length;
var hasConflictCallback = (typeof conflictCallback == FUNCTION);
while (i--)
{
entry = entries[i];
key = entry[0];
value = entry[1];
// Check for a conflict. The default behaviour is to overwrite the value for an existing key
if (hasConflictCallback && (thisValue = that.get(key)))
{
value = conflictCallback(key, thisValue, value);
}
that.put(key, value);
}
};
this.clone = function ()
{
var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam);
clone.putAll(that);
return clone;
};
/**
* Added by martin@playcratlabs.com to support debug dumping of hash arrays
*/
this.toString = function ()
{
var result = '';
var keys = this.keys();
for (var i = 0; i < keys.length; i++)
{
var obj = this.get(keys[i]);
result += keys[i].toString() + ' = ' + obj.toString() + '\n';
}
return result;
}
}
return Hashtable;
})();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+204
View File
@@ -0,0 +1,204 @@
body {
overflow: hidden;
font-family: "Ubuntu sans", Arial, sans-serif;
font-size: 14px;
color: #ddd;
line-height: 1em;
background: #1e1e1e;
}
#panel-center {
margin: 0;
position: absolute;
z-index: 20;
top: 0;
bottom: 0;
left: 200px;
right: 400px;
}
#panel-left {
margin: 0;
position: absolute;
z-index: 10;
top: 0;
bottom: 0;
left: 0;
width: 200px;
background: #191919;
}
#panel-right {
margin: 0;
position: absolute;
z-index: 30;
top: 0;
bottom: 0;
right: 0;
width: 400px;
background: #191919;
}
#editor {
margin: 0;
position: absolute;
top: 0;
bottom: 50%;
left: 0;
right: 0;
z-index: 100;
}
#diagram {
margin: 0;
position: absolute;
bottom: 0;
top: 50%;
left: 0;
right: 0;
z-index: 200;
}
#editor-separator, #preview-separator {
margin: 0;
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 3px;
z-index: 300;
background: #191919;
border-top: 1px solid #292929;
border-bottom: 1px solid #292929;
}
#preview-separator {
z-index: 400;
left: 3px;
}
#left-separator {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 3px;
z-index: 300;
background: #191919;
}
#right-separator {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 3px;
z-index: 300;
background: #191919;
}
#preview {
margin: 0;
position: absolute;
top: 0;
bottom: 50%;
left: 3px;
width: 396px;
background: #1e1e1e;
border-left: 1px solid #292929;
z-index: 210;
}
#console {
margin: 0;
position: absolute;
bottom: 0;
top: 50%;
left: 0;
left: 3px;
width: 396px;
z-index: 220;
background: #1e1e1e;
border-left: 1px solid #292929;
}
#console-text {
background: transparent;
border: none;
width: 100%;
height: 100%;
color: #666;
text-shadow: 0px 1px 1px #000000;
filter: dropshadow(color=#000000, offx=0, offy=1);
padding: 6px;
outline: none;
overflow: hidden;
scroll: none;
}
#preview canvas {
background: #161616;
}
#modules {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 196px;
font-size: 13px;
background: #1e1e1e;
border-right: 1px solid #292929;
}
#modules ul {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
list-style: none;
margin: 10px 0;
padding: 0;
}
#modules li {
list-style: none;
line-height: 2.0em;
padding: 0 20px;
background: #1e1e1e;
cursor: pointer;
text-shadow: 0px 1px 1px #000000;
filter: dropshadow(color=#000000, offx=0, offy=1);
}
#modules li:hover {
background: #434343; /* Old browsers */
background: -moz-linear-gradient(top, #888 0%, #4e4e4e 4%, #434343 96%, #1e1e1e 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#888), color-stop(4%,#4e4e4e), color-stop(96%,#434343), color-stop(100%,#1e1e1e)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #888 0%, #4e4e4e 4%, #434343 96%, #1e1e1e 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #888 0%, #4e4e4e 4%, #434343 96%, #1e1e1e 100%); /* Opera 11.10+ */
background: -ms-linear-gradient(top, #888 0%, #4e4e4e 4%, #434343 96%, #1e1e1e 100%); /* IE10+ */
background: linear-gradient(to bottom, #888 0%, #4e4e4e 4%, #434343 96%, #1e1e1e 100%); /* W3C */
}
#modules li.active {
background: #292929;
}
#render
{
position: absolute;
z-index: 9999999;
}
File diff suppressed because one or more lines are too long
+542
View File
@@ -0,0 +1,542 @@
/**
* gamecore.js - Copyright 2012 Playcraft Labs, Inc. (see licence.txt)
* pool.js
*/
/**
* @class gamecore.Pool
* Easy (high-performance) object pooling
*
* A pool of objects for use in situations where you want to minimize object life cycling (and
* subsequently garbage collection). It also serves as a very high speed, minimal overhead
* collection for small numbers of objects.
* <p>
* This class maintains mutual an array of objects which are free. If you wish to maintain a list of both
* free and used then see the gamecore.DualPool.
* <p>
* Pools are managed by class type, and will auto-expand as required. You can create a custom initial pool
* size by deriving from the Pool class and statically overriding INITIAL_POOL_SIZE.
* <p>
* Keep in mind that objects that are pooled are not constructed; they are "reset" when handed out.
* You need to "acquire" one and then reset its state, usually via a static create factory method.
* <p>
* Example:
* <code>
* Point = gamecore.Pooled('Point',
* {
* // Static constructor
* create:function (x, y)
* {
* var n = this._super();
* n.x = x;
* n.y = y;
* return n;
* }
* },
* {
* x:0, y:0, // instance
*
* init: function(x, y)
* {
* this.x = x;
* this.y = y;
* }
* }
* </code>
* To then access the object from the pool, use create, instead of new. Then release it.
* <code>
* var p = Point.create(100, 100);
* // ... do something
* p.release();
* </code>
*
*/
gamecore.Pool = gamecore.Base.extend('gamecore.Pool',
{
INITIAL_POOL_SIZE:1,
pools:new gamecore.Hashtable(), // all your pools belong to us
totalPooled:0,
totalUsed:0,
/**
* Acquire an object from a pool based on the class[name]. Typically this method is
* automatically called from
* @param classType Class of object to create
*/
acquire:function (classType)
{
var pool = this.getPool(classType);
if (pool == undefined || pool == null)
{
// create a pool for this type of class
//this.info('Constructing a new pool for ' + classType.fullName + ' objects.');
pool = new gamecore.Pool(classType, this.INITIAL_POOL_SIZE);
this.pools.put(classType.fullName, pool);
}
return pool.acquire();
},
/**
* Releases an object back into it's corresponding object pool
* @param pooledObj Object to return to the pool
*/
release:function (pooledObj)
{
var pool = this.pools.get(pooledObj.Class.fullName);
if (pool == undefined)
throw "Oops, trying to release an object of type " + pooledObj.Class.fullName +
" but no pool exists. Did you new an object instead of using create.";
pool.release(pooledObj);
},
/**
* Returns the pool associated with the given classType, or null if no pool currently exists
*/
getPool:function (classType)
{
return this.pools.get(classType.fullName);
},
getStats:function ()
{
var s = '';
var keys = this.pools.keys();
for (var i = 0; i < keys.length; i++)
{
var key = keys[i];
var pool = this.pools.get(key);
s += key + ': ' + pool.getStats() + '\n';
}
return s;
}
},
{
freeList:null,
expansion: 1,
traces: null,
/**
* Constructs a pool using a base of objects passed in as an array.
* @param classType Class name of the type of objects in the pool
* @param initial Starting number of objects in the pool
*/
init:function (classType, initial)
{
this._super();
this.classType = classType;
this.freeList = [];
// instantiate the initial objects for the pool
this.expand(initial);
},
startTracing:function ()
{
if (this.tracing) return;
this.tracing = true;
if (this.traces)
this.traces.clear();
else
this.traces = new gamecore.Hashtable();
},
stopTracing:function ()
{
this.tracing = false;
},
/**
* Expand the pool of objects by constructing a bunch of new ones. The pool will
* automatically expand itself by 10% each time it runs out of space, so generally you
* shouldn't need to use this.
* @param howMany Number of new objects you want to add
*/
expand:function (howMany)
{
gamecore.Pool.totalPooled += howMany;
//debug: if you want to track expansion
//this.debug('expanding ' + this.classType.fullName + ' by ' + howMany + ' total=' + gamecore.Pool.totalPooled);
for (var i = 0; i < howMany; i++)
this.freeList.push(new this.classType());
},
getFreeCount: function()
{
return this.freeList.length;
},
/**
* Returns the next free object by moving it from the free pool to the used
* one. If no free objects are available it returns the oldest from the used
* pool.
* access to the object
*/
acquire:function ()
{
// check if we have anymore to give out
if (this.freeList.length <= 0)
{
// create some more space (expand by 20%, minimum 1)
this.expansion = Math.round(this.expansion*1.2)+1;
this.expand(this.expansion);
}
if (this.tracing)
{
var stack = printStackTrace();
var pos = stack.length - 1;
while (stack[pos].indexOf('Class.addTo') == 0 && pos > 0)
pos--;
var count = this.traces.get(stack[pos]);
if (count == null)
this.traces.put(stack[pos], { value:1 });
else
count.value++;
}
return this.freeList.pop();
},
/**
* Releases an object by moving it from the used list back to the free list.
* @param obj {pc.Base} The obj to release back into the pool
*/
release:function (obj)
{
this.freeList.push(obj);
},
getStats:function ()
{
var s = this.Class.fullName + ' stats: ' + this.freeList.length + ' free.\n';
if (this.tracing)
{
s += 'TRACING\n';
var traceKeys = this.traces.keys();
for (var k in traceKeys)
s += traceKeys[k] + ' (' + this.traces.get(traceKeys[k]).value + ')\n';
}
return s;
},
dump:function (msg)
{
this.info('================== ' + msg + ' ===================');
this.info('FREE');
this.freeList.dump();
},
/**
* Returns the number of objects in the pool
*/
size:function ()
{
return this.freeList.length;
},
/**
* Returns the LinkedList of currently free objects in the pool
*/
getFreeList:function ()
{
return this.freeList;
}
});
/**
* @class gamecore.DualPool
* Easy (high-performance) object pooling
*
* A pool of objects for use in situations where you want to minimize object life cycling (and
* subsequently garbage collection). It also serves as a very high speed, minimal overhead
* collection for small numbers of objects.
* <p>
* This class maintains mutual set of doubly-linked lists in order to differentiate between
* objects that are in use and those that are unallocated from the pool. This allows for much
* faster cycling of only the in-use objects.
* <p>
* Pools are managed by class type, and will auto-expand as required. You can create a custom initial pool
* size by deriving from the Pool class and statically overriding INITIAL_POOL_SIZE.
* <p>
* Keep in mind that objects that are pooled are not constructed; they are "reset" when handed out.
* You need to "acquire" one and then reset its state, usually via a static create factory method.
* <p>
* Example:
* <code>
* Point = gamecore.Pooled('Point',
* {
* // Static constructor
* create:function (x, y)
* {
* var n = this._super();
* n.x = x;
* n.y = y;
* return n;
* }
* },
* {
* x:0, y:0, // instance
*
* init: function(x, y)
* {
* this.x = x;
* this.y = y;
* }
* }
* </code>
* To then access the object from the pool, use create, instead of new. Then release it.
* <code>
* var p = Point.create(100, 100);
* // ... do something
* p.release();
* </code>
*
*/
gamecore.DualPool = gamecore.Pool.extend('gamecore.DualPool',
{
acquire:function (classType)
{
var pool = this.getPool(classType);
if (pool == undefined || pool == null)
{
pool = new gamecore.DualPool(classType, this.INITIAL_POOL_SIZE);
this.pools.put(classType.fullName, pool);
}
return pool.acquire();
},
getStats:function ()
{
var s = '';
var keys = this.pools.keys();
for (var i = 0; i < keys.length; i++)
{
var key = keys[i];
var pool = this.pools.get(key);
s += key + ' (free: ' + pool.freeList.length() + ' used: ' + pool.usedList.length() + ')\n';
}
return s;
}
},
///
/// INSTANCE
///
{
freeList:null,
usedList:null,
/**
* Constructs a pool using a base of objects passed in as an array.
* @param classType Class name of the type of objects in the pool
* @param initial Starting number of objects in the pool
*/
init:function (classType, initial)
{
this.classType = classType;
this.usedList = new gamecore.LinkedList();
this.freeList = new gamecore.LinkedList();
// instantiate the initial objects for the pool
this.expand(initial);
},
/**
* Expand the pool of objects by constructing a bunch of new ones. The pool will
* automatically expand itself by 10% each time it runs out of space, so generally you
* shouldn't need to use this.
* @param howMany Number of new objects you want to add
*/
expand:function (howMany)
{
// this.info('Expanding ' + this.classType.fullName + ' pool from ' + this.size() +
// ' to ' + (this.size() + howMany) + ' objects');
gamecore.Pool.totalPooled += howMany;
for (var i = 0; i < howMany; i++)
this.freeList.add(new this.classType());
},
/**
* Returns the next free object by moving it from the free pool to the used
* one. If no free objects are available it returns the oldest from the used
* pool.
* access to the object
*/
returnObj:null,
acquire:function ()
{
// check if we have anymore to give out
if (this.freeList.first == null)
// create some more space (expand by 20%, minimum 1)
this.expand(Math.round(this.size() / 5) + 1);
this.returnObj = this.freeList.first.obj;
this.freeList.remove(this.returnObj);
this.returnObj.destroyed = false;
this.usedList.add(this.returnObj);
if (this.tracing)
{
var stack = printStackTrace();
var pos = stack.length - 1;
while (stack[pos].indexOf('Class.addTo') == 0 && pos > 0)
pos--;
var count = this.traces.get(stack[pos]);
if (count == null)
this.traces.put(stack[pos], { value:1 });
else
count.value++;
}
return this.returnObj;
},
/**
* Releases an object by moving it from the used list back to the free list.
* @param obj {pc.Base} The obj to release back into the pool
*/
release:function (obj)
{
this.freeList.add(obj);
this.usedList.remove(obj);
},
dump:function (msg)
{
this.info('================== ' + msg + ' ===================');
this.info('FREE');
this.freeList.dump();
this.info('USED');
this.usedList.dump();
},
/**
* Returns the number of objects in the pool
*/
size:function ()
{
return this.freeList.count + this.usedList.count;
},
/**
* Returns the LinkedList of current used objects in the pool
* @return {*}
*/
getUsedList:function ()
{
return this.usedList;
}
});
/**
* @class gamecore.Pooled
* Used as a base class for objects which are life cycle managed in an object pool.
*/
gamecore.Pooled = gamecore.Base('gamecore.Pooled',
///
/// STATICS
///
{
/**
* Static factory method for creating a new object based on its class. This method
* should be called using this._super from the Class.create that derives from this.
* @returns An object from the pool
*/
create:function ()
{
return gamecore.Pool.acquire(this);
},
getPool:function ()
{
return gamecore.Pool.getPool(this);
}
},
///
/// INSTANCE
///
{
destroyed:false,
init:function ()
{
this._super();
},
release:function ()
{
this.onRelease();
gamecore.Pool.release(this);
},
onRelease:function ()
{
}
});
/**
* @class gamecore.DualPooled
* Used as a base class for objects which are life cycle managed in an object pool (the DualPool edition)
*/
gamecore.DualPooled = gamecore.Base('gamecore.DualPooled',
///
/// STATICS
///
{
/**
* Static factory method for creating a new object based on its class. This method
* should be called using this._super from the Class.create that derives from this.
* @returns An object from the pool
*/
create:function ()
{
return gamecore.DualPool.acquire(this);
},
getPool:function ()
{
return gamecore.DualPool.getPool(this);
}
},
///
/// INSTANCE
///
{
destroyed:false,
init:function ()
{
this._super();
},
release:function ()
{
this.onRelease();
gamecore.DualPool.release(this);
},
onRelease:function ()
{
}
});
+1
View File
@@ -0,0 +1 @@
ace.define("ace/theme/monokai",["require","exports","module","ace/lib/dom"],function(a,b,c){b.isDark=!0,b.cssClass="ace-monokai",b.cssText=".ace-monokai .ace_editor { border: 2px solid rgb(159, 159, 159)}.ace-monokai .ace_editor.ace_focus { border: 2px solid #327fbd}.ace-monokai .ace_gutter { background: #2f3129; color: #f1f1f1}.ace-monokai .ace_print_margin { width: 1px; background: #555651}.ace-monokai .ace_scroller { background-color: #272822}.ace-monokai .ace_text-layer { color: #F8F8F2}.ace-monokai .ace_cursor { border-left: 2px solid #F8F8F0}.ace-monokai .ace_cursor.ace_overwrite { border-left: 0px; border-bottom: 1px solid #F8F8F0}.ace-monokai .ace_marker-layer .ace_selection { background: #49483E}.ace-monokai.multiselect .ace_selection.start { box-shadow: 0 0 3px 0px #272822; border-radius: 2px}.ace-monokai .ace_marker-layer .ace_step { background: rgb(102, 82, 0)}.ace-monokai .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; border: 1px solid #49483E}.ace-monokai .ace_marker-layer .ace_active_line { background: #202020}.ace-monokai .ace_gutter_active_line { background-color: #272727}.ace-monokai .ace_marker-layer .ace_selected_word { border: 1px solid #49483E}.ace-monokai .ace_invisible { color: #49483E}.ace-monokai .ace_entity.ace_name.ace_tag,.ace-monokai .ace_keyword,.ace-monokai .ace_meta,.ace-monokai .ace_storage { color: #F92672}.ace-monokai .ace_constant.ace_character,.ace-monokai .ace_constant.ace_language,.ace-monokai .ace_constant.ace_numeric,.ace-monokai .ace_constant.ace_other { color: #AE81FF}.ace-monokai .ace_invalid { color: #F8F8F0; background-color: #F92672}.ace-monokai .ace_invalid.ace_deprecated { color: #F8F8F0; background-color: #AE81FF}.ace-monokai .ace_support.ace_constant,.ace-monokai .ace_support.ace_function { color: #66D9EF}.ace-monokai .ace_fold { background-color: #A6E22E; border-color: #F8F8F2}.ace-monokai .ace_storage.ace_type,.ace-monokai .ace_support.ace_class,.ace-monokai .ace_support.ace_type { font-style: italic; color: #66D9EF}.ace-monokai .ace_entity.ace_name.ace_function,.ace-monokai .ace_entity.ace_other.ace_attribute-name,.ace-monokai .ace_variable { color: #A6E22E}.ace-monokai .ace_variable.ace_parameter { font-style: italic; color: #FD971F}.ace-monokai .ace_string { color: #E6DB74}.ace-monokai .ace_comment { color: #75715E}.ace-monokai .ace_markup.ace_underline { text-decoration: underline}.ace-monokai .ace_indent-guide { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNQ11D6z7Bq1ar/ABCKBG6g04U2AAAAAElFTkSuQmCC) right repeat-y}";var d=a("../lib/dom");d.importCssString(b.cssText,b.cssClass)})
View File
+767
View File
File diff suppressed because one or more lines are too long
+3
View File
File diff suppressed because one or more lines are too long
+18
View File
@@ -0,0 +1,18 @@
{
"name": "HexGL",
"description": "HexGL v1.0.1-ffos",
"launch_path": "/index-mobile.html",
"icons": {
"32": "/icon_32.png",
"64": "/icon_64.png",
"128": "/icon_128.png",
"256": "/icon_256.png"
},
"developer": {
"name": "Thibaut Despoulain (BKcore)",
"url": "http://bkcore.com"
},
"installs_allowed_from": ["*"],
"default_locale": "en",
"orientation": ["landscape"]
}
+10
View File
@@ -0,0 +1,10 @@
{
"name": "HexGL",
"description": "HexGL v1.0.1-ffos",
"package_path" : "package.zip",
"version": "1",
"developer": {
"name": "Thibaut Despoulain (BKcore)",
"url": "http://bkcore.com"
}
}
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Some files were not shown because too many files have changed in this diff Show More