diff --git a/Games/CopyCat/README.md b/Games/CopyCat/README.md new file mode 100644 index 0000000000..9bf23db0a0 --- /dev/null +++ b/Games/CopyCat/README.md @@ -0,0 +1,32 @@ +

CopyCat

+ +## **Description 📃* +

The game contains two cats and two homes separated by one division of water you have to make use of arrows such that they reach their homes and if you fall in water the game restrats from that level . Once you win the level you get entered into another level.

+ +## **How to play? 🕹️** +
  • Once you enter to the starting page three options comes play , select and control choose one option using arrows and then press the key 'x' .
  • +
  • Press 'x' to select any option
  • +
  • to start the game choose arrows and press 'x' on the keyboard to play option , once the game has started make the use of arrows to move the cats to their respective home
  • +
  • The home of the cat represented by animated circle
  • +
  • Press 'e' to exit the game
  • +
  • Press 'r' to restart the game
  • + + +# Screenshots - +

    Beginning of the Game


    +
  • Select play option using arrows and Press 'x' from keyboard to start the game.
  • +Screenshot 2024-06-12 at 10 39 44 PM +
  • Level 1
  • +Screenshot 2024-06-12 at 10 40 10 PM +
  • Level 2
  • +Screenshot 2024-06-12 at 10 43 33 PM +
  • Level 3
  • +Screenshot 2024-06-12 at 10 43 56 PM +
  • Level 4
  • +Screenshot 2024-06-12 at 10 44 12 PM +
  • Level 5
  • +Screenshot 2024-06-12 at 10 45 17 PM +
  • Level 6
  • +Screenshot 2024-06-12 at 10 45 35 PM +

    +

    END


    \ No newline at end of file diff --git a/Games/CopyCat/index.html b/Games/CopyCat/index.html new file mode 100644 index 0000000000..81bc7d9968 --- /dev/null +++ b/Games/CopyCat/index.html @@ -0,0 +1,12 @@ + + + + + + CopyCat + + + + + + \ No newline at end of file diff --git a/Games/CopyCat/script.js b/Games/CopyCat/script.js new file mode 100644 index 0000000000..9f3d97131b --- /dev/null +++ b/Games/CopyCat/script.js @@ -0,0 +1,2317 @@ +// ---------- +// Utility +// ---------- +Util = {}; +Util.timeStamp = function() { + return window.performance.now(); +}; +Util.random = function(min, max) { + return min + Math.random() * (max - min); +}; +Util.array2D = function(tableau, largeur) { + var result = []; + for (var i = 0; i < tableau.length; i += largeur) + result.push(tableau.slice(i, i + largeur)); + return result; +}; +Util.toDio = function(array) { + let tab = array.map(x => { + if (x !== 0) { + return x - 1; + } else { + return x; + } + }); + let render = Util.array2D(tab, 16); + return JSON.stringify(render); +}; +Util.map = function(a, b, c, d, e) { + return (a - b) / (c - b) * (e - d) + d; +}; +Util.lerp = function(value1, value2, amount) { + return value1 + (value2 - value1) * amount; +}; +Util.linearTween = function(currentTime, start, degreeOfChange, duration) { + return degreeOfChange * currentTime / duration + start; +}; +Util.easeInOutQuad = function(t, b, c, d) { + t /= d / 2; + if (t < 1) return c / 2 * t * t + b; + t--; + return -c / 2 * (t * (t - 2) - 1) + b; +}; +Util.easeInOutExpo = function(t, b, c, d) { + t /= d / 2; + if (t < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; + t--; + return c / 2 * (-Math.pow(2, -10 * t) + 2) + b; +}; + +// ---------- +// Scene +// ---------- +class Scene { + constructor(name) { + this.name = name; + this.loop = true; + this.init_once = false; + } + giveWorld(world) { + this.world = world; + this.ctx = world.ctx; + } + keyEvents(event) {} + init() {} + render() {} + addEntity() {} +} +class Entity { + constructor(scene, x, y) { + this.scene = scene; + this.world = scene.world; + this.ctx = this.world.ctx; + this.body = new Body(this, x, y); + } + setSprite(sprite_data) { + this.sprite = new Sprite(this, sprite_data); + } + display() { + if (this.sprite === undefined) { + this.ctx.strokeStyle = "#000"; + this.ctx.strokeRect( + this.body.position.x, + this.body.position.y, + this.body.size.x, + this.body.size.y + ); + } else { + this.sprite.display(); + } + } + integration() { + this.body.integration(); + } +} + +// class for animated sprites ! +class Sprite { + constructor(entity, sprite_data) { + this.entity = entity; + this.world = this.entity.world; + this.tile_size = this.world.tile_size; + this.ctx = this.world.ctx; + // image data + this.image = this.world.assets.image[sprite_data.image].image; + // sprite + this.size = sprite_data.size; + this.current_frame = 0; + this.animations = {}; + this.current_animation = undefined; + this.width = this.image.width / this.size.x; + this.height = this.image.height / this.size.y; + // timer + this.tick = 0; + this.speed = 0.2; + // offset + this.offset = { + x: 0, + y: 0 + }; + } + addAnimation(name, frames) { + this.animations[name] = frames; + this.current_animation = name; + } + animate(animation_name) { + this.current_animation = animation_name; + if (this.tick < 1) { + this.tick += this.speed; + } else { + this.tick = 0; + if (this.current_frame < this.animations[animation_name].length - 1) { + this.current_frame += 1; + } else { + this.current_frame = 0; + } + } + } + display() { + this.ctx.drawImage( + this.image, + Math.floor( + this.animations[this.current_animation][this.current_frame] % this.width + ) * this.size.x, + Math.floor( + this.animations[this.current_animation][this.current_frame] / this.width + ) * this.size.y, + this.size.x, + this.size.y, + this.entity.body.position.x + + (this.tile_size / 2 - this.size.x / 2) + + this.offset.x, + this.entity.body.position.y + + (this.tile_size / 2 - this.size.x / 2) + + this.offset.y, + this.size.x, + this.size.y + ); + } +} + +class Body { + constructor(entity, x, y) { + this.world = entity.world; + this.step = this.world.FPS.step; + this.position = new Vector(x, y); + this.next_position = new Vector(x, y); + this.velocity = new Vector(0, 0); + this.stepped_velocity = new Vector(0, 0); + this.acceleration = new Vector(0, 0); + this.drag = 0.98; + this.size = { + x: 16, + y: 16 + }; + } + setSize(x, y) { + this.size.x = x; + this.size.y = y; + } + updateVelocity() { + this.velocity.add(this.acceleration); + this.velocity.mult(this.drag); + this.stepped_velocity = this.velocity.copy(); + this.stepped_velocity.mult(this.step); + this.next_position = this.position.copy(); + this.next_position.add(this.stepped_velocity); + // reset acceleration + this.acceleration.mult(0); + } + updatePosition() { + this.position.add(this.stepped_velocity); + } + integration() { + this.updateVelocity(); + this.updatePosition(); + } + applyForce(force_vector) { + this.acceleration.add(force_vector); + } +} + +class Vector { + constructor(x, y) { + this.x = x || 0; + this.y = y || 0; + } + set(x, y) { + this.x = x; + this.y = y; + } + add(vector) { + this.x += vector.x; + this.y += vector.y; + } + sub(vector) { + this.x -= vector.x; + this.y -= vector.y; + } + mult(scalar) { + this.x *= scalar; + this.y *= scalar; + } + div(scalar) { + this.x /= scalar; + this.y /= scalar; + } + limit(limit_value) { + if (this.mag() > limit_value) this.setMag(limit_value); + } + mag() { + return Math.hypot(this.x, this.y); + } + setMag(new_mag) { + if (this.mag() > 0) { + this.normalize(); + } else { + this.x = 1; + this.y = 0; + } + this.mult(new_mag); + } + dist(vector) { + return new Vector(this.x - vector.x, this.y - vector.y).mag(); + } + normalize() { + let mag = this.mag(); + if (mag > 0) { + this.x /= mag; + this.y /= mag; + } + } + heading() { + return Math.atan2(this.x, this.y); + } + setHeading(angle) { + let mag = this.mag(); + this.x = Math.cos(angle) * mag; + this.y = Math.sin(angle) * mag; + } + copy() { + return new Vector(this.x, this.y); + } +} + +class Box { + constructor(world, box_data) { + this.world = world; + this.ctx = world.ctx; + this.c_ctx = world.c_ctx; + this.box_data = box_data; + this.resolution = box_data.resolution; + this.image = world.assets.image[box_data.image].image; + } + display(x, y, width, height) { + // background + this.ctx.fillRect(x + 1, y + 1, width - 2, height - 2); + // corners + this.ctx.lineWidth = 2; + let coners = [0, 2, 6, 8]; + for (let i = 0; i < 4; i++) { + let pos_x = x + Math.floor(i % 2) * (width - this.resolution), + pos_y = y + Math.floor(i / 2) * (height - this.resolution); + let clip_x = Math.floor(i % 2) * (this.resolution * 2), + clip_y = Math.floor(i / 2) * (this.resolution * 2); + this.ctx.drawImage( + this.image, + clip_x, + clip_y, + this.resolution, + this.resolution, + pos_x, + pos_y, + this.resolution, + this.resolution + ); + } + let offset = this.resolution * 3; + // top + this.ctx.drawImage( + this.image, + 8, + 0, + this.resolution, + this.resolution, + x + 8, + y, + this.resolution + width - offset, + this.resolution + ); + // bottom + this.ctx.drawImage( + this.image, + 8, + 16, + this.resolution, + this.resolution, + x + 8, + y + height - this.resolution, + this.resolution + width - offset, + this.resolution + ); + // left + this.ctx.drawImage( + this.image, + 0, + 8, + this.resolution, + this.resolution, + x, + y + 8, + this.resolution, + this.resolution + height - offset + ); + // right + this.ctx.drawImage( + this.image, + 16, + 8, + this.resolution, + this.resolution, + x + width - this.resolution, + y + this.resolution, + this.resolution, + this.resolution + height - offset + ); + } +} +// ---------- +// 🕹️ Diorama.js +// ---------- +class Diorama { + constructor(parameters) { + this.parameters = parameters; + // Game and author's name + this.game_info = { + name: parameters.name || "Untitled", + author: parameters.author || "Anonymous" + }; + // canvas + this.background_color = parameters.background_color || "#000"; + this.initCanvas(parameters); + // Assets + this.counter = 0; + this.toLoad = parameters.assets.length; + this.assets = { + image: {}, + audio: {} + }; + this.audio_muted = false; + // keyboard event + this.keys = {}; + // Scenes + this.scenes = {}; + this.start_screen = parameters.start_screen || undefined; + this.current_scene = ""; + // Bitmap font Data + this.alphabet = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ?!:',.()<>[]"; + this.fonts = {}; + // Maps + this.tile_size = parameters.tile_size || 16; + this.tiles_data = {}; + if (parameters.tiles !== undefined) { + parameters.tiles.map(tile => { + this.tiles_data[tile.id] = tile; + }); + } + this.mapsMax = parameters.maps.length; + this.maps = {}; + if (parameters.maps !== undefined) { + parameters.maps.map(map => { + this.maps[map.name] = map; + }); + } + // Box system + this.boxes = {}; + // By default the current font is the first font you create + this.currentFont = undefined; + // Game loop Data + this.FPS = { + now: 0, + delta: 0, + last: Util.timeStamp(), + step: 1 / (parameters.frame_rate || 60) + }; + this.requestChange = { + value: false, + action: "" + }; + this.main_loop = undefined; + } + // --- + // Setup & Loading + // --- + ready() { + this.loadAssets(this.parameters.assets); + } + initCanvas(parameters) { + this.canvas = document.createElement("canvas"); + this.ctx = this.canvas.getContext("2d"); + this.W = this.canvas.width = parameters.width || 256; + this.H = this.canvas.height = parameters.height || 256; + this.scale = parameters.scale || 1; + this.full = false; + this.ctx.imageSmoothingEnabled = false; + this.canvas.classList.add("crisp"); + document.body.appendChild(this.canvas); + // cache canvas + this.cache = document.createElement("canvas"); + this.c_ctx = this.cache.getContext("2d"); + } + loader() { + // increment loader + this.clear("#222"); + this.counter += 1; + let padding = 20; + let width = this.W - padding * 2, + x = padding, + y = this.H - padding * 2; + this.ctx.fillStyle = "#111"; + this.ctx.fillRect(x, y, width, 20); + this.ctx.fillStyle = "#333"; + this.ctx.fillRect(x, y, this.counter * width / this.toLoad, 20); + this.ctx.strokeStyle = "#000"; + this.ctx.lineWidth = 4; + this.ctx.strokeRect(x, y, width, 20); + if (this.counter === this.toLoad) { + this.launch(); + } + } + loadAssets(assets) { + if (assets === undefined) console.log("Nothing to load"); + assets.map(obj => this.checkAssets(obj)); + } + checkAssets(obj) { + let subject = obj; + switch (obj.type) { + case "img": + let img = new Image(); + img.onload = () => { + this.loader(); + }; + img.onerror = () => { + console.log("can't load Image: " + obj.name); + }; + img.src = obj.path; + subject.image = img; + this.assets.image[obj.name] = subject; + break; + case "audio": + let audio = new Audio(obj.path); + audio.addEventListener("canplaythrough", this.loader()); + audio.onerror = () => { + console.log("can't load audio: " + obj.name); + }; + subject.audio = audio; + this.assets.audio[obj.name] = subject; + break; + case undefined: + console.log(obj.name, " doesn't have any type"); + break; + default: + console.log(obj.name, " has a none known type"); + } + } + launch() { + this.eventSetup(); + this.initBoxes(this.parameters.boxes); + this.initFonts(this.parameters.fonts); + this.startScene(this.start_screen); + } + initBoxes(boxes_data) { + if (boxes_data === undefined) return false; + boxes_data.map(box => { + this.boxes[box.name] = new Box(this, box); + }); + } + drawBox(box_name, x, y, width, height) { + this.boxes[box_name].display(x, y, width, height); + } + // --- + // Font manager + // --- + setFont(font_name) { + this.currentFont = font_name; + } + initFonts(fonts_data) { + if (fonts_data === undefined && fonts_data.length > 0) return false; + fonts_data.map(font => { + if (this.assets.image[font.image] === undefined) { + console.log("can't load font, " + font.image + " doesn't exist"); + return false; + } + font.image = this.assets.image[font.image].image; + this.fonts[font.name] = font; + }); + // set current font to the first font ! + this.currentFont = Object.keys(this.fonts)[0]; + } + write(text, x, y, justify, colorID) { + if (this.currentFont === undefined) { + console.log("No bitmap_font"); + return false; + } + if (typeof justify === "string") { + switch (justify) { + case "center": + x -= text.length * this.fonts[this.currentFont].size.x / 2; + break; + case "right": + x -= text.length * this.fonts[this.currentFont].size.x; + break; + default: + } + this.writeLine(text, x, y, colorID || 0); + } else { + this.writeParagraph(text, x, y, justify, colorID || 0); + } + } + writeParagraph(text, x, y, justify, colorID) { + let y_offset = 0, + line_height = this.fonts[this.currentFont].size.y + 5, + size_x = this.fonts[this.currentFont].size.x, + words = text.split(" "), + line = ""; + for (let i = 0; i < words.length; i++) { + line += words[i] + " "; + let nextword_width = 0, + next_word = words[i + 1], + line_length = line.length * size_x; + next_word ? (nextword_width = next_word.length * size_x) : 0; + if (line_length + nextword_width > justify) { + this.writeLine(line, x, y + y_offset, 0, colorID); + y_offset += line_height; + line = ""; + } else { + this.writeLine(line, x, y + y_offset, 0, colorID); + } + } + } + writeLine(text, x, y, colorID) { + // write line + let size_x = this.fonts[this.currentFont].size.x, + size_y = this.fonts[this.currentFont].size.y, + font_img = this.fonts[this.currentFont].image; + for (let i = 0; i < text.length; i++) { + let index = this.alphabet.indexOf(text.charAt(i)), + clipX = size_x * index, + posX = x + i * size_x; + this.ctx.drawImage( + font_img, + clipX, + colorID * size_y, + size_x, + size_y, + posX, + y, + size_x, + size_y + ); + } + } + // ----------------- + // Events + // ----------------- + eventSetup() { + document.addEventListener("keydown", event => this.keyDown(event), false); + document.addEventListener("keyup", event => this.keyUp(event), false); + } + keyDown(event) { + event.preventDefault(); + this.keys[event.code] = true; + if (this.keys.KeyF) { + this.fullScreen(); + } + if (this.keys.KeyM) { + this.mute(); + } + this.current_scene.keyEvents(event); + } + keyUp(event) { + event.preventDefault(); + this.keys[event.code] = false; + } + // --- + // Scene Manager + // --- + startScene(scene_name) { + // check if the scene exist + if (this.scenes[scene_name] === undefined) + return scene_name + " - doesn't exist"; + // request the change of scene if this.main_loop is active + if (this.main_loop !== undefined) { + this.requestChange.value = true; + this.requestChange.action = scene_name; + return false; + } + this.requestChange.value = false; + this.requestChange.action = ""; + this.FPS.last = Util.timeStamp(); + this.current_scene = this.scenes[scene_name]; + this.initScene(); + // does this scenes needs a gameloop ? + if (this.current_scene.loop === true) { + this.gameLoop(); + } else { + this.mainRender(); + } + } + initScene() { + if (this.current_scene.init_once) return false; + this.current_scene.init(); + } + addScene(scene) { + // links this world to this scene + scene.giveWorld(this); + this.scenes[scene.name] = scene; + } + // --- + // Main Loop + // --- + mainRender() { + this.clear(); + this.current_scene.render(); + } + loopCheck() { + if (this.requestChange.value === false) { + this.main_loop = requestAnimationFrame(() => this.gameLoop()); + } else { + cancelAnimationFrame(this.main_loop); + this.main_loop = undefined; + this.startScene(this.requestChange.action); + } + } + gameLoop() { + this.FPS.now = Util.timeStamp(); + this.FPS.delta += Math.min(1, (this.FPS.now - this.FPS.last) / 1000); + while (this.FPS.delta > this.FPS.step) { + this.FPS.delta -= this.FPS.step; + this.mainRender(); + } + this.FPS.last = this.FPS.now; + this.loopCheck(); + } + // Basic functions + soundLevel(volume) { + for (let [k, v] of Object.entries(this.assets.audio)) { + v.audio.volume = volume; + } + } + mute() { + this.audio_muted = !this.audio_muted; + for (let [k, v] of Object.entries(this.assets.audio)) { + v.audio.muted = this.audio_muted; + } + } + clear(custom_color) { + this.ctx.fillStyle = custom_color || this.background_color; + this.ctx.fillRect(0, 0, this.W, this.H); + } + setScale() { + this.canvas.style.width = this.W * this.scale + "px"; + this.canvas.style.height = this.H * this.scale + "px"; + } + fullScreen() { + this.full = !this.full; + if (!this.full) { + this.setScale(); + } else { + this.canvas.style.width = "100%"; + this.canvas.style.height = "100%"; + } + } + // --- + // Tile map + // --- + getTile(layer_id, x, y) { + if (x < 0 || x > this.terrain.layers[layer_id].size.x - 1) return false; + if (y < 0 || y > this.terrain.layers[layer_id].size.y - 1) return false; + let tile = this.tiles_data[this.terrain.layers[layer_id].geometry[y][x]]; + if (tile === undefined) return false; + return tile; + } + findTile(layer_id, tile_id) { + let layer = this.terrain.layers[layer_id]; + let result = []; + for (let y = 0; y < layer.size.y; y++) { + for (let x = 0; x < layer.size.x; x++) { + let id = layer.geometry[y][x]; + if (id === tile_id) { + result.push({ x: x, y: y }); + } + } + } + return result; + } + initMap(map_name) { + this.terrain = JSON.parse(JSON.stringify(this.maps[map_name])); + // give size to layers + for (var i = 0; i < this.terrain.layers.length; i++) { + this.terrain.layers[i].size = { + x: this.terrain.layers[i].geometry[0].length, + y: this.terrain.layers[i].geometry.length + }; + } + this.terrain.tileset = this.assets.image[this.maps[map_name].tileset].image; + this.terrain.tileset_size = { + width: this.terrain.tileset.width / this.tile_size, + height: this.terrain.tileset.height / this.tile_size + 1 + }; + this.terrain.layers.forEach((layer, index) => { + this.marchingSquare(layer); + this.bitMasking(layer); + + // create a cache for reducing draw call in the gameLoop + this.terrainCache(layer); + // prepare animated tiles + layer.animated = []; + for (var id in this.tiles_data) { + if (this.tiles_data[id].animated === true) { + let tiles = this.findTile(index, parseInt(id)); + layer.animated.push({ + id: id, + spritesheet: this.assets.image[this.tiles_data[id].spritesheet] + .image, + positions: tiles, + current: 0, + steps: this.tiles_data[id].steps, + max_frame: + this.assets.image[this.tiles_data[id].spritesheet].image.width / + this.tile_size + }); + } + } + }); + this.clear("black"); + } + terrainCache(layer) { + layer.cache = {}; + let c = (layer.cache.c = document.createElement("canvas")); + let ctx = (layer.cache.ctx = layer.cache.c.getContext("2d")); + let W = (c.width = layer.size.x * this.tile_size), + H = (c.height = layer.size.y * this.tile_size); + // Draw on cache + this.ctx.clearRect(0, 0, W, H); + this.drawLayer(layer); + ctx.drawImage(this.canvas, 0, 0); + this.clear(); + } + marchingSquare(layer) { + layer.square = []; + for (let y = 0; y < layer.size.y; y++) { + for (let x = 0; x < layer.size.x; x++) { + let p1 = 0, + p2 = 0, + p3 = 0, + p4 = 0; + + if (y + 1 < layer.size.y && x + 1 < layer.size.x) { + p1 = layer.geometry[y][x]; + p2 = layer.geometry[y][x + 1]; + p3 = layer.geometry[y + 1][x + 1]; + p4 = layer.geometry[y + 1][x]; + } + let id = p1 * 8 + p2 * 4 + p3 * 2 + p4; + layer.square.push(id); + } + } + + layer.square = Util.array2D(layer.square, layer.size.x); + } + bitMasking(layer) { + layer.bitMask = []; + for (let y = 0; y < layer.size.y; y++) { + for (let x = 0; x < layer.size.x; x++) { + let id = layer.geometry[y][x]; + let neighbor = [0, 0, 0, 0]; + if (y - 1 > -1) { + if (id === layer.geometry[y - 1][x]) { + //top + neighbor[0] = 1; + } + } else { + neighbor[0] = 1; + } + if (x - 1 > -1) { + if (id === layer.geometry[y][x - 1]) { + // left + neighbor[1] = 1; + } + } else { + neighbor[1] = 1; + } + if (x + 1 < layer.size.x) { + if (id === layer.geometry[y][x + 1]) { + // right + neighbor[2] = 1; + } + } else { + neighbor[2] = 1; + } + + if (y + 1 < layer.size.y) { + if (id === layer.geometry[y + 1][x]) { + //down + neighbor[3] = 1; + } + } else { + neighbor[3] = 1; + } + id = + 1 * neighbor[0] + 2 * neighbor[1] + 4 * neighbor[2] + 8 * neighbor[3]; + layer.bitMask.push(id); + } + } + layer.bitMask = Util.array2D(layer.bitMask, layer.size.x); + } + renderMap() { + this.terrain.layers.forEach(layer => { + this.ctx.drawImage(layer.cache.c, 0, 0); + // draw animated layer + layer.animated.forEach(tile => { + if (tile.current < tile.max_frame - 1) { + tile.current += tile.steps; + } else { + tile.current = 0; + } + // render animated tiles + tile.positions.forEach(position => { + let x = position.x * this.tile_size, + y = position.y * this.tile_size; + this.ctx.drawImage( + tile.spritesheet, + Math.floor(tile.current) * this.tile_size, + 0, + this.tile_size, + this.tile_size, + x, + y, + this.tile_size, + this.tile_size + ); + }); + }); + }); + } + drawMap() { + this.terrain.layers.forEach(layer => { + this.drawLayer(layer); + }); + } + drawLayer(layer) { + for (let y = 0; y < layer.size.y; y++) { + for (let x = 0; x < layer.size.x; x++) { + // ID of the tile + let id = layer.geometry[y][x]; + // Don't draw invisible tiles + // Position of the tile :) + let positionX = x * this.tile_size + layer.offset.x, + positionY = y * this.tile_size + layer.offset.y; + let sourceX = + Math.floor(id % this.terrain.tileset_size.width) * this.tile_size, + sourceY = + Math.floor(id / this.terrain.tileset_size.width) * this.tile_size; + if (this.tiles_data[id] && this.tiles_data[id].look === "bitmask") { + sourceX = Math.floor(layer.bitMask[y][x]) * this.tile_size; + sourceY = this.tiles_data[id].line * this.tile_size; + } + + if (layer.look === "square") { + if (layer.square[y][x] === 0) continue; + positionX += this.tile_size / 2; + positionY += this.tile_size / 2; + sourceX = Math.floor(layer.square[y][x] % 16) * 16; + sourceY = 7 * this.tile_size; + } + + if (this.tiles_data[id] && this.tiles_data[id].animated === true) { + // hide animated sprites on the cache + continue; + } + + // render tile + + this.ctx.drawImage( + this.terrain.tileset, + sourceX, + sourceY, + this.tile_size, + this.tile_size, + positionX, + positionY, + this.tile_size, + this.tile_size + ); + } + } + } +} +let parameters = { + name: "Copycat", + start_screen: "menu", + background_color: "#223d8c", + width: 256, + height: 256, + tile_size: 16, + assets: [ + // Images + { + type: "img", + name: "coderscrux_font", + path: "https://image.ibb.co/fCOd7T/coderscrux_font.png" + }, + { + type: "img", + name: "controls", + path: "https://image.ibb.co/nApwu8/controls.png" + }, + { + type: "img", + name: "player_sprite", + path: "https://image.ibb.co/co3NZ8/player.png" + }, + { + type: "img", + name: "spawn_effect", + path: "https://image.ibb.co/njVQnT/spawn_effect.png" + }, + { + type: "img", + name: "water_splash", + path: "https://image.ibb.co/jm7hZ8/water_splash.png" + }, + { + type: "img", + name: "shadow", + path: "https://image.ibb.co/djchZ8/shadow.png" + }, + { + type: "img", + name: "main_title", + path: "https://image.ibb.co/mrBLMo/main_title.png" + }, + { + type: "img", + name: "origami_dark", + path: "https://image.ibb.co/gzk2Z8/origami_dark.png" + }, + { + type: "img", + name: "origami_light", + path: "https://image.ibb.co/jruknT/origami_light.png" + }, + { + type: "img", + name: "box_texture", + path: "https://image.ibb.co/kpO0Mo/box.png" + }, + { + type: "img", + name: "selection", + path: "https://image.ibb.co/fmJpE8/selection.png" + }, + { + type: "img", + name: "flat_frame", + path: "https://image.ibb.co/hqSugo/flat_frame.png" + }, + { + type: "img", + name: "pattern", + path: "https://image.ibb.co/cv02Z8/pattern.png" + }, + { + type: "img", + name: "cursor", + path: "https://image.ibb.co/bFiNZ8/cursor.png" + }, + { + type: "img", + name: "demo_tileset", + path: "https://image.ibb.co/b8rLMo/demo_tileset.png" + }, + { + type: "img", + name: "exit", + path: "https://image.ibb.co/esCS1o/exit.png" + }, + { + type: "img", + name: "water_sprite", + path: "https://image.ibb.co/cSFEgo/water_sprite.png" + }, + { + type: "img", + name: "dust_effect", + path: "https://image.ibb.co/mKy0Mo/dust.png" + }, + // Audio + { + type: "audio", + name: "jingle", + path: "http://www.noiseforfun.com/waves/musical-and-jingles/NFF-bravo.wav" + }, + { + type: "audio", + name: "mouvement", + path: + "http://www.noiseforfun.com/waves/interface-and-media/NFF-select-04.wav" + }, + { + type: "audio", + name: "selection", + path: + "http://www.noiseforfun.com/waves/interface-and-media/NFF-select.wav" + }, + { + type: "audio", + name: "apparition", + path: + "http://www.noiseforfun.com/waves/interface-and-media/NFF-bubble-input.wav" + }, + { + type: "audio", + name: "eboulement", + path: + "http://www.noiseforfun.com/waves/action-and-game/NFF-moving-block.wav" + }, + { + type: "audio", + name: "splash", + path: + "http://www.noiseforfun.com/waves/action-and-game/NFF-mud-splash.wav" + }, + { + type: "audio", + name: "bump", + path: "http://www.noiseforfun.com/waves/action-and-game/NFF-bump.wav" + } + + // Bitmap font + ], + fonts: [ + // basic font + { + name: "coderscrux", + image: "coderscrux_font", + size: { x: 6, y: 9 } + }, + { + name: "origami_dark", + image: "origami_dark", + size: { x: 8, y: 9 } + }, + { + name: "origami_light", + image: "origami_light", + size: { x: 8, y: 9 } + } + ], + // box system + boxes: [ + { + name: "box", + resolution: 8, + image: "box_texture" + }, + { + name: "selection", + resolution: 8, + image: "selection" + }, + { + name: "flat_frame", + resolution: 8, + image: "flat_frame" + } + ], + tiles: [ + { name: "empty", id: 0, collision: false, visibility: false }, + { name: "water", id: 1, collision: false, look: "square", line: 7 }, + { name: "shores", id: 2, collision: false, look: "bitmask", line: 6 }, + { name: "ground", id: 3, collision: false, look: "bitmask", line: 1 }, + { name: "wall", id: 4, collision: true, look: "bitmask", line: 2 }, + { name: "fence", id: 11, collision: true, look: "bitmask", line: 4 }, + { name: "bush", id: 5, collision: true }, + { name: "ice", id: 6, collision: false, look: "bitmask", line: 3 }, + { name: "spawn", id: 7, collision: false }, + { + name: "exit", + id: 8, + collision: false, + animated: true, + spritesheet: "exit", + steps: 0.4 + }, + { + name: "waves", + id: 16, + collision: false, + animated: true, + spritesheet: "water_sprite", + steps: 0.2 + }, + { name: "trap", id: 9, collision: false }, + { name: "hole", id: 10, collision: true }, + // arrows + { name: "arrowLeft", id: 12, collision: false }, + { name: "arrowUp", id: 13, collision: false }, + { name: "arrowRight", id: 14, collision: false }, + { name: "arrowDown", id: 15, collision: false } + ], + maps: [ + // map 1 + { + name: "map_1", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } + // wall layer + ] + }, + // map 2 + { + name: "map_2", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 11, 11, 11, 11, 11, 0, 0, 11, 11, 11, 11, 11, 0, 0], + [0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0], + [0, 0, 11, 0, 8, 0, 11, 0, 0, 11, 0, 8, 0, 11, 0, 0], + [0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0], + [0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0], + [0, 0, 11, 4, 4, 0, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0], + [0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 4, 4, 11, 0, 0], + [0, 0, 11, 0, 0, 0, 11, 0, 0, 11, 0, 0, 0, 11, 0, 0], + [0, 0, 11, 0, 7, 0, 11, 0, 0, 11, 0, 7, 0, 11, 0, 0], + [0, 0, 11, 11, 11, 11, 11, 0, 0, 11, 11, 11, 11, 11, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } + // wall layer + ] + }, + { + name: "map_3", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 4, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 4, 8, 0, 0, 0, 0, 0, 0, 5, 0, 0, 4, 0, 0], + [0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 4, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0], + [0, 0, 4, 7, 0, 0, 0, 0, 0, 0, 0, 7, 0, 4, 0, 0], + [0, 0, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4, 4, 4, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } + // wall layer + ] + }, + + { + name: "map_4", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0], + [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0], + [0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0], + [0, 0, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 11, 0, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 0, 11, 0, 0, 0, 11, 0, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 0, 11, 0, 0, 0, 11, 9, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 8, 11, 0, 0, 0, 11, 8, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 0, 11, 0, 0, 0, 11, 7, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 0, 11, 0, 0, 0, 11, 0, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 0, 11, 5, 0, 0, 11, 11, 11, 0, 0, 0], + [0, 0, 0, 0, 11, 7, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 11, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ] + } // wall layer + ] + }, + { + name: "map_5", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 0, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 4, 6, 6, 6, 6, 8, 4, 4], + [4, 4, 4, 6, 6, 4, 6, 6, 4, 6, 4, 4, 6, 4, 4, 4], + [4, 4, 4, 4, 6, 6, 6, 6, 4, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 5, 6, 6, 6, 6, 0, 0, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 0, 7, 4, 4, 4], + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4], + [4, 5, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4], + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] + ] + } // wall layer + ] + }, + + { + name: "map_6", + tileset: "demo_tileset", + // ground + layers: [ + // ground layer + { + name: "ground", + offset: { + x: 0, + y: 4 + }, + geometry: [ + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], + [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + ] + }, + // ice / arrows / layer + { + name: "onGround", + offset: { + x: 0, + y: 0 + }, + geometry: [ + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 4, 4, 6, 6, 14, 0, 6, 6, 6, 6, 15, 4, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 8, 6, 6, 6, 4, 6, 6, 4, 4, 4], + [4, 4, 4, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 4, 4, 4], + [4, 4, 4, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 4, 4, 4], + [4, 4, 4, 6, 6, 14, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 0, 6, 4, 4, 4, 4], + [4, 4, 4, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 8, 13, 4, 4, 4], + [4, 4, 4, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 4, 4, 4], + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] + ] + } // wall layer + ] + } + ] +}; + +// Don't mind me +// just too lazy to modify the maps by hand +parameters.maps.forEach(map => { + new_layer = {}; + new_layer.name = "water"; + new_layer.look = "square"; + new_layer.offset = { x: 0, y: 8 }; + new_layer.geometry = Array(16) + .fill() + .map(() => Array(16).fill(0)); + map.layers.unshift(new_layer); + // + new_layer = {}; + new_layer.name = "splash"; + new_layer.offset = { x: 0, y: 8 }; + new_layer.geometry = Array(16) + .fill() + .map(() => Array(16).fill(0)); + map.layers.splice(2, 0, new_layer); + + let water = map.layers[0]; + let ground = map.layers[1]; + let splash = map.layers[2]; + + for (let y = 0; y < ground.geometry.length; y++) { + for (let x = 0; x < ground.geometry[0].length; x++) { + if ( + y - 1 > 0 && + ground.geometry[y][x] !== 3 && + ground.geometry[y - 1][x] == 3 + ) { + ground.geometry[y][x] = 2; + } + } + } + + for (let y = 0; y < ground.geometry.length; y++) { + for (let x = 0; x < ground.geometry[0].length; x++) { + if (ground.geometry[y][x] == 2) { + splash.geometry[y][x] = 16; + } + } + } + + for (let y = 0; y < water.geometry.length; y++) { + for (let x = 0; x < water.geometry[0].length; x++) { + if (ground.geometry[y][x] == 3) { + water.geometry[y][x] = 1; + } + + if (ground.geometry[y][x] !== 3 && ground.geometry[y][x + 1] == 3) { + water.geometry[y][x] = 1; + } + if (ground.geometry[y][x] !== 3 && ground.geometry[y][x - 1] == 3) { + water.geometry[y][x] = 1; + } + if ( + y + 1 < water.geometry.length && + ground.geometry[y][x] !== 3 && + ground.geometry[y + 1][x] == 3 + ) { + water.geometry[y][x] = 1; + } + if ( + y - 1 > 0 && + ground.geometry[y][x] !== 3 && + ground.geometry[y - 1][x] == 3 + ) { + water.geometry[y][x] = 1; + } + } + } + + for (let y = 0; y < water.geometry.length; y++) { + for (let x = 0; x < water.geometry[0].length; x++) { + if (water.geometry[y][x] == -1) { + water.geometry[y][x] = 1; + } + } + } +}); +// menu scene +let menu = new Scene("menu"); +menu.keyEvents = function(event) { + if (this.world.keys.ArrowDown && this.selection < this.button.length - 1) { + this.world.assets.audio.selection.audio.play(); + this.selection += 1; + } else if (this.world.keys.ArrowUp && this.selection > 0) { + this.world.assets.audio.selection.audio.play(); + this.selection -= 1; + } + if (this.world.keys.KeyX) { + this.world.assets.audio.selection.audio.play(); + this.world.startScene(this.button[this.selection].link); + } +}; +menu.init = function() { + this.init_once = true; + // custom data + this.button = [ + { + name: "PLAY", + link: "inGame" + }, + { + name: "SELECT", + link: "levels" + }, + { + name: "CONTROLS", + link: "controls" + } + ]; + this.texteMax = + Math.max(...this.button.map(button => button.name.length)) * 6; + this.selection = 0; + this.select_pos = { + x: this.world.W / 2, + y: 110 + }; + this.cursor_phase = 0; + this.cursor = this.world.assets.image.cursor.image; + // background + let background_image = this.world.assets.image.pattern.image; + this.pattern = this.world.ctx.createPattern(background_image, "repeat"); + this.offset = { + x: 0, + y: 0 + }; + // add cat on + this.cat = new Entity(this, -this.world.tile_size, -this.world.tile_size); + let sprite_data = { + image: "player_sprite", + size: { + x: 18, + y: 18 + } + }; + this.cat.setSprite(sprite_data); + this.cat.sprite.addAnimation("idle", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + this.cat.sprite.speed = 0.2; + this.cat.sprite.offset.y = -3; +}; +menu.render = function() { + this.animatedBackground(); + this.ctx.drawImage(this.world.assets.image["main_title"].image, 0, 0); + this.displaySelection(); + // notice + this.world.ctx.fillStyle = "rgba(0,0,0,0.6)"; + this.world.ctx.fillRect(0, this.world.H - 50, this.world.W, 33); + this.world.setFont("origami_light"); + this.world.write( + "Arrow keys to select", + this.world.W / 2, + this.world.H - 46, + "center" + ); + this.world.write( + "[x] to Confirm", + this.world.W / 2, + this.world.H - 30, + "center" + ); +}; +menu.displaySelection = function() { + // display box + this.ctx.fillStyle = "#82769e"; + this.world.drawBox( + "box", + this.select_pos.x - (this.texteMax + 60) / 2, + this.select_pos.y - 16, + this.texteMax + 60, + this.button.length * 20 + 20 + ); + // display text and cursor + for (i in this.button) { + if (i == this.selection) { + this.world.setFont("origami_light"); + } else { + this.world.setFont("origami_dark"); + } + let title = this.button[i].name; + this.world.write( + title, + this.select_pos.x, + this.select_pos.y + i * 20, + "center" + ); + } + this.cursor_phase += 0.1; + if (this.cursor_phase > 1 / Math.sin(0.2)) { + this.cursor_phase = -1; + } + let x = this.select_pos.x + Math.sin(this.cursor_phase) * 2 - 20; + this.world.ctx.drawImage( + this.cursor, + x - this.button[this.selection].name.length * 10 / 2, + this.select_pos.y + 20 * this.selection - 2 + ); +}; +menu.animatedBackground = function() { + this.offset.x += 0.8; + this.offset.y += 0.6; + if (this.offset.x > 63) { + this.offset.x = 0; + } + if (this.offset.y > 63) { + this.offset.y = 0; + } + let ctx = this.world.ctx; + ctx.save(); + ctx.translate(this.offset.x, this.offset.y); + ctx.fillStyle = this.pattern; + ctx.fillRect(-this.offset.x, -this.offset.y, this.world.W, this.world.H); + ctx.restore(); +}; +let levels = new Scene("levels"); +levels.keyEvents = function(event) { + if (this.world.keys.KeyE) { + this.world.assets.audio.selection.audio.play(); + this.world.startScene("menu"); + } + if (this.world.keys.ArrowDown && this.selection + 5 < this.world.mapsMax) { + this.world.assets.audio.selection.audio.play(); + this.selection += 5; + } + if (this.world.keys.ArrowUp && this.selection - 5 >= 0) { + this.world.assets.audio.selection.audio.play(); + this.selection -= 5; + } + if (this.world.keys.ArrowRight && this.selection + 1 < this.world.mapsMax) { + this.world.assets.audio.selection.audio.play(); + this.selection += 1; + } + if (this.world.keys.ArrowLeft && this.selection - 1 >= 0) { + this.world.assets.audio.selection.audio.play(); + this.selection -= 1; + } + if (this.world.keys.KeyX) { + this.world.assets.audio.selection.audio.play(); + this.world.current_level = this.selection + 1; + this.world.startScene("inGame"); + } +}; +levels.init = function() { + this.init_once = true; + this.selection = 0; + this.scale = 0; +}; +levels.render = function() { + this.world.clear("black"); + // animate selection + this.scale += 0.1; + if (this.scale > 1 / Math.sin(0.2)) { + this.scale = -1; + } + let offset = Math.sin(this.scale) * 2; + // display box + this.ctx.fillStyle = "#82769e"; + this.world.drawBox("box", 16, 16, this.world.W - 32, this.world.H - 46 - 32); + this.world.setFont("origami_light"); + this.world.setFont("origami_dark"); + let show = Math.min(this.world.mapsMax, 20); + for (let i = 0; i < show; i++) { + let level_id = i + 20 * Math.floor(this.selection / 20); + let position_x = 32 + Math.floor(i % 5) * 40, + position_y = 32 + Math.floor(i / 5) * 40; + if (level_id == this.selection) { + this.world.setFont("origami_light"); + this.world.drawBox( + "selection", + position_x - offset / 2, + position_y - offset / 2, + 24 + offset, + 24 + offset + ); + } else { + this.world.setFont("origami_dark"); + this.world.drawBox("flat_frame", position_x, position_y, 24, 24); + } + this.world.write( + (level_id + 1).toString(), + position_x + 13, + position_y + 8, + "center" + ); + } + // notice + this.world.ctx.fillStyle = "rgba(0,0,0,0.6)"; + this.world.ctx.fillRect(0, this.world.H - 50, this.world.W, 33); + this.world.setFont("origami_light"); + this.world.write( + "Arrow keys to select", + this.world.W / 2, + this.world.H - 46, + "center" + ); + this.world.write( + "[x] to Confirm, [E] to exit", + this.world.W / 2, + this.world.H - 30, + "center" + ); +}; +let inGame = new Scene("inGame"); +inGame.keyEvents = function(event) { + if (this.world.keys.KeyE && this.userInput) { + this.transition.start( + 0, + Math.max(this.world.W / 2, this.world.H / 2), + () => { + this.world.startScene("menu"); + } + ); + } + if (this.world.keys.KeyR && this.userInput) { + this.transition.start( + 0, + Math.max(this.world.W / 2, this.world.H / 2), + () => { + this.world.startScene("inGame"); + } + ); + } +}; +inGame.init = function() { + this.won = false; + this.userInput = true; + this.world.initMap("map_" + this.world.current_level); + this.cats = []; + let spawn_cat = () => { + // add cats on spawn tile_size + let spawns = this.world.findTile(3, 7); + spawns.forEach(spawn => { + this.addCat(spawn.x, spawn.y); + }); + }; + // effects + this.effects = []; + // transition effects + this.transition = { + scene: this, + active: true, + // between 0 and 100 + state: 0, + value: 0, + duration: 500, + start: 0, + // between whatever and whatever + from: 0, + to: Math.max(this.world.W, this.world.H), + // + start: function(from, to, callback) { + this.scene.userInput = false; + this.active = true; + this.from = from; + this.start_time = new Date(); + this.to = to; + this.callback = callback; + }, + update: function() { + let time = new Date() - this.start_time; + if (time < this.duration) { + this.value = Util.easeInOutQuad( + time, + this.from, + this.to - this.from, + this.duration + ); + } else { + this.active = false; + this.scene.userInput = true; + if (this.callback !== undefined) { + this.callback(); + } + } + }, + render: function() { + this.scene.ctx.fillStyle = "black"; + this.scene.ctx.fillRect(0, 0, this.scene.world.W, this.value); + this.scene.ctx.fillRect( + 0, + this.scene.world.H, + this.scene.world.W, + -this.value + ); + this.scene.ctx.fillRect(0, 0, this.value, this.scene.world.H); + this.scene.ctx.fillRect( + this.scene.world.W, + 0, + -this.value, + this.scene.world.H + ); + } + }; + this.transition.start( + Math.max(this.world.W / 2, this.world.H / 2), + 0, + spawn_cat + ); +}; +inGame.addCat = function(x, y) { + let cat = new Cat(this, x, y); + let sprite_data = { + image: "player_sprite", + size: { + x: 18, + y: 18 + } + }; + cat.setSprite(sprite_data); + cat.sprite.addAnimation("idle", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + cat.sprite.speed = 0.2; + cat.sprite.offset.y = -3; + let spawn_data = { + image: "spawn_effect", + frames: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + size: { + x: 20, + y: 40 + } + }; + let spawn_effect = new Effect(this, spawn_data, x, y - 1, () => { + this.cats.push(cat); + this.world.assets.audio.apparition.audio.play(); + }); + spawn_effect.trigger = 4; + this.effects.push(spawn_effect); +}; +inGame.render = function() { + this.control(); + this.world.renderMap(); + for (let i = this.cats.length; i--; ) { + this.cats[i].sprite.animate("idle"); + // draw shadow and cat + this.ctx.drawImage( + this.world.assets.image["shadow"].image, + this.cats[i].body.position.x, + this.cats[i].body.position.y + 2 + ); + this.cats[i].display(); + this.cats[i].translation(); + } + for (let i = this.effects.length; i--; ) { + this.effects[i].render(); + } + if (this.transition.active) { + this.transition.update(); + this.transition.render(); + } +}; +inGame.control = function() { + if (this.userInput == false) return false; + if (this.world.keys.ArrowUp) { + this.moveCats(0, -1); + } + if (this.world.keys.ArrowDown) { + this.moveCats(0, 1); + } + if (this.world.keys.ArrowLeft) { + this.moveCats(-1, 0); + } + if (this.world.keys.ArrowRight) { + this.moveCats(1, 0); + } +}; +inGame.moveCats = function(x, y) { + // see if every cat are ready to move + let canMove = this.cats.every(cat => { + return cat.inTranslation == false; + }); + if (!canMove) return false; + this.cats.forEach(cat => { + if (cat.canBeControlled === false) return false; + if (cat.isDead) return false; + cat.move(x, y); + }); + this.collisionCats(); + this.cats.forEach(cat => { + cat.applyMove(); + }); +}; +inGame.collisionCats = function() { + // check for other cats ! + let need_to_check = true; + while (need_to_check === true) { + need_to_check = false; + this.cats.forEach(cat => { + if (cat.checkOthers()) { + cat.target = cat.old_position.copy(); + need_to_check = true; + } + }); + } +}; +inGame.checkWin = function() { + if (this.cats.length === 0) { + // everyone is dead :/ + this.transition.start( + 0, + Math.max(this.world.W / 2, this.world.H / 2), + () => { + this.world.startScene("inGame"); + } + ); + return false; + } + let win = this.cats.every(cat => { + let tile = this.world.getTile(3, cat.target.x, cat.target.y); + return tile.name == "exit"; + }); + if ( + win === true && + this.cats.length >= this.world.findTile(3, 8).length && + !this.won + ) { + this.won = true; + + this.world.assets.audio.jingle.audio.play(); + + if ( + this.world.maps["map_" + (this.world.current_level + 1)] !== undefined + ) { + this.transition.start( + 0, + Math.max(this.world.W / 2, this.world.H / 2), + () => { + this.world.current_level += 1; + this.world.startScene("inGame"); + } + ); + } else { + this.transition.start( + 0, + Math.max(this.world.W / 2, this.world.H / 2), + () => { + this.world.startScene("menu"); + } + ); + } + } +}; +// destroy itself when animation is finish +class Effect extends Entity { + constructor(scene, sprite_data, x, y, callback) { + super(scene, x * scene.world.tile_size, y * scene.world.tile_size); + this.setSprite(sprite_data); + this.sprite.addAnimation("full", sprite_data.frames); + this.sprite.speed = 0.4; + this.sprite.offset.y = -3; + this.trigger = sprite_data.frames.length; + this.callback = callback || undefined; + } + render() { + if (this.sprite.current_frame + 1 === this.trigger) { + if (this.callback !== undefined) { + this.callback(); + this.callback = undefined; + } + } + if ( + this.sprite.current_frame + 1 === + this.sprite.animations[this.sprite.current_animation].length + ) { + this.scene.effects.splice(this.scene.effects.indexOf(this), 1); + } + this.sprite.animate("full"); + this.display(); + } +} +class Cat extends Entity { + constructor(scene, x, y) { + super(scene, x * scene.world.tile_size, y * scene.world.tile_size); + this.old_position = new Vector(x, y); + this.target = new Vector(x, y); + this.canBeControlled = true; + this.inTranslation = false; + this.lastDirection = new Vector(0, 0); + this.isDead = false; + // Trasnlation of the cat when they move + this.transition = { + start: new Date(), + duration: 300, + type: Util.easeInOutQuad, + start_pos: new Vector() + }; + } + // apply translation on cat when necessary + translation() { + if (this.inTranslation) { + // get current time ! + let time = new Date() - this.transition.start; + if (time < this.transition.duration) { + let x = this.transition.type( + time, + this.transition.start_pos.x, + this.transition.target.x - this.transition.start_pos.x, + this.transition.duration + ), + y = this.transition.type( + time, + this.transition.start_pos.y, + this.transition.target.y - this.transition.start_pos.y, + this.transition.duration + ); + this.body.position = new Vector(x, y); + } else { + // apply position when translation is finish :) ! + this.old_position = this.target.copy(); + let next_move = this.target.copy(); + next_move.mult(this.world.tile_size); + this.body.position = next_move; + this.inTranslation = false; + if (this.isDead) { + // delete cat + let spawn_data = { + image: "water_splash", + frames: [0, 1, 2, 3, 4, 5, 6, 7, 8], + size: { + x: 20, + y: 32 + } + }; + let spawn_effect = new Effect( + this.scene, + spawn_data, + this.target.x, + this.target.y - 1, + () => { + this.scene.cats.splice(this.scene.cats.indexOf(this), 1); + this.world.assets.audio.splash.audio.play(); + this.scene.checkWin(); + } + ); + spawn_effect.sprite.offset.y = 0; + spawn_effect.trigger = 2; + this.scene.effects.push(spawn_effect); + } + if (this.canBeControlled === false) { + this.move(this.lastDirection.x, this.lastDirection.y); + this.scene.collisionCats(); + this.applyMove(); + } else { + this.world.assets.audio.mouvement.audio.play(); + // check arrows + let current_tile = this.world.getTile( + 3, + this.target.x, + this.target.y + ); + switch (current_tile.name) { + case "arrowRight": + this.move(1, 0); + this.scene.collisionCats(); + this.applyMove(); + break; + case "arrowLeft": + this.move(-1, 0); + this.scene.collisionCats(); + this.applyMove(); + break; + case "arrowUp": + this.move(0, -1); + this.scene.collisionCats(); + this.applyMove(); + break; + case "arrowDown": + this.move(0, 1); + this.scene.collisionCats(); + this.applyMove(); + break; + default: + } + } + // check if we won when a cat finish a step + this.scene.checkWin(); + } + } + } + move(x, y) { + this.target = this.old_position.copy(); + let direction = new Vector(x, y); + // get future position + let future_position = this.target.copy(); + future_position.add(direction); + let layers = this.world.terrain.layers; + let future_tile = layers.map(layer => { + let index = layers.indexOf(layer); + return this.world.getTile(index, future_position.x, future_position.y); + }); + let collision = future_tile.every(tile => { + if (tile == false) { + return tile == false; + } else { + return tile.collision === false; + } + }); + if (collision == true) { + this.target.add(direction); + } + if (future_tile[3].name === "ice") { + this.canBeControlled = false; + this.transition.type = Util.linearTween; + this.transition.duration = 100; + return false; + } + if (future_tile[3].name === "trap") { + let dust_data = { + image: "dust_effect", + frames: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + size: { + x: 32, + y: 32 + } + }; + let dust_effect = new Effect( + this.scene, + dust_data, + this.target.x, + this.target.y + ); + this.scene.effects.push(dust_effect); + this.world.assets.audio.eboulement.audio.play(); + + this.world.terrain.layers[3].geometry[future_position.y][ + future_position.x + ] = 10; + // cache the map + this.world.terrainCache(this.world.terrain.layers[3]); + return false; + } + if (future_tile[1].name !== "ground") { + this.transition.type = Util.easeInOutQuad; + this.transition.duration = 200; + this.isDead = true; + return false; + } else { + this.canBeControlled = true; + this.transition.type = Util.easeInOutQuad; + this.transition.duration = 200; + return false; + } + } + applyMove() { + // prevent cat to move if his target equal his actual position :V + if ( + this.old_position.x === this.target.x && + this.old_position.y === this.target.y + ) { + this.canBeControlled = true; + this.world.assets.audio.bump.audio.play(); + return false; + } + this.lastDirection = new Vector( + this.target.x - this.old_position.x, + this.target.y - this.old_position.y + ); + this.shouldMove = false; + this.transition.start_pos = this.old_position.copy(); + this.transition.start_pos.mult(this.world.tile_size); + this.transition.target = this.target.copy(); + this.transition.target.mult(this.world.tile_size); + this.transition.start = new Date(); + this.inTranslation = true; + } + checkOthers() { + let others = this.scene.cats; + let result = false; + for (let i = 0; i < others.length; i++) { + if (this === others[i]) continue; + if ( + others[i].target.x === this.target.x && + others[i].target.y === this.target.y + ) { + result = true; + break; + } + } + return result; + } +} +let controls = new Scene("controls"); +controls.keyEvents = function(event) { + if (this.world.keys.KeyE) { + this.world.startScene("menu"); + } +}; +controls.init = function() { + this.loop = false; + this.controls = this.world.assets.image.controls.image; +}; +controls.render = function() { + this.world.clear("black"); + this.ctx.drawImage(this.controls, 0, 0); + // notice + this.world.setFont("origami_light"); + this.world.write( + "[E] to exit", + this.world.W / 2, + this.world.H - 46, + "center" + ); +}; + +let game = new Diorama(parameters); +// global variables +game.current_level = 1; +// Add the different scenes here +// the addScene function link the scene with the world (game) +game.addScene(menu); +game.addScene(levels); +game.addScene(controls); +game.addScene(inGame); +game.ready(); +// everything start being loaded now ! +// the ready function must be called last ! +// Making the game full screen and with a 10% audio volume by default +game.soundLevel(0.2); +game.fullScreen(); \ No newline at end of file diff --git a/Games/CopyCat/style.css b/Games/CopyCat/style.css new file mode 100644 index 0000000000..310533f426 --- /dev/null +++ b/Games/CopyCat/style.css @@ -0,0 +1,24 @@ +html, +body { + margin: 0; + padding: 0; + height: 100%; +} +body { + color:white; + background-color: #000; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} +canvas { + flex-shrink: 0; + background-color: #000; + object-fit: contain; +} +.crisp{ + image-rendering: -moz-crisp-edges; + image-rendering: -webkit-crisp-edges; + image-rendering: pixelated; +} \ No newline at end of file diff --git a/README.md b/README.md index 0c95c64584..e80465005a 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,9 @@ This repository also provides one such platforms where contributers come over an | [Emoji_Intruder](https://github.com/kunjgit/GameZone/tree/main/Games/Emoji_Intruder) | [Guess The Weapon](https://github.com/kunjgit/GameZone/tree/main/Games/Guess_The_Weapon) | [Guess Who](https://github.com/kunjgit/GameZone/tree/main/Games/Guess_Who) | [Pop My Balloon](https://github.com/kunjgit/GameZone/tree/main/Games/Pop_My_Balloon) | [Tower Stack](https://github.com/kunjgit/GameZone/tree/main/Games/Tower_Stack) | | [Maze_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Maze_Game) | [TriHand_Tactics](https://github.com/kunjgit/GameZone/tree/main/Games/TriHand_Tactics) | [Earth_Guardian](https://github.com/kunjgit/GameZone/tree/main/Games/Earth_Guardian) | [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) | | [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) | [CatchTheBall](https://github.com/kunjgit/GameZone/tree/main/Games/CatchTheBall) | -| [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) | [DoraemonRun ](https://github.com/kunjgit/GameZone/tree/main/Games/DoraemonRun) | +| [Ball_Shooting_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Ball_Shooting_Game) | +| [CopyCat](https://github.com/kunjgit/GameZone/tree/main/Games/CopyCat) | +[DoraemonRun ](https://github.com/kunjgit/GameZone/tree/main/Games/DoraemonRun) | | [Memory_Cards_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Memory_Cards_Game) | | [Typing_Speed_Test2](https://github.com/kunjgit/GameZone/tree/main/Games/Typing_Speed_Test2) | [Tic Tac Toe Responsive ](https://github.com/kunjgit/GameZone/tree/main/Games/Tic_tac_toe_responsive) | | [Technical_Mind_Game](https://github.com/kunjgit/GameZone/tree/main/Games/Technical_Mind_Game) | diff --git a/assets/images/CopyCat.png b/assets/images/CopyCat.png new file mode 100644 index 0000000000..306bea8ced Binary files /dev/null and b/assets/images/CopyCat.png differ diff --git a/assets/images/CopyCat1.png b/assets/images/CopyCat1.png new file mode 100644 index 0000000000..510fbcab13 Binary files /dev/null and b/assets/images/CopyCat1.png differ