/* * gameQuery rev. $Revision$ * * Copyright (c) 2008 Selim Arsever (gamequery.onaluf.org) * licensed under the MIT (MIT-LICENSE.txt) */ // this allow to used the convenient $ notation in a plugins (function($) { $.extend({ gameQuery: { /** * This is the Animation Object */ Animation: function (options) { // private default values var defaults = { imageURL: "", numberOfFrame: 1, delta: 32, rate: 30, type: 0, distance: 0 }; // options extends defaults options = $.extend(defaults, options); //"public" attributes: this.imageURL = options.imageURL; // The url of the image to be used as an animation or sprite this.numberOfFrame = options.numberOfFrame;// The number of frame to be displayed when playing the animation this.delta = options.delta; // The the distance in pixels between two frame this.rate = options.rate; // The rate at which the frame must be played in miliseconds this.type = options.type; // The type of the animation.This is bitwise OR of the properties. this.distance = options.distance; // The the distance in pixels between two animation //Whenever a new animation is created we add it to the ResourceManager animation list $.gameQuery.resourceManager.addAnimation(this); return true; }, // "constants" for the different type of an animation ANIMATION_VERTICAL: 1, // genertated by a verical offset of the background ANIMATION_HORIZONTAL: 2, // genertated by a horizontal offset of the background ANIMATION_ONCE: 4, // played only once (else looping indefinitly) ANIMATION_CALLBACK: 8, // A callack is exectued at the end of a cycle ANIMATION_MULTI: 16, // The image file contains many animations // basic values playground: null, refreshRate: 30, /** * An object to manages the resources loading **/ resourceManager: { animations: [], // List of animation / images used in the game sounds: [], // List of sounds used in the game callbacks: [], // List of the functions called at each refresh running: false, // State of the game, /** * This function the covers things to load befor to start the game. **/ preload: function() { //Start loading the images for (var i in this.animations){ this.animations[i].domO = new Image(); this.animations[i].domO.src = this.animations[i].imageURL; } //Start loading the sounds for (var i in this.sounds){ this.sounds[i].load(); } $.gameQuery.resourceManager.waitForResources(); }, /** * This function the waits for all the resources called for in preload() to finish loading. **/ waitForResources: function() { var loadbarEnabled = ($.gameQuery.loadbar != undefined); if(loadbarEnabled){ $($.gameQuery.loadbar.id).width(0); var loadBarIncremant = $.gameQuery.loadbar.width / ($.gameQuery.resourceManager.animations.length + $.gameQuery.resourceManager.sounds.length); } //check the images var imageCount = 0; for(var i=0; i < $.gameQuery.resourceManager.animations.length; i++){ if($.gameQuery.resourceManager.animations[i].domO.complete){ imageCount++; } } //check the sounds var soundCount = 0; for(var i=0; i < $.gameQuery.resourceManager.sounds.length; i++){ if($.gameQuery.resourceManager.sounds[i].ready){ soundCount++; } } //update the loading bar if(loadbarEnabled){ $("#"+$.gameQuery.loadbar.id).width((imageCount+soundCount)*loadBarIncremant); } if(imageCount < ($.gameQuery.resourceManager.animations.length + $.gameQuery.resourceManager.sounds.length)){ imgWait=setTimeout('jQuery.gameQuery.resourceManager.waitForResources()', 100); } else { // all the resources are loaded! // We can associate the animation's images to their coresponding sprites $(".sceengraph").children().each(function(){ // recursive call on the children: $(this).children().each(arguments.callee); // add the image as a background if(this.gameQuery && this.gameQuery.animation){ $(this).css("background-image", "url("+this.gameQuery.animation.imageURL+")"); // we set the correct kind of repeat if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) { $(this).css("background-repeat", "repeat-x"); } else if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) { $(this).css("background-repeat", "repeat-y"); } else { $(this).css("background-repeat", "no-repeat"); } } }); // And launch the refresh loop $.gameQuery.resourceManager.running = true; setInterval("jQuery.gameQuery.resourceManager.refresh()",($.gameQuery.refreshRate)); if($.gameQuery.startCallback){ $.gameQuery.startCallback(); } //make the sceengraph visible $.gameQuery.playground.children(".sceengraph").css("visibility","visible"); } }, /** * This function refresh a unique sprite **/ refreshSprite: function() { //Call this function on all the children: $(this).children().each($.gameQuery.resourceManager.refreshSprite); // is 'this' a sprite ? if(this.gameQuery != undefined){ // does 'this' has an animation ? if(this.gameQuery.animation != null){ //Do we have anything to do? if(this.gameQuery.idleCounter == this.gameQuery.animation.rate-1){ // does 'this' loops? if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_ONCE){ if(this.gameQuery.currentFrame < this.gameQuery.animation.numberOfFrame-2){ this.gameQuery.currentFrame++; } else if(this.gameQuery.currentFrame == this.gameQuery.animation.numberOfFrame-2) { this.gameQuery.currentFrame++; // does 'this' has a callback ? if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){ if(this.gameQuery.callback != null){ this.gameQuery.callback(this); } } } } else { this.gameQuery.currentFrame = (this.gameQuery.currentFrame+1)%this.gameQuery.animation.numberOfFrame; if(this.gameQuery.currentFrame == this.gameQuery.animation.numberOfFrame-1){ // does 'this' has a callback ? if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){ if(this.gameQuery.callback != null){ this.gameQuery.callback(this); } } } } // update the background: if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL){ if($(this).data("multi")){ $(this).css("background-position",""+$(this).data("multi")+"px "+(-this.gameQuery.animation.delta*this.gameQuery.currentFrame)+"px"); } else { $(this).css("background-position","0px "+(-this.gameQuery.animation.delta*this.gameQuery.currentFrame)+"px"); } } else if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) { if($(this).data("multi")){ $(this).css("background-position",""+(-this.gameQuery.animation.delta*this.gameQuery.currentFrame)+"px "+$(this).data("multi")+"px"); } else { $(this).css("background-position",""+(-this.gameQuery.animation.delta*this.gameQuery.currentFrame)+"px 0px"); } } } this.gameQuery.idleCounter = (this.gameQuery.idleCounter+1)%this.gameQuery.animation.rate; } } return true; }, /** * This function is called periodically to refresh the state of the game. **/ refresh: function() { $(".sceengraph").children().each(this.refreshSprite); var deadCallback= new Array(); for (var i in this.callbacks){ if(this.callbacks[i].idleCounter == this.callbacks[i].rate-1){ var returnedValue = this.callbacks[i].fn(); if(typeof returnedValue == 'boolean'){ // if we have a boolean: 'true' means 'no more execution', 'false' means 'execute once more' if(returnedValue){ deadCallback.push(parseInt(i)); } } else if(typeof returnedValue == 'number') { // if we have a number it re-defines the time to the nex call this.callbacks[i].rate = parseInt(returnedValue/$.gameQuery.refreshRate); this.callbacks[i].idleCounter = 0; } } this.callbacks[i].idleCounter = (this.callbacks[i].idleCounter+1)%this.callbacks[i].rate; } for (i in deadCallback){ this.callbacks.splice(deadCallback[i],1); } }, addAnimation: function(animation) { if($.inArray(animation,this.animations)<0){ //normalize the animationRate: animation.rate = parseInt(animation.rate/$.gameQuery.refreshRate); if(animation.rate==0){ animation.rate = 1; } this.animations.push(animation); } }, addSound: function(sound){ if($.inArray(sound,this.sounds)<0){ this.sounds.push(sound); } }, registerCallback: function(fn, rate){ rate = parseInt(rate/$.gameQuery.refreshRate); if(rate==0){ rate = 1; } this.callbacks.push({fn: fn, rate: rate, idleCounter: 0}); } } }}); $.fn.extend({ /** * Define the div to use for the display the game and initailize it. * This could be called on any node it doesn't matter. * The returned node is the playground node. * This IS a desrtuctive call **/ playground: function(div, options) { if(div != undefined){ options = $.extend({ height: 320, width: 480, refreshRate: 30, position: "absolute", keyTracker: false }, options); //We save the playground node and set some variable for this node: $.gameQuery.playground = $(div); $.gameQuery.refreshRate = options.refreshRate; $.gameQuery.playground[0].height = options.height; $.gameQuery.playground[0].width = options.width; // We initialize the apearance of the div $.gameQuery.playground.css("position", options.position); $.gameQuery.playground.css("display", "block"); $.gameQuery.playground.css("overflow","hidden"); $.gameQuery.playground.height(options.height); $.gameQuery.playground.width(options.width); // We create the sceen graph: $.gameQuery.playground.append("