diff --git a/01_Programming/BasicExample1/BasicExample.html b/01_Programming/BasicExample1/BasicExample.html new file mode 100644 index 0000000..607322e --- /dev/null +++ b/01_Programming/BasicExample1/BasicExample.html @@ -0,0 +1,10 @@ + + + + BasicExample + + +
Hello, how are you?
+
I'm doing great, thank you!
+ + diff --git a/01_Programming/BasicExample2/BasicExample.html b/01_Programming/BasicExample2/BasicExample.html new file mode 100644 index 0000000..308cfc4 --- /dev/null +++ b/01_Programming/BasicExample2/BasicExample.html @@ -0,0 +1,21 @@ + + + +BasicExample + + + +
+ +
+ + \ No newline at end of file diff --git a/01_Programming/BasicExample3/BasicExample.html b/01_Programming/BasicExample3/BasicExample.html new file mode 100644 index 0000000..44a5f3d --- /dev/null +++ b/01_Programming/BasicExample3/BasicExample.html @@ -0,0 +1,12 @@ + + + +BasicExample + + + +
+ +
+ + \ No newline at end of file diff --git a/01_Programming/BasicExample3/BasicExample.js b/01_Programming/BasicExample3/BasicExample.js new file mode 100644 index 0000000..72f133d --- /dev/null +++ b/01_Programming/BasicExample3/BasicExample.js @@ -0,0 +1,10 @@ +"use strict"; + +function changeCanvasColor () { + var canvas = document.getElementById("mycanvas"); + var context = canvas.getContext("2d"); + context.fillStyle = "blue"; + context.fillRect(0, 0, canvas.width, canvas.height); +} + +document.addEventListener( 'DOMContentLoaded', changeCanvasColor); \ No newline at end of file diff --git a/01_Programming/verySimplehtml.html b/01_Programming/verySimplehtml.html new file mode 100644 index 0000000..e286baf --- /dev/null +++ b/01_Programming/verySimplehtml.html @@ -0,0 +1,14 @@ + + + +BasicExample + + + + + + diff --git a/02_GameProgrammingBasics/BasicGame/BasicGame.html b/02_GameProgrammingBasics/BasicGame/BasicGame.html new file mode 100644 index 0000000..a5db9bc --- /dev/null +++ b/02_GameProgrammingBasics/BasicGame/BasicGame.html @@ -0,0 +1,12 @@ + + + +BasicGame + + + +
+ +
+ + \ No newline at end of file diff --git a/02_GameProgrammingBasics/BasicGame/BasicGame.js b/02_GameProgrammingBasics/BasicGame/BasicGame.js new file mode 100644 index 0000000..332554e --- /dev/null +++ b/02_GameProgrammingBasics/BasicGame/BasicGame.js @@ -0,0 +1,24 @@ +var canvas = undefined; +var canvasContext = undefined; + +function start () { + canvas = document.getElementById("myCanvas"); + canvasContext = canvas.getContext("2d"); + gameLoop(); +} + +document.addEventListener( 'DOMContentLoaded', start); + +function update () { +} + +function draw () { +} + +function gameLoop () { + canvasContext.fillStyle = "blue"; + canvasContext.fillRect(0, 0, canvas.width, canvas.height); + update(); + draw(); + window.setTimeout(gameLoop, 1000 / 60); +} \ No newline at end of file diff --git a/03_CreatingGameWorld/MovingSquare/MovingSquare.html b/03_CreatingGameWorld/MovingSquare/MovingSquare.html new file mode 100644 index 0000000..b9f34e5 --- /dev/null +++ b/03_CreatingGameWorld/MovingSquare/MovingSquare.html @@ -0,0 +1,12 @@ + + + +MovingSquare + + + +
+ +
+ + \ No newline at end of file diff --git a/03_CreatingGameWorld/MovingSquare/MovingSquare.js b/03_CreatingGameWorld/MovingSquare/MovingSquare.js new file mode 100644 index 0000000..1036a53 --- /dev/null +++ b/03_CreatingGameWorld/MovingSquare/MovingSquare.js @@ -0,0 +1,37 @@ +"use strict"; + +var Game = { + canvas : undefined, + canvasContext : undefined, + rectanglePosition : 0 +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext("2d"); + Game.mainLoop(); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + var d = new Date(); + Game.rectanglePosition = d.getTime() % Game.canvas.width; +}; + +Game.draw = function () { + Game.canvasContext.fillStyle = "blue"; + Game.canvasContext.fillRect(Game.rectanglePosition, 100, 50, 50); +}; + diff --git a/04_GameAssets/FlyingSprite/FlyingSprite.html b/04_GameAssets/FlyingSprite/FlyingSprite.html new file mode 100644 index 0000000..3aa1d56 --- /dev/null +++ b/04_GameAssets/FlyingSprite/FlyingSprite.html @@ -0,0 +1,12 @@ + + + +FlyingSprite + + + +
+ +
+ + \ No newline at end of file diff --git a/04_GameAssets/FlyingSprite/FlyingSprite.js b/04_GameAssets/FlyingSprite/FlyingSprite.js new file mode 100644 index 0000000..6d5234b --- /dev/null +++ b/04_GameAssets/FlyingSprite/FlyingSprite.js @@ -0,0 +1,50 @@ +"use strict"; + +var Game = { + canvas : undefined, + canvasContext : undefined, + backgroundSprite : undefined, + balloonSprite : undefined, + balloonPosition : { x : 0, y : 50 } +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext("2d"); + Game.backgroundSprite = new Image(); + Game.backgroundSprite.src = "spr_background.jpg"; + Game.balloonSprite = new Image(); + Game.balloonSprite.src = "spr_balloon.png"; + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + 0, 0, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + var d = new Date(); + Game.balloonPosition.x = d.getTime() * 0.3 % Game.canvas.width; +}; + +Game.draw = function () { + Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 }); + Game.drawImage(Game.balloonSprite, Game.balloonPosition); +}; \ No newline at end of file diff --git a/04_GameAssets/FlyingSprite/spr_background.jpg b/04_GameAssets/FlyingSprite/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/04_GameAssets/FlyingSprite/spr_background.jpg differ diff --git a/04_GameAssets/FlyingSprite/spr_balloon.png b/04_GameAssets/FlyingSprite/spr_balloon.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/04_GameAssets/FlyingSprite/spr_balloon.png differ diff --git a/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.html b/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.html new file mode 100644 index 0000000..6db815c --- /dev/null +++ b/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.html @@ -0,0 +1,12 @@ + + + +FlyingSpriteWithSound + + + +
+ +
+ + \ No newline at end of file diff --git a/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.js b/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.js new file mode 100644 index 0000000..ff5ebe3 --- /dev/null +++ b/04_GameAssets/FlyingSpriteWithSound/FlyingSpriteWithSound.js @@ -0,0 +1,55 @@ +"use strict"; + +var Game = { + canvas : undefined, + canvasContext : undefined, + backgroundSprite : undefined, + balloonSprite : undefined, + balloonPosition : { x : 0, y : 50 }, + backgroundMusic : undefined +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext("2d"); + Game.backgroundSprite = new Image(); + Game.backgroundSprite.src = "spr_background.jpg"; + Game.balloonSprite = new Image(); + Game.balloonSprite.src = "spr_balloon.png"; + Game.backgroundMusic = new Audio(); + Game.backgroundMusic.src = "snd_music.mp3"; + Game.backgroundMusic.volume = 0.4; + Game.backgroundMusic.play(); + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + 0, 0, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + var d = new Date(); + Game.balloonPosition.x = d.getTime() * 0.3 % Game.canvas.width; +}; + +Game.draw = function () { + Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 }); + Game.drawImage(Game.balloonSprite, Game.balloonPosition); +}; \ No newline at end of file diff --git a/04_GameAssets/FlyingSpriteWithSound/snd_music.mp3 b/04_GameAssets/FlyingSpriteWithSound/snd_music.mp3 new file mode 100644 index 0000000..2347c04 Binary files /dev/null and b/04_GameAssets/FlyingSpriteWithSound/snd_music.mp3 differ diff --git a/04_GameAssets/FlyingSpriteWithSound/spr_background.jpg b/04_GameAssets/FlyingSpriteWithSound/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/04_GameAssets/FlyingSpriteWithSound/spr_background.jpg differ diff --git a/04_GameAssets/FlyingSpriteWithSound/spr_balloon.png b/04_GameAssets/FlyingSpriteWithSound/spr_balloon.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/04_GameAssets/FlyingSpriteWithSound/spr_balloon.png differ diff --git a/04_GameAssets/SpriteDrawing/SpriteDrawing.html b/04_GameAssets/SpriteDrawing/SpriteDrawing.html new file mode 100644 index 0000000..4ecccc4 --- /dev/null +++ b/04_GameAssets/SpriteDrawing/SpriteDrawing.html @@ -0,0 +1,12 @@ + + + +SpriteDrawing + + + +
+ +
+ + \ No newline at end of file diff --git a/04_GameAssets/SpriteDrawing/SpriteDrawing.js b/04_GameAssets/SpriteDrawing/SpriteDrawing.js new file mode 100644 index 0000000..c7218e4 --- /dev/null +++ b/04_GameAssets/SpriteDrawing/SpriteDrawing.js @@ -0,0 +1,43 @@ +"use strict"; + +var Game = { + canvas : undefined, + canvasContext : undefined, + balloonSprite : undefined +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext("2d"); + Game.balloonSprite = new Image(); + Game.balloonSprite.src = "spr_balloon.png"; + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + 0, 0, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { +}; + +Game.draw = function () { + Game.drawImage(Game.balloonSprite, { x : 100, y : 100 }); +}; \ No newline at end of file diff --git a/04_GameAssets/SpriteDrawing/spr_balloon.png b/04_GameAssets/SpriteDrawing/spr_balloon.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/04_GameAssets/SpriteDrawing/spr_balloon.png differ diff --git a/05_KnowingPlayer/Balloon1/Balloon.html b/05_KnowingPlayer/Balloon1/Balloon.html new file mode 100644 index 0000000..a627f00 --- /dev/null +++ b/05_KnowingPlayer/Balloon1/Balloon.html @@ -0,0 +1,13 @@ + + + + Balloon + + + + +
+ +
+ + \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon1/Balloon.js b/05_KnowingPlayer/Balloon1/Balloon.js new file mode 100644 index 0000000..1385419 --- /dev/null +++ b/05_KnowingPlayer/Balloon1/Balloon.js @@ -0,0 +1,53 @@ +"use strict"; + +function handleMouseMove(evt) { + Game.balloonPosition = { x : evt.clientX, y : evt.clientY }; +} + +var Game = { + canvas : undefined, + canvasContext : undefined, + backgroundSprite : undefined, + balloonSprite : undefined, + balloonPosition : { x : 0, y : 0 } +}; + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + 0, 0, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext('2d'); + document.onmousemove = handleMouseMove; + Game.backgroundSprite = new Image(); + Game.backgroundSprite.src = "spr_background.jpg"; + Game.balloonSprite = new Image(); + Game.balloonSprite.src = "spr_balloon.png"; + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { +}; + +Game.draw = function () { + Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 }); + Game.drawImage(Game.balloonSprite, Game.balloonPosition); +}; \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon1/game-layout.css b/05_KnowingPlayer/Balloon1/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/05_KnowingPlayer/Balloon1/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon1/spr_background.jpg b/05_KnowingPlayer/Balloon1/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/05_KnowingPlayer/Balloon1/spr_background.jpg differ diff --git a/05_KnowingPlayer/Balloon1/spr_balloon.png b/05_KnowingPlayer/Balloon1/spr_balloon.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/05_KnowingPlayer/Balloon1/spr_balloon.png differ diff --git a/05_KnowingPlayer/Balloon2/Balloon.html b/05_KnowingPlayer/Balloon2/Balloon.html new file mode 100644 index 0000000..a627f00 --- /dev/null +++ b/05_KnowingPlayer/Balloon2/Balloon.html @@ -0,0 +1,13 @@ + + + + Balloon + + + + +
+ +
+ + \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon2/Balloon.js b/05_KnowingPlayer/Balloon2/Balloon.js new file mode 100644 index 0000000..eeda5a1 --- /dev/null +++ b/05_KnowingPlayer/Balloon2/Balloon.js @@ -0,0 +1,55 @@ +"use strict"; + +function handleMouseMove(evt) { + Game.mousePosition = { x : evt.pageX, y : evt.pageY }; +} + +var Game = { + canvas : undefined, + canvasContext : undefined, + backgroundSprite : undefined, + balloonSprite : undefined, + mousePosition : { x : 0, y : 0 }, + balloonOrigin : { x : 0, y : 0 } +}; + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position, origin) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + -origin.x, -origin.y, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext('2d'); + document.onmousemove = handleMouseMove; + Game.backgroundSprite = new Image(); + Game.backgroundSprite.src = "spr_background.jpg"; + Game.balloonSprite = new Image(); + Game.balloonSprite.src = "spr_balloon.png"; + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.mainLoop = function() { + Game.clearCanvas(); + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { +}; + +Game.draw = function () { + Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 }, { x : 0, y : 0 }); + Game.balloonOrigin = { x : Game.balloonSprite.width / 2, y : Game.balloonSprite.height }; + Game.drawImage(Game.balloonSprite, Game.mousePosition, Game.balloonOrigin); +}; \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon2/game-layout.css b/05_KnowingPlayer/Balloon2/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/05_KnowingPlayer/Balloon2/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/05_KnowingPlayer/Balloon2/spr_background.jpg b/05_KnowingPlayer/Balloon2/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/05_KnowingPlayer/Balloon2/spr_background.jpg differ diff --git a/05_KnowingPlayer/Balloon2/spr_balloon.png b/05_KnowingPlayer/Balloon2/spr_balloon.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/05_KnowingPlayer/Balloon2/spr_balloon.png differ diff --git a/05_KnowingPlayer/Painter1/Painter.html b/05_KnowingPlayer/Painter1/Painter.html new file mode 100644 index 0000000..cfb32c2 --- /dev/null +++ b/05_KnowingPlayer/Painter1/Painter.html @@ -0,0 +1,13 @@ + + + + Painter + + + + +
+ +
+ + \ No newline at end of file diff --git a/05_KnowingPlayer/Painter1/Painter.js b/05_KnowingPlayer/Painter1/Painter.js new file mode 100644 index 0000000..a2888b6 --- /dev/null +++ b/05_KnowingPlayer/Painter1/Painter.js @@ -0,0 +1,62 @@ +"use strict"; + +function handleMouseMove(evt) { + Game.mousePosition = { x : evt.pageX, y : evt.pageY }; +} + +var Game = { + canvas : undefined, + canvasContext : undefined, + backgroundSprite : undefined, + cannonBarrelSprite : undefined, + mousePosition : { x : 0, y : 0 }, + cannonPosition : { x : 72, y : 405 }, + cannonOrigin : { x : 34, y : 34 }, + cannonRotation : 0 +}; + +Game.clearCanvas = function () { + Game.canvasContext.clearRect(0, 0, Game.canvas.width, Game.canvas.height); +}; + +Game.drawImage = function (sprite, position, rotation, origin) { + Game.canvasContext.save(); + Game.canvasContext.translate(position.x, position.y); + Game.canvasContext.rotate(rotation); + Game.canvasContext.drawImage(sprite, 0, 0, sprite.width, sprite.height, + -origin.x, -origin.y, sprite.width, sprite.height); + Game.canvasContext.restore(); +}; + +Game.start = function () { + Game.canvas = document.getElementById("myCanvas"); + Game.canvasContext = Game.canvas.getContext('2d'); + + document.onmousemove = handleMouseMove; + + Game.backgroundSprite = new Image(); + Game.backgroundSprite.src = "spr_background.jpg"; + Game.cannonBarrelSprite = new Image(); + Game.cannonBarrelSprite.src = "spr_cannon_barrel.png"; + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.mainLoop = function() { + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + var opposite = Game.mousePosition.y - Game.cannonPosition.y; + var adjacent = Game.mousePosition.x - Game.cannonPosition.x; + Game.cannonRotation = Math.atan2(opposite, adjacent); +}; + +Game.draw = function () { + Game.clearCanvas(); + Game.drawImage(Game.backgroundSprite, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + Game.drawImage(Game.cannonBarrelSprite, Game.cannonPosition, Game.cannonRotation, Game.cannonOrigin); +}; \ No newline at end of file diff --git a/05_KnowingPlayer/Painter1/game-layout.css b/05_KnowingPlayer/Painter1/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/05_KnowingPlayer/Painter1/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/05_KnowingPlayer/Painter1/spr_background.jpg b/05_KnowingPlayer/Painter1/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/05_KnowingPlayer/Painter1/spr_background.jpg differ diff --git a/05_KnowingPlayer/Painter1/spr_cannon_barrel.png b/05_KnowingPlayer/Painter1/spr_cannon_barrel.png new file mode 100644 index 0000000..a1c43aa Binary files /dev/null and b/05_KnowingPlayer/Painter1/spr_cannon_barrel.png differ diff --git a/06_ReactingInput/Painter2/Painter.html b/06_ReactingInput/Painter2/Painter.html new file mode 100644 index 0000000..cfb32c2 --- /dev/null +++ b/06_ReactingInput/Painter2/Painter.html @@ -0,0 +1,13 @@ + + + + Painter + + + + +
+ +
+ + \ No newline at end of file diff --git a/06_ReactingInput/Painter2/Painter.js b/06_ReactingInput/Painter2/Painter.js new file mode 100644 index 0000000..c1cad24 --- /dev/null +++ b/06_ReactingInput/Painter2/Painter.js @@ -0,0 +1,139 @@ +"use strict"; + +var sprites = {}; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function handleMouseMove(evt) { + Mouse.position = { x : evt.pageX, y : evt.pageY }; +} + +function handleMouseDown(evt) { + if (evt.which === 1) + Mouse.leftDown = true; +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +var Keyboard = { keyDown : -1 }; + +var Keys = { + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90 +}; + +var Mouse = { + position : { x : 0, y : 0 }, + leftDown : false +}; + +var Canvas2D = { + canvas : undefined, + canvasContext : undefined +}; + +Canvas2D.initialize = function (canvasName) { + Canvas2D.canvas = document.getElementById(canvasName); + Canvas2D.canvasContext = Canvas2D.canvas.getContext('2d'); +}; + +Canvas2D.clear = function () { + Canvas2D.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D.drawImage = function (sprite, position, rotation, origin) { + Canvas2D.canvasContext.save(); + Canvas2D.canvasContext.translate(position.x, position.y); + Canvas2D.canvasContext.rotate(rotation); + Canvas2D.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + Canvas2D.canvasContext.restore(); +}; + +var cannon = {}; + +cannon.initialize = function() { + cannon.position = { x : 72, y : 405 }; + cannon.colorPosition = { x : 55, y : 388 }; + cannon.origin = { x : 34, y : 34 }; + cannon.currentColor = sprites.cannon_red; + cannon.rotation = 0; +}; + +cannon.update = function () { + if (Keyboard.keyDown === Keys.R) + cannon.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + cannon.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + cannon.currentColor = sprites.cannon_blue; + if (Mouse.leftDown) { + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + cannon.rotation = Math.atan2(opposite, adjacent); + } else + cannon.rotation = 0; +}; + +cannon.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, cannon.position, cannon.rotation, cannon.origin); + Canvas2D.drawImage(cannon.currentColor, cannon.colorPosition, 0, { x : 0, y : 0 }); +}; + +var Game = {}; + +Game.start = function () { + Canvas2D.initialize("myCanvas"); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; + + var spriteFolder = "../../assets/Painter/sprites/"; + sprites.background = new Image(); + sprites.background.src = spriteFolder + "spr_background.jpg"; + sprites.cannon_barrel = new Image(); + sprites.cannon_barrel.src = spriteFolder + "spr_cannon_barrel.png"; + sprites.cannon_red = new Image(); + sprites.cannon_red.src = spriteFolder + "spr_cannon_red.png"; + sprites.cannon_green = new Image(); + sprites.cannon_green.src = spriteFolder + "spr_cannon_green.png"; + sprites.cannon_blue = new Image(); + sprites.cannon_blue.src = spriteFolder + "spr_cannon_blue.png"; + + cannon.initialize(); + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.mainLoop = function() { + Game.update(); + Game.draw(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + cannon.update(); +}; + +Game.draw = function () { + Canvas2D.clear(); + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + cannon.draw(); +}; \ No newline at end of file diff --git a/06_ReactingInput/Painter2/game-layout.css b/06_ReactingInput/Painter2/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/06_ReactingInput/Painter2/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/06_ReactingInput/Painter2a/Painter.html b/06_ReactingInput/Painter2a/Painter.html new file mode 100644 index 0000000..cfb32c2 --- /dev/null +++ b/06_ReactingInput/Painter2a/Painter.html @@ -0,0 +1,13 @@ + + + + Painter + + + + +
+ +
+ + \ No newline at end of file diff --git a/06_ReactingInput/Painter2a/Painter.js b/06_ReactingInput/Painter2a/Painter.js new file mode 100644 index 0000000..7e22ae2 --- /dev/null +++ b/06_ReactingInput/Painter2a/Painter.js @@ -0,0 +1,152 @@ +"use strict"; + +var sprites = {}; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function handleMouseMove(evt) { + Mouse.position = { x : evt.pageX, y : evt.pageY }; +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +var Keyboard = { keyDown : -1 }; + +var Keys = { + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90 +}; + +var Mouse = { + position : { x : 0, y : 0 }, + leftDown : false, + leftPressed : false +}; + +Mouse.reset = function() { + Mouse.leftPressed = false; +}; + +var Canvas2D = { + canvas : undefined, + canvasContext : undefined +}; + +Canvas2D.initialize = function (canvasName) { + Canvas2D.canvas = document.getElementById(canvasName); + Canvas2D.canvasContext = Canvas2D.canvas.getContext('2d'); +}; + +Canvas2D.clear = function () { + Canvas2D.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D.drawImage = function (sprite, position, rotation, origin) { + Canvas2D.canvasContext.save(); + Canvas2D.canvasContext.translate(position.x, position.y); + Canvas2D.canvasContext.rotate(rotation); + Canvas2D.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + Canvas2D.canvasContext.restore(); +}; + +var cannon = {}; + +cannon.initialize = function() { + cannon.position = { x : 72, y : 405 }; + cannon.colorPosition = { x : 55, y : 388 }; + cannon.origin = { x : 34, y : 34 }; + cannon.currentColor = sprites.cannon_red; + cannon.rotation = 0; + cannon.calculateAngle = false; +}; + +cannon.update = function () { + if (Keyboard.keyDown === Keys.R) + cannon.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + cannon.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + cannon.currentColor = sprites.cannon_blue; + if (Mouse.leftPressed) + cannon.calculateAngle = !cannon.calculateAngle; + + if (cannon.calculateAngle) { + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + cannon.rotation = Math.atan2(opposite, adjacent); + } else + cannon.rotation = 0; +}; + +cannon.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, cannon.position, cannon.rotation, cannon.origin); + Canvas2D.drawImage(cannon.currentColor, cannon.colorPosition, 0, { x : 0, y : 0 }); +}; + +var Game = {}; + +Game.start = function () { + Canvas2D.initialize("myCanvas"); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; + + var spriteFolder = "../../assets/Painter/sprites/"; + sprites.background = new Image(); + sprites.background.src = spriteFolder + "spr_background.jpg"; + sprites.cannon_barrel = new Image(); + sprites.cannon_barrel.src = spriteFolder + "spr_cannon_barrel.png"; + sprites.cannon_red = new Image(); + sprites.cannon_red.src = spriteFolder + "spr_cannon_red.png"; + sprites.cannon_green = new Image(); + sprites.cannon_green.src = spriteFolder + "spr_cannon_green.png"; + sprites.cannon_blue = new Image(); + sprites.cannon_blue.src = spriteFolder + "spr_cannon_blue.png"; + + cannon.initialize(); + window.setTimeout(Game.mainLoop, 500); +}; + +document.addEventListener( 'DOMContentLoaded', Game.start); + +Game.mainLoop = function() { + Game.update(); + Game.draw(); + Mouse.reset(); + window.setTimeout(Game.mainLoop, 1000 / 60); +}; + +Game.update = function () { + cannon.update(); +}; + +Game.draw = function () { + Canvas2D.clear(); + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + cannon.draw(); +}; \ No newline at end of file diff --git a/06_ReactingInput/Painter2a/game-layout.css b/06_ReactingInput/Painter2a/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/06_ReactingInput/Painter2a/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/07_BasicGameObjects/LAB.min.js b/07_BasicGameObjects/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/07_BasicGameObjects/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/07_BasicGameObjects/Painter3/Cannon.js b/07_BasicGameObjects/Painter3/Cannon.js new file mode 100644 index 0000000..bc5586a --- /dev/null +++ b/07_BasicGameObjects/Painter3/Cannon.js @@ -0,0 +1,28 @@ +"use strict"; + +var cannon = {}; + +cannon.initialize = function() { + cannon.position = { x : 72, y : 405 }; + cannon.colorPosition = { x : 55, y : 388 }; + cannon.origin = { x : 34, y : 34 }; + cannon.currentColor = sprites.cannon_red; + cannon.rotation = 0; +}; + +cannon.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + cannon.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + cannon.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + cannon.currentColor = sprites.cannon_blue; + var opposite = Mouse.position.y - cannon.position.y; + var adjacent = Mouse.position.x - cannon.position.x; + cannon.rotation = Math.atan2(opposite, adjacent); +}; + +cannon.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, cannon.position, cannon.rotation, cannon.origin); + Canvas2D.drawImage(cannon.currentColor, cannon.colorPosition, 0, { x : 0, y : 0 }); +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter3/Canvas2D.js b/07_BasicGameObjects/Painter3/Canvas2D.js new file mode 100644 index 0000000..a654973 --- /dev/null +++ b/07_BasicGameObjects/Painter3/Canvas2D.js @@ -0,0 +1,26 @@ +"use strict"; + +var Canvas2D = { + canvas : undefined, + canvasContext : undefined +}; + +Canvas2D.initialize = function (canvasName) { + Canvas2D.canvas = document.getElementById(canvasName); + Canvas2D.canvasContext = Canvas2D.canvas.getContext('2d'); +}; + +Canvas2D.clear = function () { + Canvas2D.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D.drawImage = function (sprite, position, rotation, origin) { + Canvas2D.canvasContext.save(); + Canvas2D.canvasContext.translate(position.x, position.y); + Canvas2D.canvasContext.rotate(rotation); + Canvas2D.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + Canvas2D.canvasContext.restore(); +}; diff --git a/07_BasicGameObjects/Painter3/Painter.html b/07_BasicGameObjects/Painter3/Painter.html new file mode 100644 index 0000000..d6a75ab --- /dev/null +++ b/07_BasicGameObjects/Painter3/Painter.html @@ -0,0 +1,28 @@ + + + + Painter + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/07_BasicGameObjects/Painter3/Painter.js b/07_BasicGameObjects/Painter3/Painter.js new file mode 100644 index 0000000..7c6f6da --- /dev/null +++ b/07_BasicGameObjects/Painter3/Painter.js @@ -0,0 +1,78 @@ +"use strict"; + +var sprites = {}; + +window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + +var Game = { + spritesStillLoading : 0 +}; + +Game.start = function (canvasName) { + Canvas2D.initialize(canvasName); + Keyboard.initialize(); + Mouse.initialize(); + Game.loadAssets(); + Game.assetLoadingLoop(); +}; + +Game.initialize = function() { + cannon.initialize(); +}; + +Game.handleInput = function () { + cannon.handleInput(); +}; + +Game.update = function () { +}; + +Game.draw = function () { + Canvas2D.clear(); + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + cannon.draw(); +}; + +Game.loadAssets = function () { + var spriteFolder = "../../assets/Painter/sprites/"; + + sprites.background = Game.loadSprite(spriteFolder + "spr_background.jpg"); + sprites.cannon_barrel = Game.loadSprite(spriteFolder + "spr_cannon_barrel.png"); + sprites.cannon_red = Game.loadSprite(spriteFolder + "spr_cannon_red.png"); + sprites.cannon_green = Game.loadSprite(spriteFolder + "spr_cannon_green.png"); + sprites.cannon_blue = Game.loadSprite(spriteFolder + "spr_cannon_blue.png"); +}; + +Game.loadSprite = function (imageName) { + var image = new Image(); + image.src = imageName; + Game.spritesStillLoading += 1; + image.onload = function () { + Game.spritesStillLoading -= 1; + }; + return image; +}; + +Game.assetLoadingLoop = function () { + if (Game.spritesStillLoading > 0) + window.requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + Game.mainLoop(); + } +}; + +Game.mainLoop = function () { + Game.handleInput(); + Game.update(); + Game.draw(); + Mouse.reset(); + window.requestAnimationFrame(Game.mainLoop); +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter3/game-layout.css b/07_BasicGameObjects/Painter3/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/07_BasicGameObjects/Painter3/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/07_BasicGameObjects/Painter3/input/Keyboard.js b/07_BasicGameObjects/Painter3/input/Keyboard.js new file mode 100644 index 0000000..dd340df --- /dev/null +++ b/07_BasicGameObjects/Painter3/input/Keyboard.js @@ -0,0 +1,16 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +var Keyboard = { keyDown : -1 }; + +Keyboard.initialize = function () { + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +}; diff --git a/07_BasicGameObjects/Painter3/input/Mouse.js b/07_BasicGameObjects/Painter3/input/Mouse.js new file mode 100644 index 0000000..896f241 --- /dev/null +++ b/07_BasicGameObjects/Painter3/input/Mouse.js @@ -0,0 +1,34 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = { x : evt.pageX, y : evt.pageY }; +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +var Mouse = { + position : { x : 0, y : 0 }, + leftDown : false, + leftPressed : false +}; + +Mouse.initialize = function () { + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +}; + +Mouse.reset = function() { + Mouse.leftPressed = false; +}; diff --git a/07_BasicGameObjects/Painter3/system/Keys.js b/07_BasicGameObjects/Painter3/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/07_BasicGameObjects/Painter3/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/Ball.js b/07_BasicGameObjects/Painter4/Ball.js new file mode 100644 index 0000000..64ebb9c --- /dev/null +++ b/07_BasicGameObjects/Painter4/Ball.js @@ -0,0 +1,53 @@ +"use strict"; + +var ball = { +}; + +ball.initialize = function() { + ball.position = { x : 0, y : 0 }; + ball.velocity = { x : 0, y : 0 }; + ball.origin = { x : 0, y : 0 }; + ball.currentColor = sprites.ball_red; + ball.shooting = false; +}; + +ball.handleInput = function (delta) { + if (Mouse.leftPressed && !ball.shooting) { + ball.shooting = true; + ball.velocity.x = (Mouse.position.x - ball.position.x) * 1.2; + ball.velocity.y = (Mouse.position.y - ball.position.y) * 1.2; + } +}; + +ball.update = function (delta) { + if (ball.shooting) { + ball.velocity.x *= 0.99; + ball.velocity.y += 6; + ball.position.x += ball.velocity.x * delta; + ball.position.y += ball.velocity.y * delta; + } + else { + if (cannon.currentColor === sprites.cannon_red) + ball.currentColor = sprites.ball_red; + else if (cannon.currentColor === sprites.cannon_green) + ball.currentColor = sprites.ball_green; + else + ball.currentColor = sprites.ball_blue; + ball.position = cannon.ballPosition(); + ball.position.x -= ball.currentColor.width / 2; + ball.position.y -= ball.currentColor.height / 2; + } + if (painterGameWorld.isOutsideWorld(ball.position)) + ball.reset(); +}; + +ball.reset = function () { + ball.position = { x : 0, y : 0 }; + ball.shooting = false; +}; + +ball.draw = function () { + if (!ball.shooting) + return; + Canvas2D.drawImage(ball.currentColor, ball.position, 0, ball.origin); +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/Cannon.js b/07_BasicGameObjects/Painter4/Cannon.js new file mode 100644 index 0000000..178d25f --- /dev/null +++ b/07_BasicGameObjects/Painter4/Cannon.js @@ -0,0 +1,38 @@ +"use strict"; + +var cannon = { +}; + +cannon.initialize = function() { + cannon.position = { x : 72, y : 405 }; + cannon.colorPosition = { x : 55, y : 388 }; + cannon.origin = { x : 34, y : 34 }; + cannon.currentColor = sprites.cannon_red; + cannon.rotation = 0; +}; + +cannon.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + cannon.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + cannon.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + cannon.currentColor = sprites.cannon_blue; + var opposite = Mouse.position.y - cannon.position.y; + var adjacent = Mouse.position.x - cannon.position.x; + cannon.rotation = Math.atan2(opposite, adjacent); +}; + +cannon.update = function (delta) { +}; + +cannon.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, cannon.position, cannon.rotation, cannon.origin); + Canvas2D.drawImage(cannon.currentColor, cannon.colorPosition, 0, { x : 0, y : 0 }); +}; + +cannon.ballPosition = function() { + var opposite = Math.sin(cannon.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(cannon.rotation) * sprites.cannon_barrel.width * 0.6; + return { x : cannon.position.x + adjacent, y : cannon.position.y + opposite }; +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/Canvas2D.js b/07_BasicGameObjects/Painter4/Canvas2D.js new file mode 100644 index 0000000..925ee25 --- /dev/null +++ b/07_BasicGameObjects/Painter4/Canvas2D.js @@ -0,0 +1,26 @@ +"use strict"; + +var Canvas2D = { + canvas : undefined, + canvasContext : undefined +}; + +Canvas2D.initialize = function (canvasName) { + Canvas2D.canvas = document.getElementById(canvasName); + Canvas2D.canvasContext = Canvas2D.canvas.getContext('2d'); +}; + +Canvas2D.clear = function () { + Canvas2D.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D.drawImage = function (sprite, position, rotation, origin) { + Canvas2D.canvasContext.save(); + Canvas2D.canvasContext.translate(position.x, position.y); + Canvas2D.canvasContext.rotate(rotation); + Canvas2D.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + Canvas2D.canvasContext.restore(); +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/Game.js b/07_BasicGameObjects/Painter4/Game.js new file mode 100644 index 0000000..0ba3569 --- /dev/null +++ b/07_BasicGameObjects/Painter4/Game.js @@ -0,0 +1,59 @@ +"use strict"; + +window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + +var Game = { + spritesStillLoading : 0, + gameWorld : undefined +} + +Game.start = function (canvasName, x, y) { + Canvas2D.initialize(canvasName); + Game.size = { x : x, y : y }; + Keyboard.initialize(); + Mouse.initialize(); + Game.loadAssets(); + Game.assetLoadingLoop(); +}; + +Game.loadAssets = function () { +}; + +Game.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this.spritesStillLoading += 1; + image.onload = function () { + Game.spritesStillLoading -= 1; + }; + return image; +}; + +Game.assetLoadingLoop = function () { + if (!Game.spritesStillLoading > 0) + window.requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + window.requestAnimationFrame(Game.mainLoop); + } +}; + +Game.mainLoop = function () { + var delta = 1 / 60; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + Mouse.reset(); + requestAnimationFrame(Game.mainLoop); +}; + diff --git a/07_BasicGameObjects/Painter4/Painter.html b/07_BasicGameObjects/Painter4/Painter.html new file mode 100644 index 0000000..d11ae25 --- /dev/null +++ b/07_BasicGameObjects/Painter4/Painter.html @@ -0,0 +1,30 @@ + + + + Painter + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/Painter.js b/07_BasicGameObjects/Painter4/Painter.js new file mode 100644 index 0000000..a2b34ba --- /dev/null +++ b/07_BasicGameObjects/Painter4/Painter.js @@ -0,0 +1,26 @@ +"use strict"; + +var sprites = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); +}; + +Game.initialize = function () { + + console.log("Creating game world"); + cannon.initialize(); + ball.initialize(); + Game.gameWorld = painterGameWorld; +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/PainterGameWorld.js b/07_BasicGameObjects/Painter4/PainterGameWorld.js new file mode 100644 index 0000000..c9f1351 --- /dev/null +++ b/07_BasicGameObjects/Painter4/PainterGameWorld.js @@ -0,0 +1,29 @@ +"use strict"; + +var painterGameWorld = { +}; + +painterGameWorld.handleInput = function (delta) { + ball.handleInput(delta); + cannon.handleInput(delta); +}; + +painterGameWorld.update = function (delta) { + ball.update(delta); + cannon.update(delta); +}; + +painterGameWorld.draw = function () { + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + ball.draw(); + cannon.draw(); +}; + +painterGameWorld.reset = function () { + ball.reset(); + cannon.reset(); +}; + +painterGameWorld.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/game-layout.css b/07_BasicGameObjects/Painter4/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/07_BasicGameObjects/Painter4/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/input/Keyboard.js b/07_BasicGameObjects/Painter4/input/Keyboard.js new file mode 100644 index 0000000..554d72e --- /dev/null +++ b/07_BasicGameObjects/Painter4/input/Keyboard.js @@ -0,0 +1,16 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +var Keyboard = { keyDown : -1 }; + +Keyboard.initialize = function () { + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/input/Mouse.js b/07_BasicGameObjects/Painter4/input/Mouse.js new file mode 100644 index 0000000..44895e8 --- /dev/null +++ b/07_BasicGameObjects/Painter4/input/Mouse.js @@ -0,0 +1,34 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = { x : evt.pageX, y : evt.pageY }; +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +var Mouse = { + position : { x : 0, y : 0 }, + leftDown : false, + leftPressed : false +}; + +Mouse.initialize = function () { + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +}; + +Mouse.reset = function() { + Mouse.leftPressed = false; +}; \ No newline at end of file diff --git a/07_BasicGameObjects/Painter4/system/Keys.js b/07_BasicGameObjects/Painter4/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/07_BasicGameObjects/Painter4/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/08_GameObjectTypes/LAB.min.js b/08_GameObjectTypes/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/08_GameObjectTypes/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Ball.js b/08_GameObjectTypes/Painter5/Ball.js new file mode 100644 index 0000000..f8d0c3b --- /dev/null +++ b/08_GameObjectTypes/Painter5/Ball.js @@ -0,0 +1,50 @@ +"use strict"; + +function Ball() { + this.position = { x : 0, y : 0 }; + this.velocity = { x : 0, y : 0 }; + this.origin = { x : 0, y : 0 }; + this.currentColor = sprites.ball_red; + this.shooting = false; +} + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity.x = (Mouse.position.x - this.position.x) * 1.2; + this.velocity.y = (Mouse.position.y - this.position.y) * 1.2; + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + this.position.x += this.velocity.x * delta; + this.position.y += this.velocity.y * delta; + } + else { + if (Game.gameWorld.cannon.currentColor === sprites.cannon_red) + this.currentColor = sprites.ball_red; + else if (Game.gameWorld.cannon.currentColor === sprites.cannon_green) + this.currentColor = sprites.ball_green; + else + this.currentColor = sprites.ball_blue; + this.position = Game.gameWorld.cannon.ballPosition(); + this.position.x -= this.currentColor.width / 2; + this.position.y -= this.currentColor.height / 2; + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = { x : 0, y : 0 }; + this.shooting = false; +}; + +Ball.prototype.draw = function () { + if (!this.shooting) + return; + Canvas2D.drawImage(this.currentColor, this.position, 0, this.origin); +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Cannon.js b/08_GameObjectTypes/Painter5/Cannon.js new file mode 100644 index 0000000..4605f3b --- /dev/null +++ b/08_GameObjectTypes/Painter5/Cannon.js @@ -0,0 +1,39 @@ +"use strict"; + +function Cannon() { + this.position = { x : 72, y : 405 }; + this.colorPosition = { x : 55, y : 388 }; + this.origin = { x : 34, y : 34 }; + this.currentColor = sprites.cannon_red; + this.rotation = 0; +} + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + this.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + this.currentColor = sprites.cannon_blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.update = function (delta) { +}; + +Cannon.prototype.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, this.origin); + Canvas2D.drawImage(this.currentColor, this.colorPosition, 0, { x : 0, y : 0 }); +}; + +Cannon.prototype.ballPosition = function() { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return { x : this.position.x + adjacent, y : this.position.y + opposite }; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Canvas2D.js b/08_GameObjectTypes/Painter5/Canvas2D.js new file mode 100644 index 0000000..01e244b --- /dev/null +++ b/08_GameObjectTypes/Painter5/Canvas2D.js @@ -0,0 +1,28 @@ +"use strict"; + +function Canvas2D_Singleton() { + this.canvas = undefined; + this.canvasContext = undefined; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this.canvas = document.getElementById(canvasName); + this.canvasContext = this.canvas.getContext('2d'); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, origin) { + this.canvasContext.save(); + this.canvasContext.translate(position.x, position.y); + this.canvasContext.rotate(rotation); + this.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + this.canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Game.js b/08_GameObjectTypes/Painter5/Game.js new file mode 100644 index 0000000..4ca17a3 --- /dev/null +++ b/08_GameObjectTypes/Painter5/Game.js @@ -0,0 +1,66 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this.size = undefined; + this.spritesStillLoading = 0; + this.gameWorld = undefined; +} + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this.size = { x : x, y : y }; + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { + this.gameWorld = new PainterGameWorld(); +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this.spritesStillLoading += 1; + image.onload = function () { + Game.spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this.spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + Mouse.reset(); + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/08_GameObjectTypes/Painter5/PaintCan.js b/08_GameObjectTypes/Painter5/PaintCan.js new file mode 100644 index 0000000..08cb929 --- /dev/null +++ b/08_GameObjectTypes/Painter5/PaintCan.js @@ -0,0 +1,52 @@ +"use strict"; + +function PaintCan(positionOffset) { + this.currentColor = sprites.can_red; + this.velocity = { x : 0, y : 0 }; + this.position = { x : 0, y : 0 }; + this.origin = { x : 0, y : 0 }; + this.positionOffset = positionOffset; + this.reset(); +} + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position = { x : this.positionOffset, y : -200 }; + this.velocity = { x : 0, y : 0 }; +}; + +PaintCan.prototype.update = function (delta) { + this.position.x += this.velocity.x * delta; + this.position.y += this.velocity.y * delta; + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.currentColor = this.calculateRandomColor(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) + this.moveToTop(); + this.minVelocity += 0.01; +}; + +PaintCan.prototype.draw = function () { + Canvas2D.drawImage(this.currentColor, this.position, 0, this.origin); +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return { x : 0, y : Math.random() * 30 + this.minVelocity }; +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return sprites.can_red; + else if (randomval == 1) + return sprites.can_green; + else + return sprites.can_blue; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Painter.html b/08_GameObjectTypes/Painter5/Painter.html new file mode 100644 index 0000000..bc1620f --- /dev/null +++ b/08_GameObjectTypes/Painter5/Painter.html @@ -0,0 +1,32 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/Painter.js b/08_GameObjectTypes/Painter5/Painter.js new file mode 100644 index 0000000..990b924 --- /dev/null +++ b/08_GameObjectTypes/Painter5/Painter.js @@ -0,0 +1,24 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.jpg"); +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/PainterGameWorld.js b/08_GameObjectTypes/Painter5/PainterGameWorld.js new file mode 100644 index 0000000..e9cad3b --- /dev/null +++ b/08_GameObjectTypes/Painter5/PainterGameWorld.js @@ -0,0 +1,45 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450); + this.can2 = new PaintCan(575); + this.can3 = new PaintCan(700); +} + +PainterGameWorld.prototype.handleInput = function (delta) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); +}; + +PainterGameWorld.prototype.update = function (delta) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); +}; + +PainterGameWorld.prototype.reset = function () { + this.ball.reset(); + this.cannon.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/game-layout.css b/08_GameObjectTypes/Painter5/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/08_GameObjectTypes/Painter5/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/08_GameObjectTypes/Painter5/input/Keyboard.js b/08_GameObjectTypes/Painter5/input/Keyboard.js new file mode 100644 index 0000000..2bff5d9 --- /dev/null +++ b/08_GameObjectTypes/Painter5/input/Keyboard.js @@ -0,0 +1,17 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +var Keyboard = new Keyboard_Singleton(); diff --git a/08_GameObjectTypes/Painter5/input/Mouse.js b/08_GameObjectTypes/Painter5/input/Mouse.js new file mode 100644 index 0000000..e789a14 --- /dev/null +++ b/08_GameObjectTypes/Painter5/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = { x : evt.pageX, y : evt.pageY }; +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = { x : 0, y : 0 }; + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); diff --git a/08_GameObjectTypes/Painter5/system/Keys.js b/08_GameObjectTypes/Painter5/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/08_GameObjectTypes/Painter5/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Ball.js b/08_GameObjectTypes/Painter6/Ball.js new file mode 100644 index 0000000..28fc91d --- /dev/null +++ b/08_GameObjectTypes/Painter6/Ball.js @@ -0,0 +1,48 @@ +"use strict"; + +function Ball() { + this.position = new Vector2(); + this.velocity = new Vector2(); + this.origin = new Vector2(); + this.currentColor = sprites.ball_red; + this.shooting = false; +} + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + this.position.addTo(this.velocity.multiply(delta)); + } + else { + if (Game.gameWorld.cannon.currentColor === sprites.cannon_red) + this.currentColor = sprites.ball_red; + else if (Game.gameWorld.cannon.currentColor === sprites.cannon_green) + this.currentColor = sprites.ball_green; + else + this.currentColor = sprites.ball_blue; + var center = new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + this.position = Game.gameWorld.cannon.ballPosition().subtractFrom(center); + + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = new Vector2(); + this.shooting = false; +}; + +Ball.prototype.draw = function () { + if (!this.shooting) + return; + Canvas2D.drawImage(this.currentColor, this.position, 0, this.origin); +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Cannon.js b/08_GameObjectTypes/Painter6/Cannon.js new file mode 100644 index 0000000..029c098 --- /dev/null +++ b/08_GameObjectTypes/Painter6/Cannon.js @@ -0,0 +1,39 @@ +"use strict"; + +function Cannon() { + this.position = new Vector2(72, 405); + this.colorPosition = new Vector2(55, 388); + this.origin = new Vector2(34, 34); + this.currentColor = sprites.cannon_red; + this.rotation = 0; +} + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.currentColor = sprites.cannon_red; + else if (Keyboard.keyDown === Keys.G) + this.currentColor = sprites.cannon_green; + else if (Keyboard.keyDown === Keys.B) + this.currentColor = sprites.cannon_blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.update = function (delta) { +}; + +Cannon.prototype.draw = function () { + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, this.origin); + Canvas2D.drawImage(this.currentColor, this.colorPosition, 0, new Vector2()); +}; + +Cannon.prototype.ballPosition = function() { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Canvas2D.js b/08_GameObjectTypes/Painter6/Canvas2D.js new file mode 100644 index 0000000..01e244b --- /dev/null +++ b/08_GameObjectTypes/Painter6/Canvas2D.js @@ -0,0 +1,28 @@ +"use strict"; + +function Canvas2D_Singleton() { + this.canvas = undefined; + this.canvasContext = undefined; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this.canvas = document.getElementById(canvasName); + this.canvasContext = this.canvas.getContext('2d'); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, origin) { + this.canvasContext.save(); + this.canvasContext.translate(position.x, position.y); + this.canvasContext.rotate(rotation); + this.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + this.canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Game.js b/08_GameObjectTypes/Painter6/Game.js new file mode 100644 index 0000000..af623b4 --- /dev/null +++ b/08_GameObjectTypes/Painter6/Game.js @@ -0,0 +1,65 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this.size = undefined; + this.spritesStillLoading = 0; + this.gameWorld = undefined; +} + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this.size = { x : x, y : y }; + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this.spritesStillLoading += 1; + image.onload = function () { + Game.spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this.spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + Mouse.reset(); + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/08_GameObjectTypes/Painter6/PaintCan.js b/08_GameObjectTypes/Painter6/PaintCan.js new file mode 100644 index 0000000..59b97ef --- /dev/null +++ b/08_GameObjectTypes/Painter6/PaintCan.js @@ -0,0 +1,50 @@ +"use strict"; + +function PaintCan(xPosition) { + this.currentColor = sprites.can_red; + this.velocity = new Vector2(); + this.position = new Vector2(xPosition, -200); + this.origin = new Vector2(); + this.reset(); +} + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position.y = -200; + this.velocity = new Vector2(); +}; + +PaintCan.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.currentColor = this.calculateRandomColor(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) + this.moveToTop(); + this.minVelocity += 0.01; +}; + +PaintCan.prototype.draw = function () { + Canvas2D.drawImage(this.currentColor, this.position, 0, this.origin); +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return sprites.can_red; + else if (randomval == 1) + return sprites.can_green; + else + return sprites.can_blue; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Painter.html b/08_GameObjectTypes/Painter6/Painter.html new file mode 100644 index 0000000..5e26186 --- /dev/null +++ b/08_GameObjectTypes/Painter6/Painter.html @@ -0,0 +1,33 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/Painter.js b/08_GameObjectTypes/Painter6/Painter.js new file mode 100644 index 0000000..f57a249 --- /dev/null +++ b/08_GameObjectTypes/Painter6/Painter.js @@ -0,0 +1,29 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.jpg"); +}; + +Game.initialize = function () { + + Game.gameWorld = new PainterGameWorld(); +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/PainterGameWorld.js b/08_GameObjectTypes/Painter6/PainterGameWorld.js new file mode 100644 index 0000000..e9cad3b --- /dev/null +++ b/08_GameObjectTypes/Painter6/PainterGameWorld.js @@ -0,0 +1,45 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450); + this.can2 = new PaintCan(575); + this.can3 = new PaintCan(700); +} + +PainterGameWorld.prototype.handleInput = function (delta) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); +}; + +PainterGameWorld.prototype.update = function (delta) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background, { x : 0, y : 0 }, 0, { x : 0, y : 0 }); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); +}; + +PainterGameWorld.prototype.reset = function () { + this.ball.reset(); + this.cannon.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/game-layout.css b/08_GameObjectTypes/Painter6/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/08_GameObjectTypes/Painter6/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/geom/Vector2.js b/08_GameObjectTypes/Painter6/geom/Vector2.js new file mode 100644 index 0000000..ea34ef7 --- /dev/null +++ b/08_GameObjectTypes/Painter6/geom/Vector2.js @@ -0,0 +1,93 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/08_GameObjectTypes/Painter6/input/Keyboard.js b/08_GameObjectTypes/Painter6/input/Keyboard.js new file mode 100644 index 0000000..2bff5d9 --- /dev/null +++ b/08_GameObjectTypes/Painter6/input/Keyboard.js @@ -0,0 +1,17 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +var Keyboard = new Keyboard_Singleton(); diff --git a/08_GameObjectTypes/Painter6/input/Mouse.js b/08_GameObjectTypes/Painter6/input/Mouse.js new file mode 100644 index 0000000..8d4071e --- /dev/null +++ b/08_GameObjectTypes/Painter6/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = new Vector2(); + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); diff --git a/08_GameObjectTypes/Painter6/system/Keys.js b/08_GameObjectTypes/Painter6/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/08_GameObjectTypes/Painter6/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/09_ColorsCollisions/LAB.min.js b/09_ColorsCollisions/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/09_ColorsCollisions/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Ball.js b/09_ColorsCollisions/Painter7/Ball.js new file mode 100644 index 0000000..90deb1a --- /dev/null +++ b/09_ColorsCollisions/Painter7/Ball.js @@ -0,0 +1,89 @@ +"use strict"; + +function Ball() { + this.currentColor = sprites.ball_red; + this.velocity = Vector2.zero; + this.position = Vector2.zero; + this.origin = Vector2.zero; + this.shooting = false; +} + +Object.defineProperty(Ball.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.ball_red) + return Color.red; + else if (this.currentColor === sprites.ball_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.ball_red; + else if (value === Color.green) + this.currentColor = sprites.ball_green; + else if (value === Color.blue) + this.currentColor = sprites.ball_blue; + } + }); + +Object.defineProperty(Ball.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(Ball.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(Ball.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(Ball.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + this.position.addTo(this.velocity.multiply(delta)); + } + else { + this.color = Game.gameWorld.cannon.color; + this.position = Game.gameWorld.cannon.ballPosition.subtractFrom(this.center); + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = Vector2.zero; + this.shooting = false; +}; + +Ball.prototype.draw = function () { + if (!this.shooting) + return; + Canvas2D.drawImage(this.currentColor, this.position, 0, this.origin); +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Cannon.js b/09_ColorsCollisions/Painter7/Cannon.js new file mode 100644 index 0000000..274f9c9 --- /dev/null +++ b/09_ColorsCollisions/Painter7/Cannon.js @@ -0,0 +1,92 @@ +"use strict"; + +function Cannon() { + this.position = new Vector2(72, 405); + this.origin = new Vector2(34, 34); + this.currentColor = sprites.cannon_red; + this.rotation = 0; +} + +Object.defineProperty(Cannon.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.cannon_red) + return Color.red; + else if (this.currentColor === sprites.cannon_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.cannon_red; + else if (value === Color.green) + this.currentColor = sprites.cannon_green; + else if (value === Color.blue) + this.currentColor = sprites.cannon_blue; + } + }); + +Object.defineProperty(Cannon.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(Cannon.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(Cannon.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(Cannon.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +Object.defineProperty(Cannon.prototype, "ballPosition", + { + get: function () { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); + } + }); + + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.color = Color.red; + else if (Keyboard.keyDown === Keys.G) + this.color = Color.green; + else if (Keyboard.keyDown === Keys.B) + this.color = Color.blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + + +Cannon.prototype.update = function (delta) { +}; + +Cannon.prototype.draw = function () { + var colorPosition = this.position.subtract(this.size.divideBy(2)); + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, this.origin); + Canvas2D.drawImage(this.currentColor, colorPosition); +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Canvas2D.js b/09_ColorsCollisions/Painter7/Canvas2D.js new file mode 100644 index 0000000..33d7c47 --- /dev/null +++ b/09_ColorsCollisions/Painter7/Canvas2D.js @@ -0,0 +1,38 @@ +"use strict"; + +function Canvas2D_Singleton() { + console.log("Creating Canvas2D object"); + this._canvas = null; + this._canvasContext = null; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this._canvas = document.getElementById(canvasName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + } +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, origin) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this._canvasContext.save(); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x, -origin.y, + sprite.width, sprite.height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Game.js b/09_ColorsCollisions/Painter7/Game.js new file mode 100644 index 0000000..0d0d219 --- /dev/null +++ b/09_ColorsCollisions/Painter7/Game.js @@ -0,0 +1,84 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + console.log("Creating game"); + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this.gameWorld = null; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + Mouse.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/09_ColorsCollisions/Painter7/PaintCan.js b/09_ColorsCollisions/Painter7/PaintCan.js new file mode 100644 index 0000000..19a2725 --- /dev/null +++ b/09_ColorsCollisions/Painter7/PaintCan.js @@ -0,0 +1,107 @@ +"use strict"; + +function PaintCan(xPosition) { + this.currentColor = sprites.can_red; + this.velocity = Vector2.zero; + this.position = new Vector2(xPosition, -200); + this.origin = Vector2.zero; + this.reset(); +} + +Object.defineProperty(PaintCan.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.can_red) + return Color.red; + else if (this.currentColor === sprites.can_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.can_red; + else if (value === Color.green) + this.currentColor = sprites.can_green; + else if (value === Color.blue) + this.currentColor = sprites.can_blue; + } + }); + +Object.defineProperty(PaintCan.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(PaintCan.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(PaintCan.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(PaintCan.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position.y = -200; + this.velocity = Vector2.zero; +}; + +PaintCan.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.color = this.calculateRandomColor(); + } + + // calculate the distance between this can and the ball + var ball = Game.gameWorld.ball; + var distance = ball.position.add(ball.center).subtractFrom(this.position).subtractFrom(this.center); + + if (Math.abs(distance.x) < this.center.x && Math.abs(distance.y) < this.center.y) { + this.color = ball.color; + ball.reset(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) + this.moveToTop(); + this.minVelocity += 0.01; +}; + +PaintCan.prototype.draw = function () { + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, this.origin); +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return Color.red; + else if (randomval == 1) + return Color.green; + else + return Color.blue; +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Painter.html b/09_ColorsCollisions/Painter7/Painter.html new file mode 100644 index 0000000..478f156 --- /dev/null +++ b/09_ColorsCollisions/Painter7/Painter.html @@ -0,0 +1,34 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/Painter.js b/09_ColorsCollisions/Painter7/Painter.js new file mode 100644 index 0000000..f57a249 --- /dev/null +++ b/09_ColorsCollisions/Painter7/Painter.js @@ -0,0 +1,29 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.jpg"); +}; + +Game.initialize = function () { + + Game.gameWorld = new PainterGameWorld(); +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/PainterGameWorld.js b/09_ColorsCollisions/Painter7/PainterGameWorld.js new file mode 100644 index 0000000..0d4eadb --- /dev/null +++ b/09_ColorsCollisions/Painter7/PainterGameWorld.js @@ -0,0 +1,45 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450); + this.can2 = new PaintCan(575); + this.can3 = new PaintCan(700); +} + +PainterGameWorld.prototype.handleInput = function (delta) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); +}; + +PainterGameWorld.prototype.update = function (delta) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); +}; + +PainterGameWorld.prototype.reset = function () { + this.ball.reset(); + this.cannon.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/game-layout.css b/09_ColorsCollisions/Painter7/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/09_ColorsCollisions/Painter7/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/geom/Vector2.js b/09_ColorsCollisions/Painter7/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/09_ColorsCollisions/Painter7/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/input/Keyboard.js b/09_ColorsCollisions/Painter7/input/Keyboard.js new file mode 100644 index 0000000..2bff5d9 --- /dev/null +++ b/09_ColorsCollisions/Painter7/input/Keyboard.js @@ -0,0 +1,17 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +var Keyboard = new Keyboard_Singleton(); diff --git a/09_ColorsCollisions/Painter7/input/Mouse.js b/09_ColorsCollisions/Painter7/input/Mouse.js new file mode 100644 index 0000000..8d4071e --- /dev/null +++ b/09_ColorsCollisions/Painter7/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = new Vector2(); + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); diff --git a/09_ColorsCollisions/Painter7/myGame.js b/09_ColorsCollisions/Painter7/myGame.js new file mode 100644 index 0000000..e69de29 diff --git a/09_ColorsCollisions/Painter7/system/Color.js b/09_ColorsCollisions/Painter7/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/09_ColorsCollisions/Painter7/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/09_ColorsCollisions/Painter7/system/Keys.js b/09_ColorsCollisions/Painter7/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/09_ColorsCollisions/Painter7/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/10_LimitedLives/LAB.min.js b/10_LimitedLives/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/10_LimitedLives/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Ball.js b/10_LimitedLives/Painter8/Ball.js new file mode 100644 index 0000000..ed779d8 --- /dev/null +++ b/10_LimitedLives/Painter8/Ball.js @@ -0,0 +1,91 @@ +"use strict"; + +function Ball() { + this.currentColor = sprites.ball_red; + this.velocity = Vector2.zero; + this.position = Vector2.zero; + this.origin = Vector2.zero; + this.rotation = 0; + this.shooting = false; + this.reset(); +} + +Object.defineProperty(Ball.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.ball_red) + return Color.red; + else if (this.currentColor === sprites.ball_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.ball_red; + else if (value === Color.green) + this.currentColor = sprites.ball_green; + else if (value === Color.blue) + this.currentColor = sprites.ball_blue; + } + }); + +Object.defineProperty(Ball.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(Ball.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(Ball.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(Ball.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + this.position.addTo(this.velocity.multiply(delta)); + } + else { + this.color = Game.gameWorld.cannon.color; + this.position = Game.gameWorld.cannon.ballPosition.subtractFrom(this.center); + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = new Vector2(65, 390); + this.shooting = false; +}; + +Ball.prototype.draw = function () { + if (!this.shooting) + return; + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, 1, this.origin); +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Cannon.js b/10_LimitedLives/Painter8/Cannon.js new file mode 100644 index 0000000..849a044 --- /dev/null +++ b/10_LimitedLives/Painter8/Cannon.js @@ -0,0 +1,96 @@ +"use strict"; + +function Cannon() { + this.position = new Vector2(72, 405); + this.origin = new Vector2(34, 34); + this.currentColor = sprites.cannon_red; + this.velocity = Vector2.zero; + this.rotation = 0; + this.visible = true; +} + +Object.defineProperty(Cannon.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.cannon_red) + return Color.red; + else if (this.currentColor === sprites.cannon_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.cannon_red; + else if (value === Color.green) + this.currentColor = sprites.cannon_green; + else if (value === Color.blue) + this.currentColor = sprites.cannon_blue; + } + }); + +Object.defineProperty(Cannon.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(Cannon.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(Cannon.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(Cannon.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +Object.defineProperty(Cannon.prototype, "ballPosition", + { + get: function () { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); + } + }); + + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.color = Color.red; + else if (Keyboard.keyDown === Keys.G) + this.color = Color.green; + else if (Keyboard.keyDown === Keys.B) + this.color = Color.blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +Cannon.prototype.draw = function () { + if (!this.visible) + return; + var colorPosition = this.position.subtract(this.size.divideBy(2)); + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, 1, this.origin); + Canvas2D.drawImage(this.currentColor, colorPosition); +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Canvas2D.js b/10_LimitedLives/Painter8/Canvas2D.js new file mode 100644 index 0000000..90a5b9b --- /dev/null +++ b/10_LimitedLives/Painter8/Canvas2D.js @@ -0,0 +1,56 @@ +"use strict"; + +function Canvas2D_Singleton() { + console.log("Creating Canvas2D object"); + this.canvas = null; + this.canvasContext = null; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this.canvas = document.getElementById(canvasName); + + if (this.canvas.getContext) + this.canvasContext = this.canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + } +}; + +Canvas2D_Singleton.prototype.clear = function () { + this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this.canvasContext.save(); + this.canvasContext.translate(position.x, position.y); + this.canvasContext.rotate(rotation); + this.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x * scale, -origin.y * scale, + sprite.width, sprite.height * scale); + this.canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, color, textAlign, fontname, fontsize) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this.canvasContext.save(); + this.canvasContext.translate(position.x, position.y); + this.canvasContext.textBaseline = 'top'; + this.canvasContext.font = fontsize + " " + fontname; + this.canvasContext.fillStyle = color.toString(); + this.canvasContext.textAlign = textAlign; + this.canvasContext.fillText(text, 0, 0); + this.canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Game.js b/10_LimitedLives/Painter8/Game.js new file mode 100644 index 0000000..f9392b7 --- /dev/null +++ b/10_LimitedLives/Painter8/Game.js @@ -0,0 +1,66 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this.size = null; + this.spritesStillLoading = 0; + this.gameWorld = null; +} + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this.size = new Vector2(x, y); + + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this.spritesStillLoading += 1; + image.onload = function () { + Game.spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this.spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + Mouse.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/10_LimitedLives/Painter8/PaintCan.js b/10_LimitedLives/Painter8/PaintCan.js new file mode 100644 index 0000000..8f5436f --- /dev/null +++ b/10_LimitedLives/Painter8/PaintCan.js @@ -0,0 +1,112 @@ +"use strict"; + +function PaintCan(xPosition, targetColor) { + this.currentColor = sprites.can_red; + this.velocity = Vector2.zero; + this.position = new Vector2(xPosition, -200); + this.origin = Vector2.zero; + this.targetColor = targetColor; + this.reset(); +} + +Object.defineProperty(PaintCan.prototype, "color", + { + get: function () { + if (this.currentColor === sprites.can_red) + return Color.red; + else if (this.currentColor === sprites.can_green) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = sprites.can_red; + else if (value === Color.green) + this.currentColor = sprites.can_green; + else if (value === Color.blue) + this.currentColor = sprites.can_blue; + } + }); + +Object.defineProperty(PaintCan.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(PaintCan.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(PaintCan.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(PaintCan.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position.y = -200; + this.velocity = Vector2.zero; +}; + +PaintCan.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.color = this.calculateRandomColor(); + } + + // calculate the distance between this can and the ball + var ball_center = Game.gameWorld.ball.center; + var ball_position = Game.gameWorld.ball.position; + var distance = ball_position.add(ball_center).subtractFrom(this.position).subtractFrom(this.center); + + if (Math.abs(distance.x) < this.center.x && Math.abs(distance.y) < this.center.y) { + this.color = Game.gameWorld.ball.color; + Game.gameWorld.ball.reset(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) { + if (this.color !== this.targetColor) + Game.gameWorld.lives -= 1; + this.moveToTop(); + } + this.minVelocity += 0.01; +}; + +PaintCan.prototype.draw = function () { + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, 1, this.origin); +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return Color.red; + else if (randomval == 1) + return Color.green; + else + return Color.blue; +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Painter.html b/10_LimitedLives/Painter8/Painter.html new file mode 100644 index 0000000..478f156 --- /dev/null +++ b/10_LimitedLives/Painter8/Painter.html @@ -0,0 +1,34 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/10_LimitedLives/Painter8/Painter.js b/10_LimitedLives/Painter8/Painter.js new file mode 100644 index 0000000..44d07d2 --- /dev/null +++ b/10_LimitedLives/Painter8/Painter.js @@ -0,0 +1,29 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.png"); +}; + +Game.initialize = function () { + + Game.gameWorld = new PainterGameWorld(); +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/PainterGameWorld.js b/10_LimitedLives/Painter8/PainterGameWorld.js new file mode 100644 index 0000000..edc98cb --- /dev/null +++ b/10_LimitedLives/Painter8/PainterGameWorld.js @@ -0,0 +1,65 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450, Color.red); + this.can2 = new PaintCan(575, Color.green); + this.can3 = new PaintCan(700, Color.blue); + + this.lives = 5; +} + +PainterGameWorld.prototype.handleInput = function (delta) { + if (this.lives > 0) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); + } + else { + if (Mouse.leftPressed) + this.reset(); + } +}; + +PainterGameWorld.prototype.update = function (delta) { + if (this.lives <= 0) + return; + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); + for (var i = 0; i < this.lives; i++) { + Canvas2D.drawImage(sprites.lives, new Vector2(i * sprites.lives.width + 15, 60)); + } + if (this.lives <= 0) { + console.log("Game over!"); + Canvas2D.drawImage(sprites.gameover, + new Vector2(Game.size.x - sprites.gameover.width, + Game.size.y - sprites.gameover.height).divideBy(2)); + } +}; + +PainterGameWorld.prototype.reset = function () { + this.lives = 5; + this.cannon.reset(); + this.ball.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/game-layout.css b/10_LimitedLives/Painter8/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/10_LimitedLives/Painter8/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/10_LimitedLives/Painter8/geom/Vector2.js b/10_LimitedLives/Painter8/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/10_LimitedLives/Painter8/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/input/Keyboard.js b/10_LimitedLives/Painter8/input/Keyboard.js new file mode 100644 index 0000000..e28efe9 --- /dev/null +++ b/10_LimitedLives/Painter8/input/Keyboard.js @@ -0,0 +1,17 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +var Keyboard = new Keyboard_Singleton(); \ No newline at end of file diff --git a/10_LimitedLives/Painter8/input/Mouse.js b/10_LimitedLives/Painter8/input/Mouse.js new file mode 100644 index 0000000..779d85f --- /dev/null +++ b/10_LimitedLives/Painter8/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = new Vector2(); + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/10_LimitedLives/Painter8/system/Color.js b/10_LimitedLives/Painter8/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/10_LimitedLives/Painter8/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/10_LimitedLives/Painter8/system/Keys.js b/10_LimitedLives/Painter8/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/10_LimitedLives/Painter8/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/LAB.min.js b/11_OrganizingGameObjects/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/11_OrganizingGameObjects/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Ball.js b/11_OrganizingGameObjects/Painter9/Ball.js new file mode 100644 index 0000000..4659e1d --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Ball.js @@ -0,0 +1,35 @@ +"use strict"; + +function Ball() { + ThreeColorGameObject.call(this, sprites.ball_red, sprites.ball_green, sprites.ball_blue); + this.shooting = false; + this.reset(); +} + +Ball.prototype = Object.create(ThreeColorGameObject.prototype); + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + } +}; + +Ball.prototype.update = function (delta) { + ThreeColorGameObject.prototype.update.call(this, delta); + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + } + else { + this.color = Game.gameWorld.cannon.color; + this.position = Game.gameWorld.cannon.ballPosition.subtractFrom(this.center); + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = new Vector2(65, 390); + this.shooting = false; +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Cannon.js b/11_OrganizingGameObjects/Painter9/Cannon.js new file mode 100644 index 0000000..621bb57 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Cannon.js @@ -0,0 +1,42 @@ +"use strict"; + +function Cannon() { + ThreeColorGameObject.call(this, sprites.cannon_red, sprites.cannon_green, sprites.cannon_blue); + this.position = new Vector2(72, 405); + this.origin = new Vector2(34, 34); +} + +Cannon.prototype = Object.create(ThreeColorGameObject.prototype); + +Object.defineProperty(Cannon.prototype, "ballPosition", + { + get: function () { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); + } + }); + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.color = Color.red; + else if (Keyboard.keyDown === Keys.G) + this.color = Color.green; + else if (Keyboard.keyDown === Keys.B) + this.color = Color.blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.draw = function () { + if (!this.visible) + return; + var colorPosition = this.position.subtract(this.size.divideBy(2)); + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, 1, this.origin); + Canvas2D.drawImage(this.currentColor, colorPosition); +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Canvas2D.js b/11_OrganizingGameObjects/Painter9/Canvas2D.js new file mode 100644 index 0000000..dec06b3 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Canvas2D.js @@ -0,0 +1,39 @@ +"use strict"; + +function Canvas2D_Singleton() { + console.log("Creating Canvas2D object"); + this.canvas = null; + this.canvasContext = null; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this.canvas = document.getElementById(canvasName); + + if (this.canvas.getContext) + this.canvasContext = this.canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + } +}; + +Canvas2D_Singleton.prototype.clear = function () { + this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this.canvasContext.save(); + this.canvasContext.translate(position.x, position.y); + this.canvasContext.rotate(rotation); + this.canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x * scale, -origin.y * scale, + sprite.width, sprite.height * scale); + this.canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Game.js b/11_OrganizingGameObjects/Painter9/Game.js new file mode 100644 index 0000000..38ebc43 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Game.js @@ -0,0 +1,75 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._size = null; + this._spritesStillLoading = 0; + this.gameWorld = null; +} + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + Mouse.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/11_OrganizingGameObjects/Painter9/PaintCan.js b/11_OrganizingGameObjects/Painter9/PaintCan.js new file mode 100644 index 0000000..efa82a4 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/PaintCan.js @@ -0,0 +1,60 @@ +"use strict"; + +function PaintCan(positionOffset, targetColor) { + ThreeColorGameObject.call(this, sprites.can_red, sprites.can_green, sprites.can_blue); + this.positionOffset = positionOffset; + this.targetColor = targetColor; + this.reset(); +} + +PaintCan.prototype = Object.create(ThreeColorGameObject.prototype); + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position = new Vector2(this.positionOffset, -200); + this.velocity = Vector2.zero; +}; + +PaintCan.prototype.update = function (delta) { + ThreeColorGameObject.prototype.update.call(this, delta); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.color = this.calculateRandomColor(); + } + + // calculate the distance between this can and the ball + var ball_center = Game.gameWorld.ball.center; + var ball_position = Game.gameWorld.ball.position; + var distance = ball_position.add(ball_center).subtractFrom(this.position).subtractFrom(this.center); + + if (Math.abs(distance.x) < this.center.x && Math.abs(distance.y) < this.center.y) { + this.color = Game.gameWorld.ball.color; + Game.gameWorld.ball.reset(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) { + if (this.color !== this.targetColor) + Game.gameWorld.lives -= 1; + this.moveToTop(); + } + this.minVelocity += 0.01; +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return Color.red; + else if (randomval == 1) + return Color.green; + else + return Color.blue; +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Painter.html b/11_OrganizingGameObjects/Painter9/Painter.html new file mode 100644 index 0000000..46fa2d8 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Painter.html @@ -0,0 +1,35 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/Painter.js b/11_OrganizingGameObjects/Painter9/Painter.js new file mode 100644 index 0000000..6638e93 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/Painter.js @@ -0,0 +1,54 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.png"); +}; + +Game.initialize = function () { + + Game.gameWorld = new PainterGameWorld(); +}; + +function Vehicle() { + this.numberOfWheels = 0; + this.brand = ""; +} + +Vehicle.prototype.what = function() { + return "nrOfWheels = " + this.numberOfWheels + ", brand = " + this.brand; +}; + +function Car(brand) { + Vehicle.call(this); + this.numberOfWeels = 4; + this.brand = brand; + this.convertible = false; +} +Car.prototype = Object.create(Vehicle.prototype); + +function Motorcycle(brand) { + Vehicle.call(this); + this.numberOfWheels = 2; + this.brand = brand; + this.cylinders = 4; +} +Motorcycle.prototype = Object.create(Vehicle.prototype); \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/PainterGameWorld.js b/11_OrganizingGameObjects/Painter9/PainterGameWorld.js new file mode 100644 index 0000000..bc6a2e9 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/PainterGameWorld.js @@ -0,0 +1,64 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450, Color.red); + this.can2 = new PaintCan(575, Color.green); + this.can3 = new PaintCan(700, Color.blue); + + this.lives = 5; +} + +PainterGameWorld.prototype.handleInput = function (delta) { + if (this.lives > 0) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); + } + else { + if (Mouse.left.pressed || Touch.pressed) + this.reset(); + } +}; + +PainterGameWorld.prototype.update = function (delta) { + if (this.lives > 0) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); + } +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); + for (var i = 0; i < this.lives; i++) { + Canvas2D.drawImage(sprites.lives, new Vector2(i * sprites.lives.width + 15, 60)); + } + if (this.lives <= 0) + Canvas2D.drawImage(sprites.gameover, + new Vector2(Game.size.x - sprites.gameover.width, + Game.size.y - sprites.gameover.height).divideBy(2)); +}; + +PainterGameWorld.prototype.reset = function () { + this.score = 0; + this.lives = 5; + this.cannon.reset(); + this.ball.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/ThreeColorGameObject.js b/11_OrganizingGameObjects/Painter9/ThreeColorGameObject.js new file mode 100644 index 0000000..9e0fa92 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/ThreeColorGameObject.js @@ -0,0 +1,71 @@ +"use strict"; + +function ThreeColorGameObject(sprColorRed, sprColorGreen, sprColorBlue) { + this.colorRed = sprColorRed; + this.colorGreen = sprColorGreen; + this.colorBlue = sprColorBlue; + this.currentColor = this.colorRed; + this.velocity = Vector2.zero; + this.position = Vector2.zero; + this.origin = Vector2.zero; + this.rotation = 0; + this.visible = true; +} + +Object.defineProperty(ThreeColorGameObject.prototype, "color", + { + get: function () { + if (this.currentColor === this.colorRed) + return Color.red; + else if (this.currentColor === this.colorGreen) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = this.colorRed; + else if (value === Color.green) + this.currentColor = this.colorGreen; + else if (value === Color.blue) + this.currentColor = this.colorBlue; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +ThreeColorGameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +ThreeColorGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, 1, this.origin); +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/game-layout.css b/11_OrganizingGameObjects/Painter9/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/geom/Vector2.js b/11_OrganizingGameObjects/Painter9/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/input/Keyboard.js b/11_OrganizingGameObjects/Painter9/input/Keyboard.js new file mode 100644 index 0000000..62a1e63 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/input/Keyboard.js @@ -0,0 +1,21 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.down = function (key) { + return this.keyDown === key; +}; + +var Keyboard = new Keyboard_Singleton(); \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/input/Mouse.js b/11_OrganizingGameObjects/Painter9/input/Mouse.js new file mode 100644 index 0000000..779d85f --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = new Vector2(); + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/system/Color.js b/11_OrganizingGameObjects/Painter9/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/11_OrganizingGameObjects/Painter9/system/Keys.js b/11_OrganizingGameObjects/Painter9/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/11_OrganizingGameObjects/Painter9/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/12_FinishingGame/LAB.min.js b/12_FinishingGame/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/12_FinishingGame/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Ball.js b/12_FinishingGame/PainterFinal/Ball.js new file mode 100644 index 0000000..66fa100 --- /dev/null +++ b/12_FinishingGame/PainterFinal/Ball.js @@ -0,0 +1,36 @@ +"use strict"; + +function Ball() { + ThreeColorGameObject.call(this, sprites.ball_red, sprites.ball_green, sprites.ball_blue); + this.shooting = false; + this.reset(); +} + +Ball.prototype = Object.create(ThreeColorGameObject.prototype); + +Ball.prototype.handleInput = function (delta) { + if (Mouse.leftPressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + sounds.shoot_paint.play(); + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + ThreeColorGameObject.prototype.update.call(this, delta); + } + else { + this.color = Game.gameWorld.cannon.color; + this.position = Game.gameWorld.cannon.ballPosition.subtractFrom(this.center); + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = new Vector2(65, 390); + this.shooting = false; +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Cannon.js b/12_FinishingGame/PainterFinal/Cannon.js new file mode 100644 index 0000000..0f7d168 --- /dev/null +++ b/12_FinishingGame/PainterFinal/Cannon.js @@ -0,0 +1,43 @@ +"use strict"; + +function Cannon() { + ThreeColorGameObject.call(this, sprites.cannon_red, sprites.cannon_green, sprites.cannon_blue); + this.position = new Vector2(72, 405); + this.origin = new Vector2(34, 34); +} + +Cannon.prototype = Object.create(ThreeColorGameObject.prototype); + +Object.defineProperty(Cannon.prototype, "ballPosition", + { + get: function () { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); + } + }); + + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Keyboard.keyDown === Keys.R) + this.color = Color.red; + else if (Keyboard.keyDown === Keys.G) + this.color = Color.green; + else if (Keyboard.keyDown === Keys.B) + this.color = Color.blue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.draw = function () { + if (!this.visible) + return; + var colorPosition = this.position.subtract(this.size.divideBy(2)); + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, 1, this.origin); + Canvas2D.drawImage(this.currentColor, colorPosition); +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Canvas2D.js b/12_FinishingGame/PainterFinal/Canvas2D.js new file mode 100644 index 0000000..b895453 --- /dev/null +++ b/12_FinishingGame/PainterFinal/Canvas2D.js @@ -0,0 +1,56 @@ +"use strict"; + +function Canvas2D_Singleton() { + console.log("Creating Canvas2D object"); + this._canvas = null; + this._canvasContext = null; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this._canvas = document.getElementById(canvasName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + } +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this._canvasContext.save(); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x * scale, -origin.y * scale, + sprite.width, sprite.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, color, textAlign, fontname, fontsize) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Game.js b/12_FinishingGame/PainterFinal/Game.js new file mode 100644 index 0000000..ee748a3 --- /dev/null +++ b/12_FinishingGame/PainterFinal/Game.js @@ -0,0 +1,76 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + console.log("Creating game"); + this._totalTime = 0; + this.size = null; + this._spritesStillLoading = 0; + this.gameWorld = null; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this.size = new Vector2(x, y); + + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); + console.log("'"); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + Mouse.reset(); + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/12_FinishingGame/PainterFinal/PaintCan.js b/12_FinishingGame/PainterFinal/PaintCan.js new file mode 100644 index 0000000..b953a47 --- /dev/null +++ b/12_FinishingGame/PainterFinal/PaintCan.js @@ -0,0 +1,65 @@ +"use strict"; + +function PaintCan(positionOffset, targetColor) { + ThreeColorGameObject.call(this, sprites.can_red, sprites.can_green, sprites.can_blue); + this.positionOffset = positionOffset; + this.targetColor = targetColor; + this.reset(); +} + +PaintCan.prototype = Object.create(ThreeColorGameObject.prototype); + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position = new Vector2(this.positionOffset, -200); + this.velocity = Vector2.zero; +}; + +PaintCan.prototype.update = function (delta) { + ThreeColorGameObject.prototype.update.call(this, delta); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.color = this.calculateRandomColor(); + } + + // calculate the distance between this can and the ball + var ball_center = Game.gameWorld.ball.center; + var ball_position = Game.gameWorld.ball.position; + var distance = ball_position.add(ball_center).subtractFrom(this.position).subtractFrom(this.center); + + if (Math.abs(distance.x) < this.center.x && Math.abs(distance.y) < this.center.y) { + this.color = Game.gameWorld.ball.color; + Game.gameWorld.ball.reset(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) { + if (this.color === this.targetColor) { + Game.gameWorld.score += 10; + sounds.collect_points.play(); + } + else + Game.gameWorld.lives -= 1; + this.moveToTop(); + } + this.minVelocity += 0.01; + this.rotation = Math.sin(this.position.y / 50) * 0.05; +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return Color.red; + else if (randomval == 1) + return Color.green; + else + return Color.blue; +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Painter.html b/12_FinishingGame/PainterFinal/Painter.html new file mode 100644 index 0000000..ba04531 --- /dev/null +++ b/12_FinishingGame/PainterFinal/Painter.html @@ -0,0 +1,36 @@ + + + + + Painter + + + + + +
+ +
+ + \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Painter.js b/12_FinishingGame/PainterFinal/Painter.js new file mode 100644 index 0000000..77de59a --- /dev/null +++ b/12_FinishingGame/PainterFinal/Painter.js @@ -0,0 +1,42 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/Painter/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.scorebar = loadSprite("spr_scorebar.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + sprites.gameover = loadSprite("spr_gameover_click.png"); + + sounds.music = loadSound("snd_music"); + sounds.collect_points = loadSound("snd_collect_points"); + sounds.shoot_paint = loadSound("snd_shoot_paint"); +}; + +Game.initialize = function () { + // sound + sounds.music.volume = 0.3; + sounds.music.play(); + + // create the game world + Game.gameWorld = new PainterGameWorld(); +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/PainterGameWorld.js b/12_FinishingGame/PainterFinal/PainterGameWorld.js new file mode 100644 index 0000000..9047bad --- /dev/null +++ b/12_FinishingGame/PainterFinal/PainterGameWorld.js @@ -0,0 +1,67 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450, Color.red); + this.can2 = new PaintCan(575, Color.green); + this.can3 = new PaintCan(700, Color.blue); + + this.score = 0; + this.lives = 5; +} + +PainterGameWorld.prototype.handleInput = function (delta) { + if (this.lives > 0) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); + } + else { + if (Mouse.leftPressed) + this.reset(); + } +}; + +PainterGameWorld.prototype.update = function (delta) { + if (this.lives > 0) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); + } +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background); + Canvas2D.drawImage(sprites.scorebar, new Vector2(10, 10)); + Canvas2D.drawText("Score: " + this.score, new Vector2(20, 22), Color.white); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); + for (var i = 0; i < this.lives; i++) { + Canvas2D.drawImage(sprites.lives, new Vector2(i * sprites.lives.width + 15, 60)); + } + if (this.lives <= 0) + Canvas2D.drawImage(sprites.gameover, + new Vector2(Game.size.x - sprites.gameover.width, + Game.size.y - sprites.gameover.height).divideBy(2)); +}; + +PainterGameWorld.prototype.reset = function () { + this.score = 0; + this.lives = 5; + this.cannon.reset(); + this.ball.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/Sound.js b/12_FinishingGame/PainterFinal/Sound.js new file mode 100644 index 0000000..5d9671b --- /dev/null +++ b/12_FinishingGame/PainterFinal/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/ThreeColorGameObject.js b/12_FinishingGame/PainterFinal/ThreeColorGameObject.js new file mode 100644 index 0000000..1a397a2 --- /dev/null +++ b/12_FinishingGame/PainterFinal/ThreeColorGameObject.js @@ -0,0 +1,71 @@ +"use strict"; + +function ThreeColorGameObject(spr_color_red, spr_color_green, spr_color_blue) { + this.colorRed = spr_color_red; + this.colorGreen = spr_color_green; + this.colorBlue = spr_color_blue; + this.currentColor = this.colorRed; + this.velocity = Vector2.zero; + this.position = Vector2.zero; + this.origin = Vector2.zero; + this.rotation = 0; + this.visible = true; +} + +Object.defineProperty(ThreeColorGameObject.prototype, "color", + { + get: function () { + if (this.currentColor === this.colorRed) + return Color.red; + else if (this.currentColor === this.colorGreen) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = this.colorRed; + else if (value === Color.green) + this.currentColor = this.colorGreen; + else if (value === Color.blue) + this.currentColor = this.colorBlue; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +ThreeColorGameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +ThreeColorGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, 1, this.origin); +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/game-layout.css b/12_FinishingGame/PainterFinal/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/12_FinishingGame/PainterFinal/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/geom/Vector2.js b/12_FinishingGame/PainterFinal/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/12_FinishingGame/PainterFinal/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/input/Keyboard.js b/12_FinishingGame/PainterFinal/input/Keyboard.js new file mode 100644 index 0000000..62a1e63 --- /dev/null +++ b/12_FinishingGame/PainterFinal/input/Keyboard.js @@ -0,0 +1,21 @@ +"use strict"; + +function handleKeyDown(evt) { + Keyboard.keyDown = evt.keyCode; +} + +function handleKeyUp(evt) { + Keyboard.keyDown = -1; +} + +function Keyboard_Singleton() { + this.keyDown = -1; + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.down = function (key) { + return this.keyDown === key; +}; + +var Keyboard = new Keyboard_Singleton(); \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/input/Mouse.js b/12_FinishingGame/PainterFinal/input/Mouse.js new file mode 100644 index 0000000..779d85f --- /dev/null +++ b/12_FinishingGame/PainterFinal/input/Mouse.js @@ -0,0 +1,33 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse.position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + if (evt.which === 1) { + if (!Mouse.leftDown) + Mouse.leftPressed = true; + Mouse.leftDown = true; + } +} + +function handleMouseUp(evt) { + if (evt.which === 1) + Mouse.leftDown = false; +} + +function Mouse_Singleton() { + this.position = new Vector2(); + this.leftDown = false; + this.leftPressed = false; + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Mouse_Singleton.prototype.reset = function () { + this.leftPressed = false; +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/system/Color.js b/12_FinishingGame/PainterFinal/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/12_FinishingGame/PainterFinal/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/12_FinishingGame/PainterFinal/system/Keys.js b/12_FinishingGame/PainterFinal/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/12_FinishingGame/PainterFinal/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/Canvas2D.js b/13_AdaptingDevices/JewelJam1/Canvas2D.js new file mode 100644 index 0000000..cfc68bd --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/Canvas2D.js @@ -0,0 +1,117 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x * scale, -origin.y * scale, + sprite.width * scale, sprite.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/13_AdaptingDevices/JewelJam1/Game.js b/13_AdaptingDevices/JewelJam1/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/JewelJam.html b/13_AdaptingDevices/JewelJam1/JewelJam.html new file mode 100644 index 0000000..5ba6d5a --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/JewelJam.html @@ -0,0 +1,40 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/JewelJam.js b/13_AdaptingDevices/JewelJam1/JewelJam.js new file mode 100644 index 0000000..a77253d --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/JewelJam.js @@ -0,0 +1,34 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/JewelJam/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.jewel = loadSprite("spr_single_jewel1.png"); +}; + +Game.initialize = function () { + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/13_AdaptingDevices/JewelJam1/JewelJamGameWorld.js b/13_AdaptingDevices/JewelJam1/JewelJamGameWorld.js new file mode 100644 index 0000000..12368e9 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/JewelJamGameWorld.js @@ -0,0 +1,17 @@ +"use strict"; + +function JewelJamGameWorld(layer, id) { + +} + +JewelJamGameWorld.prototype.handleInput = function (delta) { + +}; + +JewelJamGameWorld.prototype.update = function (delta) { + +}; + +JewelJamGameWorld.prototype.draw = function (delta) { + Canvas2D.drawImage(sprites.background); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/Sound.js b/13_AdaptingDevices/JewelJam1/Sound.js new file mode 100644 index 0000000..3782425 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/game-layout.css b/13_AdaptingDevices/JewelJam1/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/geom/Rectangle.js b/13_AdaptingDevices/JewelJam1/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/geom/Vector2.js b/13_AdaptingDevices/JewelJam1/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/input/ButtonState.js b/13_AdaptingDevices/JewelJam1/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/input/Keyboard.js b/13_AdaptingDevices/JewelJam1/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/13_AdaptingDevices/JewelJam1/input/Mouse.js b/13_AdaptingDevices/JewelJam1/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/13_AdaptingDevices/JewelJam1/input/Touch.js b/13_AdaptingDevices/JewelJam1/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/system/Color.js b/13_AdaptingDevices/JewelJam1/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/13_AdaptingDevices/JewelJam1/system/Keys.js b/13_AdaptingDevices/JewelJam1/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/13_AdaptingDevices/JewelJam1/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/13_AdaptingDevices/LAB.min.js b/13_AdaptingDevices/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/13_AdaptingDevices/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Ball.js b/13_AdaptingDevices/PainterWithTouch/Ball.js new file mode 100644 index 0000000..1207b11 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Ball.js @@ -0,0 +1,58 @@ +"use strict"; + +function Ball() { + ThreeColorGameObject.call(this, sprites.ball_red, sprites.ball_green, sprites.ball_blue); + this.shooting = false; + this.readyToShoot = false; + this.lastTouchPosition = Vector2.zero; + this.reset(); +} + +Ball.prototype = Object.create(ThreeColorGameObject.prototype); + +Ball.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +Ball.prototype.handleInputMouse = function (delta) { + if (Mouse.left.pressed && !this.shooting) { + this.shooting = true; + this.velocity = Mouse.position.subtract(this.position).multiplyWith(1.2); + sounds.shoot_paint.play(); + } +}; + +Ball.prototype.handleInputTouch = function (delta) { + if (Touch.isTouching && !Touch.containsTouch(Game.gameWorld.cannon.colorSelectRectangle)) { + this.lastTouchPosition = Touch.getPosition(0).copy(); + this.readyToShoot = true; + } + if (!Touch.isTouching && this.readyToShoot && !this.shooting) { + this.shooting = true; + this.readyToShoot = false; + this.velocity = this.lastTouchPosition.subtract(this.position).multiplyWith(1.2); + sounds.shoot_paint.play(); + } +}; + +Ball.prototype.update = function (delta) { + if (this.shooting) { + this.velocity.x *= 0.99; + this.velocity.y += 6; + ThreeColorGameObject.prototype.update.call(this, delta); + } + else { + this.color = Game.gameWorld.cannon.color; + this.position = Game.gameWorld.cannon.ballPosition.subtractFrom(this.center); + } + if (Game.gameWorld.isOutsideWorld(this.position)) + this.reset(); +}; + +Ball.prototype.reset = function () { + this.position = new Vector2(65, 390); + this.shooting = false; +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Cannon.js b/13_AdaptingDevices/PainterWithTouch/Cannon.js new file mode 100644 index 0000000..1217787 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Cannon.js @@ -0,0 +1,75 @@ +"use strict"; + +function Cannon() { + ThreeColorGameObject.call(this, sprites.cannon_red, sprites.cannon_green, sprites.cannon_blue); + this.position = new Vector2(72, 405); + this.origin = new Vector2(34, 34); +} + +Cannon.prototype = Object.create(ThreeColorGameObject.prototype); + +Object.defineProperty(Cannon.prototype, "colorSelectRectangle", + { + get: function () { + return new Rectangle(this.position.x - this.origin.x, this.position.y - this.origin.y, + sprites.cannon_barrel.height, sprites.cannon_barrel.height); + } + }); + +Object.defineProperty(Cannon.prototype, "ballPosition", + { + get: function () { + var opposite = Math.sin(this.rotation) * sprites.cannon_barrel.width * 0.6; + var adjacent = Math.cos(this.rotation) * sprites.cannon_barrel.width * 0.6; + return new Vector2(this.position.x + adjacent, this.position.y + opposite); + } + }); + + +Cannon.prototype.reset = function () { + this.position = new Vector2(72, 405); +}; + +Cannon.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +Cannon.prototype.handleInputMouse = function (delta) { + if (Keyboard.pressed(Keys.R)) + this.currentColor = this.colorRed; + else if (Keyboard.pressed(Keys.G)) + this.currentColor = this.colorGreen; + else if (Keyboard.pressed(Keys.B)) + this.currentColor = this.colorBlue; + var opposite = Mouse.position.y - this.position.y; + var adjacent = Mouse.position.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); +}; + +Cannon.prototype.handleInputTouch = function (delta) { + var rect = this.colorSelectRectangle; + if (Touch.containsTouchPress(rect)) { + if (this.currentColor === this.colorRed) + this.currentColor = this.colorGreen; + else if (this.currentColor === this.colorGreen) + this.currentColor = this.colorBlue; + else + this.currentColor = this.colorRed; + } else if (Touch.isTouching && !Touch.containsTouch(rect)) { + var touchPos = Touch.getPosition(0); + var opposite = touchPos.y - this.position.y; + var adjacent = touchPos.x - this.position.x; + this.rotation = Math.atan2(opposite, adjacent); + } +}; + +Cannon.prototype.draw = function () { + if (!this.visible) + return; + var colorPosition = this.position.subtract(this.size.divideBy(2)); + Canvas2D.drawImage(sprites.cannon_barrel, this.position, this.rotation, 1, this.origin); + Canvas2D.drawImage(this.currentColor, colorPosition); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Canvas2D.js b/13_AdaptingDevices/PainterWithTouch/Canvas2D.js new file mode 100644 index 0000000..b895453 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Canvas2D.js @@ -0,0 +1,56 @@ +"use strict"; + +function Canvas2D_Singleton() { + console.log("Creating Canvas2D object"); + this._canvas = null; + this._canvasContext = null; +} + +Canvas2D_Singleton.prototype.initialize = function (canvasName) { + this._canvas = document.getElementById(canvasName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + } +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + + this._canvasContext.save(); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, 0, 0, + sprite.width, sprite.height, + -origin.x * scale, -origin.y * scale, + sprite.width, sprite.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, color, textAlign, fontname, fontsize) { + position = typeof position !== 'undefined' ? position : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Game.js b/13_AdaptingDevices/PainterWithTouch/Game.js new file mode 100644 index 0000000..2790258 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Game.js @@ -0,0 +1,87 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + console.log("Creating game"); + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this.gameWorld = null; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Game_Singleton.prototype.start = function (canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + if (!this._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); + diff --git a/13_AdaptingDevices/PainterWithTouch/PaintCan.js b/13_AdaptingDevices/PainterWithTouch/PaintCan.js new file mode 100644 index 0000000..b953a47 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/PaintCan.js @@ -0,0 +1,65 @@ +"use strict"; + +function PaintCan(positionOffset, targetColor) { + ThreeColorGameObject.call(this, sprites.can_red, sprites.can_green, sprites.can_blue); + this.positionOffset = positionOffset; + this.targetColor = targetColor; + this.reset(); +} + +PaintCan.prototype = Object.create(ThreeColorGameObject.prototype); + +PaintCan.prototype.reset = function () { + this.moveToTop(); + this.minVelocity = 30; +}; + +PaintCan.prototype.moveToTop = function () { + this.position = new Vector2(this.positionOffset, -200); + this.velocity = Vector2.zero; +}; + +PaintCan.prototype.update = function (delta) { + ThreeColorGameObject.prototype.update.call(this, delta); + + if (this.velocity.y === 0 && Math.random() < 0.01) { + this.velocity = this.calculateRandomVelocity(); + this.color = this.calculateRandomColor(); + } + + // calculate the distance between this can and the ball + var ball_center = Game.gameWorld.ball.center; + var ball_position = Game.gameWorld.ball.position; + var distance = ball_position.add(ball_center).subtractFrom(this.position).subtractFrom(this.center); + + if (Math.abs(distance.x) < this.center.x && Math.abs(distance.y) < this.center.y) { + this.color = Game.gameWorld.ball.color; + Game.gameWorld.ball.reset(); + } + + if (Game.gameWorld.isOutsideWorld(this.position)) { + if (this.color === this.targetColor) { + Game.gameWorld.score += 10; + sounds.collect_points.play(); + } + else + Game.gameWorld.lives -= 1; + this.moveToTop(); + } + this.minVelocity += 0.01; + this.rotation = Math.sin(this.position.y / 50) * 0.05; +}; + +PaintCan.prototype.calculateRandomVelocity = function () { + return new Vector2(0, Math.random() * 30 + this.minVelocity); +}; + +PaintCan.prototype.calculateRandomColor = function () { + var randomval = Math.floor(Math.random() * 3); + if (randomval == 0) + return Color.red; + else if (randomval == 1) + return Color.green; + else + return Color.blue; +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Painter.html b/13_AdaptingDevices/PainterWithTouch/Painter.html new file mode 100644 index 0000000..95bf53e --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Painter.html @@ -0,0 +1,39 @@ + + + + + Painter + + + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Painter.js b/13_AdaptingDevices/PainterWithTouch/Painter.js new file mode 100644 index 0000000..b386308 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Painter.js @@ -0,0 +1,45 @@ +"use strict"; + +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/Painter/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/Painter/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.scorebar = loadSprite("spr_scorebar.jpg"); + sprites.cannon_barrel = loadSprite("spr_cannon_barrel.png"); + sprites.cannon_red = loadSprite("spr_cannon_red.png"); + sprites.cannon_green = loadSprite("spr_cannon_green.png"); + sprites.cannon_blue = loadSprite("spr_cannon_blue.png"); + sprites.ball_red = loadSprite("spr_ball_red.png"); + sprites.ball_green = loadSprite("spr_ball_green.png"); + sprites.ball_blue = loadSprite("spr_ball_blue.png"); + sprites.can_red = loadSprite("spr_can_red.png"); + sprites.can_green = loadSprite("spr_can_green.png"); + sprites.can_blue = loadSprite("spr_can_blue.png"); + sprites.lives = loadSprite("spr_lives.png"); + if (Touch.isTouchDevice) + sprites.gameover = loadSprite("spr_gameover_tap.png"); + else + sprites.gameover = loadSprite("spr_gameover_click.png"); + + sounds.music = loadSound("snd_music"); + sounds.collect_points = loadSound("snd_collect_points"); + sounds.shoot_paint = loadSound("snd_shoot_paint"); +}; + +Game.initialize = function () { + // sound + sounds.music.volume = 0.3; + sounds.music.play(); + + // create the game world + Game.gameWorld = new PainterGameWorld(); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/PainterGameWorld.js b/13_AdaptingDevices/PainterWithTouch/PainterGameWorld.js new file mode 100644 index 0000000..cfcc3c3 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/PainterGameWorld.js @@ -0,0 +1,67 @@ +"use strict"; + +function PainterGameWorld() { + this.cannon = new Cannon(); + this.ball = new Ball(); + + this.can1 = new PaintCan(450, Color.red); + this.can2 = new PaintCan(575, Color.green); + this.can3 = new PaintCan(700, Color.blue); + + this.score = 0; + this.lives = 5; +} + +PainterGameWorld.prototype.handleInput = function (delta) { + if (this.lives > 0) { + this.ball.handleInput(delta); + this.cannon.handleInput(delta); + } + else { + if (Mouse.left.pressed || Touch.isPressing) + this.reset(); + } +}; + +PainterGameWorld.prototype.update = function (delta) { + if (this.lives > 0) { + this.ball.update(delta); + this.cannon.update(delta); + this.can1.update(delta); + this.can2.update(delta); + this.can3.update(delta); + } +}; + +PainterGameWorld.prototype.draw = function () { + Canvas2D.drawImage(sprites.background); + Canvas2D.drawImage(sprites.scorebar, new Vector2(10, 10)); + Canvas2D.drawText("Score: " + this.score, new Vector2(20, 22), Color.white); + + this.ball.draw(); + this.cannon.draw(); + this.can1.draw(); + this.can2.draw(); + this.can3.draw(); + for (var i = 0; i < this.lives; i++) { + Canvas2D.drawImage(sprites.lives, new Vector2(i * sprites.lives.width + 15, 60)); + } + if (this.lives <= 0) + Canvas2D.drawImage(sprites.gameover, + new Vector2(Game.size.x - sprites.gameover.width, + Game.size.y - sprites.gameover.height).divideBy(2)); +}; + +PainterGameWorld.prototype.reset = function () { + this.score = 0; + this.lives = 5; + this.cannon.reset(); + this.ball.reset(); + this.can1.reset(); + this.can2.reset(); + this.can3.reset(); +}; + +PainterGameWorld.prototype.isOutsideWorld = function (position) { + return position.x < 0 || position.x > Game.size.x || position.y > Game.size.y; +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/Sound.js b/13_AdaptingDevices/PainterWithTouch/Sound.js new file mode 100644 index 0000000..468570a --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/Sound.js @@ -0,0 +1,36 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + console.log("Setting volume"); + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/ThreeColorGameObject.js b/13_AdaptingDevices/PainterWithTouch/ThreeColorGameObject.js new file mode 100644 index 0000000..1a397a2 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/ThreeColorGameObject.js @@ -0,0 +1,71 @@ +"use strict"; + +function ThreeColorGameObject(spr_color_red, spr_color_green, spr_color_blue) { + this.colorRed = spr_color_red; + this.colorGreen = spr_color_green; + this.colorBlue = spr_color_blue; + this.currentColor = this.colorRed; + this.velocity = Vector2.zero; + this.position = Vector2.zero; + this.origin = Vector2.zero; + this.rotation = 0; + this.visible = true; +} + +Object.defineProperty(ThreeColorGameObject.prototype, "color", + { + get: function () { + if (this.currentColor === this.colorRed) + return Color.red; + else if (this.currentColor === this.colorGreen) + return Color.green; + else + return Color.blue; + }, + set: function (value) { + if (value === Color.red) + this.currentColor = this.colorRed; + else if (value === Color.green) + this.currentColor = this.colorGreen; + else if (value === Color.blue) + this.currentColor = this.colorBlue; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "width", + { + get: function () { + return this.currentColor.width; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "height", + { + get: function () { + return this.currentColor.height; + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.currentColor.width, this.currentColor.height); + } + }); + +Object.defineProperty(ThreeColorGameObject.prototype, "center", + { + get: function () { + return new Vector2(this.currentColor.width / 2, this.currentColor.height / 2); + } + }); + +ThreeColorGameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +ThreeColorGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.currentColor, this.position, this.rotation, 1, this.origin); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/game-layout.css b/13_AdaptingDevices/PainterWithTouch/game-layout.css new file mode 100644 index 0000000..b8e98c2 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/game-layout.css @@ -0,0 +1,3 @@ +html, body { + margin: 0; +} \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/geom/Rectangle.js b/13_AdaptingDevices/PainterWithTouch/geom/Rectangle.js new file mode 100644 index 0000000..424d189 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/geom/Rectangle.js @@ -0,0 +1,63 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/geom/Vector2.js b/13_AdaptingDevices/PainterWithTouch/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/input/ButtonState.js b/13_AdaptingDevices/PainterWithTouch/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/input/Keyboard.js b/13_AdaptingDevices/PainterWithTouch/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/13_AdaptingDevices/PainterWithTouch/input/Mouse.js b/13_AdaptingDevices/PainterWithTouch/input/Mouse.js new file mode 100644 index 0000000..b751735 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/input/Mouse.js @@ -0,0 +1,88 @@ +"use strict"; + +function handleMouseMove(evt) { + Mouse._position = new Vector2(evt.pageX, evt.pageY); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/13_AdaptingDevices/PainterWithTouch/input/Touch.js b/13_AdaptingDevices/PainterWithTouch/input/Touch.js new file mode 100644 index 0000000..cb8a2d5 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/input/Touch.js @@ -0,0 +1,122 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var mx = this._touches[index].pageX; + var my = this._touches[index].pageY; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/system/Color.js b/13_AdaptingDevices/PainterWithTouch/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/13_AdaptingDevices/PainterWithTouch/system/Keys.js b/13_AdaptingDevices/PainterWithTouch/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/13_AdaptingDevices/PainterWithTouch/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/Canvas2D.js b/14_GameObjectStructure/JewelJam2/Canvas2D.js new file mode 100644 index 0000000..f9ed84b --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/Canvas2D.js @@ -0,0 +1,118 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x * scale, -origin.y * scale, + sourceRect.width * scale, sourceRect.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/14_GameObjectStructure/JewelJam2/Game.js b/14_GameObjectStructure/JewelJam2/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/JewelJam.html b/14_GameObjectStructure/JewelJam2/JewelJam.html new file mode 100644 index 0000000..583144d --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/JewelJam.html @@ -0,0 +1,40 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/JewelJam.js b/14_GameObjectStructure/JewelJam2/JewelJam.js new file mode 100644 index 0000000..df54f85 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/JewelJam.js @@ -0,0 +1,30 @@ +"use strict"; + +var sprites = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + sprites.background = loadSprite("spr_background.jpg"); + sprites.single_jewel1 = loadSprite("spr_single_jewel1.png"); + sprites.single_jewel2 = loadSprite("spr_single_jewel2.png"); + sprites.single_jewel3 = loadSprite("spr_single_jewel3.png"); +}; + +Game.initialize = function () { + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/14_GameObjectStructure/JewelJam2/JewelJamGameWorld.js b/14_GameObjectStructure/JewelJam2/JewelJamGameWorld.js new file mode 100644 index 0000000..09ec94f --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/JewelJamGameWorld.js @@ -0,0 +1,52 @@ +"use strict"; + +function JewelJamGameWorld() { + + this.rows = 10; + this.columns = 5; + this.grid = new Array(this.rows * this.columns); + for (var i = 0; i < this.rows * this.columns; i++) { + var randomval = Math.floor(Math.random() * 3) + 1; + this.grid[i] = sprites["single_jewel" + randomval]; + } +} + +JewelJamGameWorld.prototype.setGridValue = function (x, y, value) { + var index = y * this.columns + x; + this.grid[index] = value; +}; + +JewelJamGameWorld.prototype.getGridValue = function (x, y) { + var index = y * this.columns + x; + return this.grid[index]; +}; + +JewelJamGameWorld.prototype.moveRowDown = function () { + for (var y = this.rows - 2; y >= 0; y--) { + for (var x = 0; x < this.columns; x++) { + this.setGridValue(x, y + 1, this.getGridValue(x, y)); + } + } + for (x = 0; x < this.columns; x++) { + var randomval = Math.floor(Math.random() * 3) + 1; + this.setGridValue(x, 0, sprites["single_jewel" + randomval]); + } +}; + +JewelJamGameWorld.prototype.handleInput = function (delta) { + if (Keyboard.pressed(Keys.space)) + this.moveRowDown(); +}; + +JewelJamGameWorld.prototype.update = function (delta) { +}; + +JewelJamGameWorld.prototype.draw = function (delta) { + Canvas2D.drawImage(sprites.background); + for (var row = 0; row < this.rows; row++) { + for (var col = 0; col < this.columns; col++) { + var position = new Vector2(85 + col * 85, 150 + row * 85); + Canvas2D.drawImage(this.getGridValue(col, row), position); + } + } +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/Sound.js b/14_GameObjectStructure/JewelJam2/Sound.js new file mode 100644 index 0000000..e4b7afe --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function(value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/game-layout.css b/14_GameObjectStructure/JewelJam2/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/geom/Rectangle.js b/14_GameObjectStructure/JewelJam2/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/geom/Vector2.js b/14_GameObjectStructure/JewelJam2/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/input/ButtonState.js b/14_GameObjectStructure/JewelJam2/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/input/Keyboard.js b/14_GameObjectStructure/JewelJam2/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/14_GameObjectStructure/JewelJam2/input/Mouse.js b/14_GameObjectStructure/JewelJam2/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/14_GameObjectStructure/JewelJam2/input/Touch.js b/14_GameObjectStructure/JewelJam2/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/system/Color.js b/14_GameObjectStructure/JewelJam2/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam2/system/Keys.js b/14_GameObjectStructure/JewelJam2/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/14_GameObjectStructure/JewelJam2/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/Canvas2D.js b/14_GameObjectStructure/JewelJam3/Canvas2D.js new file mode 100644 index 0000000..f9ed84b --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/Canvas2D.js @@ -0,0 +1,118 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x * scale, -origin.y * scale, + sourceRect.width * scale, sourceRect.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/14_GameObjectStructure/JewelJam3/Game.js b/14_GameObjectStructure/JewelJam3/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/JewelJam.html b/14_GameObjectStructure/JewelJam3/JewelJam.html new file mode 100644 index 0000000..0c11cbc --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/JewelJam.html @@ -0,0 +1,46 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/JewelJam.js b/14_GameObjectStructure/JewelJam3/JewelJam.js new file mode 100644 index 0000000..62df32f --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/JewelJam.js @@ -0,0 +1,42 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/JewelJam/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.single_jewel1 = loadSprite("spr_single_jewel1.png"); + sprites.single_jewel2 = loadSprite("spr_single_jewel2.png"); + sprites.single_jewel3 = loadSprite("spr_single_jewel3.png"); +}; + +Game.initialize = function () { + // define the layers + ID.layer_background = 1; + ID.layer_objects = 20; + + // create the game world + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/14_GameObjectStructure/JewelJam3/Sound.js b/14_GameObjectStructure/JewelJam3/Sound.js new file mode 100644 index 0000000..e4b7afe --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function(value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/game-layout.css b/14_GameObjectStructure/JewelJam3/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/GameObject.js b/14_GameObjectStructure/JewelJam3/gameobjects/GameObject.js new file mode 100644 index 0000000..cd0abfc --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/GameObject.js @@ -0,0 +1,57 @@ +"use strict"; + +function GameObject(layer) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; + +GameObject.prototype.reset = function () { + this._visible = true; +}; diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectGrid.js b/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..87e9f3f --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectGrid.js @@ -0,0 +1,57 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer) { + GameObjectList.call(this, layer); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; + +GameObjectGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] === gameobject) { + var row = Math.floor(i / this.columns); + var col = i % this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectList.js b/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectList.js new file mode 100644 index 0000000..74047c3 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/GameObjectList.js @@ -0,0 +1,68 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer) { + GameObject.call(this, layer); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + if (!this.visible) + return; + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/Jewel.js b/14_GameObjectStructure/JewelJam3/gameobjects/Jewel.js new file mode 100644 index 0000000..a07b90c --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/Jewel.js @@ -0,0 +1,18 @@ +"use strict"; + +function Jewel(layer) { + var randomval = Math.floor(Math.random() * 3) + 1; + var spr = sprites["single_jewel" + randomval]; + SpriteGameObject.call(this, spr, layer); +} + +Jewel.prototype = Object.create(SpriteGameObject.prototype); + +Jewel.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + + if (this.parent.dragging) + return; + var anchor = this.parent.getAnchorPosition(this); + this.velocity = anchor.subtractFrom(this.position).multiplyWith(15); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/JewelGrid.js b/14_GameObjectStructure/JewelJam3/gameobjects/JewelGrid.js new file mode 100644 index 0000000..77c8d64 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/JewelGrid.js @@ -0,0 +1,105 @@ +"use strict"; + +function JewelGrid(rows, columns, layer) { + GameObjectGrid.call(this, rows, columns, layer); + this.dragging = false; + this._dragRow = 0; + this._draggingLastX = 0; + this._touchIndex = 0; +} + +JewelGrid.prototype = Object.create(GameObjectGrid.prototype); + +JewelGrid.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +JewelGrid.prototype.handleInputMouse = function (delta) { + if (Mouse.left.down && !this.dragging) { + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + if (Mouse.containsMouseDown(rect)) { + this.dragging = true; + this._dragRow = Math.floor((Mouse.position.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = Mouse.position.x - this.worldPosition.x; + } + } + if (!Mouse.left.down) { + this.dragging = false; + } + + if (this.dragging) { + var newpos = Mouse.position.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.handleInputTouch = function (delta) { + var pos, newpos; + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + + if (Touch.isTouching && !this.dragging) { + if (Touch.containsTouch(rect)) { + this._touchIndex = Touch.getIndexInRect(rect); + pos = Touch.getPosition(this._touchIndex); + this.dragging = true; + this._dragRow = Math.floor((pos.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = pos.x - this.worldPosition.x; + } + } + if (!Touch.isTouching) { + this.dragging = false; + } + + if (this.dragging) { + pos = Touch.getPosition(this._touchIndex); + newpos = pos.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.shiftRowRight = function (selectedRow) { + var lastObj = this.at(this._columns - 1, selectedRow); + var positionOffset = lastObj.position.x - (this.columns - 1) * this.cellWidth; + for (var x = this._columns - 1; x > 0; x -= 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + (x - 1)]; + this._gameObjects[selectedRow * this._columns] = lastObj; + lastObj.position = new Vector2(-this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.shiftRowLeft = function (selectedRow) { + var firstObj = this.at(0, selectedRow); + var positionOffset = firstObj.position.x; + for (var x = 0; x < this._columns - 1; x += 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + x + 1]; + this._gameObjects[selectedRow * this._columns + (this._columns - 1)] = firstObj; + firstObj.position = new Vector2(this._columns * this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/JewelJamGameWorld.js b/14_GameObjectStructure/JewelJam3/gameobjects/JewelJamGameWorld.js new file mode 100644 index 0000000..9c3c27d --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/JewelJamGameWorld.js @@ -0,0 +1,19 @@ +"use strict"; + +function JewelJamGameWorld(layer) { + GameObjectList.call(this, layer); + + this.add(new SpriteGameObject(sprites.background, ID.layer_background)); + + var rows = 10, columns = 5; + var grid = new JewelGrid(rows, columns, ID.layer_objects); + grid.position = new Vector2(85, 150); + grid.cellWidth = 85; + grid.cellHeight = 85; + this.add(grid); + for (var i = 0; i < rows * columns; i++) { + grid.add(new Jewel()); + } +} + +JewelJamGameWorld.prototype = Object.create(GameObjectList.prototype); \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/gameobjects/SpriteGameObject.js b/14_GameObjectStructure/JewelJam3/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..dc4c523 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/gameobjects/SpriteGameObject.js @@ -0,0 +1,36 @@ +"use strict"; + +function SpriteGameObject(sprite, layer) { + GameObject.call(this, layer); + this.sprite = sprite; + this.origin = Vector2.zero; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +SpriteGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/geom/Rectangle.js b/14_GameObjectStructure/JewelJam3/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/geom/Vector2.js b/14_GameObjectStructure/JewelJam3/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/input/ButtonState.js b/14_GameObjectStructure/JewelJam3/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/input/Keyboard.js b/14_GameObjectStructure/JewelJam3/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/14_GameObjectStructure/JewelJam3/input/Mouse.js b/14_GameObjectStructure/JewelJam3/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/14_GameObjectStructure/JewelJam3/input/Touch.js b/14_GameObjectStructure/JewelJam3/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/system/Color.js b/14_GameObjectStructure/JewelJam3/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/14_GameObjectStructure/JewelJam3/system/Keys.js b/14_GameObjectStructure/JewelJam3/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/14_GameObjectStructure/JewelJam3/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/14_GameObjectStructure/LAB.min.js b/14_GameObjectStructure/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/14_GameObjectStructure/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/Canvas2D.js b/15_GameplayProgramming/JewelJam4/Canvas2D.js new file mode 100644 index 0000000..f9ed84b --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/Canvas2D.js @@ -0,0 +1,118 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x * scale, -origin.y * scale, + sourceRect.width * scale, sourceRect.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/15_GameplayProgramming/JewelJam4/Game.js b/15_GameplayProgramming/JewelJam4/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/JewelJam.html b/15_GameplayProgramming/JewelJam4/JewelJam.html new file mode 100644 index 0000000..d9537ff --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/JewelJam.html @@ -0,0 +1,49 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/JewelJam.js b/15_GameplayProgramming/JewelJam4/JewelJam.js new file mode 100644 index 0000000..6a16735 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/JewelJam.js @@ -0,0 +1,71 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/JewelJam/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.frame_score = loadSprite("spr_frame_score.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.frame_help = loadSprite("spr_frame_help.png"); + sprites.jewelcart = loadSprite("spr_jewelcart.png"); + sprites.jewels = loadSprite("spr_jewels@27.png"); + if (Touch.isTouchDevice) { + sprites.gameover = loadSprite("spr_gameover_tap.png"); + sprites.title = loadSprite("spr_title_tap.jpg"); + } else { + sprites.gameover = loadSprite("spr_gameover_click.png"); + sprites.title = loadSprite("spr_title_click.jpg"); + } +}; + +Game.initialize = function () { + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.title = 1; + ID.help_frame = 2; + ID.jewel_cart = 3; + ID.grid = 4; + ID.game_over = 5; + ID.score = 6; + ID.double_timer = 7; + ID.triple_timer = 8; + + // create the game world + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/15_GameplayProgramming/JewelJam4/Sound.js b/15_GameplayProgramming/JewelJam4/Sound.js new file mode 100644 index 0000000..e4b7afe --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function(value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/game-layout.css b/15_GameplayProgramming/JewelJam4/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/GameObject.js b/15_GameplayProgramming/JewelJam4/gameobjects/GameObject.js new file mode 100644 index 0000000..c211ee3 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/GameObject.js @@ -0,0 +1,59 @@ +"use strict"; + +function GameObject(layer, id) { + + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectGrid.js b/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..e79c4ed --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectGrid.js @@ -0,0 +1,46 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer, id) { + GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectList.js b/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectList.js new file mode 100644 index 0000000..c595ced --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/GameObjectList.js @@ -0,0 +1,81 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + if (!this.visible) + return; + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/Jewel.js b/15_GameplayProgramming/JewelJam4/gameobjects/Jewel.js new file mode 100644 index 0000000..40e5a88 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/Jewel.js @@ -0,0 +1,25 @@ +"use strict"; + +function Jewel(layer, id) { + SpriteGameObject.call(this, sprites.jewels, layer, id); + this.variation = Math.floor(Math.random() * 27); +} + +Jewel.prototype = Object.create(SpriteGameObject.prototype); + +Jewel.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + + if (this.parent.dragging) + return; + var anchor = this.parent.getAnchorPosition(this); + this.velocity = anchor.subtractFrom(this.position).multiplyWith(15); +}; + +Jewel.prototype.draw = function () { + if (!this.visible) + return; + var imagePart = new Rectangle(this.variation * this.height, 0, + this.height, this.height); + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin, imagePart); +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/JewelCart.js b/15_GameplayProgramming/JewelJam4/gameobjects/JewelCart.js new file mode 100644 index 0000000..693596e --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/JewelCart.js @@ -0,0 +1,24 @@ +"use strict"; + +function JewelCart(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + this.push = 50; + this.minxpos = 0; + this.velocity = new Vector2(10, 0); +} + +JewelCart.prototype = Object.create(SpriteGameObject.prototype); + +JewelCart.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + this.push = Math.max(this.push - 0.00001, 1); +}; + +JewelCart.prototype.reset = function () { + SpriteGameObject.prototype.reset.call(this); + this.position.x = this.minxpos; +}; + +JewelCart.prototype.pushCart = function () { + this.position.x = Math.max(this.position.x - this.push, this.minxpos); +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/JewelGrid.js b/15_GameplayProgramming/JewelJam4/gameobjects/JewelGrid.js new file mode 100644 index 0000000..930c117 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/JewelGrid.js @@ -0,0 +1,174 @@ +"use strict"; + +function JewelGrid(rows, columns, layer, id) { + GameObjectGrid.call(this, rows, columns, layer, id); + this.dragging = false; + this._dragRow = 0; + this._draggingLastX = 0; + this._touchIndex = 0; +} + +JewelGrid.prototype = Object.create(GameObjectGrid.prototype); + +JewelGrid.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +JewelGrid.prototype.handleInputMouse = function (delta) { + if (Mouse.left.down && !this.dragging) { + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + if (Mouse.containsMouseDown(rect)) { + this.dragging = true; + this._dragRow = Math.floor((Mouse.position.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = Mouse.position.x - this.worldPosition.x; + } + } + if (!Mouse.left.down) { + this.dragging = false; + } + + if (this.dragging) { + var newpos = Mouse.position.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.handleInputTouch = function (delta) { + var pos, newpos; + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + + if (Touch.isTouching && !this.dragging) { + if (Touch.containsTouch(rect)) { + this._touchIndex = Touch.getIndexInRect(rect); + pos = Touch.getPosition(this._touchIndex); + this.dragging = true; + this._dragRow = Math.floor((pos.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = pos.x - this.worldPosition.x; + } + } + if (!Touch.isTouching) { + this.dragging = false; + } + + if (this.dragging) { + pos = Touch.getPosition(this._touchIndex); + newpos = pos.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + if (this.dragging) + return; + + var middleCol = Math.floor(this._columns / 2); + var i = 0; + var score = this.root.find(ID.score); + while (i < this._rows - 2) { + if (this.isValidCombination(this.at(middleCol, i), + this.at(middleCol, i + 1), + this.at(middleCol, i + 2))) { + this.removeJewel(middleCol, i, -this.cellHeight); + this.removeJewel(middleCol, i + 1, -this.cellHeight * 2); + this.removeJewel(middleCol, i + 2, -this.cellHeight * 3); + score.score += 10; + + var jewelCart = this.root.find(ID.jewel_cart); + jewelCart.pushCart(); + + i = 0; + } + else + i++; + } +}; + +JewelGrid.prototype.isValidCombination = function (a, b, c) { + var curra = a.variation; + var currb = b.variation; + var currc = c.variation; + var divider = 9; + + for (var i = 0; i < 3; i += 1) { + if ((Math.floor(curra / divider) + Math.floor(currb / divider) + Math.floor(currc / divider)) % 3 !== 0) + return false; + curra = curra % divider; + currb = currb % divider; + currc = currc % divider; + divider = Math.floor(divider / 3); + } + return true; +}; + +JewelGrid.prototype.reset = function () { + this.clear(); + for (var i = 0; i < this._rows * this._columns; i += 1) + this.add(new Jewel(ID.layer_objects)); +}; + +JewelGrid.prototype.removeJewel = function (x, y, newYPosition) { + for (var row = y; row > 0; row -= 1) + this._gameObjects[row * this._columns + x] = this._gameObjects[(row - 1) * this._columns + x]; + var jewel = new Jewel(); + this.addAt(jewel, x, 0); + jewel.position.y = newYPosition; +}; + +JewelGrid.prototype.shiftRowRight = function (selectedRow) { + var lastObj = this.at(this._columns - 1, selectedRow); + var positionOffset = lastObj.position.x - (this.columns - 1) * this.cellWidth; + for (var x = this._columns - 1; x > 0; x -= 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + (x - 1)]; + this._gameObjects[selectedRow * this._columns] = lastObj; + lastObj.position = new Vector2(-this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.shiftRowLeft = function (selectedRow) { + var firstObj = this.at(0, selectedRow); + var positionOffset = firstObj.position.x; + for (var x = 0; x < this._columns - 1; x += 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + x + 1]; + this._gameObjects[selectedRow * this._columns + (this._columns - 1)] = firstObj; + firstObj.position = new Vector2(this._columns * this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/JewelJamGameWorld.js b/15_GameplayProgramming/JewelJam4/gameobjects/JewelJamGameWorld.js new file mode 100644 index 0000000..f9276ab --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/JewelJamGameWorld.js @@ -0,0 +1,35 @@ +"use strict"; + +function JewelJamGameWorld(layer, id) { + GameObjectList.call(this, layer, id); + + // add a background sprite + this.add(new SpriteGameObject(sprites.background, ID.layer_background)); + + // add a score frame + var scoreFrame = new SpriteGameObject(sprites.frame_score, ID.layer_overlays); + scoreFrame.position = new Vector2(20, 20); + this.add(scoreFrame); + + var score = new ScoreGameObject("Segoe UI Mono", "40px", ID.layer_overlays_1, ID.score); + score.position = new Vector2(270, 35); + score.color = Color.white; + + this.add(score); + + var jewelCart = new JewelCart(sprites.jewelcart, ID.layer_objects, ID.jewel_cart); + jewelCart.position = new Vector2(410, 230); + jewelCart.minxpos = 410; + this.add(jewelCart); + + // add the grid + var rows = 10, columns = 5; + var grid = new JewelGrid(rows, columns, ID.layer_objects, ID.grid); + grid.position = new Vector2(85, 150); + grid.cellWidth = 85; + grid.cellHeight = 85; + grid.reset(); + this.add(grid); +} + +JewelJamGameWorld.prototype = Object.create(GameObjectList.prototype); \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/ScoreGameObject.js b/15_GameplayProgramming/JewelJam4/gameobjects/ScoreGameObject.js new file mode 100644 index 0000000..a543568 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/ScoreGameObject.js @@ -0,0 +1,24 @@ +"use strict"; + +function ScoreGameObject(fontName, fontSize, layer, id) { + Label.call(this, fontName, fontSize, layer, id); + this.text = 0; + this._align = "right"; +} + +ScoreGameObject.prototype = Object.create(Label.prototype); + +Object.defineProperty(ScoreGameObject.prototype, "score", + { + get: function () { + return this._contents; + }, + set: function (value) { + if (value >= 0) + this.text = value; + } + }); + +ScoreGameObject.prototype.reset = function () { + this.text = 0; +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gameobjects/SpriteGameObject.js b/15_GameplayProgramming/JewelJam4/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..60d9b69 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gameobjects/SpriteGameObject.js @@ -0,0 +1,36 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + this.sprite = sprite; + this.origin = Vector2.zero; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +SpriteGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin); +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/geom/Rectangle.js b/15_GameplayProgramming/JewelJam4/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/geom/Vector2.js b/15_GameplayProgramming/JewelJam4/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/gui/Label.js b/15_GameplayProgramming/JewelJam4/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/15_GameplayProgramming/JewelJam4/input/ButtonState.js b/15_GameplayProgramming/JewelJam4/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/input/Keyboard.js b/15_GameplayProgramming/JewelJam4/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/15_GameplayProgramming/JewelJam4/input/Mouse.js b/15_GameplayProgramming/JewelJam4/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/15_GameplayProgramming/JewelJam4/input/Touch.js b/15_GameplayProgramming/JewelJam4/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/system/Color.js b/15_GameplayProgramming/JewelJam4/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/15_GameplayProgramming/JewelJam4/system/Keys.js b/15_GameplayProgramming/JewelJam4/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/15_GameplayProgramming/JewelJam4/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/15_GameplayProgramming/LAB.min.js b/15_GameplayProgramming/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/15_GameplayProgramming/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/16_GameStates/JewelJam5/Canvas2D.js b/16_GameStates/JewelJam5/Canvas2D.js new file mode 100644 index 0000000..f9ed84b --- /dev/null +++ b/16_GameStates/JewelJam5/Canvas2D.js @@ -0,0 +1,118 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x * scale, -origin.y * scale, + sourceRect.width * scale, sourceRect.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/16_GameStates/JewelJam5/Game.js b/16_GameStates/JewelJam5/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/16_GameStates/JewelJam5/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/16_GameStates/JewelJam5/JewelJam.html b/16_GameStates/JewelJam5/JewelJam.html new file mode 100644 index 0000000..f09f3b1 --- /dev/null +++ b/16_GameStates/JewelJam5/JewelJam.html @@ -0,0 +1,50 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/16_GameStates/JewelJam5/JewelJam.js b/16_GameStates/JewelJam5/JewelJam.js new file mode 100644 index 0000000..07a4965 --- /dev/null +++ b/16_GameStates/JewelJam5/JewelJam.js @@ -0,0 +1,75 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/JewelJam/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.frame_score = loadSprite("spr_frame_score.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.frame_help = loadSprite("spr_frame_help.png"); + sprites.jewelcart = loadSprite("spr_jewelcart.png"); + sprites.jewels = loadSprite("spr_jewels@27.png"); + if (Touch.isTouchDevice) { + sprites.gameover = loadSprite("spr_gameover_tap.png"); + sprites.title = loadSprite("spr_title_tap.jpg"); + } else { + sprites.gameover = loadSprite("spr_gameover_click.png"); + sprites.title = loadSprite("spr_title_click.jpg"); + } + sprites.frame_selector = loadSprite("spr_frame_selector.png"); + sprites.glitter = loadSprite("spr_glitter.png"); + sprites.double = loadSprite("spr_double.png"); + sprites.triple = loadSprite("spr_triple.png"); +}; + +Game.initialize = function () { + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.title = 1; + ID.help_frame = 2; + ID.jewel_cart = 3; + ID.grid = 4; + ID.game_over = 5; + ID.score = 6; + ID.double_timer = 7; + ID.triple_timer = 8; + + // create the game world + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/16_GameStates/JewelJam5/Sound.js b/16_GameStates/JewelJam5/Sound.js new file mode 100644 index 0000000..e4b7afe --- /dev/null +++ b/16_GameStates/JewelJam5/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function(value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/game-layout.css b/16_GameStates/JewelJam5/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/16_GameStates/JewelJam5/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/GameObject.js b/16_GameStates/JewelJam5/gameobjects/GameObject.js new file mode 100644 index 0000000..589d431 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/GameObject.js @@ -0,0 +1,61 @@ +"use strict"; + +function GameObject(layer, id) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + if (typeof this.position === 'undefined') { + console.log("oops..."); + } + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/GameObjectGrid.js b/16_GameStates/JewelJam5/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..e79c4ed --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/GameObjectGrid.js @@ -0,0 +1,46 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer, id) { + GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; diff --git a/16_GameStates/JewelJam5/gameobjects/GameObjectList.js b/16_GameStates/JewelJam5/gameobjects/GameObjectList.js new file mode 100644 index 0000000..c595ced --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/GameObjectList.js @@ -0,0 +1,81 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + if (!this.visible) + return; + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; diff --git a/16_GameStates/JewelJam5/gameobjects/Jewel.js b/16_GameStates/JewelJam5/gameobjects/Jewel.js new file mode 100644 index 0000000..40e5a88 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/Jewel.js @@ -0,0 +1,25 @@ +"use strict"; + +function Jewel(layer, id) { + SpriteGameObject.call(this, sprites.jewels, layer, id); + this.variation = Math.floor(Math.random() * 27); +} + +Jewel.prototype = Object.create(SpriteGameObject.prototype); + +Jewel.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + + if (this.parent.dragging) + return; + var anchor = this.parent.getAnchorPosition(this); + this.velocity = anchor.subtractFrom(this.position).multiplyWith(15); +}; + +Jewel.prototype.draw = function () { + if (!this.visible) + return; + var imagePart = new Rectangle(this.variation * this.height, 0, + this.height, this.height); + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin, imagePart); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/JewelCart.js b/16_GameStates/JewelJam5/gameobjects/JewelCart.js new file mode 100644 index 0000000..693596e --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/JewelCart.js @@ -0,0 +1,24 @@ +"use strict"; + +function JewelCart(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + this.push = 50; + this.minxpos = 0; + this.velocity = new Vector2(10, 0); +} + +JewelCart.prototype = Object.create(SpriteGameObject.prototype); + +JewelCart.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + this.push = Math.max(this.push - 0.00001, 1); +}; + +JewelCart.prototype.reset = function () { + SpriteGameObject.prototype.reset.call(this); + this.position.x = this.minxpos; +}; + +JewelCart.prototype.pushCart = function () { + this.position.x = Math.max(this.position.x - this.push, this.minxpos); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/JewelGrid.js b/16_GameStates/JewelJam5/gameobjects/JewelGrid.js new file mode 100644 index 0000000..930c117 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/JewelGrid.js @@ -0,0 +1,174 @@ +"use strict"; + +function JewelGrid(rows, columns, layer, id) { + GameObjectGrid.call(this, rows, columns, layer, id); + this.dragging = false; + this._dragRow = 0; + this._draggingLastX = 0; + this._touchIndex = 0; +} + +JewelGrid.prototype = Object.create(GameObjectGrid.prototype); + +JewelGrid.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +JewelGrid.prototype.handleInputMouse = function (delta) { + if (Mouse.left.down && !this.dragging) { + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + if (Mouse.containsMouseDown(rect)) { + this.dragging = true; + this._dragRow = Math.floor((Mouse.position.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = Mouse.position.x - this.worldPosition.x; + } + } + if (!Mouse.left.down) { + this.dragging = false; + } + + if (this.dragging) { + var newpos = Mouse.position.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.handleInputTouch = function (delta) { + var pos, newpos; + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + + if (Touch.isTouching && !this.dragging) { + if (Touch.containsTouch(rect)) { + this._touchIndex = Touch.getIndexInRect(rect); + pos = Touch.getPosition(this._touchIndex); + this.dragging = true; + this._dragRow = Math.floor((pos.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = pos.x - this.worldPosition.x; + } + } + if (!Touch.isTouching) { + this.dragging = false; + } + + if (this.dragging) { + pos = Touch.getPosition(this._touchIndex); + newpos = pos.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + if (this.dragging) + return; + + var middleCol = Math.floor(this._columns / 2); + var i = 0; + var score = this.root.find(ID.score); + while (i < this._rows - 2) { + if (this.isValidCombination(this.at(middleCol, i), + this.at(middleCol, i + 1), + this.at(middleCol, i + 2))) { + this.removeJewel(middleCol, i, -this.cellHeight); + this.removeJewel(middleCol, i + 1, -this.cellHeight * 2); + this.removeJewel(middleCol, i + 2, -this.cellHeight * 3); + score.score += 10; + + var jewelCart = this.root.find(ID.jewel_cart); + jewelCart.pushCart(); + + i = 0; + } + else + i++; + } +}; + +JewelGrid.prototype.isValidCombination = function (a, b, c) { + var curra = a.variation; + var currb = b.variation; + var currc = c.variation; + var divider = 9; + + for (var i = 0; i < 3; i += 1) { + if ((Math.floor(curra / divider) + Math.floor(currb / divider) + Math.floor(currc / divider)) % 3 !== 0) + return false; + curra = curra % divider; + currb = currb % divider; + currc = currc % divider; + divider = Math.floor(divider / 3); + } + return true; +}; + +JewelGrid.prototype.reset = function () { + this.clear(); + for (var i = 0; i < this._rows * this._columns; i += 1) + this.add(new Jewel(ID.layer_objects)); +}; + +JewelGrid.prototype.removeJewel = function (x, y, newYPosition) { + for (var row = y; row > 0; row -= 1) + this._gameObjects[row * this._columns + x] = this._gameObjects[(row - 1) * this._columns + x]; + var jewel = new Jewel(); + this.addAt(jewel, x, 0); + jewel.position.y = newYPosition; +}; + +JewelGrid.prototype.shiftRowRight = function (selectedRow) { + var lastObj = this.at(this._columns - 1, selectedRow); + var positionOffset = lastObj.position.x - (this.columns - 1) * this.cellWidth; + for (var x = this._columns - 1; x > 0; x -= 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + (x - 1)]; + this._gameObjects[selectedRow * this._columns] = lastObj; + lastObj.position = new Vector2(-this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.shiftRowLeft = function (selectedRow) { + var firstObj = this.at(0, selectedRow); + var positionOffset = firstObj.position.x; + for (var x = 0; x < this._columns - 1; x += 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + x + 1]; + this._gameObjects[selectedRow * this._columns + (this._columns - 1)] = firstObj; + firstObj.position = new Vector2(this._columns * this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/JewelJamGameWorld.js b/16_GameStates/JewelJam5/gameobjects/JewelJamGameWorld.js new file mode 100644 index 0000000..62659a5 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/JewelJamGameWorld.js @@ -0,0 +1,114 @@ +"use strict"; + +function JewelJamGameWorld(layer, id) { + GameObjectList.call(this, layer, id); + + // the title screen + var titleScreen = new SpriteGameObject(sprites.title, ID.layer_overlays_2, ID.title); + this.add(titleScreen); + + // add a background sprite + var background = new SpriteGameObject(sprites.background, ID.layer_background); + this.add(background); + + // add a score frame + var scoreFrame = new SpriteGameObject(sprites.frame_score, ID.layer_overlays); + scoreFrame.position = new Vector2(20, 20); + this.add(scoreFrame); + + var score = new ScoreGameObject("Segoe UI Mono", "40px", ID.layer_overlays_1, ID.score); + score.position = new Vector2(270, 35); + score.color = Color.white; + + this.add(score); + + this.helpButton = new Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new Vector2(1268, 20); + this.add(this.helpButton); + + var helpFrame = new SpriteGameObject(sprites.frame_help, ID.layer_overlays, ID.help_frame); + helpFrame.position = new Vector2(636, 120); + helpFrame.visible = false; + this.add(helpFrame); + + var jewelCart = new JewelCart(sprites.jewelcart, ID.layer_objects, ID.jewel_cart); + jewelCart.position = new Vector2(410, 230); + jewelCart.minxpos = 410; + this.add(jewelCart); + + // add the grid + var rows = 10, columns = 5; + var grid = new JewelGrid(rows, columns, ID.layer_objects, ID.grid); + grid.position = new Vector2(85, 150); + grid.cellWidth = 85; + grid.cellHeight = 85; + grid.reset(); + this.add(grid); + + // game over overlay + var gameOver = new SpriteGameObject(sprites.gameover, ID.layer_overlays_1, ID.game_over); + gameOver.visible = false; + gameOver.position = gameOver.screenCenter; + this.add(gameOver); +} + +JewelJamGameWorld.prototype = Object.create(GameObjectList.prototype); + +JewelJamGameWorld.prototype.handleInput = function (delta) { + // title screen + var titleScreen = this.root.find(ID.title); + if (titleScreen.visible) { + if (Mouse.left.pressed || Touch.isPressing) + titleScreen.visible = false; + return; + } + + // game over + if (this.gameOver()) { + if (Mouse.left.pressed || Touch.isPressing) + this.reset(); + return; + } + + // playing + GameObjectList.prototype.handleInput.call(this, delta); + var helpFrame = this.root.find(ID.help_frame); + if (this.helpButton.pressed || (helpFrame.visible && (Mouse.left.pressed || Touch.isPressing))) { + helpFrame.visible = !helpFrame.visible; + } +}; + +JewelJamGameWorld.prototype.update = function (delta) { + // title screen + var titleScreen = this.root.find(ID.title); + if (titleScreen.visible) + return; + + // game over + var gameOver = this.root.find(ID.game_over); + if (this.gameOver()) { + gameOver.visible = true; + } + + // playing + var helpFrame = this.root.find(ID.help_frame); + var grid = this.root.find(ID.grid); + grid.visible = !helpFrame.visible; + if (!helpFrame.visible) + GameObjectList.prototype.update.call(this, delta); +}; + +JewelJamGameWorld.prototype.gameOver = function () { + var jewelCart = this.root.find(ID.jewel_cart); + return jewelCart.position.x > Game.size.x; +}; + +JewelJamGameWorld.prototype.reset = function () { + GameObjectList.prototype.reset.call(this); + var gameOver = this.root.find(ID.game_over); + gameOver.visible = false; + var titleScreen = this.root.find(ID.title); + titleScreen.visible = false; + var helpFrame = this.root.find(ID.help_frame); + helpFrame.visible = false; +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/ScoreGameObject.js b/16_GameStates/JewelJam5/gameobjects/ScoreGameObject.js new file mode 100644 index 0000000..a543568 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/ScoreGameObject.js @@ -0,0 +1,24 @@ +"use strict"; + +function ScoreGameObject(fontName, fontSize, layer, id) { + Label.call(this, fontName, fontSize, layer, id); + this.text = 0; + this._align = "right"; +} + +ScoreGameObject.prototype = Object.create(Label.prototype); + +Object.defineProperty(ScoreGameObject.prototype, "score", + { + get: function () { + return this._contents; + }, + set: function (value) { + if (value >= 0) + this.text = value; + } + }); + +ScoreGameObject.prototype.reset = function () { + this.text = 0; +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gameobjects/SpriteGameObject.js b/16_GameStates/JewelJam5/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..f4971d2 --- /dev/null +++ b/16_GameStates/JewelJam5/gameobjects/SpriteGameObject.js @@ -0,0 +1,65 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + this.sprite = sprite; + this.origin = Vector2.zero; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/geom/Rectangle.js b/16_GameStates/JewelJam5/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/16_GameStates/JewelJam5/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/geom/Vector2.js b/16_GameStates/JewelJam5/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/16_GameStates/JewelJam5/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gui/Button.js b/16_GameStates/JewelJam5/gui/Button.js new file mode 100644 index 0000000..9182296 --- /dev/null +++ b/16_GameStates/JewelJam5/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/gui/Label.js b/16_GameStates/JewelJam5/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/16_GameStates/JewelJam5/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/16_GameStates/JewelJam5/input/ButtonState.js b/16_GameStates/JewelJam5/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/16_GameStates/JewelJam5/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/16_GameStates/JewelJam5/input/Keyboard.js b/16_GameStates/JewelJam5/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/16_GameStates/JewelJam5/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/16_GameStates/JewelJam5/input/Mouse.js b/16_GameStates/JewelJam5/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/16_GameStates/JewelJam5/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/16_GameStates/JewelJam5/input/Touch.js b/16_GameStates/JewelJam5/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/16_GameStates/JewelJam5/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/16_GameStates/JewelJam5/system/Color.js b/16_GameStates/JewelJam5/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/16_GameStates/JewelJam5/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/16_GameStates/JewelJam5/system/Keys.js b/16_GameStates/JewelJam5/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/16_GameStates/JewelJam5/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/16_GameStates/LAB.min.js b/16_GameStates/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/16_GameStates/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/Canvas2D.js b/17_FinishingGame/JewelJamFinal/Canvas2D.js new file mode 100644 index 0000000..f9ed84b --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/Canvas2D.js @@ -0,0 +1,118 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + window.onresize = Canvas2D_Singleton.prototype.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x * scale, -origin.y * scale, + sourceRect.width * scale, sourceRect.height * scale); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); + diff --git a/17_FinishingGame/JewelJamFinal/Game.js b/17_FinishingGame/JewelJamFinal/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/JewelJam.html b/17_FinishingGame/JewelJamFinal/JewelJam.html new file mode 100644 index 0000000..2e22c19 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/JewelJam.html @@ -0,0 +1,53 @@ + + + + + + + Jewel Jam + + + + Jewel Jam + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/JewelJam.js b/17_FinishingGame/JewelJamFinal/JewelJam.js new file mode 100644 index 0000000..1ee9de6 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/JewelJam.js @@ -0,0 +1,84 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return Game.loadSprite("../../assets/JewelJam/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/JewelJam/sounds/" + sound, looping); + }; + + sprites.background = loadSprite("spr_background.jpg"); + sprites.frame_score = loadSprite("spr_frame_score.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.frame_help = loadSprite("spr_frame_help.png"); + sprites.jewelcart = loadSprite("spr_jewelcart.png"); + sprites.jewels = loadSprite("spr_jewels@27.png"); + if (Touch.isTouchDevice) { + sprites.gameover = loadSprite("spr_gameover_tap.png"); + sprites.title = loadSprite("spr_title_tap.jpg"); + } else { + sprites.gameover = loadSprite("spr_gameover_click.png"); + sprites.title = loadSprite("spr_title_click.jpg"); + } + sprites.frame_selector = loadSprite("spr_frame_selector.png"); + sprites.glitter = loadSprite("spr_glitter.png"); + sprites.double = loadSprite("spr_double.png"); + sprites.triple = loadSprite("spr_triple.png"); + + sounds.music = loadSound("snd_music", true); + sounds.combi = loadSound("snd_combi"); + sounds.double = loadSound("snd_double"); + sounds.triple = loadSound("snd_triple"); + sounds.gameover = loadSound("snd_gameover"); +}; + +Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.title = 1; + ID.help_frame = 2; + ID.jewel_cart = 3; + ID.grid = 4; + ID.game_over = 5; + ID.score = 6; + ID.double_timer = 7; + ID.triple_timer = 8; + + // create the game world + Game.gameWorld = new JewelJamGameWorld(); +}; + + + + + + + + + + + + + diff --git a/17_FinishingGame/JewelJamFinal/Math.js b/17_FinishingGame/JewelJamFinal/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/Sound.js b/17_FinishingGame/JewelJamFinal/Sound.js new file mode 100644 index 0000000..e4b7afe --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function(value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function() { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/game-layout.css b/17_FinishingGame/JewelJamFinal/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/GameObject.js b/17_FinishingGame/JewelJamFinal/gameobjects/GameObject.js new file mode 100644 index 0000000..09a33ff --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/GameObject.js @@ -0,0 +1,58 @@ +"use strict"; + +function GameObject(layer, id) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectGrid.js b/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..e79c4ed --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectGrid.js @@ -0,0 +1,46 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer, id) { + GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectList.js b/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectList.js new file mode 100644 index 0000000..c595ced --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/GameObjectList.js @@ -0,0 +1,81 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + if (!this.visible) + return; + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/GlitterField.js b/17_FinishingGame/JewelJamFinal/gameobjects/GlitterField.js new file mode 100644 index 0000000..8e3edd2 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/GlitterField.js @@ -0,0 +1,50 @@ +"use strict"; + +function GlitterField(density, width, height, layer, id) { + GameObject.call(this, layer, id); + this.positions = []; + this.scales = []; + this._scaleMax = 1; + this._scaleStep = 0.05; + this.width = width; + this.height = height; + + // fill it with random positions + for (var i = 0; i < density; i++) { + this.positions.push(this.createRandomPosition()); + this.scales.push(0); + } +} + +GlitterField.prototype = Object.create(GameObject.prototype); + +GlitterField.prototype.createRandomPosition = function () { + return new Vector2(Math.random() * this.width, Math.random() * this.height); +}; + +GlitterField.prototype.update = function (delta) { + for (var i = 0; i < this.scales.length; i += 1) { + if (this.scales[i] === 0 && Math.random() < 0.01) + this.scales[i] += this._scaleStep; + else if (this.scales[i] !== 0) { + this.scales[i] += this._scaleStep; + if (this.scales[i] >= this._scaleMax * 2) { + this.scales[i] = 0; + this.positions[i] = this.createRandomPosition(); + } + } + } +}; + +GlitterField.prototype.draw = function () { + if (!this.visible) + return; + var origin = new Vector2(sprites.glitter.width / 2, sprites.glitter.height / 2); + for (var i = 0; i < this.scales.length; i++) { + var scale = this.scales[i]; + if (this.scales[i] > this._scaleMax) + scale = this._scaleMax * 2 - this.scales[i]; + var pos = this.worldPosition.addTo(this.positions[i]); + Canvas2D.drawImage(sprites.glitter, pos, 0, scale, origin); + } +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/Jewel.js b/17_FinishingGame/JewelJamFinal/gameobjects/Jewel.js new file mode 100644 index 0000000..40e5a88 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/Jewel.js @@ -0,0 +1,25 @@ +"use strict"; + +function Jewel(layer, id) { + SpriteGameObject.call(this, sprites.jewels, layer, id); + this.variation = Math.floor(Math.random() * 27); +} + +Jewel.prototype = Object.create(SpriteGameObject.prototype); + +Jewel.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + + if (this.parent.dragging) + return; + var anchor = this.parent.getAnchorPosition(this); + this.velocity = anchor.subtractFrom(this.position).multiplyWith(15); +}; + +Jewel.prototype.draw = function () { + if (!this.visible) + return; + var imagePart = new Rectangle(this.variation * this.height, 0, + this.height, this.height); + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin, imagePart); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/JewelCart.js b/17_FinishingGame/JewelJamFinal/gameobjects/JewelCart.js new file mode 100644 index 0000000..f99a2f8 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/JewelCart.js @@ -0,0 +1,33 @@ +"use strict"; + +function JewelCart(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + this.push = 50; + this.minxpos = 0; + this.velocity = new Vector2(10, 0); + this.glitters = new GlitterField(2, 435, 75); + this.glitters.position = new Vector2(275, 475); + this.glitters.parent = this; +} + +JewelCart.prototype = Object.create(SpriteGameObject.prototype); + +JewelCart.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + this.push = Math.max(this.push - 0.00001, 1); + this.glitters.update(delta); +}; + +JewelCart.prototype.reset = function () { + SpriteGameObject.prototype.reset.call(this); + this.position.x = this.minxpos; +}; + +JewelCart.prototype.draw = function () { + SpriteGameObject.prototype.draw.call(this); + this.glitters.draw(); +}; + +JewelCart.prototype.pushCart = function () { + this.position.x = Math.max(this.position.x - this.push, this.minxpos); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/JewelGrid.js b/17_FinishingGame/JewelJamFinal/gameobjects/JewelGrid.js new file mode 100644 index 0000000..d7b9ef4 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/JewelGrid.js @@ -0,0 +1,191 @@ +"use strict"; + +function JewelGrid(rows, columns, layer, id) { + GameObjectGrid.call(this, rows, columns, layer, id); + this.dragging = false; + this._dragRow = 0; + this._draggingLastX = 0; + this._touchIndex = 0; +} + +JewelGrid.prototype = Object.create(GameObjectGrid.prototype); + +JewelGrid.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.handleInputTouch(delta); + else + this.handleInputMouse(delta); +}; + +JewelGrid.prototype.handleInputMouse = function (delta) { + if (Mouse.left.down && !this.dragging) { + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + if (Mouse.containsMouseDown(rect)) { + this.dragging = true; + this._dragRow = Math.floor((Mouse.position.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = Mouse.position.x - this.worldPosition.x; + } + } + if (!Mouse.left.down) { + this.dragging = false; + } + + if (this.dragging) { + var newpos = Mouse.position.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.handleInputTouch = function (delta) { + var pos, newpos; + var rect = new Rectangle(this.worldPosition.x, this.worldPosition.y, this.columns * this.cellHeight, this.rows * this.cellWidth); + + if (Touch.isTouching && !this.dragging) { + if (Touch.containsTouch(rect)) { + this._touchIndex = Touch.getIndexInRect(rect); + pos = Touch.getPosition(this._touchIndex); + this.dragging = true; + this._dragRow = Math.floor((pos.y - this.worldPosition.y) / this.cellHeight); + this._draggingLastX = pos.x - this.worldPosition.x; + } + } + if (!Touch.isTouching) { + this.dragging = false; + } + + if (this.dragging) { + pos = Touch.getPosition(this._touchIndex); + newpos = pos.x - this.worldPosition.x; + + // reposition each jewel in the row + for (var i = 0; i < this.columns; i++) { + var currObj = this.at(i, this._dragRow); + currObj.position.x += (newpos - this._draggingLastX); + } + // check if we need to shift a row + var firstObj = this.at(0, this._dragRow); + if (firstObj.position.x < -this.cellWidth / 2 && newpos - this._draggingLastX < 0) + this.shiftRowLeft(this._dragRow); + var lastObj = this.at(this.columns - 1, this._dragRow); + if (lastObj.position.x > (this.columns - 1) * this.cellWidth + this.cellWidth / 2 && newpos - this._draggingLastX > 0) + this.shiftRowRight(this._dragRow); + this._draggingLastX = newpos; + } +}; + +JewelGrid.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + if (this.dragging) + return; + + var middleCol = Math.floor(this._columns / 2); + var nrCombis = 0; + var i = 0; + var score = this.root.find(ID.score); + while (i < this._rows - 2) { + if (this.isValidCombination(this.at(middleCol, i), + this.at(middleCol, i + 1), + this.at(middleCol, i + 2))) { + this.removeJewel(middleCol, i, -this.cellHeight); + this.removeJewel(middleCol, i + 1, -this.cellHeight * 2); + this.removeJewel(middleCol, i + 2, -this.cellHeight * 3); + score.score += 10; + + var jewelCart = this.root.find(ID.jewel_cart); + jewelCart.pushCart(); + + nrCombis++; + i = 0; + } + else + i++; + } + if (nrCombis === 1) { + sounds.combi.play(); + } + else if (nrCombis === 2) { + score.score += 50; + var doubleTimer = this.root.find(ID.double_timer); + doubleTimer.startVisible(); + sounds.double.play(); + } + else if (nrCombis >= 3) { + score.score += 100; + var tripleTimer = this.root.find(ID.triple_timer); + tripleTimer.startVisible(); + sounds.triple.play(); + } +}; + +JewelGrid.prototype.isValidCombination = function (a, b, c) { + var curra = a.variation; + var currb = b.variation; + var currc = c.variation; + var divider = 9; + + for (var i = 0; i < 3; i += 1) { + if ((Math.floor(curra / divider) + Math.floor(currb / divider) + Math.floor(currc / divider)) % 3 !== 0) + return false; + curra = curra % divider; + currb = currb % divider; + currc = currc % divider; + divider = Math.floor(divider / 3); + } + return true; +}; + +JewelGrid.prototype.reset = function () { + this.clear(); + for (var i = 0; i < this._rows * this._columns; i += 1) + this.add(new Jewel(ID.layer_objects)); +}; + +JewelGrid.prototype.removeJewel = function (x, y, newYPosition) { + for (var row = y; row > 0; row -= 1) + this._gameObjects[row * this._columns + x] = this._gameObjects[(row - 1) * this._columns + x]; + var jewel = new Jewel(); + this.addAt(jewel, x, 0); + jewel.position.y = newYPosition; +}; + +JewelGrid.prototype.shiftRowRight = function (selectedRow) { + var lastObj = this.at(this._columns - 1, selectedRow); + var positionOffset = lastObj.position.x - (this.columns - 1) * this.cellWidth; + for (var x = this._columns - 1; x > 0; x -= 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + (x - 1)]; + this._gameObjects[selectedRow * this._columns] = lastObj; + lastObj.position = new Vector2(-this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.shiftRowLeft = function (selectedRow) { + var firstObj = this.at(0, selectedRow); + var positionOffset = firstObj.position.x; + for (var x = 0; x < this._columns - 1; x += 1) + this._gameObjects[selectedRow * this._columns + x] = this._gameObjects[selectedRow * this._columns + x + 1]; + this._gameObjects[selectedRow * this._columns + (this._columns - 1)] = firstObj; + firstObj.position = new Vector2(this._columns * this.cellWidth + positionOffset, selectedRow * this.cellHeight); +}; + +JewelGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/JewelJamGameWorld.js b/17_FinishingGame/JewelJamFinal/gameobjects/JewelJamGameWorld.js new file mode 100644 index 0000000..4e85376 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/JewelJamGameWorld.js @@ -0,0 +1,136 @@ +"use strict"; + +function JewelJamGameWorld(layer, id) { + GameObjectList.call(this, layer, id); + + // the title screen + var titleScreen = new SpriteGameObject(sprites.title, ID.layer_overlays_2, ID.title); + this.add(titleScreen); + + // add a background sprite + var background = new SpriteGameObject(sprites.background, ID.layer_background); + this.add(background); + + // add a score frame + var scoreFrame = new SpriteGameObject(sprites.frame_score, ID.layer_overlays); + scoreFrame.position = new Vector2(20, 20); + this.add(scoreFrame); + + var score = new ScoreGameObject("Segoe UI Mono", "40px", ID.layer_overlays_1, ID.score); + score.position = new Vector2(270, 35); + score.color = Color.white; + + this.add(score); + + this.helpButton = new Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new Vector2(1268, 20); + this.add(this.helpButton); + + var helpFrame = new SpriteGameObject(sprites.frame_help, ID.layer_overlays, ID.help_frame); + helpFrame.position = new Vector2(636, 120); + helpFrame.visible = false; + this.add(helpFrame); + + var jewelCart = new JewelCart(sprites.jewelcart, ID.layer_objects, ID.jewel_cart); + jewelCart.position = new Vector2(410, 230); + jewelCart.minxpos = 410; + this.add(jewelCart); + + // add the playing field + var playingField = new GameObjectList(ID.layer_objects); + playingField.position = new Vector2(85, 150); + this.add(playingField); + + // add the grid + var rows = 10, columns = 5; + var grid = new JewelGrid(rows, columns, ID.layer_objects, ID.grid); + grid.cellWidth = 85; + grid.cellHeight = 85; + grid.reset(); + playingField.add(grid); + + // add glitters + playingField.add(new GlitterField(2, columns * grid.cellWidth, rows * grid.cellHeight, ID.layer_overlays_1)); + + // game over overlay + var gameOver = new SpriteGameObject(sprites.gameover, ID.layer_overlays_1, ID.game_over); + gameOver.visible = false; + gameOver.position = gameOver.screenCenter; + this.add(gameOver); + + // double/triple overlays + var doubleOverlay = new SpriteGameObject(sprites.double, ID.layer_overlays); + doubleOverlay.position = new Vector2(800, 400); + var doubleTimer = new VisibilityTimer(doubleOverlay, ID.layer_overlays, ID.double_timer); + this.add(doubleOverlay); + this.add(doubleTimer); + + var tripleOverlay = new SpriteGameObject(sprites.triple, ID.layer_overlays); + tripleOverlay.position = new Vector2(800, 400); + var tripleTimer = new VisibilityTimer(tripleOverlay, ID.layer_overlays, ID.triple_timer); + this.add(tripleOverlay); + this.add(tripleTimer); +} + +JewelJamGameWorld.prototype = Object.create(GameObjectList.prototype); + +JewelJamGameWorld.prototype.handleInput = function (delta) { + // title screen + var titleScreen = this.root.find(ID.title); + if (titleScreen.visible) { + if (Mouse.left.pressed || Touch.isPressing) + titleScreen.visible = false; + return; + } + + // game over + if (this.gameOver()) { + if (Mouse.left.pressed || Touch.isPressing) + this.reset(); + return; + } + + // playing + GameObjectList.prototype.handleInput.call(this, delta); + var helpFrame = this.root.find(ID.help_frame); + if (this.helpButton.pressed || (helpFrame.visible && (Mouse.left.pressed || Touch.isPressing))) { + helpFrame.visible = !helpFrame.visible; + } +}; + +JewelJamGameWorld.prototype.update = function (delta) { + // title screen + var titleScreen = this.root.find(ID.title); + if (titleScreen.visible) + return; + + // game over + var gameOver = this.root.find(ID.game_over); + if (this.gameOver() && !gameOver.visible) { + gameOver.visible = true; + sounds.gameover.play(); + return; + } + + // playing + var helpFrame = this.root.find(ID.help_frame); + var grid = this.root.find(ID.grid); + grid.visible = !helpFrame.visible; + if (!helpFrame.visible) + GameObjectList.prototype.update.call(this, delta); +}; + +JewelJamGameWorld.prototype.gameOver = function () { + var jewelCart = this.root.find(ID.jewel_cart); + return jewelCart.position.x > Game.size.x; +}; + +JewelJamGameWorld.prototype.reset = function () { + GameObjectList.prototype.reset.call(this); + var gameOver = this.root.find(ID.game_over); + gameOver.visible = false; + var titleScreen = this.root.find(ID.title); + titleScreen.visible = false; + var helpFrame = this.root.find(ID.help_frame); + helpFrame.visible = false; +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/ScoreGameObject.js b/17_FinishingGame/JewelJamFinal/gameobjects/ScoreGameObject.js new file mode 100644 index 0000000..a543568 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/ScoreGameObject.js @@ -0,0 +1,24 @@ +"use strict"; + +function ScoreGameObject(fontName, fontSize, layer, id) { + Label.call(this, fontName, fontSize, layer, id); + this.text = 0; + this._align = "right"; +} + +ScoreGameObject.prototype = Object.create(Label.prototype); + +Object.defineProperty(ScoreGameObject.prototype, "score", + { + get: function () { + return this._contents; + }, + set: function (value) { + if (value >= 0) + this.text = value; + } + }); + +ScoreGameObject.prototype.reset = function () { + this.text = 0; +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/SpriteGameObject.js b/17_FinishingGame/JewelJamFinal/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..f4971d2 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/SpriteGameObject.js @@ -0,0 +1,65 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + this.sprite = sprite; + this.origin = Vector2.zero; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawImage(this.sprite, this.worldPosition, 0, 1, this.origin); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gameobjects/VisibilityTimer.js b/17_FinishingGame/JewelJamFinal/gameobjects/VisibilityTimer.js new file mode 100644 index 0000000..0c6974d --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gameobjects/VisibilityTimer.js @@ -0,0 +1,23 @@ +"use strict"; + +function VisibilityTimer(target, layer, id) { + GameObject.call(this, layer, id); + this._target = target; + this._timeLeft = 0; + this.totalTime = 1; +} + +VisibilityTimer.prototype = Object.create(GameObject.prototype); + +VisibilityTimer.prototype.update = function (delta) { + if (this._timeLeft > 0) { + this._timeLeft -= delta; + this._target.visible = true; + } else + this._target.visible = false; +}; + +VisibilityTimer.prototype.startVisible = function () { + this._timeLeft = this.totalTime; + this._target.visible = true; +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/geom/Rectangle.js b/17_FinishingGame/JewelJamFinal/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/geom/Vector2.js b/17_FinishingGame/JewelJamFinal/geom/Vector2.js new file mode 100644 index 0000000..5b19710 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor === Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor === Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor === Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor === Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor === Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor === Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor === Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor === Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gui/Button.js b/17_FinishingGame/JewelJamFinal/gui/Button.js new file mode 100644 index 0000000..9182296 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/gui/Label.js b/17_FinishingGame/JewelJamFinal/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/17_FinishingGame/JewelJamFinal/input/ButtonState.js b/17_FinishingGame/JewelJamFinal/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/input/Keyboard.js b/17_FinishingGame/JewelJamFinal/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/17_FinishingGame/JewelJamFinal/input/Mouse.js b/17_FinishingGame/JewelJamFinal/input/Mouse.js new file mode 100644 index 0000000..22bc378 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); diff --git a/17_FinishingGame/JewelJamFinal/input/Touch.js b/17_FinishingGame/JewelJamFinal/input/Touch.js new file mode 100644 index 0000000..fea1e09 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/input/Touch.js @@ -0,0 +1,124 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isPressing", + { + get: function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + if (this._touchPresses[i]) { + return true; + } + return false; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.isValid = function (index) { + return index >= 0 && index < this._touches.length; +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/system/Color.js b/17_FinishingGame/JewelJamFinal/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/17_FinishingGame/JewelJamFinal/system/Keys.js b/17_FinishingGame/JewelJamFinal/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/17_FinishingGame/JewelJamFinal/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/17_FinishingGame/LAB.min.js b/17_FinishingGame/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/17_FinishingGame/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/18_SpriteSheets/LAB.min.js b/18_SpriteSheets/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/18_SpriteSheets/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/Canvas2D.js b/18_SpriteSheets/PenguinPairs1/Canvas2D.js new file mode 100644 index 0000000..62d50c1 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/Canvas2D.js @@ -0,0 +1,138 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); diff --git a/18_SpriteSheets/PenguinPairs1/Game.js b/18_SpriteSheets/PenguinPairs1/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/LAB.min.js b/18_SpriteSheets/PenguinPairs1/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/PenguinPairs.html b/18_SpriteSheets/PenguinPairs1/PenguinPairs.html new file mode 100644 index 0000000..fa154ad --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/PenguinPairs.html @@ -0,0 +1,41 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/PenguinPairs.js b/18_SpriteSheets/PenguinPairs1/PenguinPairs.js new file mode 100644 index 0000000..124ace2 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/PenguinPairs.js @@ -0,0 +1,37 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.background_level = loadSprite("spr_background_level.jpg"); + sprites.penguin = loadSprite("spr_penguin@4x2.png"); +}; + +Game.initialize = function () { + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // create the game world + Game.gameWorld = new PenguinPairsGameWorld(); +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/SpriteSheet.js b/18_SpriteSheets/PenguinPairs1/SpriteSheet.js new file mode 100644 index 0000000..d533348 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/SpriteSheet.js @@ -0,0 +1,77 @@ +"use strict"; + +function SpriteSheet(imageName) { + console.log("Loading sprite: " + imageName); + Game._spritesStillLoading += 1; + Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._image.onload = function () { + Game._spritesStillLoading -= 1; + }; + + this._sheetColumns = 1; + this._sheetRows = 1; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; +} + +Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + +SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/game-layout.css b/18_SpriteSheets/PenguinPairs1/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/gameobjects/GameObject.js b/18_SpriteSheets/PenguinPairs1/gameobjects/GameObject.js new file mode 100644 index 0000000..09a33ff --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/gameobjects/GameObject.js @@ -0,0 +1,58 @@ +"use strict"; + +function GameObject(layer, id) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/gameobjects/GameObjectList.js b/18_SpriteSheets/PenguinPairs1/gameobjects/GameObjectList.js new file mode 100644 index 0000000..062be32 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/gameobjects/GameObjectList.js @@ -0,0 +1,79 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; diff --git a/18_SpriteSheets/PenguinPairs1/gameobjects/PenguinPairsGameWorld.js b/18_SpriteSheets/PenguinPairs1/gameobjects/PenguinPairsGameWorld.js new file mode 100644 index 0000000..3897c10 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/gameobjects/PenguinPairsGameWorld.js @@ -0,0 +1,21 @@ +"use strict"; + +function PenguinPairsGameWorld(layer, id) { + GameObjectList.call(this, layer, id); + + this.add(new SpriteGameObject(sprites.background_level, ID.layer_background)); + + this.penguin = new SpriteGameObject(sprites.penguin, ID.layer_objects); + this.penguin.position = new Vector2(500, 420); + this.add(this.penguin); + +} + +PenguinPairsGameWorld.prototype = Object.create(GameObjectList.prototype); + +PenguinPairsGameWorld.prototype.handleInput = function (delta) { + if (Keyboard.pressed(Keys.left)) + this.penguin.sheetIndex--; + else if (Keyboard.pressed(Keys.right)) + this.penguin.sheetIndex++; +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/gameobjects/SpriteGameObject.js b/18_SpriteSheets/PenguinPairs1/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..fddf0d3 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/gameobjects/SpriteGameObject.js @@ -0,0 +1,84 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + + this.sprite = sprite; + this.origin = Vector2.zero; + this._sheetIndex = 0; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0 && value < this.sprite.nrSheetElements) + this._sheetIndex = value; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex); +}; diff --git a/18_SpriteSheets/PenguinPairs1/geom/Rectangle.js b/18_SpriteSheets/PenguinPairs1/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/geom/Vector2.js b/18_SpriteSheets/PenguinPairs1/geom/Vector2.js new file mode 100644 index 0000000..04bf6a8 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/input/ButtonState.js b/18_SpriteSheets/PenguinPairs1/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/input/Keyboard.js b/18_SpriteSheets/PenguinPairs1/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/18_SpriteSheets/PenguinPairs1/input/Mouse.js b/18_SpriteSheets/PenguinPairs1/input/Mouse.js new file mode 100644 index 0000000..662f762 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/input/Touch.js b/18_SpriteSheets/PenguinPairs1/input/Touch.js new file mode 100644 index 0000000..7adb4ce --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/input/Touch.js @@ -0,0 +1,108 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return Vector2.zero; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/system/Color.js b/18_SpriteSheets/PenguinPairs1/system/Color.js new file mode 100644 index 0000000..cde103d --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + +var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" +}; \ No newline at end of file diff --git a/18_SpriteSheets/PenguinPairs1/system/Keys.js b/18_SpriteSheets/PenguinPairs1/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/18_SpriteSheets/PenguinPairs1/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/19_MenusSettings/LAB.min.js b/19_MenusSettings/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/19_MenusSettings/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/Canvas2D.js b/19_MenusSettings/PenguinPairs2/Canvas2D.js new file mode 100644 index 0000000..8df997b --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/Canvas2D.js @@ -0,0 +1,149 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect, mirror) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + if (mirror) { + this._canvasContext.scale(scale * canvasScale.x * -1, scale * canvasScale.y); + this._canvasContext.translate(-position.x - sourceRect.width, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + sourceRect.width - origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + else { + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/Game.js b/19_MenusSettings/PenguinPairs2/Game.js new file mode 100644 index 0000000..bab8420 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/Game.js @@ -0,0 +1,103 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + this.gameWorld = null; + + // for debugging + this.log = ""; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.loadSprite = function (imageName) { + console.log("Loading sprite: " + imageName); + var image = new Image(); + image.src = imageName; + this._spritesStillLoading += 1; + this._totalSprites += 1; + image.onload = function () { + Game._spritesStillLoading -= 1; + }; + return image; +}; + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + Game.gameWorld.handleInput(delta); + Game.gameWorld.update(delta); + Canvas2D.clear(); + Game.gameWorld.draw(); + + // displaying the number of touches + Canvas2D.drawText(Game.log); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/LAB.min.js b/19_MenusSettings/PenguinPairs2/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/Math.js b/19_MenusSettings/PenguinPairs2/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/PenguinPairs.html b/19_MenusSettings/PenguinPairs2/PenguinPairs.html new file mode 100644 index 0000000..072eba5 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/PenguinPairs.html @@ -0,0 +1,47 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/PenguinPairs.js b/19_MenusSettings/PenguinPairs2/PenguinPairs.js new file mode 100644 index 0000000..2cbc880 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/PenguinPairs.js @@ -0,0 +1,49 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +var GameSettings = { + hints: true +}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.background_options = loadSprite("spr_background_options.jpg"); + sprites.button_back = loadSprite("spr_button_back.jpg"); + sprites.button_offon = loadSprite("spr_button_offon@2.png"); + sprites.slider_bar = loadSprite("spr_slider_bar.jpg"); + sprites.slider_button = loadSprite("spr_slider_button.jpg"); + + sounds.music = loadSound("snd_music", true); +}; + +Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // create the game world + Game.gameWorld = new PenguinPairsGameWorld(); +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/Sound.js b/19_MenusSettings/PenguinPairs2/Sound.js new file mode 100644 index 0000000..5d9671b --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/SpriteSheet.js b/19_MenusSettings/PenguinPairs2/SpriteSheet.js new file mode 100644 index 0000000..7486243 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/SpriteSheet.js @@ -0,0 +1,79 @@ +"use strict"; + +function SpriteSheet(imageName) { + console.log("Loading sprite: " + imageName); + Game._spritesStillLoading += 1; + Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._sheetColumns = 1; + this._sheetRows = 1; + this._collisionMask = null; + + var sprite = this; + this._image.onload = function () { + Game._spritesStillLoading -= 1; + }; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split("/")[0].split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; +} + +Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + +SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/game-layout.css b/19_MenusSettings/PenguinPairs2/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gameobjects/GameObject.js b/19_MenusSettings/PenguinPairs2/gameobjects/GameObject.js new file mode 100644 index 0000000..09a33ff --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gameobjects/GameObject.js @@ -0,0 +1,58 @@ +"use strict"; + +function GameObject(layer, id) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gameobjects/GameObjectList.js b/19_MenusSettings/PenguinPairs2/gameobjects/GameObjectList.js new file mode 100644 index 0000000..b153446 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gameobjects/GameObjectList.js @@ -0,0 +1,79 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gameobjects/PenguinPairsGameWorld.js b/19_MenusSettings/PenguinPairs2/gameobjects/PenguinPairsGameWorld.js new file mode 100644 index 0000000..76dd019 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gameobjects/PenguinPairsGameWorld.js @@ -0,0 +1,42 @@ +"use strict"; + +function PenguinPairsGameWorld(layer, id) { + GameObjectList.call(this, layer, id); + + var background = new SpriteGameObject(sprites.background_options, ID.layer_background); + this.add(background); + + var onOffLabel = new Label("Arial", "60px", ID.layer_overlays); + onOffLabel.text = "Hints"; + onOffLabel.position = new Vector2(150, 360); + onOffLabel.color = Color.darkBlue; + this.add(onOffLabel); + + this.onOffButton = new OnOffButton(sprites.button_offon, ID.layer_overlays); + this.onOffButton.position = new Vector2(650, 340); + this.onOffButton.on = GameSettings.hints; + this.add(this.onOffButton); + + var musicText = new Label("Arial", "60px", ID.layer_overlays); + musicText.text = "Music volume"; + musicText.position = new Vector2(150, 490); + musicText.color = Color.darkBlue; + this.add(musicText); + + this.musicSlider = new Slider(sprites.slider_bar, sprites.slider_button, ID.layer_overlays); + this.musicSlider.position = new Vector2(650, 500); + this.musicSlider.value = sounds.music.volume; + this.add(this.musicSlider); + + this.backButton = new Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +PenguinPairsGameWorld.prototype = Object.create(GameObjectList.prototype); + +PenguinPairsGameWorld.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + sounds.music.volume = this.musicSlider.value; + GameSettings.hints = this.onOffButton.on; +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gameobjects/SpriteGameObject.js b/19_MenusSettings/PenguinPairs2/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..c14bde2 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gameobjects/SpriteGameObject.js @@ -0,0 +1,86 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + + this.sprite = sprite; + + this.origin = Vector2.zero; + this.mirror = false; + this._sheetIndex = 0; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0 && value < this.sprite.nrSheetElements) + this._sheetIndex = value; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex, this.mirror); +}; diff --git a/19_MenusSettings/PenguinPairs2/geom/Rectangle.js b/19_MenusSettings/PenguinPairs2/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/geom/Vector2.js b/19_MenusSettings/PenguinPairs2/geom/Vector2.js new file mode 100644 index 0000000..04bf6a8 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gui/Button.js b/19_MenusSettings/PenguinPairs2/gui/Button.js new file mode 100644 index 0000000..d1d9d63 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; diff --git a/19_MenusSettings/PenguinPairs2/gui/Label.js b/19_MenusSettings/PenguinPairs2/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/19_MenusSettings/PenguinPairs2/gui/OnOffButton.js b/19_MenusSettings/PenguinPairs2/gui/OnOffButton.js new file mode 100644 index 0000000..530a822 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gui/OnOffButton.js @@ -0,0 +1,28 @@ +"use strict"; + +function OnOffButton(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); +} + +OnOffButton.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(OnOffButton.prototype, "on", + { + get: function () { + return this.sheetIndex === 1; + }, + set: function (value) { + if (value) + this.sheetIndex = 1; + else + this.sheetIndex = 0; + } + }); + +OnOffButton.prototype.handleInput = function (delta) { + if (!this.visible) + return; + if (Touch.containsTouchPress(this.boundingBox) || + Mouse.containsMousePress(this.boundingBox)) + this.sheetIndex = 1 - this.sheetIndex; +}; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/gui/Slider.js b/19_MenusSettings/PenguinPairs2/gui/Slider.js new file mode 100644 index 0000000..dfe0ad6 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/gui/Slider.js @@ -0,0 +1,74 @@ +"use strict"; + +function Slider(sliderback, sliderfront, layer) { + GameObjectList.call(this, layer); + this.dragging = false; + this.draggingId = -1; + this.leftmargin = 5; + this.rightmargin = 7; + + this.back = new SpriteGameObject(sliderback); + this.front = new SpriteGameObject(sliderfront, 1); + this.front.position = new Vector2(this.leftmargin, 8); + this.add(this.back); + this.add(this.front); +} + +Slider.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(Slider.prototype, "value", + { + get: function () { + return (this.front.position.x - this.back.position.x - this.leftmargin) / + (this.back.width - this.front.width - this.leftmargin - this.rightmargin); + }, + set: function (value) { + var newxpos = value * (this.back.width - this.front.width - this.leftmargin - this.rightmargin) + + this.back.position.x + this.leftmargin; + this.front.position = new Vector2(newxpos, this.front.position.y); + } + }); + +Slider.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (Touch.isTouchDevice) { + this.handleInputTouch(delta); + } else { + this.handleInputMouse(delta); + } +}; + +Slider.prototype.handleInputMouse = function (delta) { + if (!Mouse.left.down) { + this.dragging = false; + this.draggingId = -1; + return; + } + if (this.back.boundingBox.contains(Mouse.position)) + this.dragging = true; + if (this.dragging) { + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(Mouse.position.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + } +}; + +Slider.prototype.handleInputTouch = function (delta) { + if (!Touch.isTouching) { + this.dragging = false; + this.draggingId = -1; + return; + } + if (Touch.containsTouch(this.back.boundingBox)) { + this.draggingId = Touch.getIndexInRect(this.back.boundingBox); + this.dragging = true; + } + if (this.dragging) { + var touchPos = Touch.getPosition(this.draggingId); + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(touchPos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + } +}; diff --git a/19_MenusSettings/PenguinPairs2/input/ButtonState.js b/19_MenusSettings/PenguinPairs2/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/input/Keyboard.js b/19_MenusSettings/PenguinPairs2/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/19_MenusSettings/PenguinPairs2/input/Mouse.js b/19_MenusSettings/PenguinPairs2/input/Mouse.js new file mode 100644 index 0000000..662f762 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/input/Touch.js b/19_MenusSettings/PenguinPairs2/input/Touch.js new file mode 100644 index 0000000..7adb4ce --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/input/Touch.js @@ -0,0 +1,108 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return Vector2.zero; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/system/Color.js b/19_MenusSettings/PenguinPairs2/system/Color.js new file mode 100644 index 0000000..19ee82a --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + + var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" + }; \ No newline at end of file diff --git a/19_MenusSettings/PenguinPairs2/system/Keys.js b/19_MenusSettings/PenguinPairs2/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/19_MenusSettings/PenguinPairs2/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/20_GameStateManagement/LAB.min.js b/20_GameStateManagement/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/20_GameStateManagement/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/Canvas2D.js b/20_GameStateManagement/PenguinPairs3/Canvas2D.js new file mode 100644 index 0000000..8df997b --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/Canvas2D.js @@ -0,0 +1,149 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect, mirror) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + if (mirror) { + this._canvasContext.scale(scale * canvasScale.x * -1, scale * canvasScale.y); + this._canvasContext.translate(-position.x - sourceRect.width, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + sourceRect.width - origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + else { + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/Game.js b/20_GameStateManagement/PenguinPairs3/Game.js new file mode 100644 index 0000000..65f776e --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/Game.js @@ -0,0 +1,85 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + GameStateManager.handleInput(delta); + GameStateManager.update(delta); + Canvas2D.clear(); + GameStateManager.draw(); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/GameStateManager.js b/20_GameStateManagement/PenguinPairs3/GameStateManager.js new file mode 100644 index 0000000..50fd349 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/GameStateManager.js @@ -0,0 +1,45 @@ +"use strict"; + +function GameStateManager_Singleton() { + this._gameStates = []; + this._currentGameState = null; +} + +GameStateManager_Singleton.prototype.add = function (gamestate) { + this._gameStates.push(gamestate); + this._currentGameState = gamestate; + return this._gameStates.length - 1; +}; + +GameStateManager_Singleton.prototype.get = function (id) { + if (id < 0 || id >= this._gameStates.length) + return null; + else + return this._gameStates[id]; +}; + +GameStateManager_Singleton.prototype.switchTo = function (id) { + this._currentGameState = this.get(id); +}; + +GameStateManager_Singleton.prototype.handleInput = function (delta) { + if (this._currentGameState !== null) + this._currentGameState.handleInput(delta); +}; + +GameStateManager_Singleton.prototype.update = function (delta) { + if (this._currentGameState !== null) + this._currentGameState.update(delta); +}; + +GameStateManager_Singleton.prototype.draw = function () { + if (this._currentGameState !== null) + this._currentGameState.draw(); +}; + +GameStateManager_Singleton.prototype.reset = function () { + if (this._currentGameState !== null) + this._currentGameState.reset(); +}; + +var GameStateManager = new GameStateManager_Singleton(); diff --git a/20_GameStateManagement/PenguinPairs3/LAB.min.js b/20_GameStateManagement/PenguinPairs3/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/Math.js b/20_GameStateManagement/PenguinPairs3/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/PenguinPairs.html b/20_GameStateManagement/PenguinPairs3/PenguinPairs.html new file mode 100644 index 0000000..9d4ec30 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/PenguinPairs.html @@ -0,0 +1,53 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/PenguinPairs.js b/20_GameStateManagement/PenguinPairs3/PenguinPairs.js new file mode 100644 index 0000000..f4a76a8 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/PenguinPairs.js @@ -0,0 +1,69 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +var GameSettings = { + hints: true +}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("spr_background_title.jpg"); + sprites.background_help = loadSprite("spr_background_help.jpg"); + sprites.background_level = loadSprite("spr_background_level.jpg"); + sprites.background_levelselect = loadSprite("spr_background_levelselect.jpg"); + sprites.background_options = loadSprite("spr_background_options.jpg"); + sprites.button_back = loadSprite("spr_button_back.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.button_hint = loadSprite("spr_button_hint.png"); + sprites.button_offon = loadSprite("spr_button_offon@2.png"); + sprites.button_options = loadSprite("spr_button_options.jpg"); + sprites.button_play = loadSprite("spr_button_play.jpg"); + sprites.button_quit = loadSprite("spr_button_quit.png"); + sprites.button_retry = loadSprite("spr_button_retry.png"); + sprites.help = loadSprite("spr_help.jpg"); + sprites.level_unsolved = loadSprite("spr_level_unsolved.png"); + sprites.level_solved = loadSprite("spr_level_solved@6.png"); + sprites.level_locked = loadSprite("spr_lock.png"); + sprites.slider_bar = loadSprite("spr_slider_bar.jpg"); + sprites.slider_button = loadSprite("spr_slider_button.jpg"); + + sounds.music = loadSound("snd_music", true); +}; + +Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // create the different game modes + ID.game_state_title = GameStateManager.add(new TitleMenuState()); + ID.game_state_help = GameStateManager.add(new HelpState()); + ID.game_state_options = GameStateManager.add(new OptionsMenuState()); + ID.game_state_levelselect = GameStateManager.add(new LevelMenuState()); + + // set the current game mode + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/Sound.js b/20_GameStateManagement/PenguinPairs3/Sound.js new file mode 100644 index 0000000..5d9671b --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/SpriteSheet.js b/20_GameStateManagement/PenguinPairs3/SpriteSheet.js new file mode 100644 index 0000000..7486243 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/SpriteSheet.js @@ -0,0 +1,79 @@ +"use strict"; + +function SpriteSheet(imageName) { + console.log("Loading sprite: " + imageName); + Game._spritesStillLoading += 1; + Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._sheetColumns = 1; + this._sheetRows = 1; + this._collisionMask = null; + + var sprite = this; + this._image.onload = function () { + Game._spritesStillLoading -= 1; + }; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split("/")[0].split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; +} + +Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + +SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/game-layout.css b/20_GameStateManagement/PenguinPairs3/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/gameobjects/GameObject.js b/20_GameStateManagement/PenguinPairs3/gameobjects/GameObject.js new file mode 100644 index 0000000..09a33ff --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gameobjects/GameObject.js @@ -0,0 +1,58 @@ +"use strict"; + +function GameObject(layer, id) { + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.handleInput = function (delta) { +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; + +GameObject.prototype.draw = function () { +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/gameobjects/GameObjectList.js b/20_GameStateManagement/PenguinPairs3/gameobjects/GameObjectList.js new file mode 100644 index 0000000..b153446 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gameobjects/GameObjectList.js @@ -0,0 +1,79 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/gameobjects/SpriteGameObject.js b/20_GameStateManagement/PenguinPairs3/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..ad72ce1 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gameobjects/SpriteGameObject.js @@ -0,0 +1,86 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + + this.sprite = sprite; + + this.origin = Vector2.zero; + this.mirror = false; + this._sheetIndex = 0; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0) + this._sheetIndex = value % this.sprite.nrSheetElements; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex, this.mirror); +}; diff --git a/20_GameStateManagement/PenguinPairs3/geom/Rectangle.js b/20_GameStateManagement/PenguinPairs3/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/geom/Vector2.js b/20_GameStateManagement/PenguinPairs3/geom/Vector2.js new file mode 100644 index 0000000..04bf6a8 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/gui/Button.js b/20_GameStateManagement/PenguinPairs3/gui/Button.js new file mode 100644 index 0000000..d1d9d63 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; diff --git a/20_GameStateManagement/PenguinPairs3/gui/Label.js b/20_GameStateManagement/PenguinPairs3/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/20_GameStateManagement/PenguinPairs3/gui/LevelButton.js b/20_GameStateManagement/PenguinPairs3/gui/LevelButton.js new file mode 100644 index 0000000..c7470bd --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gui/LevelButton.js @@ -0,0 +1,47 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new SpriteGameObject(sprites.level_solved, ID.layer_overlays); + this._levelUnsolved = new SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + this._levelLocked = new SpriteGameObject(sprites.level_locked, ID.layer_overlays_2); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + this.add(this._levelLocked); + + var textLabel = new Label("Arial", "20px", ID.layer_overlays_1); + textLabel.text = levelIndex + 1; + textLabel.position = new Vector2(this._levelSolved.width - textLabel.width, + this._levelSolved.height - textLabel.height + 50).divideBy(2); + textLabel.color = Color.black; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (Touch.isTouchDevice) + this.pressed = this.visible && Touch.containsTouchPress(this._levelLocked.boundingBox); + else + this.pressed = this.visible && Mouse.left.pressed && + this._levelLocked.boundingBox.contains(Mouse.position); +}; diff --git a/20_GameStateManagement/PenguinPairs3/gui/OnOffButton.js b/20_GameStateManagement/PenguinPairs3/gui/OnOffButton.js new file mode 100644 index 0000000..78d25bb --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gui/OnOffButton.js @@ -0,0 +1,28 @@ +"use strict"; + +function OnOffButton(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); +} + +OnOffButton.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(OnOffButton.prototype, "on", + { + get: function () { + return this.sheetIndex === 1; + }, + set: function (value) { + if (value) + this.sheetIndex = 1; + else + this.sheetIndex = 0; + } + }); + +OnOffButton.prototype.handleInput = function (delta) { + if (!this.visible) + return; + if (Touch.containsTouchPress(this.boundingBox) || + Mouse.containsMousePress(this.boundingBox)) + this.sheetIndex = (this.sheetIndex + 1) % 2; +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/gui/Slider.js b/20_GameStateManagement/PenguinPairs3/gui/Slider.js new file mode 100644 index 0000000..20dca1f --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/gui/Slider.js @@ -0,0 +1,61 @@ +"use strict"; + +function Slider(sliderback, sliderfront, layer) { + GameObjectList.call(this, layer); + this.dragging = false; + this.draggingId = -1; + this.leftmargin = 5; + this.rightmargin = 7; + + this.back = new SpriteGameObject(sliderback); + this.front = new SpriteGameObject(sliderfront, 1); + this.front.position = new Vector2(this.leftmargin, 8); + this.add(this.back); + this.add(this.front); +} + +Slider.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(Slider.prototype, "value", + { + get: function () { + return (this.front.position.x - this.back.position.x - this.leftmargin) / + (this.back.width - this.front.width - this.leftmargin - this.rightmargin); + }, + set: function (value) { + var newxpos = value * (this.back.width - this.front.width - this.leftmargin - this.rightmargin) + + this.back.position.x + this.leftmargin; + this.front.position = new Vector2(newxpos, this.front.position.y); + } + }); + +Slider.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (Mouse.left.down || Touch.isTouching) { + if (Touch.isTouchDevice) { + if (Touch.containsTouch(this.back.boundingBox)) + this.draggingId = Touch.getIndexInRect((this.back.boundingBox)); + if (Touch.containsTouch(this.back.boundingBox) || this.dragging) { + var touchPos = Touch.getPosition(this.draggingId); + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(touchPos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } else { + var mousePos = Mouse.position; + if (this.back.boundingBox.contains(mousePos) || this.dragging) { + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(mousePos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } + } + else { + this.dragging = false; + this.draggingId = -1; + } +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/input/ButtonState.js b/20_GameStateManagement/PenguinPairs3/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/input/Keyboard.js b/20_GameStateManagement/PenguinPairs3/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/20_GameStateManagement/PenguinPairs3/input/Mouse.js b/20_GameStateManagement/PenguinPairs3/input/Mouse.js new file mode 100644 index 0000000..662f762 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/input/Touch.js b/20_GameStateManagement/PenguinPairs3/input/Touch.js new file mode 100644 index 0000000..7adb4ce --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/input/Touch.js @@ -0,0 +1,108 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return Vector2.zero; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/states/HelpState.js b/20_GameStateManagement/PenguinPairs3/states/HelpState.js new file mode 100644 index 0000000..60f3ca6 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/states/HelpState.js @@ -0,0 +1,22 @@ +"use strict"; + +function HelpState(layer) { + GameObjectList.call(this, layer); + + // the background + var background = new SpriteGameObject(sprites.background_help); + this.add(background); + + // add a back button + this.backButton = new Button(sprites.button_back, 100); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +HelpState.prototype = Object.create(GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/states/LevelMenuState.js b/20_GameStateManagement/PenguinPairs3/states/LevelMenuState.js new file mode 100644 index 0000000..f7cad35 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/states/LevelMenuState.js @@ -0,0 +1,45 @@ +"use strict"; + +function LevelMenuState(layer) { + GameObjectList.call(this, layer); + + this.background = new SpriteGameObject(sprites.background_levelselect, ID.layer_background); + this.add(this.background); + + this.back = new Button(sprites.button_back, ID.layer_overlays); + this.back.position = new Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0; i < 12; i += 1) { + var row = Math.floor(i / 5); + var column = i % 5; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new Vector2(column * (level.width + 30) + 155, + row * (level.height + 5) + 230); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i += 1) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + // start playing the level + } + else if (this.back.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/states/OptionsMenuState.js b/20_GameStateManagement/PenguinPairs3/states/OptionsMenuState.js new file mode 100644 index 0000000..f4ea732 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/states/OptionsMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function OptionsMenuState(layer) { + GameObjectList.call(this, layer); + + var background = new SpriteGameObject(sprites.background_options, ID.layer_background); + this.add(background); + + var onOffLabel = new Label("Arial", "60px", ID.layer_overlays); + onOffLabel.text = "Hints"; + onOffLabel.position = new Vector2(150, 360); + onOffLabel.color = Color.darkBlue; + this.add(onOffLabel); + + this.onOffButton = new OnOffButton(sprites.button_offon, ID.layer_overlays); + this.onOffButton.position = new Vector2(650, 340); + this.onOffButton.on = GameSettings.hints; + this.add(this.onOffButton); + + var musicText = new Label("Arial", "60px", ID.layer_overlays); + musicText.text = "Music volume"; + musicText.position = new Vector2(150, 490); + musicText.color = Color.darkBlue; + this.add(musicText); + + this.musicSlider = new Slider(sprites.slider_bar, sprites.slider_button, ID.layer_overlays); + this.musicSlider.position = new Vector2(650, 500); + this.musicSlider.value = sounds.music.volume; + this.add(this.musicSlider); + + this.backButton = new Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +OptionsMenuState.prototype = Object.create(GameObjectList.prototype); + +OptionsMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; + +OptionsMenuState.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + sounds.music.volume = this.musicSlider.value; + GameSettings.hints = this.onOffButton.on; +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/states/TitleMenuState.js b/20_GameStateManagement/PenguinPairs3/states/TitleMenuState.js new file mode 100644 index 0000000..570edba --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/states/TitleMenuState.js @@ -0,0 +1,31 @@ +"use strict"; + +function TitleMenuState(layer) { + GameObjectList.call(this, layer); + + this.add(new SpriteGameObject(sprites.background_title, ID.layer_background)); + + this.playButton = new Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new Vector2(415, 540); + this.add(this.playButton); + + this.optionsButton = new Button(sprites.button_options, ID.layer_overlays); + this.optionsButton.position = new Vector2(415, 650); + this.add(this.optionsButton); + + this.helpButton = new Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new Vector2(415, 760); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + GameStateManager.switchTo(ID.game_state_help); + else if (this.optionsButton.pressed) + GameStateManager.switchTo(ID.game_state_options); +}; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/system/Color.js b/20_GameStateManagement/PenguinPairs3/system/Color.js new file mode 100644 index 0000000..19ee82a --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + + var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" + }; \ No newline at end of file diff --git a/20_GameStateManagement/PenguinPairs3/system/Keys.js b/20_GameStateManagement/PenguinPairs3/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/20_GameStateManagement/PenguinPairs3/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/LAB.min.js b/21_StoringRecallingGameData/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/21_StoringRecallingGameData/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/Canvas2D.js b/21_StoringRecallingGameData/PenguinPairs4/Canvas2D.js new file mode 100644 index 0000000..8df997b --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/Canvas2D.js @@ -0,0 +1,149 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect, mirror) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + if (mirror) { + this._canvasContext.scale(scale * canvasScale.x * -1, scale * canvasScale.y); + this._canvasContext.translate(-position.x - sourceRect.width, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + sourceRect.width - origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + else { + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/Game.js b/21_StoringRecallingGameData/PenguinPairs4/Game.js new file mode 100644 index 0000000..65f776e --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/Game.js @@ -0,0 +1,85 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + GameStateManager.handleInput(delta); + GameStateManager.update(delta); + Canvas2D.clear(); + GameStateManager.draw(); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/GameStateManager.js b/21_StoringRecallingGameData/PenguinPairs4/GameStateManager.js new file mode 100644 index 0000000..df07f76 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/GameStateManager.js @@ -0,0 +1,47 @@ +"use strict"; + +function GameStateManager_Singleton() { + this._gameStates = []; + this._currentGameState = null; +} + +GameStateManager_Singleton.prototype.add = function (gamestate) { + this._gameStates.push(gamestate); + this._currentGameState = gamestate; + return this._gameStates.length - 1; +}; + +GameStateManager_Singleton.prototype.get = function (id) { + if (id < 0 || id >= this._gameStates.length) + return null; + else + return this._gameStates[id]; +}; + +GameStateManager_Singleton.prototype.switchTo = function (id) { + if (id < 0 || id >= this._gameStates.length) + return; + this._currentGameState = this._gameStates[id]; +}; + +GameStateManager_Singleton.prototype.handleInput = function (delta) { + if (this._currentGameState != null) + this._currentGameState.handleInput(delta); +}; + +GameStateManager_Singleton.prototype.update = function (delta) { + if (this._currentGameState != null) + this._currentGameState.update(delta); +}; + +GameStateManager_Singleton.prototype.draw = function () { + if (this._currentGameState != null) + this._currentGameState.draw(); +}; + +GameStateManager_Singleton.prototype.reset = function () { + if (this._currentGameState != null) + this._currentGameState.reset(); +}; + +var GameStateManager = new GameStateManager_Singleton(); diff --git a/21_StoringRecallingGameData/PenguinPairs4/IGameLoopObject.js b/21_StoringRecallingGameData/PenguinPairs4/IGameLoopObject.js new file mode 100644 index 0000000..ef9d8f6 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/IGameLoopObject.js @@ -0,0 +1,16 @@ +"use strict"; + +function IGameLoopObject() { +} + +IGameLoopObject.prototype.handleInput = function (delta) { +}; + +IGameLoopObject.prototype.update = function (delta) { +}; + +IGameLoopObject.prototype.draw = function () { +}; + +IGameLoopObject.prototype.reset = function () { +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/LAB.min.js b/21_StoringRecallingGameData/PenguinPairs4/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/Math.js b/21_StoringRecallingGameData/PenguinPairs4/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.html b/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.html new file mode 100644 index 0000000..8f550b4 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.html @@ -0,0 +1,60 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.js b/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.js new file mode 100644 index 0000000..74bc8a8 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/PenguinPairs.js @@ -0,0 +1,92 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +var GameSettings = { + hints: true +}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.arrow = loadSprite("spr_arrow@4.png"); + sprites.arrow_hover = loadSprite("spr_arrow_hover@4.png"); + sprites.arrow_hint = loadSprite("spr_arrow_hint@4.png"); + sprites.background_title = loadSprite("spr_background_title.jpg"); + sprites.background_help = loadSprite("spr_background_help.jpg"); + sprites.background_level = loadSprite("spr_background_level.jpg"); + sprites.background_levelselect = loadSprite("spr_background_levelselect.jpg"); + sprites.background_options = loadSprite("spr_background_options.jpg"); + sprites.button_back = loadSprite("spr_button_back.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.button_hint = loadSprite("spr_button_hint.png"); + sprites.button_offon = loadSprite("spr_button_offon@2.png"); + sprites.button_options = loadSprite("spr_button_options.jpg"); + sprites.button_play = loadSprite("spr_button_play.jpg"); + sprites.button_quit = loadSprite("spr_button_quit.png"); + sprites.button_retry = loadSprite("spr_button_retry.png"); + sprites.field = loadSprite("spr_field@2.png"); + sprites.frame_goal = loadSprite("spr_frame_goal.jpg"); + sprites.help = loadSprite("spr_help.jpg"); + if (Touch.isTouchDevice) + sprites.level_finished = loadSprite("spr_level_finished_tap.png"); + else + sprites.level_finished = loadSprite("spr_level_finished_click.png"); + sprites.level_unsolved = loadSprite("spr_level_unsolved.png"); + sprites.level_solved = loadSprite("spr_level_solved@6.png"); + sprites.level_locked = loadSprite("spr_lock.png"); + sprites.penguin = loadSprite("spr_penguin@8.png"); + sprites.penguin_boxed = loadSprite("spr_penguin_boxed@8.png"); + sprites.penguin_empty = loadSprite("spr_penguin_empty.png"); + sprites.penguin_pairs = loadSprite("spr_penguin_pairs@8.png"); + sprites.shark = loadSprite("spr_shark.png"); + sprites.slider_bar = loadSprite("spr_slider_bar.jpg"); + sprites.slider_button = loadSprite("spr_slider_button.jpg"); + sprites.wall = loadSprite("spr_wall.png"); + + sounds.music = loadSound("snd_music", true); + sounds.eat = loadSound("snd_eat"); + sounds.lost = loadSound("snd_lost"); + sounds.won = loadSound("snd_won"); + sounds.pair = loadSound("snd_pair"); +}; + +Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.tiles = 1; + + // create the different game modes + ID.game_state_title = GameStateManager.add(new TitleMenuState()); + ID.game_state_help = GameStateManager.add(new HelpState()); + ID.game_state_options = GameStateManager.add(new OptionsMenuState()); + ID.game_state_playing = GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = GameStateManager.add(new LevelMenuState()); + + // set the current game mode + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/Sound.js b/21_StoringRecallingGameData/PenguinPairs4/Sound.js new file mode 100644 index 0000000..5d9671b --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/SpriteSheet.js b/21_StoringRecallingGameData/PenguinPairs4/SpriteSheet.js new file mode 100644 index 0000000..7486243 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/SpriteSheet.js @@ -0,0 +1,79 @@ +"use strict"; + +function SpriteSheet(imageName) { + console.log("Loading sprite: " + imageName); + Game._spritesStillLoading += 1; + Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._sheetColumns = 1; + this._sheetRows = 1; + this._collisionMask = null; + + var sprite = this; + this._image.onload = function () { + Game._spritesStillLoading -= 1; + }; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split("/")[0].split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; +} + +Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + +SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/game-layout.css b/21_StoringRecallingGameData/PenguinPairs4/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Animal.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Animal.js new file mode 100644 index 0000000..c4261a1 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Animal.js @@ -0,0 +1,32 @@ +"use strict"; + +function Animal(color, sprite, layer) { + SpriteGameObject.call(this, sprite, layer); + + this.initialPosition = Vector2.zero; + this.boxed = (color === color.toUpperCase()); + this.initialEmptyBox = this.boxed && color.toLowerCase() === "x"; + this.sheetIndex = "brgyopmx".indexOf(color.toLowerCase()); +} + +Animal.prototype = Object.create(SpriteGameObject.prototype); + +Animal.prototype.reset = function () { + this.position = this.initialPosition.copy(); + this.velocity = Vector2.zero; + this.visible = true; + if (this.initialEmptyBox) + this.sheetIndex = 7; +}; + +Animal.prototype.isSeal = function () { + return this.sheetIndex === 7 && !this.boxed; +}; + +Animal.prototype.isMulticolor = function () { + return this.sheetIndex === 6 && !this.boxed; +}; + +Animal.prototype.isEmptyBox = function () { + return this.sheetIndex === 7 && this.boxed; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObject.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObject.js new file mode 100644 index 0000000..9d3151e --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObject.js @@ -0,0 +1,56 @@ +"use strict"; + +function GameObject(layer, id) { + IGameLoopObject.call(this); + + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +GameObject.prototype = Object.create(IGameLoopObject.prototype); + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectGrid.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..a25f664 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectGrid.js @@ -0,0 +1,57 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer, id) { + GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; + +GameObjectGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectList.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectList.js new file mode 100644 index 0000000..b153446 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/GameObjectList.js @@ -0,0 +1,79 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Level.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Level.js new file mode 100644 index 0000000..efc8da9 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Level.js @@ -0,0 +1,116 @@ +"use strict"; + +function Level(levelIndex) { + GameObjectList.call(this); + + var levelData = window.LEVELS[levelIndex]; + + this.animals = []; + this.sharks = []; + this.levelIndex = levelIndex; + + this.add(new SpriteGameObject(sprites.background_level, ID.layer_background)); + + this.hintButton = new Button(sprites.button_hint, ID.layer_overlays); + this.hintButton.position = new Vector2(916, 20); + this.add(this.hintButton); + + this.retryButton = new Button(sprites.button_retry, ID.layer_overlays); + this.retryButton.position = new Vector2(916, 20); + this.retryButton.visible = false; + this.add(this.retryButton); + + this.quitButton = new Button(sprites.button_quit, ID.layer_overlays); + this.quitButton.position = new Vector2(1058, 20); + this.add(this.quitButton); + + var width = levelData.tiles[0].length; + var height = levelData.tiles.length; + + var playingField = new GameObjectList(ID.layer_objects); + playingField.position = new Vector2((Game.size.x - width * 73) / 2, 100); + this.add(playingField); + + var tileField = new GameObjectGrid(height, width, ID.layer_objects, ID.tiles); + tileField.cellHeight = 72; + tileField.cellWidth = 73; + for (var row = 0; row < height; row++) { + for (var col = 0; col < width; col++) { + var t = null; + + switch (levelData.tiles[row][col]) { + case '.' : + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + break; + case ' ': + t = new Tile(sprites.wall, ID.layer_objects); + t.type = TileType.background; + tileField.addAt(t, col, row); + break; + case 'r': + case 'b': + case 'g': + case 'o': + case 'p': + case 'y': + case 'm': + case 'x': + case 'R': + case 'B': + case 'G': + case 'O': + case 'P': + case 'Y': + case 'M': + case 'X': + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var animalSprite = sprites.penguin; + if (levelData.tiles[row][col] === levelData.tiles[row][col].toUpperCase()) + animalSprite = sprites.penguin_boxed; + var p = new Animal(levelData.tiles[row][col], animalSprite, ID.layer_objects_1); + p.position = t.position.copy(); + p.initialPosition = t.position.copy(); + playingField.add(p); + this.animals.push(p); + break; + case '@': + t = new Tile(sprites.field); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var s = new SpriteGameObject(sprites.shark, ID.layer_objects_1); + s.position = t.position.copy(); + playingField.add(s); + this.sharks.push(s); + break; + default : + t = new Tile(sprites.wall, ID.layer_objects); + tileField.addAt(t, col, row); + t.type = TileType.wall; + break; + } + } + } + playingField.add(tileField); + + var helpField = new SpriteGameObject(sprites.help, ID.layer_overlays); + helpField.position = new Vector2(helpField.screenCenterX, 780); + this.add(helpField); + var helpLabel = new Label("Arial", "24px", ID.layer_overlays_1); + helpLabel.text = levelData.hint; + helpLabel.color = Color.darkBlue; + helpLabel.position = new Vector2(helpLabel.screenCenterX, 780 + (helpField.height - helpLabel.height) / 2); + this.add(helpLabel); +} + +Level.prototype = Object.create(GameObjectList.prototype); + +Level.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.quitButton.pressed) { + GameStateManager.switchTo(ID.game_state_levelselect); + } +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/SpriteGameObject.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..ad72ce1 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/SpriteGameObject.js @@ -0,0 +1,86 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + + this.sprite = sprite; + + this.origin = Vector2.zero; + this.mirror = false; + this._sheetIndex = 0; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0) + this._sheetIndex = value % this.sprite.nrSheetElements; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex, this.mirror); +}; diff --git a/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Tile.js b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Tile.js new file mode 100644 index 0000000..3b19fba --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gameobjects/Tile.js @@ -0,0 +1,20 @@ +"use strict"; + +var TileType = { + normal: 0, + background: 1, + wall: 2 +}; + +function Tile(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); + this.type = TileType.normal; +} + +Tile.prototype = Object.create(SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type === TileType.background) + return; + SpriteGameObject.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/geom/Rectangle.js b/21_StoringRecallingGameData/PenguinPairs4/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/geom/Vector2.js b/21_StoringRecallingGameData/PenguinPairs4/geom/Vector2.js new file mode 100644 index 0000000..04bf6a8 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gui/Button.js b/21_StoringRecallingGameData/PenguinPairs4/gui/Button.js new file mode 100644 index 0000000..d1d9d63 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; diff --git a/21_StoringRecallingGameData/PenguinPairs4/gui/Label.js b/21_StoringRecallingGameData/PenguinPairs4/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/21_StoringRecallingGameData/PenguinPairs4/gui/LevelButton.js b/21_StoringRecallingGameData/PenguinPairs4/gui/LevelButton.js new file mode 100644 index 0000000..4ba1fcb --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gui/LevelButton.js @@ -0,0 +1,59 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new SpriteGameObject(sprites.level_solved, ID.layer_overlays); + this._levelUnsolved = new SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + + this.add(this._levelUnsolved); + + this._levelLocked = new SpriteGameObject(sprites.level_locked, ID.layer_overlays_2); + this.add(this._levelLocked); + + var textLabel = new Label("Arial", "20px", ID.layer_overlays_1); + textLabel.text = levelIndex + 1; + textLabel.position = new Vector2(this._levelSolved.width - textLabel.width, + this._levelSolved.height - textLabel.height + 50).divideBy(2); + textLabel.color = Color.black; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (Touch.isTouchDevice) + this.pressed = this.visible && Touch.containsTouchPress(this._levelLocked.boundingBox); + else + this.pressed = this.visible && Mouse.left.pressed && + this._levelLocked.boundingBox.contains(Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gui/OnOffButton.js b/21_StoringRecallingGameData/PenguinPairs4/gui/OnOffButton.js new file mode 100644 index 0000000..78d25bb --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gui/OnOffButton.js @@ -0,0 +1,28 @@ +"use strict"; + +function OnOffButton(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); +} + +OnOffButton.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(OnOffButton.prototype, "on", + { + get: function () { + return this.sheetIndex === 1; + }, + set: function (value) { + if (value) + this.sheetIndex = 1; + else + this.sheetIndex = 0; + } + }); + +OnOffButton.prototype.handleInput = function (delta) { + if (!this.visible) + return; + if (Touch.containsTouchPress(this.boundingBox) || + Mouse.containsMousePress(this.boundingBox)) + this.sheetIndex = (this.sheetIndex + 1) % 2; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/gui/Slider.js b/21_StoringRecallingGameData/PenguinPairs4/gui/Slider.js new file mode 100644 index 0000000..20dca1f --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/gui/Slider.js @@ -0,0 +1,61 @@ +"use strict"; + +function Slider(sliderback, sliderfront, layer) { + GameObjectList.call(this, layer); + this.dragging = false; + this.draggingId = -1; + this.leftmargin = 5; + this.rightmargin = 7; + + this.back = new SpriteGameObject(sliderback); + this.front = new SpriteGameObject(sliderfront, 1); + this.front.position = new Vector2(this.leftmargin, 8); + this.add(this.back); + this.add(this.front); +} + +Slider.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(Slider.prototype, "value", + { + get: function () { + return (this.front.position.x - this.back.position.x - this.leftmargin) / + (this.back.width - this.front.width - this.leftmargin - this.rightmargin); + }, + set: function (value) { + var newxpos = value * (this.back.width - this.front.width - this.leftmargin - this.rightmargin) + + this.back.position.x + this.leftmargin; + this.front.position = new Vector2(newxpos, this.front.position.y); + } + }); + +Slider.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (Mouse.left.down || Touch.isTouching) { + if (Touch.isTouchDevice) { + if (Touch.containsTouch(this.back.boundingBox)) + this.draggingId = Touch.getIndexInRect((this.back.boundingBox)); + if (Touch.containsTouch(this.back.boundingBox) || this.dragging) { + var touchPos = Touch.getPosition(this.draggingId); + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(touchPos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } else { + var mousePos = Mouse.position; + if (this.back.boundingBox.contains(mousePos) || this.dragging) { + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(mousePos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } + } + else { + this.dragging = false; + this.draggingId = -1; + } +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/input/ButtonState.js b/21_StoringRecallingGameData/PenguinPairs4/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/input/Keyboard.js b/21_StoringRecallingGameData/PenguinPairs4/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/21_StoringRecallingGameData/PenguinPairs4/input/Mouse.js b/21_StoringRecallingGameData/PenguinPairs4/input/Mouse.js new file mode 100644 index 0000000..662f762 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/input/Touch.js b/21_StoringRecallingGameData/PenguinPairs4/input/Touch.js new file mode 100644 index 0000000..7adb4ce --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/input/Touch.js @@ -0,0 +1,108 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return Vector2.zero; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/states/HelpState.js b/21_StoringRecallingGameData/PenguinPairs4/states/HelpState.js new file mode 100644 index 0000000..60f3ca6 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/states/HelpState.js @@ -0,0 +1,22 @@ +"use strict"; + +function HelpState(layer) { + GameObjectList.call(this, layer); + + // the background + var background = new SpriteGameObject(sprites.background_help); + this.add(background); + + // add a back button + this.backButton = new Button(sprites.button_back, 100); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +HelpState.prototype = Object.create(GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/states/LevelMenuState.js b/21_StoringRecallingGameData/PenguinPairs4/states/LevelMenuState.js new file mode 100644 index 0000000..f8d2837 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/states/LevelMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function LevelMenuState(layer) { + GameObjectList.call(this, layer); + + this.background = new SpriteGameObject(sprites.background_levelselect, ID.layer_background); + this.add(this.background); + + this.back = new Button(sprites.button_back, ID.layer_overlays); + this.back.position = new Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0; i < window.LEVELS.length; i += 1) { + var row = Math.floor(i / 5); + var column = i % 5; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new Vector2(column * (level.width + 30) + 155, + row * (level.height + 5) + 230); + //console.log("Level " + i + " at position " + level.position); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i += 1) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/states/OptionsMenuState.js b/21_StoringRecallingGameData/PenguinPairs4/states/OptionsMenuState.js new file mode 100644 index 0000000..f4ea732 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/states/OptionsMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function OptionsMenuState(layer) { + GameObjectList.call(this, layer); + + var background = new SpriteGameObject(sprites.background_options, ID.layer_background); + this.add(background); + + var onOffLabel = new Label("Arial", "60px", ID.layer_overlays); + onOffLabel.text = "Hints"; + onOffLabel.position = new Vector2(150, 360); + onOffLabel.color = Color.darkBlue; + this.add(onOffLabel); + + this.onOffButton = new OnOffButton(sprites.button_offon, ID.layer_overlays); + this.onOffButton.position = new Vector2(650, 340); + this.onOffButton.on = GameSettings.hints; + this.add(this.onOffButton); + + var musicText = new Label("Arial", "60px", ID.layer_overlays); + musicText.text = "Music volume"; + musicText.position = new Vector2(150, 490); + musicText.color = Color.darkBlue; + this.add(musicText); + + this.musicSlider = new Slider(sprites.slider_bar, sprites.slider_button, ID.layer_overlays); + this.musicSlider.position = new Vector2(650, 500); + this.musicSlider.value = sounds.music.volume; + this.add(this.musicSlider); + + this.backButton = new Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +OptionsMenuState.prototype = Object.create(GameObjectList.prototype); + +OptionsMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; + +OptionsMenuState.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + sounds.music.volume = this.musicSlider.value; + GameSettings.hints = this.onOffButton.on; +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/states/PlayingState.js b/21_StoringRecallingGameData/PenguinPairs4/states/PlayingState.js new file mode 100644 index 0000000..22d0b33 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/states/PlayingState.js @@ -0,0 +1,58 @@ +"use strict"; + +function PlayingState() { + IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= window.LEVELS.length) + return; + this.currentLevelIndex = levelIndex; +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel++) + this.levels.push(new Level(currLevel)); +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.penguinPairsLevels) { + window.LEVELS = JSON.parse(localStorage.penguinPairsLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.penguinPairsLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/states/TitleMenuState.js b/21_StoringRecallingGameData/PenguinPairs4/states/TitleMenuState.js new file mode 100644 index 0000000..a4eee1b --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/states/TitleMenuState.js @@ -0,0 +1,32 @@ +"use strict"; + +function TitleMenuState(layer) { + GameObjectList.call(this, layer); + + var background = new SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(background); + + this.playButton = new Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new Vector2(415, 540); + this.add(this.playButton); + + this.optionsButton = new Button(sprites.button_options, ID.layer_overlays); + this.optionsButton.position = new Vector2(415, 650); + this.add(this.optionsButton); + + this.helpButton = new Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new Vector2(415, 760); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + GameStateManager.switchTo(ID.game_state_help); + else if (this.optionsButton.pressed) + GameStateManager.switchTo(ID.game_state_options); +}; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/system/Color.js b/21_StoringRecallingGameData/PenguinPairs4/system/Color.js new file mode 100644 index 0000000..19ee82a --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + + var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" + }; \ No newline at end of file diff --git a/21_StoringRecallingGameData/PenguinPairs4/system/Keys.js b/21_StoringRecallingGameData/PenguinPairs4/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/21_StoringRecallingGameData/PenguinPairs4/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/22_PairingPenguins/LAB.min.js b/22_PairingPenguins/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/22_PairingPenguins/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/Canvas2D.js b/22_PairingPenguins/PenguinPairs5/Canvas2D.js new file mode 100644 index 0000000..8df997b --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/Canvas2D.js @@ -0,0 +1,149 @@ +"use strict"; + +function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = Vector2.zero; +} + +Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + +Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new Vector2(this._canvas.width / Game.size.x, + this._canvas.height / Game.size.y); + } + }); + +Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); +}; + +Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); +}; + +Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = Canvas2D._canvas; + var gameArea = Canvas2D._div; + var widthToHeight = Game.size.x / Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + Canvas2D._canvasOffset = offset; +}; + +Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect, mirror) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + if (mirror) { + this._canvasContext.scale(scale * canvasScale.x * -1, scale * canvasScale.y); + this._canvasContext.translate(-position.x - sourceRect.width, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + sourceRect.width - origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + else { + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : Vector2.zero; + color = typeof color !== 'undefined' ? color : Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); +}; + +Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); +}; + +var Canvas2D = new Canvas2D_Singleton(); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/Game.js b/22_PairingPenguins/PenguinPairs5/Game.js new file mode 100644 index 0000000..65f776e --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/Game.js @@ -0,0 +1,85 @@ +"use strict"; + +var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; +} + +Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new Rectangle(0, 0, this._size.x, this._size.y); + } + }); + +Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new Vector2(x, y); + + Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); +}; + +Game_Singleton.prototype.initialize = function () { +}; + +Game_Singleton.prototype.loadAssets = function () { +}; + +Game_Singleton.prototype.assetLoadingLoop = function () { + Canvas2D.clear(); + Canvas2D.drawText(Math.round((Game._totalSprites - Game._spritesStillLoading) / + Game._totalSprites * 100) + "%"); + + if (Game._spritesStillLoading > 0) + requestAnimationFrame(Game.assetLoadingLoop); + else { + Game.initialize(); + requestAnimationFrame(Game.mainLoop); + } +}; + +Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + Game._totalTime += delta; + + GameStateManager.handleInput(delta); + GameStateManager.update(delta); + Canvas2D.clear(); + GameStateManager.draw(); + + Keyboard.reset(); + Mouse.reset(); + Touch.reset(); + + requestAnimationFrame(Game.mainLoop); +}; + +var Game = new Game_Singleton(); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/GameStateManager.js b/22_PairingPenguins/PenguinPairs5/GameStateManager.js new file mode 100644 index 0000000..df07f76 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/GameStateManager.js @@ -0,0 +1,47 @@ +"use strict"; + +function GameStateManager_Singleton() { + this._gameStates = []; + this._currentGameState = null; +} + +GameStateManager_Singleton.prototype.add = function (gamestate) { + this._gameStates.push(gamestate); + this._currentGameState = gamestate; + return this._gameStates.length - 1; +}; + +GameStateManager_Singleton.prototype.get = function (id) { + if (id < 0 || id >= this._gameStates.length) + return null; + else + return this._gameStates[id]; +}; + +GameStateManager_Singleton.prototype.switchTo = function (id) { + if (id < 0 || id >= this._gameStates.length) + return; + this._currentGameState = this._gameStates[id]; +}; + +GameStateManager_Singleton.prototype.handleInput = function (delta) { + if (this._currentGameState != null) + this._currentGameState.handleInput(delta); +}; + +GameStateManager_Singleton.prototype.update = function (delta) { + if (this._currentGameState != null) + this._currentGameState.update(delta); +}; + +GameStateManager_Singleton.prototype.draw = function () { + if (this._currentGameState != null) + this._currentGameState.draw(); +}; + +GameStateManager_Singleton.prototype.reset = function () { + if (this._currentGameState != null) + this._currentGameState.reset(); +}; + +var GameStateManager = new GameStateManager_Singleton(); diff --git a/22_PairingPenguins/PenguinPairs5/IGameLoopObject.js b/22_PairingPenguins/PenguinPairs5/IGameLoopObject.js new file mode 100644 index 0000000..908b40d --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/IGameLoopObject.js @@ -0,0 +1,19 @@ +"use strict"; + +function IGameLoopObject() { +} + +IGameLoopObject.prototype.initialize = function () { +}; + +IGameLoopObject.prototype.handleInput = function (delta) { +}; + +IGameLoopObject.prototype.update = function (delta) { +}; + +IGameLoopObject.prototype.draw = function () { +}; + +IGameLoopObject.prototype.reset = function () { +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/LAB.min.js b/22_PairingPenguins/PenguinPairs5/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/Math.js b/22_PairingPenguins/PenguinPairs5/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/PenguinPairs.html b/22_PairingPenguins/PenguinPairs5/PenguinPairs.html new file mode 100644 index 0000000..a02c579 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/PenguinPairs.html @@ -0,0 +1,64 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/PenguinPairs.js b/22_PairingPenguins/PenguinPairs5/PenguinPairs.js new file mode 100644 index 0000000..f94035d --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/PenguinPairs.js @@ -0,0 +1,94 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +var GameSettings = { + hints: true +}; + +Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.arrow = loadSprite("spr_arrow@4.png"); + sprites.arrow_hover = loadSprite("spr_arrow_hover@4.png"); + sprites.arrow_hint = loadSprite("spr_arrow_hint@4.png"); + sprites.background_title = loadSprite("spr_background_title.jpg"); + sprites.background_help = loadSprite("spr_background_help.jpg"); + sprites.background_level = loadSprite("spr_background_level.jpg"); + sprites.background_levelselect = loadSprite("spr_background_levelselect.jpg"); + sprites.background_options = loadSprite("spr_background_options.jpg"); + sprites.button_back = loadSprite("spr_button_back.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.button_hint = loadSprite("spr_button_hint.png"); + sprites.button_offon = loadSprite("spr_button_offon@2.png"); + sprites.button_options = loadSprite("spr_button_options.jpg"); + sprites.button_play = loadSprite("spr_button_play.jpg"); + sprites.button_quit = loadSprite("spr_button_quit.png"); + sprites.button_retry = loadSprite("spr_button_retry.png"); + sprites.field = loadSprite("spr_field@2.png"); + sprites.frame_goal = loadSprite("spr_frame_goal.jpg"); + sprites.help = loadSprite("spr_help.jpg"); + if (Touch.isTouchDevice) + sprites.level_finished = loadSprite("spr_level_finished_tap.png"); + else + sprites.level_finished = loadSprite("spr_level_finished_click.png"); + sprites.level_unsolved = loadSprite("spr_level_unsolved.png"); + sprites.level_solved = loadSprite("spr_level_solved@6.png"); + sprites.level_locked = loadSprite("spr_lock.png"); + sprites.penguin = loadSprite("spr_penguin@8.png"); + sprites.penguin_boxed = loadSprite("spr_penguin_boxed@8.png"); + sprites.penguin_empty = loadSprite("spr_penguin_empty.png"); + sprites.penguin_pairs = loadSprite("spr_penguin_pairs@8.png"); + sprites.shark = loadSprite("spr_shark.png"); + sprites.slider_bar = loadSprite("spr_slider_bar.jpg"); + sprites.slider_button = loadSprite("spr_slider_button.jpg"); + sprites.wall = loadSprite("spr_wall.png"); + + sounds.music = loadSound("snd_music", true); + sounds.eat = loadSound("snd_eat"); + sounds.lost = loadSound("snd_lost"); + sounds.won = loadSound("snd_won"); + sounds.pair = loadSound("snd_pair"); +}; + +Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.tiles = 1; + ID.pairList = 2; + ID.animalSelector = 3; + + // create the different game modes + ID.game_state_title = GameStateManager.add(new TitleMenuState()); + ID.game_state_help = GameStateManager.add(new HelpState()); + ID.game_state_options = GameStateManager.add(new OptionsMenuState()); + ID.game_state_playing = GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = GameStateManager.add(new LevelMenuState()); + + // set the current game mode + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/Sound.js b/22_PairingPenguins/PenguinPairs5/Sound.js new file mode 100644 index 0000000..5d9671b --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/Sound.js @@ -0,0 +1,35 @@ +"use strict"; + +function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; +} + +Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + +Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/SpriteSheet.js b/22_PairingPenguins/PenguinPairs5/SpriteSheet.js new file mode 100644 index 0000000..7486243 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/SpriteSheet.js @@ -0,0 +1,79 @@ +"use strict"; + +function SpriteSheet(imageName) { + console.log("Loading sprite: " + imageName); + Game._spritesStillLoading += 1; + Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._sheetColumns = 1; + this._sheetRows = 1; + this._collisionMask = null; + + var sprite = this; + this._image.onload = function () { + Game._spritesStillLoading -= 1; + }; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split("/")[0].split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; +} + +Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + +Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + +Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + +SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/game-layout.css b/22_PairingPenguins/PenguinPairs5/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/Animal.js b/22_PairingPenguins/PenguinPairs5/gameobjects/Animal.js new file mode 100644 index 0000000..1dc8a7e --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/Animal.js @@ -0,0 +1,117 @@ +"use strict"; + +function Animal(color, sprite, layer) { + SpriteGameObject.call(this, sprite, layer); + + this.initialPosition = Vector2.zero; + this.boxed = (color === color.toUpperCase()); + this.initialEmptyBox = this.boxed && color.toLowerCase() === "x"; + this.sheetIndex = "brgyopmx".indexOf(color.toLowerCase()); +} + +Animal.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(Animal.prototype, "currentBlock", + { + get: function () { + var tileField = this.root.find(ID.tiles); + var p = new Vector2(Math.floor(this.position.x / tileField.cellWidth), + Math.floor(this.position.y / tileField.cellHeight)); + if (this.velocity.x > 0) + p.x++; + if (this.velocity.y > 0) + p.y++; + return p; + } + }); + +Animal.prototype.reset = function () { + this.position = this.initialPosition.copy(); + this.velocity = Vector2.zero; + this.visible = true; + if (this.initialEmptyBox) + this.sheetIndex = 7; +}; + +Animal.prototype.handleInput = function (delta) { + if (!this.visible || this.boxed || !this.velocity.isZero) + return; + if (Touch.isTouchDevice) { + if (!Touch.containsTouchPress(this.boundingBox)) + return; + } else { + if (!Mouse.left.pressed || !this.boundingBox.contains(Mouse.position)) + return; + } + var animalSelector = this.root.find(ID.animalSelector); + if (animalSelector.visible) + return; + + animalSelector.position = this.position; + animalSelector.visible = true; + animalSelector.selectedAnimal = this; + Mouse.reset(); + Touch.reset(); +}; + +Animal.prototype.update = function (delta) { + SpriteGameObject.prototype.update.call(this, delta); + if (!this.visible || this.velocity.isZero) + return; + + var target = this.currentBlock; + var tileField = this.root.find(ID.tiles); + + if (tileField.getTileType(target) === TileType.background) { + this.visible = false; + this.velocity = Vector2.zero; + } + else if (tileField.getTileType(target) === TileType.wall) + this.stopMoving(); + else { + var s = this.root.findSharkAtPosition(target); + if (s !== null && s.visible) { + s.visible = false; + this.visible = false; + this.stopMoving(); + } + + var a = this.root.findAnimalAtPosition(target); + if (a === null || !a.visible) + return; + if (a.isSeal()) + this.stopMoving(); + else if (a.isEmptyBox()) { + this.visible = false; + a.sheetIndex = this.sheetIndex; + } + else if (a.sheetIndex === this.sheetIndex || this.isMulticolor() || a.isMulticolor()) { + a.visible = false; + this.visible = false; + this.root.find(ID.pairList).addPair(this.sheetIndex); + } + else + this.stopMoving(); + } +}; + +Animal.prototype.stopMoving = function () { + var tileField = this.root.find(ID.tiles); + this.velocity.normalize(); + var currBlock = this.currentBlock; + this.position = new Vector2(Math.floor(currBlock.x - this.velocity.x) * tileField.cellWidth, + Math.floor(currBlock.y - this.velocity.y) * tileField.cellHeight); + this.velocity = Vector2.zero; +}; + +Animal.prototype.isSeal = function () { + return this.sheetIndex === 7 && !this.boxed; +}; + +Animal.prototype.isMulticolor = function () { + return this.sheetIndex === 6 && !this.boxed; +}; + +Animal.prototype.isEmptyBox = function () { + return this.sheetIndex === 7 && this.boxed; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/AnimalSelector.js b/22_PairingPenguins/PenguinPairs5/gameobjects/AnimalSelector.js new file mode 100644 index 0000000..02831dd --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/AnimalSelector.js @@ -0,0 +1,44 @@ +"use strict"; + +function AnimalSelector(layer, id) { + GameObjectList.call(this, layer, id); + this._arrowright = new Arrow(0); + this._arrowright.position = new Vector2(this._arrowright.width, 0); + this.add(this._arrowright); + this._arrowup = new Arrow(1); + this._arrowup.position = new Vector2(0, -this._arrowright.height); + this.add(this._arrowup); + this._arrowleft = new Arrow(2); + this._arrowleft.position = new Vector2(-this._arrowright.width, 0); + this.add(this._arrowleft); + this._arrowdown = new Arrow(3); + this._arrowdown.position = new Vector2(0, this._arrowright.height); + this.add(this._arrowdown); + this.selectedAnimal = null; + this.visible = false; +} + +AnimalSelector.prototype = Object.create(GameObjectList.prototype); + +AnimalSelector.prototype.handleInput = function (delta) { + if (!this.visible) + return; + GameObjectList.prototype.handleInput.call(this, delta); + + var animalVelocity = Vector2.zero; + if (this._arrowdown.pressed) + animalVelocity.y = 1; + else if (this._arrowup.pressed) + animalVelocity.y = -1; + else if (this._arrowleft.pressed) + animalVelocity.x = -1; + else if (this._arrowright.pressed) + animalVelocity.x = 1; + animalVelocity.multiplyWith(300); + if (Mouse.left.pressed || Touch.containsTouchPress(Game.screenRect)) + this.visible = false; + + if (this.selectedAnimal === null || animalVelocity.isZero) + return; + this.selectedAnimal.velocity = animalVelocity; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/Arrow.js b/22_PairingPenguins/PenguinPairs5/gameobjects/Arrow.js new file mode 100644 index 0000000..7e55c6d --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/Arrow.js @@ -0,0 +1,22 @@ +"use strict"; + +function Arrow(sheetIndex, layer, id) { + Button.call(this, sprites.arrow, layer, id); + this.sheetIndex = sheetIndex; + this.arrowHover = new SpriteGameObject(sprites.arrow_hover); + this.arrowHover.sheetIndex = sheetIndex; + this.arrowHover.visible = false; + this.arrowHover.parent = this; +} + +Arrow.prototype = Object.create(Button.prototype); + +Arrow.prototype.handleInput = function (delta) { + Button.prototype.handleInput.call(this, delta); + this.arrowHover.visible = !Touch.isTouchDevice && this.boundingBox.contains(Mouse.position); +}; + +Arrow.prototype.draw = function () { + Button.prototype.draw.call(this); + this.arrowHover.draw(); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/GameObject.js b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObject.js new file mode 100644 index 0000000..9d3151e --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObject.js @@ -0,0 +1,56 @@ +"use strict"; + +function GameObject(layer, id) { + IGameLoopObject.call(this); + + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = Vector2.zero; + this.velocity = Vector2.zero; + this._visible = true; +} + +GameObject.prototype = Object.create(IGameLoopObject.prototype); + +Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + +Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + +Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + +GameObject.prototype.reset = function () { + this._visible = true; +}; + +GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectGrid.js b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..a25f664 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectGrid.js @@ -0,0 +1,57 @@ +"use strict"; + +function GameObjectGrid(rows, columns, layer, id) { + GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; +} + +GameObjectGrid.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } +}); + +Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } +}); + +GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new Vector2(col * this.cellWidth, row * this.cellHeight); +}; + +GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; +}; + +GameObjectGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new Vector2(col * this.cellWidth, row * this.cellHeight); + } + return Vector2.zero; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectList.js b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectList.js new file mode 100644 index 0000000..b153446 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/GameObjectList.js @@ -0,0 +1,79 @@ +"use strict"; + +GameObjectList.prototype = Object.create(GameObject.prototype); + +function GameObjectList(layer, id) { + GameObject.call(this, layer, id); + + this._gameObjects = []; +} + +Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } +}); + +GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); +}; + +GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } +}; + +GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; +}; + +GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; +}; + +GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; +}; + +GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); +}; + +GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); +}; + +GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); +}; + +GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/Level.js b/22_PairingPenguins/PenguinPairs5/gameobjects/Level.js new file mode 100644 index 0000000..73ca34c --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/Level.js @@ -0,0 +1,141 @@ +"use strict"; + +function Level(levelIndex) { + GameObjectList.call(this); + + var levelData = window.LEVELS[levelIndex]; + + this.animals = []; + this.sharks = []; + this.levelIndex = levelIndex; + this.firstMoveMade = false; + + this.add(new SpriteGameObject(sprites.background_level, ID.layer_background)); + + this.hintButton = new Button(sprites.button_hint, ID.layer_overlays); + this.hintButton.position = new Vector2(916, 20); + this.add(this.hintButton); + + this.retryButton = new Button(sprites.button_retry, ID.layer_overlays); + this.retryButton.position = new Vector2(916, 20); + this.retryButton.visible = false; + this.add(this.retryButton); + + this.quitButton = new Button(sprites.button_quit, ID.layer_overlays); + this.quitButton.position = new Vector2(1058, 20); + this.add(this.quitButton); + + var width = levelData.tiles[0].length; + var height = levelData.tiles.length; + + var playingField = new GameObjectList(ID.layer_objects); + playingField.position = new Vector2((Game.size.x - width * 73) / 2, 100); + this.add(playingField); + + var tileField = new TileField(height, width, ID.layer_objects, ID.tiles); + tileField.cellHeight = 72; + tileField.cellWidth = 73; + for (var row = 0; row < height; row++) { + for (var col = 0; col < width; col++) { + var t = null; + + switch (levelData.tiles[row][col]) { + case '.' : + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + break; + case ' ': + t = new Tile(sprites.wall, ID.layer_objects); + t.type = TileType.background; + tileField.addAt(t, col, row); + break; + case 'r': + case 'b': + case 'g': + case 'o': + case 'p': + case 'y': + case 'm': + case 'x': + case 'R': + case 'B': + case 'G': + case 'O': + case 'P': + case 'Y': + case 'M': + case 'X': + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var animalSprite = sprites.penguin; + if (levelData.tiles[row][col] === levelData.tiles[row][col].toUpperCase()) + animalSprite = sprites.penguin_boxed; + var p = new Animal(levelData.tiles[row][col], animalSprite, ID.layer_objects_1); + p.position = t.position.copy(); + p.initialPosition = t.position.copy(); + playingField.add(p); + this.animals.push(p); + break; + case '@': + t = new Tile(sprites.field); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var s = new SpriteGameObject(sprites.shark, ID.layer_objects_1); + s.position = t.position.copy(); + playingField.add(s); + this.sharks.push(s); + break; + default : + t = new Tile(sprites.wall, ID.layer_objects); + tileField.addAt(t, col, row); + t.type = TileType.wall; + break; + } + } + } + playingField.add(tileField); + + playingField.add(new AnimalSelector(ID.layer_objects_2, ID.animalSelector)); + + var pairList = new PairList(levelData.nrPairs, ID.layer_overlays, ID.pairList); + pairList.position = new Vector2(20, 15); + this.add(pairList); + + var helpField = new SpriteGameObject(sprites.help, ID.layer_overlays); + helpField.position = new Vector2(helpField.screenCenterX, 780); + this.add(helpField); + var helpLabel = new Label("Arial", "24px", ID.layer_overlays_1); + helpLabel.text = levelData.hint; + helpLabel.color = Color.darkBlue; + helpLabel.position = new Vector2(helpLabel.screenCenterX, 780 + (helpField.height - helpLabel.height) / 2); + this.add(helpLabel); +} + +Level.prototype = Object.create(GameObjectList.prototype); + +Level.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.retryButton.pressed) + this.reset(); + else if (this.quitButton.pressed) { + GameStateManager.switchTo(ID.game_state_levelselect); + } +}; + +Level.prototype.findAnimalAtPosition = function (pos) { + for (var i = 0, j = this.animals.length; i < j; ++i) + if (this.animals[i].currentBlock.equals(pos) && this.animals[i].velocity.isZero) + return this.animals[i]; + return null; +}; + +Level.prototype.findSharkAtPosition = function (pos) { + var tileField = this.find(ID.tiles); + var sharkPos = new Vector2(pos.x * tileField.cellWidth, pos.y * tileField.cellHeight); + for (var i = 0, l = this.sharks.length; i < l; ++i) + if (this.sharks[i].position.equals(sharkPos)) + return this.sharks[i]; + return null; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/PairList.js b/22_PairingPenguins/PenguinPairs5/gameobjects/PairList.js new file mode 100644 index 0000000..262280c --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/PairList.js @@ -0,0 +1,45 @@ +"use strict"; + +function PairList(nrPairs, layer, id) { + SpriteGameObject.call(this, sprites.frame_goal, layer, id); + this._pairs = []; + for (var i = 0; i < nrPairs; i++) + this._pairs.push(7); + this._pairSprite = new SpriteGameObject(sprites.penguin_pairs); + this._pairSprite.parent = this; +} +PairList.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(PairList.prototype, "completed", + { + get: function () { + for (var i = 0, l = this._pairs.length; i < l; i++) + if (this._pairs[i] === 7) + return false; + return true; + } + }); + +PairList.prototype.addPair = function (index) { + var i = 0; + while (i < this._pairs.length && this._pairs[i] !== 7) + i++; + if (i < this._pairs.length) + this._pairs[i] = index; +}; + +PairList.prototype.reset = function () { + for (var i = 0, l = this._pairs.length; i < l; i++) + this._pairs[i] = 7; +}; + +PairList.prototype.draw = function () { + SpriteGameObject.prototype.draw.call(this); + if (!this.visible) + return; + for (var i = 0, l = this._pairs.length; i < l; i++) { + this._pairSprite.position = new Vector2(110 + i * this.height, 8); + this._pairSprite.sheetIndex = this._pairs[i]; + this._pairSprite.draw(); + } +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/SpriteGameObject.js b/22_PairingPenguins/PenguinPairs5/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..ad72ce1 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/SpriteGameObject.js @@ -0,0 +1,86 @@ +"use strict"; + +function SpriteGameObject(sprite, layer, id) { + GameObject.call(this, layer, id); + + this.sprite = sprite; + + this.origin = Vector2.zero; + this.mirror = false; + this._sheetIndex = 0; +} + +SpriteGameObject.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0) + this._sheetIndex = value % this.sprite.nrSheetElements; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + +SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex, this.mirror); +}; diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/Tile.js b/22_PairingPenguins/PenguinPairs5/gameobjects/Tile.js new file mode 100644 index 0000000..3b19fba --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/Tile.js @@ -0,0 +1,20 @@ +"use strict"; + +var TileType = { + normal: 0, + background: 1, + wall: 2 +}; + +function Tile(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); + this.type = TileType.normal; +} + +Tile.prototype = Object.create(SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type === TileType.background) + return; + SpriteGameObject.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gameobjects/TileField.js b/22_PairingPenguins/PenguinPairs5/gameobjects/TileField.js new file mode 100644 index 0000000..ddf33f8 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gameobjects/TileField.js @@ -0,0 +1,17 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (pos) { + if (this.isOutsideField(pos)) + return TileType.background; + return this.at(pos.x, pos.y).type; +}; + +TileField.prototype.isOutsideField = function (pos) { + return (pos.x < 0 || pos.x >= this.columns || pos.y < 0 || pos.y >= this.rows); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/geom/Rectangle.js b/22_PairingPenguins/PenguinPairs5/geom/Rectangle.js new file mode 100644 index 0000000..b3b7f74 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/geom/Rectangle.js @@ -0,0 +1,72 @@ +"use strict"; + +function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; +} + +Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + +Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + +Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + +Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + +Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + +Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new Vector2(this.x, this.y); + } + }); + +Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new Vector2(this.width, this.height); + } + }); + +Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); +}; + +Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); +}; + +Rectangle.prototype.draw = function () { + Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/geom/Vector2.js b/22_PairingPenguins/PenguinPairs5/geom/Vector2.js new file mode 100644 index 0000000..04bf6a8 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/geom/Vector2.js @@ -0,0 +1,114 @@ +"use strict"; + +function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; +} + +Object.defineProperty(Vector2, "zero", + { + get: function () { + return new Vector2(); + } + }); + +Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + +Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + +Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; +}; + +Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); +}; + +Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; +}; + +Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); +}; + +Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; +}; + +Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); +}; + +Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; +}; + +Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); +}; + +Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; +}; + +Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); +}; + +Vector2.prototype.copy = function () { + return new Vector2(this.x, this.y); +}; + +Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gui/Button.js b/22_PairingPenguins/PenguinPairs5/gui/Button.js new file mode 100644 index 0000000..d1d9d63 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gui/Button.js @@ -0,0 +1,18 @@ +"use strict"; + +function Button(sprite, layer, id) { + SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; +} + +Button.prototype = Object.create(SpriteGameObject.prototype); + +Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (Touch.containsTouchPress(boundingBox) || + Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (Touch.containsTouch(boundingBox) || + Mouse.containsMouseDown(boundingBox)); +}; diff --git a/22_PairingPenguins/PenguinPairs5/gui/Label.js b/22_PairingPenguins/PenguinPairs5/gui/Label.js new file mode 100644 index 0000000..41c5af6 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gui/Label.js @@ -0,0 +1,92 @@ +"use strict"; + +function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; +} + +function Label(fontname, fontsize, layer, id) { + GameObject.call(this, layer, id); + + this.color = Color.black; + this.origin = Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = Vector2.zero; +} + +Label.prototype = Object.create(GameObject.prototype); + +Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + +Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + +Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (Game.size.x - this.width) / 2 + this.origin.x; + } + }); + +Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (Game.size.y - this.height) / 2 + this.origin.y; + } + }); + +Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + +Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + +Label.prototype.draw = function () { + if (!this.visible) + return; + Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); +}; diff --git a/22_PairingPenguins/PenguinPairs5/gui/LevelButton.js b/22_PairingPenguins/PenguinPairs5/gui/LevelButton.js new file mode 100644 index 0000000..4ba1fcb --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gui/LevelButton.js @@ -0,0 +1,59 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new SpriteGameObject(sprites.level_solved, ID.layer_overlays); + this._levelUnsolved = new SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + + this.add(this._levelUnsolved); + + this._levelLocked = new SpriteGameObject(sprites.level_locked, ID.layer_overlays_2); + this.add(this._levelLocked); + + var textLabel = new Label("Arial", "20px", ID.layer_overlays_1); + textLabel.text = levelIndex + 1; + textLabel.position = new Vector2(this._levelSolved.width - textLabel.width, + this._levelSolved.height - textLabel.height + 50).divideBy(2); + textLabel.color = Color.black; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (Touch.isTouchDevice) + this.pressed = this.visible && Touch.containsTouchPress(this._levelLocked.boundingBox); + else + this.pressed = this.visible && Mouse.left.pressed && + this._levelLocked.boundingBox.contains(Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gui/OnOffButton.js b/22_PairingPenguins/PenguinPairs5/gui/OnOffButton.js new file mode 100644 index 0000000..78d25bb --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gui/OnOffButton.js @@ -0,0 +1,28 @@ +"use strict"; + +function OnOffButton(sprite, layer) { + SpriteGameObject.call(this, sprite, layer); +} + +OnOffButton.prototype = Object.create(SpriteGameObject.prototype); + +Object.defineProperty(OnOffButton.prototype, "on", + { + get: function () { + return this.sheetIndex === 1; + }, + set: function (value) { + if (value) + this.sheetIndex = 1; + else + this.sheetIndex = 0; + } + }); + +OnOffButton.prototype.handleInput = function (delta) { + if (!this.visible) + return; + if (Touch.containsTouchPress(this.boundingBox) || + Mouse.containsMousePress(this.boundingBox)) + this.sheetIndex = (this.sheetIndex + 1) % 2; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/gui/Slider.js b/22_PairingPenguins/PenguinPairs5/gui/Slider.js new file mode 100644 index 0000000..20dca1f --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/gui/Slider.js @@ -0,0 +1,61 @@ +"use strict"; + +function Slider(sliderback, sliderfront, layer) { + GameObjectList.call(this, layer); + this.dragging = false; + this.draggingId = -1; + this.leftmargin = 5; + this.rightmargin = 7; + + this.back = new SpriteGameObject(sliderback); + this.front = new SpriteGameObject(sliderfront, 1); + this.front.position = new Vector2(this.leftmargin, 8); + this.add(this.back); + this.add(this.front); +} + +Slider.prototype = Object.create(GameObjectList.prototype); + +Object.defineProperty(Slider.prototype, "value", + { + get: function () { + return (this.front.position.x - this.back.position.x - this.leftmargin) / + (this.back.width - this.front.width - this.leftmargin - this.rightmargin); + }, + set: function (value) { + var newxpos = value * (this.back.width - this.front.width - this.leftmargin - this.rightmargin) + + this.back.position.x + this.leftmargin; + this.front.position = new Vector2(newxpos, this.front.position.y); + } + }); + +Slider.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (Mouse.left.down || Touch.isTouching) { + if (Touch.isTouchDevice) { + if (Touch.containsTouch(this.back.boundingBox)) + this.draggingId = Touch.getIndexInRect((this.back.boundingBox)); + if (Touch.containsTouch(this.back.boundingBox) || this.dragging) { + var touchPos = Touch.getPosition(this.draggingId); + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(touchPos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } else { + var mousePos = Mouse.position; + if (this.back.boundingBox.contains(mousePos) || this.dragging) { + this.front.position = new Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(mousePos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } + } + else { + this.dragging = false; + this.draggingId = -1; + } +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/input/ButtonState.js b/22_PairingPenguins/PenguinPairs5/input/ButtonState.js new file mode 100644 index 0000000..e3094f4 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/input/ButtonState.js @@ -0,0 +1,6 @@ +"use strict"; + +function ButtonState() { + this.down = false; + this.pressed = false; +} \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/input/Keyboard.js b/22_PairingPenguins/PenguinPairs5/input/Keyboard.js new file mode 100644 index 0000000..8411e4b --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/input/Keyboard.js @@ -0,0 +1,40 @@ +"use strict"; + +function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!Keyboard._keyStates[code].down) + Keyboard._keyStates[code].pressed = true; + Keyboard._keyStates[code].down = true; +} + +function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + Keyboard._keyStates[code].down = false; +} + +function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; +} + +Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; +}; + +Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; +}; + +Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; +}; + +var Keyboard = new Keyboard_Singleton(); diff --git a/22_PairingPenguins/PenguinPairs5/input/Mouse.js b/22_PairingPenguins/PenguinPairs5/input/Mouse.js new file mode 100644 index 0000000..662f762 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/input/Mouse.js @@ -0,0 +1,92 @@ +"use strict"; + +function handleMouseMove(evt) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + Mouse._position = new Vector2(mx, my); +} + +function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!Mouse._left.down) + Mouse._left.pressed = true; + Mouse._left.down = true; + } else if (evt.which === 2) { + if (!Mouse._middle.down) + Mouse._middle.pressed = true; + Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!Mouse._right.down) + Mouse._right.pressed = true; + Mouse._right.down = true; + } +} + +function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + Mouse._left.down = false; + else if (evt.which === 2) + Mouse._middle.down = false; + else if (evt.which === 3) + Mouse._right.down = false; +} + +function Mouse_Singleton() { + this._position = Vector2.zero; + this._left = new ButtonState(); + this._middle = new ButtonState(); + this._right = new ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; +} + +Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + +Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + +Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; +}; + +Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); +}; + +Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); +}; + +var Mouse = new Mouse_Singleton(); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/input/Touch.js b/22_PairingPenguins/PenguinPairs5/input/Touch.js new file mode 100644 index 0000000..7adb4ce --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/input/Touch.js @@ -0,0 +1,108 @@ +"use strict"; + +function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + Touch._touches.push(touches[i]); + Touch._touchPresses.push(true); + } +} + +function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1, touches[i]); + } +} + +function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = Touch.getTouchIndexFromId(touches[i].identifier); + Touch._touches.splice(id, 1); + Touch._touchPresses.splice(id, 1); + } +} + +function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); +} + +Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + +Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + +Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; +}; + +Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; +}; + +Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = Canvas2D.scale; + var canvasOffset = Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new Vector2(mx, my); +}; + +Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return Vector2.zero; +}; + +Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; +}; + +Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; +}; + +var Touch = new Touch_Singleton(); \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/states/HelpState.js b/22_PairingPenguins/PenguinPairs5/states/HelpState.js new file mode 100644 index 0000000..60f3ca6 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/states/HelpState.js @@ -0,0 +1,22 @@ +"use strict"; + +function HelpState(layer) { + GameObjectList.call(this, layer); + + // the background + var background = new SpriteGameObject(sprites.background_help); + this.add(background); + + // add a back button + this.backButton = new Button(sprites.button_back, 100); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +HelpState.prototype = Object.create(GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/states/LevelMenuState.js b/22_PairingPenguins/PenguinPairs5/states/LevelMenuState.js new file mode 100644 index 0000000..f8d2837 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/states/LevelMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function LevelMenuState(layer) { + GameObjectList.call(this, layer); + + this.background = new SpriteGameObject(sprites.background_levelselect, ID.layer_background); + this.add(this.background); + + this.back = new Button(sprites.button_back, ID.layer_overlays); + this.back.position = new Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0; i < window.LEVELS.length; i += 1) { + var row = Math.floor(i / 5); + var column = i % 5; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new Vector2(column * (level.width + 30) + 155, + row * (level.height + 5) + 230); + //console.log("Level " + i + " at position " + level.position); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i += 1) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/states/OptionsMenuState.js b/22_PairingPenguins/PenguinPairs5/states/OptionsMenuState.js new file mode 100644 index 0000000..f4ea732 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/states/OptionsMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function OptionsMenuState(layer) { + GameObjectList.call(this, layer); + + var background = new SpriteGameObject(sprites.background_options, ID.layer_background); + this.add(background); + + var onOffLabel = new Label("Arial", "60px", ID.layer_overlays); + onOffLabel.text = "Hints"; + onOffLabel.position = new Vector2(150, 360); + onOffLabel.color = Color.darkBlue; + this.add(onOffLabel); + + this.onOffButton = new OnOffButton(sprites.button_offon, ID.layer_overlays); + this.onOffButton.position = new Vector2(650, 340); + this.onOffButton.on = GameSettings.hints; + this.add(this.onOffButton); + + var musicText = new Label("Arial", "60px", ID.layer_overlays); + musicText.text = "Music volume"; + musicText.position = new Vector2(150, 490); + musicText.color = Color.darkBlue; + this.add(musicText); + + this.musicSlider = new Slider(sprites.slider_bar, sprites.slider_button, ID.layer_overlays); + this.musicSlider.position = new Vector2(650, 500); + this.musicSlider.value = sounds.music.volume; + this.add(this.musicSlider); + + this.backButton = new Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new Vector2(415, 720); + this.add(this.backButton); +} + +OptionsMenuState.prototype = Object.create(GameObjectList.prototype); + +OptionsMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + GameStateManager.switchTo(ID.game_state_title); +}; + +OptionsMenuState.prototype.update = function (delta) { + GameObjectList.prototype.update.call(this, delta); + sounds.music.volume = this.musicSlider.value; + GameSettings.hints = this.onOffButton.on; +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/states/PlayingState.js b/22_PairingPenguins/PenguinPairs5/states/PlayingState.js new file mode 100644 index 0000000..e87ebd8 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/states/PlayingState.js @@ -0,0 +1,59 @@ +"use strict"; + +function PlayingState() { + IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= window.LEVELS.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel++) + this.levels.push(new Level(currLevel)); +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.penguinPairsLevels) { + window.LEVELS = JSON.parse(localStorage.penguinPairsLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.penguinPairsLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/states/TitleMenuState.js b/22_PairingPenguins/PenguinPairs5/states/TitleMenuState.js new file mode 100644 index 0000000..a4eee1b --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/states/TitleMenuState.js @@ -0,0 +1,32 @@ +"use strict"; + +function TitleMenuState(layer) { + GameObjectList.call(this, layer); + + var background = new SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(background); + + this.playButton = new Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new Vector2(415, 540); + this.add(this.playButton); + + this.optionsButton = new Button(sprites.button_options, ID.layer_overlays); + this.optionsButton.position = new Vector2(415, 650); + this.add(this.optionsButton); + + this.helpButton = new Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new Vector2(415, 760); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + GameStateManager.switchTo(ID.game_state_help); + else if (this.optionsButton.pressed) + GameStateManager.switchTo(ID.game_state_options); +}; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/system/Color.js b/22_PairingPenguins/PenguinPairs5/system/Color.js new file mode 100644 index 0000000..19ee82a --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/system/Color.js @@ -0,0 +1,144 @@ +"use strict"; + + var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" + }; \ No newline at end of file diff --git a/22_PairingPenguins/PenguinPairs5/system/Keys.js b/22_PairingPenguins/PenguinPairs5/system/Keys.js new file mode 100644 index 0000000..ba8c419 --- /dev/null +++ b/22_PairingPenguins/PenguinPairs5/system/Keys.js @@ -0,0 +1,47 @@ +"use strict"; + +var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 +}; \ No newline at end of file diff --git a/23_FinishingGame/LAB.min.js b/23_FinishingGame/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/23_FinishingGame/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/PenguinPairs.html b/23_FinishingGame/PenguinPairsFinal/PenguinPairs.html new file mode 100644 index 0000000..d861335 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/PenguinPairs.html @@ -0,0 +1,68 @@ + + + + + + + + + + Penguin Pairs + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/PenguinPairs.js b/23_FinishingGame/PenguinPairsFinal/PenguinPairs.js new file mode 100644 index 0000000..32be35c --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/PenguinPairs.js @@ -0,0 +1,95 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +var GameSettings = { + hints: true +}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new powerupjs.SpriteSheet("../../assets/PenguinPairs/sprites/" + sprite); + }; + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/PenguinPairs/sounds/" + sound, looping); + }; + + sprites.arrow = loadSprite("spr_arrow@4.png"); + sprites.arrow_hover = loadSprite("spr_arrow_hover@4.png"); + sprites.arrow_hint = loadSprite("spr_arrow_hint@4.png"); + sprites.background_title = loadSprite("spr_background_title.jpg"); + sprites.background_help = loadSprite("spr_background_help.jpg"); + sprites.background_level = loadSprite("spr_background_level.jpg"); + sprites.background_levelselect = loadSprite("spr_background_levelselect.jpg"); + sprites.background_options = loadSprite("spr_background_options.jpg"); + sprites.button_back = loadSprite("spr_button_back.jpg"); + sprites.button_help = loadSprite("spr_button_help.jpg"); + sprites.button_hint = loadSprite("spr_button_hint.png"); + sprites.button_offon = loadSprite("spr_button_offon@2.png"); + sprites.button_options = loadSprite("spr_button_options.jpg"); + sprites.button_play = loadSprite("spr_button_play.jpg"); + sprites.button_quit = loadSprite("spr_button_quit.png"); + sprites.button_retry = loadSprite("spr_button_retry.png"); + sprites.field = loadSprite("spr_field@2.png"); + sprites.frame_goal = loadSprite("spr_frame_goal.jpg"); + sprites.help = loadSprite("spr_help.jpg"); + if (powerupjs.Touch.isTouchDevice) + sprites.level_finished = loadSprite("spr_level_finished_tap.png"); + else + sprites.level_finished = loadSprite("spr_level_finished_click.png"); + sprites.level_unsolved = loadSprite("spr_level_unsolved.png"); + sprites.level_solved = loadSprite("spr_level_solved@6.png"); + sprites.level_locked = loadSprite("spr_lock.png"); + sprites.penguin = loadSprite("spr_penguin@8.png"); + sprites.penguin_boxed = loadSprite("spr_penguin_boxed@8.png"); + sprites.penguin_empty = loadSprite("spr_penguin_empty.png"); + sprites.penguin_pairs = loadSprite("spr_penguin_pairs@8.png"); + sprites.shark = loadSprite("spr_shark.png"); + sprites.slider_bar = loadSprite("spr_slider_bar.jpg"); + sprites.slider_button = loadSprite("spr_slider_button.jpg"); + sprites.wall = loadSprite("spr_wall.png"); + + sounds.music = loadSound("snd_music", true); + sounds.eat = loadSound("snd_eat"); + sounds.lost = loadSound("snd_lost"); + sounds.won = loadSound("snd_won"); + sounds.pair = loadSound("snd_pair"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_objects_1 = 21; + ID.layer_objects_2 = 22; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.tiles = 1; + ID.pairList = 2; + ID.animalSelector = 3; + + // create the different game modes + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_options = powerupjs.GameStateManager.add(new OptionsMenuState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + ID.game_state_levelfinished = powerupjs.GameStateManager.add(new LevelFinishedState()); + + // set the current game mode + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/game-layout.css b/23_FinishingGame/PenguinPairsFinal/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/Animal.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/Animal.js new file mode 100644 index 0000000..95f5e59 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/Animal.js @@ -0,0 +1,120 @@ +"use strict"; + +function Animal(color, sprite, layer) { + powerupjs.SpriteGameObject.call(this, sprite, layer); + + this.initialPosition = powerupjs.Vector2.zero; + this.boxed = (color === color.toUpperCase()); + this.initialEmptyBox = this.boxed && color.toLowerCase() === "x"; + this.sheetIndex = "brgyopmx".indexOf(color.toLowerCase()); +} + +Animal.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Object.defineProperty(Animal.prototype, "currentBlock", + { + get: function () { + var tileField = this.root.find(ID.tiles); + var p = new powerupjs.Vector2(Math.floor(this.position.x / tileField.cellWidth), + Math.floor(this.position.y / tileField.cellHeight)); + if (this.velocity.x > 0) + p.x++; + if (this.velocity.y > 0) + p.y++; + return p; + } + }); + +Animal.prototype.reset = function () { + this.position = this.initialPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + this.visible = true; + if (this.initialEmptyBox) + this.sheetIndex = 7; +}; + +Animal.prototype.handleInput = function (delta) { + if (!this.visible || this.boxed || this.velocity.x != 0 || this.velocity.y != 0) + return; + if (powerupjs.Touch.isTouchDevice) { + if (!powerupjs.Touch.containsTouchPress(this.boundingBox)) + return; + } else { + if (!powerupjs.Mouse.left.pressed || !this.boundingBox.contains(powerupjs.Mouse.position)) + return; + } + var animalSelector = this.root.find(ID.animalSelector); + if (animalSelector.visible) + return; + + animalSelector.position = this.position; + animalSelector.visible = true; + animalSelector.selectedAnimal = this; + powerupjs.Mouse.reset(); + powerupjs.Touch.reset(); +}; + +Animal.prototype.update = function (delta) { + powerupjs.SpriteGameObject.prototype.update.call(this, delta); + if (!this.visible || this.velocity.isZero) + return; + + var target = this.currentBlock; + var tileField = this.root.find(ID.tiles); + + if (tileField.getTileType(target) == TileType.background) { + this.visible = false; + this.velocity = powerupjs.Vector2.zero; + sounds.lost.play(); + } + else if (tileField.getTileType(target) == TileType.wall) + this.stopMoving(); + else { + var a = this.root.findAnimalAtPosition(target); + if (a != null && a.visible) { + if (a.isSeal()) + this.stopMoving(); + else if (a.isEmptyBox()) { + this.visible = false; + a.sheetIndex = this.sheetIndex; + } + else if (a.sheetIndex == this.sheetIndex || this.isMulticolor() || a.isMulticolor()) { + a.visible = false; + this.visible = false; + this.root.find(ID.pairList).addPair(this.sheetIndex); + sounds.pair.play(); + } + else + this.stopMoving(); + } + + var s = this.root.findSharkAtPosition(target); + if (s != null && s.visible) { + s.visible = false; + this.visible = false; + this.stopMoving(); + sounds.eat.play(); + } + } +}; + +Animal.prototype.stopMoving = function () { + var tileField = this.root.find(ID.tiles); + this.velocity.normalize(); + var currBlock = this.currentBlock; + this.position = new powerupjs.Vector2(Math.floor(currBlock.x - this.velocity.x) * tileField.cellWidth, + Math.floor(currBlock.y - this.velocity.y) * tileField.cellHeight); + this.velocity = powerupjs.Vector2.zero; +}; + +Animal.prototype.isSeal = function () { + return this.sheetIndex === 7 && !this.boxed; +}; + +Animal.prototype.isMulticolor = function () { + return this.sheetIndex === 6 && !this.boxed; +}; + +Animal.prototype.isEmptyBox = function () { + return this.sheetIndex === 7 && this.boxed; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/AnimalSelector.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/AnimalSelector.js new file mode 100644 index 0000000..7230501 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/AnimalSelector.js @@ -0,0 +1,45 @@ +"use strict"; + +function AnimalSelector(layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + this._arrowright = new Arrow(0); + this._arrowright.position = new powerupjs.Vector2(this._arrowright.width, 0); + this.add(this._arrowright); + this._arrowup = new Arrow(1); + this._arrowup.position = new powerupjs.Vector2(0, -this._arrowright.height); + this.add(this._arrowup); + this._arrowleft = new Arrow(2); + this._arrowleft.position = new powerupjs.Vector2(-this._arrowright.width, 0); + this.add(this._arrowleft); + this._arrowdown = new Arrow(3); + this._arrowdown.position = new powerupjs.Vector2(0, this._arrowright.height); + this.add(this._arrowdown); + this.selectedAnimal = null; + this.visible = false; +} + +AnimalSelector.prototype = Object.create(powerupjs.GameObjectList.prototype); + +AnimalSelector.prototype.handleInput = function (delta) { + if (!this.visible) + return; + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var animalVelocity = powerupjs.Vector2.zero; + if (this._arrowdown.pressed) + animalVelocity.y = 1; + else if (this._arrowup.pressed) + animalVelocity.y = -1; + else if (this._arrowleft.pressed) + animalVelocity.x = -1; + else if (this._arrowright.pressed) + animalVelocity.x = 1; + animalVelocity.multiplyWith(300); + if (powerupjs.Mouse.left.pressed || powerupjs.Touch.containsTouchPress(powerupjs.Game.screenRect)) + this.visible = false; + + if (this.selectedAnimal === null || animalVelocity.isZero) + return; + this.selectedAnimal.velocity = animalVelocity; + this.root.firstMoveMade = true; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/Arrow.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/Arrow.js new file mode 100644 index 0000000..d6b043c --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/Arrow.js @@ -0,0 +1,22 @@ +"use strict"; + +function Arrow(sheetIndex, layer, id) { + powerupjs.Button.call(this, sprites.arrow, layer, id); + this.sheetIndex = sheetIndex; + this.arrowHover = new powerupjs.SpriteGameObject(sprites.arrow_hover); + this.arrowHover.sheetIndex = sheetIndex; + this.arrowHover.visible = false; + this.arrowHover.parent = this; +} + +Arrow.prototype = Object.create(powerupjs.Button.prototype); + +Arrow.prototype.handleInput = function (delta) { + powerupjs.Button.prototype.handleInput.call(this, delta); + this.arrowHover.visible = !powerupjs.Touch.isTouchDevice && this.boundingBox.contains(powerupjs.Mouse.position); +}; + +Arrow.prototype.draw = function () { + powerupjs.Button.prototype.draw.call(this); + this.arrowHover.draw(); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/Level.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/Level.js new file mode 100644 index 0000000..fff39b8 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/Level.js @@ -0,0 +1,169 @@ +"use strict"; + +function Level(levelIndex) { + powerupjs.GameObjectList.call(this); + + var levelData = window.LEVELS[levelIndex]; + + this.animals = []; + this.sharks = []; + this.levelIndex = levelIndex; + this.firstMoveMade = false; + + this.add(new powerupjs.SpriteGameObject(sprites.background_level, ID.layer_background)); + + this.hintButton = new powerupjs.Button(sprites.button_hint, ID.layer_overlays); + this.hintButton.position = new powerupjs.Vector2(916, 20); + this.add(this.hintButton); + + this.retryButton = new powerupjs.Button(sprites.button_retry, ID.layer_overlays); + this.retryButton.position = new powerupjs.Vector2(916, 20); + this.retryButton.visible = false; + this.add(this.retryButton); + + this.quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + this.quitButton.position = new powerupjs.Vector2(1058, 20); + this.add(this.quitButton); + + var width = levelData.tiles[0].length; + var height = levelData.tiles.length; + + var playingField = new powerupjs.GameObjectList(ID.layer_objects); + playingField.position = new powerupjs.Vector2((powerupjs.Game.size.x - width * 73) / 2, 100); + this.add(playingField); + + var hint = new powerupjs.SpriteGameObject(sprites.arrow_hint, ID.layer_objects_2); + hint.sheetIndex = levelData.hint_arrow_direction; + hint.position = new powerupjs.Vector2(levelData.hint_arrow_x * 73, levelData.hint_arrow_y * 72); + playingField.add(hint); + + this.hintVisible = new VisibilityTimer(hint); + playingField.add(this.hintVisible); + + var tileField = new TileField(height, width, ID.layer_objects, ID.tiles); + tileField.cellHeight = 72; + tileField.cellWidth = 73; + for (var row = 0; row < height; row++) { + for (var col = 0; col < width; col++) { + var t = null; + + switch (levelData.tiles[row][col]) { + case '.' : + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + break; + case ' ': + t = new Tile(sprites.wall, ID.layer_objects); + t.type = TileType.background; + tileField.addAt(t, col, row); + break; + case 'r': + case 'b': + case 'g': + case 'o': + case 'p': + case 'y': + case 'm': + case 'x': + case 'R': + case 'B': + case 'G': + case 'O': + case 'P': + case 'Y': + case 'M': + case 'X': + t = new Tile(sprites.field, ID.layer_objects); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var animalSprite = sprites.penguin; + if (levelData.tiles[row][col] === levelData.tiles[row][col].toUpperCase()) + animalSprite = sprites.penguin_boxed; + var p = new Animal(levelData.tiles[row][col], animalSprite, ID.layer_objects_1); + p.position = t.position.copy(); + p.initialPosition = t.position.copy(); + playingField.add(p); + this.animals.push(p); + break; + case '@': + t = new Tile(sprites.field); + t.sheetIndex = row + col % 2; + tileField.addAt(t, col, row); + var s = new powerupjs.SpriteGameObject(sprites.shark, ID.layer_objects_1); + s.position = t.position.copy(); + playingField.add(s); + this.sharks.push(s); + break; + default : + t = new Tile(sprites.wall, ID.layer_objects); + tileField.addAt(t, col, row); + t.type = TileType.wall; + break; + } + } + } + playingField.add(tileField); + + playingField.add(new AnimalSelector(ID.layer_objects_2, ID.animalSelector)); + + var pairList = new PairList(levelData.nrPairs, ID.layer_overlays, ID.pairList); + pairList.position = new powerupjs.Vector2(20, 15); + this.add(pairList); + + var helpField = new powerupjs.SpriteGameObject(sprites.help, ID.layer_overlays); + helpField.position = new powerupjs.Vector2(helpField.screenCenterX, 780); + this.add(helpField); + var helpLabel = new powerupjs.Label("Arial", "24px", ID.layer_overlays_1); + helpLabel.text = levelData.hint; + helpLabel.color = powerupjs.Color.darkBlue; + helpLabel.position = new powerupjs.Vector2(helpLabel.screenCenterX, 780 + (helpField.height - helpLabel.height) / 2); + this.add(helpLabel); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(Level.prototype, "completed", + { + get: function () { + return this.find(ID.pairList).completed; + } + }); + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.hintButton.pressed) + this.hintVisible.startVisible(); + else if (this.retryButton.pressed) + this.reset(); + else if (this.quitButton.pressed) { + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + } +}; + +Level.prototype.update = function (delta) { + this.hintButton.visible = GameSettings.hints && !this.firstMoveMade; + this.retryButton.visible = !this.hintButton.visible; + powerupjs.GameObjectList.prototype.update.call(this, delta); +}; + +Level.prototype.reset = function () { + powerupjs.GameObjectList.prototype.reset.call(this); + this.firstMoveMade = false; +}; + +Level.prototype.findAnimalAtPosition = function (pos) { + for (var i = 0, j = this.animals.length; i < j; ++i) + if (this.animals[i].currentBlock.equals(pos) && this.animals[i].velocity.isZero) + return this.animals[i]; + return null; +}; + +Level.prototype.findSharkAtPosition = function (pos) { + var tileField = this.find(ID.tiles); + var sharkPos = new powerupjs.Vector2(pos.x * tileField.cellWidth, pos.y * tileField.cellHeight); + for (var i = 0, l = this.sharks.length; i < l; ++i) + if (this.sharks[i].position.equals(sharkPos)) + return this.sharks[i]; + return null; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/PairList.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/PairList.js new file mode 100644 index 0000000..10bf94a --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/PairList.js @@ -0,0 +1,45 @@ +"use strict"; + +function PairList(nrPairs, layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.frame_goal, layer, id); + this._pairs = []; + for (var i = 0; i < nrPairs; i++) + this._pairs.push(7); + this._pairSprite = new powerupjs.SpriteGameObject(sprites.penguin_pairs); + this._pairSprite.parent = this; +} +PairList.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Object.defineProperty(PairList.prototype, "completed", + { + get: function () { + for (var i = 0, l = this._pairs.length; i < l; i++) + if (this._pairs[i] === 7) + return false; + return true; + } + }); + +PairList.prototype.addPair = function (index) { + var i = 0; + while (i < this._pairs.length && this._pairs[i] !== 7) + i++; + if (i < this._pairs.length) + this._pairs[i] = index; +}; + +PairList.prototype.reset = function () { + for (var i = 0, l = this._pairs.length; i < l; i++) + this._pairs[i] = 7; +}; + +PairList.prototype.draw = function () { + powerupjs.SpriteGameObject.prototype.draw.call(this); + if (!this.visible) + return; + for (var i = 0, l = this._pairs.length; i < l; i++) { + this._pairSprite.position = new powerupjs.Vector2(110 + i * this.height, 8); + this._pairSprite.sheetIndex = this._pairs[i]; + this._pairSprite.draw(); + } +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/Tile.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/Tile.js new file mode 100644 index 0000000..ffdc0ac --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/Tile.js @@ -0,0 +1,20 @@ +"use strict"; + +var TileType = { + normal: 0, + background: 1, + wall: 2 +}; + +function Tile(sprite, layer) { + powerupjs.SpriteGameObject.call(this, sprite, layer); + this.type = TileType.normal; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type === TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/TileField.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/TileField.js new file mode 100644 index 0000000..043bedd --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/TileField.js @@ -0,0 +1,17 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + powerupjs.GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(powerupjs.GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (pos) { + if (this.isOutsideField(pos)) + return TileType.background; + return this.at(pos.x, pos.y).type; +}; + +TileField.prototype.isOutsideField = function (pos) { + return (pos.x < 0 || pos.x >= this.columns || pos.y < 0 || pos.y >= this.rows); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gameobjects/VisibilityTimer.js b/23_FinishingGame/PenguinPairsFinal/gameobjects/VisibilityTimer.js new file mode 100644 index 0000000..a931536 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gameobjects/VisibilityTimer.js @@ -0,0 +1,21 @@ +"use strict"; + +function VisibilityTimer(target, layer) { + powerupjs.GameObject.call(this, layer); + this.target = target; + this._timeLeft = 0; + this._totalTime = 1; +} + +VisibilityTimer.prototype = Object.create(powerupjs.GameObject.prototype); + +VisibilityTimer.prototype.update = function (delta) { + this._timeLeft -= delta; + if (this._timeLeft <= 0) + this.target.visible = false; +}; + +VisibilityTimer.prototype.startVisible = function () { + this._timeLeft = this._totalTime; + this.target.visible = true; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gui/LevelButton.js b/23_FinishingGame/PenguinPairsFinal/gui/LevelButton.js new file mode 100644 index 0000000..1403bbf --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gui/LevelButton.js @@ -0,0 +1,59 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_2); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_1); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width, + this._levelSolved.height - textLabel.height + 50).divideBy(2); + textLabel.color = powerupjs.Color.black; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouchPress(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gui/OnOffButton.js b/23_FinishingGame/PenguinPairsFinal/gui/OnOffButton.js new file mode 100644 index 0000000..900c0e4 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gui/OnOffButton.js @@ -0,0 +1,28 @@ +"use strict"; + +function OnOffButton(sprite, layer) { + powerupjs.SpriteGameObject.call(this, sprite, layer); +} + +OnOffButton.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Object.defineProperty(OnOffButton.prototype, "on", + { + get: function () { + return this.sheetIndex === 1; + }, + set: function (value) { + if (value) + this.sheetIndex = 1; + else + this.sheetIndex = 0; + } + }); + +OnOffButton.prototype.handleInput = function (delta) { + if (!this.visible) + return; + if (powerupjs.Touch.containsTouchPress(this.boundingBox) || + powerupjs.Mouse.containsMousePress(this.boundingBox)) + this.sheetIndex = (this.sheetIndex + 1) % 2; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/gui/Slider.js b/23_FinishingGame/PenguinPairsFinal/gui/Slider.js new file mode 100644 index 0000000..75a50b0 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/gui/Slider.js @@ -0,0 +1,61 @@ +"use strict"; + +function Slider(sliderback, sliderfront, layer) { + powerupjs.GameObjectList.call(this, layer); + this.dragging = false; + this.draggingId = -1; + this.leftmargin = 5; + this.rightmargin = 7; + + this.back = new powerupjs.SpriteGameObject(sliderback); + this.front = new powerupjs.SpriteGameObject(sliderfront, 1); + this.front.position = new powerupjs.Vector2(this.leftmargin, 8); + this.add(this.back); + this.add(this.front); +} + +Slider.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(Slider.prototype, "value", + { + get: function () { + return (this.front.position.x - this.back.position.x - this.leftmargin) / + (this.back.width - this.front.width - this.leftmargin - this.rightmargin); + }, + set: function (value) { + var newxpos = value * (this.back.width - this.front.width - this.leftmargin - this.rightmargin) + + this.back.position.x + this.leftmargin; + this.front.position = new powerupjs.Vector2(newxpos, this.front.position.y); + } + }); + +Slider.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (powerupjs.Mouse.left.down || powerupjs.Touch.isTouching) { + if (powerupjs.Touch.isTouchDevice) { + if (powerupjs.Touch.containsTouch(this.back.boundingBox)) + this.draggingId = powerupjs.Touch.getIndexInRect((this.back.boundingBox)); + if (powerupjs.Touch.containsTouch(this.back.boundingBox) || this.dragging) { + var touchPos = powerupjs.Touch.getPosition(this.draggingId); + this.front.position = new powerupjs.Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(touchPos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } else { + var mousePos = powerupjs.Mouse.position; + if (this.back.boundingBox.contains(mousePos) || this.dragging) { + this.front.position = new powerupjs.Vector2(0, this.front.position.y); + this.front.position.x = Math.clamp(mousePos.x - this.back.worldPosition.x - this.front.width / 2, + this.back.position.x + this.leftmargin, + this.back.position.x + this.back.width - this.front.width - this.rightmargin); + this.dragging = true; + } + } + } + else { + this.dragging = false; + this.draggingId = -1; + } +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/HelpState.js b/23_FinishingGame/PenguinPairsFinal/states/HelpState.js new file mode 100644 index 0000000..8843785 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/HelpState.js @@ -0,0 +1,22 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, 100); + this.backButton.position = new powerupjs.Vector2(415, 720); + this.add(this.backButton); +} + +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/LevelFinishedState.js b/23_FinishingGame/PenguinPairsFinal/states/LevelFinishedState.js new file mode 100644 index 0000000..d287ef8 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/LevelFinishedState.js @@ -0,0 +1,27 @@ +"use strict"; + +function LevelFinishedState() { + powerupjs.GameObjectList.call(this); + this.playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + this.overlay = new powerupjs.SpriteGameObject(sprites.level_finished, ID.layer_overlays); + this.overlay.position = this.overlay.screenCenter; + this.add(this.overlay); +} + +LevelFinishedState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelFinishedState.prototype.handleInput = function (delta) { + if (powerupjs.Touch.isTouchDevice) { + if (!powerupjs.Touch.containsTouchPress(this.overlay.boundingBox)) + return; + } + else if (!powerupjs.Mouse.left.pressed) + return; + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + this.playingState.nextLevel(); +}; + +LevelFinishedState.prototype.draw = function () { + this.playingState.draw(); + powerupjs.GameObjectList.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/LevelMenuState.js b/23_FinishingGame/PenguinPairsFinal/states/LevelMenuState.js new file mode 100644 index 0000000..1b4fbe5 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/LevelMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.background_levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0; i < window.LEVELS.length; i += 1) { + var row = Math.floor(i / 5); + var column = i % 5; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 30) + 155, + row * (level.height + 5) + 230); + //console.log("Level " + i + " at position " + level.position); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i += 1) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/OptionsMenuState.js b/23_FinishingGame/PenguinPairsFinal/states/OptionsMenuState.js new file mode 100644 index 0000000..d377c9f --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/OptionsMenuState.js @@ -0,0 +1,48 @@ +"use strict"; + +function OptionsMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + var background = new powerupjs.SpriteGameObject(sprites.background_options, ID.layer_background); + this.add(background); + + var onOffLabel = new powerupjs.Label("Arial", "60px", ID.layer_overlays); + onOffLabel.text = "Hints"; + onOffLabel.position = new powerupjs.Vector2(150, 360); + onOffLabel.color = powerupjs.Color.darkBlue; + this.add(onOffLabel); + + this.onOffButton = new OnOffButton(sprites.button_offon, ID.layer_overlays); + this.onOffButton.position = new powerupjs.Vector2(650, 340); + this.onOffButton.on = GameSettings.hints; + this.add(this.onOffButton); + + var musicText = new powerupjs.Label("Arial", "60px", ID.layer_overlays); + musicText.text = "Music volume"; + musicText.position = new powerupjs.Vector2(150, 490); + musicText.color = powerupjs.Color.darkBlue; + this.add(musicText); + + this.musicSlider = new Slider(sprites.slider_bar, sprites.slider_button, ID.layer_overlays); + this.musicSlider.position = new powerupjs.Vector2(650, 500); + this.musicSlider.value = sounds.music.volume; + this.add(this.musicSlider); + + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(415, 720); + this.add(this.backButton); +} + +OptionsMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +OptionsMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; + +OptionsMenuState.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + sounds.music.volume = this.musicSlider.value; + GameSettings.hints = this.onOffButton.on; +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/PlayingState.js b/23_FinishingGame/PenguinPairsFinal/states/PlayingState.js new file mode 100644 index 0000000..c7355f1 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/PlayingState.js @@ -0,0 +1,74 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); + if (this.currentLevel.completed) { + sounds.won.play(); + window.LEVELS[this.currentLevelIndex].solved = true; + powerupjs.GameStateManager.switchTo(ID.game_state_levelfinished); + } +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= window.LEVELS.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel++) + this.levels.push(new Level(currLevel)); +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.penguinPairsLevels) { + window.LEVELS = JSON.parse(localStorage.penguinPairsLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.penguinPairsLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/23_FinishingGame/PenguinPairsFinal/states/TitleMenuState.js b/23_FinishingGame/PenguinPairsFinal/states/TitleMenuState.js new file mode 100644 index 0000000..03341f3 --- /dev/null +++ b/23_FinishingGame/PenguinPairsFinal/states/TitleMenuState.js @@ -0,0 +1,32 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + var background = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(background); + + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(415, 540); + this.add(this.playButton); + + this.optionsButton = new powerupjs.Button(sprites.button_options, ID.layer_overlays); + this.optionsButton.position = new powerupjs.Vector2(415, 650); + this.add(this.optionsButton); + + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(415, 760); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); + else if (this.optionsButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_options); +}; \ No newline at end of file diff --git a/24_GameStructure/LAB.min.js b/24_GameStructure/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/24_GameStructure/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/24_GameStructure/TickTick1/TickTick.html b/24_GameStructure/TickTick1/TickTick.html new file mode 100644 index 0000000..898a5fe --- /dev/null +++ b/24_GameStructure/TickTick1/TickTick.html @@ -0,0 +1,62 @@ + + + + + + + + + + TickTick + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/24_GameStructure/TickTick1/TickTick.js b/24_GameStructure/TickTick1/TickTick.js new file mode 100644 index 0000000..84cfafe --- /dev/null +++ b/24_GameStructure/TickTick1/TickTick.js @@ -0,0 +1,113 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/" + sprite/*, collisionMask*/); + }; + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/TickTick/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("backgrounds/spr_title.jpg"); + sprites.background_help = loadSprite("backgrounds/spr_help.jpg"); + sprites.background_sky = loadSprite("backgrounds/spr_sky.jpg"); + sprites.cloud_1 = loadSprite("backgrounds/spr_cloud_1.png"); + sprites.cloud_2 = loadSprite("backgrounds/spr_cloud_2.png"); + sprites.cloud_3 = loadSprite("backgrounds/spr_cloud_3.png"); + sprites.cloud_4 = loadSprite("backgrounds/spr_cloud_4.png"); + sprites.cloud_5 = loadSprite("backgrounds/spr_cloud_5.png"); + sprites.mountain_1 = loadSprite("backgrounds/spr_mountain_1.png"); + sprites.mountain_2 = loadSprite("backgrounds/spr_mountain_2.png"); + sprites.levelselect = loadSprite("backgrounds/spr_levelselect.jpg"); + + if (powerupjs.Touch.isTouchDevice) { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_tap.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_tap.png"); + } else { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_click.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_click.png"); + } + sprites.frame_hint = loadSprite("overlays/spr_frame_hint.png"); + sprites.timer = loadSprite("overlays/spr_timer.png"); + + sprites.button_play = loadSprite("gui/spr_button_play.png"); + sprites.button_help = loadSprite("gui/spr_button_help.png"); + sprites.button_back = loadSprite("gui/spr_button_back.png"); + sprites.button_quit = loadSprite("gui/spr_button_quit.png"); + sprites.level_solved = loadSprite("gui/spr_level_solved.png"); + sprites.level_unsolved = loadSprite("gui/spr_level_unsolved.png"); + sprites.level_locked = loadSprite("gui/spr_level_locked.png"); + sprites.buttons_player = loadSprite("gui/spr_buttons_player@3.png"); + + sprites.wall = loadSprite("tiles/spr_wall.png"); + sprites.wall_hot = loadSprite("tiles/spr_wall_hot.png"); + sprites.wall_ice = loadSprite("tiles/spr_wall_ice.png"); + sprites.platform = loadSprite("tiles/spr_platform.png"); + sprites.platform_hot = loadSprite("tiles/spr_platform_hot.png"); + sprites.platform_ice = loadSprite("tiles/spr_platform_ice.png"); + + sprites.goal = loadSprite("spr_goal.png", true); + sprites.water = loadSprite("spr_water.png"); + + // player animations + sprites.player_idle = loadSprite("player/spr_idle.png", true); + sprites.player_run = loadSprite("player/spr_run@13.png", true); + sprites.player_jump = loadSprite("player/spr_jump@14.png", true); + sprites.player_celebrate = loadSprite("player/spr_celebrate@14.png"); + sprites.player_die = loadSprite("player/spr_die@5.png"); + sprites.player_explode = loadSprite("player/spr_explode@5x5.png"); + + // enemy animations + sprites.rocket = loadSprite("rocket/spr_rocket@3.png", true); + sprites.flame = loadSprite("flame/spr_flame@9.png", true); + sprites.turtle_sneeze = loadSprite("turtle/spr_sneeze@9.png", true); + sprites.turtle_idle = loadSprite("turtle/spr_idle.png", true); + sprites.sparky_electrocute = loadSprite("sparky/spr_electrocute@6x5.png", true); + sprites.sparky_idle = loadSprite("sparky/spr_idle.png", true); + + sounds.music = loadSound("snd_music", true); + sounds.player_die = loadSound("snd_player_die"); + sounds.player_explode = loadSound("snd_player_explode"); + sounds.player_fall = loadSound("snd_player_fall"); + sounds.player_jump = loadSound("snd_player_jump"); + sounds.player_won = loadSound("snd_player_won"); + sounds.water_collected = loadSound("snd_water_collected"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.player = 1; + ID.timer = 2; + ID.tiles = 3; + ID.exit = 4; + ID.hint_timer = 5; + ID.button_walkleft = 6; + ID.button_walkright = 7; + ID.button_jump = 8; + + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/game-layout.css b/24_GameStructure/TickTick1/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/24_GameStructure/TickTick1/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/24_GameStructure/TickTick1/gameobjects/Tile.js b/24_GameStructure/TickTick1/gameobjects/Tile.js new file mode 100644 index 0000000..19a1c2e --- /dev/null +++ b/24_GameStructure/TickTick1/gameobjects/Tile.js @@ -0,0 +1,24 @@ +"use strict"; + +var TileType = { + background: 0, + normal: 1, + platform: 2 +}; + +function Tile(sprite, tileTp, layer, id) { + sprite = typeof sprite !== 'undefined' ? sprite : sprites.wall; + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.hot = false; + this.ice = false; + this.type = typeof tileTp !== 'undefined' ? tileTp : TileType.background; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type == TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; diff --git a/24_GameStructure/TickTick1/gameobjects/WaterDrop.js b/24_GameStructure/TickTick1/gameobjects/WaterDrop.js new file mode 100644 index 0000000..a4e2b6a --- /dev/null +++ b/24_GameStructure/TickTick1/gameobjects/WaterDrop.js @@ -0,0 +1,16 @@ +"use strict"; + +function WaterDrop(layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.water, layer, id); + this._bounce = 0; +} + +WaterDrop.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +WaterDrop.prototype.update = function (delta) { + this.position.y -= this._bounce; + var t = powerupjs.Game.totalTime * 3 + this.position.x; + this._bounce = Math.sin(t) * 5; + this.position.y += this._bounce; + powerupjs.SpriteGameObject.prototype.update.call(this, delta); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/gui/LevelButton.js b/24_GameStructure/TickTick1/gui/LevelButton.js new file mode 100644 index 0000000..45e419a --- /dev/null +++ b/24_GameStructure/TickTick1/gui/LevelButton.js @@ -0,0 +1,57 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays_1); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_1); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_2); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width - 10, 10); + textLabel.color = powerupjs.Color.white; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouch(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/level/Level.js b/24_GameStructure/TickTick1/level/Level.js new file mode 100644 index 0000000..f808776 --- /dev/null +++ b/24_GameStructure/TickTick1/level/Level.js @@ -0,0 +1,109 @@ +"use strict"; + +function Level(levelIndex, id) { + powerupjs.GameObjectList.call(this, id); + + this._levelIndex = levelIndex; + this._waterdrops = new powerupjs.GameObjectList(ID.layer_objects); + + this._quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + this._quitButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - this._quitButton.width - 10, 10); + this.add(this._quitButton); + + this.add(this._waterdrops); + + // backgrounds + var backgrounds = new powerupjs.GameObjectList(ID.layer_background); + var background_main = new powerupjs.SpriteGameObject(sprites.background_sky, ID.layer_background_1); + background_main.position = new powerupjs.Vector2(0, powerupjs.Game.size.y - background_main.height); + backgrounds.add(background_main); + this.add(backgrounds); + + this.loadTiles(); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Level.prototype.loadTiles = function () { + var levelData = window.LEVELS[this._levelIndex]; + + var hintField = new powerupjs.GameObjectList(ID.layer_overlays); + this.add(hintField); + var hintFrame = new powerupjs.SpriteGameObject(sprites.frame_hint, ID.layer_overlays_1); + hintField.position = new powerupjs.Vector2(hintFrame.screenCenterX, 10); + hintField.add(hintFrame); + var hintText = new powerupjs.Label("Arial", "14pt", ID.layer_overlays_2); + hintText.text = levelData.hint; + hintText.position = new powerupjs.Vector2(120, 25); + hintText.color = powerupjs.Color.black; + hintField.add(hintText); + + var tiles = new powerupjs.GameObjectGrid(levelData.tiles.length, levelData.tiles[0].length, 1, ID.tiles); + this.add(tiles); + tiles.cellWidth = 72; + tiles.cellHeight = 55; + for (var y = 0, ly = tiles.rows; y < ly; ++y) + for (var x = 0, lx = tiles.columns; x < lx; ++x) { + var t = this.loadTile(levelData.tiles[y][x], x, y); + tiles.add(t, x, y); + } +}; + +Level.prototype.loadTile = function (tileType, x, y) { + switch (tileType) { + case '.': + return new Tile(); + case '-': + return this.loadBasicTile(sprites.platform, TileType.platform); + case '+': + return this.loadBasicTile(sprites.platform_hot, TileType.platform, true, false); + case '@': + return this.loadBasicTile(sprites.platform_ice, TileType.platform, false, true); + case 'X': + return this.loadEndTile(x, y); + case 'W': + return this.loadWaterTile(x, y); + case '#': + return this.loadBasicTile(sprites.wall, TileType.normal); + case '^': + return this.loadBasicTile(sprites.wall_hot, TileType.normal, true, false); + case '*': + return this.loadBasicTile(sprites.wall_ice, TileType.normal, false, true); + default: + return new Tile(); + } +}; + +Level.prototype.loadBasicTile = function (id, tileType, hot, ice) { + var t = new Tile(id, tileType); + t.hot = hot; + t.ice = ice; + return t; +}; + +Level.prototype.loadEndTile = function (x, y) { + var tiles = this.find(ID.tiles); + var exit = new powerupjs.SpriteGameObject(sprites.goal, ID.layer_objects, ID.exit); + exit.position = new powerupjs.Vector2(x * tiles.cellWidth, (y + 1) * tiles.cellHeight); + exit.origin = new powerupjs.Vector2(0, exit.height); + this.add(exit); + return new Tile(); +}; + +Level.prototype.loadWaterTile = function (x, y) { + var tiles = this.find(ID.tiles); + var w = new WaterDrop(ID.layer_objects); + w.origin = w.center.copy(); + w.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 0.5) * tiles.cellHeight - 10); + this._waterdrops.add(w); + return new Tile(); +}; + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + if (!this._quitButton.pressed) + return; + this.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/states/HelpState.js b/24_GameStructure/TickTick1/states/HelpState.js new file mode 100644 index 0000000..c71e040 --- /dev/null +++ b/24_GameStructure/TickTick1/states/HelpState.js @@ -0,0 +1,21 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help, ID.layer_background); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(this.backButton.screenCenterX, 750); + this.add(this.backButton); +} +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/states/LevelMenuState.js b/24_GameStructure/TickTick1/states/LevelMenuState.js new file mode 100644 index 0000000..c00b239 --- /dev/null +++ b/24_GameStructure/TickTick1/states/LevelMenuState.js @@ -0,0 +1,46 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0, j = window.LEVELS.length; i < j; ++i) { + var row = Math.floor(i / 4); + var column = i % 4; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 20) + 390, row * (level.height + 20) + 180); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i++) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/states/PlayingState.js b/24_GameStructure/TickTick1/states/PlayingState.js new file mode 100644 index 0000000..bc7aae4 --- /dev/null +++ b/24_GameStructure/TickTick1/states/PlayingState.js @@ -0,0 +1,70 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= this.levels.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + this.levels.push(new Level(currLevel)); + } +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.tickTickLevels) { + window.LEVELS = JSON.parse(localStorage.tickTickLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.tickTickLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/24_GameStructure/TickTick1/states/TitleMenuState.js b/24_GameStructure/TickTick1/states/TitleMenuState.js new file mode 100644 index 0000000..0628cb6 --- /dev/null +++ b/24_GameStructure/TickTick1/states/TitleMenuState.js @@ -0,0 +1,29 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the title screen + var titleScreen = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(titleScreen); + + // add a play button + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(this.playButton.screenCenterX, 540); + this.add(this.playButton); + + // add a help button + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(this.helpButton.screenCenterX, 600); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); +}; \ No newline at end of file diff --git a/25_Animation/AnimationSample/AnimationSample.html b/25_Animation/AnimationSample/AnimationSample.html new file mode 100644 index 0000000..f89674f --- /dev/null +++ b/25_Animation/AnimationSample/AnimationSample.html @@ -0,0 +1,55 @@ + + + + + + + + + + Animation Sample + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/25_Animation/AnimationSample/AnimationSample.js b/25_Animation/AnimationSample/AnimationSample.js new file mode 100644 index 0000000..f957da5 --- /dev/null +++ b/25_Animation/AnimationSample/AnimationSample.js @@ -0,0 +1,18 @@ +"use strict"; + +var ID = {}; +var sprites = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/player/" + sprite); + }; + + sprites.run = loadSprite("spr_run@13.png"); + sprites.idle = loadSprite("spr_idle.png"); +}; + +powerupjs.Game.initialize = function () { + ID.game_state_animation = powerupjs.GameStateManager.add(new AnimationState()); + powerupjs.GameStateManager.switchTo(ID.game_state_animation); +}; \ No newline at end of file diff --git a/25_Animation/AnimationSample/AnimationState.js b/25_Animation/AnimationSample/AnimationState.js new file mode 100644 index 0000000..263ba5d --- /dev/null +++ b/25_Animation/AnimationSample/AnimationState.js @@ -0,0 +1,11 @@ +"use strict"; + +function AnimationState(layer) { + powerupjs.GameObjectList.call(this, layer); + + var player = new Player(); + player.position = new powerupjs.Vector2(50, 300); + this.add(player); +} + +AnimationState.prototype = Object.create(powerupjs.GameObjectList.prototype); diff --git a/25_Animation/AnimationSample/Player.js b/25_Animation/AnimationSample/Player.js new file mode 100644 index 0000000..09daf58 --- /dev/null +++ b/25_Animation/AnimationSample/Player.js @@ -0,0 +1,33 @@ +"use strict"; + +function Player() { + powerupjs.AnimatedGameObject.call(this); + + this.loadAnimation(sprites.idle, "idle", true); + this.loadAnimation(sprites.run, "run", true, 0.05); + this.playAnimation("idle"); + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} + +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.handleInput = function (delta) { + var walkingSpeed = 400; + if (powerupjs.Keyboard.down(powerupjs.Keys.left)) + this.velocity.x = -walkingSpeed; + else if (powerupjs.Keyboard.down(powerupjs.Keys.right)) + this.velocity.x = walkingSpeed; + else + this.velocity.x = 0; + if (this.velocity.x != 0) + this.mirror = this.velocity.x < 0; +}; + +Player.prototype.update = function (delta) { + if (this.velocity.x === 0) + this.playAnimation("idle"); + else + this.playAnimation("run"); + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); +}; \ No newline at end of file diff --git a/25_Animation/AnimationSample/game-layout.css b/25_Animation/AnimationSample/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/25_Animation/AnimationSample/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/25_Animation/LAB.min.js b/25_Animation/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/25_Animation/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/CollisionTest.html b/26_GamePhysics/CollisionTest/CollisionTest.html new file mode 100644 index 0000000..fd9c474 --- /dev/null +++ b/26_GamePhysics/CollisionTest/CollisionTest.html @@ -0,0 +1,55 @@ + + + + + + + + + + Collision Test + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/CollisionTest.js b/26_GamePhysics/CollisionTest/CollisionTest.js new file mode 100644 index 0000000..b9010c3 --- /dev/null +++ b/26_GamePhysics/CollisionTest/CollisionTest.js @@ -0,0 +1,28 @@ +/* + + Note that if you run the CollisionTest example from a local file, it only works in browsers that allow + reading canvas pixel data we a web application is run locally. Chrome doesn't allow this for example, but + Firefox and Internet Explorer do. + + */ + +"use strict"; + +var ID = {}; +var sprites = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("assets/sprites/" + sprite, collisionMask); + }; + + sprites.run = loadSprite("spr_run@13.png", true); + sprites.idle = loadSprite("spr_idle.png", true); + sprites.sparky_idle = loadSprite("spr_sparky_idle.png", true); + sprites.sparky_electrocute = loadSprite("spr_sparky_electrocute@6x5.png"); +}; + +powerupjs.Game.initialize = function () { + ID.game_state_main = powerupjs.GameStateManager.add(new MainState()); + powerupjs.GameStateManager.switchTo(ID.game_state_main); +}; \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/MainState.js b/26_GamePhysics/CollisionTest/MainState.js new file mode 100644 index 0000000..28172ad --- /dev/null +++ b/26_GamePhysics/CollisionTest/MainState.js @@ -0,0 +1,24 @@ +"use strict"; + +function MainState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.player = new Player(); + this.player.position = new powerupjs.Vector2(50, 300); + this.add(this.player); + + this.sparky = new Sparky(); + this.sparky.position = new powerupjs.Vector2(150, 200); + this.add(this.sparky); +} + +MainState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +MainState.prototype.draw = function () { + powerupjs.GameObjectList.prototype.draw.call(this); + // draw the bounding boxes + this.player.boundingBox.draw(); + this.sparky.boundingBox.draw(); + if (this.player.collidesWith(this.sparky)) + powerupjs.Canvas2D.drawText("Collision"); +}; \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/Player.js b/26_GamePhysics/CollisionTest/Player.js new file mode 100644 index 0000000..312a868 --- /dev/null +++ b/26_GamePhysics/CollisionTest/Player.js @@ -0,0 +1,16 @@ +"use strict"; + +function Player() { + powerupjs.AnimatedGameObject.call(this); + + this.loadAnimation(sprites.idle, "idle", true); + this.loadAnimation(sprites.run, "run", true, 0.05); + this.playAnimation("idle"); + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.handleInput = function (delta) { + this.position = powerupjs.Mouse.position; +}; \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/README.txt b/26_GamePhysics/CollisionTest/README.txt new file mode 100644 index 0000000..03c3f32 --- /dev/null +++ b/26_GamePhysics/CollisionTest/README.txt @@ -0,0 +1 @@ +Note that if you run the CollisionTest example from a local file, it only works in browsers that allow reading canvas pixel data we a web application is run locally. Chrome doesn't allow this for example, but Firefox and Internet Explorer do. \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/Sparky.js b/26_GamePhysics/CollisionTest/Sparky.js new file mode 100644 index 0000000..4e069c9 --- /dev/null +++ b/26_GamePhysics/CollisionTest/Sparky.js @@ -0,0 +1,24 @@ +"use strict"; + +function Sparky() { + powerupjs.AnimatedGameObject.call(this); + + this.curr = "idle"; + this.loadAnimation(sprites.sparky_idle, "idle", true); + this.loadAnimation(sprites.sparky_electrocute, "electrocute", true); + + this.playAnimation(this.curr); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} + +Sparky.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Sparky.prototype.handleInput = function (delta) { + if (powerupjs.Keyboard.pressed(powerupjs.Keys.space)) { + if (this.curr === "electrocute") + this.curr = "idle"; + else + this.curr = "electrocute"; + this.playAnimation(this.curr) + } +}; \ No newline at end of file diff --git a/26_GamePhysics/CollisionTest/assets/sprites/spr_idle.png b/26_GamePhysics/CollisionTest/assets/sprites/spr_idle.png new file mode 100644 index 0000000..0d10bfe Binary files /dev/null and b/26_GamePhysics/CollisionTest/assets/sprites/spr_idle.png differ diff --git a/26_GamePhysics/CollisionTest/assets/sprites/spr_run@13.png b/26_GamePhysics/CollisionTest/assets/sprites/spr_run@13.png new file mode 100644 index 0000000..b2f6e34 Binary files /dev/null and b/26_GamePhysics/CollisionTest/assets/sprites/spr_run@13.png differ diff --git a/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_electrocute@6x5.png b/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_electrocute@6x5.png new file mode 100644 index 0000000..5a63819 Binary files /dev/null and b/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_electrocute@6x5.png differ diff --git a/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_idle.png b/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_idle.png new file mode 100644 index 0000000..03a457c Binary files /dev/null and b/26_GamePhysics/CollisionTest/assets/sprites/spr_sparky_idle.png differ diff --git a/26_GamePhysics/CollisionTest/game-layout.css b/26_GamePhysics/CollisionTest/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/26_GamePhysics/CollisionTest/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/26_GamePhysics/LAB.min.js b/26_GamePhysics/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/26_GamePhysics/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/TickTick.html b/26_GamePhysics/TickTick2/TickTick.html new file mode 100644 index 0000000..6d4996d --- /dev/null +++ b/26_GamePhysics/TickTick2/TickTick.html @@ -0,0 +1,63 @@ + + + + + + + + + + TickTick + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/TickTick.js b/26_GamePhysics/TickTick2/TickTick.js new file mode 100644 index 0000000..84cfafe --- /dev/null +++ b/26_GamePhysics/TickTick2/TickTick.js @@ -0,0 +1,113 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/" + sprite/*, collisionMask*/); + }; + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/TickTick/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("backgrounds/spr_title.jpg"); + sprites.background_help = loadSprite("backgrounds/spr_help.jpg"); + sprites.background_sky = loadSprite("backgrounds/spr_sky.jpg"); + sprites.cloud_1 = loadSprite("backgrounds/spr_cloud_1.png"); + sprites.cloud_2 = loadSprite("backgrounds/spr_cloud_2.png"); + sprites.cloud_3 = loadSprite("backgrounds/spr_cloud_3.png"); + sprites.cloud_4 = loadSprite("backgrounds/spr_cloud_4.png"); + sprites.cloud_5 = loadSprite("backgrounds/spr_cloud_5.png"); + sprites.mountain_1 = loadSprite("backgrounds/spr_mountain_1.png"); + sprites.mountain_2 = loadSprite("backgrounds/spr_mountain_2.png"); + sprites.levelselect = loadSprite("backgrounds/spr_levelselect.jpg"); + + if (powerupjs.Touch.isTouchDevice) { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_tap.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_tap.png"); + } else { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_click.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_click.png"); + } + sprites.frame_hint = loadSprite("overlays/spr_frame_hint.png"); + sprites.timer = loadSprite("overlays/spr_timer.png"); + + sprites.button_play = loadSprite("gui/spr_button_play.png"); + sprites.button_help = loadSprite("gui/spr_button_help.png"); + sprites.button_back = loadSprite("gui/spr_button_back.png"); + sprites.button_quit = loadSprite("gui/spr_button_quit.png"); + sprites.level_solved = loadSprite("gui/spr_level_solved.png"); + sprites.level_unsolved = loadSprite("gui/spr_level_unsolved.png"); + sprites.level_locked = loadSprite("gui/spr_level_locked.png"); + sprites.buttons_player = loadSprite("gui/spr_buttons_player@3.png"); + + sprites.wall = loadSprite("tiles/spr_wall.png"); + sprites.wall_hot = loadSprite("tiles/spr_wall_hot.png"); + sprites.wall_ice = loadSprite("tiles/spr_wall_ice.png"); + sprites.platform = loadSprite("tiles/spr_platform.png"); + sprites.platform_hot = loadSprite("tiles/spr_platform_hot.png"); + sprites.platform_ice = loadSprite("tiles/spr_platform_ice.png"); + + sprites.goal = loadSprite("spr_goal.png", true); + sprites.water = loadSprite("spr_water.png"); + + // player animations + sprites.player_idle = loadSprite("player/spr_idle.png", true); + sprites.player_run = loadSprite("player/spr_run@13.png", true); + sprites.player_jump = loadSprite("player/spr_jump@14.png", true); + sprites.player_celebrate = loadSprite("player/spr_celebrate@14.png"); + sprites.player_die = loadSprite("player/spr_die@5.png"); + sprites.player_explode = loadSprite("player/spr_explode@5x5.png"); + + // enemy animations + sprites.rocket = loadSprite("rocket/spr_rocket@3.png", true); + sprites.flame = loadSprite("flame/spr_flame@9.png", true); + sprites.turtle_sneeze = loadSprite("turtle/spr_sneeze@9.png", true); + sprites.turtle_idle = loadSprite("turtle/spr_idle.png", true); + sprites.sparky_electrocute = loadSprite("sparky/spr_electrocute@6x5.png", true); + sprites.sparky_idle = loadSprite("sparky/spr_idle.png", true); + + sounds.music = loadSound("snd_music", true); + sounds.player_die = loadSound("snd_player_die"); + sounds.player_explode = loadSound("snd_player_explode"); + sounds.player_fall = loadSound("snd_player_fall"); + sounds.player_jump = loadSound("snd_player_jump"); + sounds.player_won = loadSound("snd_player_won"); + sounds.water_collected = loadSound("snd_water_collected"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.player = 1; + ID.timer = 2; + ID.tiles = 3; + ID.exit = 4; + ID.hint_timer = 5; + ID.button_walkleft = 6; + ID.button_walkright = 7; + ID.button_jump = 8; + + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/game-layout.css b/26_GamePhysics/TickTick2/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/26_GamePhysics/TickTick2/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/gameobjects/Player.js b/26_GamePhysics/TickTick2/gameobjects/Player.js new file mode 100644 index 0000000..e5e8189 --- /dev/null +++ b/26_GamePhysics/TickTick2/gameobjects/Player.js @@ -0,0 +1,133 @@ +"use strict"; + +function Player(start, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + this._startPosition = start; + + this.loadAnimation(sprites.player_idle, "idle", true); + this.loadAnimation(sprites.player_run, "run", true, 0.05); + this.loadAnimation(sprites.player_jump, "jump", false, 0.05); + this.loadAnimation(sprites.player_celebrate, "celebrate", false, 0.05); + this.loadAnimation(sprites.player_die, "die", false); + this.loadAnimation(sprites.player_explode, "explode", false, 0.04); + + this.reset(); +} + +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.reset = function () { + this.playAnimation("idle"); + + this._previousYPosition = this.boundingBox.bottom; + this.position = this._startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + + this.onTheGround = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +}; + +Player.prototype.handleInput = function (delta) { + var walkingSpeed = 400; + if (this.walkingOnIce) { + walkingSpeed *= 1.5; + this.velocity.x = Math.sign(this.velocity.x) * walkingSpeed; + } else if (this.onTheGround) + this.velocity.x = 0; + + if (powerupjs.Touch.isTouchDevice) { + // get the buttons + var walkLeftButton = this.root.find(ID.button_walkleft); + var walkRightButton = this.root.find(ID.button_walkright); + if (walkLeftButton.down) + this.velocity.x = -walkingSpeed; + else if (walkRightButton.down) + this.velocity.x = walkingSpeed; + } else { + if (powerupjs.Keyboard.down(powerupjs.Keys.left)) + this.velocity.x = -walkingSpeed; + else if (powerupjs.Keyboard.down(powerupjs.Keys.right)) + this.velocity.x = walkingSpeed; + } + + if (this.velocity.x != 0) + this.mirror = this.velocity.x < 0; + + if (powerupjs.Touch.isTouchDevice) { + var jumpButton = this.root.find(ID.button_jump); + if (jumpButton.pressed && this.onTheGround) + this.jump(); + } else { + if (powerupjs.Keyboard.pressed(powerupjs.Keys.space) && this.onTheGround) + this.jump(); + } +}; + +Player.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + this.doPhysics(); + if (this.onTheGround) + if (this.velocity.x == 0) + this.playAnimation("idle"); + else + this.playAnimation("run"); + else if (this.velocity.y < 0) + this.playAnimation("jump"); +}; + +Player.prototype.jump = function (speed) { + sounds.player_jump.play(); + speed = typeof speed !== 'undefined' ? speed : 1100; + this.velocity.y = -speed; +}; + +Player.prototype.doPhysics = function () { + this.velocity.y += 55; + this.handleCollisions(); +}; + +Player.prototype.handleCollisions = function () { + this.onTheGround = false; + this.walkingOnIce = false; + this.walkingOnHot = false; + + var tiles = this.root.find(ID.tiles); + + var x_floor = Math.floor(this.position.x / tiles.cellWidth); + var y_floor = Math.floor(this.position.y / tiles.cellHeight); + + for (var y = y_floor - 2; y <= y_floor + 1; ++y) + for (var x = x_floor - 1; x <= x_floor + 1; ++x) { + var tileType = tiles.getTileType(x, y); + if (tileType === TileType.background) + continue; + var tileBounds = new powerupjs.Rectangle(x * tiles.cellWidth, y * tiles.cellHeight, + tiles.cellWidth, tiles.cellHeight); + var boundingBox = this.boundingBox; + boundingBox.height += 1; + if (!tileBounds.intersects(boundingBox)) + continue; + var depth = boundingBox.calculateIntersectionDepth(tileBounds); + if (Math.abs(depth.x) < Math.abs(depth.y)) { + if (tileType === TileType.normal) + this.position.x += depth.x; + continue; + } + if (this._previousYPosition <= tileBounds.top && tileType !== TileType.background) { + this.onTheGround = true; + this.velocity.y = 0; + var currentTile = tiles.at(x, y); + if (currentTile !== null) { + this.walkingOnIce = this.walkingOnIce || currentTile.ice; + this.walkingOnHot = this.walkingOnHot || currentTile.hot; + } + } + if (tileType === TileType.normal || this.onTheGround) + this.position.y += depth.y + 1; + } + this._previousYPosition = this.position.y; +}; + diff --git a/26_GamePhysics/TickTick2/gameobjects/Tile.js b/26_GamePhysics/TickTick2/gameobjects/Tile.js new file mode 100644 index 0000000..19a1c2e --- /dev/null +++ b/26_GamePhysics/TickTick2/gameobjects/Tile.js @@ -0,0 +1,24 @@ +"use strict"; + +var TileType = { + background: 0, + normal: 1, + platform: 2 +}; + +function Tile(sprite, tileTp, layer, id) { + sprite = typeof sprite !== 'undefined' ? sprite : sprites.wall; + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.hot = false; + this.ice = false; + this.type = typeof tileTp !== 'undefined' ? tileTp : TileType.background; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type == TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; diff --git a/26_GamePhysics/TickTick2/gameobjects/TileField.js b/26_GamePhysics/TickTick2/gameobjects/TileField.js new file mode 100644 index 0000000..83b0fe1 --- /dev/null +++ b/26_GamePhysics/TickTick2/gameobjects/TileField.js @@ -0,0 +1,15 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + powerupjs.GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(powerupjs.GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (x, y) { + if (x < 0 || x >= this.columns) + return TileType.normal; + if (y < 0 || y >= this.rows) + return TileType.background; + return this.at(x, y).type; +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/gameobjects/WaterDrop.js b/26_GamePhysics/TickTick2/gameobjects/WaterDrop.js new file mode 100644 index 0000000..a4e2b6a --- /dev/null +++ b/26_GamePhysics/TickTick2/gameobjects/WaterDrop.js @@ -0,0 +1,16 @@ +"use strict"; + +function WaterDrop(layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.water, layer, id); + this._bounce = 0; +} + +WaterDrop.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +WaterDrop.prototype.update = function (delta) { + this.position.y -= this._bounce; + var t = powerupjs.Game.totalTime * 3 + this.position.x; + this._bounce = Math.sin(t) * 5; + this.position.y += this._bounce; + powerupjs.SpriteGameObject.prototype.update.call(this, delta); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/gui/LevelButton.js b/26_GamePhysics/TickTick2/gui/LevelButton.js new file mode 100644 index 0000000..45e419a --- /dev/null +++ b/26_GamePhysics/TickTick2/gui/LevelButton.js @@ -0,0 +1,57 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays_1); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_1); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_2); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width - 10, 10); + textLabel.color = powerupjs.Color.white; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouch(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/level/Level.js b/26_GamePhysics/TickTick2/level/Level.js new file mode 100644 index 0000000..26d13b9 --- /dev/null +++ b/26_GamePhysics/TickTick2/level/Level.js @@ -0,0 +1,134 @@ +"use strict"; + +function Level(levelIndex, id) { + powerupjs.GameObjectList.call(this, id); + + this._levelIndex = levelIndex; + this._waterdrops = new powerupjs.GameObjectList(ID.layer_objects); + this._enemies = new powerupjs.GameObjectList(ID.layer_objects); + this._quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + + this._quitButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - this._quitButton.width - 10, 10); + this.add(this._quitButton); + + this.add(this._waterdrops); + this.add(this._enemies); + + // backgrounds + var backgrounds = new powerupjs.GameObjectList(ID.layer_background); + var background_main = new powerupjs.SpriteGameObject(sprites.background_sky, ID.layer_background_1); + background_main.position = new powerupjs.Vector2(0, powerupjs.Game.size.y - background_main.height); + backgrounds.add(background_main); + this.add(backgrounds); + + this.loadTiles(); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Level.prototype.loadTiles = function () { + var levelData = window.LEVELS[this._levelIndex]; + + var hintField = new powerupjs.GameObjectList(ID.layer_overlays); + this.add(hintField); + var hintFrame = new powerupjs.SpriteGameObject(sprites.frame_hint, ID.layer_overlays_1); + hintField.position = new powerupjs.Vector2(hintFrame.screenCenterX, 10); + hintField.add(hintFrame); + var hintText = new powerupjs.Label("Arial", "14pt", ID.layer_overlays_2); + hintText.text = levelData.hint; + hintText.position = new powerupjs.Vector2(120, 25); + hintText.color = powerupjs.Color.black; + hintField.add(hintText); + + if (powerupjs.Touch.isTouchDevice) { + var walkLeftButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkleft); + walkLeftButton.position = new powerupjs.Vector2(10, 500); + this.add(walkLeftButton); + var walkRightButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkright); + walkRightButton.position = new powerupjs.Vector2(walkRightButton.width + 20, 500); + walkRightButton.sheetIndex = 1; + this.add(walkRightButton); + var jumpButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_jump); + jumpButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - jumpButton.width - 10, 500); + jumpButton.sheetIndex = 2; + this.add(jumpButton); + } + + var tiles = new TileField(levelData.tiles.length, levelData.tiles[0].length, 1, ID.tiles); + this.add(tiles); + tiles.cellWidth = 72; + tiles.cellHeight = 55; + for (var y = 0, ly = tiles.rows; y < ly; ++y) + for (var x = 0, lx = tiles.columns; x < lx; ++x) { + var t = this.loadTile(levelData.tiles[y][x], x, y); + tiles.add(t, x, y); + } +}; + +Level.prototype.loadTile = function (tileType, x, y) { + switch (tileType) { + case '.': + return new Tile(); + case '-': + return this.loadBasicTile(sprites.platform, TileType.platform); + case '+': + return this.loadBasicTile(sprites.platform_hot, TileType.platform, true, false); + case '@': + return this.loadBasicTile(sprites.platform_ice, TileType.platform, false, true); + case 'X': + return this.loadEndTile(x, y); + case 'W': + return this.loadWaterTile(x, y); + case '1': + return this.loadStartTile(x, y); + case '#': + return this.loadBasicTile(sprites.wall, TileType.normal); + case '^': + return this.loadBasicTile(sprites.wall_hot, TileType.normal, true, false); + case '*': + return this.loadBasicTile(sprites.wall_ice, TileType.normal, false, true); + default: + return new Tile(); + } +}; + +Level.prototype.loadBasicTile = function (id, tileType, hot, ice) { + var t = new Tile(id, tileType); + t.hot = hot; + t.ice = ice; + return t; +}; + +Level.prototype.loadStartTile = function (x, y) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this.add(new Player(startPosition, ID.layer_objects, ID.player)); + return new Tile(); +}; + +Level.prototype.loadEndTile = function (x, y) { + var tiles = this.find(ID.tiles); + var exit = new powerupjs.SpriteGameObject(sprites.goal, ID.layer_objects, ID.exit); + exit.position = new powerupjs.Vector2(x * tiles.cellWidth, (y + 1) * tiles.cellHeight); + exit.origin = new powerupjs.Vector2(0, exit.height); + this.add(exit); + return new Tile(); +}; + +Level.prototype.loadWaterTile = function (x, y) { + var tiles = this.find(ID.tiles); + var w = new WaterDrop(ID.layer_objects); + w.origin = w.center.copy(); + w.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 0.5) * tiles.cellHeight - 10); + this._waterdrops.add(w); + return new Tile(); +}; + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + if (!this._quitButton.pressed) + return; + this.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/states/HelpState.js b/26_GamePhysics/TickTick2/states/HelpState.js new file mode 100644 index 0000000..c71e040 --- /dev/null +++ b/26_GamePhysics/TickTick2/states/HelpState.js @@ -0,0 +1,21 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help, ID.layer_background); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(this.backButton.screenCenterX, 750); + this.add(this.backButton); +} +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/states/LevelMenuState.js b/26_GamePhysics/TickTick2/states/LevelMenuState.js new file mode 100644 index 0000000..c00b239 --- /dev/null +++ b/26_GamePhysics/TickTick2/states/LevelMenuState.js @@ -0,0 +1,46 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0, j = window.LEVELS.length; i < j; ++i) { + var row = Math.floor(i / 4); + var column = i % 4; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 20) + 390, row * (level.height + 20) + 180); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i++) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/states/PlayingState.js b/26_GamePhysics/TickTick2/states/PlayingState.js new file mode 100644 index 0000000..1b54c7c --- /dev/null +++ b/26_GamePhysics/TickTick2/states/PlayingState.js @@ -0,0 +1,75 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); + + if (this.currentLevel.gameOver) + powerupjs.GameStateManager.switchTo(ID.game_state_gameover); + else if (this.currentLevel.completed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelfinished); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= this.levels.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + this.levels.push(new Level(currLevel)); + } +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.tickTickLevels) { + window.LEVELS = JSON.parse(localStorage.tickTickLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.tickTickLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/26_GamePhysics/TickTick2/states/TitleMenuState.js b/26_GamePhysics/TickTick2/states/TitleMenuState.js new file mode 100644 index 0000000..0628cb6 --- /dev/null +++ b/26_GamePhysics/TickTick2/states/TitleMenuState.js @@ -0,0 +1,29 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the title screen + var titleScreen = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(titleScreen); + + // add a play button + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(this.playButton.screenCenterX, 540); + this.add(this.playButton); + + // add a help button + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(this.helpButton.screenCenterX, 600); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/LAB.min.js b/27_IntelligentEnemies/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/27_IntelligentEnemies/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/TickTick.html b/27_IntelligentEnemies/TickTick3/TickTick.html new file mode 100644 index 0000000..3691cd9 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/TickTick.html @@ -0,0 +1,70 @@ + + + + + + + + + + TickTick + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/TickTick.js b/27_IntelligentEnemies/TickTick3/TickTick.js new file mode 100644 index 0000000..84cfafe --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/TickTick.js @@ -0,0 +1,113 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/" + sprite/*, collisionMask*/); + }; + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/TickTick/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("backgrounds/spr_title.jpg"); + sprites.background_help = loadSprite("backgrounds/spr_help.jpg"); + sprites.background_sky = loadSprite("backgrounds/spr_sky.jpg"); + sprites.cloud_1 = loadSprite("backgrounds/spr_cloud_1.png"); + sprites.cloud_2 = loadSprite("backgrounds/spr_cloud_2.png"); + sprites.cloud_3 = loadSprite("backgrounds/spr_cloud_3.png"); + sprites.cloud_4 = loadSprite("backgrounds/spr_cloud_4.png"); + sprites.cloud_5 = loadSprite("backgrounds/spr_cloud_5.png"); + sprites.mountain_1 = loadSprite("backgrounds/spr_mountain_1.png"); + sprites.mountain_2 = loadSprite("backgrounds/spr_mountain_2.png"); + sprites.levelselect = loadSprite("backgrounds/spr_levelselect.jpg"); + + if (powerupjs.Touch.isTouchDevice) { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_tap.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_tap.png"); + } else { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_click.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_click.png"); + } + sprites.frame_hint = loadSprite("overlays/spr_frame_hint.png"); + sprites.timer = loadSprite("overlays/spr_timer.png"); + + sprites.button_play = loadSprite("gui/spr_button_play.png"); + sprites.button_help = loadSprite("gui/spr_button_help.png"); + sprites.button_back = loadSprite("gui/spr_button_back.png"); + sprites.button_quit = loadSprite("gui/spr_button_quit.png"); + sprites.level_solved = loadSprite("gui/spr_level_solved.png"); + sprites.level_unsolved = loadSprite("gui/spr_level_unsolved.png"); + sprites.level_locked = loadSprite("gui/spr_level_locked.png"); + sprites.buttons_player = loadSprite("gui/spr_buttons_player@3.png"); + + sprites.wall = loadSprite("tiles/spr_wall.png"); + sprites.wall_hot = loadSprite("tiles/spr_wall_hot.png"); + sprites.wall_ice = loadSprite("tiles/spr_wall_ice.png"); + sprites.platform = loadSprite("tiles/spr_platform.png"); + sprites.platform_hot = loadSprite("tiles/spr_platform_hot.png"); + sprites.platform_ice = loadSprite("tiles/spr_platform_ice.png"); + + sprites.goal = loadSprite("spr_goal.png", true); + sprites.water = loadSprite("spr_water.png"); + + // player animations + sprites.player_idle = loadSprite("player/spr_idle.png", true); + sprites.player_run = loadSprite("player/spr_run@13.png", true); + sprites.player_jump = loadSprite("player/spr_jump@14.png", true); + sprites.player_celebrate = loadSprite("player/spr_celebrate@14.png"); + sprites.player_die = loadSprite("player/spr_die@5.png"); + sprites.player_explode = loadSprite("player/spr_explode@5x5.png"); + + // enemy animations + sprites.rocket = loadSprite("rocket/spr_rocket@3.png", true); + sprites.flame = loadSprite("flame/spr_flame@9.png", true); + sprites.turtle_sneeze = loadSprite("turtle/spr_sneeze@9.png", true); + sprites.turtle_idle = loadSprite("turtle/spr_idle.png", true); + sprites.sparky_electrocute = loadSprite("sparky/spr_electrocute@6x5.png", true); + sprites.sparky_idle = loadSprite("sparky/spr_idle.png", true); + + sounds.music = loadSound("snd_music", true); + sounds.player_die = loadSound("snd_player_die"); + sounds.player_explode = loadSound("snd_player_explode"); + sounds.player_fall = loadSound("snd_player_fall"); + sounds.player_jump = loadSound("snd_player_jump"); + sounds.player_won = loadSound("snd_player_won"); + sounds.water_collected = loadSound("snd_water_collected"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.player = 1; + ID.timer = 2; + ID.tiles = 3; + ID.exit = 4; + ID.hint_timer = 5; + ID.button_walkleft = 6; + ID.button_walkright = 7; + ID.button_jump = 8; + + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/game-layout.css b/27_IntelligentEnemies/TickTick3/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/Player.js b/27_IntelligentEnemies/TickTick3/gameobjects/Player.js new file mode 100644 index 0000000..e9666c3 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/Player.js @@ -0,0 +1,144 @@ +"use strict"; + +function Player(start, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + this._startPosition = start; + this._previousYPosition = 0; + + this.onTheGround = false; + this.alive = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + this.finished = false; + this.exploded = false; + + this.loadAnimation(sprites.player_idle, "idle", true); + this.loadAnimation(sprites.player_run, "run", true, 0.05); + this.loadAnimation(sprites.player_jump, "jump", false, 0.05); + this.loadAnimation(sprites.player_celebrate, "celebrate", false, 0.05); + this.loadAnimation(sprites.player_die, "die", false); + this.loadAnimation(sprites.player_explode, "explode", false, 0.04); + + this.reset(); +} + +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.reset = function () { + this.playAnimation("idle"); + + this._previousYPosition = this.boundingBox.bottom; + this.position = this._startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + + this.onTheGround = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +}; + +Player.prototype.handleInput = function (delta) { + var walkingSpeed = 400; + if (this.walkingOnIce) { + walkingSpeed *= 1.5; + this.velocity.x = Math.sign(this.velocity.x) * walkingSpeed; + } else if (this.onTheGround) + this.velocity.x = 0; + + if (powerupjs.Touch.isTouchDevice) { + // get the buttons + var walkLeftButton = this.root.find(ID.button_walkleft); + var walkRightButton = this.root.find(ID.button_walkright); + if (walkLeftButton.down) + this.velocity.x = -walkingSpeed; + else if (walkRightButton.down) + this.velocity.x = walkingSpeed; + } else { + if (powerupjs.Keyboard.down(powerupjs.Keys.left)) + this.velocity.x = -walkingSpeed; + else if (powerupjs.Keyboard.down(powerupjs.Keys.right)) + this.velocity.x = walkingSpeed; + } + + if (this.velocity.x != 0) + this.mirror = this.velocity.x < 0; + + if (powerupjs.Touch.isTouchDevice) { + var jumpButton = this.root.find(ID.button_jump); + if (jumpButton.pressed && this.onTheGround) + this.jump(); + } else { + if (powerupjs.Keyboard.pressed(powerupjs.Keys.space) && this.onTheGround) + this.jump(); + } +}; + +Player.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + this.doPhysics(); + if (!this.alive || this.finished) + return; + + if (this.onTheGround) + if (this.velocity.x == 0) + this.playAnimation("idle"); + else + this.playAnimation("run"); + else if (this.velocity.y < 0) + this.playAnimation("jump"); +}; + +Player.prototype.jump = function (speed) { + sounds.player_jump.play(); + speed = typeof speed !== 'undefined' ? speed : 1100; + this.velocity.y = -speed; +}; + +Player.prototype.doPhysics = function () { + this.velocity.y += 55; + this.handleCollisions(); +}; + +Player.prototype.handleCollisions = function () { + this.onTheGround = false; + this.walkingOnIce = false; + this.walkingOnHot = false; + + var tiles = this.root.find(ID.tiles); + + var x_floor = Math.floor(this.position.x / tiles.cellWidth); + var y_floor = Math.floor(this.position.y / tiles.cellHeight); + + for (var y = y_floor - 2; y <= y_floor + 1; ++y) + for (var x = x_floor - 1; x <= x_floor + 1; ++x) { + var tileType = tiles.getTileType(x, y); + if (tileType === TileType.background) + continue; + var tileBounds = new powerupjs.Rectangle(x * tiles.cellWidth, y * tiles.cellHeight, + tiles.cellWidth, tiles.cellHeight); + var boundingBox = this.boundingBox; + boundingBox.height += 1; + if (!tileBounds.intersects(boundingBox)) + continue; + var depth = boundingBox.calculateIntersectionDepth(tileBounds); + if (Math.abs(depth.x) < Math.abs(depth.y)) { + if (tileType === TileType.normal) + this.position.x += depth.x; + continue; + } + if (this._previousYPosition <= tileBounds.top && tileType !== TileType.background) { + this.onTheGround = true; + this.velocity.y = 0; + var currentTile = tiles.at(x, y); + if (currentTile !== null) { + this.walkingOnIce = this.walkingOnIce || currentTile.ice; + this.walkingOnHot = this.walkingOnHot || currentTile.hot; + } + } + if (tileType === TileType.normal || this.onTheGround) + this.position.y += depth.y + 1; + } + this._previousYPosition = this.position.y; +}; + diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/Tile.js b/27_IntelligentEnemies/TickTick3/gameobjects/Tile.js new file mode 100644 index 0000000..19a1c2e --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/Tile.js @@ -0,0 +1,24 @@ +"use strict"; + +var TileType = { + background: 0, + normal: 1, + platform: 2 +}; + +function Tile(sprite, tileTp, layer, id) { + sprite = typeof sprite !== 'undefined' ? sprite : sprites.wall; + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.hot = false; + this.ice = false; + this.type = typeof tileTp !== 'undefined' ? tileTp : TileType.background; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type == TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/TileField.js b/27_IntelligentEnemies/TickTick3/gameobjects/TileField.js new file mode 100644 index 0000000..83b0fe1 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/TileField.js @@ -0,0 +1,15 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + powerupjs.GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(powerupjs.GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (x, y) { + if (x < 0 || x >= this.columns) + return TileType.normal; + if (y < 0 || y >= this.rows) + return TileType.background; + return this.at(x, y).type; +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/WaterDrop.js b/27_IntelligentEnemies/TickTick3/gameobjects/WaterDrop.js new file mode 100644 index 0000000..a4e2b6a --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/WaterDrop.js @@ -0,0 +1,16 @@ +"use strict"; + +function WaterDrop(layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.water, layer, id); + this._bounce = 0; +} + +WaterDrop.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +WaterDrop.prototype.update = function (delta) { + this.position.y -= this._bounce; + var t = powerupjs.Game.totalTime * 3 + this.position.x; + this._bounce = Math.sin(t) * 5; + this.position.y += this._bounce; + powerupjs.SpriteGameObject.prototype.update.call(this, delta); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PatrollingEnemy.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PatrollingEnemy.js new file mode 100644 index 0000000..c1f8ee6 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PatrollingEnemy.js @@ -0,0 +1,42 @@ +"use strict"; + +function PatrollingEnemy(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this._waitTime = 0; + this.velocity.x = 120; + + this.loadAnimation(sprites.flame, "default", true); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} +PatrollingEnemy.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +PatrollingEnemy.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this._waitTime > 0) { + this._waitTime -= delta; + if (this._waitTime <= 0) + this.turnAround(); + } + else { + var tiles = this.root.find(ID.tiles); + var posX = this.boundingBox.left; + if (!this.mirror) + posX = this.boundingBox.right; + var tileX = Math.floor(posX / tiles.cellWidth); + var tileY = Math.floor(this.position.y / tiles.cellHeight); + if (tiles.getTileType(tileX, tileY - 1) === TileType.normal || + tiles.getTileType(tileX, tileY) === TileType.background) { + this._waitTime = 0.5; + this.velocity.x = 0; + } + } +}; + +PatrollingEnemy.prototype.turnAround = function () { + this.mirror = !this.mirror; + this.velocity.x = 120; + if (this.mirror) + this.velocity.x = -this.velocity.x; +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PlayerFollowingEnemy.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PlayerFollowingEnemy.js new file mode 100644 index 0000000..f2eb88d --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/PlayerFollowingEnemy.js @@ -0,0 +1,17 @@ +"use strict"; + +function PlayerFollowingEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +PlayerFollowingEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +PlayerFollowingEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + + var player = this.root.find(ID.player); + var direction = player.position.x - this.position.x; + if (Math.sign(direction) !== Math.sign(this.velocity.x) && player.velocity.x !== 0 + && this.velocity.x !== 0) + this.turnAround(); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Rocket.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Rocket.js new file mode 100644 index 0000000..8b6bfb2 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Rocket.js @@ -0,0 +1,41 @@ +"use strict"; + +function Rocket(moveToLeft, startPosition, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.spawnTime = 0; + this.startPosition = startPosition; + this.mirror = moveToLeft; + + this.loadAnimation(sprites.rocket, "default", true, 0.5); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Rocket.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Rocket.prototype.reset = function () { + this.visible = false; + this.position = this.startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + this.spawnTime = Math.random() * 5; +}; + +Rocket.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.spawnTime > 0) { + this.spawnTime -= delta; + return; + } + this.visible = true; + this.velocity.x = 600; + if (this.mirror) + this.velocity.x *= -1; + // check if we are far enough outside the screen + var screenBox = new powerupjs.Rectangle(-200, -200, powerupjs.Game.size.x + 400, + powerupjs.Game.size.y + 400); + if (!screenBox.intersects(this.boundingBox)) + this.reset(); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Sparky.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Sparky.js new file mode 100644 index 0000000..235ec18 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Sparky.js @@ -0,0 +1,48 @@ +"use strict"; + +function Sparky(initialY, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.idleTime = 0; + this.yoffset = 0; + this.initialY = initialY; + + this.loadAnimation(sprites.sparky_electrocute, "electrocute", false); + this.loadAnimation(sprites.sparky_idle, "idle", true); + this.playAnimation("idle"); + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + this.reset(); +} + +Sparky.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Sparky.prototype.reset = function () { + this.idleTime = Math.random() * 5; + this.position.y = this.initialY; + this.yoffset = 120; + this.velocity = powerupjs.Vector2.zero; +}; + +Sparky.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.idleTime <= 0) { + this.playAnimation("electrocute"); + if (this.velocity.y != 0) { + // falling down or going up + this.yoffset -= this.velocity.y * delta; + if (this.yoffset <= 0) + this.velocity.y = 0; + else if (this.yoffset >= 120) + this.reset(); + } + else if (this.animationEnded()) + this.velocity.y = -60; + } + else { + this.playAnimation("idle"); + this.idleTime -= delta; + if (this.idleTime <= 0) + this.velocity.y = 300; + } +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Turtle.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Turtle.js new file mode 100644 index 0000000..d3a0d6c --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/Turtle.js @@ -0,0 +1,37 @@ +"use strict"; + +function Turtle(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.waitTime = 0; + this.sneezing = false; + + this.loadAnimation(sprites.turtle_sneeze, "sneeze", false); + this.loadAnimation(sprites.turtle_idle, "idle", true); + this.playAnimation("idle"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Turtle.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Turtle.prototype.reset = function () { + this.waitTime = 5; + this.sneezing = false; +}; + +Turtle.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.sneezing) + this.playAnimation("sneeze"); + else + this.playAnimation("idle"); + + if (this.waitTime > 0) + this.waitTime -= delta; + else { + this.sneezing = !this.sneezing; + this.waitTime = 5; + } +}; diff --git a/27_IntelligentEnemies/TickTick3/gameobjects/enemies/UnpredictableEnemy.js b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/UnpredictableEnemy.js new file mode 100644 index 0000000..cf009ed --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gameobjects/enemies/UnpredictableEnemy.js @@ -0,0 +1,16 @@ +"use strict"; + +function UnpredictableEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +UnpredictableEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +UnpredictableEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + if (this._waitTime <= 0 && Math.random() < 0.01) { + this.turnAround(); + this.velocity.x = Math.sign(this.velocity.x) * + Math.random() * 300; + } +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/gui/LevelButton.js b/27_IntelligentEnemies/TickTick3/gui/LevelButton.js new file mode 100644 index 0000000..45e419a --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/gui/LevelButton.js @@ -0,0 +1,57 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays_1); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_1); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_2); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width - 10, 10); + textLabel.color = powerupjs.Color.white; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouch(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/level/Level.js b/27_IntelligentEnemies/TickTick3/level/Level.js new file mode 100644 index 0000000..c113764 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/level/Level.js @@ -0,0 +1,192 @@ +"use strict"; + +function Level(levelIndex, id) { + powerupjs.GameObjectList.call(this, id); + + this._levelIndex = levelIndex; + this._waterdrops = new powerupjs.GameObjectList(ID.layer_objects); + this._enemies = new powerupjs.GameObjectList(ID.layer_objects); + this._quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + + this._quitButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - this._quitButton.width - 10, 10); + this.add(this._quitButton); + + this.add(this._waterdrops); + this.add(this._enemies); + + // backgrounds + var backgrounds = new powerupjs.GameObjectList(ID.layer_background); + var background_main = new powerupjs.SpriteGameObject(sprites.background_sky, ID.layer_background_1); + background_main.position = new powerupjs.Vector2(0, powerupjs.Game.size.y - background_main.height); + backgrounds.add(background_main); + + this.add(backgrounds); + + this.loadTiles(); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Level.prototype.loadTiles = function () { + var levelData = window.LEVELS[this._levelIndex]; + + var hintField = new powerupjs.GameObjectList(ID.layer_overlays); + this.add(hintField); + var hintFrame = new powerupjs.SpriteGameObject(sprites.frame_hint, ID.layer_overlays_1); + hintField.position = new powerupjs.Vector2(hintFrame.screenCenterX, 10); + hintField.add(hintFrame); + var hintText = new powerupjs.Label("Arial", "14pt", ID.layer_overlays_2); + hintText.text = levelData.hint; + hintText.position = new powerupjs.Vector2(120, 25); + hintText.color = powerupjs.Color.black; + hintField.add(hintText); + + if (powerupjs.Touch.isTouchDevice) { + var walkLeftButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkleft); + walkLeftButton.position = new powerupjs.Vector2(10, 500); + this.add(walkLeftButton); + var walkRightButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkright); + walkRightButton.position = new powerupjs.Vector2(walkRightButton.width + 20, 500); + walkRightButton.sheetIndex = 1; + this.add(walkRightButton); + var jumpButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_jump); + jumpButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - jumpButton.width - 10, 500); + jumpButton.sheetIndex = 2; + this.add(jumpButton); + } + + var tiles = new TileField(levelData.tiles.length, levelData.tiles[0].length, 1, ID.tiles); + this.add(tiles); + tiles.cellWidth = 72; + tiles.cellHeight = 55; + for (var y = 0, ly = tiles.rows; y < ly; ++y) + for (var x = 0, lx = tiles.columns; x < lx; ++x) { + var t = this.loadTile(levelData.tiles[y][x], x, y); + tiles.add(t, x, y); + } +}; + +Level.prototype.loadTile = function (tileType, x, y) { + switch (tileType) { + case '.': + return new Tile(); + case '-': + return this.loadBasicTile(sprites.platform, TileType.platform); + case '+': + return this.loadBasicTile(sprites.platform_hot, TileType.platform, true, false); + case '@': + return this.loadBasicTile(sprites.platform_ice, TileType.platform, false, true); + case 'X': + return this.loadEndTile(x, y); + case 'W': + return this.loadWaterTile(x, y); + case '1': + return this.loadStartTile(x, y); + case '#': + return this.loadBasicTile(sprites.wall, TileType.normal); + case '^': + return this.loadBasicTile(sprites.wall_hot, TileType.normal, true, false); + case '*': + return this.loadBasicTile(sprites.wall_ice, TileType.normal, false, true); + case 'R': + return this.loadRocketTile(x, y, true); + case 'r': + return this.loadRocketTile(x, y, false); + case 'S': + return this.loadSparkyTile(x, y); + case 'T': + return this.loadTurtleTile(x, y); + case 'A': + case 'B': + case 'C': + return this.loadFlameTile(x, y, tileType); + + default: + return new Tile(); + } +}; + +Level.prototype.loadBasicTile = function (id, tileType, hot, ice) { + var t = new Tile(id, tileType); + t.hot = hot; + t.ice = ice; + return t; +}; + +Level.prototype.loadStartTile = function (x, y) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this.add(new Player(startPosition, ID.layer_objects, ID.player)); + return new Tile(); +}; + +Level.prototype.loadEndTile = function (x, y) { + var tiles = this.find(ID.tiles); + var exit = new powerupjs.SpriteGameObject(sprites.goal, ID.layer_objects, ID.exit); + exit.position = new powerupjs.Vector2(x * tiles.cellWidth, (y + 1) * tiles.cellHeight); + exit.origin = new powerupjs.Vector2(0, exit.height); + this.add(exit); + return new Tile(); +}; + +Level.prototype.loadWaterTile = function (x, y) { + var tiles = this.find(ID.tiles); + var w = new WaterDrop(ID.layer_objects); + w.origin = w.center.copy(); + w.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 0.5) * tiles.cellHeight - 10); + this._waterdrops.add(w); + return new Tile(); +}; + +Level.prototype.loadRocketTile = function (x, y, moveToLeft) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + var enemy = new Rocket(moveToLeft, startPosition, ID.layer_objects); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadTurtleTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Turtle(ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight + 25); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadSparkyTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Sparky((y + 1) * tiles.cellHeight - 100, ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight - 100); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadFlameTile = function (x, y, enemyType) { + var enemy = null; + switch (enemyType) { + case 'A': + enemy = new UnpredictableEnemy(ID.layer_objects); + break; + case 'B': + enemy = new PlayerFollowingEnemy(ID.layer_objects); + break; + case 'C': + default: + enemy = new PatrollingEnemy(ID.layer_objects); + break; + } + var tiles = this.find(ID.tiles); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + if (!this._quitButton.pressed) + return; + this.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/states/HelpState.js b/27_IntelligentEnemies/TickTick3/states/HelpState.js new file mode 100644 index 0000000..c71e040 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/states/HelpState.js @@ -0,0 +1,21 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help, ID.layer_background); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(this.backButton.screenCenterX, 750); + this.add(this.backButton); +} +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/states/LevelMenuState.js b/27_IntelligentEnemies/TickTick3/states/LevelMenuState.js new file mode 100644 index 0000000..c00b239 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/states/LevelMenuState.js @@ -0,0 +1,46 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0, j = window.LEVELS.length; i < j; ++i) { + var row = Math.floor(i / 4); + var column = i % 4; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 20) + 390, row * (level.height + 20) + 180); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i++) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/states/PlayingState.js b/27_IntelligentEnemies/TickTick3/states/PlayingState.js new file mode 100644 index 0000000..383b06d --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/states/PlayingState.js @@ -0,0 +1,83 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); + + //--- CODE ADDED FOR TESTING --- + // In the lines below, I set all levels to unlocked so you can test the additions to the game. + // In the final version of the game (Ch 29), these lines are removed again + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + window.LEVELS[currLevel].locked = false; + } + //--- END CODE ADDED FOR TESTING --- +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); + + if (this.currentLevel.gameOver) + powerupjs.GameStateManager.switchTo(ID.game_state_gameover); + else if (this.currentLevel.completed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelfinished); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= this.levels.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + this.levels.push(new Level(currLevel)); + } +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.tickTickLevels) { + window.LEVELS = JSON.parse(localStorage.tickTickLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.tickTickLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/27_IntelligentEnemies/TickTick3/states/TitleMenuState.js b/27_IntelligentEnemies/TickTick3/states/TitleMenuState.js new file mode 100644 index 0000000..0628cb6 --- /dev/null +++ b/27_IntelligentEnemies/TickTick3/states/TitleMenuState.js @@ -0,0 +1,29 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the title screen + var titleScreen = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(titleScreen); + + // add a play button + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(this.playButton.screenCenterX, 540); + this.add(this.playButton); + + // add a help button + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(this.helpButton.screenCenterX, 600); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/LAB.min.js b/28_PlayerInteraction/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/28_PlayerInteraction/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/TickTick.html b/28_PlayerInteraction/TickTick4/TickTick.html new file mode 100644 index 0000000..3691cd9 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/TickTick.html @@ -0,0 +1,70 @@ + + + + + + + + + + TickTick + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/TickTick.js b/28_PlayerInteraction/TickTick4/TickTick.js new file mode 100644 index 0000000..8b490b7 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/TickTick.js @@ -0,0 +1,113 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/" + sprite/*, collisionMask*/); + } + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/TickTick/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("backgrounds/spr_title.jpg"); + sprites.background_help = loadSprite("backgrounds/spr_help.jpg"); + sprites.background_sky = loadSprite("backgrounds/spr_sky.jpg"); + sprites.cloud_1 = loadSprite("backgrounds/spr_cloud_1.png"); + sprites.cloud_2 = loadSprite("backgrounds/spr_cloud_2.png"); + sprites.cloud_3 = loadSprite("backgrounds/spr_cloud_3.png"); + sprites.cloud_4 = loadSprite("backgrounds/spr_cloud_4.png"); + sprites.cloud_5 = loadSprite("backgrounds/spr_cloud_5.png"); + sprites.mountain_1 = loadSprite("backgrounds/spr_mountain_1.png"); + sprites.mountain_2 = loadSprite("backgrounds/spr_mountain_2.png"); + sprites.levelselect = loadSprite("backgrounds/spr_levelselect.jpg"); + + if (powerupjs.Touch.isTouchDevice) { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_tap.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_tap.png"); + } else { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_click.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_click.png"); + } + sprites.frame_hint = loadSprite("overlays/spr_frame_hint.png"); + sprites.timer = loadSprite("overlays/spr_timer.png"); + + sprites.button_play = loadSprite("gui/spr_button_play.png"); + sprites.button_help = loadSprite("gui/spr_button_help.png"); + sprites.button_back = loadSprite("gui/spr_button_back.png"); + sprites.button_quit = loadSprite("gui/spr_button_quit.png"); + sprites.level_solved = loadSprite("gui/spr_level_solved.png"); + sprites.level_unsolved = loadSprite("gui/spr_level_unsolved.png"); + sprites.level_locked = loadSprite("gui/spr_level_locked.png"); + sprites.buttons_player = loadSprite("gui/spr_buttons_player@3.png"); + + sprites.wall = loadSprite("tiles/spr_wall.png"); + sprites.wall_hot = loadSprite("tiles/spr_wall_hot.png"); + sprites.wall_ice = loadSprite("tiles/spr_wall_ice.png"); + sprites.platform = loadSprite("tiles/spr_platform.png"); + sprites.platform_hot = loadSprite("tiles/spr_platform_hot.png"); + sprites.platform_ice = loadSprite("tiles/spr_platform_ice.png"); + + sprites.goal = loadSprite("spr_goal.png", true); + sprites.water = loadSprite("spr_water.png"); + + // player animations + sprites.player_idle = loadSprite("player/spr_idle.png", true); + sprites.player_run = loadSprite("player/spr_run@13.png", true); + sprites.player_jump = loadSprite("player/spr_jump@14.png", true); + sprites.player_celebrate = loadSprite("player/spr_celebrate@14.png"); + sprites.player_die = loadSprite("player/spr_die@5.png"); + sprites.player_explode = loadSprite("player/spr_explode@5x5.png"); + + // enemy animations + sprites.rocket = loadSprite("rocket/spr_rocket@3.png", true); + sprites.flame = loadSprite("flame/spr_flame@9.png", true); + sprites.turtle_sneeze = loadSprite("turtle/spr_sneeze@9.png", true); + sprites.turtle_idle = loadSprite("turtle/spr_idle.png", true); + sprites.sparky_electrocute = loadSprite("sparky/spr_electrocute@6x5.png", true); + sprites.sparky_idle = loadSprite("sparky/spr_idle.png", true); + + sounds.music = loadSound("snd_music", true); + sounds.player_die = loadSound("snd_player_die"); + sounds.player_explode = loadSound("snd_player_explode"); + sounds.player_fall = loadSound("snd_player_fall"); + sounds.player_jump = loadSound("snd_player_jump"); + sounds.player_won = loadSound("snd_player_won"); + sounds.water_collected = loadSound("snd_water_collected"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.player = 1; + ID.timer = 2; + ID.tiles = 3; + ID.exit = 4; + ID.hint_timer = 5; + ID.button_walkleft = 6; + ID.button_walkright = 7; + ID.button_jump = 8; + + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/game-layout.css b/28_PlayerInteraction/TickTick4/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/Player.js b/28_PlayerInteraction/TickTick4/gameobjects/Player.js new file mode 100644 index 0000000..c290e37 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/Player.js @@ -0,0 +1,166 @@ +"use strict"; + +function Player(start, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + this._startPosition = start; + this._previousYPosition = 0; + + this.onTheGround = false; + this.alive = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + this.finished = false; + this.exploded = false; + + this.loadAnimation(sprites.player_idle, "idle", true); + this.loadAnimation(sprites.player_run, "run", true, 0.05); + this.loadAnimation(sprites.player_jump, "jump", false, 0.05); + this.loadAnimation(sprites.player_celebrate, "celebrate", false, 0.05); + this.loadAnimation(sprites.player_die, "die", false); + this.loadAnimation(sprites.player_explode, "explode", false, 0.04); + + this.reset(); +} + +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.reset = function () { + this.playAnimation("idle"); + + this._previousYPosition = this.boundingBox.bottom; + this.position = this._startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + + this.onTheGround = true; + this.alive = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +}; + +Player.prototype.handleInput = function (delta) { + if (!this.alive) + return; + var walkingSpeed = 400; + if (this.walkingOnIce) { + walkingSpeed *= 1.5; + this.velocity.x = Math.sign(this.velocity.x) * walkingSpeed; + } else if (this.onTheGround) + this.velocity.x = 0; + + if (powerupjs.Touch.isTouchDevice) { + // get the buttons + var walkLeftButton = this.root.find(ID.button_walkleft); + var walkRightButton = this.root.find(ID.button_walkright); + if (walkLeftButton.down) + this.velocity.x = -walkingSpeed; + else if (walkRightButton.down) + this.velocity.x = walkingSpeed; + } else { + if (powerupjs.Keyboard.down(powerupjs.Keys.left)) + this.velocity.x = -walkingSpeed; + else if (powerupjs.Keyboard.down(powerupjs.Keys.right)) + this.velocity.x = walkingSpeed; + } + + if (this.velocity.x != 0) + this.mirror = this.velocity.x < 0; + + if (powerupjs.Touch.isTouchDevice) { + var jumpButton = this.root.find(ID.button_jump); + if (jumpButton.pressed && this.onTheGround) + this.jump(); + } else { + if (powerupjs.Keyboard.pressed(powerupjs.Keys.space) && this.onTheGround) + this.jump(); + } +}; + +Player.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + this.doPhysics(); + if (!this.alive) + return; + + if (this.onTheGround) + if (this.velocity.x == 0) + this.playAnimation("idle"); + else + this.playAnimation("run"); + else if (this.velocity.y < 0) + this.playAnimation("jump"); + + var tiles = this.root.find(ID.tiles); + if (this.boundingBox.top >= tiles.rows * tiles.cellHeight) + this.die(true); +}; + +Player.prototype.die = function (falling) { + if (!this.alive) + return; + this.alive = false; + this.velocity.x = 0; + if (falling) { + sounds.player_fall.play(); + } + else { + this.velocity.y = -900; + sounds.player_die.play(); + } + this.playAnimation("die"); +}; + +Player.prototype.jump = function (speed) { + sounds.player_jump.play(); + speed = typeof speed !== 'undefined' ? speed : 1100; + this.velocity.y = -speed; +}; + +Player.prototype.doPhysics = function () { + this.velocity.y += 55; + if (this.alive) + this.handleCollisions(); +}; + +Player.prototype.handleCollisions = function () { + this.onTheGround = false; + this.walkingOnIce = false; + this.walkingOnHot = false; + + var tiles = this.root.find(ID.tiles); + + var x_floor = Math.floor(this.position.x / tiles.cellWidth); + var y_floor = Math.floor(this.position.y / tiles.cellHeight); + + for (var y = y_floor - 2; y <= y_floor + 1; ++y) + for (var x = x_floor - 1; x <= x_floor + 1; ++x) { + var tileType = tiles.getTileType(x, y); + if (tileType === TileType.background) + continue; + var tileBounds = new powerupjs.Rectangle(x * tiles.cellWidth, y * tiles.cellHeight, + tiles.cellWidth, tiles.cellHeight); + var boundingBox = this.boundingBox; + boundingBox.height += 1; + if (!tileBounds.intersects(boundingBox)) + continue; + var depth = boundingBox.calculateIntersectionDepth(tileBounds); + if (Math.abs(depth.x) < Math.abs(depth.y)) { + if (tileType === TileType.normal) + this.position.x += depth.x; + continue; + } + if (this._previousYPosition <= tileBounds.top && tileType !== TileType.background) { + this.onTheGround = true; + this.velocity.y = 0; + var currentTile = tiles.at(x, y); + if (currentTile !== null) { + this.walkingOnIce = this.walkingOnIce || currentTile.ice; + this.walkingOnHot = this.walkingOnHot || currentTile.hot; + } + } + if (tileType === TileType.normal || this.onTheGround) + this.position.y += depth.y + 1; + } + this._previousYPosition = this.position.y; +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/Tile.js b/28_PlayerInteraction/TickTick4/gameobjects/Tile.js new file mode 100644 index 0000000..19a1c2e --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/Tile.js @@ -0,0 +1,24 @@ +"use strict"; + +var TileType = { + background: 0, + normal: 1, + platform: 2 +}; + +function Tile(sprite, tileTp, layer, id) { + sprite = typeof sprite !== 'undefined' ? sprite : sprites.wall; + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.hot = false; + this.ice = false; + this.type = typeof tileTp !== 'undefined' ? tileTp : TileType.background; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type == TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; diff --git a/28_PlayerInteraction/TickTick4/gameobjects/TileField.js b/28_PlayerInteraction/TickTick4/gameobjects/TileField.js new file mode 100644 index 0000000..83b0fe1 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/TileField.js @@ -0,0 +1,15 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + powerupjs.GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(powerupjs.GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (x, y) { + if (x < 0 || x >= this.columns) + return TileType.normal; + if (y < 0 || y >= this.rows) + return TileType.background; + return this.at(x, y).type; +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/WaterDrop.js b/28_PlayerInteraction/TickTick4/gameobjects/WaterDrop.js new file mode 100644 index 0000000..06adbe8 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/WaterDrop.js @@ -0,0 +1,22 @@ +"use strict"; + +function WaterDrop(layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.water, layer, id); + this._bounce = 0; +} + +WaterDrop.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +WaterDrop.prototype.update = function (delta) { + this.position.y -= this._bounce; + var t = powerupjs.Game.totalTime * 3 + this.position.x; + this._bounce = Math.sin(t) * 5; + this.position.y += this._bounce; + powerupjs.SpriteGameObject.prototype.update.call(this, delta); + + var player = this.root.find(ID.player); + if (this.collidesWith(player)) { + this.visible = false; + sounds.water_collected.play(); + } +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/PatrollingEnemy.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/PatrollingEnemy.js new file mode 100644 index 0000000..706571d --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/PatrollingEnemy.js @@ -0,0 +1,49 @@ +"use strict"; + +function PatrollingEnemy(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this._waitTime = 0; + this.velocity.x = 120; + + this.loadAnimation(sprites.flame, "default", true); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} +PatrollingEnemy.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +PatrollingEnemy.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this._waitTime > 0) { + this._waitTime -= delta; + if (this._waitTime <= 0) + this.turnAround(); + } + else { + var tiles = this.root.find(ID.tiles); + var posX = this.boundingBox.left; + if (!this.mirror) + posX = this.boundingBox.right; + var tileX = Math.floor(posX / tiles.cellWidth); + var tileY = Math.floor(this.position.y / tiles.cellHeight); + if (tiles.getTileType(tileX, tileY - 1) === TileType.normal || + tiles.getTileType(tileX, tileY) === TileType.background) { + this._waitTime = 0.5; + this.velocity.x = 0; + } + } + this.checkPlayerCollision(); +}; + +PatrollingEnemy.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.collidesWith(player)) + player.die(false); +}; + +PatrollingEnemy.prototype.turnAround = function () { + this.mirror = !this.mirror; + this.velocity.x = 120; + if (this.mirror) + this.velocity.x = -this.velocity.x; +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/PlayerFollowingEnemy.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/PlayerFollowingEnemy.js new file mode 100644 index 0000000..f2eb88d --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/PlayerFollowingEnemy.js @@ -0,0 +1,17 @@ +"use strict"; + +function PlayerFollowingEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +PlayerFollowingEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +PlayerFollowingEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + + var player = this.root.find(ID.player); + var direction = player.position.x - this.position.x; + if (Math.sign(direction) !== Math.sign(this.velocity.x) && player.velocity.x !== 0 + && this.velocity.x !== 0) + this.turnAround(); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/Rocket.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Rocket.js new file mode 100644 index 0000000..db0ef7d --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Rocket.js @@ -0,0 +1,48 @@ +"use strict"; + +function Rocket(moveToLeft, startPosition, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.spawnTime = 0; + this.startPosition = startPosition; + this.mirror = moveToLeft; + + this.loadAnimation(sprites.rocket, "default", true, 0.5); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Rocket.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Rocket.prototype.reset = function () { + this.visible = false; + this.position = this.startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + this.spawnTime = Math.random() * 5; +}; + +Rocket.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.spawnTime > 0) { + this.spawnTime -= delta; + return; + } + this.visible = true; + this.velocity.x = 600; + if (this.mirror) + this.velocity.x *= -1; + // check if we are far enough outside the screen + var screenBox = new powerupjs.Rectangle(-200, -200, powerupjs.Game.size.x + 400, + powerupjs.Game.size.y + 400); + if (!screenBox.intersects(this.boundingBox)) + this.reset(); + this.checkPlayerCollision(); +}; + +Rocket.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.collidesWith(player)) + player.die(false); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/Sparky.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Sparky.js new file mode 100644 index 0000000..8460a7a --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Sparky.js @@ -0,0 +1,55 @@ +"use strict"; + +function Sparky(initialY, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.idleTime = 0; + this.yoffset = 0; + this.initialY = initialY; + + this.loadAnimation(sprites.sparky_electrocute, "electrocute", false); + this.loadAnimation(sprites.sparky_idle, "idle", true); + this.playAnimation("idle"); + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + this.reset(); +} + +Sparky.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Sparky.prototype.reset = function () { + this.idleTime = Math.random() * 5; + this.position.y = this.initialY; + this.yoffset = 120; + this.velocity = powerupjs.Vector2.zero; +}; + +Sparky.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.idleTime <= 0) { + this.playAnimation("electrocute"); + if (this.velocity.y != 0) { + // falling down or going up + this.yoffset -= this.velocity.y * delta; + if (this.yoffset <= 0) + this.velocity.y = 0; + else if (this.yoffset >= 120) + this.reset(); + } + else if (this.animationEnded()) + this.velocity.y = -60; + } + else { + this.playAnimation("idle"); + this.idleTime -= delta; + if (this.idleTime <= 0) + this.velocity.y = 300; + } + this.checkPlayerCollision(); +}; + +Sparky.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.idleTime <= 0 && this.collidesWith(player)) + player.die(false); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/Turtle.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Turtle.js new file mode 100644 index 0000000..576f43d --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/Turtle.js @@ -0,0 +1,49 @@ +"use strict"; + +function Turtle(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.waitTime = 0; + this.sneezing = false; + + this.loadAnimation(sprites.turtle_sneeze, "sneeze", false); + this.loadAnimation(sprites.turtle_idle, "idle", true); + this.playAnimation("idle"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Turtle.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Turtle.prototype.reset = function () { + this.waitTime = 5; + this.sneezing = false; +}; + +Turtle.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.sneezing) + this.playAnimation("sneeze"); + else + this.playAnimation("idle"); + + if (this.waitTime > 0) + this.waitTime -= delta; + else { + this.sneezing = !this.sneezing; + this.waitTime = 5; + } + this.checkPlayerCollision(); +}; + +Turtle.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (!this.collidesWith(player)) + return; + + if (this.sneezing) + player.die(false); + else if (player.velocity.y > 0 && player.alive) + player.jump(1500); +}; diff --git a/28_PlayerInteraction/TickTick4/gameobjects/enemies/UnpredictableEnemy.js b/28_PlayerInteraction/TickTick4/gameobjects/enemies/UnpredictableEnemy.js new file mode 100644 index 0000000..cf009ed --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gameobjects/enemies/UnpredictableEnemy.js @@ -0,0 +1,16 @@ +"use strict"; + +function UnpredictableEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +UnpredictableEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +UnpredictableEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + if (this._waitTime <= 0 && Math.random() < 0.01) { + this.turnAround(); + this.velocity.x = Math.sign(this.velocity.x) * + Math.random() * 300; + } +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/gui/LevelButton.js b/28_PlayerInteraction/TickTick4/gui/LevelButton.js new file mode 100644 index 0000000..45e419a --- /dev/null +++ b/28_PlayerInteraction/TickTick4/gui/LevelButton.js @@ -0,0 +1,57 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays_1); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_1); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_2); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width - 10, 10); + textLabel.color = powerupjs.Color.white; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouch(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/level/Level.js b/28_PlayerInteraction/TickTick4/level/Level.js new file mode 100644 index 0000000..84c1b6b --- /dev/null +++ b/28_PlayerInteraction/TickTick4/level/Level.js @@ -0,0 +1,191 @@ +"use strict"; + +function Level(levelIndex, id) { + powerupjs.GameObjectList.call(this, id); + + this._levelIndex = levelIndex; + this._waterdrops = new powerupjs.GameObjectList(ID.layer_objects); + this._enemies = new powerupjs.GameObjectList(ID.layer_objects); + this._quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + + this._quitButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - this._quitButton.width - 10, 10); + this.add(this._quitButton); + + this.add(this._waterdrops); + this.add(this._enemies); + + // backgrounds + var backgrounds = new powerupjs.GameObjectList(ID.layer_background); + var background_main = new powerupjs.SpriteGameObject(sprites.background_sky, ID.layer_background_1); + background_main.position = new powerupjs.Vector2(0, powerupjs.Game.size.y - background_main.height); + backgrounds.add(background_main); + this.add(backgrounds); + + this.loadTiles(); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Level.prototype.loadTiles = function () { + var levelData = window.LEVELS[this._levelIndex]; + + var hintField = new powerupjs.GameObjectList(ID.layer_overlays); + this.add(hintField); + var hintFrame = new powerupjs.SpriteGameObject(sprites.frame_hint, ID.layer_overlays_1); + hintField.position = new powerupjs.Vector2(hintFrame.screenCenterX, 10); + hintField.add(hintFrame); + var hintText = new powerupjs.Label("Arial", "14pt", ID.layer_overlays_2); + hintText.text = levelData.hint; + hintText.position = new powerupjs.Vector2(120, 25); + hintText.color = powerupjs.Color.black; + hintField.add(hintText); + + if (powerupjs.Touch.isTouchDevice) { + var walkLeftButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkleft); + walkLeftButton.position = new powerupjs.Vector2(10, 500); + this.add(walkLeftButton); + var walkRightButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkright); + walkRightButton.position = new powerupjs.Vector2(walkRightButton.width + 20, 500); + walkRightButton.sheetIndex = 1; + this.add(walkRightButton); + var jumpButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_jump); + jumpButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - jumpButton.width - 10, 500); + jumpButton.sheetIndex = 2; + this.add(jumpButton); + } + + var tiles = new TileField(levelData.tiles.length, levelData.tiles[0].length, 1, ID.tiles); + this.add(tiles); + tiles.cellWidth = 72; + tiles.cellHeight = 55; + for (var y = 0, ly = tiles.rows; y < ly; ++y) + for (var x = 0, lx = tiles.columns; x < lx; ++x) { + var t = this.loadTile(levelData.tiles[y][x], x, y); + tiles.add(t, x, y); + } +}; + +Level.prototype.loadTile = function (tileType, x, y) { + switch (tileType) { + case '.': + return new Tile(); + case '-': + return this.loadBasicTile(sprites.platform, TileType.platform); + case '+': + return this.loadBasicTile(sprites.platform_hot, TileType.platform, true, false); + case '@': + return this.loadBasicTile(sprites.platform_ice, TileType.platform, false, true); + case 'X': + return this.loadEndTile(x, y); + case 'W': + return this.loadWaterTile(x, y); + case '1': + return this.loadStartTile(x, y); + case '#': + return this.loadBasicTile(sprites.wall, TileType.normal); + case '^': + return this.loadBasicTile(sprites.wall_hot, TileType.normal, true, false); + case '*': + return this.loadBasicTile(sprites.wall_ice, TileType.normal, false, true); + case 'R': + return this.loadRocketTile(x, y, true); + case 'r': + return this.loadRocketTile(x, y, false); + case 'S': + return this.loadSparkyTile(x, y); + case 'T': + return this.loadTurtleTile(x, y); + case 'A': + case 'B': + case 'C': + return this.loadFlameTile(x, y, tileType); + + default: + return new Tile(); + } +}; + +Level.prototype.loadBasicTile = function (id, tileType, hot, ice) { + var t = new Tile(id, tileType); + t.hot = hot; + t.ice = ice; + return t; +}; + +Level.prototype.loadStartTile = function (x, y) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this.add(new Player(startPosition, ID.layer_objects, ID.player)); + return new Tile(); +}; + +Level.prototype.loadEndTile = function (x, y) { + var tiles = this.find(ID.tiles); + var exit = new powerupjs.SpriteGameObject(sprites.goal, ID.layer_objects, ID.exit); + exit.position = new powerupjs.Vector2(x * tiles.cellWidth, (y + 1) * tiles.cellHeight); + exit.origin = new powerupjs.Vector2(0, exit.height); + this.add(exit); + return new Tile(); +}; + +Level.prototype.loadWaterTile = function (x, y) { + var tiles = this.find(ID.tiles); + var w = new WaterDrop(ID.layer_objects); + w.origin = w.center.copy(); + w.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 0.5) * tiles.cellHeight - 10); + this._waterdrops.add(w); + return new Tile(); +}; + +Level.prototype.loadRocketTile = function (x, y, moveToLeft) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + var enemy = new Rocket(moveToLeft, startPosition, ID.layer_objects); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadTurtleTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Turtle(ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight + 25); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadSparkyTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Sparky((y + 1) * tiles.cellHeight - 100, ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight - 100); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadFlameTile = function (x, y, enemyType) { + var enemy = null; + switch (enemyType) { + case 'A': + enemy = new UnpredictableEnemy(ID.layer_objects); + break; + case 'B': + enemy = new PlayerFollowingEnemy(ID.layer_objects); + break; + case 'C': + default: + enemy = new PatrollingEnemy(ID.layer_objects); + break; + } + var tiles = this.find(ID.tiles); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + if (!this._quitButton.pressed) + return; + this.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/states/HelpState.js b/28_PlayerInteraction/TickTick4/states/HelpState.js new file mode 100644 index 0000000..c71e040 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/states/HelpState.js @@ -0,0 +1,21 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help, ID.layer_background); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(this.backButton.screenCenterX, 750); + this.add(this.backButton); +} +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/states/LevelMenuState.js b/28_PlayerInteraction/TickTick4/states/LevelMenuState.js new file mode 100644 index 0000000..c00b239 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/states/LevelMenuState.js @@ -0,0 +1,46 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0, j = window.LEVELS.length; i < j; ++i) { + var row = Math.floor(i / 4); + var column = i % 4; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 20) + 390, row * (level.height + 20) + 180); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i++) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/states/PlayingState.js b/28_PlayerInteraction/TickTick4/states/PlayingState.js new file mode 100644 index 0000000..52093c9 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/states/PlayingState.js @@ -0,0 +1,78 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); + + //--- CODE ADDED FOR TESTING --- + // In the lines below, I set all levels to unlocked so you can test the additions to the game. + // In the final version of the game (Ch 29), these lines are removed again + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + window.LEVELS[currLevel].locked = false; + } + //--- END CODE ADDED FOR TESTING --- +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= this.levels.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + this.levels.push(new Level(currLevel)); + } +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.tickTickLevels) { + window.LEVELS = JSON.parse(localStorage.tickTickLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.tickTickLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/28_PlayerInteraction/TickTick4/states/TitleMenuState.js b/28_PlayerInteraction/TickTick4/states/TitleMenuState.js new file mode 100644 index 0000000..0628cb6 --- /dev/null +++ b/28_PlayerInteraction/TickTick4/states/TitleMenuState.js @@ -0,0 +1,29 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the title screen + var titleScreen = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(titleScreen); + + // add a play button + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(this.playButton.screenCenterX, 540); + this.add(this.playButton); + + // add a help button + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(this.helpButton.screenCenterX, 600); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); +}; \ No newline at end of file diff --git a/29_FinishingGame/LAB.min.js b/29_FinishingGame/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/29_FinishingGame/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/TickTick.html b/29_FinishingGame/TickTickFinal/TickTick.html new file mode 100644 index 0000000..3bf4d10 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/TickTick.html @@ -0,0 +1,75 @@ + + + + + + + + + + TickTick + + + +
+ Your browser doesn't support canvas tag +
+ + \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/TickTick.js b/29_FinishingGame/TickTickFinal/TickTick.js new file mode 100644 index 0000000..607739d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/TickTick.js @@ -0,0 +1,115 @@ +"use strict"; + +var ID = {}; +var sprites = {}; +var sounds = {}; + +powerupjs.Game.loadAssets = function () { + var loadSprite = function (sprite, collisionMask) { + return new powerupjs.SpriteSheet("../../assets/TickTick/sprites/" + sprite/*, collisionMask*/); + }; + + var loadSound = function (sound, looping) { + return new powerupjs.Sound("../../assets/TickTick/sounds/" + sound, looping); + }; + + sprites.background_title = loadSprite("backgrounds/spr_title.jpg"); + sprites.background_help = loadSprite("backgrounds/spr_help.jpg"); + sprites.background_sky = loadSprite("backgrounds/spr_sky.jpg"); + sprites.cloud_1 = loadSprite("backgrounds/spr_cloud_1.png"); + sprites.cloud_2 = loadSprite("backgrounds/spr_cloud_2.png"); + sprites.cloud_3 = loadSprite("backgrounds/spr_cloud_3.png"); + sprites.cloud_4 = loadSprite("backgrounds/spr_cloud_4.png"); + sprites.cloud_5 = loadSprite("backgrounds/spr_cloud_5.png"); + sprites.mountain_1 = loadSprite("backgrounds/spr_mountain_1.png"); + sprites.mountain_2 = loadSprite("backgrounds/spr_mountain_2.png"); + sprites.levelselect = loadSprite("backgrounds/spr_levelselect.jpg"); + + if (powerupjs.Touch.isTouchDevice) { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_tap.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_tap.png"); + } else { + sprites.overlay_gameover = loadSprite("overlays/spr_gameover_click.png"); + sprites.overlay_welldone = loadSprite("overlays/spr_welldone_click.png"); + } + sprites.frame_hint = loadSprite("overlays/spr_frame_hint.png"); + sprites.timer = loadSprite("overlays/spr_timer.png"); + + sprites.button_play = loadSprite("gui/spr_button_play.png"); + sprites.button_help = loadSprite("gui/spr_button_help.png"); + sprites.button_back = loadSprite("gui/spr_button_back.png"); + sprites.button_quit = loadSprite("gui/spr_button_quit.png"); + sprites.level_solved = loadSprite("gui/spr_level_solved.png"); + sprites.level_unsolved = loadSprite("gui/spr_level_unsolved.png"); + sprites.level_locked = loadSprite("gui/spr_level_locked.png"); + sprites.buttons_player = loadSprite("gui/spr_buttons_player@3.png"); + + sprites.wall = loadSprite("tiles/spr_wall.png"); + sprites.wall_hot = loadSprite("tiles/spr_wall_hot.png"); + sprites.wall_ice = loadSprite("tiles/spr_wall_ice.png"); + sprites.platform = loadSprite("tiles/spr_platform.png"); + sprites.platform_hot = loadSprite("tiles/spr_platform_hot.png"); + sprites.platform_ice = loadSprite("tiles/spr_platform_ice.png"); + + sprites.goal = loadSprite("spr_goal.png", true); + sprites.water = loadSprite("spr_water.png"); + + // player animations + sprites.player_idle = loadSprite("player/spr_idle.png", true); + sprites.player_run = loadSprite("player/spr_run@13.png", true); + sprites.player_jump = loadSprite("player/spr_jump@14.png", true); + sprites.player_celebrate = loadSprite("player/spr_celebrate@14.png"); + sprites.player_die = loadSprite("player/spr_die@5.png"); + sprites.player_explode = loadSprite("player/spr_explode@5x5.png"); + + // enemy animations + sprites.rocket = loadSprite("rocket/spr_rocket@3.png", true); + sprites.flame = loadSprite("flame/spr_flame@9.png", true); + sprites.turtle_sneeze = loadSprite("turtle/spr_sneeze@9.png", true); + sprites.turtle_idle = loadSprite("turtle/spr_idle.png", true); + sprites.sparky_electrocute = loadSprite("sparky/spr_electrocute@6x5.png", true); + sprites.sparky_idle = loadSprite("sparky/spr_idle.png", true); + + sounds.music = loadSound("snd_music", true); + sounds.player_die = loadSound("snd_player_die"); + sounds.player_explode = loadSound("snd_player_explode"); + sounds.player_fall = loadSound("snd_player_fall"); + sounds.player_jump = loadSound("snd_player_jump"); + sounds.player_won = loadSound("snd_player_won"); + sounds.water_collected = loadSound("snd_water_collected"); +}; + +powerupjs.Game.initialize = function () { + // play the music + sounds.music.volume = 0.3; + sounds.music.play(); + + // define the layers + ID.layer_background = 1; + ID.layer_background_1 = 2; + ID.layer_background_2 = 3; + ID.layer_background_3 = 4; + ID.layer_tiles = 10; + ID.layer_objects = 20; + ID.layer_overlays = 30; + ID.layer_overlays_1 = 31; + ID.layer_overlays_2 = 32; + + // define object IDs + ID.player = 1; + ID.timer = 2; + ID.tiles = 3; + ID.exit = 4; + ID.hint_timer = 5; + ID.button_walkleft = 6; + ID.button_walkright = 7; + ID.button_jump = 8; + + ID.game_state_title = powerupjs.GameStateManager.add(new TitleMenuState()); + ID.game_state_help = powerupjs.GameStateManager.add(new HelpState()); + ID.game_state_playing = powerupjs.GameStateManager.add(new PlayingState()); + ID.game_state_levelselect = powerupjs.GameStateManager.add(new LevelMenuState()); + ID.game_state_gameover = powerupjs.GameStateManager.add(new GameOverState()); + ID.game_state_levelfinished = powerupjs.GameStateManager.add(new LevelFinishedState()); + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/game-layout.css b/29_FinishingGame/TickTickFinal/game-layout.css new file mode 100644 index 0000000..e3fa340 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/game-layout.css @@ -0,0 +1,13 @@ +html, body { + margin: 0; +} + +#gameArea { + position: absolute; +} + +#mycanvas { + position: absolute; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/Clouds.js b/29_FinishingGame/TickTickFinal/gameobjects/Clouds.js new file mode 100644 index 0000000..2a6bcf4 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/Clouds.js @@ -0,0 +1,34 @@ +"use strict"; + +function Clouds(layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + for (var i = 0; i < 3; i++) { + var cloud = new powerupjs.SpriteGameObject(sprites["cloud_" + Math.ceil(Math.random()*5)]); + cloud.position = new powerupjs.Vector2(Math.random() * powerupjs.Game.size.x - cloud.width / 2, + Math.random() * powerupjs.Game.size.y - cloud.height / 2); + cloud.velocity = new powerupjs.Vector2((Math.random() * 2 - 1) * 20, 0); + this.add(cloud); + } +} + +Clouds.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Clouds.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + + for (var i = 0, l = this.length; i < l; ++i) { + var c = this.at(i); + if ((c.velocity.x < 0 && c.position.x + c.width < 0) || (c.velocity.x > 0 && c.position.x > powerupjs.Game.size.x)) { + this.remove(c); + var cloud = new powerupjs.SpriteGameObject(sprites["cloud_" + Math.ceil(Math.random()*5)]); + cloud.velocity = new powerupjs.Vector2(((Math.random() * 2) - 1) * 20, 0); + var cloudHeight = Math.random() * powerupjs.Game.size.y - cloud.height / 2; + if (cloud.velocity.x < 0) + cloud.position = new powerupjs.Vector2(powerupjs.Game.size.x, cloudHeight); + else + cloud.position = new powerupjs.Vector2(-cloud.width, cloudHeight); + this.add(cloud); + return; + } + } +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/Player.js b/29_FinishingGame/TickTickFinal/gameobjects/Player.js new file mode 100644 index 0000000..3b44619 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/Player.js @@ -0,0 +1,195 @@ +"use strict"; + +function Player(start, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + this._startPosition = start; + this._previousYPosition = 0; + + this.onTheGround = false; + this.alive = true; + this.walkingOnIce = false; + this.walkingOnHot = false; + this.finished = false; + this.exploded = false; + + this.loadAnimation(sprites.player_idle, "idle", true); + this.loadAnimation(sprites.player_run, "run", true, 0.05); + this.loadAnimation(sprites.player_jump, "jump", false, 0.05); + this.loadAnimation(sprites.player_celebrate, "celebrate", false, 0.05); + this.loadAnimation(sprites.player_die, "die", false); + this.loadAnimation(sprites.player_explode, "explode", false, 0.04); + + this.reset(); +} + +Player.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Player.prototype.reset = function () { + this.playAnimation("idle"); + + this._previousYPosition = this.boundingBox.bottom; + this.position = this._startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + + this.onTheGround = true; + this.alive = true; + this.exploded = false; + this.walkingOnIce = false; + this.walkingOnHot = false; + this.finished = false; + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +}; + +Player.prototype.handleInput = function (delta) { + if (!this.alive || this.finished) + return; + var walkingSpeed = 400; + if (this.walkingOnIce) { + walkingSpeed *= 1.5; + this.velocity.x = Math.sign(this.velocity.x) * walkingSpeed; + } else if (this.onTheGround) + this.velocity.x = 0; + + if (powerupjs.Touch.isTouchDevice) { + // get the buttons + var walkLeftButton = this.root.find(ID.button_walkleft); + var walkRightButton = this.root.find(ID.button_walkright); + if (walkLeftButton.down) + this.velocity.x = -walkingSpeed; + else if (walkRightButton.down) + this.velocity.x = walkingSpeed; + } else { + if (powerupjs.Keyboard.down(powerupjs.Keys.left)) + this.velocity.x = -walkingSpeed; + else if (powerupjs.Keyboard.down(powerupjs.Keys.right)) + this.velocity.x = walkingSpeed; + } + + if (this.velocity.x != 0) + this.mirror = this.velocity.x < 0; + + if (powerupjs.Touch.isTouchDevice) { + var jumpButton = this.root.find(ID.button_jump); + if (jumpButton.pressed && this.onTheGround) + this.jump(); + } else { + if (powerupjs.Keyboard.pressed(powerupjs.Keys.space) && this.onTheGround) + this.jump(); + } +}; + +Player.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + this.doPhysics(); + if (!this.alive || this.finished) + return; + + if (this.onTheGround) + if (this.velocity.x == 0) + this.playAnimation("idle"); + else + this.playAnimation("run"); + else if (this.velocity.y < 0) + this.playAnimation("jump"); + + var timer = this.root.find(ID.timer); + if (this.walkingOnHot) + timer.multiplier = 2; + else if (this.walkingOnIce) + timer.multiplier = 0.5; + else + timer.multiplier = 1; + + var tiles = this.root.find(ID.tiles); + if (this.boundingBox.top >= tiles.rows * tiles.cellHeight) + this.die(true); +}; + +Player.prototype.die = function (falling) { + if (!this.alive || this.finished) + return; + this.alive = false; + this.velocity.x = 0; + if (falling) { + sounds.player_fall.play(); + } + else { + this.velocity.y = -900; + sounds.player_die.play(); + } + this.playAnimation("die"); +}; + +Player.prototype.explode = function () { + if (!this.alive || this.finished) + return; + this.alive = false; + this.exploded = true; + this.velocity = powerupjs.Vector2.zero; + this.playAnimation("explode"); + sounds.player_explode.play(); +}; + +Player.prototype.levelFinished = function () { + this.finished = true; + this.velocity.x = 0; + this.playAnimation("celebrate"); + sounds.player_won.play(); +}; + +Player.prototype.jump = function (speed) { + sounds.player_jump.play(); + speed = typeof speed !== 'undefined' ? speed : 1100; + this.velocity.y = -speed; +}; + +Player.prototype.doPhysics = function () { + if (!this.exploded) + this.velocity.y += 55; + if (this.alive) + this.handleCollisions(); +}; + +Player.prototype.handleCollisions = function () { + this.onTheGround = false; + this.walkingOnIce = false; + this.walkingOnHot = false; + + var tiles = this.root.find(ID.tiles); + + var x_floor = Math.floor(this.position.x / tiles.cellWidth); + var y_floor = Math.floor(this.position.y / tiles.cellHeight); + + for (var y = y_floor - 2; y <= y_floor + 1; ++y) + for (var x = x_floor - 1; x <= x_floor + 1; ++x) { + var tileType = tiles.getTileType(x, y); + if (tileType === TileType.background) + continue; + var tileBounds = new powerupjs.Rectangle(x * tiles.cellWidth, y * tiles.cellHeight, + tiles.cellWidth, tiles.cellHeight); + var boundingBox = this.boundingBox; + boundingBox.height += 1; + if (!tileBounds.intersects(boundingBox)) + continue; + var depth = boundingBox.calculateIntersectionDepth(tileBounds); + if (Math.abs(depth.x) < Math.abs(depth.y)) { + if (tileType === TileType.normal) + this.position.x += depth.x; + continue; + } + if (this._previousYPosition <= tileBounds.top && tileType !== TileType.background) { + this.onTheGround = true; + this.velocity.y = 0; + var currentTile = tiles.at(x, y); + if (currentTile !== null) { + this.walkingOnIce = this.walkingOnIce || currentTile.ice; + this.walkingOnHot = this.walkingOnHot || currentTile.hot; + } + } + if (tileType === TileType.normal || this.onTheGround) + this.position.y += depth.y + 1; + } + this._previousYPosition = this.position.y; +}; + diff --git a/29_FinishingGame/TickTickFinal/gameobjects/Tile.js b/29_FinishingGame/TickTickFinal/gameobjects/Tile.js new file mode 100644 index 0000000..19a1c2e --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/Tile.js @@ -0,0 +1,24 @@ +"use strict"; + +var TileType = { + background: 0, + normal: 1, + platform: 2 +}; + +function Tile(sprite, tileTp, layer, id) { + sprite = typeof sprite !== 'undefined' ? sprite : sprites.wall; + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.hot = false; + this.ice = false; + this.type = typeof tileTp !== 'undefined' ? tileTp : TileType.background; +} + +Tile.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +Tile.prototype.draw = function () { + if (this.type == TileType.background) + return; + powerupjs.SpriteGameObject.prototype.draw.call(this); +}; diff --git a/29_FinishingGame/TickTickFinal/gameobjects/TileField.js b/29_FinishingGame/TickTickFinal/gameobjects/TileField.js new file mode 100644 index 0000000..83b0fe1 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/TileField.js @@ -0,0 +1,15 @@ +"use strict"; + +function TileField(rows, columns, layer, id) { + powerupjs.GameObjectGrid.call(this, rows, columns, layer, id); +} + +TileField.prototype = Object.create(powerupjs.GameObjectGrid.prototype); + +TileField.prototype.getTileType = function (x, y) { + if (x < 0 || x >= this.columns) + return TileType.normal; + if (y < 0 || y >= this.rows) + return TileType.background; + return this.at(x, y).type; +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/TimerGameObject.js b/29_FinishingGame/TickTickFinal/gameobjects/TimerGameObject.js new file mode 100644 index 0000000..1a0e8bb --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/TimerGameObject.js @@ -0,0 +1,40 @@ +"use strict"; + +function TimerGameObject(layer, id) { + powerupjs.Label.call(this, "Arial", "26pt", layer, id); + this.reset() +} +TimerGameObject.prototype = Object.create(powerupjs.Label.prototype); + +Object.defineProperty(TimerGameObject.prototype, "gameOver", + { + get: function () { + return this._timeLeft <= 0; + } + }); + +TimerGameObject.prototype.update = function (delta) { + if (!this.running) + return; + this._timeLeft -= delta * this.multiplier; + if (this._timeLeft < 0) + this.running = false; + + var minutes = Math.floor(this._timeLeft / 60); + var seconds = Math.ceil(this._timeLeft % 60); + if (this._timeLeft < 0) + minutes = seconds = 0; + this.text = minutes + ":" + seconds; + if (seconds < 10) + this.text = minutes + ":0" + seconds; + this.color = powerupjs.Color.yellow; + if (this._timeLeft <= 10 && seconds % 2 === 0) + this.color = powerupjs.Color.red; +}; + +TimerGameObject.prototype.reset = function () { + powerupjs.Label.prototype.reset.call(this); + this._timeLeft = 30; + this.running = true; + this.multiplier = 1; +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/VisibilityTimer.js b/29_FinishingGame/TickTickFinal/gameobjects/VisibilityTimer.js new file mode 100644 index 0000000..22e5b32 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/VisibilityTimer.js @@ -0,0 +1,21 @@ +"use strict"; + +function VisibilityTimer(target, layer, id) { + powerupjs.GameObject.call(this, layer, id); + this._target = target; + this._timeLeft = 0; + this._totalTime = 5; +} + +VisibilityTimer.prototype = Object.create(powerupjs.GameObject.prototype); + +VisibilityTimer.prototype.update = function (delta) { + this._timeLeft -= delta; + if (this._timeLeft <= 0) + this._target.visible = false; +}; + +VisibilityTimer.prototype.startVisible = function () { + this._timeLeft = this._totalTime; + this._target.visible = true; +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/WaterDrop.js b/29_FinishingGame/TickTickFinal/gameobjects/WaterDrop.js new file mode 100644 index 0000000..3e83635 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/WaterDrop.js @@ -0,0 +1,23 @@ +"use strict"; + +function WaterDrop(layer, id) { + powerupjs.SpriteGameObject.call(this, sprites.water, layer, id); + this._bounce = 0; +} + +WaterDrop.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + +WaterDrop.prototype.update = function (delta) { + this.position.y -= this._bounce; + var t = powerupjs.Game.totalTime * 3 + this.position.x; + this._bounce = Math.sin(t) * 5; + this.position.y += this._bounce; + powerupjs.SpriteGameObject.prototype.update.call(this, delta); + + var player = this.root.find(ID.player); + if (this.collidesWith(player)) { + this.visible = false; + sounds.water_collected.play(); + + } +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/PatrollingEnemy.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/PatrollingEnemy.js new file mode 100644 index 0000000..706571d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/PatrollingEnemy.js @@ -0,0 +1,49 @@ +"use strict"; + +function PatrollingEnemy(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this._waitTime = 0; + this.velocity.x = 120; + + this.loadAnimation(sprites.flame, "default", true); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); +} +PatrollingEnemy.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +PatrollingEnemy.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this._waitTime > 0) { + this._waitTime -= delta; + if (this._waitTime <= 0) + this.turnAround(); + } + else { + var tiles = this.root.find(ID.tiles); + var posX = this.boundingBox.left; + if (!this.mirror) + posX = this.boundingBox.right; + var tileX = Math.floor(posX / tiles.cellWidth); + var tileY = Math.floor(this.position.y / tiles.cellHeight); + if (tiles.getTileType(tileX, tileY - 1) === TileType.normal || + tiles.getTileType(tileX, tileY) === TileType.background) { + this._waitTime = 0.5; + this.velocity.x = 0; + } + } + this.checkPlayerCollision(); +}; + +PatrollingEnemy.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.collidesWith(player)) + player.die(false); +}; + +PatrollingEnemy.prototype.turnAround = function () { + this.mirror = !this.mirror; + this.velocity.x = 120; + if (this.mirror) + this.velocity.x = -this.velocity.x; +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/PlayerFollowingEnemy.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/PlayerFollowingEnemy.js new file mode 100644 index 0000000..f2eb88d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/PlayerFollowingEnemy.js @@ -0,0 +1,17 @@ +"use strict"; + +function PlayerFollowingEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +PlayerFollowingEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +PlayerFollowingEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + + var player = this.root.find(ID.player); + var direction = player.position.x - this.position.x; + if (Math.sign(direction) !== Math.sign(this.velocity.x) && player.velocity.x !== 0 + && this.velocity.x !== 0) + this.turnAround(); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/Rocket.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Rocket.js new file mode 100644 index 0000000..db0ef7d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Rocket.js @@ -0,0 +1,48 @@ +"use strict"; + +function Rocket(moveToLeft, startPosition, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.spawnTime = 0; + this.startPosition = startPosition; + this.mirror = moveToLeft; + + this.loadAnimation(sprites.rocket, "default", true, 0.5); + this.playAnimation("default"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Rocket.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Rocket.prototype.reset = function () { + this.visible = false; + this.position = this.startPosition.copy(); + this.velocity = powerupjs.Vector2.zero; + this.spawnTime = Math.random() * 5; +}; + +Rocket.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.spawnTime > 0) { + this.spawnTime -= delta; + return; + } + this.visible = true; + this.velocity.x = 600; + if (this.mirror) + this.velocity.x *= -1; + // check if we are far enough outside the screen + var screenBox = new powerupjs.Rectangle(-200, -200, powerupjs.Game.size.x + 400, + powerupjs.Game.size.y + 400); + if (!screenBox.intersects(this.boundingBox)) + this.reset(); + this.checkPlayerCollision(); +}; + +Rocket.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.collidesWith(player)) + player.die(false); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/Sparky.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Sparky.js new file mode 100644 index 0000000..8460a7a --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Sparky.js @@ -0,0 +1,55 @@ +"use strict"; + +function Sparky(initialY, layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.idleTime = 0; + this.yoffset = 0; + this.initialY = initialY; + + this.loadAnimation(sprites.sparky_electrocute, "electrocute", false); + this.loadAnimation(sprites.sparky_idle, "idle", true); + this.playAnimation("idle"); + + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + this.reset(); +} + +Sparky.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Sparky.prototype.reset = function () { + this.idleTime = Math.random() * 5; + this.position.y = this.initialY; + this.yoffset = 120; + this.velocity = powerupjs.Vector2.zero; +}; + +Sparky.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.idleTime <= 0) { + this.playAnimation("electrocute"); + if (this.velocity.y != 0) { + // falling down or going up + this.yoffset -= this.velocity.y * delta; + if (this.yoffset <= 0) + this.velocity.y = 0; + else if (this.yoffset >= 120) + this.reset(); + } + else if (this.animationEnded()) + this.velocity.y = -60; + } + else { + this.playAnimation("idle"); + this.idleTime -= delta; + if (this.idleTime <= 0) + this.velocity.y = 300; + } + this.checkPlayerCollision(); +}; + +Sparky.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (this.idleTime <= 0 && this.collidesWith(player)) + player.die(false); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/Turtle.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Turtle.js new file mode 100644 index 0000000..576f43d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/Turtle.js @@ -0,0 +1,49 @@ +"use strict"; + +function Turtle(layer, id) { + powerupjs.AnimatedGameObject.call(this, layer, id); + + this.waitTime = 0; + this.sneezing = false; + + this.loadAnimation(sprites.turtle_sneeze, "sneeze", false); + this.loadAnimation(sprites.turtle_idle, "idle", true); + this.playAnimation("idle"); + this.origin = new powerupjs.Vector2(this.width / 2, this.height); + + this.reset(); +} + +Turtle.prototype = Object.create(powerupjs.AnimatedGameObject.prototype); + +Turtle.prototype.reset = function () { + this.waitTime = 5; + this.sneezing = false; +}; + +Turtle.prototype.update = function (delta) { + powerupjs.AnimatedGameObject.prototype.update.call(this, delta); + if (this.sneezing) + this.playAnimation("sneeze"); + else + this.playAnimation("idle"); + + if (this.waitTime > 0) + this.waitTime -= delta; + else { + this.sneezing = !this.sneezing; + this.waitTime = 5; + } + this.checkPlayerCollision(); +}; + +Turtle.prototype.checkPlayerCollision = function () { + var player = this.root.find(ID.player); + if (!this.collidesWith(player)) + return; + + if (this.sneezing) + player.die(false); + else if (player.velocity.y > 0 && player.alive) + player.jump(1500); +}; diff --git a/29_FinishingGame/TickTickFinal/gameobjects/enemies/UnpredictableEnemy.js b/29_FinishingGame/TickTickFinal/gameobjects/enemies/UnpredictableEnemy.js new file mode 100644 index 0000000..cf009ed --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gameobjects/enemies/UnpredictableEnemy.js @@ -0,0 +1,16 @@ +"use strict"; + +function UnpredictableEnemy(layer, id) { + PatrollingEnemy.call(this, layer, id); +} + +UnpredictableEnemy.prototype = Object.create(PatrollingEnemy.prototype); + +UnpredictableEnemy.prototype.update = function (delta) { + PatrollingEnemy.prototype.update.call(this, delta); + if (this._waitTime <= 0 && Math.random() < 0.01) { + this.turnAround(); + this.velocity.x = Math.sign(this.velocity.x) * + Math.random() * 300; + } +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/gui/LevelButton.js b/29_FinishingGame/TickTickFinal/gui/LevelButton.js new file mode 100644 index 0000000..45e419a --- /dev/null +++ b/29_FinishingGame/TickTickFinal/gui/LevelButton.js @@ -0,0 +1,57 @@ +"use strict"; + +function LevelButton(levelIndex, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.pressed = false; + this.levelIndex = levelIndex; + this._levelSolved = new powerupjs.SpriteGameObject(sprites.level_solved, ID.layer_overlays_1); + this._levelUnsolved = new powerupjs.SpriteGameObject(sprites.level_unsolved, ID.layer_overlays); + + this._levelSolved.sheetIndex = levelIndex; + this.add(this._levelSolved); + this.add(this._levelUnsolved); + + this._levelLocked = new powerupjs.SpriteGameObject(sprites.level_locked, ID.layer_overlays_1); + this.add(this._levelLocked); + + var textLabel = new powerupjs.Label("Arial", "20px", ID.layer_overlays_2); + textLabel.text = levelIndex + 1; + textLabel.position = new powerupjs.Vector2(this._levelSolved.width - textLabel.width - 10, 10); + textLabel.color = powerupjs.Color.white; + this.add(textLabel); +} + +LevelButton.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(LevelButton.prototype, "width", + { + get: function () { + return this._levelLocked.width; + } + }); + +Object.defineProperty(LevelButton.prototype, "height", + { + get: function () { + return this._levelLocked.height; + } + }); + +LevelButton.prototype.handleInput = function (delta) { + if (window.LEVELS[this.levelIndex].locked) + return; + if (powerupjs.Touch.isTouchDevice) + this.pressed = this.visible && powerupjs.Touch.containsTouch(this._levelLocked.boundingBox); + else + this.pressed = this.visible && powerupjs.Mouse.left.pressed && + this._levelLocked.boundingBox.contains(powerupjs.Mouse.position); +}; + +LevelButton.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + var currLevel = window.LEVELS[this.levelIndex]; + this._levelLocked.visible = currLevel.locked; + this._levelSolved.visible = currLevel.solved; + this._levelUnsolved.visible = !currLevel.solved; +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/level/Level.js b/29_FinishingGame/TickTickFinal/level/Level.js new file mode 100644 index 0000000..5b4fc57 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/level/Level.js @@ -0,0 +1,264 @@ +"use strict"; + +function Level(levelIndex, id) { + powerupjs.GameObjectList.call(this, id); + + this._levelIndex = levelIndex; + this._waterdrops = new powerupjs.GameObjectList(ID.layer_objects); + this._enemies = new powerupjs.GameObjectList(ID.layer_objects); + this._quitButton = new powerupjs.Button(sprites.button_quit, ID.layer_overlays); + + this._quitButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - this._quitButton.width - 10, 10); + this.add(this._quitButton); + + this.add(this._waterdrops); + this.add(this._enemies); + + // backgrounds + var backgrounds = new powerupjs.GameObjectList(ID.layer_background); + var background_main = new powerupjs.SpriteGameObject(sprites.background_sky, ID.layer_background_1); + background_main.position = new powerupjs.Vector2(0, powerupjs.Game.size.y - background_main.height); + backgrounds.add(background_main); + + for (var i = 0; i < 5; i++) { + var sprid = "mountain_" + (Math.ceil(Math.random()*2)); + var mountain = new powerupjs.SpriteGameObject(sprites[sprid], ID.layer_background_2); + mountain.position = new powerupjs.Vector2(Math.random() * powerupjs.Game.size.x - mountain.width / 2, + powerupjs.Game.size.y - mountain.height); + backgrounds.add(mountain); + } + + var clouds = new Clouds(ID.layer_background_3); + backgrounds.add(clouds); + this.add(backgrounds); + + // timer + var timerBackground = new powerupjs.SpriteGameObject(sprites.timer, ID.layer_overlays); + timerBackground.position = new powerupjs.Vector2(10, 10); + this.add(timerBackground); + var timer = new TimerGameObject(ID.layer_overlays_1, ID.timer); + timer.position = new powerupjs.Vector2(25, 33); + this.add(timer); + + this.loadTiles(); +} + +Level.prototype = Object.create(powerupjs.GameObjectList.prototype); + +Object.defineProperty(Level.prototype, "completed", + { + get: function () { + var player = this.find(ID.player); + var exit = this.find(ID.exit); + if (!exit.collidesWith(player)) + return false; + for (var i = 0, l = this._waterdrops.length; i < l; ++i) { + if (this._waterdrops.at(i).visible) + return false; + } + return true; + } + }); + +Object.defineProperty(Level.prototype, "gameOver", + { + get: function () { + var timer = this.find(ID.timer); + var player = this.find(ID.player); + return !player.alive || timer.gameOver; + } + }); + +Level.prototype.loadTiles = function () { + var levelData = window.LEVELS[this._levelIndex]; + + var hintField = new powerupjs.GameObjectList(ID.layer_overlays); + this.add(hintField); + var hintFrame = new powerupjs.SpriteGameObject(sprites.frame_hint, ID.layer_overlays_1); + hintField.position = new powerupjs.Vector2(hintFrame.screenCenterX, 10); + hintField.add(hintFrame); + var hintText = new powerupjs.Label("Arial", "14pt", ID.layer_overlays_2); + hintText.text = levelData.hint; + hintText.position = new powerupjs.Vector2(120, 25); + hintText.color = powerupjs.Color.black; + hintField.add(hintText); + var hintTimer = new VisibilityTimer(hintField, ID.layer_overlays, ID.hint_timer); + this.add(hintTimer); + + if (powerupjs.Touch.isTouchDevice) { + var walkLeftButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkleft); + walkLeftButton.position = new powerupjs.Vector2(10, 500); + this.add(walkLeftButton); + var walkRightButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_walkright); + walkRightButton.position = new powerupjs.Vector2(walkRightButton.width + 20, 500); + walkRightButton.sheetIndex = 1; + this.add(walkRightButton); + var jumpButton = new powerupjs.Button(sprites.buttons_player, ID.layer_overlays, ID.button_jump); + jumpButton.position = new powerupjs.Vector2(powerupjs.Game.size.x - jumpButton.width - 10, 500); + jumpButton.sheetIndex = 2; + this.add(jumpButton); + } + + var tiles = new TileField(levelData.tiles.length, levelData.tiles[0].length, 1, ID.tiles); + this.add(tiles); + tiles.cellWidth = 72; + tiles.cellHeight = 55; + for (var y = 0, ly = tiles.rows; y < ly; ++y) + for (var x = 0, lx = tiles.columns; x < lx; ++x) { + var t = this.loadTile(levelData.tiles[y][x], x, y); + tiles.add(t, x, y); + } +}; + +Level.prototype.loadTile = function (tileType, x, y) { + switch (tileType) { + case '.': + return new Tile(); + case '-': + return this.loadBasicTile(sprites.platform, TileType.platform); + case '+': + return this.loadBasicTile(sprites.platform_hot, TileType.platform, true, false); + case '@': + return this.loadBasicTile(sprites.platform_ice, TileType.platform, false, true); + case 'X': + return this.loadEndTile(x, y); + case 'W': + return this.loadWaterTile(x, y); + case '1': + return this.loadStartTile(x, y); + case '#': + return this.loadBasicTile(sprites.wall, TileType.normal); + case '^': + return this.loadBasicTile(sprites.wall_hot, TileType.normal, true, false); + case '*': + return this.loadBasicTile(sprites.wall_ice, TileType.normal, false, true); + case 'R': + return this.loadRocketTile(x, y, true); + case 'r': + return this.loadRocketTile(x, y, false); + case 'S': + return this.loadSparkyTile(x, y); + case 'T': + return this.loadTurtleTile(x, y); + case 'A': + case 'B': + case 'C': + return this.loadFlameTile(x, y, tileType); + + default: + return new Tile(); + } +}; + +Level.prototype.loadBasicTile = function (id, tileType, hot, ice) { + var t = new Tile(id, tileType); + t.hot = hot; + t.ice = ice; + return t; +}; + +Level.prototype.loadStartTile = function (x, y) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this.add(new Player(startPosition, ID.layer_objects, ID.player)); + return new Tile(); +}; + +Level.prototype.loadEndTile = function (x, y) { + var tiles = this.find(ID.tiles); + var exit = new powerupjs.SpriteGameObject(sprites.goal, ID.layer_objects, ID.exit); + exit.position = new powerupjs.Vector2(x * tiles.cellWidth, (y + 1) * tiles.cellHeight); + exit.origin = new powerupjs.Vector2(0, exit.height); + this.add(exit); + return new Tile(); +}; + +Level.prototype.loadWaterTile = function (x, y) { + var tiles = this.find(ID.tiles); + var w = new WaterDrop(ID.layer_objects); + w.origin = w.center.copy(); + w.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 0.5) * tiles.cellHeight - 10); + this._waterdrops.add(w); + return new Tile(); +}; + +Level.prototype.loadRocketTile = function (x, y, moveToLeft) { + var tiles = this.find(ID.tiles); + var startPosition = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + var enemy = new Rocket(moveToLeft, startPosition, ID.layer_objects); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadTurtleTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Turtle(ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight + 25); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadSparkyTile = function (x, y) { + var tiles = this.find(ID.tiles); + var enemy = new Sparky((y + 1) * tiles.cellHeight - 100, ID.layer_objects); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight - 100); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.loadFlameTile = function (x, y, enemyType) { + var enemy = null; + switch (enemyType) { + case 'A': + enemy = new UnpredictableEnemy(ID.layer_objects); + break; + case 'B': + enemy = new PlayerFollowingEnemy(ID.layer_objects); + break; + case 'C': + default: + enemy = new PatrollingEnemy(ID.layer_objects); + break; + } + var tiles = this.find(ID.tiles); + enemy.position = new powerupjs.Vector2((x + 0.5) * tiles.cellWidth, (y + 1) * tiles.cellHeight); + this._enemies.add(enemy); + return new Tile(); +}; + +Level.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + if (!this._quitButton.pressed) + return; + this.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); +}; + +Level.prototype.update = function (delta) { + powerupjs.GameObjectList.prototype.update.call(this, delta); + + var timer = this.find(ID.timer); + var player = this.find(ID.player); + + // check if we died + if (!player.alive) + timer.running = false; + + // check if we ran out of time + if (timer.gameOver) + player.explode(); + + // check if we won + if (this.completed && timer.running) { + player.levelFinished(); + timer.running = false; + window.LEVELS[this._levelIndex].solved = true; + } +}; + +Level.prototype.reset = function () { + powerupjs.GameObjectList.prototype.reset.call(this); + var hintTimer = this.find(ID.hint_timer); + hintTimer.startVisible(); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/GameOverState.js b/29_FinishingGame/TickTickFinal/states/GameOverState.js new file mode 100644 index 0000000..790117f --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/GameOverState.js @@ -0,0 +1,30 @@ +"use strict"; + +function GameOverState() { + powerupjs.GameObjectList.call(this); + this.playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + this.overlay = new powerupjs.SpriteGameObject(sprites.overlay_gameover, ID.layer_overlays); + this.overlay.position = this.overlay.screenCenter; + this.add(this.overlay); +} +GameOverState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +GameOverState.prototype.handleInput = function (delta) { + if (powerupjs.Touch.isTouchDevice) { + if (!powerupjs.Touch.containsTouch(this.overlay.boundingBox)) + return; + } + else if (!powerupjs.Mouse.containsMousePress(this.overlay.boundingBox)) + return; + this.playingState.reset(); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); +}; + +GameOverState.prototype.update = function (delta) { + this.playingState.update(delta); +}; + +GameOverState.prototype.draw = function () { + this.playingState.draw(); + powerupjs.GameObjectList.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/HelpState.js b/29_FinishingGame/TickTickFinal/states/HelpState.js new file mode 100644 index 0000000..c71e040 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/HelpState.js @@ -0,0 +1,21 @@ +"use strict"; + +function HelpState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the background + var background = new powerupjs.SpriteGameObject(sprites.background_help, ID.layer_background); + this.add(background); + + // add a back button + this.backButton = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.backButton.position = new powerupjs.Vector2(this.backButton.screenCenterX, 750); + this.add(this.backButton); +} +HelpState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +HelpState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.backButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/LevelFinishedState.js b/29_FinishingGame/TickTickFinal/states/LevelFinishedState.js new file mode 100644 index 0000000..0dff11d --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/LevelFinishedState.js @@ -0,0 +1,31 @@ +"use strict"; + +function LevelFinishedState() { + powerupjs.GameObjectList.call(this); + this.playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + this.overlay = new powerupjs.SpriteGameObject(sprites.overlay_welldone, ID.layer_overlays); + this.overlay.position = this.overlay.screenCenter; + this.add(this.overlay); +} + +LevelFinishedState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelFinishedState.prototype.handleInput = function (delta) { + if (powerupjs.Touch.isTouchDevice) { + if (!powerupjs.Touch.containsTouch(this.overlay.boundingBox)) + return; + } + else if (!powerupjs.Mouse.containsMousePress(this.overlay.boundingBox)) + return; + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + this.playingState.nextLevel(); +}; + +LevelFinishedState.prototype.update = function (delta) { + this.playingState.update(delta); +}; + +LevelFinishedState.prototype.draw = function () { + this.playingState.draw(); + powerupjs.GameObjectList.prototype.draw.call(this); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/LevelMenuState.js b/29_FinishingGame/TickTickFinal/states/LevelMenuState.js new file mode 100644 index 0000000..c00b239 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/LevelMenuState.js @@ -0,0 +1,46 @@ +"use strict"; + +function LevelMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + this.background = new powerupjs.SpriteGameObject(sprites.levelselect, ID.layer_background); + this.add(this.background); + + this.back = new powerupjs.Button(sprites.button_back, ID.layer_overlays); + this.back.position = new powerupjs.Vector2(this.back.screenCenterX, 750); + this.add(this.back); + + this.levelButtons = []; + + for (var i = 0, j = window.LEVELS.length; i < j; ++i) { + var row = Math.floor(i / 4); + var column = i % 4; + var level = new LevelButton(i, ID.layer_overlays); + level.position = new powerupjs.Vector2(column * (level.width + 20) + 390, row * (level.height + 20) + 180); + this.add(level); + this.levelButtons.push(level); + } +} + +LevelMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +LevelMenuState.prototype.getSelectedLevel = function () { + for (var i = 0, j = this.levelButtons.length; i < j; i++) { + if (this.levelButtons[i].pressed) + return this.levelButtons[i].levelIndex; + } + return -1; +}; + +LevelMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + + var selectedLevel = this.getSelectedLevel(); + if (selectedLevel != -1) { + var playingState = powerupjs.GameStateManager.get(ID.game_state_playing); + playingState.goToLevel(selectedLevel); + powerupjs.GameStateManager.switchTo(ID.game_state_playing); + } + else if (this.back.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_title); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/PlayingState.js b/29_FinishingGame/TickTickFinal/states/PlayingState.js new file mode 100644 index 0000000..1b54c7c --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/PlayingState.js @@ -0,0 +1,75 @@ +"use strict"; + +function PlayingState() { + powerupjs.IGameLoopObject.call(this); + + this.currentLevelIndex = -1; + this.levels = []; + + this.loadLevelsStatus(); + this.loadLevels(); +} + +PlayingState.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + +Object.defineProperty(PlayingState.prototype, "currentLevel", { + get: function () { + return this.levels[this.currentLevelIndex]; + } +}); + +PlayingState.prototype.handleInput = function (delta) { + this.currentLevel.handleInput(delta); +}; + +PlayingState.prototype.update = function (delta) { + this.currentLevel.update(delta); + + if (this.currentLevel.gameOver) + powerupjs.GameStateManager.switchTo(ID.game_state_gameover); + else if (this.currentLevel.completed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelfinished); +}; + +PlayingState.prototype.draw = function () { + this.currentLevel.draw(); +}; + +PlayingState.prototype.reset = function () { + this.currentLevel.reset(); +}; + +PlayingState.prototype.goToLevel = function (levelIndex) { + if (levelIndex < 0 || levelIndex >= this.levels.length) + return; + this.currentLevelIndex = levelIndex; + this.currentLevel.reset(); +}; + +PlayingState.prototype.nextLevel = function () { + if (this.currentLevelIndex >= window.LEVELS.length - 1) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else { + this.goToLevel(this.currentLevelIndex + 1); + window.LEVELS[this.currentLevelIndex].locked = false; + } + this.writeLevelsStatus(); +}; + +PlayingState.prototype.loadLevels = function () { + for (var currLevel = 0; currLevel < window.LEVELS.length; currLevel += 1) { + this.levels.push(new Level(currLevel)); + } +}; + +PlayingState.prototype.loadLevelsStatus = function () { + if (localStorage && localStorage.tickTickLevels) { + window.LEVELS = JSON.parse(localStorage.tickTickLevels); + } +}; + +PlayingState.prototype.writeLevelsStatus = function () { + if (!localStorage) + return; + localStorage.tickTickLevels = JSON.stringify(window.LEVELS); +}; \ No newline at end of file diff --git a/29_FinishingGame/TickTickFinal/states/TitleMenuState.js b/29_FinishingGame/TickTickFinal/states/TitleMenuState.js new file mode 100644 index 0000000..0628cb6 --- /dev/null +++ b/29_FinishingGame/TickTickFinal/states/TitleMenuState.js @@ -0,0 +1,29 @@ +"use strict"; + +function TitleMenuState(layer) { + powerupjs.GameObjectList.call(this, layer); + + // the title screen + var titleScreen = new powerupjs.SpriteGameObject(sprites.background_title, ID.layer_background); + this.add(titleScreen); + + // add a play button + this.playButton = new powerupjs.Button(sprites.button_play, ID.layer_overlays); + this.playButton.position = new powerupjs.Vector2(this.playButton.screenCenterX, 540); + this.add(this.playButton); + + // add a help button + this.helpButton = new powerupjs.Button(sprites.button_help, ID.layer_overlays); + this.helpButton.position = new powerupjs.Vector2(this.helpButton.screenCenterX, 600); + this.add(this.helpButton); +} + +TitleMenuState.prototype = Object.create(powerupjs.GameObjectList.prototype); + +TitleMenuState.prototype.handleInput = function (delta) { + powerupjs.GameObjectList.prototype.handleInput.call(this, delta); + if (this.playButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_levelselect); + else if (this.helpButton.pressed) + powerupjs.GameStateManager.switchTo(ID.game_state_help); +}; \ No newline at end of file diff --git a/assets/JewelJam/sounds/snd_combi.mp3 b/assets/JewelJam/sounds/snd_combi.mp3 new file mode 100644 index 0000000..7f8c6a4 Binary files /dev/null and b/assets/JewelJam/sounds/snd_combi.mp3 differ diff --git a/assets/JewelJam/sounds/snd_combi.ogg b/assets/JewelJam/sounds/snd_combi.ogg new file mode 100644 index 0000000..806bb35 Binary files /dev/null and b/assets/JewelJam/sounds/snd_combi.ogg differ diff --git a/assets/JewelJam/sounds/snd_double.mp3 b/assets/JewelJam/sounds/snd_double.mp3 new file mode 100644 index 0000000..7f8c6a4 Binary files /dev/null and b/assets/JewelJam/sounds/snd_double.mp3 differ diff --git a/assets/JewelJam/sounds/snd_double.ogg b/assets/JewelJam/sounds/snd_double.ogg new file mode 100644 index 0000000..b218559 Binary files /dev/null and b/assets/JewelJam/sounds/snd_double.ogg differ diff --git a/assets/JewelJam/sounds/snd_gameover.mp3 b/assets/JewelJam/sounds/snd_gameover.mp3 new file mode 100644 index 0000000..a0751f6 Binary files /dev/null and b/assets/JewelJam/sounds/snd_gameover.mp3 differ diff --git a/assets/JewelJam/sounds/snd_gameover.ogg b/assets/JewelJam/sounds/snd_gameover.ogg new file mode 100644 index 0000000..6ca77fa Binary files /dev/null and b/assets/JewelJam/sounds/snd_gameover.ogg differ diff --git a/assets/JewelJam/sounds/snd_music.mp3 b/assets/JewelJam/sounds/snd_music.mp3 new file mode 100644 index 0000000..eb9e689 Binary files /dev/null and b/assets/JewelJam/sounds/snd_music.mp3 differ diff --git a/assets/JewelJam/sounds/snd_music.ogg b/assets/JewelJam/sounds/snd_music.ogg new file mode 100644 index 0000000..2619d7e Binary files /dev/null and b/assets/JewelJam/sounds/snd_music.ogg differ diff --git a/assets/JewelJam/sounds/snd_triple.mp3 b/assets/JewelJam/sounds/snd_triple.mp3 new file mode 100644 index 0000000..185210b Binary files /dev/null and b/assets/JewelJam/sounds/snd_triple.mp3 differ diff --git a/assets/JewelJam/sounds/snd_triple.ogg b/assets/JewelJam/sounds/snd_triple.ogg new file mode 100644 index 0000000..5509fbd Binary files /dev/null and b/assets/JewelJam/sounds/snd_triple.ogg differ diff --git a/assets/JewelJam/sprites/spr_background.jpg b/assets/JewelJam/sprites/spr_background.jpg new file mode 100644 index 0000000..c51bc30 Binary files /dev/null and b/assets/JewelJam/sprites/spr_background.jpg differ diff --git a/assets/JewelJam/sprites/spr_button_help.jpg b/assets/JewelJam/sprites/spr_button_help.jpg new file mode 100644 index 0000000..c71d2cc Binary files /dev/null and b/assets/JewelJam/sprites/spr_button_help.jpg differ diff --git a/assets/JewelJam/sprites/spr_double.png b/assets/JewelJam/sprites/spr_double.png new file mode 100644 index 0000000..e80f308 Binary files /dev/null and b/assets/JewelJam/sprites/spr_double.png differ diff --git a/assets/JewelJam/sprites/spr_frame_help.png b/assets/JewelJam/sprites/spr_frame_help.png new file mode 100644 index 0000000..cc6e9da Binary files /dev/null and b/assets/JewelJam/sprites/spr_frame_help.png differ diff --git a/assets/JewelJam/sprites/spr_frame_score.jpg b/assets/JewelJam/sprites/spr_frame_score.jpg new file mode 100644 index 0000000..3e19eee Binary files /dev/null and b/assets/JewelJam/sprites/spr_frame_score.jpg differ diff --git a/assets/JewelJam/sprites/spr_frame_selector.png b/assets/JewelJam/sprites/spr_frame_selector.png new file mode 100644 index 0000000..2ec2d52 Binary files /dev/null and b/assets/JewelJam/sprites/spr_frame_selector.png differ diff --git a/assets/JewelJam/sprites/spr_gameover_click.png b/assets/JewelJam/sprites/spr_gameover_click.png new file mode 100644 index 0000000..f5163b0 Binary files /dev/null and b/assets/JewelJam/sprites/spr_gameover_click.png differ diff --git a/assets/JewelJam/sprites/spr_gameover_tap.png b/assets/JewelJam/sprites/spr_gameover_tap.png new file mode 100644 index 0000000..c54e903 Binary files /dev/null and b/assets/JewelJam/sprites/spr_gameover_tap.png differ diff --git a/assets/JewelJam/sprites/spr_glitter.png b/assets/JewelJam/sprites/spr_glitter.png new file mode 100644 index 0000000..e275058 Binary files /dev/null and b/assets/JewelJam/sprites/spr_glitter.png differ diff --git a/assets/JewelJam/sprites/spr_jewelcart.png b/assets/JewelJam/sprites/spr_jewelcart.png new file mode 100644 index 0000000..9b1bc46 Binary files /dev/null and b/assets/JewelJam/sprites/spr_jewelcart.png differ diff --git a/assets/JewelJam/sprites/spr_jewels@27.png b/assets/JewelJam/sprites/spr_jewels@27.png new file mode 100644 index 0000000..2426222 Binary files /dev/null and b/assets/JewelJam/sprites/spr_jewels@27.png differ diff --git a/assets/JewelJam/sprites/spr_single_jewel1.png b/assets/JewelJam/sprites/spr_single_jewel1.png new file mode 100644 index 0000000..09ae7da Binary files /dev/null and b/assets/JewelJam/sprites/spr_single_jewel1.png differ diff --git a/assets/JewelJam/sprites/spr_single_jewel2.png b/assets/JewelJam/sprites/spr_single_jewel2.png new file mode 100644 index 0000000..11a1d1c Binary files /dev/null and b/assets/JewelJam/sprites/spr_single_jewel2.png differ diff --git a/assets/JewelJam/sprites/spr_single_jewel3.png b/assets/JewelJam/sprites/spr_single_jewel3.png new file mode 100644 index 0000000..26e4c9a Binary files /dev/null and b/assets/JewelJam/sprites/spr_single_jewel3.png differ diff --git a/assets/JewelJam/sprites/spr_title_click.jpg b/assets/JewelJam/sprites/spr_title_click.jpg new file mode 100644 index 0000000..a45eea7 Binary files /dev/null and b/assets/JewelJam/sprites/spr_title_click.jpg differ diff --git a/assets/JewelJam/sprites/spr_title_tap.jpg b/assets/JewelJam/sprites/spr_title_tap.jpg new file mode 100644 index 0000000..78f8640 Binary files /dev/null and b/assets/JewelJam/sprites/spr_title_tap.jpg differ diff --git a/assets/JewelJam/sprites/spr_triple.png b/assets/JewelJam/sprites/spr_triple.png new file mode 100644 index 0000000..5fb4c61 Binary files /dev/null and b/assets/JewelJam/sprites/spr_triple.png differ diff --git a/assets/Painter/sounds/snd_collect_points.mp3 b/assets/Painter/sounds/snd_collect_points.mp3 new file mode 100644 index 0000000..02c9796 Binary files /dev/null and b/assets/Painter/sounds/snd_collect_points.mp3 differ diff --git a/assets/Painter/sounds/snd_collect_points.ogg b/assets/Painter/sounds/snd_collect_points.ogg new file mode 100644 index 0000000..e00de47 Binary files /dev/null and b/assets/Painter/sounds/snd_collect_points.ogg differ diff --git a/assets/Painter/sounds/snd_music.mp3 b/assets/Painter/sounds/snd_music.mp3 new file mode 100644 index 0000000..2347c04 Binary files /dev/null and b/assets/Painter/sounds/snd_music.mp3 differ diff --git a/assets/Painter/sounds/snd_music.ogg b/assets/Painter/sounds/snd_music.ogg new file mode 100644 index 0000000..c52ec30 Binary files /dev/null and b/assets/Painter/sounds/snd_music.ogg differ diff --git a/assets/Painter/sounds/snd_shoot_paint.mp3 b/assets/Painter/sounds/snd_shoot_paint.mp3 new file mode 100644 index 0000000..de9456c Binary files /dev/null and b/assets/Painter/sounds/snd_shoot_paint.mp3 differ diff --git a/assets/Painter/sounds/snd_shoot_paint.ogg b/assets/Painter/sounds/snd_shoot_paint.ogg new file mode 100644 index 0000000..a2773ff Binary files /dev/null and b/assets/Painter/sounds/snd_shoot_paint.ogg differ diff --git a/assets/Painter/sprites/spr_background.jpg b/assets/Painter/sprites/spr_background.jpg new file mode 100644 index 0000000..9b7cb5f Binary files /dev/null and b/assets/Painter/sprites/spr_background.jpg differ diff --git a/assets/Painter/sprites/spr_ball_blue.png b/assets/Painter/sprites/spr_ball_blue.png new file mode 100644 index 0000000..087cd6d Binary files /dev/null and b/assets/Painter/sprites/spr_ball_blue.png differ diff --git a/assets/Painter/sprites/spr_ball_green.png b/assets/Painter/sprites/spr_ball_green.png new file mode 100644 index 0000000..af20c27 Binary files /dev/null and b/assets/Painter/sprites/spr_ball_green.png differ diff --git a/assets/Painter/sprites/spr_ball_red.png b/assets/Painter/sprites/spr_ball_red.png new file mode 100644 index 0000000..2c95334 Binary files /dev/null and b/assets/Painter/sprites/spr_ball_red.png differ diff --git a/assets/Painter/sprites/spr_can_blue.png b/assets/Painter/sprites/spr_can_blue.png new file mode 100644 index 0000000..1d8064c Binary files /dev/null and b/assets/Painter/sprites/spr_can_blue.png differ diff --git a/assets/Painter/sprites/spr_can_green.png b/assets/Painter/sprites/spr_can_green.png new file mode 100644 index 0000000..f118a73 Binary files /dev/null and b/assets/Painter/sprites/spr_can_green.png differ diff --git a/assets/Painter/sprites/spr_can_red.png b/assets/Painter/sprites/spr_can_red.png new file mode 100644 index 0000000..3b12b00 Binary files /dev/null and b/assets/Painter/sprites/spr_can_red.png differ diff --git a/assets/Painter/sprites/spr_cannon_barrel.png b/assets/Painter/sprites/spr_cannon_barrel.png new file mode 100644 index 0000000..a1c43aa Binary files /dev/null and b/assets/Painter/sprites/spr_cannon_barrel.png differ diff --git a/assets/Painter/sprites/spr_cannon_blue.png b/assets/Painter/sprites/spr_cannon_blue.png new file mode 100644 index 0000000..03b5b09 Binary files /dev/null and b/assets/Painter/sprites/spr_cannon_blue.png differ diff --git a/assets/Painter/sprites/spr_cannon_green.png b/assets/Painter/sprites/spr_cannon_green.png new file mode 100644 index 0000000..74f6260 Binary files /dev/null and b/assets/Painter/sprites/spr_cannon_green.png differ diff --git a/assets/Painter/sprites/spr_cannon_red.png b/assets/Painter/sprites/spr_cannon_red.png new file mode 100644 index 0000000..dcae2cb Binary files /dev/null and b/assets/Painter/sprites/spr_cannon_red.png differ diff --git a/assets/Painter/sprites/spr_gameover_click.png b/assets/Painter/sprites/spr_gameover_click.png new file mode 100644 index 0000000..154198d Binary files /dev/null and b/assets/Painter/sprites/spr_gameover_click.png differ diff --git a/assets/Painter/sprites/spr_gameover_tap.png b/assets/Painter/sprites/spr_gameover_tap.png new file mode 100644 index 0000000..b1fb8ac Binary files /dev/null and b/assets/Painter/sprites/spr_gameover_tap.png differ diff --git a/assets/Painter/sprites/spr_lives.png b/assets/Painter/sprites/spr_lives.png new file mode 100644 index 0000000..b1453b9 Binary files /dev/null and b/assets/Painter/sprites/spr_lives.png differ diff --git a/assets/Painter/sprites/spr_scorebar.jpg b/assets/Painter/sprites/spr_scorebar.jpg new file mode 100644 index 0000000..3de325f Binary files /dev/null and b/assets/Painter/sprites/spr_scorebar.jpg differ diff --git a/assets/PenguinPairs/levels.js b/assets/PenguinPairs/levels.js new file mode 100644 index 0000000..70a2103 --- /dev/null +++ b/assets/PenguinPairs/levels.js @@ -0,0 +1,228 @@ +window.LEVELS = []; + +window.LEVELS.push({ + locked : false, + solved : false, + hint : "Click on a penguin and select the arrow to let it move towards the other penguin.", + nrPairs : 1, + hint_arrow_x : 4, + hint_arrow_y : 3, + hint_arrow_direction : 3, + tiles : ["#########", + "#.......#", + "#...r...#", + "#.......#", + "#.......#", + "#.......#", + "#...r...#", + "#.......#", + "#########"] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Don't let the penguins fall in the water!", + nrPairs : 1, + hint_arrow_x : 3, + hint_arrow_y : 1, + hint_arrow_direction : 2, + tiles : ["#.......#", + "#...r...#", + "#.......#", + "#. .#", + "#. .#", + "#. .#", + "#.......#", + "#...r...#", + "#.......#"] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "The order in which you move the penguins matters.", + nrPairs : 1, + hint_arrow_x : 8, + hint_arrow_y : 1, + hint_arrow_direction : 2, + tiles : [" #######", + " #....r.", + " #.#####", + " #.# ", + " #.# ", + "#####.#####", + ".r.........", + "###########"] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Create multiple pairs. The number of required pairs is indicated at the top.", + nrPairs : 5, + hint_arrow_x : 2, + hint_arrow_y : 1, + hint_arrow_direction : 3, + tiles : ["..y....", + ".y...y.", + ".......", + ".y...y.", + "..y.y..", + ".......", + ".y.....", + ".....y.", + "..y...."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Pair each penguin with a similar one.", + nrPairs : 2, + hint_arrow_x : 5, + hint_arrow_y : 6, + hint_arrow_direction : 1, + tiles : [".......", + ".b...b.", + ".......", + ".r.....", + ".......", + ".......", + ".......", + ".....r.", + "......."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Animals sometimes must be sacrificed to arrange all required pairs.", + nrPairs : 2, + hint_arrow_x : 1, + hint_arrow_y : 4, + hint_arrow_direction : 2, + tiles : [" ... ", + "..r.. ", + "..... ", + "..b..*", + "..g.. ", + "..b..*", + "..... ", + "..r.. ", + " ... "] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Multi-colored penguins pair with any penguin.", + nrPairs : 2, + hint_arrow_x : 4, + hint_arrow_y : 3, + hint_arrow_direction : 0, + tiles : [" ... ", + " ..r.. ", + " ..... ", + " ..b..#", + "#..m.. ", + " ..b..#", + " ..... ", + " ..g.. ", + " ... "] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Use the seals to stop the penguins from moving.", + nrPairs : 1, + hint_arrow_x : 5, + hint_arrow_y : 4, + hint_arrow_direction : 1, + tiles : [".......", + ".....x.", + ".x.x...", + ".......", + "...x...", + ".....b.", + ".......", + "..b....", + "......."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Use all the penguins to pair the blue penguins.", + nrPairs : 1, + hint_arrow_x : 4, + hint_arrow_y : 1, + hint_arrow_direction : 2, + tiles : [".......", + ".g...r.", + ".......", + ".....b.", + ".......", + ".y...p.", + ".......", + "...b...", + "......."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "The penguin in the hole cannot move.", + nrPairs : 1, + hint_arrow_x : 5, + hint_arrow_y : 2, + hint_arrow_direction : 1, + tiles : [".......", + ".....x.", + ".x.....", + ".....x.", + ".......", + ".x...r.", + "..x....", + "...R...", + "......."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Catch the penguin in the hole.", + nrPairs : 1, + hint_arrow_x : 5, + hint_arrow_y : 4, + hint_arrow_direction : 3, + tiles : [".......", + ".......", + ".......", + ".r..Xx.", + ".......", + ".......", + ".......", + "...r...", + ".....x."] +}); + +window.LEVELS.push({ + locked : true, + solved : false, + hint : "Use the seals to get the sharks out of the way in order to make the pair.", + nrPairs : 1, + hint_arrow_x : 5, + hint_arrow_y : 4, + hint_arrow_direction : 3, + tiles : [".......", + "...p...", + ".......", + ".x.@.x.", + "...@...", + "...@...", + ".@...x.", + ".x.p...", + "......."] +}); \ No newline at end of file diff --git a/assets/PenguinPairs/sounds/snd_eat.mp3 b/assets/PenguinPairs/sounds/snd_eat.mp3 new file mode 100644 index 0000000..d820876 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_eat.mp3 differ diff --git a/assets/PenguinPairs/sounds/snd_eat.ogg b/assets/PenguinPairs/sounds/snd_eat.ogg new file mode 100644 index 0000000..0fe0ed8 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_eat.ogg differ diff --git a/assets/PenguinPairs/sounds/snd_lost.mp3 b/assets/PenguinPairs/sounds/snd_lost.mp3 new file mode 100644 index 0000000..a0751f6 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_lost.mp3 differ diff --git a/assets/PenguinPairs/sounds/snd_lost.ogg b/assets/PenguinPairs/sounds/snd_lost.ogg new file mode 100644 index 0000000..86d3fb2 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_lost.ogg differ diff --git a/assets/PenguinPairs/sounds/snd_music.mp3 b/assets/PenguinPairs/sounds/snd_music.mp3 new file mode 100644 index 0000000..96a688b Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_music.mp3 differ diff --git a/assets/PenguinPairs/sounds/snd_music.ogg b/assets/PenguinPairs/sounds/snd_music.ogg new file mode 100644 index 0000000..cf5d863 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_music.ogg differ diff --git a/assets/PenguinPairs/sounds/snd_pair.mp3 b/assets/PenguinPairs/sounds/snd_pair.mp3 new file mode 100644 index 0000000..7f8c6a4 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_pair.mp3 differ diff --git a/assets/PenguinPairs/sounds/snd_pair.ogg b/assets/PenguinPairs/sounds/snd_pair.ogg new file mode 100644 index 0000000..b602158 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_pair.ogg differ diff --git a/assets/PenguinPairs/sounds/snd_won.mp3 b/assets/PenguinPairs/sounds/snd_won.mp3 new file mode 100644 index 0000000..185210b Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_won.mp3 differ diff --git a/assets/PenguinPairs/sounds/snd_won.ogg b/assets/PenguinPairs/sounds/snd_won.ogg new file mode 100644 index 0000000..7ee19f7 Binary files /dev/null and b/assets/PenguinPairs/sounds/snd_won.ogg differ diff --git a/assets/PenguinPairs/sprites/spr_arrow@4.png b/assets/PenguinPairs/sprites/spr_arrow@4.png new file mode 100644 index 0000000..2c67f31 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_arrow@4.png differ diff --git a/assets/PenguinPairs/sprites/spr_arrow_hint@4.png b/assets/PenguinPairs/sprites/spr_arrow_hint@4.png new file mode 100644 index 0000000..44ffcc1 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_arrow_hint@4.png differ diff --git a/assets/PenguinPairs/sprites/spr_arrow_hover@4.png b/assets/PenguinPairs/sprites/spr_arrow_hover@4.png new file mode 100644 index 0000000..f623c43 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_arrow_hover@4.png differ diff --git a/assets/PenguinPairs/sprites/spr_background_help.jpg b/assets/PenguinPairs/sprites/spr_background_help.jpg new file mode 100644 index 0000000..bf41e5f Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_background_help.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_background_level.jpg b/assets/PenguinPairs/sprites/spr_background_level.jpg new file mode 100644 index 0000000..415d9a6 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_background_level.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_background_levelselect.jpg b/assets/PenguinPairs/sprites/spr_background_levelselect.jpg new file mode 100644 index 0000000..3490b0e Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_background_levelselect.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_background_options.jpg b/assets/PenguinPairs/sprites/spr_background_options.jpg new file mode 100644 index 0000000..1ac8b18 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_background_options.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_background_title.jpg b/assets/PenguinPairs/sprites/spr_background_title.jpg new file mode 100644 index 0000000..394bda9 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_background_title.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_button_back.jpg b/assets/PenguinPairs/sprites/spr_button_back.jpg new file mode 100644 index 0000000..7757076 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_back.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_button_help.jpg b/assets/PenguinPairs/sprites/spr_button_help.jpg new file mode 100644 index 0000000..d2f61e3 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_help.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_button_hint.png b/assets/PenguinPairs/sprites/spr_button_hint.png new file mode 100644 index 0000000..2cf4c7f Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_hint.png differ diff --git a/assets/PenguinPairs/sprites/spr_button_offon@2.png b/assets/PenguinPairs/sprites/spr_button_offon@2.png new file mode 100644 index 0000000..c576aed Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_offon@2.png differ diff --git a/assets/PenguinPairs/sprites/spr_button_options.jpg b/assets/PenguinPairs/sprites/spr_button_options.jpg new file mode 100644 index 0000000..2f9a0ef Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_options.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_button_play.jpg b/assets/PenguinPairs/sprites/spr_button_play.jpg new file mode 100644 index 0000000..ff2822b Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_play.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_button_quit.png b/assets/PenguinPairs/sprites/spr_button_quit.png new file mode 100644 index 0000000..feb7f61 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_quit.png differ diff --git a/assets/PenguinPairs/sprites/spr_button_retry.png b/assets/PenguinPairs/sprites/spr_button_retry.png new file mode 100644 index 0000000..b20e268 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_button_retry.png differ diff --git a/assets/PenguinPairs/sprites/spr_field@2.png b/assets/PenguinPairs/sprites/spr_field@2.png new file mode 100644 index 0000000..9811900 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_field@2.png differ diff --git a/assets/PenguinPairs/sprites/spr_frame_goal.jpg b/assets/PenguinPairs/sprites/spr_frame_goal.jpg new file mode 100644 index 0000000..60ee73c Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_frame_goal.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_help.jpg b/assets/PenguinPairs/sprites/spr_help.jpg new file mode 100644 index 0000000..a2cffa7 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_help.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_level_finished_click.png b/assets/PenguinPairs/sprites/spr_level_finished_click.png new file mode 100644 index 0000000..7ecbef5 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_level_finished_click.png differ diff --git a/assets/PenguinPairs/sprites/spr_level_finished_tap.png b/assets/PenguinPairs/sprites/spr_level_finished_tap.png new file mode 100644 index 0000000..0074d9d Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_level_finished_tap.png differ diff --git a/assets/PenguinPairs/sprites/spr_level_solved@6.png b/assets/PenguinPairs/sprites/spr_level_solved@6.png new file mode 100644 index 0000000..c78493c Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_level_solved@6.png differ diff --git a/assets/PenguinPairs/sprites/spr_level_unsolved.png b/assets/PenguinPairs/sprites/spr_level_unsolved.png new file mode 100644 index 0000000..cef6f3f Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_level_unsolved.png differ diff --git a/assets/PenguinPairs/sprites/spr_lock.png b/assets/PenguinPairs/sprites/spr_lock.png new file mode 100644 index 0000000..ec1dc38 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_lock.png differ diff --git a/assets/PenguinPairs/sprites/spr_penguin@4x2.png b/assets/PenguinPairs/sprites/spr_penguin@4x2.png new file mode 100644 index 0000000..fb36169 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_penguin@4x2.png differ diff --git a/assets/PenguinPairs/sprites/spr_penguin@8.png b/assets/PenguinPairs/sprites/spr_penguin@8.png new file mode 100644 index 0000000..751be2f Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_penguin@8.png differ diff --git a/assets/PenguinPairs/sprites/spr_penguin_boxed@8.png b/assets/PenguinPairs/sprites/spr_penguin_boxed@8.png new file mode 100644 index 0000000..d189d81 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_penguin_boxed@8.png differ diff --git a/assets/PenguinPairs/sprites/spr_penguin_empty.png b/assets/PenguinPairs/sprites/spr_penguin_empty.png new file mode 100644 index 0000000..e4eb07d Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_penguin_empty.png differ diff --git a/assets/PenguinPairs/sprites/spr_penguin_pairs@8.png b/assets/PenguinPairs/sprites/spr_penguin_pairs@8.png new file mode 100644 index 0000000..d57c405 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_penguin_pairs@8.png differ diff --git a/assets/PenguinPairs/sprites/spr_shark.png b/assets/PenguinPairs/sprites/spr_shark.png new file mode 100644 index 0000000..677ce42 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_shark.png differ diff --git a/assets/PenguinPairs/sprites/spr_slider_bar.jpg b/assets/PenguinPairs/sprites/spr_slider_bar.jpg new file mode 100644 index 0000000..5d569e0 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_slider_bar.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_slider_button.jpg b/assets/PenguinPairs/sprites/spr_slider_button.jpg new file mode 100644 index 0000000..9a30ff2 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_slider_button.jpg differ diff --git a/assets/PenguinPairs/sprites/spr_wall.png b/assets/PenguinPairs/sprites/spr_wall.png new file mode 100644 index 0000000..dae7577 Binary files /dev/null and b/assets/PenguinPairs/sprites/spr_wall.png differ diff --git a/assets/TickTick/levels.js b/assets/TickTick/levels.js new file mode 100644 index 0000000..7a0f95b --- /dev/null +++ b/assets/TickTick/levels.js @@ -0,0 +1,211 @@ +window.LEVELS = []; + +window.LEVELS.push({ + hint : "Pick up all the waterdrops and reach the exit in time.", + locked : false, + solved : false, + tiles : ["....................", + ".................X..", + "..........##########", + "....................", + "WWW....WWWW.........", + "---....####.........", + "....................", + "WWW.................", + "###.........WWWWW...", + "............#####...", + "....WWW.............", + "....###.............", + "....................", + ".1........W.W.W.W.W.", + "####################"] +}); + +window.LEVELS.push({ + hint : "Watch out, the ice is slippery!", + locked : true, + solved : false, + tiles : ["....................", + ".......W....W.......", + "***.................", + "......***..***......", + "1..................X", + "****............****", + "....................", + "W.W.W..........W.W.W", + "*****..........*****", + "....................", + "W.W.W..........W.W.W", + "******........******", + "....................", + "...W.W........W.W...", + "...****......****..."] +}); + +window.LEVELS.push({ + hint : "Avoid the rockets, you can jump through some of the tiles.", + locked : true, + solved : false, + tiles : ["....................", + "r...............X...", + ".----..W.......--..W", + "....W.--..........##", + "...--....-----......", + "r................W.W", + ".......W....W..#####", + "....W..######.......", + "....................", + "-----..........-----", + "....................", + "W.W.............W..R", + "---............-----", + ".1...W..W..W..W.W.W.", + "###..#..#..#..######"] +}); + +window.LEVELS.push({ + hint : "Red tiles are hot and reduce the time before you explode.", + locked : true, + solved : false, + tiles : ["........W...W.......", + ".X....W...W...W....R", + "####^^^^^^^^^^^^^^..", + ".......W...W...W....", + "r....W...W...W...W..", + "..^^^^^^^^^^^^^^^^##", + "....W...W...W.......", + "..W...W...W...W....R", + "##^^^^^^^^^^^^^^^^..", + ".......W...W...W....", + "r....W...W...W...W..", + "..^^^^^^^^^^^^^^^^##", + "...W...W...W...W....", + ".W...W...W...W....1.", + "####################"] +}); + +window.LEVELS.push({ + hint : "Do not touch the flames, and watch how they move.", + locked : true, + solved : false, + tiles : ["....................", + "....................", + "..........X.........", + ".......######.......", + "..W..............W..", + "####..W.W.W.W....###", + ".......W.W.WBW......", + "......++++++++......", + "....-...........-...", + "....................", + "W..W............W.W.", + "####............####", + "....................", + ".1.........A........", + "#######..***********"] +}); + +window.LEVELS.push({ + hint : "Many, many, many, many, many rockets...", + locked : true, + solved : false, + tiles : ["....................", + "r..W...........X....", + "...--..W.......--...", + "....W.--........W..R", + "...--..........--...", + "r..W......W....W....", + "...--....--....--...", + "....W...........W...", + "...--........W.--...", + "r..W........--.W....", + "...--..........--...", + "....W...........W..R", + "...--..........--...", + ".1..................", + "######..####..######"] +}); + +window.LEVELS.push({ + hint : "Use the turtle to jump higher, but watch out when it sneezes.", + locked : true, + solved : false, + tiles : ["....................", + "r..W.W.W.......W.W.W", + "@@@@@@@@............", + ".............---....", + "....................", + "........---.......X.", + "................----", + "........C...........", + "......-------.......", + "................W..R", + "...............++...", + "W..T..........W.....", + "+++++.........++....", + ".1..............W...", + "#######......@@@@@.."] +}); + +window.LEVELS.push({ + hint : "When Sparky drops, get out of the way!", + locked : true, + solved : false, + tiles : ["....................", + "..........X.........", + "........-----.......", + "....................", + "..C...W.W.W.W.W..C..", + "..----------------..", + ".W..................", + "---.W...........W...", + "....-...........-...", + "....................", + ".S..W.....W......S.W", + "---.............----", + "....................", + ".1.....S.....W......", + "###...###...###....."] +}); + +window.LEVELS.push({ + hint : "There are many drops in the ocean...", + locked : true, + solved : false, + tiles : ["....................", + "r....W..............", + "...@@@..............", + "...S..........WWW..R", + "...@@.........WWW...", + "..........1...WWW...", + "...@@....@@...WWW...", + "....W.........@@@...", + "............. WWW..R", + "...@@.......--WWW...", + "..............WWW...", + "r...........--WWW...", + "..............WWW...", + ".X..A...T.....WWW...", + "######..###...@@@..."] +}); + +window.LEVELS.push({ + hint : "Six levels of hot flames...", + locked : true, + solved : false, + tiles : ["..........W........R", + "A...W....###........", + "+++++++...1.........", + "r.........W......S.X", + "C...W.....W......###", + "+++++++...W.........", + "..........W........R", + "..C.W.....W.........", + "+++++++...W..W....C.", + "..........W..+++++++", + "r..CW.....W.........", + "+++++++...W..W.....C", + "..........W..+++++++", + "..........W........R", + "..@@@@@@@@@@@@@@@@.."] +}); \ No newline at end of file diff --git a/assets/TickTick/sounds/snd_music.mp3 b/assets/TickTick/sounds/snd_music.mp3 new file mode 100644 index 0000000..f90bccb Binary files /dev/null and b/assets/TickTick/sounds/snd_music.mp3 differ diff --git a/assets/TickTick/sounds/snd_music.ogg b/assets/TickTick/sounds/snd_music.ogg new file mode 100644 index 0000000..84790f3 Binary files /dev/null and b/assets/TickTick/sounds/snd_music.ogg differ diff --git a/assets/TickTick/sounds/snd_player_die.mp3 b/assets/TickTick/sounds/snd_player_die.mp3 new file mode 100644 index 0000000..a1c38d5 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_die.mp3 differ diff --git a/assets/TickTick/sounds/snd_player_die.ogg b/assets/TickTick/sounds/snd_player_die.ogg new file mode 100644 index 0000000..aea48b1 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_die.ogg differ diff --git a/assets/TickTick/sounds/snd_player_explode.mp3 b/assets/TickTick/sounds/snd_player_explode.mp3 new file mode 100644 index 0000000..d820876 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_explode.mp3 differ diff --git a/assets/TickTick/sounds/snd_player_explode.ogg b/assets/TickTick/sounds/snd_player_explode.ogg new file mode 100644 index 0000000..0fe0ed8 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_explode.ogg differ diff --git a/assets/TickTick/sounds/snd_player_fall.mp3 b/assets/TickTick/sounds/snd_player_fall.mp3 new file mode 100644 index 0000000..7278c96 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_fall.mp3 differ diff --git a/assets/TickTick/sounds/snd_player_fall.ogg b/assets/TickTick/sounds/snd_player_fall.ogg new file mode 100644 index 0000000..05414d5 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_fall.ogg differ diff --git a/assets/TickTick/sounds/snd_player_jump.mp3 b/assets/TickTick/sounds/snd_player_jump.mp3 new file mode 100644 index 0000000..600e266 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_jump.mp3 differ diff --git a/assets/TickTick/sounds/snd_player_jump.ogg b/assets/TickTick/sounds/snd_player_jump.ogg new file mode 100644 index 0000000..4bf927b Binary files /dev/null and b/assets/TickTick/sounds/snd_player_jump.ogg differ diff --git a/assets/TickTick/sounds/snd_player_won.mp3 b/assets/TickTick/sounds/snd_player_won.mp3 new file mode 100644 index 0000000..ef77bb9 Binary files /dev/null and b/assets/TickTick/sounds/snd_player_won.mp3 differ diff --git a/assets/TickTick/sounds/snd_player_won.ogg b/assets/TickTick/sounds/snd_player_won.ogg new file mode 100644 index 0000000..9ec26bf Binary files /dev/null and b/assets/TickTick/sounds/snd_player_won.ogg differ diff --git a/assets/TickTick/sounds/snd_water_collected.mp3 b/assets/TickTick/sounds/snd_water_collected.mp3 new file mode 100644 index 0000000..6496561 Binary files /dev/null and b/assets/TickTick/sounds/snd_water_collected.mp3 differ diff --git a/assets/TickTick/sounds/snd_water_collected.ogg b/assets/TickTick/sounds/snd_water_collected.ogg new file mode 100644 index 0000000..8aa7084 Binary files /dev/null and b/assets/TickTick/sounds/snd_water_collected.ogg differ diff --git a/assets/TickTick/sprites/backgrounds/spr_cloud_1.png b/assets/TickTick/sprites/backgrounds/spr_cloud_1.png new file mode 100644 index 0000000..5ba9846 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_cloud_1.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_cloud_2.png b/assets/TickTick/sprites/backgrounds/spr_cloud_2.png new file mode 100644 index 0000000..6940d11 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_cloud_2.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_cloud_3.png b/assets/TickTick/sprites/backgrounds/spr_cloud_3.png new file mode 100644 index 0000000..3e702d1 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_cloud_3.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_cloud_4.png b/assets/TickTick/sprites/backgrounds/spr_cloud_4.png new file mode 100644 index 0000000..bf58ba3 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_cloud_4.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_cloud_5.png b/assets/TickTick/sprites/backgrounds/spr_cloud_5.png new file mode 100644 index 0000000..7ac217c Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_cloud_5.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_help.jpg b/assets/TickTick/sprites/backgrounds/spr_help.jpg new file mode 100644 index 0000000..ba1827e Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_help.jpg differ diff --git a/assets/TickTick/sprites/backgrounds/spr_levelselect.jpg b/assets/TickTick/sprites/backgrounds/spr_levelselect.jpg new file mode 100644 index 0000000..5a80c45 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_levelselect.jpg differ diff --git a/assets/TickTick/sprites/backgrounds/spr_mountain_1.png b/assets/TickTick/sprites/backgrounds/spr_mountain_1.png new file mode 100644 index 0000000..610bbed Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_mountain_1.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_mountain_2.png b/assets/TickTick/sprites/backgrounds/spr_mountain_2.png new file mode 100644 index 0000000..a01b671 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_mountain_2.png differ diff --git a/assets/TickTick/sprites/backgrounds/spr_sky.jpg b/assets/TickTick/sprites/backgrounds/spr_sky.jpg new file mode 100644 index 0000000..47abb5d Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_sky.jpg differ diff --git a/assets/TickTick/sprites/backgrounds/spr_title.jpg b/assets/TickTick/sprites/backgrounds/spr_title.jpg new file mode 100644 index 0000000..5d4fb07 Binary files /dev/null and b/assets/TickTick/sprites/backgrounds/spr_title.jpg differ diff --git a/assets/TickTick/sprites/flame/spr_flame@9.png b/assets/TickTick/sprites/flame/spr_flame@9.png new file mode 100644 index 0000000..388c781 Binary files /dev/null and b/assets/TickTick/sprites/flame/spr_flame@9.png differ diff --git a/assets/TickTick/sprites/gui/spr_button_back.png b/assets/TickTick/sprites/gui/spr_button_back.png new file mode 100644 index 0000000..c15efe8 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_button_back.png differ diff --git a/assets/TickTick/sprites/gui/spr_button_help.png b/assets/TickTick/sprites/gui/spr_button_help.png new file mode 100644 index 0000000..ec519a8 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_button_help.png differ diff --git a/assets/TickTick/sprites/gui/spr_button_play.png b/assets/TickTick/sprites/gui/spr_button_play.png new file mode 100644 index 0000000..8c8e328 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_button_play.png differ diff --git a/assets/TickTick/sprites/gui/spr_button_quit.png b/assets/TickTick/sprites/gui/spr_button_quit.png new file mode 100644 index 0000000..69767b3 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_button_quit.png differ diff --git a/assets/TickTick/sprites/gui/spr_buttons_player@3.png b/assets/TickTick/sprites/gui/spr_buttons_player@3.png new file mode 100644 index 0000000..dbdb65a Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_buttons_player@3.png differ diff --git a/assets/TickTick/sprites/gui/spr_level_locked.png b/assets/TickTick/sprites/gui/spr_level_locked.png new file mode 100644 index 0000000..4946e20 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_level_locked.png differ diff --git a/assets/TickTick/sprites/gui/spr_level_solved.png b/assets/TickTick/sprites/gui/spr_level_solved.png new file mode 100644 index 0000000..2136c26 Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_level_solved.png differ diff --git a/assets/TickTick/sprites/gui/spr_level_unsolved.png b/assets/TickTick/sprites/gui/spr_level_unsolved.png new file mode 100644 index 0000000..a1fe7ed Binary files /dev/null and b/assets/TickTick/sprites/gui/spr_level_unsolved.png differ diff --git a/assets/TickTick/sprites/overlays/spr_frame_hint.png b/assets/TickTick/sprites/overlays/spr_frame_hint.png new file mode 100644 index 0000000..24af1ad Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_frame_hint.png differ diff --git a/assets/TickTick/sprites/overlays/spr_gameover_click.png b/assets/TickTick/sprites/overlays/spr_gameover_click.png new file mode 100644 index 0000000..a724670 Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_gameover_click.png differ diff --git a/assets/TickTick/sprites/overlays/spr_gameover_tap.png b/assets/TickTick/sprites/overlays/spr_gameover_tap.png new file mode 100644 index 0000000..6a93d85 Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_gameover_tap.png differ diff --git a/assets/TickTick/sprites/overlays/spr_timer.png b/assets/TickTick/sprites/overlays/spr_timer.png new file mode 100644 index 0000000..0331d5f Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_timer.png differ diff --git a/assets/TickTick/sprites/overlays/spr_welldone_click.png b/assets/TickTick/sprites/overlays/spr_welldone_click.png new file mode 100644 index 0000000..bb984ac Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_welldone_click.png differ diff --git a/assets/TickTick/sprites/overlays/spr_welldone_tap.png b/assets/TickTick/sprites/overlays/spr_welldone_tap.png new file mode 100644 index 0000000..2e794f3 Binary files /dev/null and b/assets/TickTick/sprites/overlays/spr_welldone_tap.png differ diff --git a/assets/TickTick/sprites/player/spr_celebrate@14.png b/assets/TickTick/sprites/player/spr_celebrate@14.png new file mode 100644 index 0000000..905d126 Binary files /dev/null and b/assets/TickTick/sprites/player/spr_celebrate@14.png differ diff --git a/assets/TickTick/sprites/player/spr_die@5.png b/assets/TickTick/sprites/player/spr_die@5.png new file mode 100644 index 0000000..d38745b Binary files /dev/null and b/assets/TickTick/sprites/player/spr_die@5.png differ diff --git a/assets/TickTick/sprites/player/spr_explode@5x5.png b/assets/TickTick/sprites/player/spr_explode@5x5.png new file mode 100644 index 0000000..3e107f2 Binary files /dev/null and b/assets/TickTick/sprites/player/spr_explode@5x5.png differ diff --git a/assets/TickTick/sprites/player/spr_idle.png b/assets/TickTick/sprites/player/spr_idle.png new file mode 100644 index 0000000..0d10bfe Binary files /dev/null and b/assets/TickTick/sprites/player/spr_idle.png differ diff --git a/assets/TickTick/sprites/player/spr_jump@14.png b/assets/TickTick/sprites/player/spr_jump@14.png new file mode 100644 index 0000000..4061140 Binary files /dev/null and b/assets/TickTick/sprites/player/spr_jump@14.png differ diff --git a/assets/TickTick/sprites/player/spr_run@13.png b/assets/TickTick/sprites/player/spr_run@13.png new file mode 100644 index 0000000..b2f6e34 Binary files /dev/null and b/assets/TickTick/sprites/player/spr_run@13.png differ diff --git a/assets/TickTick/sprites/rocket/spr_rocket@3.png b/assets/TickTick/sprites/rocket/spr_rocket@3.png new file mode 100644 index 0000000..122eec8 Binary files /dev/null and b/assets/TickTick/sprites/rocket/spr_rocket@3.png differ diff --git a/assets/TickTick/sprites/sparky/spr_electrocute.png b/assets/TickTick/sprites/sparky/spr_electrocute.png new file mode 100644 index 0000000..1a123dc Binary files /dev/null and b/assets/TickTick/sprites/sparky/spr_electrocute.png differ diff --git a/assets/TickTick/sprites/sparky/spr_electrocute@6x5.png b/assets/TickTick/sprites/sparky/spr_electrocute@6x5.png new file mode 100644 index 0000000..5a63819 Binary files /dev/null and b/assets/TickTick/sprites/sparky/spr_electrocute@6x5.png differ diff --git a/assets/TickTick/sprites/sparky/spr_idle.png b/assets/TickTick/sprites/sparky/spr_idle.png new file mode 100644 index 0000000..03a457c Binary files /dev/null and b/assets/TickTick/sprites/sparky/spr_idle.png differ diff --git a/assets/TickTick/sprites/spr_goal.png b/assets/TickTick/sprites/spr_goal.png new file mode 100644 index 0000000..c0aa2ee Binary files /dev/null and b/assets/TickTick/sprites/spr_goal.png differ diff --git a/assets/TickTick/sprites/spr_water.png b/assets/TickTick/sprites/spr_water.png new file mode 100644 index 0000000..26808cf Binary files /dev/null and b/assets/TickTick/sprites/spr_water.png differ diff --git a/assets/TickTick/sprites/tiles/spr_platform.png b/assets/TickTick/sprites/tiles/spr_platform.png new file mode 100644 index 0000000..be1be43 Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_platform.png differ diff --git a/assets/TickTick/sprites/tiles/spr_platform_hot.png b/assets/TickTick/sprites/tiles/spr_platform_hot.png new file mode 100644 index 0000000..dbc0321 Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_platform_hot.png differ diff --git a/assets/TickTick/sprites/tiles/spr_platform_ice.png b/assets/TickTick/sprites/tiles/spr_platform_ice.png new file mode 100644 index 0000000..95e43da Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_platform_ice.png differ diff --git a/assets/TickTick/sprites/tiles/spr_wall.png b/assets/TickTick/sprites/tiles/spr_wall.png new file mode 100644 index 0000000..bf2957e Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_wall.png differ diff --git a/assets/TickTick/sprites/tiles/spr_wall_hot.png b/assets/TickTick/sprites/tiles/spr_wall_hot.png new file mode 100644 index 0000000..1cbcdba Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_wall_hot.png differ diff --git a/assets/TickTick/sprites/tiles/spr_wall_ice.png b/assets/TickTick/sprites/tiles/spr_wall_ice.png new file mode 100644 index 0000000..331e6dd Binary files /dev/null and b/assets/TickTick/sprites/tiles/spr_wall_ice.png differ diff --git a/assets/TickTick/sprites/turtle/spr_idle.png b/assets/TickTick/sprites/turtle/spr_idle.png new file mode 100644 index 0000000..68995c1 Binary files /dev/null and b/assets/TickTick/sprites/turtle/spr_idle.png differ diff --git a/assets/TickTick/sprites/turtle/spr_sneeze@9.png b/assets/TickTick/sprites/turtle/spr_sneeze@9.png new file mode 100644 index 0000000..96f8998 Binary files /dev/null and b/assets/TickTick/sprites/turtle/spr_sneeze@9.png differ diff --git a/powerupjs/Animation.js b/powerupjs/Animation.js new file mode 100644 index 0000000..a4760d5 --- /dev/null +++ b/powerupjs/Animation.js @@ -0,0 +1,14 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Animation(sprite, looping, frameTime) { + this.sprite = sprite; + this.frameTime = typeof frameTime != 'undefined' ? frameTime : 0.1; + this.looping = looping; + } + + powerupjs.Animation = Animation; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/Canvas2D.js b/powerupjs/Canvas2D.js new file mode 100644 index 0000000..d3376f6 --- /dev/null +++ b/powerupjs/Canvas2D.js @@ -0,0 +1,155 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Canvas2D_Singleton() { + this._canvas = null; + this._canvasContext = null; + this._pixeldrawingCanvas = null; + this._canvasOffset = powerupjs.Vector2.zero; + } + + Object.defineProperty(Canvas2D_Singleton.prototype, "offset", + { + get: function () { + return this._canvasOffset; + } + }); + + Object.defineProperty(Canvas2D_Singleton.prototype, "scale", + { + get: function () { + return new powerupjs.Vector2(this._canvas.width / powerupjs.Game.size.x, + this._canvas.height / powerupjs.Game.size.y); + } + }); + + Canvas2D_Singleton.prototype.initialize = function (divName, canvasName) { + this._canvas = document.getElementById(canvasName); + this._div = document.getElementById(divName); + + if (this._canvas.getContext) + this._canvasContext = this._canvas.getContext('2d'); + else { + alert('Your browser is not HTML5 compatible.!'); + return; + } + + this._pixeldrawingCanvas = document.createElement('canvas'); + + window.onresize = this.resize; + this.resize(); + }; + + Canvas2D_Singleton.prototype.clear = function () { + this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height); + }; + + Canvas2D_Singleton.prototype.resize = function () { + var gameCanvas = powerupjs.Canvas2D._canvas; + var gameArea = powerupjs.Canvas2D._div; + var widthToHeight = powerupjs.Game.size.x / powerupjs.Game.size.y; + var newWidth = window.innerWidth; + var newHeight = window.innerHeight; + var newWidthToHeight = newWidth / newHeight; + + if (newWidthToHeight > widthToHeight) { + newWidth = newHeight * widthToHeight; + } else { + newHeight = newWidth / widthToHeight; + } + gameArea.style.width = newWidth + 'px'; + gameArea.style.height = newHeight + 'px'; + + gameArea.style.marginTop = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginLeft = (window.innerWidth - newWidth) / 2 + 'px'; + gameArea.style.marginBottom = (window.innerHeight - newHeight) / 2 + 'px'; + gameArea.style.marginRight = (window.innerWidth - newWidth) / 2 + 'px'; + + gameCanvas.width = newWidth; + gameCanvas.height = newHeight; + + var offset = powerupjs.Vector2.zero; + if (gameCanvas.offsetParent) { + do { + offset.x += gameCanvas.offsetLeft; + offset.y += gameCanvas.offsetTop; + } while ((gameCanvas = gameCanvas.offsetParent)); + } + powerupjs.Canvas2D._canvasOffset = offset; + }; + + Canvas2D_Singleton.prototype.drawImage = function (sprite, position, rotation, scale, origin, sourceRect, mirror) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : powerupjs.Vector2.zero; + rotation = typeof rotation !== 'undefined' ? rotation : 0; + scale = typeof scale !== 'undefined' ? scale : 1; + origin = typeof origin !== 'undefined' ? origin : powerupjs.Vector2.zero; + sourceRect = typeof sourceRect !== 'undefined' ? sourceRect : new powerupjs.Rectangle(0, 0, sprite.width, sprite.height); + + this._canvasContext.save(); + if (mirror) { + this._canvasContext.scale(scale * canvasScale.x * -1, scale * canvasScale.y); + this._canvasContext.translate(-position.x - sourceRect.width, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + sourceRect.width - origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + else { + this._canvasContext.scale(scale * canvasScale.x, scale * canvasScale.y); + this._canvasContext.translate(position.x, position.y); + this._canvasContext.rotate(rotation); + this._canvasContext.drawImage(sprite, sourceRect.x, sourceRect.y, + sourceRect.width, sourceRect.height, + -origin.x, -origin.y, + sourceRect.width, sourceRect.height); + } + this._canvasContext.restore(); + }; + + Canvas2D_Singleton.prototype.drawText = function (text, position, origin, color, textAlign, fontname, fontsize) { + var canvasScale = this.scale; + + position = typeof position !== 'undefined' ? position : powerupjs.Vector2.zero; + origin = typeof origin !== 'undefined' ? origin : powerupjs.Vector2.zero; + color = typeof color !== 'undefined' ? color : powerupjs.Color.black; + textAlign = typeof textAlign !== 'undefined' ? textAlign : "top"; + fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.translate(position.x - origin.x, position.y - origin.y); + this._canvasContext.textBaseline = 'top'; + this._canvasContext.font = fontsize + " " + fontname; + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.textAlign = textAlign; + this._canvasContext.fillText(text, 0, 0); + this._canvasContext.restore(); + }; + + Canvas2D_Singleton.prototype.drawPixel = function (x, y, color) { + var canvasscale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasscale.x, canvasscale.y); + this._canvasContext.fillStyle = color.toString(); + this._canvasContext.fillRect(x, y, 1, 1); + this._canvasContext.restore(); + }; + + Canvas2D_Singleton.prototype.drawRectangle = function (x, y, width, height) { + var canvasScale = this.scale; + this._canvasContext.save(); + this._canvasContext.scale(canvasScale.x, canvasScale.y); + this._canvasContext.strokeRect(x, y, width, height); + this._canvasContext.restore(); + }; + + powerupjs.Canvas2D = new Canvas2D_Singleton(); + + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/Game.js b/powerupjs/Game.js new file mode 100644 index 0000000..f0a727c --- /dev/null +++ b/powerupjs/Game.js @@ -0,0 +1,90 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + var requestAnimationFrame = (function () { + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { + window.setTimeout(callback, 1000 / 60); + }; + })(); + + function Game_Singleton() { + this._totalTime = 0; + this._size = null; + this._spritesStillLoading = 0; + this._totalSprites = 0; + } + + Object.defineProperty(Game_Singleton.prototype, "totalTime", + { + get: function () { + return this._totalTime; + } + }); + + Object.defineProperty(Game_Singleton.prototype, "size", + { + get: function () { + return this._size; + } + }); + + Object.defineProperty(Game_Singleton.prototype, "screenRect", + { + get: function () { + return new powerupjs.Rectangle(0, 0, this._size.x, this._size.y); + } + }); + + Game_Singleton.prototype.start = function (divName, canvasName, x, y) { + this._size = new powerupjs.Vector2(x, y); + + powerupjs.Canvas2D.initialize(divName, canvasName); + this.loadAssets(); + this.assetLoadingLoop(); + }; + + Game_Singleton.prototype.initialize = function () { + }; + + Game_Singleton.prototype.loadAssets = function () { + }; + + Game_Singleton.prototype.assetLoadingLoop = function () { + powerupjs.Canvas2D.clear(); + powerupjs.Canvas2D.drawText(Math.round((powerupjs.Game._totalSprites - powerupjs.Game._spritesStillLoading) / + powerupjs.Game._totalSprites * 100) + "%"); + + if (powerupjs.Game._spritesStillLoading > 0) + requestAnimationFrame(powerupjs.Game.assetLoadingLoop); + else { + powerupjs.Game.initialize(); + requestAnimationFrame(powerupjs.Game.mainLoop); + } + }; + + Game_Singleton.prototype.mainLoop = function () { + var delta = 1 / 60; + powerupjs.Game._totalTime += delta; + + powerupjs.GameStateManager.handleInput(delta); + powerupjs.GameStateManager.update(delta); + powerupjs.Canvas2D.clear(); + powerupjs.GameStateManager.draw(); + + powerupjs.Keyboard.reset(); + powerupjs.Mouse.reset(); + powerupjs.Touch.reset(); + + requestAnimationFrame(powerupjs.Game.mainLoop); + }; + + powerupjs.Game = new Game_Singleton(); + return powerupjs; + +}(powerupjs || {})); diff --git a/powerupjs/GameStateManager.js b/powerupjs/GameStateManager.js new file mode 100644 index 0000000..25cccd1 --- /dev/null +++ b/powerupjs/GameStateManager.js @@ -0,0 +1,53 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function GameStateManager_Singleton() { + this._gameStates = []; + this._currentGameState = null; + } + + GameStateManager_Singleton.prototype.add = function (gamestate) { + this._gameStates.push(gamestate); + this._currentGameState = gamestate; + return this._gameStates.length - 1; + }; + + GameStateManager_Singleton.prototype.get = function (id) { + if (id < 0 || id >= this._gameStates.length) + return null; + else + return this._gameStates[id]; + }; + + GameStateManager_Singleton.prototype.switchTo = function (id) { + if (id < 0 || id >= this._gameStates.length) + return; + this._currentGameState = this._gameStates[id]; + }; + + GameStateManager_Singleton.prototype.handleInput = function (delta) { + if (this._currentGameState != null) + this._currentGameState.handleInput(delta); + }; + + GameStateManager_Singleton.prototype.update = function (delta) { + if (this._currentGameState != null) + this._currentGameState.update(delta); + }; + + GameStateManager_Singleton.prototype.draw = function () { + if (this._currentGameState != null) + this._currentGameState.draw(); + }; + + GameStateManager_Singleton.prototype.reset = function () { + if (this._currentGameState != null) + this._currentGameState.reset(); + }; + + powerupjs.GameStateManager = new GameStateManager_Singleton(); + return powerupjs; + +})(powerupjs || {}); + diff --git a/powerupjs/IGameLoopObject.js b/powerupjs/IGameLoopObject.js new file mode 100644 index 0000000..513d37c --- /dev/null +++ b/powerupjs/IGameLoopObject.js @@ -0,0 +1,26 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function IGameLoopObject() { + } + + IGameLoopObject.prototype.initialize = function () { + }; + + IGameLoopObject.prototype.handleInput = function (delta) { + }; + + IGameLoopObject.prototype.update = function (delta) { + }; + + IGameLoopObject.prototype.draw = function () { + }; + + IGameLoopObject.prototype.reset = function () { + }; + + powerupjs.IGameLoopObject = IGameLoopObject; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/LAB.min.js b/powerupjs/LAB.min.js new file mode 100644 index 0000000..e710dfe --- /dev/null +++ b/powerupjs/LAB.min.js @@ -0,0 +1,5 @@ +/*! LAB.js (LABjs :: Loading And Blocking JavaScript) + v2.0.3 (c) Kyle Simpson + MIT License +*/ +(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b0){for(var a=0;a=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); \ No newline at end of file diff --git a/powerupjs/Math.js b/powerupjs/Math.js new file mode 100644 index 0000000..5e1da3b --- /dev/null +++ b/powerupjs/Math.js @@ -0,0 +1,23 @@ +"use strict"; + +if (!Math.sign) { + Math.sign = function (value) { + if (value > 0) + return 1; + else if (value < 0) + return -1; + else + return 0; + }; +} + +if (!Math.clamp) { + Math.clamp = function (value, min, max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; + }; +} \ No newline at end of file diff --git a/powerupjs/Sound.js b/powerupjs/Sound.js new file mode 100644 index 0000000..9edfebe --- /dev/null +++ b/powerupjs/Sound.js @@ -0,0 +1,42 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Sound(sound, looping) { + this.looping = typeof looping !== 'undefined' ? looping : false; + this.snd = new Audio(); + if (this.snd.canPlayType("audio/ogg")) { + this.snd.src = sound + ".ogg"; + } else if (this.snd.canPlayType("audio/mpeg")) { + this.snd.src = sound + ".mp3"; + } else // we cannot play audio in this browser + this.snd = null; + } + + Object.defineProperty(Sound.prototype, "volume", + { + get: function () { + return this.snd.volume; + }, + set: function (value) { + this.snd.volume = value; + } + }); + + Sound.prototype.play = function () { + if (this.snd === null) + return; + this.snd.load(); + this.snd.autoplay = true; + if (!this.looping) + return; + this.snd.addEventListener('ended', function () { + this.load(); + this.autoplay = true; + }, false); + }; + + powerupjs.Sound = Sound; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/SpriteSheet.js b/powerupjs/SpriteSheet.js new file mode 100644 index 0000000..5b774e4 --- /dev/null +++ b/powerupjs/SpriteSheet.js @@ -0,0 +1,125 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function SpriteSheet(imageName, createCollisionMask) { + console.log("Loading sprite: " + imageName); + powerupjs.Game._spritesStillLoading += 1; + powerupjs.Game._totalSprites += 1; + + this._image = new Image(); + this._image.src = imageName; + this._sheetColumns = 1; + this._sheetRows = 1; + this._collisionMask = null; + + var sprite = this; + this._image.onload = function () { + if (createCollisionMask) { + console.log("Creating collision mask for sprite: " + imageName); + sprite.createPixelCollisionMask(); + } + powerupjs.Game._spritesStillLoading -= 1; + }; + + // determine the number of sheet rows and columns + var pathSplit = imageName.split('/'); + var fileName = pathSplit[pathSplit.length - 1]; + var fileSplit = fileName.split("/")[0].split(".")[0].split("@"); + if (fileSplit.length <= 1) + return; + var colRow = fileSplit[fileSplit.length - 1].split("x"); + this._sheetColumns = colRow[0]; + if (colRow.length === 2) + this._sheetRows = colRow[1]; + } + + Object.defineProperty(SpriteSheet.prototype, "image", + { + get: function () { + return this._image; + } + }); + + Object.defineProperty(SpriteSheet.prototype, "width", + { + get: function () { + return this._image.width / this._sheetColumns; + } + }); + + Object.defineProperty(SpriteSheet.prototype, "height", + { + get: function () { + return this._image.height / this._sheetRows; + } + }); + + Object.defineProperty(SpriteSheet.prototype, "size", + { + get: function () { + return new powerupjs.Vector2(this.width, this.height); + } + }); + + Object.defineProperty(SpriteSheet.prototype, "center", + { + get: function () { + return this.size.divideBy(2); + } + }); + + Object.defineProperty(SpriteSheet.prototype, "nrSheetElements", + { + get: function () { + return this._sheetRows * this._sheetColumns; + } + }); + + SpriteSheet.prototype.createPixelCollisionMask = function () { + this._collisionMask = []; + var w = this._image.width; + var h = this._image.height; + powerupjs.Canvas2D._pixeldrawingCanvas.width = w; + powerupjs.Canvas2D._pixeldrawingCanvas.height = h; + var ctx = powerupjs.Canvas2D._pixeldrawingCanvas.getContext('2d'); + + ctx.clearRect(0, 0, w, h); + ctx.save(); + ctx.drawImage(this._image, 0, 0, w, h, 0, 0, w, h); + ctx.restore(); + var imageData = ctx.getImageData(0, 0, w, h); + for (var x = 3, l = w * h * 4; x < l; x += 4) { + this._collisionMask.push(imageData.data[x]); + } + }; + + SpriteSheet.prototype.getAlpha = function (x, y, sheetIndex, mirror) { + if (this._collisionMask === null) + return 255; + + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var textureX = columnIndex * this.width + x; + if (mirror) + textureX = (columnIndex + 1) * this.width - x - 1; + var textureY = rowIndex * this.height + y; + var arrayIndex = Math.floor(textureY * this._image.width + textureX); + if (arrayIndex < 0 || arrayIndex >= this._collisionMask.length) + return 0; + else + return this._collisionMask[arrayIndex]; + }; + + SpriteSheet.prototype.draw = function (position, origin, sheetIndex, mirror) { + var columnIndex = sheetIndex % this._sheetColumns; + var rowIndex = Math.floor(sheetIndex / this._sheetColumns) % this._sheetRows; + var imagePart = new powerupjs.Rectangle(columnIndex * this.width, rowIndex * this.height, + this.width, this.height); + powerupjs.Canvas2D.drawImage(this._image, position, 0, 1, origin, imagePart, mirror); + }; + + powerupjs.SpriteSheet = SpriteSheet; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/gameobjects/AnimatedGameObject.js b/powerupjs/gameobjects/AnimatedGameObject.js new file mode 100644 index 0000000..7f6badf --- /dev/null +++ b/powerupjs/gameobjects/AnimatedGameObject.js @@ -0,0 +1,48 @@ +"use strict"; +var powerupjs = (function (powerupjs) { + + function AnimatedGameObject(layer, id) { + powerupjs.SpriteGameObject.call(this, null, layer, id); + + this._animations = {}; + this._current = null; + this._time = 0; + } + + AnimatedGameObject.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + + AnimatedGameObject.prototype.loadAnimation = function (animname, id, looping, frametime) { + this._animations[id] = new powerupjs.Animation(animname, looping, frametime); + }; + + AnimatedGameObject.prototype.playAnimation = function (id) { + if (this._current === this._animations[id]) + return; + this._sheetIndex = 0; + this._time = 0; + this._current = this._animations[id]; + this.sprite = this._current.sprite; + }; + + AnimatedGameObject.prototype.animationEnded = function () { + return !this._current.looping && this.sheetIndex >= this.sprite.nrSheetElements - 1; + }; + + AnimatedGameObject.prototype.update = function (delta) { + this._time += delta; + while (this._time > this._current.frameTime) { + this._time -= this._current.frameTime; + this._sheetIndex++; + if (this._sheetIndex > this.sprite.nrSheetElements - 1) + if (this._current.looping) + this._sheetIndex = 0; + else + this._sheetIndex = this.sprite.nrSheetElements - 1; + } + powerupjs.SpriteGameObject.prototype.update.call(this, delta); + }; + + powerupjs.AnimatedGameObject = AnimatedGameObject; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/gameobjects/GameObject.js b/powerupjs/gameobjects/GameObject.js new file mode 100644 index 0000000..fb46ca6 --- /dev/null +++ b/powerupjs/gameobjects/GameObject.js @@ -0,0 +1,63 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function GameObject(layer, id) { + powerupjs.IGameLoopObject.call(this); + + this.layer = typeof layer !== 'undefined' ? layer : 0; + this.id = typeof id !== 'undefined' ? id : 0; + this.parent = null; + this.position = powerupjs.Vector2.zero; + this.velocity = powerupjs.Vector2.zero; + this._visible = true; + } + + GameObject.prototype = Object.create(powerupjs.IGameLoopObject.prototype); + + Object.defineProperty(GameObject.prototype, "visible", + { + get: function () { + if (this.parent === null) + return this._visible; + else + return this._visible && this.parent.visible; + }, + + set: function (value) { + this._visible = value; + } + }); + + Object.defineProperty(GameObject.prototype, "root", + { + get: function () { + if (this.parent === null) + return this; + else + return this.parent.root; + } + }); + + Object.defineProperty(GameObject.prototype, "worldPosition", + { + get: function () { + if (this.parent !== null) + return this.parent.worldPosition.addTo(this.position); + else + return this.position.copy(); + } + }); + + GameObject.prototype.reset = function () { + this._visible = true; + }; + + GameObject.prototype.update = function (delta) { + this.position.addTo(this.velocity.multiply(delta)); + }; + + powerupjs.GameObject = GameObject; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/gameobjects/GameObjectGrid.js b/powerupjs/gameobjects/GameObjectGrid.js new file mode 100644 index 0000000..96c9b54 --- /dev/null +++ b/powerupjs/gameobjects/GameObjectGrid.js @@ -0,0 +1,64 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function GameObjectGrid(rows, columns, layer, id) { + powerupjs.GameObjectList.call(this, layer, id); + + this.cellWidth = 0; + this.cellHeight = 0; + this._rows = rows; + this._columns = columns; + } + + GameObjectGrid.prototype = Object.create(powerupjs.GameObjectList.prototype); + + Object.defineProperty(GameObjectGrid.prototype, "rows", { + get: function () { + return this._rows; + } + }); + + Object.defineProperty(GameObjectGrid.prototype, "columns", { + get: function () { + return this._columns; + } + }); + + GameObjectGrid.prototype.add = function (gameobject) { + var row = Math.floor(this._gameObjects.length / this._columns); + var col = this._gameObjects.length % this._columns; + this._gameObjects.push(gameobject); + gameobject.parent = this; + gameobject.position = new powerupjs.Vector2(col * this.cellWidth, row * this.cellHeight); + }; + + GameObjectGrid.prototype.addAt = function (gameobject, col, row) { + this._gameObjects[row * this._columns + col] = gameobject; + gameobject.parent = this; + gameobject.position = new powerupjs.Vector2(col * this.cellWidth, row * this.cellHeight); + }; + + GameObjectGrid.prototype.at = function (col, row) { + var index = row * this._columns + col; + if (index < 0 || index >= this._gameObjects.length) + return null; + else + return this._gameObjects[index]; + }; + + GameObjectGrid.prototype.getAnchorPosition = function (gameobject) { + var l = this._gameObjects.length; + for (var i = 0; i < l; ++i) + if (this._gameObjects[i] == gameobject) { + var row = Math.floor(i / this.columns); + var col = i - row * this.columns; + return new powerupjs.Vector2(col * this.cellWidth, row * this.cellHeight); + } + return powerupjs.Vector2.zero; + }; + + powerupjs.GameObjectGrid = GameObjectGrid; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/gameobjects/GameObjectList.js b/powerupjs/gameobjects/GameObjectList.js new file mode 100644 index 0000000..3ed1d07 --- /dev/null +++ b/powerupjs/gameobjects/GameObjectList.js @@ -0,0 +1,86 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + GameObjectList.prototype = Object.create(powerupjs.GameObject.prototype); + + function GameObjectList(layer, id) { + powerupjs.GameObject.call(this, layer, id); + + this._gameObjects = []; + } + + Object.defineProperty(GameObjectList.prototype, "length", { + get: function () { + return this._gameObjects.length; + } + }); + + GameObjectList.prototype.add = function (gameobject) { + this._gameObjects.push(gameobject); + gameobject.parent = this; + this._gameObjects.sort(function (a, b) { + return a.layer - b.layer; + }); + }; + + GameObjectList.prototype.remove = function (gameobject) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (gameobject !== this._gameObjects[i]) + continue; + this._gameObjects.splice(i, 1); + gameobject.parent = null; + return; + } + }; + + GameObjectList.prototype.at = function (index) { + if (index < 0 || index >= this._gameObjects.length) + return null; + return this._gameObjects[index]; + }; + + GameObjectList.prototype.clear = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].parent = null; + this._gameObjects = []; + }; + + GameObjectList.prototype.find = function (id) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) { + if (this._gameObjects[i].id === id) + return this._gameObjects[i]; + if (this._gameObjects[i] instanceof powerupjs.GameObjectList) { + var obj = this._gameObjects[i].find(id); + if (obj !== null) + return obj; + } + } + return null; + }; + + GameObjectList.prototype.handleInput = function (delta) { + for (var i = this._gameObjects.length - 1; i >= 0; --i) + this._gameObjects[i].handleInput(delta); + }; + + GameObjectList.prototype.update = function (delta) { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].update(delta); + }; + + GameObjectList.prototype.draw = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + if (this._gameObjects[i].visible) + this._gameObjects[i].draw(); + }; + + GameObjectList.prototype.reset = function () { + for (var i = 0, l = this._gameObjects.length; i < l; ++i) + this._gameObjects[i].reset(); + }; + + powerupjs.GameObjectList = GameObjectList; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/gameobjects/SpriteGameObject.js b/powerupjs/gameobjects/SpriteGameObject.js new file mode 100644 index 0000000..b780411 --- /dev/null +++ b/powerupjs/gameobjects/SpriteGameObject.js @@ -0,0 +1,112 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function SpriteGameObject(sprite, layer, id) { + powerupjs.GameObject.call(this, layer, id); + + this.sprite = sprite; + + this.origin = powerupjs.Vector2.zero; + this.mirror = false; + this._sheetIndex = 0; + } + + SpriteGameObject.prototype = Object.create(powerupjs.GameObject.prototype); + + Object.defineProperty(SpriteGameObject.prototype, "size", + { + get: function () { + return this.sprite.size; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "width", + { + get: function () { + return this.sprite.width; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "height", + { + get: function () { + return this.sprite.height; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "center", + { + get: function () { + return this.sprite.center; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "sheetIndex", + { + get: function () { + return this._sheetIndex; + }, + set: function (value) { + if (value >= 0) + this._sheetIndex = value % this.sprite.nrSheetElements; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "screenCenterX", + { + get: function () { + return (powerupjs.Game.size.x - this.width) / 2 + this.origin.x; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "screenCenterY", + { + get: function () { + return (powerupjs.Game.size.y - this.height) / 2 + this.origin.y; + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "screenCenter", + { + get: function () { + return powerupjs.Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + + Object.defineProperty(SpriteGameObject.prototype, "boundingBox", + { + get: function () { + var leftTop = this.worldPosition.subtractFrom((this.origin)); + return new powerupjs.Rectangle(leftTop.x, leftTop.y, this.width, this.height); + } + }); + + SpriteGameObject.prototype.draw = function () { + if (this._visible) + this.sprite.draw(this.worldPosition, this.origin, this._sheetIndex, this.mirror); + }; + + SpriteGameObject.prototype.collidesWith = function (obj) { + if (!this.visible || !obj.visible || !this.boundingBox.intersects(obj.boundingBox)) + return false; + var intersect = this.boundingBox.intersection(obj.boundingBox); + var local = intersect.position.subtractFrom(this.worldPosition.subtractFrom(this.origin)); + var objLocal = intersect.position.subtractFrom(obj.worldPosition.subtractFrom(obj.origin)); + for (var x = 0; x < intersect.width; x++) + for (var y = 0; y < intersect.height; y++) { + if (this.getAlpha(Math.floor(local.x + x), Math.floor(local.y + y)) !== 0 + && obj.getAlpha(Math.floor(objLocal.x + x), Math.floor(objLocal.y + y)) !== 0) + return true; + } + return false; + }; + + SpriteGameObject.prototype.getAlpha = function (x, y) { + return this.sprite.getAlpha(x, y, this._sheetIndex, this.mirror); + }; + + powerupjs.SpriteGameObject = SpriteGameObject; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/geom/Rectangle.js b/powerupjs/geom/Rectangle.js new file mode 100644 index 0000000..c37965f --- /dev/null +++ b/powerupjs/geom/Rectangle.js @@ -0,0 +1,102 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Rectangle(x, y, w, h) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + this.width = typeof w !== 'undefined' ? w : 1; + this.height = typeof h !== 'undefined' ? h : 1; + } + + Object.defineProperty(Rectangle.prototype, "left", + { + get: function () { + return this.x; + } + }); + + Object.defineProperty(Rectangle.prototype, "right", + { + get: function () { + return this.x + this.width; + } + }); + + Object.defineProperty(Rectangle.prototype, "top", + { + get: function () { + return this.y; + } + }); + + Object.defineProperty(Rectangle.prototype, "bottom", + { + get: function () { + return this.y + this.height; + } + }); + + Object.defineProperty(Rectangle.prototype, "center", + { + get: function () { + return this.position.addTo(this.size.divideBy(2)); + } + }); + + Object.defineProperty(Rectangle.prototype, "position", + { + get: function () { + return new powerupjs.Vector2(this.x, this.y); + } + }); + + Object.defineProperty(Rectangle.prototype, "size", + { + get: function () { + return new powerupjs.Vector2(this.width, this.height); + } + }); + + Rectangle.prototype.contains = function (v) { + v = typeof v !== 'undefined' ? v : new powerupjs.Vector2(); + return (v.x >= this.left && v.x <= this.right && + v.y >= this.top && v.y <= this.bottom); + }; + + Rectangle.prototype.intersects = function (rect) { + return (this.left <= rect.right && this.right >= rect.left && + this.top <= rect.bottom && this.bottom >= rect.top); + }; + + Rectangle.prototype.calculateIntersectionDepth = function (rect) { + var minDistance = this.size.addTo(rect.size).divideBy(2); + var distance = this.center.subtractFrom(rect.center); + var depth = powerupjs.Vector2.zero; + if (distance.x > 0) + depth.x = minDistance.x - distance.x; + else + depth.x = -minDistance.x - distance.x; + if (distance.y > 0) + depth.y = minDistance.y - distance.y; + else + depth.y = -minDistance.y - distance.y; + return depth; + }; + + Rectangle.prototype.intersection = function (rect) { + var xmin = Math.max(this.left, rect.left); + var xmax = Math.min(this.right, rect.right); + var ymin = Math.max(this.top, rect.top); + var ymax = Math.min(this.bottom, rect.bottom); + return new powerupjs.Rectangle(xmin, ymin, xmax - xmin, ymax - ymin); + }; + + Rectangle.prototype.draw = function () { + powerupjs.Canvas2D.drawRectangle(this.x, this.y, this.width, this.height); + }; + + powerupjs.Rectangle = Rectangle; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/geom/Vector2.js b/powerupjs/geom/Vector2.js new file mode 100644 index 0000000..727a7ce --- /dev/null +++ b/powerupjs/geom/Vector2.js @@ -0,0 +1,121 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Vector2(x, y) { + this.x = typeof x !== 'undefined' ? x : 0; + this.y = typeof y !== 'undefined' ? y : 0; + } + + Object.defineProperty(Vector2, "zero", + { + get: function () { + return new powerupjs.Vector2(); + } + }); + + Object.defineProperty(Vector2.prototype, "isZero", + { + get: function () { + return this.x === 0 && this.y === 0; + } + }); + + Object.defineProperty(Vector2.prototype, "length", + { + get: function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + }); + + Vector2.prototype.addTo = function (v) { + if (v.constructor == Vector2) { + this.x += v.x; + this.y += v.y; + } + else if (v.constructor == Number) { + this.x += v; + this.y += v; + } + return this; + }; + + Vector2.prototype.add = function (v) { + var result = this.copy(); + return result.addTo(v); + }; + + Vector2.prototype.subtractFrom = function (v) { + if (v.constructor == Vector2) { + this.x -= v.x; + this.y -= v.y; + } + else if (v.constructor == Number) { + this.x -= v; + this.y -= v; + } + return this; + }; + + Vector2.prototype.subtract = function (v) { + var result = this.copy(); + return result.subtractFrom(v); + }; + + Vector2.prototype.divideBy = function (v) { + if (v.constructor == Vector2) { + this.x /= v.x; + this.y /= v.y; + } + else if (v.constructor == Number) { + this.x /= v; + this.y /= v; + } + return this; + }; + + Vector2.prototype.divide = function (v) { + var result = this.copy(); + return result.divideBy(v); + }; + + Vector2.prototype.multiplyWith = function (v) { + if (v.constructor == Vector2) { + this.x *= v.x; + this.y *= v.y; + } + else if (v.constructor == Number) { + this.x *= v; + this.y *= v; + } + return this; + }; + + Vector2.prototype.multiply = function (v) { + var result = this.copy(); + return result.multiplyWith(v); + }; + + Vector2.prototype.toString = function () { + return "(" + this.x + ", " + this.y + ")"; + }; + + Vector2.prototype.normalize = function () { + var length = this.length; + if (length === 0) + return; + this.divideBy(length); + }; + + Vector2.prototype.copy = function () { + return new powerupjs.Vector2(this.x, this.y); + }; + + Vector2.prototype.equals = function (obj) { + return this.x === obj.x && this.y === obj.y; + }; + + powerupjs.Vector2 = Vector2; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/gui/Button.js b/powerupjs/gui/Button.js new file mode 100644 index 0000000..1c5725e --- /dev/null +++ b/powerupjs/gui/Button.js @@ -0,0 +1,25 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function Button(sprite, layer, id) { + powerupjs.SpriteGameObject.call(this, sprite, layer, id); + + this.pressed = false; + this.down = false; + } + + Button.prototype = Object.create(powerupjs.SpriteGameObject.prototype); + + Button.prototype.handleInput = function (delta) { + var boundingBox = this.boundingBox; + this.pressed = this.visible && (powerupjs.Touch.containsTouchPress(boundingBox) || + powerupjs.Mouse.containsMousePress(boundingBox)); + this.down = this.visible && (powerupjs.Touch.containsTouch(boundingBox) || + powerupjs.Mouse.containsMouseDown(boundingBox)); + }; + + powerupjs.Button = Button; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/gui/Label.js b/powerupjs/gui/Label.js new file mode 100644 index 0000000..1400dbe --- /dev/null +++ b/powerupjs/gui/Label.js @@ -0,0 +1,99 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function calculateTextSize(fontname, fontsize, text) { + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = -1000; + div.style.top = -1000; + document.body.appendChild(div); + text = typeof text !== 'undefined' ? text : "M"; + div.style.fontSize = "" + fontsize; + div.style.fontFamily = fontname; + div.innerHTML = text; + var size = new powerupjs.Vector2(div.offsetWidth, div.offsetHeight); + document.body.removeChild(div); + return size; + } + + function Label(fontname, fontsize, layer, id) { + powerupjs.GameObject.call(this, layer, id); + + this.color = powerupjs.Color.black; + this.origin = powerupjs.Vector2.zero; + this._fontname = typeof fontname !== 'undefined' ? fontname : "Courier New"; + this._fontsize = typeof fontsize !== 'undefined' ? fontsize : "20px"; + this._contents = ""; + this._align = "left"; + this._size = powerupjs.Vector2.zero; + } + + Label.prototype = Object.create(powerupjs.GameObject.prototype); + + Object.defineProperty(Label.prototype, "size", + { + get: function () { + return this._size; + } + }); + + Object.defineProperty(Label.prototype, "width", + { + get: function () { + return this._size.x; + } + }); + + Object.defineProperty(Label.prototype, "height", + { + get: function () { + return this._size.y; + } + }); + + Object.defineProperty(Label.prototype, "screenCenterX", + { + get: function () { + return (powerupjs.Game.size.x - this.width) / 2 + this.origin.x; + } + }); + + Object.defineProperty(Label.prototype, "screenCenterY", + { + get: function () { + return (powerupjs.Game.size.y - this.height) / 2 + this.origin.y; + } + }); + + Object.defineProperty(Label.prototype, "screenCenter", + { + get: function () { + return powerupjs.Game.size.subtract(this.size).divideBy(2).addTo(this.origin); + } + }); + + Object.defineProperty(Label.prototype, "text", + { + get: function () { + return this._contents; + }, + + set: function (value) { + this._contents = value; + this._size = calculateTextSize(this._fontname, this._fontsize, value); + } + }); + + Label.prototype.draw = function () { + if (!this.visible) + return; + powerupjs.Canvas2D.drawText(this._contents, this.worldPosition, + this.origin, this.color, this._align, + this._fontname, this._fontsize); + }; + + powerupjs.Label = Label; + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/input/ButtonState.js b/powerupjs/input/ButtonState.js new file mode 100644 index 0000000..ffa97da --- /dev/null +++ b/powerupjs/input/ButtonState.js @@ -0,0 +1,12 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + function ButtonState() { + this.down = false; + this.pressed = false; + } + + powerupjs.ButtonState = ButtonState; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/input/Keyboard.js b/powerupjs/input/Keyboard.js new file mode 100644 index 0000000..1fcbf4f --- /dev/null +++ b/powerupjs/input/Keyboard.js @@ -0,0 +1,45 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function handleKeyDown(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + if (!powerupjs.Keyboard._keyStates[code].down) + powerupjs.Keyboard._keyStates[code].pressed = true; + powerupjs.Keyboard._keyStates[code].down = true; + } + + function handleKeyUp(evt) { + var code = evt.keyCode; + if (code < 0 || code > 255) + return; + powerupjs.Keyboard._keyStates[code].down = false; + } + + function Keyboard_Singleton() { + this._keyStates = []; + for (var i = 0; i < 256; ++i) + this._keyStates.push(new powerupjs.ButtonState()); + document.onkeydown = handleKeyDown; + document.onkeyup = handleKeyUp; + } + + Keyboard_Singleton.prototype.reset = function () { + for (var i = 0; i < 256; ++i) + this._keyStates[i].pressed = false; + }; + + Keyboard_Singleton.prototype.pressed = function (key) { + return this._keyStates[key].pressed; + }; + + Keyboard_Singleton.prototype.down = function (key) { + return this._keyStates[key].down; + }; + + powerupjs.Keyboard = new Keyboard_Singleton(); + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/input/Mouse.js b/powerupjs/input/Mouse.js new file mode 100644 index 0000000..26a83eb --- /dev/null +++ b/powerupjs/input/Mouse.js @@ -0,0 +1,97 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function handleMouseMove(evt) { + var canvasScale = powerupjs.Canvas2D.scale; + var canvasOffset = powerupjs.Canvas2D.offset; + var mx = (evt.pageX - canvasOffset.x) / canvasScale.x; + var my = (evt.pageY - canvasOffset.y) / canvasScale.y; + powerupjs.Mouse._position = new powerupjs.Vector2(mx, my); + } + + function handleMouseDown(evt) { + handleMouseMove(evt); + + if (evt.which === 1) { + if (!powerupjs.Mouse._left.down) + powerupjs.Mouse._left.pressed = true; + powerupjs.Mouse._left.down = true; + } else if (evt.which === 2) { + if (!powerupjs.Mouse._middle.down) + powerupjs.Mouse._middle.pressed = true; + powerupjs.Mouse._middle.down = true; + } else if (evt.which === 3) { + if (!powerupjs.Mouse._right.down) + powerupjs.Mouse._right.pressed = true; + powerupjs.Mouse._right.down = true; + } + } + + function handleMouseUp(evt) { + handleMouseMove(evt); + + if (evt.which === 1) + powerupjs.Mouse._left.down = false; + else if (evt.which === 2) + powerupjs.Mouse._middle.down = false; + else if (evt.which === 3) + powerupjs.Mouse._right.down = false; + } + + function Mouse_Singleton() { + this._position = powerupjs.Vector2.zero; + this._left = new powerupjs.ButtonState(); + this._middle = new powerupjs.ButtonState(); + this._right = new powerupjs.ButtonState(); + document.onmousemove = handleMouseMove; + document.onmousedown = handleMouseDown; + document.onmouseup = handleMouseUp; + } + + Object.defineProperty(Mouse_Singleton.prototype, "left", + { + get: function () { + return this._left; + } + }); + + Object.defineProperty(Mouse_Singleton.prototype, "middle", + { + get: function () { + return this._middle; + } + }); + + Object.defineProperty(Mouse_Singleton.prototype, "right", + { + get: function () { + return this._right; + } + }); + + Object.defineProperty(Mouse_Singleton.prototype, "position", + { + get: function () { + return this._position; + } + }); + + Mouse_Singleton.prototype.reset = function () { + this._left.pressed = false; + this._middle.pressed = false; + this._right.pressed = false; + }; + + Mouse_Singleton.prototype.containsMouseDown = function (rect) { + return this._left.down && rect.contains(this._position); + }; + + Mouse_Singleton.prototype.containsMousePress = function (rect) { + return this._left.pressed && rect.contains(this._position); + }; + + powerupjs.Mouse = new Mouse_Singleton(); + return powerupjs; + +})(powerupjs || {}); diff --git a/powerupjs/input/Touch.js b/powerupjs/input/Touch.js new file mode 100644 index 0000000..9ed8c8c --- /dev/null +++ b/powerupjs/input/Touch.js @@ -0,0 +1,113 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + function handleTouchStart(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + powerupjs.Touch._touches.push(touches[i]); + powerupjs.Touch._touchPresses.push(true); + } + } + + function handleTouchMove(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; i++) { + var id = powerupjs.Touch.getTouchIndexFromId(touches[i].identifier); + powerupjs.Touch._touches.splice(id, 1, touches[i]); + } + } + + function handleTouchEnd(evt) { + evt.preventDefault(); + var touches = evt.changedTouches; + for (var i = 0; i < touches.length; ++i) { + var id = powerupjs.Touch.getTouchIndexFromId(touches[i].identifier); + powerupjs.Touch._touches.splice(id, 1); + powerupjs.Touch._touchPresses.splice(id, 1); + } + } + + function Touch_Singleton() { + this._touches = []; + this._touchPresses = []; + document.addEventListener('touchstart', handleTouchStart, false); + document.addEventListener('touchend', handleTouchEnd, false); + document.addEventListener('touchcancel', handleTouchEnd, false); + document.addEventListener('touchleave', handleTouchEnd, false); + document.body.addEventListener('touchmove', handleTouchMove, false); + } + + Object.defineProperty(Touch_Singleton.prototype, "nrTouches", + { + get: function () { + return this._touches.length; + } + }); + + Object.defineProperty(Touch_Singleton.prototype, "isTouching", + { + get: function () { + return this._touches.length !== 0; + } + }); + + Object.defineProperty(Touch_Singleton.prototype, "isTouchDevice", + { + get: function () { + return ('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0); + } + }); + + Touch_Singleton.prototype.getTouchIndexFromId = function (id) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (this._touches[i].identifier === id) + return i; + } + return -1; + }; + + Touch_Singleton.prototype.reset = function () { + for (var i = 0, l = this._touchPresses.length; i < l; ++i) + this._touchPresses[i] = false; + }; + + Touch_Singleton.prototype.getPosition = function (index) { + var canvasScale = powerupjs.Canvas2D.scale; + var canvasOffset = powerupjs.Canvas2D.offset; + var mx = (this._touches[index].pageX - canvasOffset.x) / canvasScale.x; + var my = (this._touches[index].pageY - canvasOffset.y) / canvasScale.y; + return new powerupjs.Vector2(mx, my); + }; + + Touch_Singleton.prototype.getIndexInRect = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + var pos = this.getPosition(i); + if (rect.contains(pos)) + return i; + } + return powerupjs.Vector2.zero; + }; + + Touch_Singleton.prototype.containsTouch = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i))) + return true; + } + return false; + }; + + Touch_Singleton.prototype.containsTouchPress = function (rect) { + for (var i = 0, l = this._touches.length; i < l; ++i) { + if (rect.contains(this.getPosition(i)) && this._touchPresses[i]) + return true; + } + return false; + }; + + powerupjs.Touch = new Touch_Singleton(); + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/system/Color.js b/powerupjs/system/Color.js new file mode 100644 index 0000000..4bbfbfd --- /dev/null +++ b/powerupjs/system/Color.js @@ -0,0 +1,151 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + var Color = { + aliceBlue: "#F0F8FF", + antiqueWhite: "#FAEBD7", + aqua: "#00FFFF", + aquamarine: "#7FFFD4", + azure: "#F0FFFF", + beige: "#F5F5DC", + bisque: "#FFE4C4", + black: "#000000", + blanchedAlmond: "#FFEBCD", + blue: "#0000FF", + blueViolet: "#8A2BE2", + brown: "#A52A2A", + burlyWood: "#DEB887", + cadetBlue: "#5F9EA0", + chartreuse: "#7FFF00", + chocolate: "#D2691E", + coral: "#FF7F50", + cornflowerBlue: "#6495ED", + cornsilk: "#FFF8DC", + crimson: "#DC143C", + cyan: "#00FFFF", + darkBlue: "#00008B", + darkCyan: "#008B8B", + darkGoldenrod: "#B8860B", + darkGray: "#A9A9A9", + darkGreen: "#006400", + darkKhaki: "#BDB76B", + darkMagenta: "#8B008B", + darkOliveGreen: "#556B2F", + darkOrange: "#FF8C00", + darkOrchid: "#9932CC", + darkRed: "#8B0000", + darkSalmon: "#E9967A", + darkSeaGreen: "#8FBC8B", + darkSlateBlue: "#483D8B", + darkSlateGray: "#2F4F4F", + darkTurquoise: "#00CED1", + darkViolet: "#9400D3", + deepPink: "#FF1493", + deepSkyBlue: "#00BFFF", + dimGray: "#696969", + dodgerBlue: "#1E90FF", + firebrick: "#B22222", + floralWhite: "#FFFAF0", + forestGreen: "#228B22", + fuchsia: "#FF00FF", + gainsboro: "#DCDCDC", + ghostWhite: "#F8F8FF", + gold: "#FFD700", + goldenrod: "#DAA520", + gray: "#808080", + green: "#008000", + greenYellow: "#ADFF2F", + honeydew: "#F0FFF0", + hotPink: "#FF69B4", + indianRed: "#CD5C5C", + indigo: "#4B0082", + ivory: "#FFFFF0", + khaki: "#F0E68C", + lavender: "#E6E6FA", + lavenderBlush: "#FFF0F5", + lawnGreen: "#7CFC00", + lemonChiffon: "#FFFACD", + lightBlue: "#ADD8E6", + lightCoral: "#F080FF", + lightCyan: "#E0FFFF", + lightGoldenrodYellow: "#FAFAD2", + lightGray: "#D3D3D3", + lightGreen: "#90EE90", + lightPink: "#FFB6C1", + lightSalmon: "#FFA07A", + lightSeaGreen: "#20B2AA", + lightSkyBlue: "#87CEFA", + lightSlateGray: "#778899", + lightSteelBlue: "#B0C4DE", + lightYellow: "#FFFFE0", + lime: "#00FF00", + limeGreen: "#32CD32", + linen: "#FAF0E6", + magenta: "#FF00FF", + maroon: "#800000", + mediumAquamarine: "#66CDAA", + mediumBlue: "#0000CD", + mediumOrchid: "#BA55D3", + mediumPurple: "#9370DB", + mediumSeaGreen: "#3CB371", + mediumSlateBlue: "#7B68EE", + mediumSpringGreen: "#00FA9A", + mediumTurquoise: "#48D1CC", + mediumVioletRed: "#C71585", + midnightBlue: "#191970", + mintCream: "#F5FFFA", + mistyRose: "#FFE4E1", + moccasin: "#FFE4B5", + navajoWhite: "#FFDEAD", + navy: "#000080", + oldLace: "#FDF5E6", + olive: "#808000", + oliveDrab: "#6B8E23", + orange: "#FFA500", + orangeRed: "#FF4500", + orchid: "#DA70D6", + paleGoldenrod: "#EEE8AA", + paleGreen: "#98FB98", + paleTurquoise: "#AFEEEE", + paleVioletRed: "#DB7093", + papayaWhip: "#FFEFD5", + peachPuff: "#FFDAB9", + peru: "#CD853F", + pink: "#FFC0CB", + plum: "#DDA0DD", + powderBlue: "#B0E0E6", + purple: "#800080", + red: "#FF0000", + rosyBrown: "#BC8F8F", + royalBlue: "#4169E1", + saddleBrown: "#8B4513", + salmon: "#FA8072", + sandyBrown: "#F4A460", + seaGreen: "#2E8B57", + seaShell: "#FFF5EE", + sienna: "#A0522D", + silver: "#C0C0C0", + skyBlue: "#87CEEB", + slateBlue: "#6A5ACD", + slateGray: "#708090", + snow: "#FFFAFA", + springGreen: "#00FF7F", + steelBlue: "#4682B4", + tan: "#D2B48C", + teal: "#008080", + thistle: "#D8BFD8", + tomato: "#FF6347", + turquoise: "#40E0D0", + violet: "#EE82EE", + wheat: "#F5DEB3", + white: "#FFFFFF", + whiteSmoke: "#F5F5F5", + yellow: "#FFFF00", + yellowGreen: "#9ACD32" + }; + + powerupjs.Color = Color; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file diff --git a/powerupjs/system/Keys.js b/powerupjs/system/Keys.js new file mode 100644 index 0000000..488d7c0 --- /dev/null +++ b/powerupjs/system/Keys.js @@ -0,0 +1,54 @@ +"use strict"; + +var powerupjs = (function (powerupjs) { + + var Keys = { + none: 0, + back: 8, + tab: 9, + enter: 13, + pause: 19, + escape: 27, + + space: 32, + + pageUp: 33, + pageDown: 34, + end: 35, + home: 36, + left: 37, + up: 38, + right: 39, + down: 40, + + insert: 45, + del: 46, + + d0: 48, + d1: 49, + d2: 50, + d3: 51, + d4: 52, + d5: 53, + d6: 54, + d7: 55, + d8: 56, + d9: 57, + + A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, + G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, + M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, + S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, + Y: 89, Z: 90, + + multiply: 42, + add: 43, + subtract: 45, + decimal: 46, + divide: 47 + }; + + powerupjs.Keys = Keys; + return powerupjs; + +})(powerupjs || {}); \ No newline at end of file