gamequery-0.7.0.js 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738
  1. /*
  2. * gameQuery rev. 0.7.0
  3. *
  4. * Copyright (c) 2012 Selim Arsever (http://gamequeryjs.com)
  5. * licensed under the MIT-License
  6. */
  7. // This allows use of the convenient $ notation in a plugin
  8. (function($) {
  9. // This prefix can be use whenever needed to namespace CSS classes, .data() inputs aso.
  10. var gQprefix = "gQ_";
  11. // Those are the possible states of the engine
  12. var STATE_NEW = 0; // Not yet started for the first time
  13. var STATE_RUNNING = 1; // Started and running
  14. var STATE_PAUSED = 2; // Paused
  15. /**
  16. * Utility function that returns the radius for a geometry.
  17. *
  18. * @param {object} elem DOM element
  19. * @param {float} angle the angle in degrees
  20. * @return {object} .x, .y radius of geometry
  21. */
  22. var proj = function (elem, angle) {
  23. switch (elem.geometry){
  24. case $.gameQuery.GEOMETRY_RECTANGLE :
  25. var b = angle*Math.PI*2/360;
  26. var Rx = Math.abs(Math.cos(b)*elem.width/2*elem.factor)+Math.abs(Math.sin(b)*elem.height/2*elem.factor);
  27. var Ry = Math.abs(Math.cos(b)*elem.height/2*elem.factor)+Math.abs(Math.sin(b)*elem.width/2*elem.factor);
  28. return {x: Rx, y: Ry};
  29. }
  30. };
  31. /**
  32. * Utility function that checks for collision between two elements.
  33. *
  34. * @param {object} elem1 DOM for the first element
  35. * @param {float} offset1 offset of the first element
  36. * @param {object} elem2 DOM for the second element
  37. * @param {float} offset2 offset of the second element
  38. * @return {boolean} if the two elements collide or not
  39. */
  40. var collide = function(elem1, offset1, elem2, offset2) {
  41. // test real collision (only for two rectangles...)
  42. if((elem1.geometry == $.gameQuery.GEOMETRY_RECTANGLE && elem2.geometry == $.gameQuery.GEOMETRY_RECTANGLE)){
  43. var dx = offset2.x + elem2.boundingCircle.x - elem1.boundingCircle.x - offset1.x;
  44. var dy = offset2.y + elem2.boundingCircle.y - elem1.boundingCircle.y - offset1.y;
  45. var a = Math.atan(dy/dx);
  46. var Dx = Math.abs(Math.cos(a-elem1.angle*Math.PI*2/360)/Math.cos(a)*dx);
  47. var Dy = Math.abs(Math.sin(a-elem1.angle*Math.PI*2/360)/Math.sin(a)*dy);
  48. var R = proj(elem2, elem2.angle-elem1.angle);
  49. if((elem1.width/2*elem1.factor+R.x <= Dx) || (elem1.height/2*elem1.factor+R.y <= Dy)) {
  50. return false;
  51. } else {
  52. var Dx = Math.abs(Math.cos(a-elem2.angle*Math.PI*2/360)/Math.cos(a)*-dx);
  53. var Dy = Math.abs(Math.sin(a-elem2.angle*Math.PI*2/360)/Math.sin(a)*-dy);
  54. var R = proj(elem1, elem1.angle-elem2.angle);
  55. if((elem2.width/2*elem2.factor+R.x <= Dx) || (elem2.height/2*elem2.factor+R.y <= Dy)) {
  56. return false;
  57. } else {
  58. return true;
  59. }
  60. }
  61. } else {
  62. return false;
  63. }
  64. };
  65. /**
  66. * Utility function computes the offset relative to the playground of a gameQuery element without using DOM's position.
  67. * This should be faster than the standand .offset() function.
  68. *
  69. * Warning: No non-gameQuery elements should be present between this element and the playground div!
  70. *
  71. * @param {jQuery} element the jQuery wrapped DOM element representing the gameQuery object.
  72. * @return {object} an object {x:, y: } containing the x and y offset. (Not top and left like jQuery's .offset())
  73. */
  74. var offset = function(element) {
  75. // Get the tileSet offset (relative to the playground)
  76. var offset = {x: 0, y: 0};
  77. var parent = element[0];
  78. while(parent !== $.gameQuery.playground[0] && parent.gameQuery !== undefined) {
  79. offset.x += parent.gameQuery.posx;
  80. offset.y += parent.gameQuery.posy;
  81. parent = parent.parentNode;
  82. }
  83. return offset
  84. }
  85. /**
  86. * Utility function computes the index range of the tiles for a tilemap.
  87. *
  88. * @param {jQuery} element the jQuery wrapped DOM element representing the tilemap.
  89. * @param {object} offset an object holding the x and y offset of the tilemap, this is optional and will be computed if not provided.
  90. * @return {object} an object {firstColumn: , lastColumn: , fristRow: , lastRow: }
  91. */
  92. var visibleTilemapIndexes = function (element, elementOffset) {
  93. if (elementOffset === undefined) {
  94. elementOffset = offset(element);
  95. }
  96. var gameQuery = element[0].gameQuery;
  97. // Activate the visible tiles
  98. return {
  99. firstRow: Math.max(Math.min(Math.floor(-elementOffset.y/gameQuery.height), gameQuery.sizey), 0),
  100. lastRow: Math.max(Math.min(Math.ceil(($.gameQuery.playground[0].height-elementOffset.y)/gameQuery.height), gameQuery.sizey), 0),
  101. firstColumn: Math.max(Math.min(Math.floor(-elementOffset.x/gameQuery.width), gameQuery.sizex), 0),
  102. lastColumn: Math.max(Math.min(Math.ceil(($.gameQuery.playground[0].width-elementOffset.x)/gameQuery.width), gameQuery.sizex), 0)
  103. }
  104. }
  105. /**
  106. * Utility function thast computes the buffered zone of a tilemap
  107. *
  108. * @param {jQuery} element the jQuery wrapped DOM element representing the tilemap.
  109. * @param {object} visible an object describing the visible zone
  110. * @return {object} an object {firstColumn: , lastColumn: , fristRow: , lastRow: }
  111. */
  112. var bufferedTilemapIndexes = function (element, visible) {
  113. var gameQuery = element[0].gameQuery;
  114. return {
  115. firstRow: Math.max(Math.min(visible.firstRow - gameQuery.buffer, gameQuery.sizey), 0),
  116. lastRow: Math.max(Math.min(visible.lastRow + gameQuery.buffer, gameQuery.sizey), 0),
  117. firstColumn: Math.max(Math.min(visible.firstColumn - gameQuery.buffer, gameQuery.sizex), 0),
  118. lastColumn: Math.max(Math.min(visible.lastColumn + gameQuery.buffer, gameQuery.sizex), 0)
  119. }
  120. }
  121. /**
  122. * Utility function that creates a tile in the given tilemap
  123. *
  124. * @param {jQuery} tileSet the jQuery element representing the tile map
  125. * @param {integer} row the row index of the tile in the tile map
  126. * @param {integer} column the column index of the tile in the tile map
  127. */
  128. var addTile = function(tileSet, row, column) {
  129. var gameQuery = tileSet[0].gameQuery;
  130. var name = tileSet.attr("id");
  131. var tileDescription;
  132. if(gameQuery.func) {
  133. tileDescription = gameQuery.tiles(row,column)-1;
  134. } else {
  135. tileDescription = gameQuery.tiles[row][column]-1;
  136. }
  137. var animation;
  138. if(gameQuery.multi) {
  139. animation = gameQuery.animations;
  140. } else {
  141. animation = gameQuery.animations[tileDescription];
  142. }
  143. if(tileDescription >= 0){
  144. tileSet.addSprite($.gameQuery.tileIdPrefix+name+"_"+row+"_"+column,
  145. {width: gameQuery.width,
  146. height: gameQuery.height,
  147. posx: column*gameQuery.width,
  148. posy: row*gameQuery.height,
  149. animation: animation});
  150. var newTile = tileSet.find("#"+$.gameQuery.tileIdPrefix+name+"_"+row+"_"+column);
  151. if (gameQuery.multi) {
  152. newTile.setAnimation(tileDescription);
  153. } else {
  154. newTile[0].gameQuery.animationNumber = tileDescription;
  155. }
  156. newTile.removeClass($.gameQuery.spriteCssClass);
  157. newTile.addClass($.gameQuery.tileCssClass);
  158. newTile.addClass($.gameQuery.tileTypePrefix+tileDescription);
  159. }
  160. }
  161. // Define the list of object/function accessible through $.
  162. $.extend({ gameQuery: {
  163. /**
  164. * This is the Animation Object
  165. */
  166. Animation: function (options, imediateCallback) {
  167. // private default values
  168. var defaults = {
  169. imageURL: "",
  170. numberOfFrame: 1,
  171. delta: 0,
  172. rate: 30,
  173. type: 0,
  174. distance: 0,
  175. offsetx: 0,
  176. offsety: 0
  177. };
  178. // options extends defaults
  179. options = $.extend(defaults, options);
  180. // "public" attributes:
  181. this.imageURL = options.imageURL; // The url of the image to be used as an animation or sprite
  182. this.numberOfFrame = options.numberOfFrame; // The number of frames to be displayed when playing the animation
  183. this.delta = options.delta; // The distance in pixels between two frames
  184. this.rate = options.rate; // The rate at which the frames change in miliseconds
  185. this.type = options.type; // The type of the animation.This is a bitwise OR of the properties.
  186. this.distance = options.distance; // The distance in pixels between two animations
  187. this.offsetx = options.offsetx; // The x coordinate where the first sprite begins
  188. this.offsety = options.offsety; // The y coordinate where the first sprite begins
  189. // Whenever a new animation is created we add it to the ResourceManager animation list
  190. $.gameQuery.resourceManager.addAnimation(this, imediateCallback);
  191. return true;
  192. },
  193. /**
  194. * "constants" for the different types of an animation
  195. */
  196. ANIMATION_VERTICAL: 1, // Generated by a vertical offset of the background
  197. ANIMATION_HORIZONTAL: 2, // Generated by a horizontal offset of the background
  198. ANIMATION_ONCE: 4, // Played only once (else looping indefinitely)
  199. ANIMATION_CALLBACK: 8, // A callback is exectued at the end of a cycle
  200. ANIMATION_MULTI: 16, // The image file contains many animations
  201. ANIMATION_PINGPONG: 32, // At the last frame of the animation it reverses (if used in conjunction with ONCE it will have no effect)
  202. // "constants" for the different type of geometry for a sprite
  203. GEOMETRY_RECTANGLE: 1,
  204. GEOMETRY_DISC: 2,
  205. // basic values
  206. refreshRate: 30,
  207. /**
  208. * An object to manage resource loading
  209. */
  210. resourceManager: {
  211. animations: [], // List of animations / images used in the game
  212. sounds: [], // List of sounds used in the game
  213. callbacks: [], // List of the functions called at each refresh
  214. loadedAnimationsPointer: 0, // Keep track of the last loaded animation
  215. loadedSoundsPointer: 0, // Keep track of the last loaded sound
  216. /**
  217. * Load resources before starting the game.
  218. */
  219. preload: function() {
  220. // Start loading the images
  221. for (var i = this.animations.length-1 ; i >= this.loadedAnimationsPointer; i --){
  222. this.animations[i].domO = new Image();
  223. this.animations[i].domO.src = this.animations[i].imageURL;
  224. }
  225. // Start loading the sounds
  226. for (var i = this.sounds.length-1 ; i >= this.loadedSoundsPointer; i --){
  227. this.sounds[i].load();
  228. }
  229. $.gameQuery.resourceManager.waitForResources();
  230. },
  231. /**
  232. * Wait for all the resources called for in preload() to finish loading.
  233. */
  234. waitForResources: function() {
  235. // Check the images
  236. var imageCount = 0;
  237. for(var i=this.loadedAnimationsPointer; i < this.animations.length; i++){
  238. if(this.animations[i].domO.complete){
  239. imageCount++;
  240. }
  241. }
  242. // Check the sounds
  243. var soundCount = 0;
  244. for(var i=this.loadedSoundsPointer; i < this.sounds.length; i++){
  245. var temp = this.sounds[i].ready();
  246. if(temp){
  247. soundCount++;
  248. }
  249. }
  250. // Call the load callback with the current progress
  251. if($.gameQuery.resourceManager.loadCallback){
  252. var percent = (imageCount + soundCount)/(this.animations.length + this.sounds.length - this.loadedAnimationsPointer - this.loadedSoundsPointer)*100;
  253. $.gameQuery.resourceManager.loadCallback(percent);
  254. }
  255. if(imageCount + soundCount < (this.animations.length + this.sounds.length - this.loadedAnimationsPointer - this.loadedSoundsPointer)){
  256. imgWait=setTimeout(function () {
  257. $.gameQuery.resourceManager.waitForResources();
  258. }, 100);
  259. } else {
  260. this.loadedAnimationsPointer = this.animations.length;
  261. this.loadedSoundsPointer = this.sounds.length;
  262. // All the resources are loaded! We can now associate the animation's images to their corresponding sprites
  263. $.gameQuery.scenegraph.children().each(function(){
  264. // recursive call on the children:
  265. $(this).children().each(arguments.callee);
  266. // add the image as a background
  267. if(this.gameQuery && this.gameQuery.animation){
  268. $(this).css("background-image", "url("+this.gameQuery.animation.imageURL+")");
  269. // we set the correct kind of repeat
  270. if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
  271. $(this).css("background-repeat", "repeat-x");
  272. } else if(this.gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
  273. $(this).css("background-repeat", "repeat-y");
  274. } else {
  275. $(this).css("background-repeat", "no-repeat");
  276. }
  277. }
  278. });
  279. // Launch the refresh loop
  280. if($.gameQuery.state === STATE_NEW){
  281. setInterval(function () {
  282. $.gameQuery.resourceManager.refresh();
  283. },($.gameQuery.refreshRate));
  284. }
  285. $.gameQuery.state = STATE_RUNNING;
  286. if($.gameQuery.startCallback){
  287. $.gameQuery.startCallback();
  288. }
  289. // Make the scenegraph visible
  290. $.gameQuery.scenegraph.css("visibility","visible");
  291. }
  292. },
  293. /**
  294. * This function refresh a unique sprite here 'this' represent a dom object
  295. */
  296. refreshSprite: function() {
  297. // Check if 'this' is a gameQuery element
  298. if(this.gameQuery != undefined){
  299. var gameQuery = this.gameQuery;
  300. // Does 'this' has an animation ?
  301. if(gameQuery.animation){
  302. // Do we have anything to do?
  303. if ( (gameQuery.idleCounter == gameQuery.animation.rate-1) && gameQuery.playing){
  304. // Does 'this' loops?
  305. if(gameQuery.animation.type & $.gameQuery.ANIMATION_ONCE){
  306. if(gameQuery.currentFrame < gameQuery.animation.numberOfFrame-1){
  307. gameQuery.currentFrame += gameQuery.frameIncrement;
  308. } else if(gameQuery.currentFrame == gameQuery.animation.numberOfFrame-1) {
  309. // Does 'this' has a callback ?
  310. if(gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){
  311. if($.isFunction(gameQuery.callback)){
  312. gameQuery.callback(this);
  313. }
  314. }
  315. }
  316. } else {
  317. if(gameQuery.animation.type & $.gameQuery.ANIMATION_PINGPONG){
  318. if(gameQuery.currentFrame == gameQuery.animation.numberOfFrame-1 && gameQuery.frameIncrement == 1) {
  319. gameQuery.frameIncrement = -1;
  320. } else if (gameQuery.currentFrame == 0 && gameQuery.frameIncrement == -1) {
  321. gameQuery.frameIncrement = 1;
  322. }
  323. }
  324. gameQuery.currentFrame = (gameQuery.currentFrame+gameQuery.frameIncrement)%gameQuery.animation.numberOfFrame;
  325. if(gameQuery.currentFrame == 0){
  326. // Does 'this' has a callback ?
  327. if(gameQuery.animation.type & $.gameQuery.ANIMATION_CALLBACK){
  328. if($.isFunction(gameQuery.callback)){
  329. gameQuery.callback(this);
  330. }
  331. }
  332. }
  333. }
  334. // Update the background
  335. if((gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animation.numberOfFrame > 1)){
  336. if(gameQuery.multi){
  337. $(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.multi)+"px "+(-gameQuery.animation.offsety-gameQuery.animation.delta*gameQuery.currentFrame)+"px");
  338. } else {
  339. $(this).css("background-position",""+(-gameQuery.animation.offsetx)+"px "+(-gameQuery.animation.offsety-gameQuery.animation.delta*gameQuery.currentFrame)+"px");
  340. }
  341. } else if((gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animation.numberOfFrame > 1)) {
  342. if(gameQuery.multi){
  343. $(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.animation.delta*gameQuery.currentFrame)+"px "+(-gameQuery.animation.offsety-gameQuery.multi)+"px");
  344. } else {
  345. $(this).css("background-position",""+(-gameQuery.animation.offsetx-gameQuery.animation.delta*gameQuery.currentFrame)+"px "+(-gameQuery.animation.offsety)+"px");
  346. }
  347. }
  348. }
  349. gameQuery.idleCounter = (gameQuery.idleCounter+1)%gameQuery.animation.rate;
  350. }
  351. }
  352. return true;
  353. },
  354. /**
  355. * This function refresh a unique tile-map, here 'this' represent a dom object
  356. */
  357. refreshTilemap: function() {
  358. // Check if 'this' is a gameQuery element
  359. if(this.gameQuery != undefined){
  360. var gameQuery = this.gameQuery;
  361. if($.isArray(gameQuery.frameTracker)){
  362. for(var i=0; i<gameQuery.frameTracker.length; i++){
  363. // Do we have anything to do?
  364. if(gameQuery.idleCounter[i] == gameQuery.animations[i].rate-1){
  365. // Does 'this' loops?
  366. if(gameQuery.animations[i].type & $.gameQuery.ANIMATION_ONCE){
  367. if(gameQuery.frameTracker[i] < gameQuery.animations[i].numberOfFrame-1){
  368. gameQuery.frameTracker[i] += gameQuery.frameIncrement[i];
  369. }
  370. } else {
  371. if(gameQuery.animations[i].type & $.gameQuery.ANIMATION_PINGPONG){
  372. if(gameQuery.frameTracker[i] == gameQuery.animations[i].numberOfFrame-1 && gameQuery.frameIncrement[i] == 1) {
  373. gameQuery.frameIncrement[i] = -1;
  374. } else if (gameQuery.frameTracker[i] == 0 && gameQuery.frameIncrement[i] == -1) {
  375. gameQuery.frameIncrement[i] = 1;
  376. }
  377. }
  378. gameQuery.frameTracker[i] = (gameQuery.frameTracker[i]+gameQuery.frameIncrement[i])%gameQuery.animations[i].numberOfFrame;
  379. }
  380. }
  381. gameQuery.idleCounter[i] = (gameQuery.idleCounter[i]+1)%gameQuery.animations[i].rate;
  382. }
  383. } else {
  384. // Do we have anything to do?
  385. if(gameQuery.idleCounter == gameQuery.animations.rate-1){
  386. // Does 'this' loops?
  387. if(gameQuery.animations.type & $.gameQuery.ANIMATION_ONCE){
  388. if(gameQuery.frameTracker < gameQuery.animations.numberOfFrame-1){
  389. gameQuery.frameTracker += gameQuery.frameIncrement;
  390. }
  391. } else {
  392. if(gameQuery.animations.type & $.gameQuery.ANIMATION_PINGPONG){
  393. if(gameQuery.frameTracker == gameQuery.animations.numberOfFrame-1 && gameQuery.frameIncrement == 1) {
  394. gameQuery.frameIncrement = -1;
  395. } else if (gameQuery.frameTracker == 0 && gameQuery.frameIncrement == -1) {
  396. gameQuery.frameIncrement = 1;
  397. }
  398. }
  399. gameQuery.frameTracker = (gameQuery.frameTracker+gameQuery.frameIncrement)%gameQuery.animations.numberOfFrame;
  400. }
  401. }
  402. gameQuery.idleCounter = (gameQuery.idleCounter+1)%gameQuery.animations.rate;
  403. }
  404. // Update the background of all active tiles
  405. $(this).find("."+$.gameQuery.tileCssClass).each(function(){
  406. if($.isArray(gameQuery.frameTracker)){
  407. var animationNumber = this.gameQuery.animationNumber
  408. if((gameQuery.animations[animationNumber].type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animations[animationNumber].numberOfFrame > 1)){
  409. $(this).css("background-position",""+(-gameQuery.animations[animationNumber].offsetx)+"px "+(-gameQuery.animations[animationNumber].offsety-gameQuery.animations[animationNumber].delta*gameQuery.frameTracker[animationNumber])+"px");
  410. } else if((gameQuery.animations[animationNumber].type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animations[animationNumber].numberOfFrame > 1)) {
  411. $(this).css("background-position",""+(-gameQuery.animations[animationNumber].offsetx-gameQuery.animations[animationNumber].delta*gameQuery.frameTracker[animationNumber])+"px "+(-gameQuery.animations[animationNumber].offsety)+"px");
  412. }
  413. } else {
  414. if((gameQuery.animations.type & $.gameQuery.ANIMATION_VERTICAL) && (gameQuery.animations.numberOfFrame > 1)){
  415. $(this).css("background-position",""+(-gameQuery.animations.offsetx-this.gameQuery.multi)+"px "+(-gameQuery.animations.offsety-gameQuery.animations.delta*gameQuery.frameTracker)+"px");
  416. } else if((gameQuery.animations.type & $.gameQuery.ANIMATION_HORIZONTAL) && (gameQuery.animations.numberOfFrame > 1)) {
  417. $(this).css("background-position",""+(-gameQuery.animations.offsetx-gameQuery.animations.delta*gameQuery.frameTracker)+"px "+(-gameQuery.animations.offsety-this.gameQuery.multi)+"px");
  418. }
  419. }
  420. });
  421. }
  422. return true;
  423. },
  424. /**
  425. * Called periodically to refresh the state of the game.
  426. */
  427. refresh: function() {
  428. if($.gameQuery.state === STATE_RUNNING) {
  429. $.gameQuery.playground.find("."+$.gameQuery.spriteCssClass).each(this.refreshSprite);
  430. $.gameQuery.playground.find("."+$.gameQuery.tilemapCssClass).each(this.refreshTilemap);
  431. var deadCallback= new Array();
  432. for (var i = this.callbacks.length-1; i >= 0; i--){
  433. if(this.callbacks[i].idleCounter == this.callbacks[i].rate-1){
  434. var returnedValue = this.callbacks[i].fn();
  435. if(typeof returnedValue == 'boolean'){
  436. // If we have a boolean: 'true' means 'no more execution', 'false' means 'keep on executing'
  437. if(returnedValue){
  438. deadCallback.push(i);
  439. }
  440. } else if(typeof returnedValue == 'number') {
  441. // If we have a number it re-defines the time to the next call
  442. this.callbacks[i].rate = Math.round(returnedValue/$.gameQuery.refreshRate);
  443. this.callbacks[i].idleCounter = 0;
  444. }
  445. }
  446. this.callbacks[i].idleCounter = (this.callbacks[i].idleCounter+1)%this.callbacks[i].rate;
  447. }
  448. for(var i = deadCallback.length-1; i >= 0; i--){
  449. this.callbacks.splice(deadCallback[i],1);
  450. }
  451. }
  452. },
  453. /**
  454. * Add an animation to the resource Manager
  455. */
  456. addAnimation: function(animation, callback) {
  457. if($.inArray(animation,this.animations)<0){
  458. //normalize the animation rate:
  459. animation.rate = Math.round(animation.rate/$.gameQuery.refreshRate);
  460. if(animation.rate==0){
  461. animation.rate = 1;
  462. }
  463. this.animations.push(animation);
  464. switch ($.gameQuery.state){
  465. case STATE_NEW:
  466. case STATE_PAUSED:
  467. // Nothing to do for now
  468. break;
  469. case STATE_RUNNING:
  470. // immediatly load the animation and call the callback if any
  471. this.animations[this.loadedAnimationsPointer].domO = new Image();
  472. this.animations[this.loadedAnimationsPointer].domO.src = animation.imageURL;
  473. if (callback !== undefined){
  474. this.animations[this.loadedAnimationsPointer].domO.onload = callback;
  475. }
  476. this.loadedAnimationsPointer++;
  477. break;
  478. }
  479. }
  480. },
  481. /**
  482. * Add a sound to the resource Manager
  483. */
  484. addSound: function(sound, callback){
  485. if($.inArray(sound,this.sounds)<0){
  486. this.sounds.push(sound);
  487. switch ($.gameQuery.state){
  488. case STATE_NEW:
  489. case STATE_PAUSED:
  490. // Nothing to do for now
  491. break;
  492. case STATE_RUNNING:
  493. // immediatly load the sound and call the callback if any
  494. sound.load();
  495. // TODO callback....
  496. this.loadedSoundsPointer++;
  497. break;
  498. }
  499. }
  500. },
  501. /**
  502. * Register a callback
  503. *
  504. * @param {function} fn the callback
  505. * @param {integer} rate the rate in ms at which the callback should be called (should be a multiple of the playground rate or will be rounded)
  506. */
  507. registerCallback: function(fn, rate){
  508. rate = Math.round(rate/$.gameQuery.refreshRate);
  509. if(rate==0){
  510. rate = 1;
  511. }
  512. this.callbacks.push({fn: fn, rate: rate, idleCounter: 0});
  513. },
  514. /**
  515. * Clear the animations and sounds
  516. */
  517. clear: function(callbacksToo){
  518. this.animations = [];
  519. this.loadedAnimationsPointer = 0;
  520. this.sounds = [];
  521. this.loadedSoundsPointer = 0;
  522. if(callbacksToo) {
  523. this.callbacks = [];
  524. }
  525. }
  526. },
  527. /**
  528. * This is a single place to update the underlying data of sprites/groups/tiles after a position or dimesion modification.
  529. */
  530. update: function(descriptor, transformation) {
  531. // Did we really receive a descriptor or a jQuery object instead?
  532. if(!$.isPlainObject(descriptor)){
  533. // Then we must get real descriptor
  534. if(descriptor.length > 0){
  535. var gameQuery = descriptor[0].gameQuery;
  536. } else {
  537. var gameQuery = descriptor.gameQuery;
  538. }
  539. } else {
  540. var gameQuery = descriptor;
  541. }
  542. // If we couldn't find one we return
  543. if(!gameQuery) return;
  544. if(gameQuery.tileSet === true){
  545. // We have a tilemap
  546. var visible = visibleTilemapIndexes(descriptor);
  547. var buffered = gameQuery.buffered;
  548. // Test what kind of transformation we have and react accordingly
  549. for(property in transformation){
  550. switch(property){
  551. case "x":
  552. if(visible.lastColumn > buffered.lastColumn) {
  553. // Detach the tilemap
  554. var parent = descriptor[0].parentNode;
  555. var tilemap = descriptor.detach();
  556. var newBuffered = bufferedTilemapIndexes(descriptor, visible);
  557. for(var i = gameQuery.buffered.firstRow; i < gameQuery.buffered.lastRow; i++){
  558. // Remove the newly invisible tiles
  559. for(var j = gameQuery.buffered.firstColumn; j < Math.min(newBuffered.firstColumn, gameQuery.buffered.lastColumn); j++) {
  560. tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
  561. }
  562. // And add the newly visible tiles
  563. for(var j = Math.max(gameQuery.buffered.lastColumn,newBuffered.firstColumn); j < newBuffered.lastColumn ; j++) {
  564. addTile(tilemap,i,j);
  565. }
  566. }
  567. gameQuery.buffered.firstColumn = newBuffered.firstColumn;
  568. gameQuery.buffered.lastColumn = newBuffered.lastColumn;
  569. // Attach the tilemap back
  570. tilemap.appendTo(parent);
  571. }
  572. if(visible.firstColumn < buffered.firstColumn) {
  573. // Detach the tilemap
  574. var parent = descriptor[0].parentNode;
  575. var tilemap = descriptor.detach();
  576. var newBuffered = bufferedTilemapIndexes(descriptor, visible);
  577. for(var i = gameQuery.buffered.firstRow; i < gameQuery.buffered.lastRow; i++){
  578. // Remove the newly invisible tiles
  579. for(var j = Math.max(newBuffered.lastColumn,gameQuery.buffered.firstColumn); j < gameQuery.buffered.lastColumn ; j++) {
  580. tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
  581. }
  582. // And add the newly visible tiles
  583. for(var j = newBuffered.firstColumn; j < Math.min(gameQuery.buffered.firstColumn,newBuffered.lastColumn); j++) {
  584. addTile(tilemap,i,j);
  585. }
  586. }
  587. gameQuery.buffered.firstColumn = newBuffered.firstColumn;
  588. gameQuery.buffered.lastColumn = newBuffered.lastColumn;
  589. // Attach the tilemap back
  590. tilemap.appendTo(parent);
  591. }
  592. break;
  593. case "y":
  594. if(visible.lastRow > buffered.lastRow) {
  595. // Detach the tilemap
  596. var parent = descriptor[0].parentNode;
  597. var tilemap = descriptor.detach();
  598. var newBuffered = bufferedTilemapIndexes(descriptor, visible);
  599. for(var j = gameQuery.buffered.firstColumn; j < gameQuery.buffered.lastColumn ; j++) {
  600. // Remove the newly invisible tiles
  601. for(var i = gameQuery.buffered.firstRow; i < Math.min(newBuffered.firstRow, gameQuery.buffered.lastRow); i++){
  602. tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
  603. }
  604. // And add the newly visible tiles
  605. for(var i = Math.max(gameQuery.buffered.lastRow, newBuffered.firstRow); i < newBuffered.lastRow; i++){
  606. addTile(tilemap,i,j);
  607. }
  608. }
  609. gameQuery.buffered.firstRow = newBuffered.firstRow;
  610. gameQuery.buffered.lastRow = newBuffered.lastRow;
  611. // Attach the tilemap back
  612. tilemap.appendTo(parent);
  613. }
  614. if(visible.firstRow < buffered.firstRow) {
  615. // Detach the tilemap
  616. var parent = descriptor[0].parentNode;
  617. var tilemap = descriptor.detach();
  618. var newBuffered = bufferedTilemapIndexes(descriptor, visible);
  619. for(var j = gameQuery.buffered.firstColumn; j < gameQuery.buffered.lastColumn ; j++) {
  620. // Remove the newly invisible tiles
  621. for(var i = Math.max(newBuffered.lastRow, gameQuery.buffered.firstRow); i < gameQuery.buffered.lastRow; i++){
  622. tilemap.find("#"+$.gameQuery.tileIdPrefix+descriptor.attr("id")+"_"+i+"_"+j).remove();
  623. }
  624. // And add the newly visible tiles
  625. for(var i = newBuffered.firstRow; i < Math.min(gameQuery.buffered.firstRow, newBuffered.lastRow); i++){
  626. addTile(tilemap,i,j);
  627. }
  628. }
  629. gameQuery.buffered.firstRow = newBuffered.firstRow;
  630. gameQuery.buffered.lastRow = newBuffered.lastRow;
  631. // Attach the tilemap back
  632. tilemap.appendTo(parent);
  633. }
  634. break;
  635. case "angle":
  636. //TODO
  637. break;
  638. case "factor":
  639. //TODO
  640. break;
  641. }
  642. }
  643. } else {
  644. var refreshBoundingCircle = $.gameQuery.playground && !$.gameQuery.playground.disableCollision;
  645. // Update the descriptor
  646. for(property in transformation){
  647. switch(property){
  648. case "x":
  649. if(refreshBoundingCircle){
  650. gameQuery.boundingCircle.x = gameQuery.posx+gameQuery.width/2;
  651. }
  652. break;
  653. case "y":
  654. if(refreshBoundingCircle){
  655. gameQuery.boundingCircle.y = gameQuery.posy+gameQuery.height/2;
  656. }
  657. break;
  658. case "w":
  659. case "h":
  660. gameQuery.boundingCircle.originalRadius = Math.sqrt(Math.pow(gameQuery.width,2) + Math.pow(gameQuery.height,2))/2
  661. gameQuery.boundingCircle.radius = gameQuery.factor*gameQuery.boundingCircle.originalRadius;
  662. break;
  663. case "angle": //(in degrees)
  664. gameQuery.angle = parseFloat(transformation.angle);
  665. break;
  666. case "factor":
  667. gameQuery.factor = parseFloat(transformation.factor);
  668. if(refreshBoundingCircle){
  669. gameQuery.boundingCircle.radius = gameQuery.factor*gameQuery.boundingCircle.originalRadius;
  670. }
  671. break;
  672. }
  673. }
  674. }
  675. },
  676. // State of the engine
  677. state: STATE_NEW,
  678. // CSS classes used to mark game element
  679. spriteCssClass: gQprefix + "sprite",
  680. groupCssClass: gQprefix + "group",
  681. tilemapCssClass: gQprefix + "tilemap",
  682. tileCssClass: gQprefix + "tile",
  683. // Prefix for CSS Ids or Classes
  684. tileTypePrefix: gQprefix + "tileType_",
  685. tileIdPrefix: gQprefix + "tile_"
  686. },
  687. /**
  688. * Mute (or unmute) all the sounds.
  689. */
  690. muteSound: function(muted){
  691. for (var i = $.gameQuery.resourceManager.sounds.length-1 ; i >= 0; i --) {
  692. $.gameQuery.resourceManager.sounds[i].muted(muted);
  693. }
  694. },
  695. /**
  696. * Accessor for the currently defined playground as a jQuery object
  697. */
  698. playground: function() {
  699. return $.gameQuery.playground
  700. },
  701. /**
  702. * Define a callback called during the loading of the game's resources.
  703. *
  704. * The function will recieve as unique parameter
  705. * a number representing the progess percentage.
  706. */
  707. loadCallback: function(callback){
  708. $.gameQuery.resourceManager.loadCallback = callback;
  709. }
  710. }); // end of the extensio of $
  711. // fragments used to create DOM element
  712. var spriteFragment = $("<div class='"+$.gameQuery.spriteCssClass+"' style='position: absolute; display: block; overflow: hidden' />");
  713. var groupFragment = $("<div class='"+$.gameQuery.groupCssClass+"' style='position: absolute; display: block; overflow: hidden' />");
  714. var tilemapFragment = $("<div class='"+$.gameQuery.tilemapCssClass+"' style='position: absolute; display: block; overflow: hidden;' />");
  715. // Define the list of object/function accessible through $("selector").
  716. $.fn.extend({
  717. /**
  718. * Defines the currently selected div to which contains the game and initialize it.
  719. *
  720. * This is a non-destructive call
  721. */
  722. playground: function(options) {
  723. if(this.length == 1){
  724. if(this[0] == document){
  725. // Old usage detected, this is not supported anymore
  726. throw "Old playground usage, use $.playground() to retreive the playground and $('mydiv').playground(options) to set the div!";
  727. }
  728. options = $.extend({
  729. height: 320,
  730. width: 480,
  731. refreshRate: 30,
  732. position: "absolute",
  733. keyTracker: false,
  734. mouseTracker: false,
  735. disableCollision: false
  736. }, options);
  737. // We save the playground node and set some variable for this node:
  738. $.gameQuery.playground = this;
  739. $.gameQuery.refreshRate = options.refreshRate;
  740. $.gameQuery.playground[0].height = options.height;
  741. $.gameQuery.playground[0].width = options.width;
  742. // We initialize the display of the div
  743. $.gameQuery.playground.css({
  744. position: options.position,
  745. display: "block",
  746. overflow: "hidden",
  747. height: options.height+"px",
  748. width: options.width+"px"
  749. })
  750. .append("<div id='"+gQprefix+"scenegraph' style='visibility: hidden'/>");
  751. $.gameQuery.scenegraph = $("#"+gQprefix+"scenegraph");
  752. // Add the keyTracker to the gameQuery object:
  753. $.gameQuery.keyTracker = {};
  754. // We only enable the real tracking if the users wants it
  755. if(options.keyTracker){
  756. $(document).keydown(function(event){
  757. $.gameQuery.keyTracker[event.keyCode] = true;
  758. });
  759. $(document).keyup(function(event){
  760. $.gameQuery.keyTracker[event.keyCode] = false;
  761. });
  762. }
  763. // Add the mouseTracker to the gameQuery object:
  764. $.gameQuery.mouseTracker = {
  765. x: 0,
  766. y: 0};
  767. // We only enable the real tracking if the users wants it
  768. var scenegraphOffset = $.gameQuery.playground.offset();
  769. if(options.mouseTracker){
  770. $($.gameQuery.playground).mousemove(function(event){
  771. $.gameQuery.mouseTracker.x = event.pageX - scenegraphOffset.left;
  772. $.gameQuery.mouseTracker.y = event.pageY - scenegraphOffset.top;
  773. });
  774. $(document).mousedown(function(event){
  775. $.gameQuery.mouseTracker[event.which] = true;
  776. });
  777. $(document).mouseup(function(event){
  778. $.gameQuery.mouseTracker[event.which] = false;
  779. });
  780. }
  781. }
  782. return this;
  783. },
  784. /**
  785. * Starts the game.
  786. *
  787. * Resources from the resource manager are preloaded if necesary
  788. * Works only for the playground node.
  789. *
  790. * This is a non-destructive call
  791. */
  792. startGame: function(callback) {
  793. $.gameQuery.startCallback = callback;
  794. $.gameQuery.resourceManager.preload();
  795. return this;
  796. },
  797. /**
  798. * TODO
  799. */
  800. pauseGame: function() {
  801. $.gameQuery.state = STATE_PAUSED;
  802. $.gameQuery.scenegraph.css("visibility","hidden");
  803. return this;
  804. },
  805. /**
  806. * Resume the game if it was paused and call the callback passed in argument once the newly added ressources are loaded.
  807. */
  808. resumeGame: function(callback) {
  809. if($.gameQuery.state === STATE_PAUSED){
  810. $.gameQuery.startCallback = callback;
  811. $.gameQuery.resourceManager.preload();
  812. }
  813. return this;
  814. },
  815. /**
  816. * Removes all the sprites, groups and tilemaps present in the scenegraph
  817. */
  818. clearScenegraph: function() {
  819. $.gameQuery.scenegraph.empty()
  820. return this;
  821. },
  822. /**
  823. * Removes all the sprites, groups and tilemaps present in the scenegraph as well as all loaded animations and sounds
  824. */
  825. clearAll: function(callbackToo) {
  826. $.gameQuery.scenegraph.empty();
  827. $.gameQuery.resourceManager.clear(callbackToo)
  828. return this;
  829. },
  830. /**
  831. * Add a group to the scene graph. Works only on the scenegraph root or on another group
  832. *
  833. * This IS a destructive call and should be terminated with end()
  834. * to go back one level up in the chaining
  835. */
  836. addGroup: function(group, options) {
  837. options = $.extend({
  838. width: 32,
  839. height: 32,
  840. posx: 0,
  841. posy: 0,
  842. posz: 0,
  843. posOffsetX: 0,
  844. posOffsetY: 0,
  845. overflow: "visible",
  846. geometry: $.gameQuery.GEOMETRY_RECTANGLE,
  847. angle: 0,
  848. factor: 1,
  849. factorh: 1,
  850. factorv: 1
  851. }, options);
  852. var newGroupElement = groupFragment.clone().attr("id",group).css({
  853. overflow: options.overflow,
  854. top: options.posy,
  855. left: options.posx,
  856. height: options.height,
  857. width: options.width
  858. });
  859. if(this == $.gameQuery.playground){
  860. $.gameQuery.scenegraph.append(newGroupElement);
  861. } else if ((this == $.gameQuery.scenegraph)||(this.hasClass($.gameQuery.groupCssClass))){
  862. this.append(newGroupElement);
  863. }
  864. newGroupElement[0].gameQuery = options;
  865. newGroupElement[0].gameQuery.boundingCircle = {x: options.posx + options.width/2,
  866. y: options.posy + options.height/0,
  867. originalRadius: Math.sqrt(Math.pow(options.width,2) + Math.pow(options.height,2))/2};
  868. newGroupElement[0].gameQuery.boundingCircle.radius = newGroupElement[0].gameQuery.boundingCircle.originalRadius;
  869. newGroupElement[0].gameQuery.group = true;
  870. return this.pushStack(newGroupElement);
  871. },
  872. /**
  873. * Add a sprite to the current node. Works only on the playground or any of its sub-nodes
  874. *
  875. * This is a non-destructive call
  876. */
  877. addSprite: function(sprite, options) {
  878. options = $.extend({
  879. width: 32,
  880. height: 32,
  881. posx: 0,
  882. posy: 0,
  883. posz: 0,
  884. posOffsetX: 0,
  885. posOffsetY: 0,
  886. idleCounter: 0,
  887. currentFrame: 0,
  888. frameIncrement: 1,
  889. geometry: $.gameQuery.GEOMETRY_RECTANGLE,
  890. angle: 0,
  891. factor: 1,
  892. playing: true,
  893. factorh: 1,
  894. factorv: 1
  895. }, options);
  896. var newSpriteElem = spriteFragment.clone().attr("id",sprite).css({
  897. height: options.height,
  898. width: options.width,
  899. left: options.posx,
  900. top: options.posy,
  901. backgroundPosition: ((options.animation)? -options.animation.offsetx : 0)+"px "+((options.animation)? -options.animation.offsety : 0)+"px"
  902. });
  903. if(this == $.gameQuery.playground){
  904. $.gameQuery.scenegraph.append(newSpriteElem);
  905. } else {
  906. this.append(newSpriteElem);
  907. }
  908. // If the game has already started we want to add the animation's image as a background now
  909. if(options.animation){
  910. // The second test is a fix for default background (https://github.com/onaluf/gameQuery/issues/3)
  911. if($.gameQuery.state === STATE_RUNNING && options.animation.imageURL !== ''){
  912. newSpriteElem.css("background-image", "url("+options.animation.imageURL+")");
  913. }
  914. if(options.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
  915. newSpriteElem.css("background-repeat", "repeat-x");
  916. } else if(options.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
  917. newSpriteElem.css("background-repeat", "repeat-y");
  918. } else {
  919. newSpriteElem.css("background-repeat", "no-repeat");
  920. }
  921. }
  922. var spriteDOMObject = newSpriteElem[0];
  923. if(spriteDOMObject != undefined){
  924. spriteDOMObject.gameQuery = options;
  925. // Compute bounding Circle
  926. spriteDOMObject.gameQuery.boundingCircle = {x: options.posx + options.width/2,
  927. y: options.posy + options.height/2,
  928. originalRadius: Math.sqrt(Math.pow(options.width,2) + Math.pow(options.height,2))/2};
  929. spriteDOMObject.gameQuery.boundingCircle.radius = spriteDOMObject.gameQuery.boundingCircle.originalRadius;
  930. }
  931. return this;
  932. },
  933. /**
  934. * Add a Tile Map to the selected element.
  935. *
  936. * This is a non-destructive call. The added sprite is NOT selected after a call to this function!
  937. */
  938. addTilemap: function(name, tileDescription, animationList, options){
  939. options = $.extend({
  940. width: 32,
  941. height: 32,
  942. sizex: 32,
  943. sizey: 32,
  944. posx: 0,
  945. posy: 0,
  946. posz: 0,
  947. posOffsetX: 0,
  948. posOffsetY: 0,
  949. factorh: 1,
  950. factorv: 1,
  951. buffer: 1
  952. }, options);
  953. var tileSet = tilemapFragment.clone().attr("id",name).css({
  954. top: options.posy,
  955. left: options.posx,
  956. height: options.height*options.sizey,
  957. width: options.width*options.sizex
  958. });
  959. if(this == $.gameQuery.playground){
  960. $.gameQuery.scenegraph.append(tileSet);
  961. } else {
  962. this.append(tileSet);
  963. }
  964. tileSet[0].gameQuery = options;
  965. var gameQuery = tileSet[0].gameQuery;
  966. gameQuery.tileSet = true;
  967. gameQuery.tiles = tileDescription;
  968. gameQuery.func = (typeof tileDescription === "function");
  969. if($.isArray(animationList)){
  970. var frameTracker = [];
  971. var idleCounter = [];
  972. var frameIncrement = [];
  973. for(var i=0; i<animationList.length; i++){
  974. frameTracker[i] = 0;
  975. idleCounter[i] = 0;
  976. frameIncrement[i] = 1;
  977. }
  978. gameQuery.frameTracker = frameTracker;
  979. gameQuery.animations = animationList;
  980. gameQuery.idleCounter = idleCounter;
  981. gameQuery.frameIncrement = frameIncrement;
  982. gameQuery.multi = false;
  983. } else {
  984. gameQuery.frameTracker = 0;
  985. gameQuery.frameIncrement = 1;
  986. gameQuery.animations = animationList;
  987. gameQuery.idleCounter = 0;
  988. gameQuery.multi = true;
  989. }
  990. // Get the tileSet offset (relative to the playground)
  991. var visible = visibleTilemapIndexes(tileSet);
  992. var buffered = bufferedTilemapIndexes(tileSet, visible);
  993. gameQuery.buffered = buffered;
  994. // For many simple animation
  995. for(var i = buffered.firstRow; i < buffered.lastRow; i++){
  996. for(var j = buffered.firstColumn; j < buffered.lastColumn ; j++) {
  997. addTile(tileSet, i, j);
  998. }
  999. }
  1000. return this.pushStack(tileSet);
  1001. },
  1002. /**
  1003. * Stop the animation at the current frame
  1004. *
  1005. * This is a non-destructive call.
  1006. */
  1007. pauseAnimation: function() {
  1008. this[0].gameQuery.playing = false;
  1009. return this;
  1010. },
  1011. /**
  1012. * Resume the animation (if paused)
  1013. *
  1014. * This is a non-destructive call.
  1015. */
  1016. resumeAnimation: function() {
  1017. this[0].gameQuery.playing = true;
  1018. return this;
  1019. },
  1020. /**
  1021. * Changes the animation associated with a sprite.
  1022. *
  1023. * WARNING: no checks are made to ensure that the object is really a sprite
  1024. *
  1025. * This is a non-destructive call.
  1026. */
  1027. setAnimation: function(animation, callback) {
  1028. var gameQuery = this[0].gameQuery;
  1029. if(typeof animation == "number"){
  1030. if(gameQuery.animation.type & $.gameQuery.ANIMATION_MULTI){
  1031. var distance = gameQuery.animation.distance * animation;
  1032. gameQuery.multi = distance;
  1033. gameQuery.frameIncrement = 1;
  1034. gameQuery.currentFrame = 0;
  1035. if(gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
  1036. this.css("background-position",""+(-distance-gameQuery.animation.offsetx)+"px "+(-gameQuery.animation.offsety)+"px");
  1037. } else if(gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
  1038. this.css("background-position",""+(-gameQuery.animation.offsetx)+"px "+(-distance-gameQuery.animation.offsety)+"px");
  1039. }
  1040. }
  1041. } else {
  1042. if(animation){
  1043. gameQuery.animation = animation;
  1044. gameQuery.currentFrame = 0;
  1045. gameQuery.frameIncrement = 1;
  1046. if (animation.imageURL !== '') {this.css("backgroundImage", "url('"+animation.imageURL+"')");}
  1047. this.css({"background-position": ""+(-animation.offsetx)+"px "+(-animation.offsety)+"px"});
  1048. if(gameQuery.animation.type & $.gameQuery.ANIMATION_VERTICAL) {
  1049. this.css("background-repeat", "repeat-x");
  1050. } else if(gameQuery.animation.type & $.gameQuery.ANIMATION_HORIZONTAL) {
  1051. this.css("background-repeat", "repeat-y");
  1052. } else {
  1053. this.css("background-repeat", "no-repeat");
  1054. }
  1055. } else {
  1056. this.css("background-image", "");
  1057. }
  1058. }
  1059. if(callback != undefined){
  1060. this[0].gameQuery.callback = callback;
  1061. }
  1062. return this;
  1063. },
  1064. /**
  1065. * Register a callback funnction
  1066. *
  1067. * This is a non-destructive call
  1068. *
  1069. * @param {Function} fn the callback function.
  1070. * @param {Number} rate time in milliseconds between calls.
  1071. */
  1072. registerCallback: function(fn, rate) {
  1073. $.gameQuery.resourceManager.registerCallback(fn, rate);
  1074. return this;
  1075. },
  1076. /**
  1077. * Retrieve a list of objects in collision with the subject.
  1078. *
  1079. * If 'this' is a sprite or a group, the function will retrieve the list of sprites (not groups!!!) that touch it. For now all abject are considered to be boxes.
  1080. *
  1081. * This IS a destructive call and should be terminated with end() to go back one level up in the chaining.
  1082. */
  1083. collision: function(arg1, arg2){
  1084. var filter, override;
  1085. if ($.isPlainObject(arg1)){
  1086. override = arg1;
  1087. } else if (typeof arg1 === "string") {
  1088. filter = arg1;
  1089. }
  1090. if ($.isPlainObject(arg2)){
  1091. override = arg2;
  1092. } else if (typeof arg2 === "string") {
  1093. filter = arg2;
  1094. }
  1095. var resultList = [];
  1096. // Retrieve 'this' offset by looking at the parents
  1097. var itsParent = this[0].parentNode, offsetX = 0, offsetY = 0;
  1098. while (itsParent != $.gameQuery.playground[0]){
  1099. if(itsParent.gameQuery){
  1100. offsetX += itsParent.gameQuery.posx;
  1101. offsetY += itsParent.gameQuery.posy;
  1102. }
  1103. itsParent = itsParent.parentNode;
  1104. }
  1105. // Retrieve the playground's absolute position and size information
  1106. var pgdGeom = {top: 0, left: 0, bottom: $.playground().height(), right: $.playground().width()};
  1107. // Retrieve the gameQuery object and correct it with the override
  1108. var gameQuery = jQuery.extend(true, {}, this[0].gameQuery);
  1109. // Retrieve the BoundingCircle and correct it with the override
  1110. var boundingCircle = jQuery.extend(true, {}, gameQuery.boundingCircle);
  1111. if(override && override.w){
  1112. gameQuery.width = override.w;
  1113. }
  1114. if(override && override.h){
  1115. gameQuery.height = override.h;
  1116. }
  1117. boundingCircle.originalRadius = Math.sqrt(Math.pow(gameQuery.width,2) + Math.pow(gameQuery.height,2))/2
  1118. boundingCircle.radius = gameQuery.factor*boundingCircle.originalRadius;
  1119. if(override && override.x){
  1120. boundingCircle.x = override.x + gameQuery.width/2.0;
  1121. }
  1122. if(override && override.y){
  1123. boundingCircle.y = override.y + gameQuery.height/2.0;
  1124. }
  1125. gameQuery.boundingCircle = boundingCircle;
  1126. // Is 'this' inside the playground ?
  1127. if( (gameQuery.boundingCircle.y + gameQuery.boundingCircle.radius + offsetY < pgdGeom.top) ||
  1128. (gameQuery.boundingCircle.x + gameQuery.boundingCircle.radius + offsetX < pgdGeom.left) ||
  1129. (gameQuery.boundingCircle.y - gameQuery.boundingCircle.radius + offsetY > pgdGeom.bottom) ||
  1130. (gameQuery.boundingCircle.x - gameQuery.boundingCircle.radius + offsetX > pgdGeom.right)){
  1131. return this.pushStack(new $([]));
  1132. }
  1133. if(this !== $.gameQuery.playground){
  1134. // We must find all the elements that touche 'this'
  1135. var elementsToCheck = new Array();
  1136. elementsToCheck.push($.gameQuery.scenegraph.children(filter).get());
  1137. elementsToCheck[0].offsetX = 0;
  1138. elementsToCheck[0].offsetY = 0;
  1139. for(var i = 0, len = elementsToCheck.length; i < len; i++) {
  1140. var subLen = elementsToCheck[i].length;
  1141. while(subLen--){
  1142. var elementToCheck = elementsToCheck[i][subLen];
  1143. // Is it a gameQuery generated element?
  1144. if(elementToCheck.gameQuery){
  1145. // We don't want to check groups
  1146. if(!elementToCheck.gameQuery.group && !elementToCheck.gameQuery.tileSet){
  1147. // Does it touche the selection?
  1148. if(this[0]!=elementToCheck){
  1149. // Check bounding circle collision
  1150. var distance = Math.sqrt(Math.pow(offsetY + gameQuery.boundingCircle.y - elementsToCheck[i].offsetY - elementToCheck.gameQuery.boundingCircle.y, 2) + Math.pow(offsetX + gameQuery.boundingCircle.x - elementsToCheck[i].offsetX - elementToCheck.gameQuery.boundingCircle.x, 2));
  1151. if(distance - gameQuery.boundingCircle.radius - elementToCheck.gameQuery.boundingCircle.radius <= 0){
  1152. // Check real collision
  1153. if(collide(gameQuery, {x: offsetX, y: offsetY}, elementToCheck.gameQuery, {x: elementsToCheck[i].offsetX, y: elementsToCheck[i].offsetY})) {
  1154. // Add to the result list if collision detected
  1155. resultList.push(elementsToCheck[i][subLen]);
  1156. }
  1157. }
  1158. }
  1159. }
  1160. // Add the children nodes to the list
  1161. var eleChildren = $(elementToCheck).children(filter);
  1162. if(eleChildren.length){
  1163. elementsToCheck.push(eleChildren.get());
  1164. elementsToCheck[len].offsetX = elementToCheck.gameQuery.posx + elementsToCheck[i].offsetX;
  1165. elementsToCheck[len].offsetY = elementToCheck.gameQuery.posy + elementsToCheck[i].offsetY;
  1166. len++;
  1167. }
  1168. }
  1169. }
  1170. }
  1171. return this.pushStack($(resultList));
  1172. }
  1173. },
  1174. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1175. /** -- Sound related functions ------------------------------------------------------------------------------------------------------------------ **/
  1176. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1177. /**
  1178. * Add the sound to the resourceManager for later use and
  1179. * associates it to the selected dom element(s).
  1180. *
  1181. * This is a non-destructive call
  1182. */
  1183. addSound: function(sound, add) {
  1184. // Does a SoundWrapper exist?
  1185. if($.gameQuery.SoundWrapper) {
  1186. var gameQuery = this[0].gameQuery;
  1187. // Should we add to existing sounds?
  1188. if(add) {
  1189. // Do we have some sound associated with 'this'?
  1190. var sounds = gameQuery.sounds;
  1191. if(sounds) {
  1192. // Yes, we add it
  1193. sounds.push(sound);
  1194. } else {
  1195. // No, we create a new sound array
  1196. gameQuery.sounds = [sound];
  1197. }
  1198. } else {
  1199. // No, we replace all sounds with this one
  1200. gameQuery.sounds = [sound];
  1201. }
  1202. }
  1203. return this;
  1204. },
  1205. /**
  1206. * Play the sound(s) associated with the selected dom element(s).
  1207. *
  1208. * This is a non-destructive call.
  1209. */
  1210. playSound: function() {
  1211. $(this).each(function(){
  1212. var gameQuery = this.gameQuery;
  1213. if(gameQuery.sounds) {
  1214. for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
  1215. gameQuery.sounds[i].play();
  1216. }
  1217. }
  1218. });
  1219. return this;
  1220. },
  1221. /**
  1222. * Stops the sound(s) associated with the selected dom element(s) and rewinds them.
  1223. *
  1224. * This is a non-destructive call.
  1225. */
  1226. stopSound: function() {
  1227. $(this).each(function(){
  1228. var gameQuery = this.gameQuery;
  1229. if(gameQuery.sounds) {
  1230. for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
  1231. gameQuery.sounds[i].stop();
  1232. }
  1233. }
  1234. });
  1235. return this;
  1236. },
  1237. /**
  1238. * Pauses the sound(s) associated with the selected dom element(s).
  1239. *
  1240. * This is a non-destructive call.
  1241. */
  1242. pauseSound: function() {
  1243. $(this).each(function(){
  1244. var gameQuery = this.gameQuery;
  1245. if(gameQuery.sounds) {
  1246. for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
  1247. gameQuery.sounds[i].pause();
  1248. }
  1249. }
  1250. });
  1251. return this;
  1252. },
  1253. /**
  1254. * Mute or unmute the selected sound or all the sounds if none is specified.
  1255. *
  1256. * This is a non-destructive call.
  1257. */
  1258. muteSound: function(muted) {
  1259. $(this).each(function(){
  1260. var gameQuery = this.gameQuery;
  1261. if(gameQuery.sounds) {
  1262. for(var i = gameQuery.sounds.length-1 ; i >= 0; i --) {
  1263. gameQuery.sounds[i].muted(muted);
  1264. }
  1265. }
  1266. });
  1267. return this;
  1268. },
  1269. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1270. /** -- Transformation functions ----------------------------------------------------------------------------------------------------------------- **/
  1271. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1272. /**
  1273. * Internal function doing the combined actions of rotate and scale.
  1274. *
  1275. * Please use .rotate() or .scale() instead since they are part of the supported API!
  1276. *
  1277. * This is a non-destructive call.
  1278. */
  1279. transform: function(angle, factor) {
  1280. var gameQuery = this[0].gameQuery;
  1281. // Mark transformed and compute bounding box
  1282. $.gameQuery.update(gameQuery,{angle: angle, factor: factor});
  1283. if(this.css("MozTransform")) {
  1284. // For firefox from 3.5
  1285. var transform = "rotate("+angle+"deg) scale("+(factor*gameQuery.factorh)+","+(factor*gameQuery.factorv)+")";
  1286. this.css("MozTransform",transform);
  1287. } else if(this.css("-o-transform")) {
  1288. // For opera from 10.50
  1289. var transform = "rotate("+angle+"deg) scale("+(factor*gameQuery.factorh)+","+(factor*gameQuery.factorv)+")";
  1290. this.css("-o-transform",transform);
  1291. } else if(this.css("msTransform")!==null && this.css("msTransform")!==undefined) {
  1292. // For ie from 9
  1293. var transform = "rotate("+angle+"deg) scale("+(factor*gameQuery.factorh)+","+(factor*gameQuery.factorv)+")";
  1294. this.css("msTransform",transform);
  1295. } else if(this.css("WebkitTransform")!==null && this.css("WebkitTransform")!==undefined) {
  1296. // For safari from 3.1 (and chrome)
  1297. var transform = "rotate("+angle+"deg) scale("+(factor*gameQuery.factorh)+","+(factor*gameQuery.factorv)+")";
  1298. this.css("WebkitTransform",transform);
  1299. } else if(this.css("filter")!==undefined){
  1300. var angle_rad = Math.PI * 2 / 360 * angle;
  1301. // For ie from 5.5
  1302. var cos = Math.cos(angle_rad) * factor;
  1303. var sin = Math.sin(angle_rad) * factor;
  1304. this.css("filter","progid:DXImageTransform.Microsoft.Matrix(M11="+(cos*gameQuery.factorh)+",M12="+(-sin*gameQuery.factorv)+",M21="+(sin*gameQuery.factorh)+",M22="+(cos*gameQuery.factorv)+",SizingMethod='auto expand',FilterType='nearest neighbor')");
  1305. var newWidth = this.width();
  1306. var newHeight = this.height();
  1307. gameQuery.posOffsetX = (newWidth-gameQuery.width)/2;
  1308. gameQuery.posOffsetY = (newHeight-gameQuery.height)/2;
  1309. this.css("left", ""+(gameQuery.posx-gameQuery.posOffsetX)+"px");
  1310. this.css("top", ""+(gameQuery.posy-gameQuery.posOffsetY)+"px");
  1311. }
  1312. return this;
  1313. },
  1314. /**
  1315. * Rotate the element(s) clock-wise.
  1316. *
  1317. * @param {Number} angle the angle in degrees
  1318. *
  1319. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current rotation angle!
  1320. */
  1321. rotate: function(angle){
  1322. var gameQuery = this[0].gameQuery;
  1323. if(angle !== undefined) {
  1324. return this.transform(angle % 360, this.scale());
  1325. } else {
  1326. var ang = gameQuery.angle;
  1327. return ang ? ang : 0;
  1328. }
  1329. },
  1330. /**
  1331. * Change the scale of the selected element(s). The passed argument is a ratio:
  1332. *
  1333. * @param {Number} factor a ratio: 1.0 = original size, 0.5 = half the original size etc.
  1334. *
  1335. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current scale factor!
  1336. */
  1337. scale: function(factor){
  1338. var gameQuery = this[0].gameQuery;
  1339. if(factor !== undefined) {
  1340. return this.transform(this.rotate(), factor);
  1341. } else {
  1342. var fac = gameQuery.factor;
  1343. return fac ? fac : 1;
  1344. }
  1345. },
  1346. /**
  1347. * Flips the element(s) horizontally.
  1348. *
  1349. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current horizontal flipping status!
  1350. */
  1351. fliph: function(flip){
  1352. var gameQuery = this[0].gameQuery;
  1353. if (flip === undefined) {
  1354. return (gameQuery.factorh !== undefined) ? (gameQuery.factorh === -1) : false;
  1355. } else if (flip) {
  1356. gameQuery.factorh = -1;
  1357. } else {
  1358. gameQuery.factorh = 1;
  1359. }
  1360. return this.transform(this.rotate(), this.scale());
  1361. },
  1362. /**
  1363. * Flips the element(s) vertically.
  1364. *
  1365. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call since the return value is the current vertical flipping status!
  1366. */
  1367. flipv: function(flip){
  1368. var gameQuery = this[0].gameQuery;
  1369. if (flip === undefined) {
  1370. return (gameQuery.factorv !== undefined) ? (gameQuery.factorv === -1) : false;;
  1371. } else if (flip) {
  1372. gameQuery.factorv = -1;
  1373. } else {
  1374. gameQuery.factorv = 1;
  1375. }
  1376. return this.transform(this.rotate(), this.scale());
  1377. },
  1378. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1379. /** -- Position getter/setter functions --------------------------------------------------------------------------------------------------------- **/
  1380. /** ---------------------------------------------------------------------------------------------------------------------------------------------------------------- **/
  1381. /**
  1382. * Main function to change the sprite/group/tilemap position on screen.
  1383. * The three first agruments are the coordiate (double) and the last one is a flag
  1384. * to specify if the coordinate given are absolute or relative.
  1385. *
  1386. * If no argument is specified then the functions act as a getter and return a
  1387. * object {x,y,z}
  1388. *
  1389. * Please note that the z coordinate is just the z-index.
  1390. *
  1391. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
  1392. */
  1393. xyz: function(x, y, z, relative) {
  1394. if (x === undefined) {
  1395. return this.getxyz();
  1396. } else {
  1397. return this.setxyz({x: x, y: y, z: z}, relative);
  1398. }
  1399. },
  1400. /**
  1401. * The following functions are all all shortcuts for the .xyz(...) function.
  1402. *
  1403. * @see xyz for detailed documentation.
  1404. *
  1405. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
  1406. */
  1407. x: function(value, relative) {
  1408. if (value === undefined) {
  1409. return this.getxyz().x;
  1410. } else {
  1411. return this.setxyz({x: value}, relative);
  1412. }
  1413. },
  1414. y: function(value, relative) {
  1415. if (value === undefined) {
  1416. return this.getxyz().y;
  1417. } else {
  1418. return this.setxyz({y: value}, relative);
  1419. }
  1420. },
  1421. z: function(value, relative) {
  1422. if (value === undefined) {
  1423. return this.getxyz().z;
  1424. } else {
  1425. return this.setxyz({z: value}, relative);
  1426. }
  1427. },
  1428. xy: function(x, y, relative) {
  1429. if (x === undefined) {
  1430. // we return the z too since it doesn't cost anything
  1431. return this.getxyz();
  1432. } else {
  1433. return this.setxyz({x: x, y: y}, relative);
  1434. }
  1435. },
  1436. /**
  1437. * Main function to change the sprite/group/tilemap dimension on screen.
  1438. * The two first arguments are the width and height (double) and the last one is a
  1439. * flag to specify if the dimensions given are absolute or relative.
  1440. *
  1441. * If no argument is specified then the functions act as a getter and
  1442. *
  1443. * return an object {w,h}
  1444. *
  1445. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
  1446. */
  1447. wh: function(w, h, relative) {
  1448. if (w === undefined) {
  1449. return this.getwh();
  1450. } else {
  1451. return this.setwh({w: w, h: h}, relative);
  1452. }
  1453. },
  1454. /**
  1455. * The following functions are all all shortcuts for the .wh(...) function.
  1456. *
  1457. * @see wh for detailed documentation.
  1458. *
  1459. * This is a non-destructive call when called with a parameter. Without parameter it IS a destructive call.
  1460. */
  1461. w: function(value, relative) {
  1462. if (value === undefined) {
  1463. return this.getwh().w;
  1464. } else {
  1465. return this.setwh({w: value}, relative);
  1466. }
  1467. },
  1468. h: function(value, relative) {
  1469. if (value === undefined) {
  1470. return this.getwh().h;
  1471. } else {
  1472. return this.setwh({h: value}, relative);
  1473. }
  1474. },
  1475. /**
  1476. * The following four functions are 'private', and are not supposed to
  1477. * be used outside of the library.
  1478. * They are NOT part of the API and so are not guaranteed to remain unchanged.
  1479. * You should really use .xyz() and .wh() instead.
  1480. */
  1481. getxyz: function() {
  1482. var gameQuery = this[0].gameQuery;
  1483. return {x: gameQuery.posx, y: gameQuery.posy, z: gameQuery.posz};
  1484. },
  1485. setxyz: function(option, relative) {
  1486. var gameQuery = this[0].gameQuery;
  1487. for (coordinate in option) {
  1488. // Update the gameQuery object
  1489. switch (coordinate) {
  1490. case 'x':
  1491. if(relative) {
  1492. option.x += gameQuery.posx;
  1493. }
  1494. gameQuery.posx = option.x;
  1495. this.css("left",""+(gameQuery.posx + gameQuery.posOffsetX)+"px");
  1496. //update the sub tile maps (if any), this forces to recompute which tiles are visible
  1497. this.find("."+$.gameQuery.tilemapCssClass).each(function(){
  1498. $(this).x(0, true);
  1499. });
  1500. break;
  1501. case 'y':
  1502. if(relative) {
  1503. option.y += gameQuery.posy;
  1504. }
  1505. gameQuery.posy = option.y;
  1506. this.css("top",""+(gameQuery.posy + gameQuery.posOffsetY)+"px");
  1507. //update the sub tile maps (if any), this forces to recompute which tiles are visible
  1508. this.find("."+$.gameQuery.tilemapCssClass).each(function(){
  1509. $(this).y(0, true);
  1510. });
  1511. break;
  1512. case 'z':
  1513. if(relative) {
  1514. option.z += gameQuery.posz;
  1515. }
  1516. gameQuery.posz = option.z;
  1517. this.css("z-index",gameQuery.posz);
  1518. break;
  1519. }
  1520. }
  1521. $.gameQuery.update(this, option);
  1522. return this;
  1523. },
  1524. getwh: function() {
  1525. var gameQuery = this[0].gameQuery;
  1526. return {w: gameQuery.width, h: gameQuery.height};
  1527. },
  1528. setwh: function(option, relative) {
  1529. var gameQuery = this[0].gameQuery;
  1530. for (coordinate in option) {
  1531. // Update the gameQuery object
  1532. switch (coordinate) {
  1533. case 'w':
  1534. if(relative) {
  1535. option.w += gameQuery.width;
  1536. }
  1537. gameQuery.width = option.w;
  1538. this.css("width","" + gameQuery.width + "px");
  1539. break;
  1540. case 'h':
  1541. if(relative) {
  1542. option.h += gameQuery.height;
  1543. }
  1544. gameQuery.height = option.h;
  1545. this.css("height","" + gameQuery.height + "px");
  1546. break;
  1547. }
  1548. }
  1549. $.gameQuery.update(this, option);
  1550. return this;
  1551. }
  1552. }); // end of the extensio of $.fn
  1553. // alias gameQuery to gQ for easier access
  1554. $.extend({ gQ: $.gameQuery});
  1555. })(jQuery);