The main game and server connections are working
This commit is contained in:
parent
4dc5ed358c
commit
c19f2dbcc3
35
.eslintrc.js
Normal file
35
.eslintrc.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
module.exports = {
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"globals": {
|
||||||
|
"Atomics": "readonly",
|
||||||
|
"SharedArrayBuffer": "readonly"
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018,
|
||||||
|
"sourceType": "module",
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -106,4 +106,5 @@ dist
|
|||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
.vscode-test
|
.vscode-test
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
27
html/index.html
Normal file
27
html/index.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Minesweeper 99</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1><i class="fas fa-bomb"></i>Minesweeper 99</h1>
|
||||||
|
|
||||||
|
<div id="main">
|
||||||
|
<div id="board">
|
||||||
|
<!-- Renderer will push content here -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="log">
|
||||||
|
<h2>Gamelog</h2>
|
||||||
|
<!-- Game log will be pushed here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
5970
package-lock.json
generated
Normal file
5970
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
31
package.json
Normal file
31
package.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "minesweeper",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Real actual competitive minesweeper in Node.JS. Flagging a mine correctly will send it to one of the other players.",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "webpack serve",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://gitea.arnweb.nl/arne/minesweeper.git"
|
||||||
|
},
|
||||||
|
"author": "Arne van Iterson",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"dependencies": {
|
||||||
|
"css-loader": "^5.0.1",
|
||||||
|
"eslint": "^7.12.1",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"jquery": "^3.5.1",
|
||||||
|
"node-sass": "^5.0.0",
|
||||||
|
"sass": "^1.29.0",
|
||||||
|
"sass-loader": "^10.0.5",
|
||||||
|
"socket.io-client": "^3.0.0",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"webpack": "^5.4.0",
|
||||||
|
"webpack-cli": "^4.2.0",
|
||||||
|
"webpack-dev-server": "^3.11.0"
|
||||||
|
}
|
||||||
|
}
|
69
scss/index.scss
Normal file
69
scss/index.scss
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
body {
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
background: #121212;
|
||||||
|
color: lightgray;
|
||||||
|
// * {
|
||||||
|
// border: 1px solid white;
|
||||||
|
// }
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div#main {
|
||||||
|
height: 100%;
|
||||||
|
width: max-content;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
column-gap: 1em;
|
||||||
|
margin: 0 auto;
|
||||||
|
div {
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 0.75em;
|
||||||
|
background-color: #1E1E1E;
|
||||||
|
&#board {
|
||||||
|
text-align: center;
|
||||||
|
width: min-content;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto auto auto auto auto auto auto auto auto auto auto auto auto auto;
|
||||||
|
column-gap: 0.25em;
|
||||||
|
row-gap: 0.25em;
|
||||||
|
span.cell {
|
||||||
|
border: 1px solid grey;
|
||||||
|
color: lightgray;
|
||||||
|
text-align: center;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
&.discovered {
|
||||||
|
background-color: #5c5c5c;
|
||||||
|
}
|
||||||
|
&.bomb {
|
||||||
|
background-color: #5e3030;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&#log {
|
||||||
|
font-size: 12px;
|
||||||
|
width: 20vw;
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
color: darkgray;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
&.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
&.alert {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
&.success {
|
||||||
|
color: cyan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
src/helpers/log.js
Normal file
8
src/helpers/log.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
function log(message, type) {
|
||||||
|
var child = document.createElement("span");
|
||||||
|
child.innerHTML = "- " + message;
|
||||||
|
child.className = type;
|
||||||
|
document.getElementById("log").appendChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default log;
|
65
src/helpers/multiplayer.js
Normal file
65
src/helpers/multiplayer.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import io from "socket.io-client";
|
||||||
|
import log from "./log.js";
|
||||||
|
|
||||||
|
class Multiplayer {
|
||||||
|
constructor() {
|
||||||
|
// Keep local and external players
|
||||||
|
this.players = [];
|
||||||
|
this.connected = false;
|
||||||
|
this.game;
|
||||||
|
|
||||||
|
// Connect to TanksJS-Server instance
|
||||||
|
log("Connecting to server...");
|
||||||
|
this.socket = io("http://localhost:3000");
|
||||||
|
this.socket.on("identify", () => {
|
||||||
|
this.socket.emit("identification", {
|
||||||
|
name: "Arn",
|
||||||
|
playersMax: 2,
|
||||||
|
gameID: "ms99"
|
||||||
|
});
|
||||||
|
log(`Connected to server as ${this.socket.id}`, "success");
|
||||||
|
this.connected = true;
|
||||||
|
this.game.active = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle player logins
|
||||||
|
this.socket.on("roomUpdate", (data) => {
|
||||||
|
console.log("roomUpdate");
|
||||||
|
// Add any player that is not the local player and is not already added
|
||||||
|
for (const id in data) {
|
||||||
|
if (id != this.socket.id && this.players.indexOf(id)) {
|
||||||
|
console.log(id);
|
||||||
|
this.players.push(id);
|
||||||
|
console.log(this.players);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any player that disconnects from the game
|
||||||
|
for (const id in this.players) {
|
||||||
|
if (data[id] == undefined) {
|
||||||
|
var index = this.players.indexOf(id);
|
||||||
|
this.players.splice(index, 1);
|
||||||
|
console.log(this.players);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle player updates
|
||||||
|
this.socket.on("update", (data) => {
|
||||||
|
console.log("update");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start game
|
||||||
|
this.socket.on("gameStart", () => {
|
||||||
|
console.log("gameStart");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setInstance(game) {
|
||||||
|
this.game = game;
|
||||||
|
log("Game instance initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Multiplayer;
|
11
src/index.js
Normal file
11
src/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
import "../scss/index.scss";
|
||||||
|
import log from "./helpers/log.js";
|
||||||
|
import Game from "./screens/game.js";
|
||||||
|
import Multiplayer from "./helpers/multiplayer.js";
|
||||||
|
|
||||||
|
var board = document.getElementById("board");
|
||||||
|
var mp = new Multiplayer();
|
||||||
|
var game = new Game(board);
|
||||||
|
mp.setInstance(game);
|
||||||
|
|
133
src/screens/game.js
Normal file
133
src/screens/game.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import * as math from "../utilities/math.js";
|
||||||
|
import log from "../helpers/log.js";
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
constructor(element) {
|
||||||
|
this.board = element;
|
||||||
|
this.children = element.children;
|
||||||
|
|
||||||
|
this.active = false;
|
||||||
|
|
||||||
|
this.sides = 15;
|
||||||
|
this.size = Math.pow(this.sides, 2);
|
||||||
|
this.bombAmount = 35;
|
||||||
|
this.bombs = [];
|
||||||
|
this.discovered = [];
|
||||||
|
|
||||||
|
for (let cell = 0; cell < this.size; cell++) {
|
||||||
|
this.bombs.push(0);
|
||||||
|
this.discovered.push(0);
|
||||||
|
|
||||||
|
var child = document.createElement("span");
|
||||||
|
child.className = "cell";
|
||||||
|
|
||||||
|
child.addEventListener("click", () => {
|
||||||
|
this.discover(cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.board.appendChild(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < this.bombAmount; index++) {
|
||||||
|
var rand = math.default.rand(0, this.size);
|
||||||
|
this.bombs[rand] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.bombs);
|
||||||
|
console.log(this.discovered);
|
||||||
|
}
|
||||||
|
|
||||||
|
discover(cell) {
|
||||||
|
if (this.active && !this.discovered[cell]) {
|
||||||
|
this.discovered[cell] = 1;
|
||||||
|
this.children[cell].classList.add("discovered");
|
||||||
|
|
||||||
|
if (this.bombs[cell]) {
|
||||||
|
log("Game over", "error");
|
||||||
|
this.active = false;
|
||||||
|
for (let index = 0; index < this.size; index++) {
|
||||||
|
if (this.bombs[index]) {
|
||||||
|
this.children[index].innerHTML = "[]";
|
||||||
|
this.children[index].classList.add("bomb");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var surrounds = [];
|
||||||
|
var limit = {
|
||||||
|
top: true,
|
||||||
|
bottom: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!(cell - this.sides < 0)) {
|
||||||
|
// Cell is not on top of the map
|
||||||
|
limit.top = false;
|
||||||
|
surrounds.push(
|
||||||
|
cell - this.sides
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cell + this.sides > this.size - 1)) {
|
||||||
|
// Cell is not on the bottom of the map
|
||||||
|
limit.bottom = false;
|
||||||
|
surrounds.push(
|
||||||
|
cell + this.sides
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell % this.sides === 0) {
|
||||||
|
// Cell is on the left side of the map
|
||||||
|
surrounds.push(cell + 1);
|
||||||
|
if (!limit.top) {
|
||||||
|
surrounds.push(cell - this.sides + 1);
|
||||||
|
}
|
||||||
|
if (!limit.bottom) {
|
||||||
|
surrounds.push(cell + this.sides + 1);
|
||||||
|
}
|
||||||
|
} else if ((cell + 1) % this.sides === 0 ) {
|
||||||
|
// Cell is on the right side of the map
|
||||||
|
surrounds.push(cell - 1);
|
||||||
|
if (!limit.top) {
|
||||||
|
surrounds.push(cell - this.sides - 1);
|
||||||
|
}
|
||||||
|
if (!limit.bottom) {
|
||||||
|
surrounds.push(cell + this.sides - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Cell is not on either sides of the map
|
||||||
|
surrounds.push(cell - 1);
|
||||||
|
surrounds.push(cell + 1);
|
||||||
|
if (!limit.top) {
|
||||||
|
surrounds.push(cell - this.sides - 1);
|
||||||
|
surrounds.push(cell - this.sides + 1);
|
||||||
|
}
|
||||||
|
if (!limit.bottom) {
|
||||||
|
surrounds.push(cell + this.sides - 1);
|
||||||
|
surrounds.push(cell + this.sides + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = 0;
|
||||||
|
surrounds.forEach((number) => {
|
||||||
|
if (number != cell) {
|
||||||
|
if (this.bombs[number]) {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value == 0) {
|
||||||
|
surrounds.forEach((number) => {
|
||||||
|
if (number != cell) {
|
||||||
|
this.discover(number);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.children[cell].innerHTML = String(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Game;
|
48
src/utilities/math.js
Normal file
48
src/utilities/math.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
function angle(a, b) {
|
||||||
|
const dx = a.x - b.x;
|
||||||
|
const dy = a.y - b.y;
|
||||||
|
const angle = Math.atan2(dy, dx);
|
||||||
|
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(x, min, max) {
|
||||||
|
return Math.max(min, Math.min(x, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
function distance(a, b) {
|
||||||
|
const dx = a.x - b.x;
|
||||||
|
const dy = a.y - b.y;
|
||||||
|
|
||||||
|
return Math.sqrt(dx * dx + dy * dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rand(min, max) {
|
||||||
|
return Math.floor(randf(min, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
function randf(min, max) {
|
||||||
|
if (max == null) {
|
||||||
|
max = min || 1;
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
return Math.random() * (max - min) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
function randOneFrom(items) {
|
||||||
|
return items[rand(items.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function randOneIn(max = 2) {
|
||||||
|
return rand(0, max) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
angle,
|
||||||
|
clamp,
|
||||||
|
distance,
|
||||||
|
rand,
|
||||||
|
randf,
|
||||||
|
randOneFrom,
|
||||||
|
randOneIn
|
||||||
|
};
|
15
webpack.config.js
Normal file
15
webpack.config.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
module: {
|
||||||
|
rules: [{
|
||||||
|
test: /\.s[ac]ss$/i,
|
||||||
|
use: [
|
||||||
|
// Creates `style` nodes from JS strings
|
||||||
|
"style-loader",
|
||||||
|
// Translates CSS into CommonJS
|
||||||
|
"css-loader",
|
||||||
|
// Compiles Sass to CSS
|
||||||
|
"sass-loader",
|
||||||
|
],
|
||||||
|
}, ]
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user