From 0afdf05f0020beaacbd11df5579320260aeb7a07 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Tue, 26 Nov 2024 09:27:27 +0530 Subject: [PATCH 01/23] udpate: enemy class WIP --- src/enemy.js | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/enemy.js diff --git a/src/enemy.js b/src/enemy.js new file mode 100644 index 0000000..58a20d6 --- /dev/null +++ b/src/enemy.js @@ -0,0 +1,3 @@ +class Enemy { + +} From ec63bb669f41ca430a64e0d5c35aef4a4c53f149 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Tue, 26 Nov 2024 09:32:42 +0530 Subject: [PATCH 02/23] update: git town config --- .git-branches.toml | 103 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 .git-branches.toml diff --git a/.git-branches.toml b/.git-branches.toml new file mode 100644 index 0000000..4a4a0b0 --- /dev/null +++ b/.git-branches.toml @@ -0,0 +1,103 @@ +# Git Town configuration file +# +# Run "git town config setup" to add additional entries +# to this file after updating Git Town. +# +# The "push-hook" setting determines whether Git Town +# permits or prevents Git hooks while pushing branches. +# Hooks are enabled by default. If your Git hooks are slow, +# you can disable them to speed up branch syncing. +# +# When disabled, Git Town pushes using the "--no-verify" switch. +# More info at https://www.git-town.com/preferences/push-hook. +push-hook = true + +# Should Git Town push the new branches it creates +# immediately to origin even if they are empty? +# +# When enabled, you can run "git push" right away +# but creating new branches is slower and +# it triggers an unnecessary CI run on the empty branch. +# +# When disabled, many Git Town commands execute faster +# and Git Town will create the missing tracking branch +# on the first run of "git sync". +push-new-branches = true + +# Should "git ship" delete the tracking branch? +# You want to disable this if your code hosting platform +# (GitHub, GitLab, etc) deletes head branches when +# merging pull requests through its UI. +ship-delete-tracking-branch = false + +# Should "git ship" sync branches before shipping them? +# +# Guidance: enable when shipping branches locally on your machine +# and disable when shipping feature branches via the code hosting +# API or web UI. +# +# When enabled, branches are always fully up to date when shipped +# and you get a chance to resolve merge conflicts +# between the feature branch to ship and the main development branch +# on the feature branch. This helps keep the main branch green. +# But this also triggers another CI run and delays shipping. +sync-before-ship = false + +# Should "git sync" also fetch updates from the upstream remote? +# +# If an "upstream" remote exists, and this setting is enabled, +# "git sync" will also update the local main branch +# with commits from the main branch at the upstream remote. +# +# This is useful if the repository you work on is a fork, +# and you want to keep it in sync with the repo it was forked from. +sync-upstream = false + +[branches] + +# The main branch is the branch from which you cut new feature branches, +# and into which you ship feature branches when they are done. +# This branch is often called "main", "master", or "development". +main = "main" + +# Perennial branches are long-lived branches. +# They are never shipped and have no ancestors. +# Typically, perennial branches have names like +# "development", "staging", "qa", "production", etc. +# +# See also the "perennial-regex" setting. +perennials = ["release"] + +# All branches whose names match this regular expression +# are also considered perennial branches. +# +# If you are not sure, leave this empty. +perennial-regex = "" + +[hosting] + +# Knowing the type of code hosting platform allows Git Town +# to open browser URLs and talk to the code hosting API. +# Most people can leave this on "auto-detect". +# Only change this if your code hosting server uses as custom URL. +platform = "github" + +# When using SSH identities, define the hostname +# of your source code repository. Only change this +# if the auto-detection does not work for you. +# origin-hostname = "" + +[sync-strategy] + +# How should Git Town synchronize feature branches? +# Feature branches are short-lived branches cut from +# the main branch and shipped back into the main branch. +# Typically you develop features and bug fixes on them, +# hence their name. +feature-branches = "merge" + +# How should Git Town synchronize perennial branches? +# Perennial branches have no parent branch. +# The only updates they receive are additional commits +# made to their tracking branch somewhere else. +perennial-branches = "merge" From 5cb5b09228879375cac8b953958052f94cea435a Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Tue, 26 Nov 2024 10:29:24 +0530 Subject: [PATCH 03/23] refactor: comments for the script file --- script.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/script.js b/script.js index cdaaadf..b7a0f49 100644 --- a/script.js +++ b/script.js @@ -1,6 +1,10 @@ import StateManager from './src/stateManager.js' +// once all the HTML, CSS and other assets have loaded, +// we start with the JS init. window.addEventListener("load", function() { + + // get reference to the canvas object defined in the html page const canvas = this.document.getElementById('canvas1'); const ctx = canvas.getContext("2d"); canvas.width = 600; @@ -8,14 +12,14 @@ window.addEventListener("load", function() { const stateManager = new StateManager(canvas); - - // clear rect and paint the whole scene again function animate() { + // clear rect and paint the whole scene again ctx.clearRect(0, 0, canvas.width, canvas.height) stateManager.render(ctx); + // recursively calls this function to re-draw the updated components window.requestAnimationFrame(animate); } + // start the recursive loop for animation animate(); -} -) +}) From d6c8031af64a48e5a62ef55ceda343be6db561d0 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Tue, 26 Nov 2024 11:22:43 +0530 Subject: [PATCH 04/23] update: commments in state manager --- src/stateManager.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stateManager.js b/src/stateManager.js index 78a739d..1d20657 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -16,8 +16,10 @@ class StateManager { // keyboard event listeners - // when key is pushed down, it is added to active key list - // we also need to make sure no duplicate keys are present + // when key is pushed down and it is not already present in the active key list + // it is added to active key list + // This is done because if you hold down the key, then it will keep firing the + // same keydown event for the key which we don't want window.addEventListener('keydown', (e) => { if (!this.activeKeys.includes(e.key)) { this.activeKeys.push(e.key); From 72234c9365c22bfeee74972a850c065872c5bc39 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Wed, 27 Nov 2024 12:14:16 +0530 Subject: [PATCH 05/23] update: add background image --- assets/background.png | Bin 0 -> 60164 bytes style.css | 1 + 2 files changed, 1 insertion(+) create mode 100644 assets/background.png diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000000000000000000000000000000000000..d92dc12cb75f97271f967c11861616e02094ee9f GIT binary patch literal 60164 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

&n7QjCO4^X&(PQ?)qXm`3>6YxJ6_$S(M1cY^* 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 literal 0 HcmV?d00001 diff --git a/style.css b/style.css index 1fefb02..bfdc010 100644 --- a/style.css +++ b/style.css @@ -4,4 +4,5 @@ top: 50%; left: 50%; translate: -50% -50%; + background: url("assets/background.png"); } From b5f2366461f09544a69fad6914d2c57ac603fe3f Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Wed, 27 Nov 2024 12:14:44 +0530 Subject: [PATCH 06/23] update: gitignore added --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store From 99c650b6a83fa7e07348aa66606fdfccbfd9f966 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Wed, 27 Nov 2024 12:14:50 +0530 Subject: [PATCH 07/23] update: css cahnges --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index b7a0f49..250b8a3 100644 --- a/script.js +++ b/script.js @@ -9,6 +9,7 @@ window.addEventListener("load", function() { const ctx = canvas.getContext("2d"); canvas.width = 600; canvas.height = 800; + ctx.fillStyle = "white"; const stateManager = new StateManager(canvas); From 96a075db2bedc3078d0461dbb1ec3a50fcbff3e3 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Thu, 28 Nov 2024 22:58:10 +0530 Subject: [PATCH 08/23] update: css styles of the canvas --- script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script.js b/script.js index 250b8a3..0d56a06 100644 --- a/script.js +++ b/script.js @@ -10,6 +10,8 @@ window.addEventListener("load", function() { canvas.width = 600; canvas.height = 800; ctx.fillStyle = "white"; + ctx.strokeStyle = "white"; + ctx.lineWidth = 5; const stateManager = new StateManager(canvas); From e51b115d71891dce912c235ee151a37f41ba80b1 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Thu, 28 Nov 2024 22:58:22 +0530 Subject: [PATCH 09/23] update: create the enemy class --- src/enemy.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/enemy.js b/src/enemy.js index 58a20d6..715ce4a 100644 --- a/src/enemy.js +++ b/src/enemy.js @@ -1,3 +1,25 @@ class Enemy { + constructor(stateManager, offsetInRaidX, offsetInRaidY) { + this.stateManager = stateManager; + this.enemyPosX = 0; + this.enemyPosY = 0; + + // the raid moves in a grid, the offset defines where the enemy is in the raid relative + // to the raid's x and y position + this.offsetInRaidX = offsetInRaidX; + this.offsetInRaidY = offsetInRaidY; + } + + render(context) { + context.strokeRect(this.enemyPosX, this.enemyPosY, this.stateManager.enemySize, this.stateManager.enemySize); + } + + // as the raid moves, add the offset to the enemy position and move it to + progress(raidPosX, raidPosY) { + this.enemyPosX = raidPosX + this.offsetInRaidX; + this.enemyPosY = raidPosY + this.offsetInRaidY; + } } + +export default Enemy; From 3fcfde6105ba8c8c2247550ae47fe5d833f6b830 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Thu, 28 Nov 2024 22:58:33 +0530 Subject: [PATCH 10/23] update: raid class to be grid of enemies --- src/raid.js | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/raid.js diff --git a/src/raid.js b/src/raid.js new file mode 100644 index 0000000..2ae1b82 --- /dev/null +++ b/src/raid.js @@ -0,0 +1,58 @@ +import Enemy from './enemy.js'; + +class Raid { + constructor(stateManager) { + this.stateManager = stateManager; + + this.height = this.stateManager.enemyRaidGridRows * this.stateManager.enemySize; + this.width = this.stateManager.enemyRaidGridColumns * this.stateManager.enemySize; + + this.raidPosX = 0; + // start the raid above the screen + this.raidPosY = -this.height; + + this.speedX = 3; + this.speedY = 0; + + this.enemies = []; + this.createEnemyRaid(); + } + + createEnemyRaid() { + for (let y = 0; y < this.stateManager.enemyRaidGridRows; y++) { + for (let x = 0; x < this.stateManager.enemyRaidGridColumns; x++) { + this.enemies.push(new Enemy(this.stateManager, x * this.stateManager.enemySize, y * this.stateManager.enemySize)); + } + } + } + + render(context) { + if (this.raidPosY < 0) { + // if the raid has just spawned above the screen, float it down + this.speedY = 5; + } else { + // by default keep the vertical speed as 0 and update only when the horizontal limit is touched + this.speedY = 0; + } + + // horizontal boundary check, if true, reverse the horizontal speed and increase the vertical speed by the height of the enemy + if (this.raidPosX > this.stateManager.width - this.width || this.raidPosX < 0) { + this.speedX *= -1; + this.speedY = this.stateManager.enemySize; + } + + // udpate position of the wave + this.raidPosX += this.speedX; + this.raidPosY += this.speedY; + + // update the position of the enemies in the wave relative to the wave position + this.enemies.forEach(enemy => { + enemy.progress(this.raidPosX, this.raidPosY); + enemy.render(context); + }) + } + + progress() { + } +} +export default Raid; From d3f17815d4a4b2180fbbeed2c6d2295f7376a89a Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Thu, 28 Nov 2024 22:58:49 +0530 Subject: [PATCH 11/23] update: show raids in the statemanager --- src/stateManager.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/stateManager.js b/src/stateManager.js index 1d20657..dbf418f 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -1,5 +1,6 @@ import Player from './player.js'; import Ammo from './ammo.js'; +import Raid from './raid.js'; class StateManager { constructor(canvas) { @@ -8,13 +9,21 @@ class StateManager { this.height = this.canvas.height; this.player = new Player(this); + // Ammunition pool this.availableAmmoPool = []; this.maxAmmo = 15; this.createAmmoPool(); - this.activeKeys = []; + // Enemy wave control + this.enemySize = 60; + this.enemyRaidGridColumns = 3; + this.enemyRaidGridRows = 3; + + this.raids = []; + this.startEnemyRaid(); // keyboard event listeners + this.activeKeys = []; // when key is pushed down and it is not already present in the active key list // it is added to active key list @@ -47,6 +56,10 @@ class StateManager { return this.availableAmmoPool.find((ammo) => ammo.free); } + startEnemyRaid() { + this.raids.push(new Raid(this)); + } + render(context) { this.player.render(context); this.player.update(); @@ -54,6 +67,10 @@ class StateManager { ammo.progress() ammo.render(context) }) + this.raids.forEach(wave => { + wave.progress(); + wave.render(context); + }) } } From c8a1c122e434ddc98d5b4e9aebb8bfebb9fd2541 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Thu, 28 Nov 2024 22:59:59 +0530 Subject: [PATCH 12/23] update: remove unsused method and call --- src/raid.js | 3 --- src/stateManager.js | 5 ++--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/raid.js b/src/raid.js index 2ae1b82..6b49078 100644 --- a/src/raid.js +++ b/src/raid.js @@ -51,8 +51,5 @@ class Raid { enemy.render(context); }) } - - progress() { - } } export default Raid; diff --git a/src/stateManager.js b/src/stateManager.js index dbf418f..f21b9b0 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -67,9 +67,8 @@ class StateManager { ammo.progress() ammo.render(context) }) - this.raids.forEach(wave => { - wave.progress(); - wave.render(context); + this.raids.forEach(raid => { + raid.render(context); }) } } From 2b3e08b4de486e35feaf7863a160bbe1f8609521 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Fri, 13 Dec 2024 22:18:04 +0700 Subject: [PATCH 13/23] update: add simple rect collision detection --- src/stateManager.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/stateManager.js b/src/stateManager.js index f21b9b0..09347b9 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -60,6 +60,16 @@ class StateManager { this.raids.push(new Raid(this)); } + checkForAmmoHit(ammo, enemy) { + // using the 2d collision check for this + // https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection + return (ammo.x < enemy.enemyPosX + this.enemySize && + ammo.x + ammo.width > enemy.enemyPosX && + ammo.y < enemy.enemyPosY + this.enemySize && + ammo.y * ammo.height > enemy.enemyPosY + ); + } + render(context) { this.player.render(context); this.player.update(); From ec06e5530cb902216fe275d1bded09728aafec78 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Fri, 13 Dec 2024 22:18:50 +0700 Subject: [PATCH 14/23] update: remove enemy if shot --- src/raid.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/raid.js b/src/raid.js index 6b49078..b771667 100644 --- a/src/raid.js +++ b/src/raid.js @@ -50,6 +50,9 @@ class Raid { enemy.progress(this.raidPosX, this.raidPosY); enemy.render(context); }) + + // check if the enemy is shot + this.enemies = this.enemies.filter(enemy => !enemy.hitByAmmo); } } export default Raid; From 8a8a272ab9b2e87426ae72a2df28f7168cc7e64b Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Fri, 13 Dec 2024 22:19:07 +0700 Subject: [PATCH 15/23] update: check for hit from ammo inside enemy class --- src/enemy.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/enemy.js b/src/enemy.js index 715ce4a..9c2d8c6 100644 --- a/src/enemy.js +++ b/src/enemy.js @@ -9,6 +9,8 @@ class Enemy { // to the raid's x and y position this.offsetInRaidX = offsetInRaidX; this.offsetInRaidY = offsetInRaidY; + + this.hitByAmmo = false; } render(context) { @@ -19,6 +21,14 @@ class Enemy { progress(raidPosX, raidPosY) { this.enemyPosX = raidPosX + this.offsetInRaidX; this.enemyPosY = raidPosY + this.offsetInRaidY; + + // check for collisions + this.stateManager.availableAmmoPool.forEach(ammo => { + if (!ammo.free && this.stateManager.checkForAmmoHit(ammo, this)) { + this.hitByAmmo = true; + ammo.reset(); + } + }) } } From 16e2fadc24a09b0469e678681475636bd97c3cd5 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:10:25 +0700 Subject: [PATCH 16/23] udpate: style for game scores --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 0d56a06..e990f1a 100644 --- a/script.js +++ b/script.js @@ -12,6 +12,7 @@ window.addEventListener("load", function() { ctx.fillStyle = "white"; ctx.strokeStyle = "white"; ctx.lineWidth = 5; + ctx.font = "20px Impact"; const stateManager = new StateManager(canvas); From de1ca3cde4db0d207f3db3040a1a4a2e08a6b8b8 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:10:54 +0700 Subject: [PATCH 17/23] update: add segment for hits and health of enemy --- src/enemy.js | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/enemy.js b/src/enemy.js index 9c2d8c6..3eefa30 100644 --- a/src/enemy.js +++ b/src/enemy.js @@ -5,12 +5,14 @@ class Enemy { this.enemyPosX = 0; this.enemyPosY = 0; + // enemy hits and health + this.hitCounter = 0; + this.healthPoints = 1; + // the raid moves in a grid, the offset defines where the enemy is in the raid relative // to the raid's x and y position this.offsetInRaidX = offsetInRaidX; this.offsetInRaidY = offsetInRaidY; - - this.hitByAmmo = false; } render(context) { @@ -22,13 +24,31 @@ class Enemy { this.enemyPosX = raidPosX + this.offsetInRaidX; this.enemyPosY = raidPosY + this.offsetInRaidY; - // check for collisions - this.stateManager.availableAmmoPool.forEach(ammo => { - if (!ammo.free && this.stateManager.checkForAmmoHit(ammo, this)) { - this.hitByAmmo = true; - ammo.reset(); + // check if an enemy has crossed the lower bounds of the game + // if yes, finish the game + if (this.enemyPosY + this.stateManager.enemySize > this.stateManager.height) { + this.stateManager.gameOver = true; + } + + // if the game is still going, check for enemy collisions + // with player and active ammunitions + if (!this.stateManager.gameOver) { + // check for collisions + this.stateManager.availableAmmoPool.forEach(ammo => { + if (!ammo.free && this.stateManager.checkEnemyCollision(this, ammo)) { + this.hitCounter++; + ammo.reset(); + if (!this.stateManager.gameOver) + this.stateManager.score++; + } + }) + + // check if enemy hit the player + if (this.stateManager.checkEnemyCollision(this, this.stateManager.player)) { + this.stateManager.playerLives--; + this.hitCounter++; } - }) + } } } From c7d011ca5b9d70fdc84ad704df72bce50e464a01 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:11:16 +0700 Subject: [PATCH 18/23] update: system to destroy a wave and enemies --- src/raid.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/raid.js b/src/raid.js index b771667..da34eca 100644 --- a/src/raid.js +++ b/src/raid.js @@ -16,6 +16,7 @@ class Raid { this.enemies = []; this.createEnemyRaid(); + this.destroyed = false; } createEnemyRaid() { @@ -27,6 +28,9 @@ class Raid { } render(context) { + if (this.enemies.length <= 0) { + return; + } if (this.raidPosY < 0) { // if the raid has just spawned above the screen, float it down this.speedY = 5; @@ -52,7 +56,7 @@ class Raid { }) // check if the enemy is shot - this.enemies = this.enemies.filter(enemy => !enemy.hitByAmmo); + this.enemies = this.enemies.filter(enemy => enemy.hitCounter < enemy.healthPoints); } } export default Raid; From c2c0e24e9e3b41be5a4cf6487cb6a75b48984984 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:11:43 +0700 Subject: [PATCH 19/23] udpate: set scoreboard and lives of player --- src/stateManager.js | 78 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/src/stateManager.js b/src/stateManager.js index 09347b9..dd69f99 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -9,18 +9,27 @@ class StateManager { this.height = this.canvas.height; this.player = new Player(this); + // game state variables + this.playerLives = 3; // starting player with 3 lives + this.score = 0; + this.gameOver = false; + // Ammunition pool this.availableAmmoPool = []; this.maxAmmo = 15; - this.createAmmoPool(); // Enemy wave control this.enemySize = 60; - this.enemyRaidGridColumns = 3; - this.enemyRaidGridRows = 3; - + this.enemyRaidGridColumns = 2; + this.enemyRaidGridRows = 2; this.raids = []; + this.raidCount = 1; + this.newRaidSpawned = false; + + + // init functions this.startEnemyRaid(); + this.createAmmoPool(); // keyboard event listeners this.activeKeys = []; @@ -60,26 +69,77 @@ class StateManager { this.raids.push(new Raid(this)); } - checkForAmmoHit(ammo, enemy) { + createNewRaid() { + this.newRaidSpawned = true; + if (Math.random() < 0.5) { + this.enemyRaidGridColumns++; + } else { + this.enemyRaidGridRows++; + } + this.startEnemyRaid(); + this.raidCount++; + + // there's a small chance of player life increasing after wave finish + if (Math.random() < 0.1) { + this.playerLives++; + } + this.newRaidSpawned = false; + } + + // check if ammo has hit enemy or enemy has hit player + checkEnemyCollision(enemy, object) { // using the 2d collision check for this // https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection - return (ammo.x < enemy.enemyPosX + this.enemySize && - ammo.x + ammo.width > enemy.enemyPosX && - ammo.y < enemy.enemyPosY + this.enemySize && - ammo.y * ammo.height > enemy.enemyPosY + return (object.x < enemy.enemyPosX + this.enemySize && + object.x + object.width > enemy.enemyPosX && + object.y < enemy.enemyPosY + this.enemySize && + object.y * object.height > enemy.enemyPosY ); } render(context) { this.player.render(context); + this.player.update(); this.availableAmmoPool.forEach(ammo => { ammo.progress() ammo.render(context) }) this.raids.forEach(raid => { + if (raid.destroyed) { + return; + } raid.render(context); + + if (raid.enemies.length < 1 && !this.newRaidSpawned && !this.gameOver) { + raid.destroyed = true; + this.createNewRaid(); + } }) + + // check for player's lives + if (this.playerLives <= 0) { + this.gameOver = true; + this.playerLives = 0; + } + + // show game stats + context.save(); + context.fillText("Score: " + this.score, 10, 30); + context.fillText("Raid: " + this.raidCount, 10, 60); + context.fillText("Lives: ", 10, 90); + for (let i = 0; i < this.playerLives; i++) { + context.fillRect(53 + 10 * (i + 1), 75, 5, 15) + } + + if (this.gameOver) { + context.textAlign = "center"; + context.font = "100px Impact"; + context.fillText("Game Over", this.width * 0.5, this.height * 0.5); + context.font = "30px Impact"; + context.fillText("Press R to restart", this.width * 0.5, this.height * 0.5 + 40); + } + context.restore(); } } From 66f5122726851c67f85b275685f7b2e7dd82722f Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:16:42 +0700 Subject: [PATCH 20/23] update: small refactors --- src/ammo.js | 2 +- src/enemy.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ammo.js b/src/ammo.js index 587079e..0db9a5a 100644 --- a/src/ammo.js +++ b/src/ammo.js @@ -4,7 +4,7 @@ class Ammo { this.height = 20; this.x = 0; this.y = 0; - this.speed = 20; + this.speed = 30; this.free = true; this.stateManager = stateManager; } diff --git a/src/enemy.js b/src/enemy.js index 3eefa30..7151900 100644 --- a/src/enemy.js +++ b/src/enemy.js @@ -30,8 +30,7 @@ class Enemy { this.stateManager.gameOver = true; } - // if the game is still going, check for enemy collisions - // with player and active ammunitions + // if the game is still going, check for enemy collisions active ammunitions if (!this.stateManager.gameOver) { // check for collisions this.stateManager.availableAmmoPool.forEach(ammo => { @@ -43,11 +42,12 @@ class Enemy { } }) - // check if enemy hit the player - if (this.stateManager.checkEnemyCollision(this, this.stateManager.player)) { - this.stateManager.playerLives--; - this.hitCounter++; - } + } + + // check if enemy hit the player + if (this.stateManager.checkEnemyCollision(this, this.stateManager.player)) { + this.stateManager.playerLives--; + this.hitCounter++; } } } From 8ff745a64a762c1e57fcb98c4789c54485e65ca2 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:30:44 +0700 Subject: [PATCH 21/23] update: move player lives inside the player class --- src/player.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/player.js b/src/player.js index 445267e..f1f3e7b 100644 --- a/src/player.js +++ b/src/player.js @@ -4,15 +4,24 @@ class Player { this.stateManager = stateManager; this.width = 100; this.height = 100; + this.speed = 5; + + // variables + this.lives = 3; // starting player with 3 lives this.x = this.stateManager.width * 0.5 - this.width * 0.5; this.y = this.stateManager.height - this.height; - this.speed = 5; } render(context) { context.fillRect(this.x, this.y, this.width, this.height); } + reset() { + this.lives = 3; // starting player with 3 lives + this.x = this.stateManager.width * 0.5 - this.width * 0.5; + this.y = this.stateManager.height - this.height; + } + update() { // Handle intent to move left if (this.stateManager.activeKeys.includes('ArrowLeft') From 7010eeabdb5a8964f3996cd1207eafde99ce1334 Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:31:04 +0700 Subject: [PATCH 22/23] update: add restart option --- src/enemy.js | 2 +- src/stateManager.js | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/enemy.js b/src/enemy.js index 7151900..a6f466e 100644 --- a/src/enemy.js +++ b/src/enemy.js @@ -46,7 +46,7 @@ class Enemy { // check if enemy hit the player if (this.stateManager.checkEnemyCollision(this, this.stateManager.player)) { - this.stateManager.playerLives--; + this.stateManager.player.lives--; this.hitCounter++; } } diff --git a/src/stateManager.js b/src/stateManager.js index dd69f99..ec8ebf3 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -10,7 +10,6 @@ class StateManager { this.player = new Player(this); // game state variables - this.playerLives = 3; // starting player with 3 lives this.score = 0; this.gameOver = false; @@ -21,7 +20,7 @@ class StateManager { // Enemy wave control this.enemySize = 60; this.enemyRaidGridColumns = 2; - this.enemyRaidGridRows = 2; + this.enemyRaidGridRows = 12; this.raids = []; this.raidCount = 1; this.newRaidSpawned = false; @@ -47,6 +46,11 @@ class StateManager { if (e.key === 'w') { this.player.fireAmmo(); } + + // if user presses r and the game is over, restart the game + if (e.key === 'r' || this.gameOver) { + this.restartGame(); + } }) // remove key from the active key list when key is no longer pressed @@ -55,6 +59,21 @@ class StateManager { }) } + restartGame() { + this.player.reset(); + this.score = 0; + this.gameOver = false; + this.availableAmmoPool = []; + this.raids = []; + this.raidCount = 1; + this.enemyRaidGridColumns = 2; + this.enemyRaidGridRows = 2; + this.newRaidSpawned = false; + + this.startEnemyRaid(); + this.createAmmoPool(); + } + createAmmoPool() { for (let index = 0; index < this.maxAmmo; index++) { this.availableAmmoPool.push(new Ammo(this)); @@ -81,7 +100,7 @@ class StateManager { // there's a small chance of player life increasing after wave finish if (Math.random() < 0.1) { - this.playerLives++; + this.player.lives++; } this.newRaidSpawned = false; } @@ -118,9 +137,9 @@ class StateManager { }) // check for player's lives - if (this.playerLives <= 0) { + if (this.player.lives <= 0) { this.gameOver = true; - this.playerLives = 0; + this.player.lives = 0; } // show game stats @@ -128,7 +147,7 @@ class StateManager { context.fillText("Score: " + this.score, 10, 30); context.fillText("Raid: " + this.raidCount, 10, 60); context.fillText("Lives: ", 10, 90); - for (let i = 0; i < this.playerLives; i++) { + for (let i = 0; i < this.player.lives; i++) { context.fillRect(53 + 10 * (i + 1), 75, 5, 15) } From 94dd52355ddec4a98efdb6becc74752f11f4f89b Mon Sep 17 00:00:00 2001 From: Ayush Pahwa Date: Sat, 14 Dec 2024 00:37:06 +0700 Subject: [PATCH 23/23] update: fix deployment --- src/stateManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stateManager.js b/src/stateManager.js index ec8ebf3..5990630 100644 --- a/src/stateManager.js +++ b/src/stateManager.js @@ -20,7 +20,7 @@ class StateManager { // Enemy wave control this.enemySize = 60; this.enemyRaidGridColumns = 2; - this.enemyRaidGridRows = 12; + this.enemyRaidGridRows = 2; this.raids = []; this.raidCount = 1; this.newRaidSpawned = false;