You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1457 lines
31 KiB
JavaScript
1457 lines
31 KiB
JavaScript
/* Prototype modifications of standard types go here. */
|
|
|
|
HTMLCanvasElement.prototype.relativeCoordinates = function(event)
|
|
{
|
|
var totalOffsetX = 0;
|
|
var totalOffsetY = 0;
|
|
var canvasX = 0;
|
|
var canvasY = 0;
|
|
var currentElement = this;
|
|
|
|
do {
|
|
if(!$(currentElement).is('body'))
|
|
{
|
|
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
|
|
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
|
|
}
|
|
} while(currentElement = currentElement.offsetParent)
|
|
|
|
canvasX = event.pageX - totalOffsetX;
|
|
canvasY = event.pageY - totalOffsetY;
|
|
|
|
return {x:canvasX, y:canvasY}
|
|
}
|
|
|
|
/* Core engine constructor. */
|
|
function Engine(settings)
|
|
{
|
|
this.resources = {};
|
|
this.sprites = {};
|
|
this.sounds = {};
|
|
this.objects = {};
|
|
this.scenes = {};
|
|
this.canvas = null;
|
|
this.settings = settings;
|
|
this.loader_created = false;
|
|
this.instance_increment = 10000;
|
|
|
|
_engine = this;
|
|
|
|
/**--------------------------------------**/
|
|
/** PRELOADER **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Preloader = function()
|
|
{
|
|
this._engine = _engine;
|
|
this.queue = [];
|
|
this.resources_left = 0;
|
|
this.resources_done = 0;
|
|
this.resources_total = 0;
|
|
this.resources_failed = 0;
|
|
this.images_total = 0;
|
|
this.scripts_total = 0;
|
|
this.sounds_total = 0;
|
|
this.sound_enabled = false;
|
|
this.sound_ignore_failure = false;
|
|
|
|
if(this._engine.loader_created == false)
|
|
{
|
|
/* This is the first Preloader that is being created. Load scripts that are needed by the engine. */
|
|
this._engine.loader_created = true;
|
|
|
|
if(typeof this._engine.settings !== "undefined")
|
|
{
|
|
if(this._engine.settings["enable_sound"])
|
|
{
|
|
this.AddScript("static/soundmanager2.js", "SoundManager2");
|
|
|
|
this.AddFunction(function(){
|
|
soundManager.setup({
|
|
url: 'static/',
|
|
onready: this._HandleSM2Ready.bind(this),
|
|
ontimeout: this._HandleSM2Failed.bind(this),
|
|
useHighPerformance: true,
|
|
preferFlash: false
|
|
});
|
|
|
|
return false;
|
|
}, "Initializing SoundManager2");
|
|
}
|
|
else
|
|
{
|
|
this._engine.sound_enabled = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add function */
|
|
this.Preloader.prototype.AddFunction = function(func, description)
|
|
{
|
|
this.queue.push({
|
|
type: "function",
|
|
func: func.bind(this),
|
|
desc: description
|
|
});
|
|
|
|
this.resources_left++;
|
|
this.resources_total++;
|
|
}
|
|
|
|
/* Add image */
|
|
this.Preloader.prototype.AddImage = function(name, path)
|
|
{
|
|
this.queue.push({
|
|
type: "image",
|
|
name: name,
|
|
path: path
|
|
});
|
|
|
|
this.resources_left++;
|
|
this.resources_total++;
|
|
}
|
|
|
|
/* Add a dictionary of items */
|
|
this.Preloader.prototype.AddItems = function(items)
|
|
{
|
|
if(typeof items.images != "undefined")
|
|
{
|
|
for(name in items.images)
|
|
{
|
|
this.AddImage(name, items.images[name]);
|
|
}
|
|
}
|
|
|
|
if(typeof items.sounds != "undefined")
|
|
{
|
|
for(name in items.sounds)
|
|
{
|
|
this.AddSound(name, items.sounds[name]);
|
|
}
|
|
}
|
|
|
|
if(typeof items.scripts != "undefined")
|
|
{
|
|
for(name in items.scripts)
|
|
{
|
|
this.AddScript(name, items.scripts[name]);
|
|
}
|
|
}
|
|
|
|
if(typeof items.functions != "undefined")
|
|
{
|
|
for(name in items.functions)
|
|
{
|
|
this.AddFunction(name, items.functions[name]);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Add script file */
|
|
this.Preloader.prototype.AddScript = function(path, description)
|
|
{
|
|
this.queue.push({
|
|
type: "script",
|
|
path: path,
|
|
desc: description
|
|
});
|
|
|
|
this.resources_left++;
|
|
this.resources_total++;
|
|
}
|
|
|
|
/* Add sound file */
|
|
this.Preloader.prototype.AddSound = function(name, path)
|
|
{
|
|
this.queue.push({
|
|
type: "sound",
|
|
name: name,
|
|
path: path
|
|
});
|
|
|
|
this.resources_left++;
|
|
this.resources_total++;
|
|
}
|
|
|
|
/* Start the preloading sequence. */
|
|
this.Preloader.prototype.Run = function(callbacks)
|
|
{
|
|
if(typeof callbacks == "undefined")
|
|
{
|
|
this.callbacks = {};
|
|
}
|
|
else
|
|
{
|
|
this.callbacks = callbacks;
|
|
}
|
|
|
|
this.RunCycle();
|
|
}
|
|
|
|
/* Process one item in the preloading sequence. */
|
|
this.Preloader.prototype.RunCycle = function()
|
|
{
|
|
if(this.queue.length > 0)
|
|
{
|
|
current_object = this.queue.shift();
|
|
|
|
if(current_object.type == "image")
|
|
{
|
|
this._DoCallback(this.callbacks.onstagechange, {
|
|
type: "image",
|
|
description: current_object.path
|
|
});
|
|
|
|
current_image = new Image();
|
|
current_image.onload = this._ReportResourceFinished.bind(this);
|
|
current_image.src = current_object.path;
|
|
|
|
this.images_total++;
|
|
this._engine.resources[current_object.name] = current_image;
|
|
}
|
|
else if(current_object.type == "sound")
|
|
{
|
|
this._DoCallback(this.callbacks.onstagechange, {
|
|
type: "sound",
|
|
description: current_object.path
|
|
});
|
|
|
|
if(this._engine.sound_enabled == true)
|
|
{
|
|
if(soundManager.canPlayURL(current_object.path))
|
|
{
|
|
var sound = soundManager.createSound({
|
|
id: current_object.name,
|
|
url: current_object.path,
|
|
autoLoad: true,
|
|
autoPlay: false,
|
|
onload: this._ReportResourceFinished.bind(this)
|
|
});
|
|
|
|
this.sounds_total++;
|
|
this._engine.resources[current_object.name] = sound;
|
|
}
|
|
else
|
|
{
|
|
throw {
|
|
"name": "SoundError",
|
|
"message": "The sound format is not supported."
|
|
}
|
|
}
|
|
}
|
|
else if(this._engine.sound_ignore_failure == true)
|
|
{
|
|
/* TODO: Log error */
|
|
}
|
|
else
|
|
{
|
|
throw {
|
|
"name": "SoundError",
|
|
"message": "Cannot add audio file because sound support is not enabled in the engine."
|
|
}
|
|
}
|
|
}
|
|
else if(current_object.type == "script")
|
|
{
|
|
this._DoCallback(this.callbacks.onstagechange, {
|
|
type: "script",
|
|
description: current_object.desc
|
|
});
|
|
|
|
$.getScript(current_object.path, this._ReportResourceFinished.bind(this));
|
|
|
|
this.scripts_total++;
|
|
}
|
|
else if(current_object.type == "function")
|
|
{
|
|
this._DoCallback(this.callbacks.onstagechange, {
|
|
type: "function",
|
|
description: current_object.desc
|
|
});
|
|
|
|
var result = current_object.func(this.callbacks);
|
|
|
|
if(result == true)
|
|
{
|
|
this._ReportResourceFinished();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this._DoCallback(this.callbacks.onfinish)
|
|
}
|
|
}
|
|
|
|
/* Check if the preloading sequence is finished and take appropriate action. */
|
|
this.Preloader.prototype._CheckIfDone = function()
|
|
{
|
|
if(this.resources_left <= 0)
|
|
{
|
|
this._DoCallback(this.callbacks.onfinish);
|
|
}
|
|
else
|
|
{
|
|
this.RunCycle();
|
|
}
|
|
}
|
|
|
|
/* Do a pre-specified callback if it exists. */
|
|
this.Preloader.prototype._DoCallback = function(callback, data)
|
|
{
|
|
if(typeof callback != "undefined")
|
|
{
|
|
callback(data);
|
|
}
|
|
}
|
|
|
|
/* Handle successful finishing of the SoundManager2 loading process. */
|
|
this.Preloader.prototype._HandleSM2Ready = function()
|
|
{
|
|
this._engine.sound_enabled = true;
|
|
this._ReportResourceFinished();
|
|
}
|
|
|
|
/* Handle failure of the SoundManager2 loading process. */
|
|
this.Preloader.prototype._HandleSM2Failed = function()
|
|
{
|
|
this._engine.sound_enabled = false;
|
|
this._ReportResourceFinished();
|
|
}
|
|
|
|
/* Mark the currently preloading resource as finished. */
|
|
this.Preloader.prototype._ReportResourceFinished = function()
|
|
{
|
|
this.resources_left--;
|
|
this.resources_done++;
|
|
|
|
this._DoCallback(this.callbacks.onprogress, {
|
|
done: this.resources_done,
|
|
total: this.resources_total,
|
|
left: this.resources_left,
|
|
failures: this.resources_failed
|
|
});
|
|
|
|
this._CheckIfDone();
|
|
}
|
|
|
|
/* Mark the currently preloading resource as failed. */
|
|
this.Preloader.prototype._ReportResourceFailed = function()
|
|
{
|
|
/* TODO: Implement. */
|
|
this.resources_left--;
|
|
this.resources_failed++;
|
|
}
|
|
|
|
|
|
/**--------------------------------------**/
|
|
/** BASE PROPERTIES AND METHODS **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Base = {
|
|
timers: {},
|
|
SetTimer: function(duration, func)
|
|
{
|
|
setTimeout((function(self, f){ return f.call.bind(f, self); })(this, func), duration);
|
|
},
|
|
SetInterval: function(name, interval, func)
|
|
{
|
|
this.timers[name] = setInterval((function(self, f){ return f.call.bind(f, self); })(this, func), interval);
|
|
},
|
|
CancelInterval: function(name)
|
|
{
|
|
clearInterval(this.timers[name]);
|
|
},
|
|
CallEvent: function(func, data)
|
|
{
|
|
if(typeof func !== "undefined")
|
|
{
|
|
return func.call(this, data);
|
|
}
|
|
},
|
|
_BubbleEvent: function(func, eventdata, instances)
|
|
{
|
|
list = instances.slice().reverse();
|
|
|
|
var hit_event = false;
|
|
|
|
for(item in list)
|
|
{
|
|
var object = list[item];
|
|
var result = object.CallEvent(object[func], eventdata);
|
|
|
|
if(typeof result != "undefined")
|
|
{
|
|
if(result == false)
|
|
{
|
|
break;
|
|
}
|
|
else if(result == true)
|
|
{
|
|
hit_event = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hit_event;
|
|
}
|
|
}
|
|
|
|
|
|
/**--------------------------------------**/
|
|
/** SCENES **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Scene = function(name, options)
|
|
{
|
|
if(typeof _engine.scenes[name] !== "undefined")
|
|
{
|
|
throw {
|
|
name: "NameError",
|
|
message: "A scene with the given name already exists."
|
|
}
|
|
}
|
|
|
|
this._engine = _engine;
|
|
this.canvas = null;
|
|
this.instances = [];
|
|
this.dirty = true;
|
|
this.width = 640;
|
|
this.height = 480;
|
|
this.fps = 45;
|
|
this.name = name;
|
|
this.cached_selectors = {};
|
|
this.mouse_coordinates = {x: 0, y: 0};
|
|
this.step_counter = 0;
|
|
this.draw_counter = 0;
|
|
this.current_fps = 0;
|
|
this.date = new Date;
|
|
|
|
$.extend(true, this, options, this._engine.Base);
|
|
|
|
this._engine.scenes[name] = this;
|
|
}
|
|
|
|
this.Scene.prototype.Add = function(object)
|
|
{
|
|
if(typeof object == "string")
|
|
{
|
|
if(typeof this._engine.objects[object] !== "undefined")
|
|
{
|
|
var instance = this._engine.CreateInstance(object);
|
|
}
|
|
else
|
|
{
|
|
throw {
|
|
"name": "ObjectError",
|
|
"message": "The specified object does not exist."
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var instance = object;
|
|
}
|
|
|
|
instance.scene = this;
|
|
this.instances.push(instance);
|
|
this.dirty = true;
|
|
}
|
|
|
|
this.Scene.prototype.Attach = function(canvas)
|
|
{
|
|
if(typeof $(canvas).data("current-scene") !== "undefined")
|
|
{
|
|
/* A different scene was previously attached to this canvas.
|
|
* Let's detach it first. */
|
|
var previous_scene = $(canvas).data("current-scene");
|
|
this._engine.scenes[previous_scene].Detach();
|
|
}
|
|
|
|
this.canvas = canvas;
|
|
this._engine.canvas = canvas;
|
|
canvas.width = this.width;
|
|
canvas.height = this.height;
|
|
$(canvas).data("current-scene", this.name);
|
|
|
|
$(canvas).click(this._ProcessClickEvent.bind(this));
|
|
$(canvas).mousemove(this._ProcessMouseMoveEvent.bind(this));
|
|
|
|
this.CallEvent(this.OnLoad);
|
|
this._Initialize();
|
|
}
|
|
|
|
this.Scene.prototype.Detach = function()
|
|
{
|
|
$(this.canvas).unbind('click');
|
|
$(this.canvas).unbind('mousemove');
|
|
$(this.canvas).removeData("current-scene");
|
|
this.canvas = null;
|
|
}
|
|
|
|
this.Scene.prototype.DrawText = function(text, options)
|
|
{
|
|
var type = "fill", color = "#000000", x = 0, y = 0;
|
|
|
|
if(typeof options.outline != "undefined" && options.outline)
|
|
{
|
|
type = "outline";
|
|
}
|
|
|
|
if(typeof options.color != "undefined")
|
|
{
|
|
color = options.color;
|
|
}
|
|
|
|
if(typeof options.x != "undefined")
|
|
{
|
|
x = options.x;
|
|
}
|
|
|
|
if(typeof options.y != "undefined")
|
|
{
|
|
y = options.y;
|
|
}
|
|
|
|
ctx = this._GetTextContext(options);
|
|
|
|
if(type == "fill")
|
|
{
|
|
ctx.fillStyle = color;
|
|
ctx.fillText(text, x, y);
|
|
}
|
|
else if(type == "outline")
|
|
{
|
|
ctx.strokeStyle = color;
|
|
ctx.outlineText(text, x, y);
|
|
}
|
|
|
|
ctx.restore();
|
|
}
|
|
|
|
this.Scene.prototype.DrawTextCentered = function(text, options)
|
|
{
|
|
var x = 0, scale = 1, width = this.GetTextWidth(text, options);
|
|
|
|
if(typeof options.x != "undefined")
|
|
{
|
|
x = options.x;
|
|
}
|
|
|
|
if(typeof options.scale != "undefined")
|
|
{
|
|
scale = options.scale;
|
|
}
|
|
|
|
x = x - ((width / 2) * scale * scale);
|
|
|
|
options.x = x;
|
|
|
|
this.DrawText(text, options);
|
|
}
|
|
|
|
this.Scene.prototype.GetTextWidth = function(text, options)
|
|
{
|
|
ctx = this._GetTextContext(options);
|
|
var width = ctx.measureText(text).width;
|
|
ctx.restore();
|
|
|
|
return width;
|
|
}
|
|
|
|
this.Scene.prototype.Redraw = function()
|
|
{
|
|
this.dirty = true;
|
|
}
|
|
|
|
this.Scene.prototype.$ = function(selector)
|
|
{
|
|
if(typeof this.cached_selectors[selector] != "undefined")
|
|
{
|
|
return this.cached_selectors[selector];
|
|
}
|
|
else
|
|
{
|
|
list = this._SelectInstances(this, this.instances, selector);
|
|
this.cached_selectors[selector] = list;
|
|
return list;
|
|
}
|
|
}
|
|
|
|
this.Scene.prototype._Draw = function()
|
|
{
|
|
this.draw_count += 1;
|
|
|
|
if(this.canvas !== null)
|
|
{
|
|
var ctx = this.canvas.getContext("2d");
|
|
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
|
|
for(i in this.instances)
|
|
{
|
|
this.instances[i]._Draw();
|
|
}
|
|
}
|
|
}
|
|
|
|
this.Scene.prototype._GetTextContext = function(options)
|
|
{
|
|
var weight = "normal", style = "normal", font = "sans-serif", size = 16, alpha = 1, scale = 1;
|
|
|
|
if(typeof options.bold != "undefined" && options.bold)
|
|
{
|
|
weight = "bold";
|
|
}
|
|
|
|
if(typeof options.italic != "undefined" && options.italic)
|
|
{
|
|
style = "italic";
|
|
}
|
|
|
|
if(typeof options.size != "undefined")
|
|
{
|
|
size = options.size;
|
|
}
|
|
|
|
if(typeof options.font != "undefined")
|
|
{
|
|
font = options.font;
|
|
}
|
|
|
|
if(typeof options.alpha != "undefined")
|
|
{
|
|
alpha = options.alpha;
|
|
}
|
|
|
|
if(typeof options.scale != "undefined")
|
|
{
|
|
scale = options.scale;
|
|
}
|
|
|
|
var ctx = this.canvas.getContext("2d");
|
|
|
|
ctx.save();
|
|
|
|
ctx.font = weight + " " + style + " " + size + "px '" + font + "'";
|
|
ctx.globalAlpha = alpha;
|
|
ctx.scale(scale, scale);
|
|
|
|
return ctx;
|
|
}
|
|
|
|
this.Scene.prototype._Initialize = function(event)
|
|
{
|
|
this.last_timestamp = this.date.getTime();
|
|
this.SetInterval("_step", (1000/this.fps), this._Step);
|
|
}
|
|
|
|
this.Scene.prototype._ProcessClickEvent = function(event)
|
|
{
|
|
var coordinates = this.canvas.relativeCoordinates(event);
|
|
|
|
var changed = this._BubbleEvent("_HandleClickEvent", {
|
|
x: coordinates.x,
|
|
y: coordinates.y,
|
|
button: event.which
|
|
}, this.$("/" + coordinates.x + "," + coordinates.y));
|
|
|
|
if(changed == true)
|
|
{
|
|
this.dirty = true;
|
|
}
|
|
}
|
|
|
|
this.Scene.prototype._ProcessMouseMoveEvent = function(event)
|
|
{
|
|
var coordinates = this.canvas.relativeCoordinates(event);
|
|
this.mouse_coordinates = coordinates;
|
|
this.mouse_moved = true;
|
|
}
|
|
|
|
this.Scene.prototype._SelectInstances = function(context, items, selector)
|
|
{
|
|
var segments = selector.split("/");
|
|
segments.shift();
|
|
|
|
methods = {
|
|
"coordinate": {
|
|
"regex": /^(!?)([0-9]+),([0-9]+)$/i,
|
|
"action": function(context, items, match){
|
|
var inverted = (match[1] == "!");
|
|
var x = match[2];
|
|
var y = match[3];
|
|
|
|
return items.filter(function(object){
|
|
var hit = false;
|
|
|
|
if(object.draw_self === true)
|
|
{
|
|
var sprite = this._engine.GetSprite(object.sprite);
|
|
|
|
if(x >= object.x && x < (object.x + sprite.width) && y > object.y && y < (object.y + sprite.height))
|
|
{
|
|
/* Initial region test succeeded.
|
|
* TODO: Add bounding rectangles. */
|
|
var relative_x = x - object.x;
|
|
var relative_y = y - object.y;
|
|
|
|
if(object.precise_collision == false)
|
|
{
|
|
hit = true;
|
|
}
|
|
else
|
|
{
|
|
alpha = sprite.GetAlpha(relative_x, relative_y) / 255;
|
|
|
|
if(alpha > object.collision_tolerance)
|
|
{
|
|
/* Alpha hit. */
|
|
hit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(inverted == true)
|
|
{
|
|
return !hit;
|
|
}
|
|
else
|
|
{
|
|
return hit;
|
|
}
|
|
});
|
|
}
|
|
},
|
|
"object": {
|
|
"regex": /^(!?)([a-z_][a-z0-9_+-]*)$/i,
|
|
"action": function(context, items, match){
|
|
inverted = (match[1] == "!");
|
|
name = match[2];
|
|
|
|
return items.filter(function(object){
|
|
if(inverted)
|
|
{
|
|
return !(name == object.name);
|
|
}
|
|
else
|
|
{
|
|
return (name == object.name);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i in segments)
|
|
{
|
|
var segment = segments[i];
|
|
|
|
for(m in methods)
|
|
{
|
|
var method = methods[m];
|
|
var match = method.regex.exec(segment);
|
|
|
|
if(match != null)
|
|
{
|
|
items = method.action(context, items, match);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return items;
|
|
}
|
|
|
|
this.Scene.prototype._Step = function(event)
|
|
{
|
|
this.step_counter++;
|
|
|
|
if(this.step_counter == this.fps)
|
|
{
|
|
this.step_counter = 0;
|
|
|
|
var date = new Date;
|
|
var current_timestamp = date.getTime()
|
|
this.current_fps = (1000 / (current_timestamp - this.last_timestamp)) * this.fps;
|
|
this.last_timestamp = current_timestamp;
|
|
}
|
|
|
|
if(this.mouse_moved == true)
|
|
{
|
|
this.dirty = this.CallEvent(this.OnMouseMove, this.mouse_coordinates) ? true : this.dirty;
|
|
|
|
var select = function(prefix, coordinates)
|
|
{
|
|
var selector = "/" + prefix + coordinates.x + "," + coordinates.y;
|
|
return this.$(selector);
|
|
}.bind(this);
|
|
|
|
this.dirty = this._BubbleEvent("_HandleMouseOutEvent", this.mouse_coordinates, select("!", this.mouse_coordinates)) ? true : this.dirty;
|
|
this.dirty = this._BubbleEvent("_HandleMouseOverEvent", this.mouse_coordinates, select("", this.mouse_coordinates)) ? true : this.dirty;
|
|
this.dirty = this._BubbleEvent("_HandleMouseMoveEvent", this.mouse_coordinates, this.$("/")) ? true : this.dirty;
|
|
|
|
this.mouse_moved = false;
|
|
}
|
|
|
|
if(this.CallEvent(this.OnStep, {}) == true)
|
|
{
|
|
this.dirty = true;
|
|
}
|
|
|
|
if(this._BubbleEvent("_HandleStepEvent", {}, this.instances) == true)
|
|
{
|
|
this.dirty = true;
|
|
}
|
|
|
|
if(this.dirty == true)
|
|
{
|
|
this.dirty = false;
|
|
this.cached_selectors = {};
|
|
this._Draw();
|
|
}
|
|
}
|
|
|
|
|
|
/**--------------------------------------**/
|
|
/** SOUNDS **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Sound = function(name, source)
|
|
{
|
|
if(typeof _engine.sounds[name] !== "undefined")
|
|
{
|
|
throw {
|
|
name: "NameError",
|
|
message: "A sound with the given name already exists."
|
|
}
|
|
}
|
|
|
|
if(typeof _engine.resources[source] === "undefined")
|
|
{
|
|
throw {
|
|
name: "ResourceError",
|
|
message: "The specified resource does not exist."
|
|
}
|
|
}
|
|
|
|
this._engine = _engine;
|
|
this.source = this._engine.resources[source];
|
|
this.name = name;
|
|
|
|
this._engine.sounds[name] = this;
|
|
}
|
|
|
|
this.Sound.prototype.Play = function(options)
|
|
{
|
|
if(this._engine.sound_enabled == true)
|
|
{
|
|
this.source.play(options);
|
|
}
|
|
}
|
|
|
|
|
|
/**--------------------------------------**/
|
|
/** OBJECTS **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Object = function(name, proto)
|
|
{
|
|
if(typeof _engine.objects[name] !== "undefined")
|
|
{
|
|
throw {
|
|
name: "NameError",
|
|
message: "An object with the given name already exists."
|
|
}
|
|
}
|
|
|
|
/* Base settings */
|
|
this._engine = _engine;
|
|
this.name = "";
|
|
|
|
/* Metrics */
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.width = 0;
|
|
this.height = 0;
|
|
this.alpha = 1;
|
|
|
|
/* Collision handling */
|
|
this.collision_tolerance = 0.2;
|
|
this.precise_collision = false;
|
|
|
|
/* Internal variables */
|
|
this.draw_self = (typeof proto.sprite !== "undefined");
|
|
this.scene = null;
|
|
this.last_moused_over = false;
|
|
|
|
/* Create a constructor for the object. */
|
|
var skeleton = new Function();
|
|
$.extend(true, skeleton.prototype, this, this._engine.Base, proto);
|
|
skeleton.prototype.name = name;
|
|
|
|
/* Store constructor and return prototype. */
|
|
this._engine.objects[name] = skeleton;
|
|
return skeleton.prototype;
|
|
}
|
|
|
|
this.Object.prototype.CreateInstance = function(vars)
|
|
{
|
|
return this._engine.CreateInstance(this, vars);
|
|
}
|
|
|
|
this.Object.prototype.Destroy = function()
|
|
{
|
|
this.CallEvent(this.OnDestroy);
|
|
|
|
if(this.scene != null)
|
|
{
|
|
var index = this.scene.instances.indexOf(this);
|
|
|
|
if(index != -1)
|
|
{
|
|
this.scene.instances.splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.Object.prototype._Draw = function()
|
|
{
|
|
if(this.draw_self == true && typeof this.sprite !== "undefined")
|
|
{
|
|
if(typeof this._engine.sprites[this.sprite] === "undefined")
|
|
{
|
|
throw {
|
|
name: "SpriteError",
|
|
message: "The specified sprite does not exist."
|
|
}
|
|
}
|
|
|
|
this._engine.sprites[this.sprite].Draw(this.scene.canvas, this);
|
|
}
|
|
|
|
this.CallEvent(this.OnDraw, {canvas: this.scene.canvas});
|
|
}
|
|
|
|
this.Object.prototype._HandleClickEvent = function(event)
|
|
{
|
|
var relative_x = event.x - this.x;
|
|
var relative_y = event.y - this.y;
|
|
|
|
events = {
|
|
1: this.OnClick,
|
|
2: this.OnMiddleClick,
|
|
3: this.OnRightClick
|
|
}
|
|
|
|
func = events[event.button];
|
|
|
|
this.CallEvent(func, {
|
|
x: event.x,
|
|
y: event.y,
|
|
button: event.button,
|
|
relative_x: relative_x,
|
|
relative_y: relative_y
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
this.Object.prototype._HandleMouseMoveEvent = function(event)
|
|
{
|
|
this.CallEvent(this.OnMouseMove, {
|
|
x: event.x,
|
|
y: event.y
|
|
});
|
|
return true;
|
|
}
|
|
|
|
this.Object.prototype._HandleMouseOutEvent = function(event)
|
|
{
|
|
if(this.last_moused_over == true)
|
|
{
|
|
this.last_moused_over = false;
|
|
|
|
this.CallEvent(this.OnMouseOut, {
|
|
x: event.x,
|
|
y: event.y
|
|
});
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
this.Object.prototype._HandleMouseOverEvent = function(event)
|
|
{
|
|
if(this.last_moused_over == false)
|
|
{
|
|
var relative_x = event.x - this.x;
|
|
var relative_y = event.y - this.y;
|
|
|
|
this.last_moused_over = true;
|
|
|
|
this.CallEvent(this.OnMouseOver, {
|
|
x: event.x,
|
|
y: event.y,
|
|
relative_x: relative_x,
|
|
relative_y: relative_y
|
|
});
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
this.Object.prototype._HandleStepEvent = function(event)
|
|
{
|
|
if(typeof this.sprite != "undefined")
|
|
{
|
|
var sprite = this._engine.sprites[this.sprite];
|
|
this.width = sprite.width;
|
|
this.height = sprite.height;
|
|
}
|
|
|
|
return this.CallEvent(this.OnStep, event);
|
|
}
|
|
|
|
/**--------------------------------------**/
|
|
/** SPRITES **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Sprite = function(name, source, options)
|
|
{
|
|
if(typeof _engine.sprites[name] !== "undefined")
|
|
{
|
|
throw {
|
|
name: "NameError",
|
|
message: "A sprite with the given name already exists."
|
|
}
|
|
}
|
|
|
|
if(typeof _engine.resources[source] === "undefined")
|
|
{
|
|
throw {
|
|
name: "ResourceError",
|
|
message: "The specified resource does not exist."
|
|
}
|
|
}
|
|
|
|
this._engine = _engine;
|
|
this.source = this._engine.resources[source];
|
|
this.name = name;
|
|
|
|
if(typeof options.tile_x !== "undefined" && typeof options.tile_y !== "undefined" && typeof options.tile_w !== "undefined" && typeof options.tile_h !== "undefined")
|
|
{
|
|
this.tile_x = options.tile_x;
|
|
this.tile_y = options.tile_y;
|
|
this.tile_w = options.tile_w;
|
|
this.tile_h = options.tile_h;
|
|
this.tile = true;
|
|
}
|
|
else if(typeof options.tile_x !== "undefined" || typeof options.tile_y !== "undefined" || typeof options.tile_w !== "undefined" || typeof options.tile_h !== "undefined")
|
|
{
|
|
throw {
|
|
name: "SpriteError",
|
|
message: "Only a part of the tile parameters were specified."
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.tile = false;
|
|
}
|
|
|
|
/* Store image data for click events and collision detection. */
|
|
var collision_canvas = document.createElement("canvas");
|
|
var width, height;
|
|
|
|
if(this.tile == false)
|
|
{
|
|
width = this.source.width;
|
|
height = this.source.height;
|
|
}
|
|
else if(this.tile == true)
|
|
{
|
|
width = this.tile_w;
|
|
height = this.tile_h;
|
|
}
|
|
|
|
collision_canvas.width = this.width = width;
|
|
collision_canvas.height = this.height = height;
|
|
|
|
this.Draw(collision_canvas, null, {x: 0, y: 0, alpha: 1});
|
|
|
|
ctx = collision_canvas.getContext("2d");
|
|
this.image_data = ctx.getImageData(0, 0, width, height);
|
|
|
|
delete collision_canvas;
|
|
|
|
/* Store in engine. */
|
|
this._engine.sprites[name] = this;
|
|
}
|
|
|
|
this.Sprite.prototype.Draw = function(canvas, object, options)
|
|
{
|
|
ctx = canvas.getContext("2d");
|
|
|
|
if((typeof object == "undefined" || object == null) && (typeof options == "undefined" || typeof options.x == "undefined" || typeof options.y == "undefined"))
|
|
{
|
|
throw {
|
|
name: "DrawError",
|
|
message: "No drawing coordinates were specified."
|
|
}
|
|
}
|
|
else if(typeof object == "undefined" || object == null)
|
|
{
|
|
var x = options.x;
|
|
var y = options.y;
|
|
}
|
|
else
|
|
{
|
|
var x = object.x;
|
|
var y = object.y;
|
|
}
|
|
|
|
if(typeof options != "undefined" && typeof options.alpha != "undefined")
|
|
{
|
|
var alpha = options.alpha;
|
|
}
|
|
else if(typeof object != "undefined" && object != null)
|
|
{
|
|
var alpha = object.alpha;
|
|
}
|
|
else
|
|
{
|
|
throw {
|
|
name: "DrawError",
|
|
message: "No alpha value was specified."
|
|
}
|
|
}
|
|
|
|
ctx.globalAlpha = alpha;
|
|
|
|
if(this.tile == true)
|
|
{
|
|
ctx.drawImage(this.source, this.tile_x, this.tile_y, this.tile_w, this.tile_h, x, y, this.tile_w, this.tile_h);
|
|
}
|
|
else
|
|
{
|
|
ctx.drawImage(this.source, x, y);
|
|
}
|
|
}
|
|
|
|
this.Sprite.prototype.GetAlpha = function(x, y)
|
|
{
|
|
var key = (((y * this.width) + x) * 4) + 3;
|
|
return this.image_data.data[key];
|
|
}
|
|
|
|
/**--------------------------------------**/
|
|
/** ENGINE FUNCTIONS **/
|
|
/**--------------------------------------**/
|
|
|
|
this.AddItems = function(items)
|
|
{
|
|
if(typeof items.sprites != "undefined")
|
|
{
|
|
|
|
for(name in items.sprites)
|
|
{
|
|
if(typeof items.sprites[name] == "string")
|
|
{
|
|
/* Stand-alone sprite. */
|
|
new _engine.Sprite(name, items.sprites[name], {});
|
|
}
|
|
else
|
|
{
|
|
/* Probably a tileset. */
|
|
new _engine.Sprite(name, items.sprites[name], {
|
|
tile_x: items.sprites[name].tile_x,
|
|
tile_y: items.sprites[name].tile_y,
|
|
tile_w: items.sprites[name].tile_w,
|
|
tile_h: items.sprites[name].tile_h,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if(typeof items.sounds != "undefined")
|
|
{
|
|
for(name in items.sounds)
|
|
{
|
|
new _engine.Sound(name, items.sounds[name]);
|
|
}
|
|
}
|
|
|
|
if(typeof items.objects != "undefined")
|
|
{
|
|
for(name in items.objects)
|
|
{
|
|
new _engine.Object(name, items.objects[name]);
|
|
}
|
|
}
|
|
|
|
if(typeof items.scenes != "undefined")
|
|
{
|
|
for(name in items.scenes)
|
|
{
|
|
new _engine.Scene(name, items.scenes[name]);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.CreateInstance = function(object, vars)
|
|
{
|
|
this.instance_increment++;
|
|
|
|
if(typeof data == "undefined")
|
|
{
|
|
var data = {id: this.instance_increment};
|
|
}
|
|
else
|
|
{
|
|
data.id = this.instance_increment;
|
|
}
|
|
|
|
if(typeof object == "string")
|
|
{
|
|
var instance = new this.objects[object]();
|
|
}
|
|
else
|
|
{
|
|
var skeleton = new Function();
|
|
skeleton.prototype = object;
|
|
var instance = new skeleton();
|
|
}
|
|
|
|
$.extend(true, instance, vars);
|
|
|
|
/* Call creation event. */
|
|
instance.CallEvent(instance.OnCreate, {});
|
|
|
|
return instance;
|
|
}
|
|
|
|
this.GetObject = function(name)
|
|
{
|
|
return this.objects[name].prototype;
|
|
}
|
|
|
|
this.GetScene = function(name)
|
|
{
|
|
return this.scenes[name];
|
|
}
|
|
|
|
this.GetSound = function(name)
|
|
{
|
|
return this.sounds[name];
|
|
}
|
|
|
|
this.GetSprite = function(name)
|
|
{
|
|
return this.sprites[name];
|
|
}
|
|
|
|
/**--------------------------------------**/
|
|
/** STANDARD LIBRARY **/
|
|
/**--------------------------------------**/
|
|
|
|
this.Math = {};
|
|
|
|
this.Math.Abs = Math.abs;
|
|
this.Math.Absolute = Math.abs;
|
|
this.Math.Acos = Math.acos;
|
|
this.Math.Arccosine = Math.acos;
|
|
this.Math.Asin = Math.asin;
|
|
this.Math.Arcsine = Math.asin;
|
|
this.Math.Atan = Math.atan;
|
|
this.Math.Arctangent = Math.atan;
|
|
this.Math.Atan2 = Math.atan2;
|
|
this.Math.Arctangent2 = Math.atan2;
|
|
this.Math.Ceil = Math.ceil;
|
|
this.Math.Ceiling = Math.ceil;
|
|
this.Math.Cos = Math.cos;
|
|
this.Math.Cosine = Math.cos;
|
|
this.Math.Exp = Math.exp;
|
|
this.Math.Floor = Math.floor;
|
|
this.Math.Log = Math.log;
|
|
this.Math.Logarithm = Math.log;
|
|
this.Math.Min = Math.min;
|
|
this.Math.Minimum = Math.min;
|
|
this.Math.Max = Math.max;
|
|
this.Math.Maximum = Math.max;
|
|
this.Math.Pow = Math.pow;
|
|
this.Math.Power = Math.pow;
|
|
this.Math.Round = Math.round;
|
|
this.Math.Sin = Math.sin;
|
|
this.Math.Sine = Math.sin;
|
|
this.Math.Sqrt = Math.sqrt;
|
|
this.Math.SquareRoot = Math.sqrt;
|
|
this.Math.Tan = Math.tan;
|
|
this.Math.Tangent = Math.tan;
|
|
|
|
this.Random = {};
|
|
|
|
this.Random.Choose = function()
|
|
{
|
|
if(arguments.length == 0)
|
|
{
|
|
/* The user passed in nothing. Bail out. */
|
|
throw {
|
|
name: "ArgumentError",
|
|
message: "No arguments were specified."
|
|
}
|
|
}
|
|
else if(arguments.length == 1 && typeof arguments[0].length != "undefined")
|
|
{
|
|
/* The user passed in an array. */
|
|
arguments = arguments[0];
|
|
}
|
|
|
|
return arguments[Math.floor(Math.random() * arguments.length)];
|
|
}
|
|
|
|
this.Random.Number = function(floor, ceiling, precision)
|
|
{
|
|
var floor = (typeof floor == "undefined") ? 0 : floor;
|
|
var ceiling = (typeof ceiling == "undefined") ? 1 : ceiling;
|
|
var precision = (typeof ceiling == "undefined") ? 0.00000001 : precision;
|
|
|
|
var base_number = Math.random();
|
|
var width = Math.abs(ceiling - floor);
|
|
var rounding_factor = 1 / precision;
|
|
|
|
var multiplied = floor + (base_number * width);
|
|
return Math.floor(multiplied * rounding_factor) / rounding_factor;
|
|
}
|
|
|
|
this.Random.Pick = function()
|
|
{
|
|
var chosen = [];
|
|
var results = [];
|
|
var _arguments = Array.prototype.slice.call(arguments);
|
|
var count = _arguments.shift();
|
|
|
|
if(arguments.length == 0)
|
|
{
|
|
/* The user passed in nothing. Bail out. */
|
|
throw {
|
|
name: "ArgumentError",
|
|
message: "No arguments were specified."
|
|
}
|
|
}
|
|
else if(_arguments.length == 1 && typeof _arguments[0].length != "undefined")
|
|
{
|
|
/* The user passed in an array. */
|
|
_arguments = _arguments[0];
|
|
}
|
|
|
|
if(count > _arguments.length)
|
|
{
|
|
/* The user requested more items than exist in the arguments. */
|
|
throw {
|
|
name: "ArgumentError",
|
|
message: "Not enough arguments were specified. The amount of specified items must be equal to or larger than the requested amount."
|
|
}
|
|
}
|
|
|
|
for(var i = 0; i < count; i++)
|
|
{
|
|
var id = 0;
|
|
|
|
do
|
|
{
|
|
id = Math.floor(Math.random() * _arguments.length);
|
|
} while (chosen.indexOf(id) != -1)
|
|
|
|
chosen.push(id);
|
|
results.push(_arguments[id]);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
this.Random.String = function(length, alphabet)
|
|
{
|
|
if(typeof alphabet == "undefined")
|
|
{
|
|
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
}
|
|
|
|
rand = "";
|
|
|
|
for(i = 0; i < length; i++)
|
|
{
|
|
rand += alphabet[Math.floor(Math.random() * alphabet.length)];
|
|
}
|
|
|
|
return rand;
|
|
}
|
|
|
|
this.Draw = {};
|
|
|
|
this.Draw.Text = function(x, y, text, options)
|
|
{
|
|
|
|
}
|
|
|
|
this.Draw.Rectangle = function(x1, y1, x2, y2, options)
|
|
{
|
|
|
|
}
|
|
|
|
this.Draw.Line = function(x1, y1, x2, y2, options)
|
|
{
|
|
|
|
}
|
|
|
|
this.Draw.BoxEllipse = function(x1, y1, x2, y2, options)
|
|
{
|
|
var x = (x1 + x2) / 2;
|
|
var y = (y1 + y2) / 2;
|
|
var rx = (x2 - x1) / 2;
|
|
var ry = (y2 - y1) / 2;
|
|
|
|
this.RadiusEllipse(x, y, rx, ry, options);
|
|
}
|
|
|
|
this.Draw.RadiusEllipse = function(x, y, rx, ry, options)
|
|
{
|
|
var canvas = $("#gamecanvas")[0];
|
|
var ctx = canvas.getContext("2d");
|
|
ctx.beginPath();
|
|
|
|
if(rx == ry)
|
|
{
|
|
/* Circle. */
|
|
ctx.arc(x, y, rx, 0, 2 * Math.PI, false);
|
|
}
|
|
else
|
|
{
|
|
/* Ellipse. */
|
|
var step = 0.1
|
|
|
|
ctx.moveTo(x + rx, y);
|
|
|
|
for (var i = 0; i < Math.PI * 2 + step; i += step)
|
|
{
|
|
ctx.lineTo(x + Math.cos(i) * rx, y + Math.sin(i) * ry);
|
|
}
|
|
}
|
|
|
|
ctx.lineWidth = 1;
|
|
ctx.strokeStyle = 'black';
|
|
ctx.stroke();
|
|
}
|
|
|
|
this.Draw.BoxPolygon = function(x1, y1, x2, y2, sides, options)
|
|
{
|
|
|
|
}
|
|
|
|
this.Draw.RadiusPolygon = function(x, y, radius, sides, options)
|
|
{
|
|
|
|
}
|
|
}
|