From f03b5a314fa64e64a5f2171382fc40536303fda7 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry Date: Sat, 16 Oct 2021 20:22:04 -0400 Subject: [PATCH 01/25] adding fetch api sol for ch8 --- ch8/FetchAPI/index.html | 12 ++++++++++++ ch8/FetchAPI/index.js | 23 +++++++++++++++++++++++ ch8/FetchAPI/template.js | 1 + 3 files changed, 36 insertions(+) create mode 100644 ch8/FetchAPI/index.html create mode 100644 ch8/FetchAPI/index.js create mode 100644 ch8/FetchAPI/template.js diff --git a/ch8/FetchAPI/index.html b/ch8/FetchAPI/index.html new file mode 100644 index 0000000..44f2d35 --- /dev/null +++ b/ch8/FetchAPI/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + + + \ No newline at end of file diff --git a/ch8/FetchAPI/index.js b/ch8/FetchAPI/index.js new file mode 100644 index 0000000..ac2c8d2 --- /dev/null +++ b/ch8/FetchAPI/index.js @@ -0,0 +1,23 @@ +console.log("About to fetch API"); + +fetch("https://jsonplaceholder.typicode.com/posts") + .then(res => { + console.log(res); + if (!res.ok) { + //means 404 error occurred + return null; + } + + return res.json(); + }) + .then(data => { + console.log(data); + }) + .catch(e => { + if (e instanceof TypeError) { + console.log("Something is wrong with requested server."); + } + else { + console.log("Unanticipated error:\n" + e); + } + }); \ No newline at end of file diff --git a/ch8/FetchAPI/template.js b/ch8/FetchAPI/template.js new file mode 100644 index 0000000..c6454c5 --- /dev/null +++ b/ch8/FetchAPI/template.js @@ -0,0 +1 @@ +fetch("https://jsonplaceholder.typicode.com/posts") \ No newline at end of file From bff4a9a60d6927acb4638b270401581abb8a251b Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:29:56 -0400 Subject: [PATCH 02/25] Create index.html --- Project_Based_Course/index.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 Project_Based_Course/index.html diff --git a/Project_Based_Course/index.html b/Project_Based_Course/index.html new file mode 100644 index 0000000..861f0a2 --- /dev/null +++ b/Project_Based_Course/index.html @@ -0,0 +1 @@ + From 5bf1f00c7901db846be9daf6b98c2a88a0719528 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:31:19 -0400 Subject: [PATCH 03/25] Add files via upload --- Project_Based_Course/index.html | 39 +++++++++- Project_Based_Course/script.js | 126 ++++++++++++++++++++++++++++++++ Project_Based_Course/style.css | 61 ++++++++++++++++ 3 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 Project_Based_Course/script.js create mode 100644 Project_Based_Course/style.css diff --git a/Project_Based_Course/index.html b/Project_Based_Course/index.html index 861f0a2..24a4a7c 100644 --- a/Project_Based_Course/index.html +++ b/Project_Based_Course/index.html @@ -1 +1,38 @@ - + + + + + + + Calculator + + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/Project_Based_Course/script.js b/Project_Based_Course/script.js new file mode 100644 index 0000000..4c8aa9d --- /dev/null +++ b/Project_Based_Course/script.js @@ -0,0 +1,126 @@ +class Calculator { + constructor(previousOperandTextElement, currentOperandTextElement) { + this.previousOperandTextElement = previousOperandTextElement + this.currentOperandTextElement = currentOperandTextElement + this.clear() + } + + clear() { + this.currentOperand = '' + this.previousOperand = '' + this.operation = undefined + } + + delete() { + this.currentOperand = this.currentOperand.toString().slice(0, -1) + } + + appendNumber(number) { + if (number === '.' && this.currentOperand.includes('.')) return + this.currentOperand = this.currentOperand.toString() + number.toString() + } + + chooseOperation(operation) { + if (this.currentOperand === '') return + if (this.previousOperand !== '') { + this.compute() + } + this.operation = operation + this.previousOperand = this.currentOperand + this.currentOperand = '' + } + + compute() { + let computation + const prev = parseFloat(this.previousOperand) + const current = parseFloat(this.currentOperand) + if (isNaN(prev) || isNaN(current)) return + switch (this.operation) { + case '+': + computation = prev + current + break + case '-': + computation = prev - current + break + case '*': + computation = prev * current + break + case '÷': + computation = prev / current + break + default: + return + } + this.currentOperand = computation + this.operation = undefined + this.previousOperand = '' + } + + getDisplayNumber(number) { + const stringNumber = number.toString() + const integerDigits = parseFloat(stringNumber.split('.')[0]) + const decimalDigits = stringNumber.split('.')[1] + let integerDisplay + if (isNaN(integerDigits)) { + integerDisplay = '' + } else { + integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 }) + } + if (decimalDigits != null) { + return `${integerDisplay}.${decimalDigits}` + } else { + return integerDisplay + } + } + + updateDisplay() { + this.currentOperandTextElement.innerText = + this.getDisplayNumber(this.currentOperand) + if (this.operation != null) { + this.previousOperandTextElement.innerText = + `${this.getDisplayNumber(this.previousOperand)} ${this.operation}` + } else { + this.previousOperandTextElement.innerText = '' + } + } +} + + +const numberButtons = document.querySelectorAll('[data-number]') +const operationButtons = document.querySelectorAll('[data-operation]') +const equalsButton = document.querySelector('[data-equals]') +const deleteButton = document.querySelector('[data-delete]') +const allClearButton = document.querySelector('[data-all-clear]') +const previousOperandTextElement = document.querySelector('[data-previous-operand]') +const currentOperandTextElement = document.querySelector('[data-current-operand]') + +const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement) + +numberButtons.forEach(button => { + button.addEventListener('click', () => { + calculator.appendNumber(button.innerText) + calculator.updateDisplay() + }) +}) + +operationButtons.forEach(button => { + button.addEventListener('click', () => { + calculator.chooseOperation(button.innerText) + calculator.updateDisplay() + }) +}) + +equalsButton.addEventListener('click', button => { + calculator.compute() + calculator.updateDisplay() +}) + +allClearButton.addEventListener('click', button => { + calculator.clear() + calculator.updateDisplay() +}) + +deleteButton.addEventListener('click', button => { + calculator.delete() + calculator.updateDisplay() +}) \ No newline at end of file diff --git a/Project_Based_Course/style.css b/Project_Based_Course/style.css new file mode 100644 index 0000000..d7047af --- /dev/null +++ b/Project_Based_Course/style.css @@ -0,0 +1,61 @@ +/* +*, *::before, *::after { + box-sizing: border-box; + font-family: Gotham Rounded, sans-serif; + font-weight: normal; +} +*/ + +body { + padding: 0; + margin: 0; + background: linear-gradient(to right, #13547a, #80d0c7); +} + +.calculator-grid { + display: grid; + justify-content: center; + align-content: center; + min-height: 100vh; + grid-template-columns: repeat(4, 100px); + grid-template-rows: minmax(120px, auto) repeat(4, 100px); +} + + +.calculator-grid > button { + cursor: pointer; + font-size: 2rem; + border: 1px solid white; + outline: none; + background-color: rgba(255, 255, 255, .75); +} + +.calculator-grid > button:hover { + background-color: rgba(255, 255, 255, .9); +} + +.span-two { + grid-column: span 2; +} + +.output { + grid-column: 1 / -1; + background-color: rgba(0, 0, 0, .75); + display: flex; + align-items: flex-end; + justify-content: space-around; + flex-direction: column; + padding: 10px; + word-wrap: break-word; + word-break: break-all; +} + +.output .previous-operand { + color: rgba(255, 255, 255, .75); + font-size: 1.5rem; +} + +.output .current-operand { + color: white; + font-size: 2.5rem; +} \ No newline at end of file From f45aaa9eb337db7349f5c5b8028a333edfb8781a Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:32:17 -0400 Subject: [PATCH 04/25] Create Filler.html --- Project_Based_Course/Calculator/Filler.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 Project_Based_Course/Calculator/Filler.html diff --git a/Project_Based_Course/Calculator/Filler.html b/Project_Based_Course/Calculator/Filler.html new file mode 100644 index 0000000..e107dd2 --- /dev/null +++ b/Project_Based_Course/Calculator/Filler.html @@ -0,0 +1 @@ + From 36afea2c27cc1ed8797c52cde41672ac315663cd Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:34:50 -0400 Subject: [PATCH 05/25] Rename Project_Based_Course/index.html to Project_Based_Course/Calculator/index.html --- Project_Based_Course/{ => Calculator}/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Project_Based_Course/{ => Calculator}/index.html (99%) diff --git a/Project_Based_Course/index.html b/Project_Based_Course/Calculator/index.html similarity index 99% rename from Project_Based_Course/index.html rename to Project_Based_Course/Calculator/index.html index 24a4a7c..f7e1cec 100644 --- a/Project_Based_Course/index.html +++ b/Project_Based_Course/Calculator/index.html @@ -35,4 +35,4 @@ - \ No newline at end of file + From 1020b24b762a3e9c0b64e297b1980b4a2eed1a86 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:35:11 -0400 Subject: [PATCH 06/25] Rename Project_Based_Course/script.js to Project_Based_Course/Calculator/script.js --- Project_Based_Course/{ => Calculator}/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Project_Based_Course/{ => Calculator}/script.js (99%) diff --git a/Project_Based_Course/script.js b/Project_Based_Course/Calculator/script.js similarity index 99% rename from Project_Based_Course/script.js rename to Project_Based_Course/Calculator/script.js index 4c8aa9d..1abbde7 100644 --- a/Project_Based_Course/script.js +++ b/Project_Based_Course/Calculator/script.js @@ -123,4 +123,4 @@ allClearButton.addEventListener('click', button => { deleteButton.addEventListener('click', button => { calculator.delete() calculator.updateDisplay() -}) \ No newline at end of file +}) From 067602882b44211853ecf97fa33d91dc0fc5f7f5 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:35:30 -0400 Subject: [PATCH 07/25] Rename Project_Based_Course/style.css to Project_Based_Course/Calculator/style.css --- Project_Based_Course/{ => Calculator}/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Project_Based_Course/{ => Calculator}/style.css (99%) diff --git a/Project_Based_Course/style.css b/Project_Based_Course/Calculator/style.css similarity index 99% rename from Project_Based_Course/style.css rename to Project_Based_Course/Calculator/style.css index d7047af..1565e12 100644 --- a/Project_Based_Course/style.css +++ b/Project_Based_Course/Calculator/style.css @@ -58,4 +58,4 @@ body { .output .current-operand { color: white; font-size: 2.5rem; -} \ No newline at end of file +} From cae29b2ef6dc90ddea071324c3701f852c6ed708 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:36:27 -0400 Subject: [PATCH 08/25] Create Filler.html --- Project_Based_Course/Tank_Game/Filler.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 Project_Based_Course/Tank_Game/Filler.html diff --git a/Project_Based_Course/Tank_Game/Filler.html b/Project_Based_Course/Tank_Game/Filler.html new file mode 100644 index 0000000..2f01528 --- /dev/null +++ b/Project_Based_Course/Tank_Game/Filler.html @@ -0,0 +1 @@ + From d1be0fb8c836bab15aee0d4ae880e8f298a3daf6 Mon Sep 17 00:00:00 2001 From: TomM-and-jerry <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 23 Apr 2022 18:37:31 -0400 Subject: [PATCH 09/25] Add files via upload --- Project_Based_Course/Tank_Game/index.html | 5 + Project_Based_Course/Tank_Game/tank_game.js | 1247 +++++++++++++++++++ 2 files changed, 1252 insertions(+) create mode 100644 Project_Based_Course/Tank_Game/index.html create mode 100644 Project_Based_Course/Tank_Game/tank_game.js diff --git a/Project_Based_Course/Tank_Game/index.html b/Project_Based_Course/Tank_Game/index.html new file mode 100644 index 0000000..b2307e9 --- /dev/null +++ b/Project_Based_Course/Tank_Game/index.html @@ -0,0 +1,5 @@ + +Tank game +

+ + diff --git a/Project_Based_Course/Tank_Game/tank_game.js b/Project_Based_Course/Tank_Game/tank_game.js new file mode 100644 index 0000000..eedc6dd --- /dev/null +++ b/Project_Based_Course/Tank_Game/tank_game.js @@ -0,0 +1,1247 @@ +/* + Created by: Anton Debner 2016 +*/ +var TankGame = (function() { + var TARGET_WIDTH = 801; // Desired width + var TARGET_HEIGHT = 601; + var WIDTH; // Actual width of game, scaled to fit screen + var HEIGHT; + var GUI_HEIGHT = 150; + var DEBUG = false; + // WASD + var P1_UP = 87; + var P1_DOWN = 83; + var P1_LEFT = 65; + var P1_RIGHT = 68; + var P1_FIRE = 49; // Character 1 + + // Arrow keys + var P2_UP = 38; + var P2_DOWN = 40; + var P2_LEFT = 37; + var P2_RIGHT = 39; + var P2_FIRE = 189; // Character - + + // Other settings + var TANK_SIZE = 15; + var TANK_SPEED = 1; + var TANK_TURN_SPEED = 5; + var WALL_WIDTH = 2; + var CELL_SIZE = 50; + var RESET_COUNTER_MAX = 200; // Time to start next round (frames) + var EPSILON = 0.001; // Used for comparing floats + + // Optimization: skip collision checks between distant objects + // NOTE: This only works if there are no large objects in the scene + var MAX_DIST_FOR_COLLISIONS = 2; // (this is multiplied by CELL_SIZE) + + // Global variables (Do not attempt to configure) + var TANK_P1, TANK_P2; + var CELLS_X, CELLS_Y; + var CANVAS, CTX, KEYSTATE, GAME_OBJECTS; + var PRERENDERED_CANVAS, PRERENDERED_CTX, PRERENDERED_REDRAW_NEEDED; + var GUI_REDRAW_NEEDED; + var END_ROUND = false; + var RESET_COUNTER; + + var P1 = 1; + var P2 = 2; + var P1_SCORE = 0; + var P2_SCORE = 0; + + + + + /* + =============================================================================== + -------------------------------------CLASSES----------------------------------- + =============================================================================== + */ + + function deg2rad(degrees) { + return degrees * (Math.PI/180); + } + + class Vector2d { + constructor(x, y) { + if (typeof x == 'undefined' || typeof y == 'undefined') { + throw "Invalid arguments"; + } + this.x = x; + this.y = y; + } + + rotate(radians) { + // Rotates coordinates counterclockwise + //radians=-radians + if (radians != 0) { + var x = this.x; + var y = this.y; + this.x = x * Math.cos(radians) - y * Math.sin(radians); + this.y = x * Math.sin(radians) + y * Math.cos(radians); + } + return this; + } + + add(vector) { + if (vector instanceof Vector2d) { + this.x += vector.x; + this.y += vector.y; + } + else throw "Invalid argument"; + return this; + } + + subtract(vector) { + if (vector instanceof Vector2d) { + this.x -= vector.x; + this.y -= vector.y; + } + else throw "Invalid argument"; + return this; + } + + multiply(value) { + if (isNaN(value) === false) { + this.x *= value; + this.y *= value; + } + else throw "Invalid argument"; + return this; + } + + get_dot_product(other) { + // Calculates the dot product between given vector + if (other instanceof Vector2d) return this.x*other.x + this.y*other.y; + else throw "Invalid argument"; + } + + get_unit_vector() { + var c = this.get_magnitude(); + var unit_vec = new Vector2d(this.x/c, this.y/c); + return unit_vec; + } + + get_right_normal() { + return new Vector2d(this.y, -this.x); + } + + get_magnitude() { + return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); + } + + get_magnitude_squared() { + return Math.pow(this.x, 2) + Math.pow(this.y, 2); + } + + get_inverted() { + return new Vector2d(-this.x, -this.y); + } + + clone() { + return new Vector2d(this.x, this.y); + } + + reflect(vector) { + /* + Reflects this vector around given unit vector. + vec1 - (2*vec2*(vec2.vec1)) + */ + if (vector instanceof Vector2d) { + var vec2 = vector.clone(); // Avoid modifying given vector + vec2.multiply(vec2.get_dot_product(this)); + vec2.multiply(2); + this.subtract(vec2); + return this; + } + else throw "Invalid argument"; + } + }; + + class GameObject { + constructor(x, y, width, height, movable = false) { + this.destructible = true; // Can this object be destroyed? + this.max_hp = 1000; + this.hp = this.max_hp; + this.pos = new Vector2d(x, y); + this.width = width; + this.height = height; + this.rotation = 0; // In degrees + this.velocity = new Vector2d(0, 0); + this.movable = movable; // Can be moved by collisions + this.color = {}; + this.color.r = 0; + this.color.g = 0; + this.color.b = 0; + this.verts = []; + this.rotated_verts = []; + this.ignored_collision_objs = [this]; + this.circle = false; + this.radius = 0; + this.unstoppable = false; // Lets object move through destructible objects + + var w = this.width / 2; + var h = this.height / 2; + this.verts.push(new Vector2d(-w, -h)); + this.verts.push(new Vector2d(-w, h)); + this.verts.push(new Vector2d(w, h)); + this.verts.push(new Vector2d(w, -h)); + + GAME_OBJECTS.push(this); + if (this.movable == false) { + PRERENDERED_REDRAW_NEEDED = true; + } + + } + + damage(amount) { + if (this.destructible && this.hp > 0) { + if (amount > 0) { + this.hp -= amount; + this.color_by_damage(); + } + else console.log("WARNING: Attempted to damage by negative amount!!!"); + } + } + + set_unstoppable(value) { + this.unstoppable = value; + } + + set_destructible(value) { + this.destructible = value; + } + + color_by_damage() { + var red = Math.round(255 - (255 * (this.hp/this.max_hp))); + this.color.r = red; + } + + rotate(degrees) { + if (degrees != 0) { + this.rotation += degrees; + while (this.rotation < 0) this.rotation += 360; + while (this.rotation > 360) this.rotation -= 360; + this.calculate_rotated_verts(); + } + } + + move(vector) { + if (vector instanceof Vector2d) { + this.pos.add(vector); + } + else throw "Invalid argument"; + } + + get_rect(local_coordinates) { + var x = this.pos.x; + var y = this.pos.y; + var w = this.width / 2; + var h = this.height / 2; + if (local_coordinates) return [-w, -h, w*2, h*2]; + else return [x-w, y-h, w*2, h*2]; + } + + calculate_rotated_verts() { + var radians = deg2rad(this.rotation) + this.rotated_verts = []; + for (var vert of this.verts) { + this.rotated_verts.push(vert.clone().rotate(radians)); + } + this.rotated_verts; + } + + get_verts() { + if (this.rotated_verts.length === 0) this.calculate_rotated_verts(); + return this.rotated_verts; + } + + on_collision(obj) { + /* + Default GameObjects do nothing on collision. Child classes can use this + for example to damage or give powerups to tanks on collision. + */ + //console.log(typeof(obj)); + } + + destroy() { + var i = GAME_OBJECTS.indexOf(this); + delete GAME_OBJECTS[i]; + if (this.movable == false) { + PRERENDERED_REDRAW_NEEDED = true; + } + } + + update() { + /* + Moves GameObject by its velocity and checks for collisions. + If collisions are found, attempts to solve them by moving itself. + */ + if (this.hp <= 0) { + this.destroy(); + return; + } + if (this.movable) { + // Move by velocity, if it has any + this.move(this.velocity); + + // Get all colliding objects + var collisions = GetCollisions(this); + + var attempts = 0; // Track attempts to prevent infinite loops + var done = false; + var max_attempts = 5; + while(done === false && attempts < max_attempts) { + var prev_pos = this.pos.clone(); + var prev_velo = this.velocity.clone(); + done = true; + if (collisions.length > 0) { + //console.log(collisions); + } + for (var i = 0; i < collisions.length; i++) { + // Loop over all collisions one at a time + var collision = collisions[i]; + var obj1 = collision.obj1; // This object (redundant) + var obj2 = collision.obj2; // The colliding object + var dir = collision.direction.clone(); + if (this.unstoppable && obj2.destructible == true) { + // Don't attempt to solve collisions for unstoppable objects + // unstoppable objects can go through almost anything. + obj1.on_collision(obj2); + obj2.on_collision(obj1); + break; + } + this.move(dir.clone().multiply(collision.magnitude)); + this.velocity.reflect(dir); + + + // Get all new collisions after moving + var new_collisions = GetCollisions(this); + if (new_collisions.length === 0) { + // Success! No new collisions found + obj1.on_collision(obj2); + obj2.on_collision(obj1); + break; // Don't check any other collisions + } + else if (i < collisions.length-1) { + // Fail! Move back to original position and attempt to solve the next collision + this.pos = prev_pos; + this.velocity = prev_velo; + } + else { + // Fail! No collisions remaining. Try to resolve collisions from the new position + obj1.on_collision(obj2); + obj2.on_collision(obj1); + collisions = new_collisions; + done = false; + attempts++; + } + } + + } + if(attempts > 1) { + console.log("Attempted to resolve collisions " + attempts + " times"); + } + } + } + + draw(context) { + context.save(); + let color = "rgb(" + this.color.r + "," + this.color.g + "," + this.color.b + ")"; + context.fillStyle = color; + context.translate(this.pos.x, this.pos.y); + context.beginPath(); + if (this.circle === true) { + context.arc(0, 0, this.radius, 0, 2 * Math.PI); + } + else { + var verts = this.get_verts(); + context.moveTo(verts[0].x, verts[0].y); + for (var vert of verts) { + context.lineTo(vert.x, vert.y); + } + context.lineTo(vert.x, vert.y); + } + context.fill(); + context.restore(); + } + }; + + var GunTypes = { + // Enumeration for different types of guns + 'normal' : 1, + 'machinegun' : 2, + 'heavy' : 3, + } + + class Gun { + /* + Handles the firing of tank guns. + */ + constructor(tank) { + this.bullet_size = 5; + this.bullet_speed = 1.5; + this.ammo = 1000; // Max shots for current gun + this.clip = 5; // Max simultaenous shots + this.fire_delay = 0.5; + this.last_shot = Date.now(); + this.tank = tank; // Used for ignoring collisions when firing + this.type = GunTypes.normal; + this.damage_amount = 4; + this.randomize_direction = false; + } + + fire(x, y, direction) { + var bullet; + if (this.randomize_direction) { + direction += Math.random() * 10 - 5; // Add a random offset of +-5 degrees + } + if (this.clip > 0 && this.ammo > 0) { + if (Date.now() - this.last_shot > this.fire_delay * 1000) { + GUI_REDRAW_NEEDED = true; // GUI has info about remaining ammo + this.clip--; + this.ammo--; + this.last_shot = Date.now(); + bullet = new Bullet(x, y, direction, this.damage_amount, this, this.bullet_size, this.bullet_speed); + } + } + return bullet; + } + + reload() { + // Adds one bullet to clip. + this.clip++; + } + + get_name() { + return "40mm gun"; + } + + get_ammo_str() { + switch (this.type) { + case GunTypes.normal: + return "infinite"; + break; + default: + return this.ammo; + } + } + }; + + class Machinegun extends Gun { + constructor(tank) { + super(tank); + this.bullet_size = 2; + this.bullet_speed = 2; + this.ammo = 75; + this.clip = 25; + this.fire_delay = 0.1; + this.damage_amount = 1; + this.randomize_direction = true; + this.type = GunTypes.machinegun; + } + + get_name() { + return ".50 caliber machine gun"; + } + }; + + class Heavygun extends Gun { + constructor(tank) { + super(tank); + this.bullet_size = 20; + this.bullet_speed = 1.5; + this.ammo = 3; + this.clip = 3; + this.fire_delay = 1; + this.damage_amount = 1000; + this.randomize_direction = false; + this.type = GunTypes.heavy; + } + + fire(x, y, direction) { + // Override firing to make the bullet unstoppable + var bullet = super.fire(x, y, direction); + if (bullet) + bullet.set_unstoppable(true); + } + + get_name() { + return "155mm heavy gun"; + } + }; + + class Tank extends GameObject { + constructor(x, y, player) { + super(x, y, TANK_SIZE, TANK_SIZE, true); // Movable=true + this.player = player; + this.speed = 1; + this.turn_speed = 5; + this.fire_delay = 0; + this.max_fire_delay = 30; + this.max_ammo = 5; + this.ammo = this.max_ammo; + this.max_hp = 10; + this.hp = this.max_hp; + this.color_by_damage(); + + // Add a gun + this.set_gun(GunTypes.normal); + var w = this.width / 2; + var h = this.height / 2; + var last_vert = this.verts.pop(); + this.verts.push(new Vector2d(w, h/2)); + this.verts.push(new Vector2d(w*2, h/2)); + this.verts.push(new Vector2d(w*2, -h/2)); + this.verts.push(new Vector2d(w, -h/2)); + this.verts.push(last_vert); + } + + set_gun(type) { + GUI_REDRAW_NEEDED = true; // GUI has info about player weapons + switch (type) { + case GunTypes.normal : + this.gun = new Gun(this); + break; + case GunTypes.machinegun : + this.gun = new Machinegun(this); + break; + case GunTypes.heavy : + this.gun = new Heavygun(this); + break; + default : + console.log("Invalid gun type given " + type); + this.gun = new Gun(this); + break; + } + } + + destroy() { + END_ROUND = true; + this.player === P1 ? P2_SCORE++ : P1_SCORE++; + for (var i = 0; i < 360; i += 60) { + // Spawn a ring of bullets on death + var radians = deg2rad(i); + var damage = 4; + var off_x = this.width * Math.cos(radians); + var off_y = this.width * Math.sin(radians); + new Bullet(this.pos.x + off_x, this.pos.y + off_y, i, damage); + } + super.destroy(); + } + + update() { + /* + Checks for user input and checks collisions. + */ + if (this.fire_delay > 0) this.fire_delay--; + var p = this.player; + var radians = deg2rad(this.rotation); + + if ((p == P1 && KEYSTATE[P1_UP]) || (p == P2 && KEYSTATE[P2_UP])) { + this.velocity.x = this.speed * Math.cos(radians); + this.velocity.y = this.speed * Math.sin(radians); + } + else if ((p == P1 && KEYSTATE[P1_DOWN]) || (p == P2 && KEYSTATE[P2_DOWN])) { + this.velocity.x = -this.speed * Math.cos(radians); + this.velocity.y = -this.speed * Math.sin(radians); + } + else { + this.velocity = new Vector2d(0, 0); + } + if ((p == P1 && KEYSTATE[P1_LEFT]) || (p == P2 && KEYSTATE[P2_LEFT])) { + this.rotate(-this.turn_speed); + + } + else if ((p == P1 && KEYSTATE[P1_RIGHT]) || (p == P2 && KEYSTATE[P2_RIGHT])) { + this.rotate(this.turn_speed); + } + + super.update(); // Move and check collisions before firing + + if ((p == P1 && KEYSTATE[P1_FIRE]) || (p == P2 && KEYSTATE[P2_FIRE])) { + if (this.gun.ammo > 0) { + var off_x = this.width * 0.9 * Math.cos(radians); + var off_y = this.width * 0.9 * Math.sin(radians); + this.gun.fire(this.pos.x + off_x, this.pos.y + off_y, this.rotation); + } + else { + this.set_gun(GunTypes.normal); // Replace all special guns with a regular gun + } + + } + } + }; + + var PowerupType = { + 'machinegun' : 1, + 'heavy' : 2, + 'speed' : 3, + 'get_random_type' : + function get_random_type() { + return Math.ceil(Math.random() * 3); + } + }; + + class Powerup extends GameObject { + constructor(x, y, type) { + super(x, y, 10, 10, true); + this.type = type; + this.max_hp = 20; + this.hp = this.max_hp; + this.last_damage_tick = Date.now(); // Cause damage to self every second + this.turn_speed = 5; + this.re_color(); + } + + re_color() { + switch(this.type) { + case PowerupType.machinegun: + this.color = {"r":0, "g": 200, "b": 200}; + break; + case PowerupType.heavy: + this.color = {"r":0, "g": 50, "b": 120}; + break; + case PowerupType.speed: + this.color = {"r":0, "g": 255, "b": 255}; + break; + default: + console.log("Powerup has invalid type!"); + this.color = {"r":0, "g": 10, "b": 10}; + } + } + + update() { + this.rotate(this.turn_speed); + if (Date.now() - this.last_damage_tick > 1000) { + this.last_damage_tick = Date.now(); + this.damage(1); // Max time to live is 20 seconds; + } + super.update(); + } + + on_collision(obj) { + if (obj instanceof Tank) { + switch (this.type) { + case PowerupType.machinegun: + obj.set_gun(GunTypes.machinegun); + break; + case PowerupType.heavy: + obj.set_gun(GunTypes.heavy); + break; + case PowerupType.speed: + obj.speed++; + break; + default: + console.log("Powerup has invalid type!"); + } + this.destroy(); + } + } + }; + + class Bullet extends GameObject { + constructor(x, y, direction, damage, gun, size, speed) { + if (typeof speed == 'undefined') speed = 1.5; + if (typeof size == 'undefined') size = 5; + super(x, y, size, size, true); + this.max_time_to_live = 15000; // After this time the bullet disappears (milliseconds) + this.remaining_bounces = 10; + this.first_bounce = true; + this.spawn_time = Date.now(); + this.ignore_owner_for = 3 * size / speed; // HACK: this value is just randomly guessed (milliseconds) + this.speed = speed; + this.gun = gun; + if (this.gun && this.gun.tank) this.ignored_collision_objs.push(this.gun.tank); // Don't collide with tank before first bounce + this.color.r = Math.round(Math.random() * 255); + this.color.g = Math.round(Math.random() * 255); + this.color.b = Math.round(Math.random() * 255); + var radians = deg2rad(direction); + this.velocity.x = this.speed * Math.cos(radians); + this.velocity.y = this.speed * Math.sin(radians); + this.damage_amount = damage; + this.radius = this.width/2; + this.circle = true; + } + + update() { + super.update(); + if (Date.now() - this.spawn_time > this.max_time_to_live) { + // This bullet has been around long enough, destroy it + this.destroy(); + } + } + + on_collision(obj) { + this.remaining_bounces--; + if (this.first_bounce && (Date.now() - this.spawn_time > this.ignore_owner_for) && this.gun && this.gun.tank) { + // After first bounce bullet can collide with the shooting tank + this.first_bounce = false; + var ind = this.ignored_collision_objs.indexOf(this.gun.tank); + if (ind > -1) delete this.ignored_collision_objs[ind]; + } + + if (this.remaining_bounces < 1) { + this.destroy(); + } + + obj.damage(this.damage_amount); + if (obj instanceof Tank) { + this.destroy(); + } + } + + destroy() { + if (this.gun) this.gun.reload(); + super.destroy(); + } + + }; + + + + + /* + =============================================================================== + ----------------------------COLLISION DETECTION-------------------------------- + =============================================================================== + */ + + class Collision { + constructor(game_obj1, game_obj2) { + this.obj1 = game_obj1; + this.obj2 = game_obj2; + this.has_collided = false; + this.direction = new Vector2d(0, 0); // Direction of penetration + this.magnitude = Number.NEGATIVE_INFINITY; // Shortest distance of penetration + } + } + + function getSATCollision(game_obj1, game_obj2) { + /* + Uses Separating Axis Test to get the direction and magnitude of + any possible collision between given two objects. + https://en.wikipedia.org/wiki/Hyperplane_separation_theorem + + Objects can have any convex shape. Circles are a special case. + */ + var obj1 = game_obj1; + var obj2 = game_obj2; + let temp_pos1 = obj1.pos.clone(); + var d_origins = temp_pos1.subtract(obj2.pos).get_magnitude(); + var collision = new Collision(game_obj1, game_obj2); + collision.has_collided = false; + if (d_origins > MAX_DIST_FOR_COLLISIONS * CELL_SIZE) { + // Optimization: Skip further checks for distant objects + return collision; + } + var verts1 = game_obj1.get_verts(); + var verts2 = game_obj2.get_verts(); + var pos1 = game_obj1.pos.clone(); + var pos2 = game_obj2.pos.clone(); + + for (var i = 0; i < verts1.length + verts2.length; i++) { + // Calculate next axis by taking the normal of a side of one object + if (i < verts1.length) { + var vert = verts1[i]; + if (i < verts1.length-1) var next_vert = verts1[i+1]; + else var next_vert = verts1[0]; + } + else { + var vert = verts2[i - verts1.length]; + if (i < verts1.length+verts2.length-1) var next_vert = verts2[i+1 - verts1.length]; + else var next_vert = verts2[0]; + } + var side = next_vert.clone().subtract(vert).get_unit_vector(); + var axis = side.get_right_normal(); + + // Get minimum and maximum projections on axis from center of obj1 + var min_dist1 = verts1[0].get_dot_product(axis); + var max_dist1 = min_dist1; + for (var j = 1; j < verts1.length; j++) { + var distance = verts1[j].get_dot_product(axis); + if (distance < min_dist1) min_dist1 = distance; + else if (distance > max_dist1) max_dist1 = distance; + } + + // Get minimum and maximum projections on axis from center of obj2 + var min_dist2 = verts2[0].get_dot_product(axis); + var max_dist2 = min_dist2; + for (var j = 1; j < verts2.length; j++) { + var distance = verts2[j].get_dot_product(axis); + if (distance < min_dist2) min_dist2 = distance; + else if (distance > max_dist2) max_dist2 = distance; + } + + // Calculate the distance between objects and flip axis if necessary + var d = new Vector2d(pos2.x - pos1.x, pos2.y - pos1.y).get_dot_product(axis); + + // Calculate the gaps between objects projected along axis + // Negative gap means that there is a collision on this axis + var gap1 = d - max_dist1 + min_dist2; + var gap2 = - d - max_dist2 + min_dist1; + if (gap1 >= -EPSILON || gap2 >= -EPSILON) { + // No collision on this axis - these objects cannot be colliding! + collision.has_collided = false; + return collision; + } + if (gap1 > gap2 && gap1 > collision.magnitude) { + collision.magnitude = gap1; + collision.direction = axis; + } + if (gap2 > gap1 && gap2 > collision.magnitude) { + collision.magnitude = gap2; + collision.direction = axis.get_inverted(); + } + } + collision.has_collided = true; + return collision; + } + + function GetCollisions(obj) { + /* + Checks collisions given gameobject and all other gameobjects. + */ + var ign1 = obj.ignored_collision_objs; + var collisions = []; + for (obj_ind in GAME_OBJECTS) { + var other_obj = GAME_OBJECTS[obj_ind]; + var ign2 = other_obj.ignored_collision_objs; + if (ign1.indexOf(other_obj) === -1 && ign2.indexOf(obj) === -1) { + var collision = getSATCollision(obj, other_obj); + if (collision.has_collided === true) { + collisions.push(collision); + } + } + } + return collisions; + } + + + + + + + /* + =============================================================================== + ---------------------------------MAIN FUNCTIONS-------------------------------- + =============================================================================== + */ + + function fitToWindow() { + /* + Scales the game if the browser window is too small. + */ + let screen_margin = 10; + WIDTH = Math.min(TARGET_WIDTH, window.innerWidth - screen_margin); + HEIGHT = Math.min(TARGET_HEIGHT, window.innerHeight - screen_margin - 300); + WIDTH = Math.max(WIDTH, 100); + HEIGHT = Math.max(HEIGHT, 100); + + CANVAS.width = WIDTH; + CANVAS.height = HEIGHT + GUI_HEIGHT; + + PRERENDERED_CANVAS.width = WIDTH; + PRERENDERED_CANVAS.height = HEIGHT + GUI_HEIGHT; + } + + function main() { + /* + Creates a new HTML5 canvas element, adds listeners for input and + contains the main loop. + */ + CANVAS = document.createElement("canvas"); + CTX = CANVAS.getContext("2d"); + + // Initialize another canvas for quickly drawing static objects + PRERENDERED_CANVAS = document.createElement("canvas"); + PRERENDERED_CTX = PRERENDERED_CANVAS.getContext("2d"); + + fitToWindow(); // Set the size of both canvas + + // Attempt to find a document element with specific id, otherwise attach the + // canvas to document body. + attach_to = document.getElementById('game_window'); + if (attach_to == null) attach_to = document.body; + attach_to.appendChild(CANVAS); + + // Add listeners for keydown and keyup + KEYSTATE = {}; + document.addEventListener("keydown", function(evt) { + if([32, 37, 38, 39, 40].indexOf(evt.keyCode) > -1) { + evt.preventDefault(); // Prevent scrolling with arrowkeys and spacebar + } + KEYSTATE[evt.keyCode] = true; + }); + + document.addEventListener("keyup", function(evt) { + delete KEYSTATE[evt.keyCode]; + }); + + init(); + RESET_COUNTER = 0; + + var previous_time = Date.now(); // Time in milliseconds + var frames = 0; + var iteration_time = 0; + + var loop = function() { + /* + The main loop where all the magic happens. + */ + var start_time = Date.now(); + // Step the game forwards and draw everything + spawn_powerup(); + update(); + draw(); + iteration_time += Date.now() - start_time; + + // FPS-counter for performance analysis + if (DEBUG) { + frames++; + if (Date.now() - previous_time > 1000) { + console.log(frames + " : " + (iteration_time/frames)); + previous_time = Date.now(); + iteration_time = 0; + frames = 0; + } + } + + + // Start a new round if necessary + if (END_ROUND === true) RESET_COUNTER++; + if (RESET_COUNTER > RESET_COUNTER_MAX) { + init(); + END_ROUND = false; + RESET_COUNTER = 0; + } + + // Wait for the browser to finish drawing before looping again + window.requestAnimationFrame(loop, CANVAS); + }; + window.requestAnimationFrame(loop, CANVAS); + } + + function spawn_powerup() { + // Randomly spawns a random powerup to the level + this.max_spawn_time = 50000; // Max time between spawns (Milliseconds) + if (typeof this.last_spawn == 'undefined') { + this.last_spawn = Date.now(); + this.next_spawn_in = Math.random() * this.max_spawn_time; + } + if (Date.now() - this.last_spawn > this.next_spawn_in) { + this.last_spawn = Date.now(); + this.next_spawn_in = Math.random() * this.max_spawn_time; + let pos = get_random_location(); + new Powerup(pos.x, pos.y, PowerupType.get_random_type()); + } + } + + function get_random_location() { + // Gets a location, which is in the middle of a random cell. + var x = (Math.floor(Math.random() * CELLS_X) + 0.5) * (CELL_SIZE); + var y = (Math.floor(Math.random() * CELLS_Y) + 0.5) * (CELL_SIZE); + return new Vector2d(x, y); + } + + function init() { + /* + Initialize global variables. + */ + GAME_OBJECTS = []; + KEYSTATE = {}; // Reset the keystate to avoid stuck buttons + PRERENDERED_REDRAW_NEEDED = true; + GUI_REDRAW_NEEDED = true; + + fitToWindow(); // Rescale the game window, if necessary + + // Generate map + maze_generator_kruskal(); + + // Create tanks at random locations + + let pos = get_random_location(); + TANK_P1 = new Tank(pos.x, pos.y, P1); + let pos2 = pos; + while (pos.x == pos2.x && pos.y == pos2.y) { + pos2 = get_random_location(); + } + TANK_P2 = new Tank(pos2.x, pos2.y, P2); + } + + function update() { + /* + Handles game logic by moving all objects and checking collisions. + */ + for (obj_ind in GAME_OBJECTS) { + obj = GAME_OBJECTS[obj_ind]; + obj.update(); + } + } + + function draw() { + /* + Handles all drawing on the HTML5 canvas. + */ + + // Game + CTX.fillStyle = "#fff"; + CTX.clearRect(0, 0, WIDTH, HEIGHT); + + // Redraw static objects only if they have been changed + if (PRERENDERED_REDRAW_NEEDED) { + PRERENDERED_CTX.fillStyle = "#fff"; + PRERENDERED_CTX.clearRect(0, 0, WIDTH, HEIGHT); + PRERENDERED_REDRAW_NEEDED = false; + for (obj_ind in GAME_OBJECTS) { + obj = GAME_OBJECTS[obj_ind]; + if (obj.movable === false) { + obj.draw(PRERENDERED_CTX); + } + } + } + + // Draw prerendered static objects + CTX.drawImage(PRERENDERED_CANVAS, 0, 0); + + // Draw other objects + for (obj_ind in GAME_OBJECTS) { + obj = GAME_OBJECTS[obj_ind]; + if (obj.movable === true) { + obj.draw(CTX); + } + } + + // GUI + if (GUI_REDRAW_NEEDED || END_ROUND) { + GUI_REDRAW_NEEDED = false; + var P2_offset_x = WIDTH - 270; + CTX.save(); + CTX.translate(0, HEIGHT); // Move to GUI space + CTX.fillStyle = "#fff"; + CTX.clearRect(0, 0, WIDTH, GUI_HEIGHT); + CTX.font = "30px Arial"; + CTX.fillStyle = (P1_SCORE >= P2_SCORE) ? "green" : "red"; + CTX.fillText("Player One: " + P1_SCORE, 30, 50); + CTX.fillStyle = (P2_SCORE >= P1_SCORE) ? "green" : "red"; + CTX.fillText("Player Two: " + P2_SCORE, P2_offset_x, 50); + if (END_ROUND === true) { + CTX.fillStyle = "blue"; + CTX.fillText("Next round in: " + (RESET_COUNTER_MAX - RESET_COUNTER), P2_offset_x/2, 50); + } + CTX.fillStyle = "#000"; + CTX.font = "16px Arial"; + CTX.fillText("Move: WASD", 30, 80); + CTX.fillText("Fire: 1", 30, 100); + CTX.fillText("Move: Arrow keys", P2_offset_x, 80); + CTX.fillText("Fire: -", P2_offset_x, 100); + CTX.font = "bold " + CTX.font; + CTX.fillText("Weapon: " + TANK_P1.gun.get_name(), 30, 120); + CTX.fillText("Ammo remaining: " + TANK_P1.gun.get_ammo_str(), 30, 140); + CTX.fillText("Weapon: " + TANK_P2.gun.get_name(), P2_offset_x, 120); + CTX.fillText("Ammo remaining: " + TANK_P2.gun.get_ammo_str(), P2_offset_x, 140); + CTX.restore(); + } + } + + + + + + /* + =============================================================================== + ---------------------------------MAP GENERATION-------------------------------- + =============================================================================== + */ + + function maze_generator_kruskal() { + /* + Uses randomized Kruskal's algorithm to generate a maze. + https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Kruskal.27s_algorithm + + Normally this algorithm generates 'perfect' mazes, with only one route + from end to beginning. For gameplay reasons multiple routes through the + maze is preferred. This is achieved by randomly deleting additional walls. + */ + + class Cell { + constructor(x, y, i, j) { + this.ind_x = i; + this.ind_y = j; + this.x = x; + this.y = y; + this.right_wall = true; + this.bottom_wall = true; + } + }; + + function shuffle(a) { + /* + Shuffles array in place. + Taken from http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript + because I'm lazy and it is a perfectly fine function. + */ + var j, x, i; + for (var i = a.length; i; i--) { + j = Math.floor(Math.random() * i); + x = a[i - 1]; + a[i - 1] = a[j]; + a[j] = x; + } + } + + function find_cell_set(cell, sets) { + // Finds the set where the given cell is found. + for (set in sets) { + if (sets[set].has(cell)) return set; + } + } + + function join_cell_sets(cell_1, cell_2, sets) { + /* + Checks if given cells are in different sets, joins the sets and returns + true. Otherwise returns false. + */ + set_ind1 = find_cell_set(cell_1, sets); + set_ind2 = find_cell_set(cell_2, sets); + if (!(set_ind1 === set_ind2)) { + var joined_set = new Set(function*() { + yield* sets[set_ind1]; yield* sets[set_ind2]; }() + ); + delete sets[set_ind1]; + delete sets[set_ind2]; + sets.push(joined_set); + return true; + } + return false; + } + cell_size_min = 30; + cell_size_max = 100; + rand = Math.random(); + CELL_SIZE = 30 + (rand * (100-30)); // A random number between min and max + CELL_SIZE = Math.floor(CELL_SIZE); + console.log(CELL_SIZE); + console.log(CELL_SIZE % 5); + CELL_SIZE -= CELL_SIZE % 5; + console.log(CELL_SIZE); + + // Create cells to assist with maze generation + var cells = []; + CELLS_X = Math.floor(WIDTH / CELL_SIZE); + CELLS_Y = Math.floor(HEIGHT / CELL_SIZE); + for (var i = 0; i < CELLS_X; i++) { + var x = i * CELL_SIZE + CELL_SIZE / 2; + var column = [] + + for (var j = 0; j < CELLS_Y; j++) { + var y = j * CELL_SIZE + CELL_SIZE / 2; + new_cell = new Cell(x, y, i, j); + column[j] = new_cell; + } + cells[i] = column; + } + + // Add all walls to arrays and create a set for each cell + var right_walls = []; + var bottom_walls = []; + var cell_sets = []; + for (var i = 0; i < CELLS_X; i++) { + for (var j = 0; j < CELLS_Y; j++) { + cell = cells[i][j]; + cell_sets.push(new Set([cell])); + right_walls.push(cell); + bottom_walls.push(cell); + } + } + + // Shuffle walls to randomize the maze + shuffle(right_walls); + shuffle(bottom_walls); + + // These variables adjust the proportion of removed horizontal and vertical + // walls. + var horiz_prob = 0.6; // value must be 0 < x <= 1 + var vert_prob = 0.7; // value must be 0 < x <= 1 + var remove_anyway_prob = 0.2; // Probability for removing extra walls + + // Remove all walls between disconnected cells + while (right_walls.length > 0 && bottom_walls.length > 0) { + // Right walls + if (right_walls.length > 0 && Math.random() < vert_prob) { + var cell = right_walls.pop(); + if (cell.ind_x + 1 < CELLS_X) { + next_cell = cells[cell.ind_x+1][cell.ind_y]; + // Check if the cell on right belongs to the same set (already connected) + if (join_cell_sets(cell, next_cell, cell_sets)) { + cell.right_wall = false; + } + // Randomly delete the wall anyway + else if (Math.random() < remove_anyway_prob) cell.right_wall = false; + } + } + + // Bottom walls + if (bottom_walls.length > 0 && Math.random() < horiz_prob) { + var cell = bottom_walls.pop(); + if (cell.ind_y + 1 < CELLS_Y) { + next_cell = cells[cell.ind_x][cell.ind_y+1]; + // Check if the cell below belongs to the same set (already connected) + if (join_cell_sets(cell, next_cell, cell_sets)) { + cell.bottom_wall = false; + } + // Randomly delete the wall anyway + else if (Math.random() < remove_anyway_prob) cell.bottom_wall = false; + } + } + } + + // Create a GameObject for every wall + for (column_ind in cells) { + column = cells[column_ind]; + for (cell_ind in column) { + cell = column[cell_ind]; + var x = cell.x; + var y = cell.y; + var s = CELL_SIZE/2; + var w = WALL_WIDTH/2; + if (cell.bottom_wall) { + let wall = new GameObject(x, y+s, s*2, w*2); + if (cell_ind == column.length-1) { + // Make the border walls indestructible + wall.set_destructible(false); + } + } + if (cell.right_wall) { + let wall = new GameObject(x+s, y, w*2, s*2); + if (column_ind == cells.length-1) { + // Make the border walls indestructible + wall.set_destructible(false); + } + } + + // Add left border wall + if (column_ind == 0) { + let wall = new GameObject(x-s+1, y, w*2, s*2); // Offset by one to improve visibility + // Make the border walls indestructible + wall.set_destructible(false); + } + // Add top border wall + if (cell_ind == 0) { + let wall = new GameObject(x, y-s+1, s*2, w*2); // Offset by one to improve visibility + // Make the border walls indestructible + wall.set_destructible(false); + } + } + } + } + + main(); + + return { + // Return some objects/methods for debugging purposes + GAME_OBJECTS : GAME_OBJECTS, + SET_DEBUG : function set_debug(value) { DEBUG = value; }, + SET_COLLISION_DISTANCE : function set_collision_distance(value) { MAX_DIST_FOR_COLLISIONS = value; }, + }; + +})(); \ No newline at end of file From 24e7594a18097273b6fe12b5dec578bef56001ca Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 18 Jun 2022 16:47:44 -0400 Subject: [PATCH 10/25] Update tank_game.js --- Project_Based_Course/Tank_Game/tank_game.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Project_Based_Course/Tank_Game/tank_game.js b/Project_Based_Course/Tank_Game/tank_game.js index eedc6dd..058fef1 100644 --- a/Project_Based_Course/Tank_Game/tank_game.js +++ b/Project_Based_Course/Tank_Game/tank_game.js @@ -1,6 +1,3 @@ -/* - Created by: Anton Debner 2016 -*/ var TankGame = (function() { var TARGET_WIDTH = 801; // Desired width var TARGET_HEIGHT = 601; @@ -1244,4 +1241,4 @@ var TankGame = (function() { SET_COLLISION_DISTANCE : function set_collision_distance(value) { MAX_DIST_FOR_COLLISIONS = value; }, }; -})(); \ No newline at end of file +})(); From 8793d85c2da156336d7a5ee7bcb4d62be5ed8bbf Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:14:51 -0400 Subject: [PATCH 11/25] Create filler.html --- Project_Based_Course/Calculator/Problem_1_Solution/filler.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 Project_Based_Course/Calculator/Problem_1_Solution/filler.html diff --git a/Project_Based_Course/Calculator/Problem_1_Solution/filler.html b/Project_Based_Course/Calculator/Problem_1_Solution/filler.html new file mode 100644 index 0000000..861f0a2 --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_1_Solution/filler.html @@ -0,0 +1 @@ + From eb4714995923edac7f15e84d4edaea87672d04cc Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:15:03 -0400 Subject: [PATCH 12/25] Add files via upload --- .../Calculator/Problem_1_Solution/style.css | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Project_Based_Course/Calculator/Problem_1_Solution/style.css diff --git a/Project_Based_Course/Calculator/Problem_1_Solution/style.css b/Project_Based_Course/Calculator/Problem_1_Solution/style.css new file mode 100644 index 0000000..644d1bf --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_1_Solution/style.css @@ -0,0 +1,112 @@ +/* +*, *::before, *::after { + box-sizing: border-box; + font-family: Gotham Rounded, sans-serif; + font-weight: normal; +} +*/ + + + + body { + padding: 0; + margin: 0; + background: #0F2027; /* fallback for old browsers */ + background: -webkit-linear-gradient(to right, #2C5364, #203A43, #0F2027); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to right, #2C5364, #203A43, #0F2027); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + + font-family: 'ReactiveAnchor-L3L0n', sans-serif; + + } + + .calculator-grid { + display: grid; + justify-content: center; + align-content: center; + min-height: 100vh; + grid-template-columns: repeat(4, 100px); + grid-template-rows: minmax(120px, auto) repeat(5, 100px); + grid-gap: 20px 20px; + } + + + .calculator-grid > button { + cursor: pointer; + font-size: 2rem; + font-family: 'ReactiveAnchor-L3L0n', sans-serif; + outline: none; + background-color: rgba(0, 0, 0, .75); + color: #2ca8fd; + border-radius: 20px; + padding: 0px; + /* Adding Transitions; different transitions properties due to compatibility issues in different browsers */ + -webkit-transition: background-color 2s ease-out; + -moz-transition: background-color 2s ease-out; + -o-transition: background-color 2s ease-out; + transition: background-color 2s ease-out; + + } + + .calculator-grid > button:hover { + background-color: rgba(255, 255, 255, .9); + font-family: 'ReactiveAnchor-L3L0n', sans-serif; + } + + .span-two { + grid-column: span 2; + } + + .output { + grid-column: 1 / -1; + background: #0F2027; /* fallback for old browsers */ + background: -webkit-linear-gradient(to right, #2C5364, #203A43, #0F2027); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to right, #2C5364, #203A43, #0F2027); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + display: flex; + align-items: flex-end; + justify-content: space-around; + flex-direction: column; + padding: 10px; + word-wrap: break-word; + word-break: break-all; + border-radius: 5px; + } + + .output .previous-operand { + color: rgba(255, 255, 255, .75); + font-size: 1.5rem; + } + + .output .current-operand { + color: white; + font-size: 2.5rem; + } + +#op { + background-color: #065caf; + color: #28a5fd; + font-family: 'ReactiveAnchor-L3L0n', sans-serif; + /* Adding Transitions for operator backgrounds*/ + -webkit-transition: background-color 2s ease-out; + -moz-transition: background-color 2s ease-out; + -o-transition: background-color 2s ease-out; + transition: background-color 2s ease-out; +} + +#op:hover { + background-color: #0f9; +} + +#misc { + background-color: #5f5f60; + color: #9d9fa0; + font-family: 'ReactiveAnchor-L3L0n', sans-serif; + /* Adding Transitions for AC / DEL button backgrounds*/ + -webkit-transition: background-color 2s ease-out; + -moz-transition: background-color 2s ease-out; + -o-transition: background-color 2s ease-out; + transition: background-color 2s ease-out; +} + +#misc:hover { + background-color: #0F2027; +} \ No newline at end of file From 208b32a54bf317865d1c79d3cb0452da9b4bdf76 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 20 Aug 2022 22:15:24 -0400 Subject: [PATCH 13/25] Delete filler.html --- Project_Based_Course/Calculator/Problem_1_Solution/filler.html | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Project_Based_Course/Calculator/Problem_1_Solution/filler.html diff --git a/Project_Based_Course/Calculator/Problem_1_Solution/filler.html b/Project_Based_Course/Calculator/Problem_1_Solution/filler.html deleted file mode 100644 index 861f0a2..0000000 --- a/Project_Based_Course/Calculator/Problem_1_Solution/filler.html +++ /dev/null @@ -1 +0,0 @@ - From d2b8bdabcf17fca5a08376c2316b2d0dbf3c7776 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 3 Sep 2022 17:27:26 -0400 Subject: [PATCH 14/25] Create filler.html --- Project_Based_Course/Calculator/Problem_2_Solution/filler.html | 1 + 1 file changed, 1 insertion(+) create mode 100644 Project_Based_Course/Calculator/Problem_2_Solution/filler.html diff --git a/Project_Based_Course/Calculator/Problem_2_Solution/filler.html b/Project_Based_Course/Calculator/Problem_2_Solution/filler.html new file mode 100644 index 0000000..861f0a2 --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_2_Solution/filler.html @@ -0,0 +1 @@ + From dff97edddf6f56368499bf0319406652bc3957be Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 3 Sep 2022 17:45:04 -0400 Subject: [PATCH 15/25] Add files via upload --- .../Calculator/Problem_2_Solution/index2.html | 41 +++++ .../Calculator/Problem_2_Solution/script2.js | 158 ++++++++++++++++++ .../Calculator/Problem_2_Solution/style2.css | 66 ++++++++ 3 files changed, 265 insertions(+) create mode 100644 Project_Based_Course/Calculator/Problem_2_Solution/index2.html create mode 100644 Project_Based_Course/Calculator/Problem_2_Solution/script2.js create mode 100644 Project_Based_Course/Calculator/Problem_2_Solution/style2.css diff --git a/Project_Based_Course/Calculator/Problem_2_Solution/index2.html b/Project_Based_Course/Calculator/Problem_2_Solution/index2.html new file mode 100644 index 0000000..3ed5aaa --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_2_Solution/index2.html @@ -0,0 +1,41 @@ + + + + + + + Calculator + + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Project_Based_Course/Calculator/Problem_2_Solution/script2.js b/Project_Based_Course/Calculator/Problem_2_Solution/script2.js new file mode 100644 index 0000000..f7f2422 --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_2_Solution/script2.js @@ -0,0 +1,158 @@ +class Calculator { + constructor(previousOperandTextElement, currentOperandTextElement) { + this.previousOperandTextElement = previousOperandTextElement + this.currentOperandTextElement = currentOperandTextElement + this.clear() + } + + clear() { + this.currentOperand = '' + this.previousOperand = '' + this.operation = undefined + } + + delete() { + this.currentOperand = this.currentOperand.toString().slice(0, -1) + } + + appendNumber(number) { + if (number === '.' && this.currentOperand.includes('.')) return + this.currentOperand = this.currentOperand.toString() + number.toString() + } + + chooseOperation(operation) { + + if (this.currentOperand === '') return + + this.operation = operation + this.previousOperand = this.currentOperand + this.currentOperand = '' + + //console.log("prev" + this.previousOperand) + //console.log("cur" + this.currentOperand) + } + + compute() { + let computation + + //console.log(this.previousOperand + ", " + this.currentOperand) + //console.log(typeof(this.previousOperand) + ", " + typeof(this.currentOperand)) + + const prev = typeof(this.previousOperand) === "string" || typeof(this.previousOperand) === "number" ? parseFloat(this.previousOperand) : "" + const current = typeof(this.currentOperand) === "string" || typeof(this.currentOperand) === "number" ? parseFloat(this.currentOperand) : "" + + //console.log(prev + "\n" + current + "\n") + + if (isNaN(prev) && isNaN(current)) return + + if (!isNaN(prev) && !isNaN(current)) + switch (this.operation) { + case '+': + computation = prev + current + break + case '-': + computation = prev - current + break + case '*': + computation = prev * current + break + case '÷': + computation = prev / current + break + // New feature 1 + case '^': + computation = prev**current + break + // New feature 1 + default: + return + } + else if (!isNaN(prev)) + switch (this.operation) { + // New feature 2 + case '√': + computation = Math.sqrt(prev) + break + case '^2': + computation = prev * prev; + break + case '^3': + computation = prev * prev * prev; + break + // New feature 2 + default: + return + } + + this.currentOperand = computation + this.operation = undefined + this.previousOperand = '' + } + + getDisplayNumber(number) { + const stringNumber = number.toString() + const integerDigits = parseFloat(stringNumber.split('.')[0]) + const decimalDigits = stringNumber.split('.')[1] + let integerDisplay + if (isNaN(integerDigits)) { + integerDisplay = '' + } else { + integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 }) + } + if (decimalDigits != null) { + return `${integerDisplay}.${decimalDigits}` + } else { + return integerDisplay + } + } + + updateDisplay() { + this.currentOperandTextElement.innerText = + this.getDisplayNumber(this.currentOperand) + if (this.operation != null) { + this.previousOperandTextElement.innerText = + `${this.getDisplayNumber(this.previousOperand)} ${this.operation}` + } else { + this.previousOperandTextElement.innerText = '' + } + } +} + +const numberButtons = document.querySelectorAll('[data-number]') +const operationButtons = document.querySelectorAll('[data-operation]') +const equalsButton = document.querySelector('[data-equals]') +const deleteButton = document.querySelector('[data-delete]') +const allClearButton = document.querySelector('[data-all-clear]') +const previousOperandTextElement = document.querySelector('[data-previous-operand]') +const currentOperandTextElement = document.querySelector('[data-current-operand]') + +const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement) + +numberButtons.forEach(button => { +button.addEventListener('click', () => { + calculator.appendNumber(button.innerText) + calculator.updateDisplay() +}) +}) + +operationButtons.forEach(button => { +button.addEventListener('click', () => { + calculator.chooseOperation(button.innerText) + calculator.updateDisplay() +}) +}) + +equalsButton.addEventListener('click', button => { +calculator.compute() +calculator.updateDisplay() +}) + +allClearButton.addEventListener('click', button => { +calculator.clear() +calculator.updateDisplay() +}) + +deleteButton.addEventListener('click', button => { +calculator.delete() +calculator.updateDisplay() +}) \ No newline at end of file diff --git a/Project_Based_Course/Calculator/Problem_2_Solution/style2.css b/Project_Based_Course/Calculator/Problem_2_Solution/style2.css new file mode 100644 index 0000000..c17866c --- /dev/null +++ b/Project_Based_Course/Calculator/Problem_2_Solution/style2.css @@ -0,0 +1,66 @@ + +*, *::before, *::after { + box-sizing: border-box; + font-family: Gotham Rounded, sans-serif; + font-weight: normal; +} + +body { + padding: 0; + margin: 0; + background: linear-gradient(to right, #13547a, #80d0c7); +} + +.calculator-grid { + display: grid; + justify-content: center; + align-content: center; + min-height: 100vh; + grid-template-columns: repeat(5, 100px); /* We need 5 columns with the new calculator */ + grid-template-rows: minmax(120px, auto) repeat(6, 100px); /* We need 6 rows */ +} + + +.calculator-grid > button { + cursor: pointer; + font-size: 2rem; + border: 1px solid white; + outline: none; + background-color: rgba(255, 255, 255, .75); +} + +.calculator-grid > button:hover { + background-color: rgba(255, 255, 255, .9); +} + +.span-two { + grid-column: span 2; +} + +/* New */ +.equal { + grid-column-start: 3; + grid-column-end: -1; +} + +.output { + grid-column: 1 / -1; + background-color: rgba(0, 0, 0, .75); + display: flex; + align-items: flex-end; + justify-content: space-around; + flex-direction: column; + padding: 10px; + word-wrap: break-word; + word-break: break-all; +} + +.output .previous-operand { + color: rgba(255, 255, 255, .75); + font-size: 1.5rem; +} + +.output .current-operand { + color: white; + font-size: 2.5rem; +} \ No newline at end of file From 3c0402be2cd4c25ec2d1d80094802df382d207ac Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 1 Oct 2022 21:11:21 -0400 Subject: [PATCH 16/25] Create Readme.txt --- ch10_JSON/Readme.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 ch10_JSON/Readme.txt diff --git a/ch10_JSON/Readme.txt b/ch10_JSON/Readme.txt new file mode 100644 index 0000000..d8f7e90 --- /dev/null +++ b/ch10_JSON/Readme.txt @@ -0,0 +1 @@ +This folder holds the solution code for the JSON chapter of Javascript intermediate From 4a9be50fcb572bbda9757d3fe806d95a16467b14 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 1 Oct 2022 21:11:33 -0400 Subject: [PATCH 17/25] Add files via upload --- ch10_JSON/JSON_P1_Solution.html | 42 +++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 ch10_JSON/JSON_P1_Solution.html diff --git a/ch10_JSON/JSON_P1_Solution.html b/ch10_JSON/JSON_P1_Solution.html new file mode 100644 index 0000000..9fff34d --- /dev/null +++ b/ch10_JSON/JSON_P1_Solution.html @@ -0,0 +1,42 @@ + + + + + + + LOL + + + + + + + + \ No newline at end of file From 086ac5267c8a22fe7f9d6f9d2d2c27f586f7db98 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 8 Oct 2022 18:07:53 -0400 Subject: [PATCH 18/25] Create JSON_P1_Template.html --- ch10_JSON/JSON_P1_Template.html | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ch10_JSON/JSON_P1_Template.html diff --git a/ch10_JSON/JSON_P1_Template.html b/ch10_JSON/JSON_P1_Template.html new file mode 100644 index 0000000..b461122 --- /dev/null +++ b/ch10_JSON/JSON_P1_Template.html @@ -0,0 +1,11 @@ + + + + + + LOL + + + + + From 5198fa09e8dd12e71382a5dbad84db1a9279025e Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:44:08 -0400 Subject: [PATCH 19/25] Rename ch10_JSON/JSON_P1_Solution.html to ch10_JSON/JSONP1/JSON_P1_Solution.html --- ch10_JSON/{ => JSONP1}/JSON_P1_Solution.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ch10_JSON/{ => JSONP1}/JSON_P1_Solution.html (96%) diff --git a/ch10_JSON/JSON_P1_Solution.html b/ch10_JSON/JSONP1/JSON_P1_Solution.html similarity index 96% rename from ch10_JSON/JSON_P1_Solution.html rename to ch10_JSON/JSONP1/JSON_P1_Solution.html index 9fff34d..c9e0928 100644 --- a/ch10_JSON/JSON_P1_Solution.html +++ b/ch10_JSON/JSONP1/JSON_P1_Solution.html @@ -39,4 +39,4 @@ // should print Spotify - \ No newline at end of file + From 24eb9b16cb8120be8aa34e19d93f75a10619f1cf Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:44:45 -0400 Subject: [PATCH 20/25] Rename ch10_JSON/JSON_P1_Template.html to ch10_JSON/JSONP1/JSON_P1_Template.html --- ch10_JSON/{ => JSONP1}/JSON_P1_Template.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ch10_JSON/{ => JSONP1}/JSON_P1_Template.html (100%) diff --git a/ch10_JSON/JSON_P1_Template.html b/ch10_JSON/JSONP1/JSON_P1_Template.html similarity index 100% rename from ch10_JSON/JSON_P1_Template.html rename to ch10_JSON/JSONP1/JSON_P1_Template.html From 2cf3ae6e9e84a3ccbb4cbe157a1b166c126e9302 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:48:36 -0400 Subject: [PATCH 21/25] Add files via upload --- ch10_JSON/index.html | 28 ++++++++++++++++++++++++++++ ch10_JSON/index.js | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 ch10_JSON/index.html create mode 100644 ch10_JSON/index.js diff --git a/ch10_JSON/index.html b/ch10_JSON/index.html new file mode 100644 index 0000000..6b7a542 --- /dev/null +++ b/ch10_JSON/index.html @@ -0,0 +1,28 @@ + + + + + + + LOL + + + +
+
+

+

+

+

+

+

+

+

+

+
+ + + + + \ No newline at end of file diff --git a/ch10_JSON/index.js b/ch10_JSON/index.js new file mode 100644 index 0000000..5b0eebd --- /dev/null +++ b/ch10_JSON/index.js @@ -0,0 +1,18 @@ +document.getElementById("Submit").onclick = function() { + let form = { + companyName: document.getElementById("companyName").value, + NumOfEmployees: document.getElementById("NumOfEmployees").value, + Revenue: document.getElementById("Revenue").value, + Rating: document.getElementById("Rating").value, + usCorp: document.getElementById("usCorp").value + } + console.log(JSON.stringify(form)) + + +} + + + + + + \ No newline at end of file From 39996ab14dba6c641cdf5f21f7259856a9c2764b Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:49:07 -0400 Subject: [PATCH 22/25] Rename ch10_JSON/index.html to ch10_JSON/JSONP2/index.html --- ch10_JSON/{ => JSONP2}/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ch10_JSON/{ => JSONP2}/index.html (95%) diff --git a/ch10_JSON/index.html b/ch10_JSON/JSONP2/index.html similarity index 95% rename from ch10_JSON/index.html rename to ch10_JSON/JSONP2/index.html index 6b7a542..4109064 100644 --- a/ch10_JSON/index.html +++ b/ch10_JSON/JSONP2/index.html @@ -25,4 +25,4 @@ - \ No newline at end of file + From 5c46ee99ca9d2c0cd68f6739b46ad8965d971f5c Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:49:33 -0400 Subject: [PATCH 23/25] Rename ch10_JSON/index.js to ch10_JSON/JSONP2/index.js --- ch10_JSON/{ => JSONP2}/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ch10_JSON/{ => JSONP2}/index.js (95%) diff --git a/ch10_JSON/index.js b/ch10_JSON/JSONP2/index.js similarity index 95% rename from ch10_JSON/index.js rename to ch10_JSON/JSONP2/index.js index 5b0eebd..b346c32 100644 --- a/ch10_JSON/index.js +++ b/ch10_JSON/JSONP2/index.js @@ -15,4 +15,4 @@ document.getElementById("Submit").onclick = function() { - \ No newline at end of file + From d85427969c812c1d7c0deb07903b6639de4eb592 Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:50:03 -0400 Subject: [PATCH 24/25] Rename JSON_P1_Template.html to JSON_Problems_Template.html --- .../JSONP1/{JSON_P1_Template.html => JSON_Problems_Template.html} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ch10_JSON/JSONP1/{JSON_P1_Template.html => JSON_Problems_Template.html} (100%) diff --git a/ch10_JSON/JSONP1/JSON_P1_Template.html b/ch10_JSON/JSONP1/JSON_Problems_Template.html similarity index 100% rename from ch10_JSON/JSONP1/JSON_P1_Template.html rename to ch10_JSON/JSONP1/JSON_Problems_Template.html From dfb791a2a1d317236da111a020971bae988fb63e Mon Sep 17 00:00:00 2001 From: Jerry W <70043570+TomM-and-jerry@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:52:04 -0400 Subject: [PATCH 25/25] Rename ch10_JSON/JSONP1/JSON_Problems_Template.html to ch10_JSON/JSON_Problems_Template.html --- ch10_JSON/{JSONP1 => }/JSON_Problems_Template.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ch10_JSON/{JSONP1 => }/JSON_Problems_Template.html (100%) diff --git a/ch10_JSON/JSONP1/JSON_Problems_Template.html b/ch10_JSON/JSON_Problems_Template.html similarity index 100% rename from ch10_JSON/JSONP1/JSON_Problems_Template.html rename to ch10_JSON/JSON_Problems_Template.html