mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 15:57:50 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad20a291ec | ||
|
|
befbeaa228 | ||
|
|
467a0a17de | ||
|
|
0bbd696fba | ||
|
|
7c97001363 | ||
|
|
35d280d5ee | ||
|
|
0fe9432528 | ||
|
|
b5c610f9ab | ||
|
|
7cc87d9a48 | ||
|
|
ee4aa21a1d | ||
|
|
4d18ca0d42 | ||
|
|
47263c65a8 | ||
|
|
a7fb2afde1 | ||
|
|
d31999e06e | ||
|
|
7b4a96a8fa | ||
|
|
c180897862 | ||
|
|
088e65c728 | ||
|
|
718ad1b58e | ||
|
|
69a2a88d74 | ||
|
|
5f819f8f17 | ||
|
|
4ad2d6c663 | ||
|
|
01ed3c6842 | ||
|
|
1833bde903 | ||
|
|
915819bb15 |
2
.github/workflows/node.js.yml
vendored
2
.github/workflows/node.js.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14.x]
|
||||
node-version: [20.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Use the official lightweight Node.js 14 image.
|
||||
# https://hub.docker.com/_/node
|
||||
FROM node:14-slim
|
||||
FROM node:20-slim
|
||||
|
||||
# Create and change to the app directory.
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14-slim
|
||||
FROM node:20-slim
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ export const defaultRoles = [
|
||||
{
|
||||
role: 'Villager',
|
||||
team: 'good',
|
||||
description: 'During the day, find the wolves and kill them.'
|
||||
description: 'During the day, find the Werewolves and kill them.'
|
||||
},
|
||||
{
|
||||
role: 'Werewolf',
|
||||
team: 'evil',
|
||||
description: "During the night, choose a villager to kill. Don't get killed."
|
||||
description: 'During the night, choose a player to eliminate.'
|
||||
},
|
||||
{
|
||||
role: 'Dream Wolf',
|
||||
team: 'evil',
|
||||
description: "You are a Werewolf, but you don't wake up with the other Werewolves until one of them dies."
|
||||
description: 'You are a Werewolf, but you don\'t wake up with the other Werewolves until one of them dies.'
|
||||
},
|
||||
{
|
||||
role: 'Sorceress',
|
||||
@@ -22,12 +22,12 @@ export const defaultRoles = [
|
||||
{
|
||||
role: 'Knowing Minion',
|
||||
team: 'evil',
|
||||
description: 'You are an evil Villager, and you know who the Werewolves are.'
|
||||
description: 'You are an evil Villager, and you know who the Werewolves are. You win if the Werewolves win.'
|
||||
},
|
||||
{
|
||||
role: 'Blind Minion',
|
||||
team: 'evil',
|
||||
description: "You are an evil villager, but you don't know who the Werewolves are."
|
||||
description: 'You are an evil villager, but you do NOT know who the Werewolves are. You win if the Werewolves win.'
|
||||
},
|
||||
{
|
||||
role: 'Seer',
|
||||
@@ -48,11 +48,11 @@ export const defaultRoles = [
|
||||
{
|
||||
role: 'Parity Hunter',
|
||||
team: 'good',
|
||||
description: 'You beat a werewolf in a 1v1 situation, winning the game for the village.'
|
||||
description: 'If you and a Werewolf are the only two players remaining, the Village wins.'
|
||||
},
|
||||
{
|
||||
role: 'Brutal Hunter',
|
||||
team: 'good',
|
||||
description: 'When you are eliminated, choose another player to go with you.'
|
||||
description: 'When you are eliminated, choose another player to be eliminated with you.'
|
||||
}
|
||||
];
|
||||
|
||||
BIN
client/src/images/framed-phone-screenshot-2_resized.webp
Normal file
BIN
client/src/images/framed-phone-screenshot-2_resized.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
client/src/images/framed-phone-screenshot_resized.webp
Normal file
BIN
client/src/images/framed-phone-screenshot_resized.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
@@ -10,17 +10,17 @@ export const HTMLFragments = {
|
||||
<canvas id="canvas"></canvas>
|
||||
<div id='game-parameters'>
|
||||
<div>
|
||||
<img alt='clock' src='/images/clock.svg'/>
|
||||
<img alt='clock' width="20" height="20" src='/images/clock.svg'/>
|
||||
<div id='timer-parameters'></div>
|
||||
</div>
|
||||
<div>
|
||||
<img alt='person' src='/images/person.svg'/>
|
||||
<img alt='person' width="22" height="20" src='/images/person.svg'/>
|
||||
<div id='game-player-count'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -84,7 +84,7 @@ export const HTMLFragments = {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id='game-role' tabindex="0">
|
||||
@@ -127,7 +127,7 @@ export const HTMLFragments = {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
@@ -187,7 +187,7 @@ export const HTMLFragments = {
|
||||
Transfer Mod Powers <img alt='transfer icon' src='/images/shuffle.svg'/>
|
||||
</button>
|
||||
<div>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="game-players-container">
|
||||
@@ -229,7 +229,7 @@ export const HTMLFragments = {
|
||||
<div id='play-pause'> </div>
|
||||
</div>
|
||||
<div>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id='game-role' tabindex="0">
|
||||
@@ -309,7 +309,7 @@ export const HTMLFragments = {
|
||||
`<div id='end-of-game-header'>
|
||||
<h2>🏁 The moderator has ended the game. Roles are revealed.</h2>
|
||||
<div id="end-of-game-buttons">
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' src='/images/info.svg'/></button>
|
||||
<button id='role-info-button' class='app-button'>Roles in This Game <img alt='Info icon' width="25" height="25" src='/images/info.svg'/></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
|
||||
@@ -44,6 +44,7 @@ function getNavbarLinks (page = null, device) {
|
||||
'<a class="' + linkClass + '" href="/create">Create</a>' +
|
||||
'<a class="' + linkClass + '" href="/how-to-use">How to Use</a>' +
|
||||
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Feedback</a>' +
|
||||
'<a class="' + linkClass + ' "href="https://buymeacoffee.com/alecm33" target="_top">Support Me</a>' +
|
||||
'<a class="' + linkClass + ' "href="https://github.com/alecm33/Werewolf" target="_top">Github</a>';
|
||||
}
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu
|
||||
document.querySelector('#create-game')?.remove();
|
||||
if (back) {
|
||||
const backButton = document.createElement('button');
|
||||
backButton.innerHTML = '<img alt="back" src="../../images/caret-back.svg"/>';
|
||||
backButton.innerHTML = '<img alt="back" width="40" height="40" src="../../images/caret-back.svg"/>';
|
||||
backButton.addEventListener('click', backHandler);
|
||||
backButton.setAttribute('id', 'step-back-button');
|
||||
backButton.classList.add('app-button');
|
||||
@@ -509,7 +509,7 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu
|
||||
|
||||
if (forward && builtGame === null) {
|
||||
const fwdButton = document.createElement('button');
|
||||
fwdButton.innerHTML = '<img alt="next" src="../../images/caret-forward.svg"/>';
|
||||
fwdButton.innerHTML = '<img alt="next" width="40" height="40" src="../../images/caret-forward.svg"/>';
|
||||
fwdButton.addEventListener('click', forwardHandler);
|
||||
fwdButton.setAttribute('id', 'step-forward-button');
|
||||
fwdButton.classList.add('app-button');
|
||||
|
||||
@@ -18,7 +18,7 @@ import { InProgress } from '../game_state/states/InProgress.js';
|
||||
import { Ended } from '../game_state/states/Ended.js';
|
||||
|
||||
export const gameHandler = (socket, window, gameDOM) => {
|
||||
window.onunload = () => {
|
||||
window.onpagehide = () => {
|
||||
socket.close();
|
||||
};
|
||||
document.body.innerHTML = gameDOM + document.body.innerHTML;
|
||||
@@ -60,8 +60,11 @@ export const gameHandler = (socket, window, gameDOM) => {
|
||||
toast(err, 'error', true, false);
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
socket.on('disconnect', (reason) => {
|
||||
toast('Disconnected. Attempting reconnect...', 'error', true, false);
|
||||
if (reason === 'io client disconnect') { // see https://socket.io/docs/v4/client-api/#event-disconnect
|
||||
socket.connect();
|
||||
}
|
||||
});
|
||||
setClientSocketHandlers(stateBucket, socket);
|
||||
resolve();
|
||||
|
||||
@@ -46,9 +46,7 @@ function attemptToJoinGame (event) {
|
||||
} else {
|
||||
res.json().then(json => {
|
||||
window.location = window.location.protocol + '//' + window.location.host +
|
||||
'/join/' + encodeURIComponent(json.accessCode) +
|
||||
'?playerCount=' + encodeURIComponent(json.playerCount) +
|
||||
'&timer=' + encodeURIComponent(getTimeString(json.timerParams));
|
||||
'/join/' + encodeURIComponent(json.accessCode);
|
||||
});
|
||||
}
|
||||
}).catch(() => {
|
||||
@@ -62,29 +60,6 @@ function attemptToJoinGame (event) {
|
||||
}
|
||||
}
|
||||
|
||||
function getTimeString (timerParams) {
|
||||
let timeString = '';
|
||||
if (timerParams) {
|
||||
const hours = timerParams.hours;
|
||||
const minutes = timerParams.minutes;
|
||||
if (hours) {
|
||||
timeString += hours > 1
|
||||
? hours + ' hours '
|
||||
: hours + ' hour ';
|
||||
}
|
||||
if (minutes) {
|
||||
timeString += minutes > 1
|
||||
? minutes + ' minutes '
|
||||
: minutes + ' minute ';
|
||||
}
|
||||
|
||||
return timeString;
|
||||
} else {
|
||||
timeString = 'untimed';
|
||||
return timeString;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
module.exports = home;
|
||||
} else {
|
||||
|
||||
@@ -299,7 +299,6 @@ button {
|
||||
}
|
||||
|
||||
#game-parameters img {
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,10 @@ button#home-create-button {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
#about-container div:nth-child(2) h2 {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#homepage-logos {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -91,7 +95,7 @@ a button {
|
||||
|
||||
#join-button:hover {
|
||||
background-color: #326243;
|
||||
border: 2px solid #1c8a36;
|
||||
border: 3px solid #1c8a36;
|
||||
}
|
||||
|
||||
#join-form div:nth-child(1) {
|
||||
|
||||
@@ -36,9 +36,9 @@ export const hiddenMenus =
|
||||
<div tabindex="-1" id="custom-role-info-modal" class="modal">
|
||||
<h3 id="custom-role-info-modal-name"></h3>
|
||||
<div id="custom-role-info-modal-image-placeholder"></div>
|
||||
<label for="custom-role-info-modal-alignment">alignment:</label>
|
||||
<label>alignment:</label>
|
||||
<div id="custom-role-info-modal-alignment"></div>
|
||||
<label for="custom-role-info-modal-alignment">description:</label>
|
||||
<label>description:</label>
|
||||
<div id="custom-role-info-modal-description"></div>
|
||||
<div class="modal-button-container single-button">
|
||||
<button id="close-custom-role-info-modal-button" class="cancel app-button">Close</button>
|
||||
|
||||
@@ -11,11 +11,9 @@
|
||||
<meta property="og:url" content="https://play-werewolf.app">
|
||||
<meta property="og:description" content="An app to create and run games of Werewolf (Mafia) with your friends. No sign-up, installation, or payment required.">
|
||||
<meta property="og:image" content="image.png">
|
||||
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
|
||||
<link rel="stylesheet" href="./styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="./styles/home.css">
|
||||
<link rel="stylesheet" href="/styles/hamburgers.css">
|
||||
@@ -34,7 +32,7 @@
|
||||
<button id="home-create-button" class="app-button">Create A Room</button>
|
||||
</a>
|
||||
<div id="join-container">
|
||||
<label for="join-form">Join A Room</label>
|
||||
<label>Join A Room</label>
|
||||
<form id="join-form">
|
||||
<div>
|
||||
<label for="room-code">Room Code</label>
|
||||
@@ -48,24 +46,17 @@
|
||||
<div id="about-container">
|
||||
<div>
|
||||
<div class="framed-phone-screenshot-container">
|
||||
<img id="framed-phone-screenshot" alt="framed phone screenshot" src="../images/framed-phone-screenshot.webp"/>
|
||||
<img id="framed-phone-screenshot" alt="framed phone screenshot" src="../images/framed-phone-screenshot_resized.webp"/>
|
||||
</div>
|
||||
<h2>Join a game and have a role dealt to your device.</h2>
|
||||
</div>
|
||||
<div>
|
||||
<div class="framed-phone-screenshot-container">
|
||||
<img id="framed-phone-screenshot-2" alt="framed phone screenshot" src="../images/framed-phone-screenshot-2.webp"/>
|
||||
<img id="framed-phone-screenshot-2" alt="framed phone screenshot" src="../images/framed-phone-screenshot-2_resized.webp"/>
|
||||
</div>
|
||||
<h2>Create your own game with default or custom roles.</h2>
|
||||
</div>
|
||||
</div>
|
||||
<footer id="footer">
|
||||
<a href="https://www.buymeacoffee.com/alecm33"><img alt="Buy Me a Coffee" src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=alecm33&button_colour=252525&font_colour=d7d7d7&font_family=Lato&outline_colour=d7d7d7&coffee_colour=d7d7d7" /></a>
|
||||
<div>
|
||||
<a aria-label="view the project on Github" href="https://github.com/AlecM33/Werewolf"><img alt="Github" src='/images/GitHub-Mark-Light-120px-plus.png'/></a>
|
||||
<a aria-label="email the creator with questions" href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top"><img alt="email" src='/images/email.svg'/></a>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="/dist/home-bundle.js.gz"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<link rel="stylesheet" href="/styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="/styles/create.css">
|
||||
<link rel="stylesheet" href="/styles/modal.css">
|
||||
<link rel="stylesheet" href="/styles/hamburgers.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -15,7 +15,7 @@ module.exports = {
|
||||
path: path.resolve(__dirname, '../dist'),
|
||||
filename: "[name]-bundle.js"
|
||||
},
|
||||
plugins: [new CompressionPlugin({ deleteOriginalAssets: true, exclude: [/521-bundle.js$/]})],
|
||||
plugins: [new CompressionPlugin({ deleteOriginalAssets: true, exclude: [/\d+-bundle.js$/]})],
|
||||
mode: "production",
|
||||
node: false,
|
||||
module: {
|
||||
|
||||
6312
package-lock.json
generated
6312
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -21,24 +21,24 @@
|
||||
},
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
"node": ">= 20.0.0"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"babel-loader": "^8.2.3",
|
||||
"body-parser": "^1.20.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"express": "^4.21.1",
|
||||
"express-force-https": "^1.0.0",
|
||||
"express-rate-limit": "^6.0.1",
|
||||
"open": "^7.0.3",
|
||||
"rate-limiter-flexible": "^2.3.6",
|
||||
"redis": "^4.5.1",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"socket.io": "^4.6.1",
|
||||
"socket.io-client": "^4.5.4"
|
||||
"socket.io": "^4.8.1",
|
||||
"socket.io-client": "^4.7.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.13",
|
||||
@@ -59,7 +59,7 @@
|
||||
"karma": "^6.3.16",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack": "^5.94.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-remove-debug": "^0.1.0"
|
||||
},
|
||||
|
||||
@@ -95,7 +95,7 @@ const ServerBootstrapper = {
|
||||
res.setHeader(
|
||||
'Content-Security-Policy',
|
||||
"default-src 'self'; font-src 'self' https://fonts.gstatic.com/; img-src 'self' https://img.buymeacoffee.com;" +
|
||||
" script-src 'self' https://cdnjs.buymeacoffee.com; style-src 'self' https://cdnjs.buymeacoffee.com https://fonts.googleapis.com/ 'nonce-" + nonce + "'; frame-src 'self'"
|
||||
" script-src 'self'; style-src 'self' https://fonts.googleapis.com/ 'nonce-" + nonce + "'; frame-src 'self'"
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -191,7 +191,7 @@ class GameManager {
|
||||
&& game.people.filter(person => person.userType === USER_TYPES.SPECTATOR).length === PRIMITIVES.MAX_SPECTATORS
|
||||
) {
|
||||
return Promise.reject({ status: 400, reason: 'There are too many people already spectating.' });
|
||||
} else if (joinAsSpectator || this.isGameStartable(game)) {
|
||||
} else if (joinAsSpectator || this.isGameStartable(game) || game.status === STATUS.IN_PROGRESS) {
|
||||
return await addSpectator(game, name, this.logger, this.namespace, this.eventManager, this.instanceId, this.refreshGame);
|
||||
}
|
||||
let moderator, newPlayer;
|
||||
|
||||
Reference in New Issue
Block a user