1 /* 2 ___ _ _ 3 / __|_ _(_)__| | 4 | (_ | '_| / _` | 5 \___|_| |_\__,_| 6 7 */ 8 /** 9 * A derpy HTML5/JS engine for side-scrolling brawler / beat 'em up / punchy-fighty games. 10 * https://github.com/magicmarkers/Grid 11 * @author magicmarkers 12 * 13 */ 14 // TODO: change collision engine, make pixel perfect. keep depth in mind. 15 // TODO: get audio stuff working 16 // TODO: parallax backgrounds 17 // TODO: boilerplate baddy AI 18 // TODO: boilerplate post-hit temporary invulnerability/blinkage for player 19 // TODO: boilerplate talk bubbles 20 21 var Grid = function(args) { 22 23 // construction 24 this.screens = args['screens']; // required, dict of screen name => Grid.Screen instances 25 this.screen = args['screen']; // required, string starting screen name 26 this.w = args['w'] || 960; 27 this.h = args['h'] || 540; // viewport height 28 this.fps = args['fps'] || 60; // frames per second 29 30 // rw 31 this.idle = false; // for debugging, idles the main loop 32 33 // ro 34 this.frame = 0; // update counter, increments once every FPS. 35 this.ctx; // canvas context, the actual viewport 36 this.ictx; // imaginary canvas context, used for collision detection etc. 37 38 // private 39 this._canvas; // DOM element of viewport 40 this._icanvas; // DOM node for imaginary (framebuffer) canvas. never inserted 41 this._loop; // _update() interval 42 43 // creates the canvases and injects the viewport one into the DOM 44 this.inject = function(parentElement) { 45 // viewport canvas 46 this._canvas = document.createElement('canvas'); 47 this._canvas.setAttribute('width',this.w); 48 this._canvas.setAttribute('height',this.h); 49 this._canvas.setAttribute('tabindex',0); 50 this._canvas.addEventListener('keydown',Grid.Keyboard._keydown,true); 51 this._canvas.addEventListener('keyup',Grid.Keyboard._keyup,true); 52 this._ctx = this._canvas.getContext('2d'); 53 this._ctx.imageSmoothingEnabled = false; 54 this._ctx.mozImageSmoothingEnabled = false; 55 this._ctx.webkitImageSmoothingEnabled = false; 56 (parentElement||document.body).appendChild(this._canvas); 57 this._canvas.focus(); 58 59 // imaginary canvas 60 this._icanvas = document.createElement('canvas'); 61 this._icanvas.setAttribute('width',this.w); 62 this._icanvas.setAttribute('height',this.h); 63 this.ictx = this._icanvas.getContext('2d'); 64 this.ictx.imageSmoothingEnabled = false; 65 this.ictx.mozImageSmoothingEnabled = false; 66 this.ictx.webkitImageSmoothingEnabled = false; 67 }; 68 69 // start the main loop 70 this.start = function() { 71 this._loop = window.setInterval(this._update.bind(this),1000/this.fps); 72 }; 73 74 // the main loop calls this once every FPS. 75 // kills the loop upon error. does nothing if idle is true 76 this._update = function() { 77 if (this.idle) return; 78 try { 79 this.screen.update(); 80 this.frame = ++this.frame % this.fps; 81 } 82 catch (e) { 83 console.log(e.stack); 84 window.clearInterval(this._loop); 85 } 86 }; 87 88 }; 89 /* 90 ___ _ _ _ _ _ _ _ 91 / __|_ _(_)__| | | | | | |_(_) | 92 | (_ | '_| / _` | _ | |_| | _| | | 93 \___|_| |_\__,_| (_) \___/ \__|_|_| 94 95 */ 96 // Utilities 97 Grid.Util = new function() { 98 99 this.PHI = (1+Math.sqrt(5))/2; // golden ratio 100 this.PHI_A = 1 - this.PHI; 101 this.PHI_B = 1 - this.PHI_A; 102 103 // determines if two boxes, a and b, are intersecting 104 this.AABB = function(a,b) { 105 return (!( 106 a.x + a.w < b.x || // a too left 107 a.x > b.x + b.w || // a too right 108 a.y + a.h < b.y || // a too bg (y) 109 a.y > b.y + b.h // a too fg (y) 110 )); 111 }; 112 113 // returns a box for the intersection of boxes a and b 114 // this may return strange things if they aren't actually intersecting. use AABB first. 115 this.AABBC = function(a,b) { 116 return new function() { 117 this.x = a.x < b.x ? b.x : a.x; 118 this.y = a.y < b.y ? b.y : a.y; 119 this.w = (a.x + a.w < b.x + b.w ? a.x + a.w : b.x + b.w) - this.x; 120 this.h = (a.y + a.h < b.y + b.h ? a.y + a.h : b.y + b.h) - this.y; 121 }; 122 }; 123 124 // imagines a box and returns a list of sprites it AABB intersects. 125 // use this for instantaneous projectiles or other things from afar. 126 // sprites is a list of sprite instances, and box is an object with x,y,w,h 127 this.scan = function(box,sprites) { 128 var intersections = []; 129 for (var i in sprites) { 130 if (Grid.Util.AABB(box,sprites[i])) intersections.append(sprites[i]); 131 } 132 return intersections; 133 }; 134 135 // returns a sorted list of sprites according to their proximity to the origin x 136 this.xSort = function(sprites,x) { 137 x=x||0; 138 return sprites.sort(function(a,b) { 139 if (a.x + a.w < x) { // a is left 140 if (b.x + b.w < x) { // b is left 141 return (a.x + a.w > b.x + b.w ? -1 : 1); 142 } 143 else { // b is right 144 return (x - (a.x + a.w) < b.x - x ? -1 : 1); 145 } 146 } 147 else { // a is right 148 if (b.x > x) { // b is right 149 return (a.x < b.x ? -1 : 1); 150 } 151 else { // b is left 152 return (a.x - x < x - (b.x + b.w) ? -1 : 1); 153 } 154 } 155 }); 156 }; 157 158 // returns a sorted list of sprites according to their proximity to the origin y 159 this.ySort = function(sprites,y) { 160 y=y||0; 161 return sprites.sort(function(a,b) { 162 if (a.y + a.h < y) { // a is above 163 if (b.y + b.h < y) { // b is above 164 return (a.y + a.h > b.y + b.h ? -1 : 1); 165 } 166 else { // b is below 167 return (y - (a.y + a.h) < b.y - y ? -1 : 1); 168 } 169 } 170 else { // a is below 171 if (b.y > y) { // b is below 172 return (a.y < b.y ? -1 : 1); 173 } 174 else { // b is above 175 return (a.y - y < y - (b.y + b.h) ? -1 : 1); 176 } 177 } 178 }); 179 }; 180 181 }; 182 183 /* 184 ___ _ _ _ __ 185 / __|_ _(_)__| | | |/ /___ _ _ 186 | (_ | '_| / _` | _ | ' </ -_) || | 187 \___|_| |_\__,_| (_) |_|\_\___|\_, | 188 |__/ 189 */ 190 // keyboard stuff 191 192 Grid.Key = new function() { 193 194 // key name => bool state 195 // sprites should poll this during update() and act accordingly. 196 // sprites should use internal flags to determine if they can be interrupted. 197 // example: if (Grid.Key.state['left'] && !this.busy) ... 198 this.state = {}; 199 200 // keyCode => key name 201 this._keys = { 202 '9':'tab', 203 '13':'enter', 204 '27':'escape', 205 '32':'space', 206 '37':'left', 207 '38':'up', 208 '39':'right', 209 '40':'down' 210 }; 211 for (var i=48;i<=57;i++) { this._keys[i] = String.fromCharCode(i); } // numbers 212 for (var i=65;i<=90;i++) { this._keys[i] = String.fromCharCode(i).toLowerCase(); } // letters 213 214 // document's keydown listener 215 this._keydown = function(e) { 216 e=e||window.event; 217 if (e.altGraphKey || e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) return; 218 var key = this._keys[e.keyCode]; 219 if (!key) return; 220 e.preventDefault(); 221 e.stopPropagation(); 222 this.state[key] = true; 223 }.bind(this); // event handlers are wacky and need "this" explicitly bound 224 225 // document's keyup listener 226 this._keyup = function(e) { 227 e=e||window.event; 228 var key = this._keys[e.keyCode]; 229 if (!key) return; 230 e.preventDefault(); 231 e.stopPropagation(); 232 this.state[key] = false; 233 }.bind(this); // event handlers are wacky and need "this" explicitly bound 234 235 }; 236 237 /* 238 ___ _ _ ___ _ 239 / __|_ _(_)__| | / __| ___ _ _ _ _ __| | 240 | (_ | '_| / _` | _ \__ \/ _ \ || | ' \/ _` | 241 \___|_| |_\__,_| (_) |___/\___/\_,_|_||_\__,_| 242 243 */ 244 // in progress 245 Audio.prototype.fadeTo = function(fadeTo,callback) { 246 if (fadeTo < 0) fadeTo = 0; 247 else if (fadeTo > 1) fadeTo = 1; 248 var that = this; 249 var setVolume = function(setTo) { 250 return function() { 251 that.volume = setTo; 252 if (setTo == fadeTo && callback) callback(); 253 }; 254 }; 255 var delay = 0; 256 if (this.volume < fadeTo) { 257 for (var v=this.volume; v<=fadeTo; v+=0.1) { 258 setTimeout(setVolume(v),delay); 259 delay+=100; 260 } 261 } 262 else if (volume > fadeTo) { 263 for (var v=this.volume; v>=fadeTo; v-=0.1) { 264 setTimeout(setVolume(v),delay); 265 delay+=100; 266 } 267 } 268 else if (callback) callback(); 269 }; 270 271 Grid.Sound = new function() { 272 273 this.music = new Audio(); // swap out for one in Library 274 275 this.muted = false; 276 this.mute = function() { 277 this.muted ^= 1; 278 this.music.muted = this.muted; 279 }; 280 281 }; 282 283 /* 284 ___ _ _ ___ 285 / __|_ _(_)__| | / __| __ _ _ ___ ___ _ _ 286 | (_ | '_| / _` | _ \__ \/ _| '_/ -_) -_) ' \ 287 \___|_| |_\__,_| (_) |___/\__|_| \___\___|_||_| 288 289 */ 290 // a screen is a collection of sprites, such as a menu. levels are stages and extend this. 291 // only one screen is drawn to the canvas. 292 293 Grid.Screen = function(args) { 294 295 // required 296 this.grid = args['grid']; // grid reference 297 298 // optional 299 this.sprites = args['sprites']||[]; // list of Grid.Sprite instances 300 301 // called by the grid. screens should draw themselves to the grid canvas context if they need to. 302 // this notifies sprites that they should "step" or otherwise incrementally change themselves if needed. 303 // sprites should return boolean of whether or not they changed 304 this.update = function() { 305 var updated = false; 306 for (var i in this.sprites) { 307 updated |= this.sprites[i].update(); 308 } 309 if (updated) this._draw(); 310 }; 311 312 // draw the screen 313 // sprites are drawn from "back to front", that is, sprites with more "depth" are drawn first as to mimic layering. 314 this._draw = function() { 315 this.grid.ctx.clearRect(0,0,this.grid.w,this.grid.h); 316 for (var i in this.sprites.sort(Grid.Sprite.sort)) { 317 this.sprites[i].draw(this.grid.ctx); 318 } 319 }; 320 }; 321 322 /* 323 ___ _ _ ___ ___ _ 324 / __|_ _(_)__| | / __| __ _ _ ___ ___ _ _ / __| |_ __ _ __ _ ___ 325 | (_ | '_| / _` | _ \__ \/ _| '_/ -_) -_) ' \ _ \__ \ _/ _` / _` / -_) 326 \___|_| |_\__,_| (_) |___/\__|_| \___\___|_||_| (_) |___/\__\__,_\__, \___| 327 |___/ 328 */ 329 // stages are screens that are game levels. they have "curtains" and a "floor". 330 // imagine looking upon a theater stage from an ideal balcony seat. 331 // curtains are used for backgrounds and parallax. 332 // the floor is the "depth" where actors move away from and toward the "front" of the stage. 333 // the defaults define the curtains and floor in terms of the golden ratio: 334 // http://en.wikipedia.org/wiki/Golden_ratio 335 // where the longer portion represents the curtains and the shorter portion is the floor. 336 337 Grid.Screen.Stage = function(args) { 338 Grid.Screen.call(this,args); 339 340 // required 341 this.player = args['player']; // Grid.Sprite.Actor.Player instance 342 343 // optional 344 this.w = args['w'] || 960; // width of the stage (can go beyond viewport for panning) 345 this.y = args['y'] || 334; // height of curtains 346 this.h = args['h'] || 206; // height ("depth") of floor. 347 this.x = args['x'] || 0; // starting pan amount from stage left 348 this.panMargin = 200; // player distance from frame edge to start panning 349 this.pannable = args['pannable']||true; // whether to pan 350 351 // private stuff temporary to update() 352 this._sprites = []; // all in-frame sprites 353 this._actors = []; // in-frame actors 354 this._walls = []; // in-frame walls 355 356 this.update = function() { 357 var updated = false; 358 this._sprites = []; 359 this._walls = []; 360 this._actors = []; 361 362 // pan 363 if (this.pannable) with (this) { 364 // pan left if: frame left > stage left && player left < frame left margin 365 if (x > 0 && player.x < x + panMargin) { 366 // amount: margin - (player left - frame left) 367 x -= panMargin - (player.x - x); 368 if (x < 0) x = 0; // stop at min left 369 } 370 // pan right if: frame right < stage right && player right > frame right margin 371 else if (x + grid.w < w && player.x + player.w > x + grid.w - panMargin) { 372 // amount: margin - ((frame right) - (player right)) 373 x += panMargin - ((x + grid.w) - (player.x + player.w)); 374 if (x > w - grid.w) x = w - grid.w; // stop at max right 375 } 376 } 377 378 // determine active sprites, in-frame and frame-edged 379 for (var i in this.sprites) { 380 var s = this.sprites[i]; 381 if (s.x + s.w < this.x || s.x > this.x + this.grid.w) continue; // sprite is out of frame 382 this._sprites.push(s); 383 if (s instanceof Grid.Sprite.Wall) this._walls.push(s); 384 else if (s instanceof Grid.Sprite.Actor) this._actors.push(s); 385 } 386 387 // update active sprites. sprites should return boolean of whether they updated 388 for (var i in this._sprites.sort(Grid.Sprite.sort)) { 389 updated |= this._sprites[i].update(); 390 } 391 392 if (updated) { 393 this._draw(); // draw the screen 394 this._collisions(); // detect collisions and notify sprites after drawing so "reactions" occur next update cycle 395 } 396 397 }; 398 399 // returns boolean. actors should call this during their update() to determine if they can move to a given position 400 this.clipping = function(actor,x,y) { 401 // ensure actor stays on stage floor vertically. their "depth" is determined by the PHI_B of their height 402 if (y < this.y - (actor.h * Grid.Const.PHI_B) || y + actor.h > this.y + this.h) return true; 403 404 // ensure actor can only walk into frame horizontally, not out of frame 405 if (x < this.x || x + actor.w > this.x + this.grid.w) return true; 406 407 // ensure actor doesn't walk through walls 408 for (var i in this._walls) { 409 if (this._walls[i].clipping(x,y,actor.w,actor.h)) return true; 410 } 411 }; 412 413 // draws the screen 414 this._draw = function() { 415 this.grid.ctx.clearRect(0,0,this.grid.w,this.grid.h); 416 for (var i in this._sprites.sort(Grid.Sprite.sort)) { 417 this._sprites[i].draw(this.grid.ctx); 418 } 419 }; 420 421 this._collisions = function() { 422 // check in-frame actors for collision 423 // this performs n(n-1)/2 AABB checks, subchecking pixels in overlapping areas 424 // a pixel is only considered collidable if its opacity is >=127 425 // when two actors collide, both are notified via actor.collide(stage,other actor) 426 // the order of notification is undefined. actors should only "be collided", not do things to effect the other actor as it might not be notified yet. 427 // TODO "z" axis 428 var _actorsLength = this._actors.length; 429 for (var i=0;i<_actorsLength;i++) { 430 var a = this._actors[i]; 431 for (var j=i+1;j<_actorsLength;j++) { 432 var b = this._actors[j]; 433 if (!Grid.Util.AABB(a,b)) continue; // no box collision, move on. 434 435 // triggers don't need pixel detection, bounding box is enough 436 if (a instanceof Grid.Sprite.Actor.Trigger || b instanceof Grid.Sprite.Actor.Trigger) { 437 a.collide(b); 438 b.collide(a); 439 continue; 440 } 441 442 // moving on to pixel detection, 443 // determine intersection 444 var c = Grid.Util.AABBC(a,b); 445 446 // get separate slices of a and b at the intersection using the scratch canvas 447 this.grid.ictx.clearRect(a.x-this.x,a.y,a.w,a.h); // clear scratch space 448 this.grid.draw(a,this.grid.ictx); // draw scratch a 449 c.a = this.grid.ictx.getImageData(c.x-this.x,c.y,c.w,c.h); // slice of a @ intersect 450 this.grid.ictx.clearRect(b.x-this.x,b.y,b.w,b.h); // clear scratch space 451 this.grid.draw(b,this.grid.ictx); // draw scratch b 452 c.b = this.grid.ictx.getImageData(c.x-this.x,c.y,c.w,c.h); // slice of b @ intersect 453 454 // compare slices on a pixel-by-pixel basis according to a resolution 455 var resolution = 5; // jump this many pixels for each check in order to speed it up. 456 // if set to 1, all pixels are checked. 457 resolution *= 4; // each pixel actually takes up 4 elements in the data. 458 459 var sliceLength = c.a.data.length; 460 for (var p=0;p<sliceLength;p+=resolution) { 461 // notify and break when two pixels collide 462 // pixels are collidable when their opacity is >= 127 463 if (c.a.data[p+3] >= 127 && c.b.data[p+3] >= 127) { 464 a.collide(b); 465 b.collide(a); 466 break; 467 } 468 } 469 } 470 } 471 }; 472 473 }; Grid.Screen.Stage.prototype = new Grid.Screen(); 474 475 /* 476 ___ _ _ ___ _ _ 477 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ 478 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) 479 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| 480 |_| 481 */ 482 // sprites are stage elements that have a drawable face 483 484 Grid.Sprite = function(args) { 485 486 // required 487 this.screen = args['screen']; // Grid.Screen reference 488 this.sheet = new Image(); 489 this.sheet.src = args['sheet']; // sprite sheet url relative to html 490 this.frames = args['frames']; // { group name : [ [ x,y ] , ... ] , ... } where x,y is sheet cropping origin point 491 this.group = args['group']; // current frame group name 492 493 // optional 494 this.rate = v['rate']||10; // updates per animation step 495 this.name = args['name']||'Sprite'; 496 this.x = args['x']||0; 497 this.y = args['y']||0; 498 this.z = args['z']||1; // 0 rug, 1 standing, 2 in air, 3 sticky 499 this.w = args['w']||64; 500 this.h = args['h']||64; 501 502 this._step = 0; // animation step index, increments once per rated update prior to step() being called, wraps at length of current group 503 504 this.setGroup = function(name) { 505 this.group = name; 506 this._step %= this.frames[this.group].length; // wrap animation step index 507 }; 508 509 this.update = function() { 510 var updated = false; 511 512 // issue a tick. this is the sprite's custom logic every grid fps. 513 updated |= this.tick(); 514 515 // rate-limited steps 516 if (this.screen.grid.frame % this.rate == 0) { 517 if (this.frames[this.group].length > 1) { 518 updated = true; 519 this._step = ++this._step % this.frames[this.group].length; // increment and wrap animation step index 520 } 521 updated |= this.step(); 522 } 523 524 return updated; 525 }; 526 527 // called whenever the screen calls update() on the sprite. 528 // use this to perform logic per grid frame, return boolean if the sprite is updated 529 this.tick = function() { 530 }; 531 532 // called every rated amount of grid frames 533 // use this to perform rated logic. it may be called whether or not an animation step was made, as groups can have a single frame. 534 // return boolean of whether the sprite is updated 535 this.step = function() { 536 }; 537 538 this.draw = function(ctx) { 539 var frame = this.frames[this.group][this._step]; 540 ctx.drawImage(this.sheet,this.frame[0],this.frame[1],this.w,this.h,this.x - (this.screen.x||0),this.y,this.w,this.h); 541 } 542 }; 543 544 Grid.Sprite.sort = function(a,b) { 545 // sorting function that prioritizes sprites with more depth 546 // this is mainly used when figuring out the drawing order. 547 if (a instanceof Grid.Sprite.Wall ^ b instanceof Grid.Sprite.Wall) return (a instanceof Grid.Sprite.Wall ? -1 : 1); // walls always behind 548 else if (a.z != b.z) return (a.z < b.z ? -1 : 1); 549 else return (a.y + a.h < b.y + b.h ? -1 : 1); // sprite's bottom is further away 550 }; 551 552 /* 553 ___ _ _ ___ _ _ __ __ _ _ 554 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ \ \ / /_ _| | | 555 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ \ \/\/ / _` | | | 556 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) \_/\_/\__,_|_|_| 557 |_| 558 */ 559 // a wall is a stage element that actors cannot clip. 560 // walls are not actors, they are inert. 561 562 Grid.Sprite.Wall = function(args) { 563 Grid.Sprite.call(this,args); 564 565 // clip check an area 566 this.clipping = function(x,y,h,w) { 567 return !( 568 ); 569 }; 570 571 }; Grid.Sprite.Wall.prototype = new Grid.Sprite(); 572 573 /* 574 ___ _ _ ___ _ _ _ _ 575 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ 576 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| 577 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| 578 |_| 579 */ 580 // actors are any non-inert stage element. these include baddies, powerups, and props. 581 // actors interact, such as via collision. 582 583 Grid.Sprite.Actor = function(args) { 584 Grid.Sprite.call(this,args); 585 586 this.name = args['name']||'Actor'; 587 588 this.busy = false; // set this to disable user input or to denote that a multi-cycle process is taking place and you dont want to be interrupted 589 590 // get collided by another actor, react to it. 591 // this is meant to be overloaded. 592 // dont modify the other actor, as notification order is undefined. it's only provided for context so you can react appropriately. 593 // chances are if its the initial collision then there will be a large swath of calls to this function within a short period of time, as 594 // collisions are checked every screen update. 595 // set flags etc accordingly to "debounce" collision notifications. 596 // for example, when the player is collided by a baddy, inflict self-damage and set a flag and timeout for recover-time invulnerability. 597 this.collide = function(actor) { 598 }; 599 600 this.move = function(stage,direction,amount) { 601 amount = amount || 5; 602 this.setGroup(direction); 603 var x = this.x; 604 var y = this.y; 605 switch (direction) { 606 case 'left' : { x -= amount; break; } 607 case 'right' : { x += amount; break; } 608 case 'up' : { y -= amount; break; } 609 case 'down' : { y += amount; break; } 610 } 611 if (!stage.clipping(this,x,y)) { 612 this.x = x; 613 this.y = y; 614 } 615 return true; 616 } 617 618 }; Grid.Sprite.Actor.prototype = new Grid.Sprite(); 619 620 /* 621 ___ _ _ ___ _ _ _ _ ___ _ 622 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ | _ \ |__ _ _ _ ___ _ _ 623 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| _ | _/ / _` | || / -_) '_| 624 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| (_) |_| |_\__,_|\_, \___|_| 625 |_| |__/ 626 */ 627 // the user 628 629 Grid.Sprite.Actor.Player = function(v) { 630 Grid.Sprite.Actor.call(this,v=v||{}); 631 this.name = 'Player'; 632 this.update = function(stage) { 633 if (!this.busy) { 634 with (Grid.Key) { 635 if (key['left'] || key['a']) this.move(stage,'left'); 636 else if (key['right'] || key['d']) this.move(stage,'right'); 637 if (key['down'] || key['s']) this.move(stage,'down'); 638 else if (key['up'] || key['w']) this.move(stage,'up'); 639 } 640 } 641 Grid.Sprite.Actor.Player.prototype.update.call(this,stage); 642 }; 643 }; Grid.Sprite.Actor.Player.prototype = new Grid.Sprite.Actor(); 644 645 /* 646 ___ _ _ ___ _ _ _ _ _____ _ 647 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ |_ _| _(_)__ _ __ _ ___ _ _ 648 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| _ | || '_| / _` / _` / -_) '_| 649 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| (_) |_||_| |_\__, \__, \___|_| 650 |_| |___/|___/ 651 */ 652 // triggers are actors that collide with just bounding box 653 654 Grid.Sprite.Actor.Trigger = function(v) { 655 Grid.Sprite.Actor.call(this,v=v||{}); 656 this.name = 'Trigger'; 657 }; Grid.Sprite.Actor.Trigger.prototype = new Grid.Sprite.Actor(); 658