kettek2/wiki/playground/pipes/js/pipes.js

216 lines
7.2 KiB
JavaScript

var ktk = ktk || {};
/* rip popular js libs */
ktk.pipes = (function() {
/**** types ****/
var T = {
Object: function() {
this.set = function(key, value) {
this._[key] = value;
}
this.get = function(key) {
return this._[key];
}
this.has = function(key) {
return (typeof this._[key] === undefined || this._[key] === null) ? false : true
}
},
Pipe: function() {
T.Object.call(this);
/*** private ***/
var _ = {
x : 0,
y : 0,
absolute_x: 0,
absolute_y: 0,
angle : 0,
name : '',
absolute_angle: 0,
rotation_force: 0, // force in angles per gametick
rotation_velocity: 0, // current rotational velocity
rotation_drag: .05, // .05 angles per gametick(16ms)
origin_x : 0,
origin_y : 0,
pivot_x : 0,
pivot_y : 0,
total_origin_x: 0,
total_origin_y: 0,
image : new Image(),
children : [],
parent : null
}; this._ = _;
_.image.src = "img/pipe.png";
/*** public ***/
this.onUpdate = function(delta) {
this.set('angle', this.get('angle')+this.get('rotation_force'));
var x = 0, y = 0;
var ox = 0, oy = 0;
if (this.has('parent')) {
this.set('absolute_x', this.get('x') + this.get('parent').get('absolute_x') + this.get('parent').get('pivot_x'));
this.set('absolute_y', this.get('y') + this.get('parent').get('absolute_y') + this.get('parent').get('pivot_y'));
this.set('absolute_angle', this.get('angle') + this.get('parent').get('absolute_angle'));
} else {
this.set('absolute_x', this.get('x'));
this.set('absolute_y', this.get('y'));
this.set('absolute_angle', this.get('angle'));
}
var xy;
if (this.has('parent')) {
xy = $.rotatePoint(this.get('absolute_x')-this.get('origin_x'), this.get('absolute_y')-this.get('origin_y'), this.get('parent').get('absolute_x')+this.get('parent').get('pivot_x'), this.get('parent').get('absolute_y')+this.get('parent').get('pivot_y'), this.get('angle'));
} else {
xy = [this.get('absolute_x'), this.get('absolute_y')];
}
this.set('absolute_x', xy[0]);
this.set('absolute_y', xy[1]);
// Update our children
for (var child_index = 0; child_index < _.children.length; child_index++) {
_.children[child_index].onUpdate(delta);
}
};
this.onRender = function() {
$.getContext().save();
$.getContext().translate(this.get('absolute_x'), this.get('absolute_y'));
$.getContext().rotate(this.get('absolute_angle') * Math.PI / 180);
$.getContext().drawImage(_.image, 0, 0);
if (this.get('name') !== '') {
$.getContext().fillText(this.get('name')+this.get('absolute_angle'), 0, 0);
}
$.getContext().restore();
// Render our children
for (var child_index = 0; child_index < _.children.length; child_index++) {
_.children[child_index].onRender();
}
};
/* hierarchy methods */
this.disown = function(object) {
var childIndex = _.children.indexOf(object);
if (childIndex >= 0) {
_.children.splice(childIndex, 1);
object.set('parent', null);
return true;
}
return false;
}
this.attach = function(object) {
if (object.has('parent')) {
object.get('parent').disown(object);
}
object.set('parent', this);
_.children.push(object);
}
}
};
/**** private ****/
var _ = {
objects: [],
accumulator: 0,
curr_timestamp: new Date(),
last_timestamp: new Date(),
last_timeout: -1,
curr_delta: 0,
last_delta: 0,
last_rendertimestamp: 0,
doUpdateLoop: function() {
_.last_timestamp = _.curr_timestamp;
_.curr_timestamp = new Date();
_.curr_delta = _.curr_timestamp - _.last_timestamp;
_.accumulator += _.curr_delta;
var thisFrameStart = new Date();
while (_.accumulator >= $.tickrate) {
_.onUpdate($.tickrate);
_.accumulator -= $.tickrate;
}
var thisFrameEnd = new Date();
var thisFrameDelta = _.last_delta = thisFrameEnd - thisFrameStart;
var delay = $.tickrate - _.accumulator;
_.last_timeout = window.setTimeout(_.doUpdateLoop, (delay >= 0 ? delay : 0));
},
/* Called whenever the physics or state should be updated */
onUpdate: function(delta) {
for (var i = 0; i < _.objects.length; i++) {
_.objects[i].onUpdate(delta);
}
},
/* Called whenever possible to re-render the scene */
onRender: function(ts) { // ts = timestamp
var delta_rendertimestamp = ts - _.last_rendertimestamp;
// Clear our screen
_.ctx.clearRect(0, 0, _.canvas.width, _.canvas.height);
_.ctx.fillText("frametime: " + _.last_delta, 0, 10);
_.ctx.fillText("fpms: " + delta_rendertimestamp, 0, 20);
// Do our rendering.
for (var i = 0; i < _.objects.length; i++) {
_.objects[i].onRender();
}
_.last_rendertimestamp = ts;
// Continue our loop
window.requestAnimationFrame(_.onRender);
}
};
/**** public ****/
var $ = {
tickrate: 16, // 16 updates per second
/* Initialization method, called externally to set up everything */
onLoad: function(el) {
if (el.tagName !== 'CANVAS') {
alert('target element _must_ be a canvas');
throw(el);
}
_.canvas = el;
_.ctx = el.getContext("2d");
_.ctx.mozImageSmoothingEnabled = false;
_.ctx.webkitImageSmoothingEnabled = false;
_.ctx.msImageSmoothingEnabled = false;
_.ctx.imageSmoothingEnabled = false;
$.setupPlayfield();
_.doUpdateLoop();
_.onRender();
},
createObject: function(object_name) {
if (!T[object_name]) {
throw object_name+" is not a valid object.";
}
return new T[object_name];
},
addObject: function(object) {
_.objects.push(object);
},
/* Called to setup our playfield. public to allow overriding. */
setupPlayfield: function() {
let obj = $.createObject("Pipe");
//obj.set('rotation_force', .25);
obj.set('x', _.canvas.width/2);
obj.set('y', _.canvas.height/2);
obj.set('name', 'A');
obj.set('origin_x', 4);
obj.set('origin_y', 8);
obj.set('pivot_x', 56);
obj.set('pivot_y', 4);
let obj2 = $.createObject("Pipe");
obj2.set('rotation_force', .5);
obj2.set('angle', 0);
obj2.set('name', 'B');
obj2.set('origin_x', 4);
obj2.set('origin_y', 8);
obj.attach(obj2);
$.addObject(obj);
},
getContext: function() {
return _.ctx;
},
rotatePoint: function(ox, oy, px, py, angle) {
var c = Math.cos(angle * Math.PI / 180);
var s = Math.sin(angle * Math.PI / 180);
ox -= px;
oy -= py;
var x = ox * c - oy * s;
var y = ox * s + oy * c;
ox = x + px;
oy = y + py;
/*ox = c * (ox - px) - s * (oy - py) + px;
oy = s * (ox - px) + c * (oy - py) + py;*/
return [ox, oy];
}
};
return $;
})();