commit d04ac221281a6aa164b47dda413da0d7acb19654 Author: gltron Date: Sun Apr 17 18:31:02 2022 +0200 V0.1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5c9f3fe --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Build +FROM rust as builder +WORKDIR /app +COPY . . +WORKDIR /app/server +RUN cargo build --release --bin out_of_quiz + +# Runner +FROM debian:bullseye-slim as runner +COPY --from=builder /app/server/target/release/out_of_quiz /app/out_of_quiz +COPY --from=builder /app/server/quotes.json /app/quotes.json +COPY --from=builder /app/client /app/static +WORKDIR app +CMD ["./out_of_quiz"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d000be --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# DeepImpact + +A musical missile command + +## Rules + +### Level + +Sprites: Background, boss, projectiles + +Sound: Music + +Text: Title, subtitle + + +-> Boss slowly rising until the end when it explode + +### Pages + +* Main menu +* Lobby (no instances, level selection, ready) +* Game (canvas) + +### Gameplay + +Control one bunker + +Multiple turret type +* Slow steady, single fire, medium aim (medium damage) +* Rapid fire, burst, quick aim (small damage) +* Very slow, single shot, low aim (high damage) + +Turret aim follow mouse with latency, cooldown between fires + + +## Tech + +Music on client -> Effects (background) +Music on server -> Compute meteor spawn, player state (health), game state, + + +### States + +Game -> LOBBY, RUNNING, END +Player -> UNKNOW, WAITING, PLAYING + +### Messages + +#### To server + +* login (string) +* logout +* ready (weapon type) +* start (music title) +* fire (angle) +* music (file) + +#### To Client + +* userlist +* state (game state) +* update (game update) +* music (file) +* play (music title) +* ok (message) +* nok (message) + +### Game Update + +``` +{ + players: [ + { + "name": string, + "health": number, + "weapon": string, + "angle": number + } + ], + meteors: [ + { + "x": number, + "y": number, + "speed": number, + "angle": number, + "health": number + } + ], + bullets: [ + { + "x": number, + "y": number, + "speed": number, + "angle": number, + "damage": number + } + ] +} +``` \ No newline at end of file diff --git a/game_assets.svg b/game_assets.svg new file mode 100644 index 0000000..60f2d29 --- /dev/null +++ b/game_assets.svg @@ -0,0 +1,6402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gameart.svg b/gameart.svg new file mode 100644 index 0000000..3b6d48a --- /dev/null +++ b/gameart.svg @@ -0,0 +1,2479 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo.svg b/logo.svg new file mode 100644 index 0000000..eafce57 --- /dev/null +++ b/logo.svg @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + deep Impact + + + + + + + + + + + + + + + + + + + + + + diff --git a/skyline.svg b/skyline.svg new file mode 100644 index 0000000..018877a --- /dev/null +++ b/skyline.svg @@ -0,0 +1,416 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/fonts/ChopinScript.otf b/src/assets/fonts/ChopinScript.otf new file mode 100644 index 0000000..e914944 Binary files /dev/null and b/src/assets/fonts/ChopinScript.otf differ diff --git a/src/assets/fonts/Cratense.ttf b/src/assets/fonts/Cratense.ttf new file mode 100644 index 0000000..30ace7a Binary files /dev/null and b/src/assets/fonts/Cratense.ttf differ diff --git a/src/assets/fonts/Gabella.ttf b/src/assets/fonts/Gabella.ttf new file mode 100644 index 0000000..d53a9e1 Binary files /dev/null and b/src/assets/fonts/Gabella.ttf differ diff --git a/src/assets/fonts/Revamped.otf b/src/assets/fonts/Revamped.otf new file mode 100644 index 0000000..12713b1 Binary files /dev/null and b/src/assets/fonts/Revamped.otf differ diff --git a/src/assets/fonts/SketchedCassiusBroken.ttf b/src/assets/fonts/SketchedCassiusBroken.ttf new file mode 100644 index 0000000..df89826 Binary files /dev/null and b/src/assets/fonts/SketchedCassiusBroken.ttf differ diff --git a/src/assets/fonts/TRON.TTF b/src/assets/fonts/TRON.TTF new file mode 100644 index 0000000..57a826d Binary files /dev/null and b/src/assets/fonts/TRON.TTF differ diff --git a/src/assets/fonts/ka1.ttf b/src/assets/fonts/ka1.ttf new file mode 100755 index 0000000..d1df852 Binary files /dev/null and b/src/assets/fonts/ka1.ttf differ diff --git a/src/assets/img/boss/e4.png b/src/assets/img/boss/e4.png new file mode 100644 index 0000000..451ab0b Binary files /dev/null and b/src/assets/img/boss/e4.png differ diff --git a/src/assets/img/boss/q4.png b/src/assets/img/boss/q4.png new file mode 100644 index 0000000..ecc19c7 Binary files /dev/null and b/src/assets/img/boss/q4.png differ diff --git a/src/assets/img/boss/r4.png b/src/assets/img/boss/r4.png new file mode 100644 index 0000000..439a708 Binary files /dev/null and b/src/assets/img/boss/r4.png differ diff --git a/src/assets/img/boss/richard.jpg b/src/assets/img/boss/richard.jpg new file mode 100644 index 0000000..266037e Binary files /dev/null and b/src/assets/img/boss/richard.jpg differ diff --git a/src/assets/img/boss/richard.png b/src/assets/img/boss/richard.png new file mode 100644 index 0000000..639268b Binary files /dev/null and b/src/assets/img/boss/richard.png differ diff --git a/src/assets/img/boss/samurai.png b/src/assets/img/boss/samurai.png new file mode 100644 index 0000000..2317b95 Binary files /dev/null and b/src/assets/img/boss/samurai.png differ diff --git a/src/assets/img/boss/t4.png b/src/assets/img/boss/t4.png new file mode 100644 index 0000000..8de645d Binary files /dev/null and b/src/assets/img/boss/t4.png differ diff --git a/src/assets/img/custom.png b/src/assets/img/custom.png new file mode 100644 index 0000000..69610e0 Binary files /dev/null and b/src/assets/img/custom.png differ diff --git a/src/assets/img/effects/explosion.png b/src/assets/img/effects/explosion.png new file mode 100644 index 0000000..2d0b4a7 Binary files /dev/null and b/src/assets/img/effects/explosion.png differ diff --git a/src/assets/img/meteor_logo.png b/src/assets/img/meteor_logo.png new file mode 100644 index 0000000..ab4b266 Binary files /dev/null and b/src/assets/img/meteor_logo.png differ diff --git a/src/assets/img/meteor_title.png b/src/assets/img/meteor_title.png new file mode 100644 index 0000000..03803b7 Binary files /dev/null and b/src/assets/img/meteor_title.png differ diff --git a/src/assets/img/meteors/diploma.png b/src/assets/img/meteors/diploma.png new file mode 100644 index 0000000..22ae493 Binary files /dev/null and b/src/assets/img/meteors/diploma.png differ diff --git a/src/assets/img/meteors/diplome.jpg b/src/assets/img/meteors/diplome.jpg new file mode 100644 index 0000000..f7ed33e Binary files /dev/null and b/src/assets/img/meteors/diplome.jpg differ diff --git a/src/assets/img/meteors/meteor.png b/src/assets/img/meteors/meteor.png new file mode 100644 index 0000000..950997d Binary files /dev/null and b/src/assets/img/meteors/meteor.png differ diff --git a/src/assets/img/meteors/meteor_2.png b/src/assets/img/meteors/meteor_2.png new file mode 100644 index 0000000..473effa Binary files /dev/null and b/src/assets/img/meteors/meteor_2.png differ diff --git a/src/assets/img/meteors/meteor_3.png b/src/assets/img/meteors/meteor_3.png new file mode 100644 index 0000000..e89f5e2 Binary files /dev/null and b/src/assets/img/meteors/meteor_3.png differ diff --git a/src/assets/img/meteors/meteor_4.png b/src/assets/img/meteors/meteor_4.png new file mode 100644 index 0000000..dc0a4ff Binary files /dev/null and b/src/assets/img/meteors/meteor_4.png differ diff --git a/src/assets/img/meteors/meteor_full.png b/src/assets/img/meteors/meteor_full.png new file mode 100644 index 0000000..a9bf6f1 Binary files /dev/null and b/src/assets/img/meteors/meteor_full.png differ diff --git a/src/assets/img/meteors/meteor_full2.png b/src/assets/img/meteors/meteor_full2.png new file mode 100644 index 0000000..d8453e4 Binary files /dev/null and b/src/assets/img/meteors/meteor_full2.png differ diff --git a/src/assets/img/meteors/plane.jpg b/src/assets/img/meteors/plane.jpg new file mode 100644 index 0000000..90a7d24 Binary files /dev/null and b/src/assets/img/meteors/plane.jpg differ diff --git a/src/assets/img/meteors/plane.png b/src/assets/img/meteors/plane.png new file mode 100644 index 0000000..07c15fd Binary files /dev/null and b/src/assets/img/meteors/plane.png differ diff --git a/src/assets/img/meteors/plane_A.png b/src/assets/img/meteors/plane_A.png new file mode 100644 index 0000000..9d0e978 Binary files /dev/null and b/src/assets/img/meteors/plane_A.png differ diff --git a/src/assets/img/skyline.png b/src/assets/img/skyline.png new file mode 100644 index 0000000..6199a6d Binary files /dev/null and b/src/assets/img/skyline.png differ diff --git a/src/assets/img/skyline_turret.png b/src/assets/img/skyline_turret.png new file mode 100644 index 0000000..7c2ae36 Binary files /dev/null and b/src/assets/img/skyline_turret.png differ diff --git a/src/assets/img/skyline_turret2.png b/src/assets/img/skyline_turret2.png new file mode 100644 index 0000000..e871893 Binary files /dev/null and b/src/assets/img/skyline_turret2.png differ diff --git a/src/assets/img/turrets/bullet_fast.png b/src/assets/img/turrets/bullet_fast.png new file mode 100644 index 0000000..4b54949 Binary files /dev/null and b/src/assets/img/turrets/bullet_fast.png differ diff --git a/src/assets/img/turrets/bullet_heavy.png b/src/assets/img/turrets/bullet_heavy.png new file mode 100644 index 0000000..e68a6ba Binary files /dev/null and b/src/assets/img/turrets/bullet_heavy.png differ diff --git a/src/assets/img/turrets/bullet_laser.png b/src/assets/img/turrets/bullet_laser.png new file mode 100644 index 0000000..abbf411 Binary files /dev/null and b/src/assets/img/turrets/bullet_laser.png differ diff --git a/src/assets/img/turrets/bullet_standard.png b/src/assets/img/turrets/bullet_standard.png new file mode 100644 index 0000000..3c8eea4 Binary files /dev/null and b/src/assets/img/turrets/bullet_standard.png differ diff --git a/src/assets/img/turrets/turret_fast.png b/src/assets/img/turrets/turret_fast.png new file mode 100644 index 0000000..c426721 Binary files /dev/null and b/src/assets/img/turrets/turret_fast.png differ diff --git a/src/assets/img/turrets/turret_heavy.png b/src/assets/img/turrets/turret_heavy.png new file mode 100644 index 0000000..fdc2af2 Binary files /dev/null and b/src/assets/img/turrets/turret_heavy.png differ diff --git a/src/assets/img/turrets/turret_laser.png b/src/assets/img/turrets/turret_laser.png new file mode 100644 index 0000000..3f2b4af Binary files /dev/null and b/src/assets/img/turrets/turret_laser.png differ diff --git a/src/assets/img/turrets/turret_standard.png b/src/assets/img/turrets/turret_standard.png new file mode 100644 index 0000000..990011b Binary files /dev/null and b/src/assets/img/turrets/turret_standard.png differ diff --git a/src/assets/music/ABIS & Signal & Tasha Baxter - The Wall (Buunshin Remix).flac b/src/assets/music/ABIS & Signal & Tasha Baxter - The Wall (Buunshin Remix).flac new file mode 100644 index 0000000..208d600 Binary files /dev/null and b/src/assets/music/ABIS & Signal & Tasha Baxter - The Wall (Buunshin Remix).flac differ diff --git a/src/assets/music/El Tigr3 - Black Star.flac b/src/assets/music/El Tigr3 - Black Star.flac new file mode 100755 index 0000000..bbcde11 Binary files /dev/null and b/src/assets/music/El Tigr3 - Black Star.flac differ diff --git a/src/assets/music/Igorrr - Very Noise.flac b/src/assets/music/Igorrr - Very Noise.flac new file mode 100755 index 0000000..44d8e99 Binary files /dev/null and b/src/assets/music/Igorrr - Very Noise.flac differ diff --git a/src/assets/music/Infected Mushroom - Guitarmass.flac b/src/assets/music/Infected Mushroom - Guitarmass.flac new file mode 100755 index 0000000..81ed95b Binary files /dev/null and b/src/assets/music/Infected Mushroom - Guitarmass.flac differ diff --git a/src/assets/music/Open The Gates.mp3 b/src/assets/music/Open The Gates.mp3 new file mode 100755 index 0000000..4cdc77b Binary files /dev/null and b/src/assets/music/Open The Gates.mp3 differ diff --git a/src/assets/music/The City Must Survive.flac b/src/assets/music/The City Must Survive.flac new file mode 100755 index 0000000..720d313 Binary files /dev/null and b/src/assets/music/The City Must Survive.flac differ diff --git a/src/assets/music/The Only Thing They Fear is V.flac b/src/assets/music/The Only Thing They Fear is V.flac new file mode 100644 index 0000000..9fc7e39 Binary files /dev/null and b/src/assets/music/The Only Thing They Fear is V.flac differ diff --git a/src/assets/music/The Scene Is Dead (MASTER BOOT RECORD Remix).mp3 b/src/assets/music/The Scene Is Dead (MASTER BOOT RECORD Remix).mp3 new file mode 100644 index 0000000..73ca1c8 Binary files /dev/null and b/src/assets/music/The Scene Is Dead (MASTER BOOT RECORD Remix).mp3 differ diff --git a/src/assets/music/jump.mp3 b/src/assets/music/jump.mp3 new file mode 100644 index 0000000..4751999 Binary files /dev/null and b/src/assets/music/jump.mp3 differ diff --git a/src/assets/music/music2.mp3 b/src/assets/music/music2.mp3 new file mode 100644 index 0000000..7879028 Binary files /dev/null and b/src/assets/music/music2.mp3 differ diff --git a/src/assets/music/nightcall_8bit.mp3 b/src/assets/music/nightcall_8bit.mp3 new file mode 100644 index 0000000..1c43b42 Binary files /dev/null and b/src/assets/music/nightcall_8bit.mp3 differ diff --git a/src/assets/sounds/153492__steelskull__minigun-shooting-awesome-sound.wav b/src/assets/sounds/153492__steelskull__minigun-shooting-awesome-sound.wav new file mode 100644 index 0000000..f18b2ac Binary files /dev/null and b/src/assets/sounds/153492__steelskull__minigun-shooting-awesome-sound.wav differ diff --git a/src/assets/sounds/557595__breviceps__minigun.wav b/src/assets/sounds/557595__breviceps__minigun.wav new file mode 100644 index 0000000..88109bc Binary files /dev/null and b/src/assets/sounds/557595__breviceps__minigun.wav differ diff --git a/src/assets/sounds/612875__sonically-sound__laser-machine-gun.flac b/src/assets/sounds/612875__sonically-sound__laser-machine-gun.flac new file mode 100644 index 0000000..24eb8ec Binary files /dev/null and b/src/assets/sounds/612875__sonically-sound__laser-machine-gun.flac differ diff --git a/src/assets/sounds/explosion.flac b/src/assets/sounds/explosion.flac new file mode 100644 index 0000000..829564c Binary files /dev/null and b/src/assets/sounds/explosion.flac differ diff --git a/src/assets/sounds/explosion.mp3 b/src/assets/sounds/explosion.mp3 new file mode 100644 index 0000000..4b1dba3 Binary files /dev/null and b/src/assets/sounds/explosion.mp3 differ diff --git a/src/assets/sounds/explosion_big.mp3 b/src/assets/sounds/explosion_big.mp3 new file mode 100644 index 0000000..5b6d6dd Binary files /dev/null and b/src/assets/sounds/explosion_big.mp3 differ diff --git a/src/assets/sounds/turret_fast.mp3 b/src/assets/sounds/turret_fast.mp3 new file mode 100644 index 0000000..8f5c4ab Binary files /dev/null and b/src/assets/sounds/turret_fast.mp3 differ diff --git a/src/assets/sounds/turret_heavy.flac b/src/assets/sounds/turret_heavy.flac new file mode 100644 index 0000000..4a4ec48 Binary files /dev/null and b/src/assets/sounds/turret_heavy.flac differ diff --git a/src/assets/sounds/turret_laser.mp3 b/src/assets/sounds/turret_laser.mp3 new file mode 100644 index 0000000..1f3166f Binary files /dev/null and b/src/assets/sounds/turret_laser.mp3 differ diff --git a/src/assets/sounds/turret_release.wav b/src/assets/sounds/turret_release.wav new file mode 100644 index 0000000..9d457ae Binary files /dev/null and b/src/assets/sounds/turret_release.wav differ diff --git a/src/assets/sounds/turret_standard.mp3 b/src/assets/sounds/turret_standard.mp3 new file mode 100644 index 0000000..1ce343b Binary files /dev/null and b/src/assets/sounds/turret_standard.mp3 differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..45ff224 --- /dev/null +++ b/src/index.html @@ -0,0 +1,109 @@ + + + + + + + + + Deep Impact + + + + + + +
+ + + + + + + +
+ + + + \ No newline at end of file diff --git a/src/scripts/game.js b/src/scripts/game.js new file mode 100644 index 0000000..7680d70 --- /dev/null +++ b/src/scripts/game.js @@ -0,0 +1,258 @@ +import { levelList } from "./levelList.js" +import Player from "./player.js" +import { Bullet, Meteor, Turret } from "./gameObjects.js" +import { Renderer } from "./renderer.js" +import { setFastTurret, setHeavyTurret, setLaserTurret, setStandardTurret } from "./turretTypes.js" +import SoundEffect from "./soundEffect.js" + +export default class Game { + + constructor() { + this.canvas = document.getElementById("mainCanvas") + this.renderer = new Renderer(this.canvas) + this.player = new Player() + this.levelList = levelList + this.currentLevel; + this.nextTurretToFire = 0 + this.turretFire = false + this.health = 10 + this.healthDisplay = document.getElementById("health") + this.showTitle = false + this.lastSlowUpdate = 0 + + this.ammoCurrent = "" + this.ammo = { + laser: 100, + fast: 100, + standard: 100, + heavy: 100 + } + + SoundEffect.setEffectVolume(0.3) + + document.addEventListener("keydown", (event) => { + switch(event.key) { + case "p": this.renderer.showStats = !this.renderer.showStats; break + case "o": this.renderer.showCollisionBox = !this.renderer.showCollisionBox; break + case "a": this.setTurretTypeAll("laser"); break + case "z": this.setTurretTypeAll("fast"); break + case "e": this.setTurretTypeAll("standard"); break + case "r": this.setTurretTypeAll("heavy"); break + } + }) + } + + setGameCanvas(gameCanvas = true) { + let backgroundColor + if (gameCanvas) { + this.canvas = document.getElementById("gameCanvas") + backgroundColor = "#a9cdd2" + } else { + this.canvas = document.getElementById("mainCanvas") + backgroundColor = "#153a3a" + } + this.renderer = new Renderer(this.canvas, backgroundColor) + this.setSize() + } + + async loadMainMenu(level) { + await level.loadLevel() + this.player.setMusic(level.musicPath) + this.player.start() + this.currentLevel = level + } + + async loadLevel(level) { + console.log('Set current level:', level.title) + + this.player.stop() + + await level.loadLevel() + await this.setupTurrets() + + this.showTitle = true + const mainGameTitle = document.getElementById("main-game-title") + mainGameTitle.querySelector("h1").innerText = level.title + mainGameTitle.querySelector("h2").innerText = level.subtitle + this.setShowTitle(true) + setTimeout(() => this.setShowTitle(false), 10000) + + this.player.setMusic(level.musicPath) + this.player.start() + + this.canvas.addEventListener("mousemove", (event) => this.setTurretTarget(event)) + this.canvas.addEventListener("mousedown", () => this.setTurretFire(true)) + this.canvas.addEventListener("mouseup", () => this.setTurretFire(false)) + this.canvas.addEventListener("dblclick", () => this.setTurretFire(!this.turretFire)) + + const volumeInput = document.getElementById("volume") + volumeInput.addEventListener("change", () => { + console.log('volume', volumeInput.value) + this.player.gain.gain.value = volumeInput.value / 10 + }) + + this.setGameOver() + this.player.audio.addEventListener('ended', () => { + this.setGameOver(true) + this.health = 0 + }) + this.currentLevel = level + this.renderer.bossImg = this.currentLevel.bossImg + + this.ammoCurrent = "standard" + this.ammo = { + laser: 100, + fast: 100, + standard: 100, + heavy: 100 + } + + this.setAmmoCounter() + } + + setShowTitle(show = false) { + document.getElementById("main-game-title").style.display = show ? "flex" : "none" + } + + setGameOver(win = false) { + const gameOverTitle = document.getElementById("game-over") + gameOverTitle.querySelector("h2").innerText = win ? "YOU HAVE OVERCOME THE ATTACK" : "YOU HAVE PERISHED" + gameOverTitle.querySelector("h3").innerText = win ? "Until next time" : "Not much to do left" + gameOverTitle.querySelector("button").innerText = win ? "yay" : "damn" + } + + setSize(height, width) { + this.canvas.width = width ?? window.innerWidth + this.canvas.height = height ?? window.innerHeight + } + + async setupTurrets() { + const turretY = this.canvas.height - 155 + const firstTurretX = (this.canvas.width - 1500) / 2 + 95 + + this.renderer.turrets.push(new Turret(firstTurretX, turretY)) + this.renderer.turrets.push(new Turret(firstTurretX + 380, turretY - 15)) + this.renderer.turrets.push(new Turret(firstTurretX + 835, turretY - 10)) + this.renderer.turrets.push(new Turret(firstTurretX + 1265, turretY + 5)) + + for (const turret of this.renderer.turrets) { + await setStandardTurret(turret) + } + + this.ammoCurrent = "standard" + } + + setTurretTypeAll(type) { + this.ammoCurrent = type + this.renderer.turrets.forEach((_, i) => this.setTurretType(i, type)) + } + + async setTurretType(i, type) { + const turret = this.renderer.turrets[i] + + switch(type) { + case "standard": await setStandardTurret(turret); break + case "fast": await setFastTurret(turret); break + case "laser": await setLaserTurret(turret); break + case "heavy": await setHeavyTurret(turret); break + } + } + + setTurretTarget(event) { + this.renderer.mouseX = event.offsetX + this.renderer.mouseY = event.offsetY + + for (const turret of this.renderer.turrets) { + const dx = this.renderer.mouseX - turret.x + const dy = this.renderer.mouseY - turret.y + turret.angle = Math.atan2(dy, dx) + } + } + + setTurretFire(fire) { + this.turretFire = fire + this.renderer.turrets.forEach((turret) => turret.animationRunning = fire) + + // if (!fire) { + // SoundEffect.play(SoundEffect.turretRelease) + // } + } + + setAmmoCounter() { + const ammoLaserCounter = document.getElementById("ammo-laser") + ammoLaserCounter.querySelector("h3").innerText = this.ammo.laser + + const ammoFastCounter = document.getElementById("ammo-fast") + ammoFastCounter.querySelector("h3").innerText = this.ammo.fast + + const ammoStandardCounter = document.getElementById("ammo-standard") + ammoStandardCounter.querySelector("h3").innerText = this.ammo.standard + + const ammoHeavyCounter = document.getElementById("ammo-heavy") + ammoHeavyCounter.querySelector("h3").innerText = this.ammo.heavy + } + + spawnBullet() { + if (this.ammo[this.ammoCurrent] <= 0) { + return + } + + const turret = this.renderer.turrets[this.nextTurretToFire] + const deltaLastShot = performance.now() - turret.lastShot + if (deltaLastShot > turret.firingRate) { + const angle = turret.angle - ((Math.floor(Math.random() * turret.spread) / 1000) * (Math.random() < 0.5 ? -1 : 1)) + this.renderer.bullets.push(new Bullet(turret.x , turret.y, angle, turret.bulletSprite, turret.damage)) + turret.lastShot = performance.now() + // SoundEffect.play(turret.sound) + } + + this.nextTurretToFire = this.nextTurretToFire < this.renderer.turrets.length - 1 ? this.nextTurretToFire + 1 : 0 + } + + spawnMeteor() { + // meteors[Math.floor(Math.random() * this.meteors.length)] + this.renderer.meteors.push(new Meteor(this.canvas.width, this.canvas.height, this.currentLevel.maxHealth, this.currentLevel.meteorImg, this.currentLevel.meteorImgList)) + } + + render() { + // Frames + const now = performance.now() + if ((now - this.renderer.lastFrame) > (1000 / 60)) { + if (this.turretFire) { + this.spawnBullet() + } + + const damageTaken = this.renderer.render() + + if (damageTaken > 0 && this.currentLevel.title !== "MainMenu") { + SoundEffect.play(SoundEffect.explosionBig) + this.health -= damageTaken + } + + this.healthDisplay.innerText = `${this.health} HP` + } + + // Slow updates + if ((now - this.lastSlowUpdate) > 1000) { + console.log('SLOW') + if (this.turretFire) { + console.log('SET AMMO', this.ammoCurrent, this.ammo) + this.ammo.laser += this.ammo.laser < 100 ? 5 : 0 + this.ammo.fast += this.ammo.fast < 100 ? 5 : 0 + this.ammo.standard += this.ammo.standard < 100 ? 5 : 0 + this.ammo.heavy += this.ammo.heavy < 100 ? 5 : 0 + + this.ammo[this.ammoCurrent] -= 6 + + if (this.ammo[this.ammoCurrent] <= 0) { + this.ammo[this.ammoCurrent] = 0 + } + + this.setAmmoCounter() + } + + this.lastSlowUpdate = now + } + } + +} \ No newline at end of file diff --git a/src/scripts/gameObjects.js b/src/scripts/gameObjects.js new file mode 100644 index 0000000..052a923 --- /dev/null +++ b/src/scripts/gameObjects.js @@ -0,0 +1,152 @@ +export class GameObject { + x = 0 + y = 0 + sizeX = 0 + sizeY = 0 + heightX = 0 + heightY = 0 + speed = 0 + angle = 0 + health = 0 + spriteImg = new Image() + isAnimated = false + removeAfterAnimation = false + animationRunning = false + frames = 0 + framesCounter = 0 + + get sprite() { + return this.spriteImg + } + + step(tickToSimulate) { + for (let i = 0; i < tickToSimulate; i++) { + this.x = this.x + this.speed * Math.cos(this.angle) + this.y = this.y + this.speed * Math.sin(this.angle) + } + + // Improve canvas performances + this.x = Math.floor(this.x) + this.y = Math.floor(this.y) + } + + // Crude collision box + collideWith(gameObject) { + const x = this.x - this.sizeX + const y = this.y - this.sizeY + const a = x + this.sizeX * 2 + const b = y + this.sizeY * 2 + + const x1 = gameObject.x - gameObject.sizeX + const y1 = gameObject.y - gameObject.sizeY + const a1 = x1 + gameObject.sizeX * 2 + const b1 = y1 + gameObject.sizeY * 2 + + return !(a < x1 || a1 < x || b < y1 || b1 < y) + } +} + +export class Meteor extends GameObject { + constructor(maxX, maxY, maxHealth, meteorSprite, meteorSpriteList) { + super() + const xOrigin = Math.floor(Math.random() * maxX - 10) + 10 + const xTarget = Math.floor(Math.random() * maxX - 20) + 20 + + const dx = xTarget - xOrigin + const dy = maxY + + this.angle = Math.atan2(dy, dx) + this.speed = Math.floor(Math.random() * 0.5) + 0.1 + this.health = Math.floor(Math.random() * (maxHealth - 1)) + 1 + + this.x = xOrigin + this.y = 0 + this.sizeX = 25 + this.sizeY = 25 + this.heightX = 256 + this.heightY = 256 + this.meteorSprite = meteorSprite + this.meteorSpriteList = meteorSpriteList + this.isAnimated = false + this.removeAfterAnimation = false + this.animationRunning = false + this.frames = 4 + this.framesCounter = 0 + } + + get sprite() { + if (this.meteorSprite) { + return this.meteorSprite + } else if (this.health <= 10) { + return this.meteorSpriteList[0] + } else if (this.health <= 20) { + return this.meteorSpriteList[1] + } else if (this.health <= 30) { + return this.meteorSpriteList[2] + } else { + return this.meteorSpriteList[3] + } + } +} + +export class Bullet extends GameObject { + constructor(x, y, angle, bulletSprite, damage) { + super() + this.x = x + this.y = y + this.sizeX = 5 + this.sizeY = 10 + this.heightX = 0 + this.heightY = 0 + this.spriteImg= bulletSprite + this.speed = 1 + this.angle = angle + this.health = 1 + this.damage = damage + this.isAnimated = false + this.removeAfterAnimation = false + this.animationRunning = false + this.frames = 0 + this.framesCounter = 0 + } +} + +export class Explosion extends GameObject { + constructor(x, y, explosionSprite) { + super() + this.x = x + this.y = y + this.sizeX = 50 + this.sizeY = 50 + this.heightX = 256 + this.heightY = 256 + this.spriteImg= explosionSprite + this.isAnimated = true + this.removeAfterAnimation = true + this.animationRunning = true + this.frames = 32 + this.framesCounter = 0 + } +} + +export class Turret extends GameObject { + constructor(x, y) { + super() + this.x = x + this.y = y + this.sizeX = 25 + this.sizeY = 35 + this.heightX = 0 + this.heightY = 0 + this.spriteImg= null + this.bulletSprite = null + this.isAnimated = false + this.removeAfterAnimation = false + this.animationRunning = false + this.frames = 0 + this.framesCounter = 0 + this.firingRate = 0 + this.spread = 0 + this.damage = 0 + } +} \ No newline at end of file diff --git a/src/scripts/index.js b/src/scripts/index.js new file mode 100644 index 0000000..35aa1d1 --- /dev/null +++ b/src/scripts/index.js @@ -0,0 +1,86 @@ +import Game from './game.js' +import { Router } from './router.js' +import { preloadEveryThing } from './preloader.js' +import { mainMenuLevel } from './levelList.js' + +const router = new Router() +const titleBack = document.getElementById("title-back") +let game + +document.addEventListener('DOMContentLoaded', async () => { + await preloadEveryThing() + init() +}) + +async function init() { + game = new Game() + router.showPage("mainMenu") + await loadMenu() + mainRenderLoop() + document.getElementById("custom-level-launch").addEventListener("click", () => setCustomLevel()) + document.getElementById("custom-level-back").addEventListener("click", () => router.showPage("mainMenu")) + document.getElementById("game-over-back").addEventListener("click", () => router.showPage("mainMenu")) +} + +async function mainRenderLoop () { + game.player.analyse() + + if (router.currentPage === "mainMenu") { + const valueBass = game.player.getRangeAverageRatio(0 ,10) + const scaleBass = (1.25 - 1) * valueBass + titleBack.style.transform = `scale(${1 + scaleBass}) translateZ(0)` + } + + if (game.currentLevel) { + const value = game.player.getRangeAverageRatio(game.currentLevel.startingValue, game.currentLevel.nbValue) + if (value > game.currentLevel.threshold) { + game.spawnMeteor() + } + + if (game.health <= 0 && router.currentPage === "mainGame") { + game.player.stop() + game = new Game() + game.setGameCanvas(false) + await game.loadMainMenu(mainMenuLevel) + router.showPage("gameOver") + } + + game.render() + } + + requestAnimationFrame(mainRenderLoop, game.canvas) +} + +async function loadMenu() { + const itemTemplate = document.getElementById("menuItem"); + const itemCustomTemplate = document.getElementById("menuItemCustom"); + + const menuContainer = document.getElementById("menuContainer"); + + for (const level of game.levelList) { + const menuItem = document.importNode(itemTemplate.content, true) + + menuItem.querySelector("img").src = level.bossPath + menuItem.querySelector("p").textContent = level.title + menuItem.querySelector("button").addEventListener("click", () => loadLevel(level)) + + menuContainer.appendChild(menuItem) + } + + const customItem = document.importNode(itemCustomTemplate.content, true) + customItem.querySelector("button").addEventListener("click", () => router.showPage("customLevel")) + menuContainer.appendChild(customItem) + + game.setGameCanvas(false) + await game.loadMainMenu(mainMenuLevel) +} + +function loadLevel(level) { + router.showPage("mainGame") + game.setGameCanvas(true) + game.loadLevel(level) +} + +function setCustomLevel() { + console.log('custom level') +} diff --git a/src/scripts/level.js b/src/scripts/level.js new file mode 100644 index 0000000..54baca4 --- /dev/null +++ b/src/scripts/level.js @@ -0,0 +1,32 @@ +import { preloadImage } from "./preloader.js" + +export class Level { + constructor(title, subtitle, maxHealth, threshold, startingValue, nbValue, musicPath, meteorPath, bossPath) { + this.title = title + this.subtitle = subtitle + this.maxHealth = maxHealth + this.startingValue = startingValue + this.nbValue = nbValue + this.threshold = threshold + this.musicPath = musicPath + this.meteorPath = meteorPath + this.bossPath = bossPath + } + + async loadLevel() { + if (this.meteorPath) { + this.meteorImg = await preloadImage(this.meteorPath) + console.log('load custom meteor', this.meteorImg) + } else { + this.meteorImgList = [] + this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor.png")) + this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_2.png")) + this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_3.png")) + this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_4.png")) + console.log('load meteor list', this.meteorImgList) + } + + this.bossImg = await preloadImage(this.bossPath) + } + +} \ No newline at end of file diff --git a/src/scripts/levelList.js b/src/scripts/levelList.js new file mode 100644 index 0000000..ac9c463 --- /dev/null +++ b/src/scripts/levelList.js @@ -0,0 +1,18 @@ +import { Level } from "./level.js" + +// Freq settings +// 0 ,10 bass +// 150, 40 med +// 400, 200 high + +const levelList = [ + new Level('Great Master', 'Bearer of the thousands', 20, 0.65, 0, 40,'assets/music/The Scene Is Dead (MASTER BOOT RECORD Remix).mp3', 'assets/img/meteors/diploma.png', 'assets/img/boss/r4.png'), + new Level('High Tech', 'Comin from above', 20, 0.65, 150, 40,'assets/music/ABIS & Signal & Tasha Baxter - The Wall (Buunshin Remix).flac', 'assets/img/meteors/plane_A.png', 'assets/img/boss/e4.png'), + new Level('Richard', '', 25, 0.75, 150, 40, 'assets/music/El Tigr3 - Black Star.flac', null, 'assets/img/boss/richard.png'), + new Level('V', '', 30, 0.75, 150, 40, 'assets/music/The Only Thing They Fear is V.flac', null, 'assets/img/boss/samurai.png'), + new Level('The End', 'Final attack', 40, 0.75, 90, 40, 'assets/music/The City Must Survive.flac', null, 'assets/img/meteors/meteor.png'), +] + +const mainMenuLevel = new Level('MainMenu', '', 1, 0.95, 150, 40, 'assets/music/nightcall_8bit.mp3', null, 'assets/img/meteors/meteor.png') + +export { levelList, mainMenuLevel } \ No newline at end of file diff --git a/src/scripts/player.js b/src/scripts/player.js new file mode 100644 index 0000000..43fcee4 --- /dev/null +++ b/src/scripts/player.js @@ -0,0 +1,86 @@ +export default class Player { + constructor() { + this.audioContext = new window.AudioContext() + this.gain = this.audioContext.createGain() + this.audio = {} + this.source = {} + + this.analyser = this.audioContext.createAnalyser() + this.analyser.fftSize = 2048 + + this.startingScale = 0 + this.pulseRatio = 1 + this.maxValueHistory = 100 + this.hzHistory = [] + this.frequences = new Uint8Array(this.analyser.frequencyBinCount) + } + + createSourceFromAudioElement(audioElement) { + const source = this.audioContext.createMediaElementSource(audioElement) + return source + } + + connectSource(source) { + source.connect(this.gain) + this.gain.connect(this.analyser) + this.analyser.connect(this.audioContext.destination) + this.audio.addEventListener('ended', this.stop) + } + + setMusic(trackUrl) { + this.audio = new Audio(trackUrl) + this.source = this.createSourceFromAudioElement(this.audio) + this.connectSource(this.source) + } + + start() { + if (this.audioContext.state === 'suspended') { + this.audioContext.resume() + .then(() => this.audio.play()) + } else { + this.audio.play() + } + } + + stop() { + this.audio?.pause() + } + + analyse() { + this.analyser.getByteFrequencyData(this.frequences) + for (let i = 0; i < this.frequences.length; i++) { + if (!this.hzHistory[i]) { + this.hzHistory[i] = [] + } + if (this.hzHistory[i].length > this.maxValueHistory) { + this.hzHistory[i].shift() + } + this.hzHistory[i].push(this.frequences[i]) + } + } + + getRangeAverageRatio(startingValue, nbValue) { + let total = 0 + for (let i = startingValue; i < nbValue + startingValue; i++) { + total += this.getFrequenceRatio(i) + } + return total / nbValue + } + + getFrequenceRatio(index) { + let min = 255 + let max = 0 + this.hzHistory[index].forEach(value => { + if (value < min) { + min = value + } + if (value > max) { + max = value + } + }) + const scale = max - min + const actualValue = this.frequences[index] - min + const percentage = scale === 0 ? 0 : actualValue / scale + return this.startingScale + this.pulseRatio * percentage + } +} \ No newline at end of file diff --git a/src/scripts/preloader.js b/src/scripts/preloader.js new file mode 100644 index 0000000..551dc74 --- /dev/null +++ b/src/scripts/preloader.js @@ -0,0 +1,24 @@ +import { levelList, mainMenuLevel } from "./levelList.js" + +function preloadImage(path) { + return new Promise(resolve => { + const image = new Image() + image.src = path + image.onerror = (e) => { + console.error('fuk', path, e) + resolve() + } + image.onload = resolve(image) + }) +} + +async function preloadEveryThing() { + for (const level of levelList) { + await level.loadLevel() + console.log('Preloaded level:', level.title) + } + await mainMenuLevel.loadLevel() + console.log('Preloaded everything') +} + +export { preloadImage, preloadEveryThing } \ No newline at end of file diff --git a/src/scripts/renderer.js b/src/scripts/renderer.js new file mode 100644 index 0000000..0cb006b --- /dev/null +++ b/src/scripts/renderer.js @@ -0,0 +1,218 @@ +import { Explosion } from "./gameObjects.js" +import { preloadImage } from "./preloader.js" +import SoundEffect from "./soundEffect.js" + +export class Renderer { + constructor(canvas, backgroundColor) { + this.canvas = canvas + this.context = this.canvas.getContext("2d", { alpha: false }) + this.meteors = [] + this.turrets = [] + this.bullets = [] + this.effects = [] + this.mouseX = -50 + this.mouseY = -50 + this.lastFrame = 0 + this.backgroundColor = backgroundColor + this.bossImg = new Image() + this.bossY = 0 + + this.showStats = false + this.showCollisionBox = false + + this.loadSprites() + } + + async loadSprites() { + this.explosionImg = await preloadImage("assets/img/effects/explosion.png") + } + + renderGameObjectSimple(gameObject) { + this.context.save() + this.context.translate(gameObject.x, gameObject.y) + this.context.rotate(gameObject.angle - Math.PI / 2) + this.context.drawImage(gameObject.sprite, 0, 0, 20, 20) + this.context.restore() + } + + renderGameObject(gameObject) { + let destroy = false + let posX = gameObject.x + let posY = gameObject.y + + const applyRotation = gameObject.angle !== 0 + if (applyRotation) { + this.context.save() + const gameObjectCenterX = gameObject.x + const gameObjectCenterY = gameObject.y + this.context.translate(gameObjectCenterX, gameObjectCenterY) + this.context.rotate(gameObject.angle + Math.PI / 2) + this.context.translate(-gameObject.sizeX / 2, -gameObject.sizeY) + posX = 0 + posY = 0 + // console.log(gameObject.sizeX, gameObject.sizeY, gameObjectCenterX, gameObjectCenterY, gameObject.angle) + } + + if (gameObject.isAnimated) { + const frameX = gameObject.heightX * (gameObject.framesCounter % 8) + const frameY = gameObject.heightY * Math.floor(gameObject.framesCounter / 8) + this.context.drawImage(gameObject.sprite, frameX, frameY, gameObject.heightX, gameObject.heightY, posX, posY, gameObject.sizeX, gameObject.sizeY) + + if (gameObject.animationRunning) { + gameObject.framesCounter++ + if (gameObject.framesCounter === gameObject.frames) { + if (gameObject.removeAfterAnimation) { + destroy = true + } else { + gameObject.framesCounter = 0 + } + } + } + + } else { + this.context.drawImage(gameObject.sprite, posX, posY, gameObject.sizeX, gameObject.sizeY) + } + + if (applyRotation) { + this.context.restore() + } + + if (this.showCollisionBox) { + this.renderCollisionBox(gameObject) + } + + return destroy + } + + renderBackground() { + this.context.fillStyle = this.backgroundColor + this.context.fillRect(0, 0, this.canvas.width, this.canvas.height) + this.context.fill() + } + + renderDebug(startTime, deltaTime) { + const endTime = performance.now() + const fps = Math.round(1 / (deltaTime / 1000)) + + this.context.font = '25px serif'; + this.context.fillStyle = 'black' + this.context.textAlign = 'start' + this.context.fillText(`FPS: ${fps}`, 10, 50) + this.context.fillText(`Delta: ${deltaTime}`, 10, 75) + this.context.fillText(`Frame time: ${endTime - startTime}`, 10, 100) + this.context.fillText(`Meteors: ${this.meteors.length}`, 10, 125) + this.context.fillText(`Bullets: ${this.bullets.length}`, 10, 150) + this.context.fillText(`Mouse x: ${this.mouseX} y:${this.mouseY}`, 10, 175) + } + + renderTarget() { + this.context.beginPath() + this.context.arc(this.mouseX, this.mouseY, 25, 0, 2 * Math.PI, false) + this.context.lineWidth = 1 + this.context.strokeStyle = 'red' + this.context.stroke() + } + + renderCollisionBox(gameObject) { + this.context.strokeRect(gameObject.x - gameObject.sizeX, gameObject.y - gameObject.sizeY, gameObject.sizeX * 2, gameObject.sizeY * 2) + } + + renderMeteorLoop(deltaTime) { + let damageTaken = 0 + for (let i = this.meteors.length - 1; i >= 0; i--) { + const meteor = this.meteors[i] + + meteor.step(deltaTime) + + if (meteor.y > this.canvas.height) { + damageTaken += 1 + this.meteors.splice(i, 1) + } else if(meteor.x < 0 || meteor.x > this.canvas.width) { + this.meteors.splice(i, 1) + } else if (meteor.health <= 0) { + SoundEffect.play(SoundEffect.explosion) + this.effects.push(new Explosion(meteor.x, meteor.y, this.explosionImg)) + this.meteors.splice(i, 1) + } else if (this.renderGameObject(meteor)) { + this.meteors.splice(i, 1) + } + } + + return damageTaken + } + + renderBulletLoop(deltaTime) { + for (let i = this.bullets.length - 1; i >= 0; i--) { + const bullet = this.bullets[i] + let collision = false + + bullet.step(deltaTime) + + if ( + bullet.y > this.canvas.height || bullet.y < 0 || bullet.x > this.canvas.width || bullet.x < 0 + ) { + this.bullets.splice(i, 1) + } else { + + for (let j = this.meteors.length - 1; j >= 0; j--) { + const meteor = this.meteors[j] + collision = bullet.collideWith(meteor) + if (collision) { + this.bullets.splice(i, 1) + meteor.health -= bullet.damage + break; + } + } + + if (!collision) { + this.renderGameObject(bullet) + } + } + } + } + + renderTurretLoop() { + for (const turret of this.turrets) { + this.renderGameObject(turret) + } + } + + renderEffectLoop(deltaTime) { + for (let i = this.effects.length - 1; i >= 0; i--) { + const effect = this.effects[i] + + effect.step(deltaTime) + + if (this.renderGameObject(effect)) { + this.effects.splice(i, 1) + } + } + } + + renderSun() { + this.context.drawImage(this.bossImg, this.canvas.width / 2, (this.canvas.height + 250) - this.bossY, 100, 100) + } + + render() { + const startTime = performance.now() + const deltaTime = startTime - this.lastFrame + + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) + this.renderBackground() + this.renderSun() + + const damageTaken = this.renderMeteorLoop(deltaTime) + this.renderBulletLoop(deltaTime) + this.renderTurretLoop() + this.renderEffectLoop(deltaTime) + + this.renderTarget() + + if (this.showStats) { + this.renderDebug(startTime, deltaTime) + } + + this.lastFrame = startTime + return damageTaken + } +} \ No newline at end of file diff --git a/src/scripts/router.js b/src/scripts/router.js new file mode 100644 index 0000000..dde76c0 --- /dev/null +++ b/src/scripts/router.js @@ -0,0 +1,23 @@ +export class Router { + currentPage = "none" + + mainMenu = document.getElementById("main-menu") + customLevel = document.getElementById("custom-level") + loading = document.getElementById("loading") + mainGame = document.getElementById("main-game") + gameOver = document.getElementById("game-over") + + hideEverything() { + this.mainMenu.style.display = "none" + this.customLevel.style.display = "none" + this.loading.style.display = "none" + this.mainGame.style.display = "none" + this.gameOver.style.display = "none" + } + + showPage(pageName) { + this.hideEverything() + this.currentPage = pageName + this[pageName].style.display = "flex" + } +} \ No newline at end of file diff --git a/src/scripts/soundEffect.js b/src/scripts/soundEffect.js new file mode 100644 index 0000000..e00f96c --- /dev/null +++ b/src/scripts/soundEffect.js @@ -0,0 +1,31 @@ +export default class SoundEffect { + static turretLaser = new Audio("assets/sounds/turret_laser.mp3") + static turretFast = new Audio("assets/sounds/turret_fast.mp3") + static turretStandard = new Audio("assets/sounds/turret_standard.mp3") + static turretHeavy = new Audio("assets/sounds/turret_heavy.flac") + static explosion = new Audio("assets/sounds/explosion.mp3") + static explosionBig = new Audio("assets/sounds/explosion_big.mp3") + static turretRelease = new Audio("assets/sounds/turret_release.wav") + + static soundList = [ + SoundEffect.turretLaser, + SoundEffect.turretFast, + SoundEffect.turretStandard, + SoundEffect.turretHeavy, + SoundEffect.explosion, + SoundEffect.explosionBig, + SoundEffect.turretRelease + ] + + static setEffectVolume(volume) { + SoundEffect.soundList.forEach((sound) => sound.volume = volume) + } + + static stopAll() { + SoundEffect.soundList.forEach((sound) => sound.pause()) + } + + static play(audio) { + audio.paused ? audio.play() : audio.currentTime = 0 + } +} \ No newline at end of file diff --git a/src/scripts/turretTypes.js b/src/scripts/turretTypes.js new file mode 100644 index 0000000..800e9a5 --- /dev/null +++ b/src/scripts/turretTypes.js @@ -0,0 +1,68 @@ +import { preloadImage } from "./preloader.js" +import SoundEffect from "./soundEffect.js" + +async function setStandardTurret(turret) { + turret.spriteImg= await preloadImage("assets/img/turrets/turret_standard.png") + turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_standard.png") + turret.firingRate = 100 + turret.lastShot = 0 + turret.spread = 100 + turret.damage = 20 + turret.isAnimated = true + turret.removeAfterAnimation = false + turret.animationRunning = false + turret.frames = 8 + turret.heightX = 256 + turret.heightY = 256 + turret.sound = SoundEffect.turretStandard +} + +async function setFastTurret(turret) { + turret.spriteImg= await preloadImage("assets/img/turrets/turret_fast.png") + turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_fast.png") + turret.firingRate = 20 + turret.lastShot = 0 + turret.spread = 200 + turret.damage = 10 + turret.isAnimated = true + turret.removeAfterAnimation = false + turret.animationRunning = false + turret.frames = 8 + turret.heightX = 256 + turret.heightY = 256 + turret.sound = SoundEffect.turretFast +} + +async function setHeavyTurret(turret) { + turret.spriteImg= await preloadImage("assets/img/turrets/turret_heavy.png") + turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_heavy.png") + turret.firingRate = 200 + turret.lastShot = 0 + turret.spread = 50 + turret.damage = 30 + turret.isAnimated = true + turret.removeAfterAnimation = false + turret.animationRunning = false + turret.frames = 16 + turret.heightX = 256 + turret.heightY = 256 + turret.sound = SoundEffect.turretHeavy +} + +async function setLaserTurret(turret) { + turret.spriteImg= await preloadImage("assets/img/turrets/turret_laser.png") + turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_laser.png") + turret.firingRate = 0 + turret.lastShot = 0 + turret.spread = 10 + turret.damage = 2 + turret.isAnimated = true + turret.removeAfterAnimation = false + turret.animationRunning = false + turret.frames = 8 + turret.heightX = 256 + turret.heightY = 256 + turret.sound = SoundEffect.turretLaser +} + +export { setStandardTurret, setFastTurret, setHeavyTurret, setLaserTurret } \ No newline at end of file diff --git a/src/style/style.css b/src/style/style.css new file mode 100644 index 0000000..5340c7c --- /dev/null +++ b/src/style/style.css @@ -0,0 +1,237 @@ +@font-face { + font-family: 'ka'; + src: url('../assets/fonts/ka1.ttf'); +} + +@font-face { + font-family: 'revamped'; + src: url('../assets/fonts/Revamped.otf'); +} + +html, body { + font-family: 'ka'; + color: rgb(223, 223, 223); + height: 100%; + width: 100%; + margin: 0; + padding: 0; +} + +input { + border: 2px solid #24201e !important; + padding: 10px 15px; + font-size: 30px; + max-width: 100px; +} + +button { + color: #04ab96; + background-color: #153a3a; + transition: 0.25s; + border: 6px solid; + border-color: #368b8c; + border-radius: 10px; + font: inherit; + font-size: 30px; + line-height: 1; + margin: 0.5em; + padding: 10px 15px; +} + +button:hover { + box-shadow: inset 0 0 0 2.2em #24201e; + border-color: #24201e; + color: #fff; + cursor: pointer; +} + +button:focus { + box-shadow: inset 0 0 0 2.2em #461f1e; + border-color: #24201e; + color: #fff; +} + +button:disabled { + box-shadow: inset 0 0 0 2.2em #5a5a5a; + border-color: #24201e; + color: #fff; +} + +.background { + position: fixed; + width: 100%; + height: 100%; + z-index: -2; + /* filter: blur(5px); */ + background-color: #153a3a; +} + +.page { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.main-container { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.title-container { + display: grid; +} + +.title-back { + height: 400px; + width: 400px; + background-color: #246363; + border-radius: 50%; +} + +.title-back-container { + z-index: -1; + top: 0px; + left: 0px; + position: absolute; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.level-select { + display: flex; +} + +.level-item img { + height: 75px; +} + +.level-item p { + margin: 0px; +} + +.main-game { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: center; +} + +.main-game-canvas { + position: fixed; + width: 100%; + height: 100%; + background-color: #a9cdd2; +} + +.main-game-skyline-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: end; + width: 100%; +} + +.main-game-skyline { + background-image: url("../assets/img/skyline.png"); + background-repeat: repeat-x; + background-size: contain; + width: 100%; + height: 68px; + z-index: 1; +} + +.main-game-skyline-turret { + width: 1500px; + z-index: 1; +} + +.main-game-dashboard { + height: 80px; + width: 100%; + background-color: #264649; + z-index: 1; + display: flex; + justify-content: center; +} + +.main-game-dashboard-inner { + display: flex; + align-items: center; +} + +.main-game-dashboard-inner h2 { + margin: 30px; + margin-right: 55px; +} + +.main-game-dashboard-inner input { + margin-left: 55px; +} + +.main-game-dashboard-bullet { + max-height: 60px; + margin: 15px; + display: flex; + align-items: center; +} + +.main-game-dashboard-bullet img { + max-height: 60px; + margin: 10px; +} + +.main-game-turret-selector { + margin: 30px; + display: flex; + justify-content: center; + align-items: center; +} + +.main-game-turret-selector h2 { + margin: 0; + margin-right: 10px; +} + +.main-game-title { + display: none; + flex-direction: column; + align-items: center; + z-index: 1; + font-size: 30px; + margin-bottom: 500px; + color: black; + font-family: 'revamped'; + animation: 4s anim-lineUp ease-out; + transition: opacity 2s; +} + +.main-game-title h2 { + margin-top: 15px; +} + +@keyframes anim-lineUp { + 0% { + opacity: 0; + transform: translateY(80%); + } + 20% { + opacity: 0; + } + 50% { + opacity: 1; + transform: translateY(0%); + } + 100% { + opacity: 1; + transform: translateY(0%); + } +} \ No newline at end of file diff --git a/turret.svg b/turret.svg new file mode 100644 index 0000000..15fcb4b --- /dev/null +++ b/turret.svg @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +