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>
|
||||
<html>
|
||||
|
||||
<head lang='en'>
|
||||
<meta charset='utf-8'>
|
||||
<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>
|
||||
<script src='bundle.js'></script>
|
||||
<script src="./libs/jquery-3.6.0.min.js"></script>
|
||||
<script src='./bundle.js'></script>
|
||||
</head>
|
||||
|
||||
<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>
|
||||
|
||||
</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;
|
||||
|
||||
export default {
|
||||
external: ["jquery"],
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
file: 'public/bundle.js',
|
||||
format: 'iife', // immediately-invoked function expression — suitable for <script> tags
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
globals: {
|
||||
jquery: "$"
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
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 { Map } from "../Graphics/Map/map";
|
||||
import { Tank } from "../Tanks/tank";
|
||||
import { SoundEnum, SoundManager } from "./soundManager";
|
||||
|
||||
export class CollisionManager {
|
||||
/**
|
||||
@ -20,6 +21,14 @@ export class CollisionManager {
|
||||
_aiTanks = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param {SoundManager} soundManager
|
||||
*/
|
||||
constructor(soundManager) {
|
||||
this.soundManager = soundManager;
|
||||
}
|
||||
|
||||
|
||||
attachMap(map) {
|
||||
this._map = map;
|
||||
}
|
||||
@ -56,6 +65,15 @@ export class CollisionManager {
|
||||
if (blockColl) {
|
||||
for (let blockInfo of Array.from(blockColl)) {
|
||||
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;
|
||||
}
|
||||
@ -67,6 +85,7 @@ export class CollisionManager {
|
||||
|
||||
if (collTank && collTank.id != tank.id) {
|
||||
collTank.isDestroyed = true;
|
||||
this.soundManager.playSound(SoundEnum.Explosion);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@ -6,6 +6,9 @@ import { MapParser } from "../Graphics/Map/mapParser";
|
||||
import { CollisionManager } from "./collisionManager";
|
||||
import { AITankManager } from "./../Tanks/aiTankManager";
|
||||
import { DirectionEnum } from "../Tanks/directionEnum";
|
||||
import { SoundEnum, SoundManager } from "./soundManager";
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
export class GameField {
|
||||
constructor(width = 512, height = 512, ctx, canvas) {
|
||||
@ -16,9 +19,10 @@ export class GameField {
|
||||
this.tileset = null;
|
||||
this.map = null;
|
||||
|
||||
this.soundManager = new SoundManager();
|
||||
|
||||
this.collisionManager = new CollisionManager();
|
||||
this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18*16, this.collisionManager);
|
||||
this.collisionManager = new CollisionManager(this.soundManager);
|
||||
this.tank = new Tank(Constants.PLAYER_TANK_ID_IMAGE.getValue(), 0, 18 * 16, this.collisionManager);
|
||||
this.aiTanks = new AITankManager(this.collisionManager);
|
||||
this.collisionManager.setMainTank(this.tank);
|
||||
}
|
||||
@ -55,36 +59,78 @@ export class GameField {
|
||||
this.tileset.addTile(386, 0, 28, 28, 200 + DirectionEnum.Down.getValue());
|
||||
this.tileset.addTile(452, 0, 28, 28, 200 + DirectionEnum.Right.getValue());
|
||||
|
||||
this.tileset.addTile(512, 8*32, 32, 32, BlockTypeEnum.Explosion1);
|
||||
this.tileset.addTile(544, 8*32, 32, 32, BlockTypeEnum.Explosion2);
|
||||
this.tileset.addTile(576, 8*32, 32, 32, BlockTypeEnum.Explosion3);
|
||||
this.tileset.addTile(512, 8 * 32, 32, 32, BlockTypeEnum.Explosion1);
|
||||
this.tileset.addTile(544, 8 * 32, 32, 32, BlockTypeEnum.Explosion2);
|
||||
this.tileset.addTile(576, 8 * 32, 32, 32, BlockTypeEnum.Explosion3);
|
||||
|
||||
await this.tileset.loadAll();
|
||||
}
|
||||
|
||||
|
||||
async gameLoop() {
|
||||
async prepareGame(level) {
|
||||
if (!this.tileset) {
|
||||
await this.setupTileset();
|
||||
}
|
||||
|
||||
if (!this.map) {
|
||||
this.map = await new MapParser().getMap(1, this.tileset);
|
||||
this.map = await new MapParser().getMap(level, this.tileset);
|
||||
this.canvas.height = this.height;
|
||||
this.canvas.width = this.width;
|
||||
this.collisionManager.attachMap(this.map);
|
||||
this.aiTanks.setCount(this.map._enemiesCount);
|
||||
}
|
||||
|
||||
if (this.map.getEagle().isDestroyed()) return;
|
||||
if (this.aiTanks.isAllTanksDied()) return;
|
||||
if (this.tank.canBeErased()) return;
|
||||
async play(level) {
|
||||
if (!this.tileset || !this.map) {
|
||||
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();
|
||||
|
||||
requestAnimationFrame(() => this.gameLoop());
|
||||
}
|
||||
|
||||
drawFieldRecursive() {
|
||||
this.drawField();
|
||||
requestAnimationFrame(() => this.drawFieldRecursive());
|
||||
}
|
||||
|
||||
drawField() {
|
||||
|
||||
let lateDraw = [];
|
||||
@ -96,10 +142,9 @@ export class GameField {
|
||||
Constants.TILE_SIZE.getValue(), Constants.TILE_SIZE.getValue());
|
||||
|
||||
let element = this.map.getElement(x, y);
|
||||
if (element.getType() == BlockTypeEnum.Forest){
|
||||
lateDraw.push({element, x, y});
|
||||
} else
|
||||
{
|
||||
if (element.getType() == BlockTypeEnum.Forest) {
|
||||
lateDraw.push({ element, x, y });
|
||||
} else {
|
||||
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 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){
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
@ -2,12 +2,19 @@ import { GameField } from "./GameLogic/gameField";
|
||||
import { TankKeyAdapter } from "./KeyAdapters/tankKeyAdapter";
|
||||
|
||||
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 ctx = canvas.getContext("2d");
|
||||
if (ctx) {
|
||||
const game = new Game(416, 416, ctx, canvas);
|
||||
game.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Game {
|
||||
@ -43,6 +50,6 @@ export class Game {
|
||||
addEventListener("keyup", (ev) => {
|
||||
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