Added JQuery, fixed offline, added start button
This commit is contained in:
parent
e3d38cd730
commit
5526696c96
BIN
public/fonts/VT323-Regular.ttf
Normal file
BIN
public/fonts/VT323-Regular.ttf
Normal file
Binary file not shown.
@ -1,13 +1,48 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head lang='en'>
|
<head lang='en'>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<meta name='viewport' content='width=device-width'>
|
<meta name='viewport' content='width=device-width'>
|
||||||
<link rel="icon" href="./favicon.ico" />
|
<link rel="icon" href="./favicon.ico" />
|
||||||
|
<link rel="stylesheet" href="./styles/main.css">
|
||||||
|
<link rel="stylesheet" href="./styles/tank-animation.css">
|
||||||
<title>BattleCity.js</title>
|
<title>BattleCity.js</title>
|
||||||
<script src='bundle.js'></script>
|
<script src="./libs/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src='./bundle.js'></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<main><canvas id="canvas"></canvas></main>
|
<main class="wrapper">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<div class="loading">
|
||||||
|
<div class="center">
|
||||||
|
<div><div class="pixel"></div></div>
|
||||||
|
<div class="text-loading">Loading...</div>
|
||||||
|
<div class='container start-level-button'>
|
||||||
|
<a href='#' class='playBut'>
|
||||||
|
|
||||||
|
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" x="0px" y="0px" width="213.7px"
|
||||||
|
height="213.7px" viewBox="0 0 213.7 213.7" enable-background="new 0 0 213.7 213.7"
|
||||||
|
xml:space="preserve">
|
||||||
|
|
||||||
|
<polygon class='triangle' id="XMLID_18_" fill="none" stroke-width="7" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" stroke-miterlimit="10" points="
|
||||||
|
73.5,62.5 148.5,105.8 73.5,149.1 " />
|
||||||
|
|
||||||
|
<circle class='circle' id="XMLID_17_" fill="none" stroke-width="7" stroke-linecap="round"
|
||||||
|
stroke-linejoin="round" stroke-miterlimit="10" cx="106.8" cy="106.8" r="103.3" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
2
public/libs/jquery-3.6.0.min.js
vendored
Normal file
2
public/libs/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
public/sound/brick.ogg
Normal file
BIN
public/sound/brick.ogg
Normal file
Binary file not shown.
BIN
public/sound/explosion.ogg
Normal file
BIN
public/sound/explosion.ogg
Normal file
Binary file not shown.
BIN
public/sound/gameover.ogg
Normal file
BIN
public/sound/gameover.ogg
Normal file
Binary file not shown.
BIN
public/sound/gamestart.ogg
Normal file
BIN
public/sound/gamestart.ogg
Normal file
Binary file not shown.
BIN
public/sound/steel.ogg
Normal file
BIN
public/sound/steel.ogg
Normal file
Binary file not shown.
118
public/styles/main.css
Normal file
118
public/styles/main.css
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: VT323;
|
||||||
|
src : url(./../fonts/VT323-Regular.ttf);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: VT323, 'Common Pixel', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
border: 1px solid #f8aa28;
|
||||||
|
border-radius: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width : 416px;
|
||||||
|
height: 416px;
|
||||||
|
|
||||||
|
border: 1px solid #f8aa28;
|
||||||
|
border-radius: 1%;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
position : absolute;
|
||||||
|
top : 0px;
|
||||||
|
left : 0px;
|
||||||
|
background-color: black;
|
||||||
|
border: 1px solid #f8aa28;
|
||||||
|
border-radius: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center:first-child {
|
||||||
|
margin-top: 5%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-loading {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.start-level-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width : 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 10vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
stroke : #f8aa28;
|
||||||
|
stroke-dasharray : 650;
|
||||||
|
stroke-dashoffset : 650;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
opacity : 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playBut {
|
||||||
|
display : inline-block;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
-webkit-transition: all 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playBut .triangle {
|
||||||
|
-webkit-transition: all 0.7s ease-in-out;
|
||||||
|
transition: all 0.7s ease-in-out;
|
||||||
|
stroke-dasharray : 240;
|
||||||
|
stroke-dashoffset : 480;
|
||||||
|
stroke : #FFF;
|
||||||
|
transform : translateY(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
.playBut:hover .triangle {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
opacity : 1;
|
||||||
|
stroke : #f8aa28;
|
||||||
|
animation : nudge 0.7s ease-in-out;
|
||||||
|
|
||||||
|
@keyframes nudge {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
transform: translateX(-5px)
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateX(5px)
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: translateX(-2px)
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.playBut:hover .circle {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
opacity : 1;
|
||||||
|
}
|
||||||
19
public/styles/tank-animation.css
Normal file
19
public/styles/tank-animation.css
Normal file
File diff suppressed because one or more lines are too long
43
public/sw.js
Normal file
43
public/sw.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const filesToCache = [
|
||||||
|
"./",
|
||||||
|
"fonts/VT323-Regular.ttf",
|
||||||
|
"bundle.js",
|
||||||
|
"bundle.js.map",
|
||||||
|
"favicon.ico",
|
||||||
|
"nesBattle.png",
|
||||||
|
"sound/brick.ogg",
|
||||||
|
"sound/explosion.ogg",
|
||||||
|
"sound/gameover.ogg",
|
||||||
|
"sound/gamestart.ogg",
|
||||||
|
"sound/steel.ogg",
|
||||||
|
"styles/main.css",
|
||||||
|
"stages/stage-1.json",
|
||||||
|
"stages/stage-2.json",
|
||||||
|
"stages/stage-3.json",
|
||||||
|
"libs/jquery-3.6.0.min.js"
|
||||||
|
]
|
||||||
|
|
||||||
|
const cacheName = "battleCity";
|
||||||
|
|
||||||
|
self.addEventListener('install', event => {
|
||||||
|
self.skipWaiting();
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(cacheName).then(cache => cache.addAll(filesToCache))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', event => {
|
||||||
|
event.respondWith (
|
||||||
|
caches.match(event.request).then(response => {
|
||||||
|
if (response) return response;
|
||||||
|
return fetch(event.request)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok)
|
||||||
|
return caches.open(cacheName).then(cache => {
|
||||||
|
cache.put(event.request.url, response.clone());
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
)
|
||||||
|
});
|
||||||
@ -7,11 +7,15 @@ import { terser } from 'rollup-plugin-terser';
|
|||||||
const production = !process.env.ROLLUP_WATCH;
|
const production = !process.env.ROLLUP_WATCH;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
external: ["jquery"],
|
||||||
input: 'src/main.js',
|
input: 'src/main.js',
|
||||||
output: {
|
output: {
|
||||||
file: 'public/bundle.js',
|
file: 'public/bundle.js',
|
||||||
format: 'iife', // immediately-invoked function expression — suitable for <script> tags
|
format: 'iife', // immediately-invoked function expression — suitable for <script> tags
|
||||||
sourcemap: true
|
sourcemap: true,
|
||||||
|
globals: {
|
||||||
|
jquery: "$"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
resolve(), // tells Rollup how to find date-fns in node_modules
|
resolve(), // tells Rollup how to find date-fns in node_modules
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { BlockTypeEnum } from "../Blocks/blockTypeEnum";
|
|||||||
import { Constants } from "../constants";
|
import { Constants } from "../constants";
|
||||||
import { Map } from "../Graphics/Map/map";
|
import { Map } from "../Graphics/Map/map";
|
||||||
import { Tank } from "../Tanks/tank";
|
import { Tank } from "../Tanks/tank";
|
||||||
|
import { SoundEnum, SoundManager } from "./soundManager";
|
||||||
|
|
||||||
export class CollisionManager {
|
export class CollisionManager {
|
||||||
/**
|
/**
|
||||||
@ -20,6 +21,14 @@ export class CollisionManager {
|
|||||||
_aiTanks = [];
|
_aiTanks = [];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SoundManager} soundManager
|
||||||
|
*/
|
||||||
|
constructor(soundManager) {
|
||||||
|
this.soundManager = soundManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
attachMap(map) {
|
attachMap(map) {
|
||||||
this._map = map;
|
this._map = map;
|
||||||
}
|
}
|
||||||
@ -56,6 +65,15 @@ export class CollisionManager {
|
|||||||
if (blockColl) {
|
if (blockColl) {
|
||||||
for (let blockInfo of Array.from(blockColl)) {
|
for (let blockInfo of Array.from(blockColl)) {
|
||||||
blockInfo.block.resolveAttack(blockInfo.boxId, direction);
|
blockInfo.block.resolveAttack(blockInfo.boxId, direction);
|
||||||
|
|
||||||
|
switch(blockInfo.block.getType()) {
|
||||||
|
case BlockTypeEnum.Brick:
|
||||||
|
this.soundManager.playSound(SoundEnum.BrickCollision);
|
||||||
|
break;
|
||||||
|
case BlockTypeEnum.Iron:
|
||||||
|
this.soundManager.playSound(SoundEnum.SteelCollision);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -67,6 +85,7 @@ export class CollisionManager {
|
|||||||
|
|
||||||
if (collTank && collTank.id != tank.id) {
|
if (collTank && collTank.id != tank.id) {
|
||||||
collTank.isDestroyed = true;
|
collTank.isDestroyed = true;
|
||||||
|
this.soundManager.playSound(SoundEnum.Explosion);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@ -6,6 +6,9 @@ import { MapParser } from "../Graphics/Map/mapParser";
|
|||||||
import { CollisionManager } from "./collisionManager";
|
import { CollisionManager } from "./collisionManager";
|
||||||
import { AITankManager } from "./../Tanks/aiTankManager";
|
import { AITankManager } from "./../Tanks/aiTankManager";
|
||||||
import { DirectionEnum } from "../Tanks/directionEnum";
|
import { DirectionEnum } from "../Tanks/directionEnum";
|
||||||
|
import { SoundEnum, SoundManager } from "./soundManager";
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
|
|
||||||
export class GameField {
|
export class GameField {
|
||||||
constructor(width = 512, height = 512, ctx, canvas) {
|
constructor(width = 512, height = 512, ctx, canvas) {
|
||||||
@ -16,8 +19,9 @@ export class GameField {
|
|||||||
this.tileset = null;
|
this.tileset = null;
|
||||||
this.map = null;
|
this.map = null;
|
||||||
|
|
||||||
|
this.soundManager = new SoundManager();
|
||||||
|
|
||||||
this.collisionManager = new CollisionManager();
|
this.collisionManager = new CollisionManager(this.soundManager);
|
||||||
this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18 * 16, this.collisionManager);
|
this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18 * 16, this.collisionManager);
|
||||||
this.aiTanks = new AITankManager(this.collisionManager);
|
this.aiTanks = new AITankManager(this.collisionManager);
|
||||||
this.collisionManager.setMainTank(this.tank);
|
this.collisionManager.setMainTank(this.tank);
|
||||||
@ -62,29 +66,71 @@ export class GameField {
|
|||||||
await this.tileset.loadAll();
|
await this.tileset.loadAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async prepareGame(level) {
|
||||||
async gameLoop() {
|
|
||||||
if (!this.tileset) {
|
if (!this.tileset) {
|
||||||
await this.setupTileset();
|
await this.setupTileset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.map) {
|
this.map = await new MapParser().getMap(level, this.tileset);
|
||||||
this.map = await new MapParser().getMap(1, this.tileset);
|
|
||||||
this.canvas.height = this.height;
|
this.canvas.height = this.height;
|
||||||
this.canvas.width = this.width;
|
this.canvas.width = this.width;
|
||||||
this.collisionManager.attachMap(this.map);
|
this.collisionManager.attachMap(this.map);
|
||||||
this.aiTanks.setCount(this.map._enemiesCount);
|
this.aiTanks.setCount(this.map._enemiesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.map.getEagle().isDestroyed()) return;
|
async play(level) {
|
||||||
if (this.aiTanks.isAllTanksDied()) return;
|
if (!this.tileset || !this.map) {
|
||||||
if (this.tank.canBeErased()) return;
|
await this.prepareGame(level);
|
||||||
|
$(".text-loading").hide();
|
||||||
|
$(".start-level-button").show();
|
||||||
|
|
||||||
|
$(".start-level-button").on("click", () => {
|
||||||
|
$(".loading").hide();
|
||||||
|
this.soundManager.playSound(SoundEnum.GameStart);
|
||||||
|
requestAnimationFrame(() => this.gameLoop());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lose() {
|
||||||
|
|
||||||
|
this.soundManager.playSound(SoundEnum.GameOver);
|
||||||
|
requestAnimationFrame(() => this.drawFieldRecursive())
|
||||||
|
}
|
||||||
|
|
||||||
|
win() {
|
||||||
|
|
||||||
|
|
||||||
|
requestAnimationFrame(() => this.drawFieldRecursive())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async gameLoop() {
|
||||||
|
if (this.map.getEagle().isDestroyed()) {
|
||||||
|
this.lose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.aiTanks.isAllTanksDied()) {
|
||||||
|
this.win();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.tank.canBeErased()) {
|
||||||
|
this.lose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.drawField();
|
this.drawField();
|
||||||
|
|
||||||
requestAnimationFrame(() => this.gameLoop());
|
requestAnimationFrame(() => this.gameLoop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawFieldRecursive() {
|
||||||
|
this.drawField();
|
||||||
|
requestAnimationFrame(() => this.drawFieldRecursive());
|
||||||
|
}
|
||||||
|
|
||||||
drawField() {
|
drawField() {
|
||||||
|
|
||||||
let lateDraw = [];
|
let lateDraw = [];
|
||||||
@ -98,8 +144,7 @@ export class GameField {
|
|||||||
let element = this.map.getElement(x, y);
|
let element = this.map.getElement(x, y);
|
||||||
if (element.getType() == BlockTypeEnum.Forest) {
|
if (element.getType() == BlockTypeEnum.Forest) {
|
||||||
lateDraw.push({ element, x, y });
|
lateDraw.push({ element, x, y });
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
element.draw(this.ctx, x * Constants.TILE_SIZE.getValue(), y * Constants.TILE_SIZE.getValue());
|
element.draw(this.ctx, x * Constants.TILE_SIZE.getValue(), y * Constants.TILE_SIZE.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/GameLogic/soundManager.js
Normal file
24
src/GameLogic/soundManager.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Constants } from "../constants";
|
||||||
|
|
||||||
|
export class SoundManager {
|
||||||
|
playSound(soundEnum) {
|
||||||
|
if (soundEnum)
|
||||||
|
soundEnum.playSound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SoundEnum {
|
||||||
|
static BrickCollision = new SoundEnum(Constants.BRICK_SOUND.getValue());
|
||||||
|
static SteelCollision = new SoundEnum(Constants.STEEL_SOUND.getValue());
|
||||||
|
static Explosion = new SoundEnum(Constants.EXPLOSION_SOUND.getValue());
|
||||||
|
static GameOver = new SoundEnum(Constants.GAME_OVER_SOUND.getValue());
|
||||||
|
static GameStart = new SoundEnum(Constants.GAME_START_SOUND.getValue());
|
||||||
|
|
||||||
|
playSound() {
|
||||||
|
this._audio.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(URL) {
|
||||||
|
this._audio = new Audio(URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,12 @@ export class Constants {
|
|||||||
|
|
||||||
static PLAYER_TANK_ID_IMAGE = new Constants(100);
|
static PLAYER_TANK_ID_IMAGE = new Constants(100);
|
||||||
|
|
||||||
|
static BRICK_SOUND = new Constants("sound/brick.ogg");
|
||||||
|
static STEEL_SOUND = new Constants("sound/steel.ogg");
|
||||||
|
static EXPLOSION_SOUND = new Constants("sound/explosion.ogg");
|
||||||
|
static GAME_START_SOUND = new Constants("sound/gamestart.ogg");
|
||||||
|
static GAME_OVER_SOUND = new Constants("sound/gameover.ogg");
|
||||||
|
|
||||||
constructor(value){
|
constructor(value){
|
||||||
this._value = value;
|
this._value = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,12 +2,19 @@ import { GameField } from "./GameLogic/gameField";
|
|||||||
import { TankKeyAdapter } from "./KeyAdapters/tankKeyAdapter";
|
import { TankKeyAdapter } from "./KeyAdapters/tankKeyAdapter";
|
||||||
|
|
||||||
export function init() {
|
export function init() {
|
||||||
|
if('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register('./sw.js')
|
||||||
|
.then(function() { console.log("Service Worker Registered"); });
|
||||||
|
}
|
||||||
|
|
||||||
const canvas = document.getElementById("canvas");
|
const canvas = document.getElementById("canvas");
|
||||||
const ctx = canvas.getContext("2d");
|
const ctx = canvas.getContext("2d");
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
const game = new Game(416, 416, ctx, canvas);
|
const game = new Game(416, 416, ctx, canvas);
|
||||||
game.start();
|
game.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Game {
|
export class Game {
|
||||||
@ -43,6 +50,6 @@ export class Game {
|
|||||||
addEventListener("keyup", (ev) => {
|
addEventListener("keyup", (ev) => {
|
||||||
if (prevEv && prevEv.key == ev.key) prevEv = undefined;
|
if (prevEv && prevEv.key == ev.key) prevEv = undefined;
|
||||||
});
|
});
|
||||||
requestAnimationFrame(() => this.gameField.gameLoop());
|
requestAnimationFrame(() => this.gameField.play(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user