gamequery-0.7.1.js 84 KB

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