This commit is contained in:
Pingumask 2023-11-10 23:28:33 +01:00
commit 080296e0aa
40 changed files with 699 additions and 620 deletions

View file

@ -1,16 +1,16 @@
module.exports = { module.exports = {
root: true, root: true,
env: { env: {
node: true node: true,
}, },
extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"], extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"],
parserOptions: { parserOptions: {
ecmaVersion: 2020 ecmaVersion: 2020,
}, },
rules: { rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"vue/multi-word-component-names": "off", "vue/multi-word-component-names": "off",
"vue/no-reserved-component-names": "off", "vue/no-reserved-component-names": "off",
} },
}; };

View file

@ -1,11 +1,17 @@
# Release Notes # Release Notes
---
## Upcomming Version ## Upcomming Version
### Version 3.17.0
- Updating english jinxes
- Adding an asterisk in the roles reference
- Correcting the message when someone wants to exile a Traveller
- Correcting Leviathan's english description
- Correcting "Late Night Drive By"'s name
- Adding a token "Used" to the Doomsayer
- Updated packages & Dockerfile for node >=18
### Version 3.16.0 (merged upstream 2.16.2) ### Version 3.16.0 (merged upstream 2.16.2)
- fixed custom script format to support new script tool JSON - fixed custom script format to support new script tool JSON
- updated packages to be compatible with Node >= 18 again - updated packages to be compatible with Node >= 18 again

View file

@ -1,10 +1,11 @@
FROM node:16 FROM node:18
RUN apt update && apt install -y\ RUN apt update && apt install -y\
git git\
WORKDIR /app && apt clean
WORKDIR /app/townsquare
COPY package*.json . COPY package*.json .
RUN npm install
# npm rebuild avoids having misconfigurations if npm install has been run in the folder from windows before building the docker image
RUN npm rebuild RUN npm rebuild
RUN npm clean-install
# npm rebuild avoids having misconfigurations if npm install has been run in the folder from windows before building the docker image
COPY . . COPY . .
CMD ["npm","run","serve"] CMD ["npm","run","serve"]

View file

@ -6,12 +6,15 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- "${SSH_KEYS}:/root/.ssh" - "${SSH_KEYS}:/root/.ssh"
- botc_front:/app/townsquare
environment: environment:
NODE_ENV: development NODE_ENV: development
ports: ports:
- "${NODE_PORT}:8080" - "${NODE_PORT}:8080"
working_dir: /app working_dir: /app/townsquare
logging: logging:
driver: "json-file" driver: "json-file"
options: options:
max-size: "200k" max-size: "200k"
volumes:
botc_front:

194
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "townsquare", "name": "townsquare",
"version": "3.16.0", "version": "3.17.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "townsquare", "name": "townsquare",
"version": "3.15.0", "version": "3.17.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.32", "@fortawesome/fontawesome-svg-core": "^1.2.32",
@ -29,6 +29,9 @@
"eslint-plugin-prettier": "^5.0.1", "eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-vue": "^9.18.1", "eslint-plugin-vue": "^9.18.1",
"prettier": "^3.0.3" "prettier": "^3.0.3"
},
"engines": {
"node": "^16"
} }
}, },
"node_modules/@aashutoshrathi/word-wrap": { "node_modules/@aashutoshrathi/word-wrap": {
@ -130,9 +133,9 @@
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.23.2", "version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz",
"integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
@ -246,9 +249,9 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.23.0", "version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
}, },
@ -624,66 +627,66 @@
} }
}, },
"node_modules/@types/body-parser": { "node_modules/@types/body-parser": {
"version": "1.19.4", "version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==", "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dependencies": { "dependencies": {
"@types/connect": "*", "@types/connect": "*",
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/bonjour": { "node_modules/@types/bonjour": {
"version": "3.5.12", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.12.tgz", "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz",
"integrity": "sha512-ky0kWSqXVxSqgqJvPIkgFkcn4C8MnRog308Ou8xBBIVo39OmUFy+jqNe0nPwLCDFxUpmT9EvT91YzOJgkDRcFg==", "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/connect": { "node_modules/@types/connect": {
"version": "3.4.37", "version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/connect-history-api-fallback": { "node_modules/@types/connect-history-api-fallback": {
"version": "1.5.2", "version": "1.5.3",
"resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.2.tgz", "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz",
"integrity": "sha512-gX2j9x+NzSh4zOhnRPSdPPmTepS4DfxES0AvIFv3jGv5QyeAJf6u6dY5/BAoAJU9Qq1uTvwOku8SSC2GnCRl6Q==", "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==",
"dependencies": { "dependencies": {
"@types/express-serve-static-core": "*", "@types/express-serve-static-core": "*",
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.44.6", "version": "8.44.7",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz",
"integrity": "sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==", "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==",
"dependencies": { "dependencies": {
"@types/estree": "*", "@types/estree": "*",
"@types/json-schema": "*" "@types/json-schema": "*"
} }
}, },
"node_modules/@types/eslint-scope": { "node_modules/@types/eslint-scope": {
"version": "3.7.6", "version": "3.7.7",
"resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz", "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
"integrity": "sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
"dependencies": { "dependencies": {
"@types/eslint": "*", "@types/eslint": "*",
"@types/estree": "*" "@types/estree": "*"
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.4", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.4.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-2JwWnHK9H+wUZNorf2Zr6ves96WHoWDJIftkcxPKsS7Djta6Zu519LarhRNljPXkpsZR2ZMwNCPeW7omW07BJw==" "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
}, },
"node_modules/@types/express": { "node_modules/@types/express": {
"version": "4.17.20", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33", "@types/express-serve-static-core": "^4.17.33",
@ -692,9 +695,9 @@
} }
}, },
"node_modules/@types/express-serve-static-core": { "node_modules/@types/express-serve-static-core": {
"version": "4.17.39", "version": "4.17.41",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
"integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==", "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"@types/qs": "*", "@types/qs": "*",
@ -708,68 +711,68 @@
"integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
}, },
"node_modules/@types/http-errors": { "node_modules/@types/http-errors": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==" "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="
}, },
"node_modules/@types/http-proxy": { "node_modules/@types/http-proxy": {
"version": "1.17.13", "version": "1.17.14",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz",
"integrity": "sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==", "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.14", "version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==" "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
}, },
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "1.3.4", "version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==" "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
}, },
"node_modules/@types/minimist": { "node_modules/@types/minimist": {
"version": "1.2.4", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.4.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-Kfe/D3hxHTusnPNRbycJE1N77WHDsdS4AjUYIzlDzhDrS47NrwuL3YW4VITxwR7KCVpzwgy4Rbj829KSSQmwXQ==" "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.8.10", "version": "20.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz",
"integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==",
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/node-forge": { "node_modules/@types/node-forge": {
"version": "1.3.8", "version": "1.3.9",
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.8.tgz", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz",
"integrity": "sha512-vGXshY9vim9CJjrpcS5raqSjEfKlJcWy2HNdgUasR66fAnVEYarrf1ULV4nfvpC1nZq/moA9qyqBcu83x+Jlrg==", "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
"version": "2.4.3", "version": "2.4.4",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
"integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==" "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
"integrity": "sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==" "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="
}, },
"node_modules/@types/qs": { "node_modules/@types/qs": {
"version": "6.9.9", "version": "6.9.10",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
"integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==" "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
}, },
"node_modules/@types/range-parser": { "node_modules/@types/range-parser": {
"version": "1.2.6", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==" "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
}, },
"node_modules/@types/retry": { "node_modules/@types/retry": {
"version": "0.12.0", "version": "0.12.0",
@ -777,26 +780,26 @@
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="
}, },
"node_modules/@types/send": { "node_modules/@types/send": {
"version": "0.17.3", "version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dependencies": { "dependencies": {
"@types/mime": "^1", "@types/mime": "^1",
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/serve-index": { "node_modules/@types/serve-index": {
"version": "1.9.3", "version": "1.9.4",
"resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.3.tgz", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz",
"integrity": "sha512-4KG+yMEuvDPRrYq5fyVm/I2uqAJSAwZK9VSa+Zf+zUq9/oxSSvy3kkIqyL+jjStv6UCVi8/Aho0NHtB1Fwosrg==", "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==",
"dependencies": { "dependencies": {
"@types/express": "*" "@types/express": "*"
} }
}, },
"node_modules/@types/serve-static": { "node_modules/@types/serve-static": {
"version": "1.15.4", "version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
"integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==", "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
"dependencies": { "dependencies": {
"@types/http-errors": "*", "@types/http-errors": "*",
"@types/mime": "*", "@types/mime": "*",
@ -804,17 +807,17 @@
} }
}, },
"node_modules/@types/sockjs": { "node_modules/@types/sockjs": {
"version": "0.3.35", "version": "0.3.36",
"resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.35.tgz", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz",
"integrity": "sha512-tIF57KB+ZvOBpAQwSaACfEu7htponHXaFzP7RfKYgsOS0NoYnn+9+jzp7bbq4fWerizI3dTB4NfAZoyeQKWJLw==", "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.8", "version": "8.5.9",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.8.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz",
"integrity": "sha512-flUksGIQCnJd6sZ1l5dqCEG/ksaoAg/eUwiLAGTJQcfgvZJKF++Ta4bJA6A5aPSJmsr+xlseHn4KLgVlNnvPTg==", "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==",
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
@ -2986,9 +2989,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.576", "version": "1.4.581",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.576.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz",
"integrity": "sha512-yXsZyXJfAqzWk1WKryr0Wl0MN2D47xodPvEEwlVePBnhU5E7raevLQR+E6b9JAD3GfL/7MbAL9ZtWQQPcLx7wA==" "integrity": "sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw=="
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
@ -3056,9 +3059,9 @@
} }
}, },
"node_modules/es-module-lexer": { "node_modules/es-module-lexer": {
"version": "1.3.1", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.0.tgz",
"integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==" "integrity": "sha512-lcCr3v3OLezdfFyx9r5NRYHOUTQNnFEQ9E87Mx8Kc+iqyJNkO7MJoB4GQRTlIMw9kLLTwGw0OAkm4BQQud/d9g=="
}, },
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.1.1",
@ -3619,9 +3622,9 @@
"dev": true "dev": true
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"dependencies": { "dependencies": {
"@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3", "@nodelib/fs.walk": "^1.2.3",
@ -8027,13 +8030,6 @@
"dependencies": { "dependencies": {
"hash-sum": "^1.0.2", "hash-sum": "^1.0.2",
"loader-utils": "^1.0.2" "loader-utils": "^1.0.2"
},
"dependencies": {
"hash-sum": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
}
} }
}, },
"node_modules/vue-style-loader/node_modules/hash-sum": { "node_modules/vue-style-loader/node_modules/hash-sum": {

View file

@ -1,6 +1,6 @@
{ {
"name": "townsquare", "name": "townsquare",
"version": "3.16.0", "version": "3.17.0",
"description": "Blood on the Clocktower Town Square", "description": "Blood on the Clocktower Town Square",
"author": "Pingumaskt", "author": "Pingumaskt",
"scripts": { "scripts": {

View file

@ -5,11 +5,11 @@
tabindex="-1" tabindex="-1"
:class="{ :class="{
night: grimoire.isNight, night: grimoire.isNight,
static: grimoire.isStatic static: grimoire.isStatic,
}" }"
:style="{ :style="{
backgroundImage: `url('${background}')`, backgroundImage: `url('${background}')`,
backgroundColor: `${backgroundColor}` backgroundColor: `${backgroundColor}`,
}" }"
> >
<video <video
@ -70,24 +70,24 @@ export default {
Menu, Menu,
EditionModal, EditionModal,
RolesModal, RolesModal,
Gradients Gradients,
}, },
computed: { computed: {
...mapState(["grimoire", "session", "edition"]), ...mapState(["grimoire", "session", "edition"]),
...mapState("players", ["players"]), ...mapState("players", ["players"]),
background: function() { background: function () {
if (this.grimoire.isStreamerMode) { if (this.grimoire.isStreamerMode) {
return "none"; return "none";
} }
return this.grimoire.background || this.edition.background || "none"; return this.grimoire.background || this.edition.background || "none";
}, },
backgroundColor: function() { backgroundColor: function () {
return this.grimoire.isStreamerMode ? "#00FF00" : "transparent"; return this.grimoire.isStreamerMode ? "#00FF00" : "transparent";
} },
}, },
data() { data() {
return { return {
version version,
}; };
}, },
methods: { methods: {
@ -136,8 +136,8 @@ export default {
case "escape": case "escape":
this.$store.commit("toggleModal"); this.$store.commit("toggleModal");
} }
} },
} },
}; };
</script> </script>
@ -147,7 +147,8 @@ export default {
@font-face { @font-face {
font-family: "Papyrus"; font-family: "Papyrus";
src: url("assets/fonts/papyrus.eot"); /* IE9*/ src: url("assets/fonts/papyrus.eot"); /* IE9*/
src: url("assets/fonts/papyrus.eot?#iefix") format("embedded-opentype"), src:
url("assets/fonts/papyrus.eot?#iefix") format("embedded-opentype"),
/* IE6-IE8 */ url("assets/fonts/papyrus.woff2") format("woff2"), /* IE6-IE8 */ url("assets/fonts/papyrus.woff2") format("woff2"),
/* chrome firefox */ url("assets/fonts/papyrus.woff") format("woff"), /* chrome firefox */ url("assets/fonts/papyrus.woff") format("woff"),
/* chrome firefox */ url("assets/fonts/papyrus.ttf") format("truetype"), /* chrome firefox */ url("assets/fonts/papyrus.ttf") format("truetype"),
@ -270,13 +271,12 @@ ul {
padding: 0; padding: 0;
border: solid 0.125em transparent; border: solid 0.125em transparent;
border-radius: 15px; border-radius: 15px;
box-shadow: inset 0 1px 1px #9c9c9c, 0 0 10px #000; box-shadow:
background: radial-gradient( inset 0 1px 1px #9c9c9c,
at 0 -15%, 0 0 10px #000;
rgba(#fff, 0.07) 70%, background:
rgba(#fff, 0) 71% radial-gradient(at 0 -15%, rgba(#fff, 0.07) 70%, rgba(#fff, 0) 71%) 0 0/ 80%
) 90% no-repeat content-box,
0 0/ 80% 90% no-repeat content-box,
linear-gradient(#4e4e4e, #040404) content-box, linear-gradient(#4e4e4e, #040404) content-box,
linear-gradient(#292929, #010101) border-box; linear-gradient(#292929, #010101) border-box;
color: white; color: white;
@ -303,7 +303,8 @@ ul {
height: 10px; height: 10px;
} }
&.townsfolk { &.townsfolk {
background: radial-gradient( background:
radial-gradient(
at 0 -15%, at 0 -15%,
rgba(255, 255, 255, 0.07) 70%, rgba(255, 255, 255, 0.07) 70%,
rgba(255, 255, 255, 0) 71% rgba(255, 255, 255, 0) 71%
@ -311,13 +312,16 @@ ul {
0 0/80% 90% no-repeat content-box, 0 0/80% 90% no-repeat content-box,
linear-gradient(#0031ad, rgba(5, 0, 0, 0.22)) content-box, linear-gradient(#0031ad, rgba(5, 0, 0, 0.22)) content-box,
linear-gradient(#292929, #001142) border-box; linear-gradient(#292929, #001142) border-box;
box-shadow: inset 0 1px 1px #002c9c, 0 0 10px #000; box-shadow:
inset 0 1px 1px #002c9c,
0 0 10px #000;
&:hover:not(.disabled) { &:hover:not(.disabled) {
color: #008cf7; color: #008cf7;
} }
} }
&.demon { &.demon {
background: radial-gradient( background:
radial-gradient(
at 0 -15%, at 0 -15%,
rgba(255, 255, 255, 0.07) 70%, rgba(255, 255, 255, 0.07) 70%,
rgba(255, 255, 255, 0) 71% rgba(255, 255, 255, 0) 71%
@ -325,7 +329,9 @@ ul {
0 0/80% 90% no-repeat content-box, 0 0/80% 90% no-repeat content-box,
linear-gradient(#ad0000, rgba(5, 0, 0, 0.22)) content-box, linear-gradient(#ad0000, rgba(5, 0, 0, 0.22)) content-box,
linear-gradient(#292929, #420000) border-box; linear-gradient(#292929, #420000) border-box;
box-shadow: inset 0 1px 1px #9c0000, 0 0 10px #000; box-shadow:
inset 0 1px 1px #9c0000,
0 0 10px #000;
} }
} }

View file

@ -2,7 +2,7 @@
{ {
"id": "_meta", "id": "_meta",
"logo": "https://cdn.shopify.com/oxygen/57467011226/524442/h11pcta59/build/_assets/Godfather_website_purple-3JVCH57E.png", "logo": "https://cdn.shopify.com/oxygen/57467011226/524442/h11pcta59/build/_assets/Godfather_website_purple-3JVCH57E.png",
"name": "Late night drive by 1.6", "name": "Late night drive by",
"author": "Aero" "author": "Aero"
}, },
{ {

View file

@ -6,13 +6,13 @@
export default { export default {
props: { props: {
timerName: String, timerName: String,
timerDuration: Number timerDuration: Number,
}, },
computed: { computed: {
style() { style() {
return `--timer: ${this.timerDuration}`; return `--timer: ${this.timerDuration}`;
} },
} },
}; };
</script> </script>

View file

@ -29,10 +29,10 @@ export default {
["demon", "#ce0100", "#000"], ["demon", "#ce0100", "#000"],
["townsfolk", "#1f65ff", "#000"], ["townsfolk", "#1f65ff", "#000"],
["minion", "#ff6900", "#000"], ["minion", "#ff6900", "#000"],
["default", "#4E4E4E", "#000"] ["default", "#4E4E4E", "#000"],
] ],
}; };
} },
}; };
</script> </script>

View file

@ -30,14 +30,14 @@ import { mapMutations, mapState, mapGetters } from "vuex";
export default { export default {
computed: { computed: {
...mapState(["locale"]), ...mapState(["locale"]),
...mapGetters({ nightOrder: "players/nightOrder" }) ...mapGetters({ nightOrder: "players/nightOrder" }),
}, },
data() { data() {
return { return {
language: window.navigator.userLanguage || window.navigator.language language: window.navigator.userLanguage || window.navigator.language,
}; };
}, },
methods: mapMutations(["toggleMenu"]) methods: mapMutations(["toggleMenu"]),
}; };
</script> </script>

View file

@ -4,11 +4,9 @@
class="nomlog-summary" class="nomlog-summary"
v-show="session.voteHistory.length && session.sessionId" v-show="session.voteHistory.length && session.sessionId"
@click="toggleModal('voteHistory')" @click="toggleModal('voteHistory')"
:title=" :title="`${session.voteHistory.length} recent ${
`${session.voteHistory.length} recent ${ session.voteHistory.length == 1 ? 'nomination' : 'nominations'
session.voteHistory.length == 1 ? 'nomination' : 'nominations' }`"
}`
"
> >
<font-awesome-icon icon="book-dead" /> <font-awesome-icon icon="book-dead" />
{{ session.voteHistory.length }} {{ session.voteHistory.length }}
@ -17,15 +15,13 @@
class="session" class="session"
:class="{ :class="{
spectator: session.isSpectator, spectator: session.isSpectator,
reconnecting: session.isReconnecting reconnecting: session.isReconnecting,
}" }"
v-if="session.sessionId" v-if="session.sessionId"
@click="leaveSession" @click="leaveSession"
:title=" :title="`${session.playerCount} other players in this session${
`${session.playerCount} other players in this session${ session.ping ? ' (' + session.ping + 'ms latency)' : ''
session.ping ? ' (' + session.ping + 'ms latency)' : '' }`"
}`
"
> >
<font-awesome-icon icon="broadcast-tower" /> <font-awesome-icon icon="broadcast-tower" />
{{ session.playerCount }} {{ session.playerCount }}
@ -76,7 +72,7 @@
<font-awesome-icon <font-awesome-icon
:icon="[ :icon="[
'fas', 'fas',
grimoire.isOrganVoteMode ? 'check-square' : 'square' grimoire.isOrganVoteMode ? 'check-square' : 'square',
]" ]"
/> />
</em> </em>
@ -87,7 +83,7 @@
<font-awesome-icon <font-awesome-icon
:icon="[ :icon="[
'fas', 'fas',
grimoire.isNightOrder ? 'check-square' : 'square' grimoire.isNightOrder ? 'check-square' : 'square',
]" ]"
/> />
</em> </em>
@ -108,7 +104,7 @@
</li> </li>
<li @click="setBackground"> <li @click="setBackground">
{{ locale.menu.grimoire.background }} {{ locale.menu.grimoire.background }}
<em><font-awesome-icon icon="image"/></em> <em><font-awesome-icon icon="image" /></em>
</li> </li>
<li v-if="!edition.isOfficial" @click="imageOptIn"> <li v-if="!edition.isOfficial" @click="imageOptIn">
<small>{{ locale.menu.grimoire.customImages }}</small> <small>{{ locale.menu.grimoire.customImages }}</small>
@ -116,7 +112,7 @@
><font-awesome-icon ><font-awesome-icon
:icon="[ :icon="[
'fas', 'fas',
grimoire.isImageOptIn ? 'check-square' : 'square' grimoire.isImageOptIn ? 'check-square' : 'square',
]" ]"
/></em> /></em>
</li> </li>
@ -126,7 +122,7 @@
><font-awesome-icon ><font-awesome-icon
:icon="[ :icon="[
'fas', 'fas',
grimoire.isStreamerMode ? 'check-square' : 'square' grimoire.isStreamerMode ? 'check-square' : 'square',
]" ]"
/></em> /></em>
</li> </li>
@ -178,11 +174,11 @@
</li> </li>
<li @click="copySessionUrl"> <li @click="copySessionUrl">
{{ locale.menu.session.link }} {{ locale.menu.session.link }}
<em><font-awesome-icon icon="copy"/></em> <em><font-awesome-icon icon="copy" /></em>
</li> </li>
<li v-if="!session.isSpectator" @click="distributeRoles"> <li v-if="!session.isSpectator" @click="distributeRoles">
{{ locale.menu.session.sendRoles }} {{ locale.menu.session.sendRoles }}
<em><font-awesome-icon icon="theater-masks"/></em> <em><font-awesome-icon icon="theater-masks" /></em>
</li> </li>
<li <li
v-if="session.voteHistory.length || !session.isSpectator" v-if="session.voteHistory.length || !session.isSpectator"
@ -205,11 +201,11 @@
</li> </li>
<li @click="randomizeSeatings" v-if="players.length > 2"> <li @click="randomizeSeatings" v-if="players.length > 2">
{{ locale.menu.players.randomize }} {{ locale.menu.players.randomize }}
<em><font-awesome-icon icon="dice"/></em> <em><font-awesome-icon icon="dice" /></em>
</li> </li>
<li @click="clearPlayers" v-if="players.length"> <li @click="clearPlayers" v-if="players.length">
{{ locale.menu.players.removeAll }} {{ locale.menu.players.removeAll }}
<em><font-awesome-icon icon="trash-alt"/></em> <em><font-awesome-icon icon="trash-alt" /></em>
</li> </li>
</template> </template>
@ -229,11 +225,11 @@
</li> </li>
<li v-if="!session.isSpectator" @click="toggleModal('fabled')"> <li v-if="!session.isSpectator" @click="toggleModal('fabled')">
{{ locale.menu.characters.addFabled }} {{ locale.menu.characters.addFabled }}
<em><font-awesome-icon icon="dragon"/></em> <em><font-awesome-icon icon="dragon" /></em>
</li> </li>
<li @click="clearRoles" v-if="players.length"> <li @click="clearRoles" v-if="players.length">
{{ locale.menu.characters.removeAll }} {{ locale.menu.characters.removeAll }}
<em><font-awesome-icon icon="trash-alt"/></em> <em><font-awesome-icon icon="trash-alt" /></em>
</li> </li>
</template> </template>
@ -250,7 +246,7 @@
</li> </li>
<li @click="toggleModal('gameState')"> <li @click="toggleModal('gameState')">
{{ locale.menu.help.gameState }} {{ locale.menu.help.gameState }}
<em><font-awesome-icon icon="file-code"/></em> <em><font-awesome-icon icon="file-code" /></em>
</li> </li>
<li> <li>
<a href="https://discord.gg/gD3AB8qCrw" target="_blank"> <a href="https://discord.gg/gD3AB8qCrw" target="_blank">
@ -284,11 +280,11 @@ import { mapMutations, mapState } from "vuex";
export default { export default {
computed: { computed: {
...mapState(["grimoire", "session", "edition", "locale"]), ...mapState(["grimoire", "session", "edition", "locale"]),
...mapState("players", ["players"]) ...mapState("players", ["players"]),
}, },
data() { data() {
return { return {
tab: "grimoire" tab: "grimoire",
}; };
}, },
methods: { methods: {
@ -302,7 +298,7 @@ export default {
if (this.session.sessionId) return; if (this.session.sessionId) return;
const sessionId = prompt( const sessionId = prompt(
this.locale.prompt.createSession, this.locale.prompt.createSession,
Math.round(Math.random() * 10000) Math.round(Math.random() * 10000),
); );
if (sessionId) { if (sessionId) {
this.$store.commit("session/clearVoteHistory"); this.$store.commit("session/clearVoteHistory");
@ -326,7 +322,7 @@ export default {
(() => { (() => {
this.$store.commit("session/distributeRoles", false); this.$store.commit("session/distributeRoles", false);
}).bind(this), }).bind(this),
2000 2000,
); );
} }
}, },
@ -409,9 +405,9 @@ export default {
"toggleNightOrder", "toggleNightOrder",
"toggleStatic", "toggleStatic",
"setZoom", "setZoom",
"toggleModal" "toggleModal",
]) ]),
} },
}; };
</script> </script>

View file

@ -10,9 +10,9 @@
'no-vote': player.isVoteless, 'no-vote': player.isVoteless,
you: session.sessionId && player.id && player.id === session.playerId, you: session.sessionId && player.id && player.id === session.playerId,
'vote-yes': session.votes[index], 'vote-yes': session.votes[index],
'vote-lock': voteLocked 'vote-lock': voteLocked,
}, },
player.role.team player.role.team,
]" ]"
> >
<div class="shroud" @click="toggleStatus()"></div> <div class="shroud" @click="toggleStatus()"></div>
@ -22,7 +22,7 @@
class="night-order first" class="night-order first"
v-if=" v-if="
nightOrder.get(player).first && nightOrder.get(player).first &&
(grimoire.isNightOrder || !session.isSpectator) (grimoire.isNightOrder || !session.isSpectator)
" "
> >
<em>{{ nightOrder.get(player).first }}.</em> <em>{{ nightOrder.get(player).first }}.</em>
@ -34,7 +34,7 @@
class="night-order other" class="night-order other"
v-if=" v-if="
nightOrder.get(player).other && nightOrder.get(player).other &&
(grimoire.isNightOrder || !session.isSpectator) (grimoire.isNightOrder || !session.isSpectator)
" "
> >
<em>{{ nightOrder.get(player).other }}.</em> <em>{{ nightOrder.get(player).other }}.</em>
@ -53,8 +53,8 @@
<font-awesome-icon <font-awesome-icon
v-if=" v-if="
!grimoire.isOrganVoteMode || !grimoire.isOrganVoteMode ||
!session.isSpectator || !session.isSpectator ||
player.id == session.playerId player.id == session.playerId
" "
icon="hand-paper" icon="hand-paper"
class="vote" class="vote"
@ -64,8 +64,8 @@
<font-awesome-icon <font-awesome-icon
v-if=" v-if="
grimoire.isOrganVoteMode && grimoire.isOrganVoteMode &&
session.isSpectator && session.isSpectator &&
player.id !== session.playerId player.id !== session.playerId
" "
icon="question" icon="question"
class="vote" class="vote"
@ -75,8 +75,8 @@
<font-awesome-icon <font-awesome-icon
v-if=" v-if="
!grimoire.isOrganVoteMode || !grimoire.isOrganVoteMode ||
!session.isSpectator || !session.isSpectator ||
player.id == session.playerId player.id == session.playerId
" "
icon="times" icon="times"
class="vote" class="vote"
@ -86,8 +86,8 @@
<font-awesome-icon <font-awesome-icon
v-if=" v-if="
grimoire.isOrganVoteMode && grimoire.isOrganVoteMode &&
session.isSpectator && session.isSpectator &&
player.id !== session.playerId player.id !== session.playerId
" "
icon="question" icon="question"
class="vote" class="vote"
@ -159,7 +159,7 @@
@click="changePronouns" @click="changePronouns"
v-if=" v-if="
!session.isSpectator || !session.isSpectator ||
(session.isSpectator && player.id === session.playerId) (session.isSpectator && player.id === session.playerId)
" "
> >
<font-awesome-icon icon="venus-mars" />{{ <font-awesome-icon icon="venus-mars" />{{
@ -230,10 +230,12 @@
backgroundImage: `url(${ backgroundImage: `url(${
reminder.image && grimoire.isImageOptIn reminder.image && grimoire.isImageOptIn
? reminder.image ? reminder.image
: require('../assets/icons/' + : require(
(reminder.imageAlt || reminder.role) + '../assets/icons/' +
'.png') (reminder.imageAlt || reminder.role) +
})` '.png',
)
})`,
}" }"
></span> ></span>
<span class="text">{{ reminder.name }}</span> <span class="text">{{ reminder.name }}</span>
@ -252,22 +254,22 @@ import { mapGetters, mapState } from "vuex";
export default { export default {
components: { components: {
Token Token,
}, },
props: { props: {
player: { player: {
type: Object, type: Object,
required: true required: true,
} },
}, },
computed: { computed: {
...mapState("players", ["players"]), ...mapState("players", ["players"]),
...mapState(["grimoire", "session", "locale"]), ...mapState(["grimoire", "session", "locale"]),
...mapGetters({ nightOrder: "players/nightOrder" }), ...mapGetters({ nightOrder: "players/nightOrder" }),
index: function() { index: function () {
return this.players.indexOf(this.player); return this.players.indexOf(this.player);
}, },
voteLocked: function() { voteLocked: function () {
const session = this.session; const session = this.session;
const players = this.players.length; const players = this.players.length;
if (!session.nomination) return false; if (!session.nomination) return false;
@ -275,7 +277,7 @@ export default {
(this.index - 1 + players - session.nomination[1]) % players; (this.index - 1 + players - session.nomination[1]) % players;
return indexAdjusted < session.lockedVote - 1; return indexAdjusted < session.lockedVote - 1;
}, },
zoom: function() { zoom: function () {
if (this.players.length < 7) { if (this.players.length < 7) {
return { width: 18 + this.grimoire.zoom + "vmin" }; return { width: 18 + this.grimoire.zoom + "vmin" };
} else if (this.players.length <= 10) { } else if (this.players.length <= 10) {
@ -285,12 +287,12 @@ export default {
} else { } else {
return { width: 12 + this.grimoire.zoom + "vmin" }; return { width: 12 + this.grimoire.zoom + "vmin" };
} }
} },
}, },
data() { data() {
return { return {
isMenuOpen: false, isMenuOpen: false,
isSwap: false isSwap: false,
}; };
}, },
methods: { methods: {
@ -346,7 +348,7 @@ export default {
this.$store.commit("players/update", { this.$store.commit("players/update", {
player: this.player, player: this.player,
property, property,
value value,
}); });
if (closeMenu) { if (closeMenu) {
this.isMenuOpen = false; this.isMenuOpen = false;
@ -383,10 +385,10 @@ export default {
if (!this.voteLocked) return; if (!this.voteLocked) return;
this.$store.commit("session/voteSync", [ this.$store.commit("session/voteSync", [
this.index, this.index,
!this.session.votes[this.index] !this.session.votes[this.index],
]); ]);
} },
} },
}; };
</script> </script>
@ -924,7 +926,10 @@ li.move:not(.from) .player .overlay svg.move {
width: 100%; width: 100%;
position: absolute; position: absolute;
top: 15%; top: 15%;
text-shadow: 0 1px 1px #f6dfbd, 0 -1px 1px #f6dfbd, 1px 0 1px #f6dfbd, text-shadow:
0 1px 1px #f6dfbd,
0 -1px 1px #f6dfbd,
1px 0 1px #f6dfbd,
-1px 0 1px #f6dfbd; -1px 0 1px #f6dfbd;
} }

View file

@ -8,7 +8,7 @@
role.image && grimoire.isImageOptIn role.image && grimoire.isImageOptIn
? role.image ? role.image
: require('../assets/icons/' + (role.imageAlt || role.id) + '.png') : require('../assets/icons/' + (role.imageAlt || role.id) + '.png')
})` })`,
}" }"
></span> ></span>
<span <span
@ -54,29 +54,29 @@ export default {
props: { props: {
role: { role: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
} },
}, },
computed: { computed: {
reminderLeaves: function() { reminderLeaves: function () {
return ( return (
(this.role.reminders || []).length + (this.role.reminders || []).length +
(this.role.remindersGlobal || []).length (this.role.remindersGlobal || []).length
); );
}, },
...mapState(["grimoire"]) ...mapState(["grimoire"]),
}, },
data() { data() {
return {}; return {};
}, },
filters: { filters: {
nameToFontSize: name => (name && name.length > 10 ? "90%" : "110%") nameToFontSize: (name) => (name && name.length > 10 ? "90%" : "110%"),
}, },
methods: { methods: {
setRole() { setRole() {
this.$emit("set-role"); this.$emit("set-role");
} },
} },
}; };
</script> </script>

View file

@ -8,7 +8,7 @@
edition.logo && grimoire.isImageOptIn edition.logo && grimoire.isImageOptIn
? edition.logo ? edition.logo
: require('../assets/editions/' + edition.id + '.png') : require('../assets/editions/' + edition.id + '.png')
})` })`,
}" }"
></li> ></li>
<li v-if="players.length - teams.traveler < 5"> <li v-if="players.length - teams.traveler < 5">
@ -99,15 +99,15 @@ import Countdown from "./Countdown";
export default { export default {
components: { components: {
Countdown Countdown,
}, },
computed: { computed: {
teams: function() { teams: function () {
const { players } = this.$store.state.players; const { players } = this.$store.state.players;
const nonTravelers = this.$store.getters["players/nonTravelers"]; const nonTravelers = this.$store.getters["players/nonTravelers"];
const alive = players.filter(player => player.isDead !== true).length; const alive = players.filter((player) => player.isDead !== true).length;
const aliveNT = players.filter( const aliveNT = players.filter(
player => player.isDead !== true && player.role.team !== "traveler" (player) => player.isDead !== true && player.role.team !== "traveler",
).length; ).length;
return { return {
...gameJSON[nonTravelers - 5], ...gameJSON[nonTravelers - 5],
@ -117,16 +117,16 @@ export default {
votes: votes:
alive + alive +
players.filter( players.filter(
player => player.isDead === true && player.isVoteless !== true (player) => player.isDead === true && player.isVoteless !== true,
).length ).length,
}; };
}, },
countdownStyle: function() { countdownStyle: function () {
return `--timer: ${this.$store.state.grimoire.timer.duration}`; return `--timer: ${this.$store.state.grimoire.timer.duration}`;
}, },
...mapState(["edition", "grimoire", "locale"]), ...mapState(["edition", "grimoire", "locale"]),
...mapState("players", ["players"]) ...mapState("players", ["players"]),
} },
}; };
</script> </script>
@ -153,7 +153,10 @@ export default {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
text-shadow: 0 2px 1px black, 0 -2px 1px black, 2px 0 1px black, text-shadow:
0 2px 1px black,
0 -2px 1px black,
2px 0 1px black,
-2px 0 1px black; -2px 0 1px black;
span { span {

View file

@ -5,7 +5,7 @@
:class="{ :class="{
public: grimoire.isPublic, public: grimoire.isPublic,
spectator: session.isSpectator, spectator: session.isSpectator,
vote: session.nomination vote: session.nomination,
}" }"
> >
<ul class="circle" :class="['size-' + players.length]"> <ul class="circle" :class="['size-' + players.length]">
@ -18,7 +18,7 @@
from: Math.max(swap, move, nominate) === index, from: Math.max(swap, move, nominate) === index,
swap: swap > -1, swap: swap > -1,
move: move > -1, move: move > -1,
nominate: nominate > -1 nominate: nominate > -1,
}" }"
></Player> ></Player>
</ul> </ul>
@ -142,7 +142,7 @@
class="night-order first" class="night-order first"
v-if=" v-if="
nightOrder.get(role).first && nightOrder.get(role).first &&
(grimoire.isNightOrder || !session.isSpectator) (grimoire.isNightOrder || !session.isSpectator)
" "
> >
<em>{{ nightOrder.get(role).first }}.</em> <em>{{ nightOrder.get(role).first }}.</em>
@ -154,7 +154,7 @@
class="night-order other" class="night-order other"
v-if=" v-if="
nightOrder.get(role).other && nightOrder.get(role).other &&
(grimoire.isNightOrder || !session.isSpectator) (grimoire.isNightOrder || !session.isSpectator)
" "
> >
<em>{{ nightOrder.get(role).other }}.</em> <em>{{ nightOrder.get(role).other }}.</em>
@ -184,12 +184,12 @@ export default {
Player, Player,
Token, Token,
RoleModal, RoleModal,
ReminderModal ReminderModal,
}, },
computed: { computed: {
...mapGetters({ nightOrder: "players/nightOrder" }), ...mapGetters({ nightOrder: "players/nightOrder" }),
...mapState(["grimoire", "roles", "session", "locale"]), ...mapState(["grimoire", "roles", "session", "locale"]),
...mapState("players", ["players", "bluffs", "fabled"]) ...mapState("players", ["players", "bluffs", "fabled"]),
}, },
data() { data() {
return { return {
@ -204,7 +204,7 @@ export default {
timerName: "Timer", timerName: "Timer",
timerDuration: 1, timerDuration: 1,
timerOn: false, timerOn: false,
timerEnder: false timerEnder: false,
}; };
}, },
methods: { methods: {
@ -259,7 +259,7 @@ export default {
if (this.session.isSpectator || this.session.lockedVote) return; if (this.session.isSpectator || this.session.lockedVote) return;
if ( if (
confirm( confirm(
`Do you really want to remove ${this.players[playerIndex].name}?` `Do you really want to remove ${this.players[playerIndex].name}?`,
) )
) { ) {
const { nomination } = this.session; const { nomination } = this.session;
@ -274,7 +274,7 @@ export default {
// update nomination array if removed player has lower index // update nomination array if removed player has lower index
this.$store.commit("session/setNomination", [ this.$store.commit("session/setNomination", [
nomination[0] > playerIndex ? nomination[0] - 1 : nomination[0], nomination[0] > playerIndex ? nomination[0] - 1 : nomination[0],
nomination[1] > playerIndex ? nomination[1] - 1 : nomination[1] nomination[1] > playerIndex ? nomination[1] - 1 : nomination[1],
]); ]);
} }
} }
@ -290,7 +290,7 @@ export default {
if (this.session.nomination) { if (this.session.nomination) {
// update nomination if one of the involved players is swapped // update nomination if one of the involved players is swapped
const swapTo = this.players.indexOf(to); const swapTo = this.players.indexOf(to);
const updatedNomination = this.session.nomination.map(nom => { const updatedNomination = this.session.nomination.map((nom) => {
if (nom === this.swap) return swapTo; if (nom === this.swap) return swapTo;
if (nom === swapTo) return this.swap; if (nom === swapTo) return this.swap;
return nom; return nom;
@ -304,7 +304,7 @@ export default {
} }
this.$store.commit("players/swap", [ this.$store.commit("players/swap", [
this.swap, this.swap,
this.players.indexOf(to) this.players.indexOf(to),
]); ]);
this.cancel(); this.cancel();
} }
@ -318,7 +318,7 @@ export default {
if (this.session.nomination) { if (this.session.nomination) {
// update nomination if it is affected by the move // update nomination if it is affected by the move
const moveTo = this.players.indexOf(to); const moveTo = this.players.indexOf(to);
const updatedNomination = this.session.nomination.map(nom => { const updatedNomination = this.session.nomination.map((nom) => {
if (nom === this.move) return moveTo; if (nom === this.move) return moveTo;
if (nom > this.move && nom <= moveTo) return nom - 1; if (nom > this.move && nom <= moveTo) return nom - 1;
if (nom < this.move && nom >= moveTo) return nom + 1; if (nom < this.move && nom >= moveTo) return nom + 1;
@ -333,7 +333,7 @@ export default {
} }
this.$store.commit("players/move", [ this.$store.commit("players/move", [
this.move, this.move,
this.players.indexOf(to) this.players.indexOf(to),
]); ]);
this.cancel(); this.cancel();
} }
@ -359,7 +359,7 @@ export default {
renameTimer() { renameTimer() {
let newName = prompt( let newName = prompt(
this.locale.townsquare.timer.prompt.name, this.locale.townsquare.timer.prompt.name,
this.timerName this.timerName,
); );
if (newName === "") { if (newName === "") {
newName = this.locale.townsquare.timer.default.text; newName = this.locale.townsquare.timer.default.text;
@ -372,7 +372,8 @@ export default {
}, },
setNominationTimer() { setNominationTimer() {
this.timerDuration = 2; this.timerDuration = 2;
this.timerName = this.timerName = this.locale.townsquare.timer.nominations.text; this.timerName = this.timerName =
this.locale.townsquare.timer.nominations.text;
}, },
setDuskTimer() { setDuskTimer() {
this.timerDuration = 1; this.timerDuration = 1;
@ -399,7 +400,7 @@ export default {
let timerText = this.locale.townsquare.timer.debate.text; let timerText = this.locale.townsquare.timer.debate.text;
timerText = timerText.replace( timerText = timerText.replace(
"$accusee", "$accusee",
this.players[this.session.nomination[1]].name this.players[this.session.nomination[1]].name,
); );
this.timerName = timerText; this.timerName = timerText;
}, },
@ -422,8 +423,8 @@ export default {
this.$store.commit("setTimer", {}); this.$store.commit("setTimer", {});
this.timerOn = false; this.timerOn = false;
clearTimeout(this.timerEnder); clearTimeout(this.timerEnder);
} },
} },
}; };
</script> </script>

View file

@ -6,16 +6,21 @@
</div> </div>
<div class="overlay"> <div class="overlay">
<audio src="../assets/sounds/countdown.mp3" preload="auto"></audio> <audio src="../assets/sounds/countdown.mp3" preload="auto"></audio>
<em class="blue">{{ nominator.name }}</em> {{ locale.vote.nominated }} <em class="blue">{{ nominator.name }}</em>
{{
nominee.role.team == "traveler"
? locale.vote.callexile
: locale.vote.nominates
}}
<em>{{ nominee.name }}</em <em>{{ nominee.name }}</em
>! >{{ locale.vote.exclam }}
<br /> <br />
<em <em
class="blue" class="blue"
v-if=" v-if="
!grimoire.isOrganVoteMode || !grimoire.isOrganVoteMode ||
nominee.role.team == 'traveler' || nominee.role.team == 'traveler' ||
!session.isSpectator !session.isSpectator
" "
> >
{{ voters.length }} {{ locale.vote.votes }} {{ voters.length }} {{ locale.vote.votes }}
@ -71,7 +76,7 @@
<div <div
class="button" class="button"
:class="{ :class="{
disabled: session.nomination[1] === session.markedPlayer disabled: session.nomination[1] === session.markedPlayer,
}" }"
@click="setMarked" @click="setMarked"
> >
@ -137,44 +142,46 @@ import Countdown from "./Countdown";
export default { export default {
components: { components: {
Countdown Countdown,
}, },
computed: { computed: {
...mapState("players", ["players"]), ...mapState("players", ["players"]),
...mapState(["session", "grimoire", "locale"]), ...mapState(["session", "grimoire", "locale"]),
...mapGetters({ alive: "players/alive" }), ...mapGetters({ alive: "players/alive" }),
nominator: function() { nominator: function () {
return this.players[this.session.nomination[0]]; return this.players[this.session.nomination[0]];
}, },
nominatorStyle: function() { nominatorStyle: function () {
const players = this.players.length; const players = this.players.length;
const nomination = this.session.nomination[0]; const nomination = this.session.nomination[0];
return { return {
transform: `rotate(${Math.round((nomination / players) * 360)}deg)`, transform: `rotate(${Math.round((nomination / players) * 360)}deg)`,
transitionDuration: this.session.votingSpeed - 100 + "ms" transitionDuration: this.session.votingSpeed - 100 + "ms",
}; };
}, },
nominee: function() { nominee: function () {
return this.players[this.session.nomination[1]]; return this.players[this.session.nomination[1]];
}, },
nomineeStyle: function() { nomineeStyle: function () {
const players = this.players.length; const players = this.players.length;
const nomination = this.session.nomination[1]; const nomination = this.session.nomination[1];
const lock = this.session.lockedVote; const lock = this.session.lockedVote;
const rotation = (360 * (nomination + Math.min(lock, players))) / players; const rotation = (360 * (nomination + Math.min(lock, players))) / players;
return { return {
transform: `rotate(${Math.round(rotation)}deg)`, transform: `rotate(${Math.round(rotation)}deg)`,
transitionDuration: this.session.votingSpeed - 100 + "ms" transitionDuration: this.session.votingSpeed - 100 + "ms",
}; };
}, },
player: function() { player: function () {
return this.players.find(p => p.id === this.session.playerId); return this.players.find((p) => p.id === this.session.playerId);
}, },
currentVote: function() { currentVote: function () {
const index = this.players.findIndex(p => p.id === this.session.playerId); const index = this.players.findIndex(
(p) => p.id === this.session.playerId,
);
return index >= 0 ? !!this.session.votes[index] : undefined; return index >= 0 ? !!this.session.votes[index] : undefined;
}, },
canVote: function() { canVote: function () {
if (!this.player) return false; if (!this.player) return false;
if (this.player.isVoteless && this.nominee.role.team !== "traveler") if (this.player.isVoteless && this.nominee.role.team !== "traveler")
return false; return false;
@ -185,26 +192,27 @@ export default {
(index - 1 + players - session.nomination[1]) % players; (index - 1 + players - session.nomination[1]) % players;
return indexAdjusted >= session.lockedVote - 1; return indexAdjusted >= session.lockedVote - 1;
}, },
voters: function() { voters: function () {
const nomination = this.session.nomination[1]; const nomination = this.session.nomination[1];
const voters = Array(this.players.length) const voters = Array(this.players.length)
.fill("") .fill("")
.map((x, index) => .map((x, index) =>
this.session.votes[index] ? this.players[index].name : "" this.session.votes[index] ? this.players[index].name : "",
); );
const reorder = [ const reorder = [
...voters.slice(nomination + 1), ...voters.slice(nomination + 1),
...voters.slice(0, nomination + 1) ...voters.slice(0, nomination + 1),
]; ];
return (this.session.lockedVote return (
? reorder.slice(0, this.session.lockedVote - 1) this.session.lockedVote
: reorder ? reorder.slice(0, this.session.lockedVote - 1)
).filter(n => !!n); : reorder
} ).filter((n) => !!n);
},
}, },
data() { data() {
return { return {
voteTimer: null voteTimer: null,
}; };
}, },
methods: { methods: {
@ -254,7 +262,9 @@ export default {
}, },
vote(vote) { vote(vote) {
if (!this.canVote) return false; if (!this.canVote) return false;
const index = this.players.findIndex(p => p.id === this.session.playerId); const index = this.players.findIndex(
(p) => p.id === this.session.playerId,
);
if (index >= 0 && !!this.session.votes[index] !== vote) { if (index >= 0 && !!this.session.votes[index] !== vote) {
this.$store.commit("session/voteSync", [index, vote]); this.$store.commit("session/voteSync", [index, vote]);
} }
@ -270,8 +280,8 @@ export default {
}, },
removeMarked() { removeMarked() {
this.$store.commit("session/setMarkedPlayer", -1); this.$store.commit("session/setMarkedPlayer", -1);
} },
} },
}; };
</script> </script>
@ -289,7 +299,10 @@ export default {
background: url("../assets/demon-head.png") center center no-repeat; background: url("../assets/demon-head.png") center center no-repeat;
background-size: auto 75%; background-size: auto 75%;
text-align: center; text-align: center;
text-shadow: 0 1px 2px #000000, 0 -1px 2px #000000, 1px 0 2px #000000, text-shadow:
0 1px 2px #000000,
0 -1px 2px #000000,
1px 0 2px #000000,
-1px 0 2px #000000; -1px 0 2px #000000;
.mark .button { .mark .button {

View file

@ -39,9 +39,9 @@
class="edition" class="edition"
:class="['edition-' + edition.id]" :class="['edition-' + edition.id]"
:style="{ :style="{
backgroundImage: `url(${require('../../assets/editions/' + backgroundImage: `url(${require(
edition.id + '../../assets/editions/' + edition.id + '.png',
'.png')})` )})`,
}" }"
:key="edition.id" :key="edition.id"
@click="setEdition(edition)" @click="setEdition(edition)"
@ -120,15 +120,15 @@ import Modal from "./Modal";
export default { export default {
components: { components: {
Modal Modal,
}, },
data: function() { data: function () {
return { return {
tab: "official" tab: "official",
}; };
}, },
computed: { computed: {
...mapState(["modals", "locale", "editions"]) ...mapState(["modals", "locale", "editions"]),
}, },
methods: { methods: {
openUpload() { openUpload() {
@ -178,7 +178,9 @@ export default {
}, },
parseRoles(roles) { parseRoles(roles) {
if (!roles || !roles.length) return; if (!roles || !roles.length) return;
roles = roles.map(role => typeof role === "string" ? { id: role } : role); roles = roles.map((role) =>
typeof role === "string" ? { id: role } : role,
);
const metaIndex = roles.findIndex(({ id }) => id === "_meta"); const metaIndex = roles.findIndex(({ id }) => id === "_meta");
let meta = {}; let meta = {};
if (metaIndex > -1) { if (metaIndex > -1) {
@ -187,7 +189,7 @@ export default {
this.$store.commit("setCustomRoles", roles); this.$store.commit("setCustomRoles", roles);
this.$store.commit( this.$store.commit(
"setEdition", "setEdition",
Object.assign({}, meta, { id: "custom" }) Object.assign({}, meta, { id: "custom" }),
); );
// check for fabled and set those too, if present // check for fabled and set those too, if present
if (roles.some((role) => this.$store.state.fabled.has(role.id || role))) { if (roles.some((role) => this.$store.state.fabled.has(role.id || role))) {
@ -200,8 +202,8 @@ export default {
this.$store.commit("players/setFabled", { fabled }); this.$store.commit("players/setFabled", { fabled });
} }
}, },
...mapMutations(["toggleModal", "setEdition"]) ...mapMutations(["toggleModal", "setEdition"]),
} },
}; };
</script> </script>
@ -222,8 +224,12 @@ ul.editions {
width: 30%; width: 30%;
margin: 5px; margin: 5px;
font-size: 120%; font-size: 120%;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, text-shadow:
1px 1px 0 #000, 0 0 5px rgba(0, 0, 0, 0.75); -1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000,
0 0 5px rgba(0, 0, 0, 0.75);
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: red; color: red;

View file

@ -20,26 +20,28 @@ export default {
...mapState(["modals", "fabled", "grimoire", "locale"]), ...mapState(["modals", "fabled", "grimoire", "locale"]),
fabled() { fabled() {
const fabled = []; const fabled = [];
this.$store.state.fabled.forEach(role => { this.$store.state.fabled.forEach((role) => {
// don't show fabled that are already in play // don't show fabled that are already in play
if ( if (
!this.$store.state.players.fabled.some(fable => fable.id === role.id) !this.$store.state.players.fabled.some(
(fable) => fable.id === role.id,
)
) { ) {
fabled.push(role); fabled.push(role);
} }
}); });
return fabled; return fabled;
} },
}, },
methods: { methods: {
setFabled(role) { setFabled(role) {
this.$store.commit("players/setFabled", { this.$store.commit("players/setFabled", {
fabled: role fabled: role,
}); });
this.$store.commit("toggleModal", "fabled"); this.$store.commit("toggleModal", "fabled");
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>

View file

@ -28,10 +28,10 @@ import { mapMutations, mapState } from "vuex";
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
gamestate: function() { gamestate: function () {
return JSON.stringify({ return JSON.stringify({
bluffs: this.players.bluffs.map(({ id }) => id), bluffs: this.players.bluffs.map(({ id }) => id),
edition: this.edition.isOfficial edition: this.edition.isOfficial
@ -40,27 +40,27 @@ export default {
roles: this.edition.isOfficial roles: this.edition.isOfficial
? "" ? ""
: this.$store.getters.customRolesStripped, : this.$store.getters.customRolesStripped,
fabled: this.players.fabled.map(fabled => fabled: this.players.fabled.map((fabled) =>
fabled.isCustom ? fabled : { id: fabled.id } fabled.isCustom ? fabled : { id: fabled.id },
), ),
players: this.players.players.map(player => ({ players: this.players.players.map((player) => ({
...player, ...player,
role: player.role.id || {} role: player.role.id || {},
})) })),
}); });
}, },
...mapState(["modals", "players", "edition", "roles", "session", "locale"]) ...mapState(["modals", "players", "edition", "roles", "session", "locale"]),
}, },
data() { data() {
return { return {
input: "" input: "",
}; };
}, },
methods: { methods: {
copy: function() { copy: function () {
navigator.clipboard.writeText(this.input || this.gamestate); navigator.clipboard.writeText(this.input || this.gamestate);
}, },
load: function() { load: function () {
if (this.session.isSpectator) return; if (this.session.isSpectator) return;
try { try {
const data = JSON.parse(this.input || this.gamestate); const data = JSON.parse(this.input || this.gamestate);
@ -75,30 +75,30 @@ export default {
bluffs.forEach((role, index) => { bluffs.forEach((role, index) => {
this.$store.commit("players/setBluff", { this.$store.commit("players/setBluff", {
index, index,
role: this.$store.state.roles.get(role) || {} role: this.$store.state.roles.get(role) || {},
}); });
}); });
} }
if (fabled) { if (fabled) {
this.$store.commit("players/setFabled", { this.$store.commit("players/setFabled", {
fabled: fabled.map( fabled: fabled.map(
f => (f) =>
this.$store.state.fabled.get(f) || this.$store.state.fabled.get(f) ||
this.$store.state.fabled.get(f.id) || this.$store.state.fabled.get(f.id) ||
f f,
) ),
}); });
} }
if (players) { if (players) {
this.$store.commit( this.$store.commit(
"players/set", "players/set",
players.map(player => ({ players.map((player) => ({
...player, ...player,
role: role:
this.$store.state.roles.get(player.role) || this.$store.state.roles.get(player.role) ||
this.$store.getters.rolesJSONbyId.get(player.role) || this.$store.getters.rolesJSONbyId.get(player.role) ||
{} {},
})) })),
); );
} }
this.toggleModal("gameState"); this.toggleModal("gameState");
@ -106,8 +106,8 @@ export default {
alert("Unable to parse JSON: " + e); alert("Unable to parse JSON: " + e);
} }
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>

View file

@ -31,16 +31,16 @@
<script> <script>
export default { export default {
data: function() { data: function () {
return { return {
isMaximized: false isMaximized: false,
}; };
}, },
methods: { methods: {
close() { close() {
this.$emit("close"); this.$emit("close");
} },
} },
}; };
</script> </script>

View file

@ -44,10 +44,12 @@
backgroundImage: `url(${ backgroundImage: `url(${
role.image && grimoire.isImageOptIn role.image && grimoire.isImageOptIn
? role.image ? role.image
: require('../../assets/icons/' + : require(
(role.imageAlt || role.id) + '../../assets/icons/' +
'.png') (role.imageAlt || role.id) +
})` '.png',
)
})`,
}" }"
></span> ></span>
<span class="reminder" v-if="role.firstNightReminder"> <span class="reminder" v-if="role.firstNightReminder">
@ -69,10 +71,12 @@
backgroundImage: `url(${ backgroundImage: `url(${
role.image && grimoire.isImageOptIn role.image && grimoire.isImageOptIn
? role.image ? role.image
: require('../../assets/icons/' + : require(
(role.imageAlt || role.id) + '../../assets/icons/' +
'.png') (role.imageAlt || role.id) +
})` '.png',
)
})`,
}" }"
></span> ></span>
<span class="name"> <span class="name">
@ -104,10 +108,10 @@ import { mapMutations, mapState } from "vuex";
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
rolesFirstNight: function() { rolesFirstNight: function () {
const rolesFirstNight = []; const rolesFirstNight = [];
// Ajouter minion / demon infos à l'ordre nocturne // Ajouter minion / demon infos à l'ordre nocturne
if (this.players.length > 6) { if (this.players.length > 6) {
@ -117,57 +121,57 @@ export default {
name: this.locale.modal.nightOrder.minionInfo, name: this.locale.modal.nightOrder.minionInfo,
firstNight: 5, firstNight: 5,
team: "minion", team: "minion",
players: this.players.filter(p => p.role.team === "minion"), players: this.players.filter((p) => p.role.team === "minion"),
firstNightReminder: this.locale.modal.nightOrder firstNightReminder:
.minionInfoDescription this.locale.modal.nightOrder.minionInfoDescription,
}, },
{ {
id: "evil", id: "evil",
name: this.locale.modal.nightOrder.demonInfo, name: this.locale.modal.nightOrder.demonInfo,
firstNight: 8, firstNight: 8,
team: "demon", team: "demon",
players: this.players.filter(p => p.role.team === "demon"), players: this.players.filter((p) => p.role.team === "demon"),
firstNightReminder: this.locale.modal.nightOrder firstNightReminder:
.demonInfoDescription this.locale.modal.nightOrder.demonInfoDescription,
} },
); );
} }
this.roles.forEach(role => { this.roles.forEach((role) => {
const players = this.players.filter(p => p.role.id === role.id); const players = this.players.filter((p) => p.role.id === role.id);
if (role.firstNight && (role.team !== "traveler" || players.length)) { if (role.firstNight && (role.team !== "traveler" || players.length)) {
rolesFirstNight.push(Object.assign({ players }, role)); rolesFirstNight.push(Object.assign({ players }, role));
} }
}); });
this.fabled this.fabled
.filter(({ firstNight }) => firstNight) .filter(({ firstNight }) => firstNight)
.forEach(fabled => { .forEach((fabled) => {
rolesFirstNight.push(Object.assign({ players: [] }, fabled)); rolesFirstNight.push(Object.assign({ players: [] }, fabled));
}); });
rolesFirstNight.sort((a, b) => a.firstNight - b.firstNight); rolesFirstNight.sort((a, b) => a.firstNight - b.firstNight);
return rolesFirstNight; return rolesFirstNight;
}, },
rolesOtherNight: function() { rolesOtherNight: function () {
const rolesOtherNight = []; const rolesOtherNight = [];
this.roles.forEach(role => { this.roles.forEach((role) => {
const players = this.players.filter(p => p.role.id === role.id); const players = this.players.filter((p) => p.role.id === role.id);
if (role.otherNight && (role.team !== "traveler" || players.length)) { if (role.otherNight && (role.team !== "traveler" || players.length)) {
rolesOtherNight.push(Object.assign({ players }, role)); rolesOtherNight.push(Object.assign({ players }, role));
} }
}); });
this.fabled this.fabled
.filter(({ otherNight }) => otherNight) .filter(({ otherNight }) => otherNight)
.forEach(fabled => { .forEach((fabled) => {
rolesOtherNight.push(Object.assign({ players: [] }, fabled)); rolesOtherNight.push(Object.assign({ players: [] }, fabled));
}); });
rolesOtherNight.sort((a, b) => a.otherNight - b.otherNight); rolesOtherNight.sort((a, b) => a.otherNight - b.otherNight);
return rolesOtherNight; return rolesOtherNight;
}, },
...mapState(["roles", "modals", "edition", "grimoire", "locale"]), ...mapState(["roles", "modals", "edition", "grimoire", "locale"]),
...mapState("players", ["players", "fabled"]) ...mapState("players", ["players", "fabled"]),
}, },
methods: { methods: {
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>

View file

@ -32,10 +32,12 @@
backgroundImage: `url(${ backgroundImage: `url(${
role.image && grimoire.isImageOptIn role.image && grimoire.isImageOptIn
? role.image ? role.image
: require('../../assets/icons/' + : require(
(role.imageAlt || role.id) + '../../assets/icons/' +
'.png') (role.imageAlt || role.id) +
})` '.png',
)
})`,
}" }"
></span> ></span>
<div class="role"> <div class="role">
@ -60,17 +62,17 @@
<span <span
class="icon" class="icon"
:style="{ :style="{
backgroundImage: `url(${require('../../assets/icons/' + backgroundImage: `url(${require(
jinx.first.id + '../../assets/icons/' + jinx.first.id + '.png',
'.png')})` )})`,
}" }"
></span> ></span>
<span <span
class="icon" class="icon"
:style="{ :style="{
backgroundImage: `url(${require('../../assets/icons/' + backgroundImage: `url(${require(
jinx.second.id + '../../assets/icons/' + jinx.second.id + '.png',
'.png')})` )})`,
}" }"
></span> ></span>
<div class="role"> <div class="role">
@ -84,6 +86,7 @@
<li></li> <li></li>
</ul> </ul>
</div> </div>
<div class="asterisk">{{ locale.modal.reference.notfirstnight }}</div>
</Modal> </Modal>
</template> </template>
@ -93,23 +96,23 @@ import { mapMutations, mapState } from "vuex";
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
/** /**
* Return a list of jinxes in the form of role IDs and a reason * Return a list of jinxes in the form of role IDs and a reason
* @returns {*[]} [{first, second, reason}] * @returns {*[]} [{first, second, reason}]
*/ */
jinxed: function() { jinxed: function () {
const jinxed = []; const jinxed = [];
this.roles.forEach(role => { this.roles.forEach((role) => {
if (this.jinxes.get(role.id)) { if (this.jinxes.get(role.id)) {
this.jinxes.get(role.id).forEach((reason, second) => { this.jinxes.get(role.id).forEach((reason, second) => {
if (this.roles.get(second)) { if (this.roles.get(second)) {
jinxed.push({ jinxed.push({
first: role, first: role,
second: this.roles.get(second), second: this.roles.get(second),
reason reason,
}); });
} }
}); });
@ -117,9 +120,9 @@ export default {
}); });
return jinxed; return jinxed;
}, },
rolesGrouped: function() { rolesGrouped: function () {
const rolesGrouped = {}; const rolesGrouped = {};
this.roles.forEach(role => { this.roles.forEach((role) => {
if (!rolesGrouped[role.team]) { if (!rolesGrouped[role.team]) {
rolesGrouped[role.team] = []; rolesGrouped[role.team] = [];
} }
@ -128,7 +131,7 @@ export default {
delete rolesGrouped["traveler"]; delete rolesGrouped["traveler"];
return rolesGrouped; return rolesGrouped;
}, },
playersByRole: function() { playersByRole: function () {
const players = {}; const players = {};
this.players.forEach(({ name, role }) => { this.players.forEach(({ name, role }) => {
if (role && role.id && role.team !== "traveler") { if (role && role.id && role.team !== "traveler") {
@ -141,11 +144,11 @@ export default {
return players; return players;
}, },
...mapState(["roles", "modals", "edition", "grimoire", "jinxes", "locale"]), ...mapState(["roles", "modals", "edition", "grimoire", "jinxes", "locale"]),
...mapState("players", ["players"]) ...mapState("players", ["players"]),
}, },
methods: { methods: {
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>
@ -211,6 +214,12 @@ h3 {
} }
} }
.asterisk {
font-size: 60%;
text-align: right;
padding-top: 20px;
}
.team { .team {
display: flex; display: flex;
align-items: stretch; align-items: stretch;

View file

@ -18,10 +18,12 @@
backgroundImage: `url(${ backgroundImage: `url(${
reminder.image && grimoire.isImageOptIn reminder.image && grimoire.isImageOptIn
? reminder.image ? reminder.image
: require('../../assets/icons/' + : require(
(reminder.imageAlt || reminder.role) + '../../assets/icons/' +
'.png') (reminder.imageAlt || reminder.role) +
})` '.png',
)
})`,
}" }"
></span> ></span>
<span class="text">{{ reminder.name }}</span> <span class="text">{{ reminder.name }}</span>
@ -39,12 +41,14 @@ import { mapMutations, mapState } from "vuex";
* @param role The role for which the reminder should be generated * @param role The role for which the reminder should be generated
* @return {function(*): {image: string|string[]|string|*, role: *, name: *, imageAlt: string|*}} * @return {function(*): {image: string|string[]|string|*, role: *, name: *, imageAlt: string|*}}
*/ */
const mapReminder = ({ id, image, imageAlt }) => name => ({ const mapReminder =
role: id, ({ id, image, imageAlt }) =>
image, (name) => ({
imageAlt, role: id,
name image,
}); imageAlt,
name,
});
export default { export default {
components: { Modal }, components: { Modal },
@ -53,31 +57,31 @@ export default {
availableReminders() { availableReminders() {
let reminders = []; let reminders = [];
const { players, bluffs } = this.$store.state.players; const { players, bluffs } = this.$store.state.players;
this.$store.state.roles.forEach(role => { this.$store.state.roles.forEach((role) => {
// add reminders from player roles // add reminders from player roles
if (players.some(p => p.role.id === role.id)) { if (players.some((p) => p.role.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))]; reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
} }
// add reminders from bluff/other roles // add reminders from bluff/other roles
else if (bluffs.some(bluff => bluff.id === role.id)) { else if (bluffs.some((bluff) => bluff.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))]; reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
} }
// add global reminders // add global reminders
if (role.remindersGlobal && role.remindersGlobal.length) { if (role.remindersGlobal && role.remindersGlobal.length) {
reminders = [ reminders = [
...reminders, ...reminders,
...role.remindersGlobal.map(mapReminder(role)) ...role.remindersGlobal.map(mapReminder(role)),
]; ];
} }
}); });
// add fabled reminders // add fabled reminders
this.$store.state.players.fabled.forEach(role => { this.$store.state.players.fabled.forEach((role) => {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))]; reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
}); });
// add out of script traveler reminders // add out of script traveler reminders
this.$store.state.otherTravelers.forEach(role => { this.$store.state.otherTravelers.forEach((role) => {
if (players.some(p => p.role.id === role.id)) { if (players.some((p) => p.role.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))]; reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
} }
}); });
@ -86,12 +90,12 @@ export default {
reminders.push({ role: "evil", name: this.locale.modal.reminder.evil }); reminders.push({ role: "evil", name: this.locale.modal.reminder.evil });
reminders.push({ reminders.push({
role: "custom", role: "custom",
name: this.locale.modal.reminder.custom name: this.locale.modal.reminder.custom,
}); });
return reminders; return reminders;
}, },
...mapState(["modals", "grimoire", "locale"]), ...mapState(["modals", "grimoire", "locale"]),
...mapState("players", ["players"]) ...mapState("players", ["players"]),
}, },
methods: { methods: {
addReminder(reminder) { addReminder(reminder) {
@ -107,12 +111,12 @@ export default {
this.$store.commit("players/update", { this.$store.commit("players/update", {
player, player,
property: "reminders", property: "reminders",
value value,
}); });
this.$store.commit("toggleModal", "reminder"); this.$store.commit("toggleModal", "reminder");
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>

View file

@ -60,12 +60,12 @@ export default {
availableRoles() { availableRoles() {
const availableRoles = []; const availableRoles = [];
const players = this.$store.state.players.players; const players = this.$store.state.players.players;
this.$store.state.roles.forEach(role => { this.$store.state.roles.forEach((role) => {
// don't show bluff roles that are already assigned to players // don't show bluff roles that are already assigned to players
if ( if (
this.playerIndex >= 0 || this.playerIndex >= 0 ||
(this.playerIndex < 0 && (this.playerIndex < 0 &&
!players.some(player => player.role.id === role.id)) !players.some((player) => player.role.id === role.id))
) { ) {
availableRoles.push(role); availableRoles.push(role);
} }
@ -75,11 +75,11 @@ export default {
}, },
...mapState(["modals", "roles", "session", "locale"]), ...mapState(["modals", "roles", "session", "locale"]),
...mapState("players", ["players"]), ...mapState("players", ["players"]),
...mapState(["otherTravelers"]) ...mapState(["otherTravelers"]),
}, },
data() { data() {
return { return {
tab: "editionRoles" tab: "editionRoles",
}; };
}, },
methods: { methods: {
@ -88,7 +88,7 @@ export default {
// assign to bluff slot (index < 0) // assign to bluff slot (index < 0)
this.$store.commit("players/setBluff", { this.$store.commit("players/setBluff", {
index: this.playerIndex * -1 - 1, index: this.playerIndex * -1 - 1,
role role,
}); });
} else { } else {
if (this.session.isSpectator && role.team === "traveler") return; if (this.session.isSpectator && role.team === "traveler") return;
@ -97,7 +97,7 @@ export default {
this.$store.commit("players/update", { this.$store.commit("players/update", {
player, player,
property: "role", property: "role",
value: role value: role,
}); });
} }
this.tab = "editionRoles"; this.tab = "editionRoles";
@ -107,8 +107,8 @@ export default {
this.tab = "editionRoles"; this.tab = "editionRoles";
this.toggleModal("role"); this.toggleModal("role");
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>
@ -122,19 +122,29 @@ ul.tokens li {
transition: transform 500ms ease; transition: transform 500ms ease;
&.townsfolk { &.townsfolk {
box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff; box-shadow:
0 0 10px $townsfolk,
0 0 10px #004cff;
} }
&.outsider { &.outsider {
box-shadow: 0 0 10px $outsider, 0 0 10px $outsider; box-shadow:
0 0 10px $outsider,
0 0 10px $outsider;
} }
&.minion { &.minion {
box-shadow: 0 0 10px $minion, 0 0 10px $minion; box-shadow:
0 0 10px $minion,
0 0 10px $minion;
} }
&.demon { &.demon {
box-shadow: 0 0 10px $demon, 0 0 10px $demon; box-shadow:
0 0 10px $demon,
0 0 10px $demon;
} }
&.traveler { &.traveler {
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler; box-shadow:
0 0 10px $traveler,
0 0 10px $traveler;
} }
&:hover { &:hover {
transform: scale(1.2); transform: scale(1.2);

View file

@ -7,8 +7,8 @@
<h3> <h3>
{{ {{
locale.modal.roles.titleStart + locale.modal.roles.titleStart +
nonTravelers + nonTravelers +
locale.modal.roles.titleEnd locale.modal.roles.titleEnd
}} }}
</h3> </h3>
<ul class="tokens" v-for="(teamRoles, team) in roleSelection" :key="team"> <ul class="tokens" v-for="(teamRoles, team) in roleSelection" :key="team">
@ -48,14 +48,14 @@
class="button" class="button"
@click="assignRoles" @click="assignRoles"
:class="{ :class="{
disabled: selectedRoles > nonTravelers || !selectedRoles disabled: selectedRoles > nonTravelers || !selectedRoles,
}" }"
> >
<font-awesome-icon icon="people-arrows" /> <font-awesome-icon icon="people-arrows" />
{{ {{
locale.modal.roles.assignStart + locale.modal.roles.assignStart +
selectedRoles + selectedRoles +
locale.modal.roles.assignEnd locale.modal.roles.assignEnd
}} }}
</div> </div>
<div class="button" @click="selectRandomRoles"> <div class="button" @click="selectRandomRoles">
@ -72,39 +72,39 @@ import gameJSON from "./../../game";
import Token from "./../Token"; import Token from "./../Token";
import { mapGetters, mapMutations, mapState } from "vuex"; import { mapGetters, mapMutations, mapState } from "vuex";
const randomElement = arr => arr[Math.floor(Math.random() * arr.length)]; const randomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
export default { export default {
components: { components: {
Token, Token,
Modal Modal,
}, },
data: function() { data: function () {
return { return {
roleSelection: {}, roleSelection: {},
game: gameJSON, game: gameJSON,
allowMultiple: false allowMultiple: false,
}; };
}, },
computed: { computed: {
selectedRoles: function() { selectedRoles: function () {
return Object.values(this.roleSelection) return Object.values(this.roleSelection)
.map(roles => roles.reduce((a, { selected }) => a + selected, 0)) .map((roles) => roles.reduce((a, { selected }) => a + selected, 0))
.reduce((a, b) => a + b, 0); .reduce((a, b) => a + b, 0);
}, },
hasSelectedSetupRoles: function() { hasSelectedSetupRoles: function () {
return Object.values(this.roleSelection).some(roles => return Object.values(this.roleSelection).some((roles) =>
roles.some(role => role.selected && role.setup) roles.some((role) => role.selected && role.setup),
); );
}, },
...mapState(["roles", "modals", "locale"]), ...mapState(["roles", "modals", "locale"]),
...mapState("players", ["players"]), ...mapState("players", ["players"]),
...mapGetters({ nonTravelers: "players/nonTravelers" }) ...mapGetters({ nonTravelers: "players/nonTravelers" }),
}, },
methods: { methods: {
selectRandomRoles() { selectRandomRoles() {
this.roleSelection = {}; this.roleSelection = {};
this.roles.forEach(role => { this.roles.forEach((role) => {
if (!this.roleSelection[role.team]) { if (!this.roleSelection[role.team]) {
this.$set(this.roleSelection, role.team, []); this.$set(this.roleSelection, role.team, []);
} }
@ -114,11 +114,11 @@ export default {
delete this.roleSelection["traveler"]; delete this.roleSelection["traveler"];
const playerCount = Math.max(5, this.nonTravelers); const playerCount = Math.max(5, this.nonTravelers);
const composition = this.game[playerCount - 5]; const composition = this.game[playerCount - 5];
Object.keys(composition).forEach(team => { Object.keys(composition).forEach((team) => {
for (let x = 0; x < composition[team]; x++) { for (let x = 0; x < composition[team]; x++) {
if (this.roleSelection[team]) { if (this.roleSelection[team]) {
const available = this.roleSelection[team].filter( const available = this.roleSelection[team].filter(
role => !role.selected (role) => !role.selected,
); );
if (available.length) { if (available.length) {
randomElement(available).selected = 1; randomElement(available).selected = 1;
@ -131,32 +131,32 @@ export default {
if (this.selectedRoles <= this.nonTravelers && this.selectedRoles) { if (this.selectedRoles <= this.nonTravelers && this.selectedRoles) {
// generate list of selected roles and randomize it // generate list of selected roles and randomize it
const roles = Object.values(this.roleSelection) const roles = Object.values(this.roleSelection)
.map(roles => .map((roles) =>
roles roles
// duplicate roles selected more than once and filter unselected // duplicate roles selected more than once and filter unselected
.reduce((a, r) => [...a, ...Array(r.selected).fill(r)], []) .reduce((a, r) => [...a, ...Array(r.selected).fill(r)], []),
) )
// flatten into a single array // flatten into a single array
.reduce((a, b) => [...a, ...b], []) .reduce((a, b) => [...a, ...b], [])
.map(a => [Math.random(), a]) .map((a) => [Math.random(), a])
.sort((a, b) => a[0] - b[0]) .sort((a, b) => a[0] - b[0])
.map(a => a[1]); .map((a) => a[1]);
this.players.forEach(player => { this.players.forEach((player) => {
if (player.role.team !== "traveler" && roles.length) { if (player.role.team !== "traveler" && roles.length) {
const value = roles.pop(); const value = roles.pop();
this.$store.commit("players/update", { this.$store.commit("players/update", {
player, player,
property: "role", property: "role",
value value,
}); });
} }
}); });
this.$store.commit("toggleModal", "roles"); this.$store.commit("toggleModal", "roles");
} }
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
}, },
mounted: function() { mounted: function () {
if (!Object.keys(this.roleSelection).length) { if (!Object.keys(this.roleSelection).length) {
this.selectRandomRoles(); this.selectRandomRoles();
} }
@ -164,8 +164,8 @@ export default {
watch: { watch: {
roles() { roles() {
this.selectRandomRoles(); this.selectRandomRoles();
} },
} },
}; };
</script> </script>
@ -190,19 +190,29 @@ ul.tokens {
} }
} }
&.townsfolk { &.townsfolk {
box-shadow: 0 0 10px $townsfolk, 0 0 10px #004cff; box-shadow:
0 0 10px $townsfolk,
0 0 10px #004cff;
} }
&.outsider { &.outsider {
box-shadow: 0 0 10px $outsider, 0 0 10px $outsider; box-shadow:
0 0 10px $outsider,
0 0 10px $outsider;
} }
&.minion { &.minion {
box-shadow: 0 0 10px $minion, 0 0 10px $minion; box-shadow:
0 0 10px $minion,
0 0 10px $minion;
} }
&.demon { &.demon {
box-shadow: 0 0 10px $demon, 0 0 10px $demon; box-shadow:
0 0 10px $demon,
0 0 10px $demon;
} }
&.traveler { &.traveler {
box-shadow: 0 0 10px $traveler, 0 0 10px $traveler; box-shadow:
0 0 10px $traveler,
0 0 10px $traveler;
} }
&:hover { &:hover {
transform: scale(1.2); transform: scale(1.2);

View file

@ -20,7 +20,7 @@
<font-awesome-icon <font-awesome-icon
:icon="[ :icon="[
'fas', 'fas',
session.isVoteHistoryAllowed ? 'check-square' : 'square' session.isVoteHistoryAllowed ? 'check-square' : 'square',
]" ]"
/> />
{{ locale.modal.voteHistory.accessibility }} {{ locale.modal.voteHistory.accessibility }}
@ -49,16 +49,8 @@
<tbody> <tbody>
<tr v-for="(vote, index) in session.voteHistory" :key="index"> <tr v-for="(vote, index) in session.voteHistory" :key="index">
<td> <td>
{{ {{ vote.timestamp.getHours().toString().padStart(2, "0") }}:{{
vote.timestamp vote.timestamp.getMinutes().toString().padStart(2, "0")
.getHours()
.toString()
.padStart(2, "0")
}}:{{
vote.timestamp
.getMinutes()
.toString()
.padStart(2, "0")
}} }}
</td> </td>
<td>{{ vote.nominator }}</td> <td>{{ vote.nominator }}</td>
@ -77,7 +69,7 @@
? 'minus-square' ? 'minus-square'
: vote.votes.length >= vote.majority : vote.votes.length >= vote.majority
? 'check-square' ? 'check-square'
: 'square' : 'square',
]" ]"
/> />
</td> </td>
@ -100,10 +92,10 @@ import { mapMutations, mapState } from "vuex";
export default { export default {
components: { components: {
Modal Modal,
}, },
computed: { computed: {
...mapState(["session", "modals", "locale"]) ...mapState(["session", "modals", "locale"]),
}, },
methods: { methods: {
clearVoteHistory() { clearVoteHistory() {
@ -112,11 +104,11 @@ export default {
setRecordVoteHistory() { setRecordVoteHistory() {
this.$store.commit( this.$store.commit(
"session/setVoteHistoryAllowed", "session/setVoteHistoryAllowed",
!this.session.isVoteHistoryAllowed !this.session.isVoteHistoryAllowed,
); );
}, },
...mapMutations(["toggleModal"]) ...mapMutations(["toggleModal"]),
} },
}; };
</script> </script>

View file

@ -56,17 +56,17 @@ const faIcons = [
"VolumeMute", "VolumeMute",
"VoteYea", "VoteYea",
"WindowMaximize", "WindowMaximize",
"WindowMinimize" "WindowMinimize",
]; ];
const fabIcons = ["Github", "Discord"]; const fabIcons = ["Github", "Discord"];
library.add( library.add(
...faIcons.map(i => fas["fa" + i]), ...faIcons.map((i) => fas["fa" + i]),
...fabIcons.map(i => fab["fa" + i]) ...fabIcons.map((i) => fab["fa" + i]),
); );
Vue.component("font-awesome-icon", FontAwesomeIcon); Vue.component("font-awesome-icon", FontAwesomeIcon);
Vue.config.productionTip = false; Vue.config.productionTip = false;
new Vue({ new Vue({
render: h => h(App), render: (h) => h(App),
store store,
}).$mount("#app"); }).$mount("#app");

View file

@ -14,9 +14,9 @@ Vue.use(Vuex);
const getRolesByEdition = (edition = editionJSON.official[0]) => { const getRolesByEdition = (edition = editionJSON.official[0]) => {
return new Map( return new Map(
rolesJSON rolesJSON
.filter(r => r.edition === edition.id || edition.roles.includes(r.id)) .filter((r) => r.edition === edition.id || edition.roles.includes(r.id))
.sort((a, b) => b.team.localeCompare(a.team)) .sort((a, b) => b.team.localeCompare(a.team))
.map(role => [role.id, role]) .map((role) => [role.id, role]),
); );
}; };
@ -24,35 +24,39 @@ const getTravelersNotInEdition = (edition = editionJSON.official[0]) => {
return new Map( return new Map(
rolesJSON rolesJSON
.filter( .filter(
r => (r) =>
r.team === "traveler" && r.team === "traveler" &&
r.edition !== edition.id && r.edition !== edition.id &&
!edition.roles.includes(r.id) !edition.roles.includes(r.id),
) )
.map(role => [role.id, role]) .map((role) => [role.id, role]),
); );
}; };
const set = key => ({ grimoire }, val) => { const set =
grimoire[key] = val; (key) =>
}; ({ grimoire }, val) => {
const toggle = key => ({ grimoire }, val) => {
if (val === true || val === false) {
grimoire[key] = val; grimoire[key] = val;
} else { };
grimoire[key] = !grimoire[key];
}
};
const clean = id => id.toLocaleLowerCase().replace(/[^a-z0-9]/g, ""); const toggle =
(key) =>
({ grimoire }, val) => {
if (val === true || val === false) {
grimoire[key] = val;
} else {
grimoire[key] = !grimoire[key];
}
};
const clean = (id) => id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
// global data maps // global data maps
const editionJSONbyId = new Map( const editionJSONbyId = new Map(
editionJSON.official.map(edition => [edition.id, edition]) editionJSON.official.map((edition) => [edition.id, edition]),
); );
const rolesJSONbyId = new Map(rolesJSON.map(role => [role.id, role])); const rolesJSONbyId = new Map(rolesJSON.map((role) => [role.id, role]));
const fabled = new Map(fabledJSON.map(role => [role.id, role])); const fabled = new Map(fabledJSON.map((role) => [role.id, role]));
// jinxes // jinxes
let jinxes = {}; let jinxes = {};
@ -64,8 +68,8 @@ try {
jinxes = new Map( jinxes = new Map(
jinxesJSON.map(({ id, hatred }) => [ jinxesJSON.map(({ id, hatred }) => [
clean(id), clean(id),
new Map(hatred.map(({ id, reason }) => [clean(id), reason])) new Map(hatred.map(({ id, reason }) => [clean(id), reason])),
]) ]),
); );
// }); // });
} catch (e) { } catch (e) {
@ -87,13 +91,13 @@ const customRole = {
remindersGlobal: [], remindersGlobal: [],
setup: false, setup: false,
team: "townsfolk", team: "townsfolk",
isCustom: true isCustom: true,
}; };
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
players, players,
session session,
}, },
state: { state: {
grimoire: { grimoire: {
@ -111,8 +115,8 @@ export default new Vuex.Store({
background: "", background: "",
timer: { timer: {
name: "", name: "",
duration: 0 duration: 0,
} },
}, },
modals: { modals: {
edition: false, edition: false,
@ -123,7 +127,7 @@ export default new Vuex.Store({
reminder: false, reminder: false,
role: false, role: false,
roles: false, roles: false,
voteHistory: false voteHistory: false,
}, },
edition: editionJSONbyId.get("tb"), edition: editionJSONbyId.get("tb"),
editions: editionJSON, editions: editionJSON,
@ -131,7 +135,7 @@ export default new Vuex.Store({
otherTravelers: getTravelersNotInEdition(), otherTravelers: getTravelersNotInEdition(),
fabled, fabled,
jinxes, jinxes,
locale locale,
}, },
getters: { getters: {
/** /**
@ -146,9 +150,9 @@ export default new Vuex.Store({
const strippedProps = [ const strippedProps = [
"firstNightReminder", "firstNightReminder",
"otherNightReminder", "otherNightReminder",
"isCustom" "isCustom",
]; ];
roles.forEach(role => { roles.forEach((role) => {
if (!role.isCustom) { if (!role.isCustom) {
customRoles.push({ id: role.id }); customRoles.push({ id: role.id });
} else { } else {
@ -167,7 +171,7 @@ export default new Vuex.Store({
}); });
return customRoles; return customRoles;
}, },
rolesJSONbyId: () => rolesJSONbyId rolesJSONbyId: () => rolesJSONbyId,
}, },
mutations: { mutations: {
setZoom: set("zoom"), setZoom: set("zoom"),
@ -202,7 +206,7 @@ export default new Vuex.Store({
setCustomRoles(state, roles) { setCustomRoles(state, roles) {
const processedRoles = roles const processedRoles = roles
// replace numerical role object keys with matching key names // replace numerical role object keys with matching key names
.map(role => { .map((role) => {
if (role[0]) { if (role[0]) {
const customKeys = Object.keys(customRole); const customKeys = Object.keys(customRole);
const mappedRole = {}; const mappedRole = {};
@ -217,19 +221,19 @@ export default new Vuex.Store({
} }
}) })
// clean up role.id // clean up role.id
.map(role => { .map((role) => {
role.id = clean(role.id); role.id = clean(role.id);
return role; return role;
}) })
// map existing roles to base definition or pre-populate custom roles to ensure all properties // map existing roles to base definition or pre-populate custom roles to ensure all properties
.map( .map(
role => (role) =>
rolesJSONbyId.get(role.id) || rolesJSONbyId.get(role.id) ||
state.roles.get(role.id) || state.roles.get(role.id) ||
Object.assign({}, customRole, role) Object.assign({}, customRole, role),
) )
// default empty icons and placeholders, clean up firstNight / otherNight // default empty icons and placeholders, clean up firstNight / otherNight
.map(role => { .map((role) => {
if (rolesJSONbyId.get(role.id)) return role; if (rolesJSONbyId.get(role.id)) return role;
role.imageAlt = // map team to generic icon role.imageAlt = // map team to generic icon
{ {
@ -237,32 +241,36 @@ export default new Vuex.Store({
outsider: "outsider", outsider: "outsider",
minion: "minion", minion: "minion",
demon: "evil", demon: "evil",
fabled: "fabled" fabled: "fabled",
}[role.team] || "custom"; }[role.team] || "custom";
role.firstNight = Math.abs(role.firstNight); role.firstNight = Math.abs(role.firstNight);
role.otherNight = Math.abs(role.otherNight); role.otherNight = Math.abs(role.otherNight);
return role; return role;
}) })
// filter out roles that don't match an existing role and also don't have name/ability/team // filter out roles that don't match an existing role and also don't have name/ability/team
.filter(role => role.name && role.ability && role.team) .filter((role) => role.name && role.ability && role.team)
// sort by team // sort by team
.sort((a, b) => b.team.localeCompare(a.team)); .sort((a, b) => b.team.localeCompare(a.team));
// convert to Map without Fabled // convert to Map without Fabled
state.roles = new Map( state.roles = new Map(
processedRoles processedRoles
.filter(role => role.team !== "fabled") .filter((role) => role.team !== "fabled")
.map(role => [role.id, role]) .map((role) => [role.id, role]),
); );
// update Fabled to include custom Fabled from this script // update Fabled to include custom Fabled from this script
state.fabled = new Map([ state.fabled = new Map([
...processedRoles.filter(r => r.team === "fabled").map(r => [r.id, r]), ...processedRoles
...fabledJSON.map(role => [role.id, role]) .filter((r) => r.team === "fabled")
.map((r) => [r.id, r]),
...fabledJSON.map((role) => [role.id, role]),
]); ]);
// update extraTravelers map to only show travelers not in this script // update extraTravelers map to only show travelers not in this script
state.otherTravelers = new Map( state.otherTravelers = new Map(
rolesJSON rolesJSON
.filter(r => r.team === "traveler" && !roles.some(i => i.id === r.id)) .filter(
.map(role => [role.id, role]) (r) => r.team === "traveler" && !roles.some((i) => i.id === r.id),
)
.map((role) => [role.id, role]),
); );
}, },
setEdition(state, edition) { setEdition(state, edition) {
@ -274,7 +282,7 @@ export default new Vuex.Store({
state.edition = edition; state.edition = edition;
} }
state.modals.edition = false; state.modals.edition = false;
} },
}, },
plugins: [persistence, socket] plugins: [persistence, socket],
}); });

View file

@ -3,7 +3,7 @@
"id": "doomsayer", "id": "doomsayer",
"firstNightReminder": "", "firstNightReminder": "",
"otherNightReminder": "", "otherNightReminder": "",
"reminders": [], "reminders": ["Used"],
"setup": false, "setup": false,
"name": "Doomsayer", "name": "Doomsayer",
"team": "fabled", "team": "fabled",

View file

@ -31,7 +31,7 @@
"hatred": [ "hatred": [
{ {
"id": "Heretic", "id": "Heretic",
"reason": "A Pit-Hag can not create a Heretic. " "reason": "A Pit-Hag cannot create a Heretic. "
}, },
{ {
"id": "Damsel", "id": "Damsel",
@ -39,7 +39,7 @@
}, },
{ {
"id": "Politician", "id": "Politician",
"reason": "A Pit-hag can not create an evil Politician." "reason": "A Pit-hag cannot create an evil Politician."
} }
] ]
}, },
@ -69,19 +69,19 @@
}, },
{ {
"id": "Ravenkeeper", "id": "Ravenkeeper",
"reason": "If Leviathan is in play & the Ravenkeeper dies by execution, they wake that night to use their ability." "reason": "If Leviathan is in play and the Ravenkeeper dies by execution, they wake that night to use their ability."
}, },
{ {
"id": "Sage", "id": "Sage",
"reason": "If Leviathan is in play & the Sage dies by execution, they wake that night to use their ability." "reason": "If Leviathan is in play and the Sage dies by execution, they wake that night to use their ability."
}, },
{ {
"id": "Farmer", "id": "Farmer",
"reason": "If Leviathan is in play & a Farmer dies by execution, a good player becomes a Farmer that night." "reason": "If Leviathan is in play and a Farmer dies by execution, a good player becomes a Farmer that night."
}, },
{ {
"id": "Mayor", "id": "Mayor",
"reason": "If Leviathan is in play & no execution occurs on day 5, good wins." "reason": "If Leviathan is in play and no execution occurs on day 5, good wins."
} }
] ]
}, },
@ -107,11 +107,11 @@
}, },
{ {
"id": "Magician", "id": "Magician",
"reason": "Only 1 jinxed character can be in play. " "reason": "Only one jinxed character can be in play. "
}, },
{ {
"id": "Scarlet Woman", "id": "Scarlet Woman",
"reason": "If there are 5 or more players alive and the player holding the Lil' Monsta token dies, the Scarlet Woman is given the Lil' Monsta token tonight." "reason": "If there are five or more players alive and the player holding the Lil' Monsta token dies, the Scarlet Woman is given the Lil' Monsta token tonight."
} }
] ]
}, },
@ -120,7 +120,7 @@
"hatred": [ "hatred": [
{ {
"id": "Gambler", "id": "Gambler",
"reason": "If the Lycanthrope is alive and the Gambler kills themself at night, no other players can die tonight." "reason": "If the Lycanthrope is alive and the Gambler kills themselves at night, no other players can die tonight."
} }
] ]
}, },
@ -129,11 +129,11 @@
"hatred": [ "hatred": [
{ {
"id": "Engineer", "id": "Engineer",
"reason": "Legion and the Engineer can not both be in play at the start of the game. If the Engineer creates Legion, most players (including all evil players) become evil Legion." "reason": "Legion and the Engineer cannot both be in play at the start of the game. If the Engineer creates Legion, most players (including all evil players) become evil Legion."
}, },
{ {
"id": "Preacher", "id": "Preacher",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
} }
] ]
}, },
@ -151,11 +151,11 @@
"hatred": [ "hatred": [
{ {
"id": "Magician", "id": "Magician",
"reason": "When the Spy sees the Grimoire, the Demon and Magician's character tokens are removed." "reason": "When the Spy sees the Grimoire, the Demon and the Magician's character tokens are removed."
}, },
{ {
"id": "Alchemist", "id": "Alchemist",
"reason": "The Alchemist can not have the Spy ability." "reason": "The Alchemist cannot have the Spy ability."
}, },
{ {
"id": "Poppy Grower", "id": "Poppy Grower",
@ -163,11 +163,11 @@
}, },
{ {
"id": "Damsel", "id": "Damsel",
"reason": "Only 1 jinxed character can be in play. " "reason": "Only one jinxed character can be in play. "
}, },
{ {
"id": "Heretic", "id": "Heretic",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
} }
] ]
}, },
@ -176,7 +176,7 @@
"hatred": [ "hatred": [
{ {
"id": "Magician", "id": "Magician",
"reason": "When the Widow sees the Grimoire, the Demon and Magician's character tokens are removed." "reason": "When the Widow sees the Grimoire, the Demon and the Magician's character tokens are removed."
}, },
{ {
"id": "Poppy Grower", "id": "Poppy Grower",
@ -184,15 +184,15 @@
}, },
{ {
"id": "Alchemist", "id": "Alchemist",
"reason": "The Alchemist can not have the Widow ability." "reason": "The Alchemist cannot have the Widow ability."
}, },
{ {
"id": "Damsel", "id": "Damsel",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
}, },
{ {
"id": "Heretic", "id": "Heretic",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
} }
] ]
}, },
@ -201,7 +201,7 @@
"hatred": [ "hatred": [
{ {
"id": "Heretic", "id": "Heretic",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
} }
] ]
}, },
@ -210,7 +210,7 @@
"hatred": [ "hatred": [
{ {
"id": "Heretic", "id": "Heretic",
"reason": "The Baron might only add 1 Outsider, not 2." "reason": "The Baron might only add one Outsider, not two."
} }
] ]
}, },
@ -219,7 +219,7 @@
"hatred": [ "hatred": [
{ {
"id": "Lil' Monsta", "id": "Lil' Monsta",
"reason": "The Marionette neighbors a Minion, not the Demon. The Marionette is not woken to choose who takes the Lil' Monsta token." "reason": "The Marionette neighbors a Minion, not the Demon. The Marionette is not woken to choose who takes the Lil' Monsta token, and does not learn they are the Marionette if they have the Lil' Monsta token."
}, },
{ {
"id": "Poppy Grower", "id": "Poppy Grower",
@ -248,7 +248,7 @@
"hatred": [ "hatred": [
{ {
"id": "Engineer", "id": "Engineer",
"reason": "Riot and the Engineer can not both be in play at the start of the game. \nIf the Engineer creates Riot, the evil players become Riot." "reason": "Riot and the Engineer cannot both be in play at the start of the game. If the Engineer creates Riot, the evil players become Riot."
}, },
{ {
"id": "Golem", "id": "Golem",
@ -256,7 +256,7 @@
}, },
{ {
"id": "Snitch", "id": "Snitch",
"reason": "If the Snitch is in play, each Riot player gets an extra 3 bluffs." "reason": "If the Snitch is in play, each Riot player gets an extra three bluffs."
}, },
{ {
"id": "Saint", "id": "Saint",
@ -264,19 +264,19 @@
}, },
{ {
"id": "Butler", "id": "Butler",
"reason": "The Butler can not nominate their master." "reason": "The Butler cannot nominate their master."
}, },
{ {
"id": "Pit-Hag", "id": "Pit-Hag",
"reason": "If the Pit-Hag creates Riot, all evil players become Riot. \nIf the Pit-Hag creates Riot after day 3, the game continues for one more day." "reason": "If the Pit-Hag creates Riot, all evil players become Riot. If the Pit-Hag creates Riot after day 3, the game continues for one more day."
}, },
{ {
"id": "Mayor", "id": "Mayor",
"reason": "If the 3rd day begins with just three players alive, the players may choose (as a group) not to nominate at all. If so (and a Mayor is alive) then the Mayor's team wins." "reason": "If the third day begins with just three players alive, the players may choose (as a group) not to nominate at all. If so (and a Mayor is alive) the Mayor's team wins."
}, },
{ {
"id": "Monk", "id": "Monk",
"reason": "If a Riot player nominates and kills the Monk-protected-player, the Monk-protected-player does not die." "reason": "If a Riot player nominates a Monk-protected player, the protected-player does not die."
}, },
{ {
"id": "Farmer", "id": "Farmer",
@ -284,7 +284,7 @@
}, },
{ {
"id": "Innkeeper", "id": "Innkeeper",
"reason": "If a Riot player nominates an Innkeeper-protected-player, the Innkeeper-protected-player does not die." "reason": "If a Riot player nominates an Innkeeper-protected player, the protected-player does not die."
}, },
{ {
"id": "Sage", "id": "Sage",
@ -300,7 +300,7 @@
}, },
{ {
"id": "Grandmother", "id": "Grandmother",
"reason": "If a Riot player nominates and kills the Grandchild, the Grandmother dies too." "reason": "If a Riot player nominates and kills the grandchild, the Grandmother dies too."
}, },
{ {
"id": "King", "id": "King",
@ -308,31 +308,31 @@
}, },
{ {
"id": "Exorcist", "id": "Exorcist",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
}, },
{ {
"id": "Minstrel", "id": "Minstrel",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
}, },
{ {
"id": "Flowergirl", "id": "Flowergirl",
"reason": "Only 1 jinxed character can be in play." "reason": "Only one jinxed character can be in play."
}, },
{ {
"id": "Undertaker", "id": "Undertaker",
"reason": "Players that die by nomination register as being executed to the Undertaker." "reason": "Players that die by nomination register as executed to the Undertaker."
}, },
{ {
"id": "Cannibal", "id": "Cannibal",
"reason": "Players that die by nomination register as being executed to the Cannibal." "reason": "Players that die by nomination register as executed to the Cannibal."
}, },
{ {
"id": "Pacifist", "id": "Pacifist",
"reason": "Players that die by nomination register as being executed to the Pacifist." "reason": "Players that die by nomination register as executed to the Pacifist."
}, },
{ {
"id": "Devil's Advocate", "id": "Devil's Advocate",
"reason": "Players that die by nomination register as being executed to the Devil's Advocate." "reason": "Players that die by nomination register as executed to the Devil's Advocate."
}, },
{ {
"id": "Investigator", "id": "Investigator",
@ -365,7 +365,7 @@
}, },
{ {
"id": "Slayer", "id": "Slayer",
"reason": "If the Slayer slays the Lleech's host, the host dies. " "reason": "If the Slayer shoots the Lleech's host, the host dies. "
}, },
{ {
"id": "Heretic", "id": "Heretic",

View file

@ -1725,7 +1725,7 @@
"Day 4", "Day 4",
"Day 5"], "Day 5"],
"setup": false, "setup": false,
"ability": "If more than 1 good player is executed, you win. All players know you are in play. After day 5, evil wins." "ability": "If more than 1 good player is executed, evil wins. All players know you are in play. After day 5, evil wins."
}, },
{ {
"id": "riot", "id": "riot",

View file

@ -70,7 +70,9 @@
"customNote": "Add a custom reminder node" "customNote": "Add a custom reminder node"
}, },
"vote":{ "vote":{
"nominated": "nominated", "nominates": "nominates",
"callexile": "calls for the exile of",
"exclam": "!",
"votes": "votes", "votes": "votes",
"inFavor": "in favor", "inFavor": "in favor",
"majorityIs": "majority is", "majorityIs": "majority is",
@ -205,6 +207,7 @@
"reference": { "reference": {
"title": "Character Reference", "title": "Character Reference",
"jinxed": "Jinxed", "jinxed": "Jinxed",
"notfirstnight": "*Not the first night",
"teamNames": { "teamNames": {
"townsfolk": "townfolk", "townsfolk": "townfolk",
"outsider": "outsider", "outsider": "outsider",

View file

@ -5,7 +5,7 @@
"team": "fabled", "team": "fabled",
"firstNightReminder": "", "firstNightReminder": "",
"otherNightReminder": "", "otherNightReminder": "",
"reminders": [], "reminders": ["Utilisé"],
"setup": false, "setup": false,
"ability": "Si 4 joueurs ou plus sont en vie, chaque joueur vivant peut, une fois par partie, décider qu'un joueur de son propre alignement meure." "ability": "Si 4 joueurs ou plus sont en vie, chaque joueur vivant peut, une fois par partie, décider qu'un joueur de son propre alignement meure."
}, },

View file

@ -70,7 +70,9 @@
"customNote": "Ajouter une note personnalisée" "customNote": "Ajouter une note personnalisée"
}, },
"vote":{ "vote":{
"nominated": "accuse", "nominates": "accuse",
"callexile": "veut exiler",
"exclam": " !",
"votes": "votes", "votes": "votes",
"inFavor": "pour", "inFavor": "pour",
"majorityIs": "majorité à", "majorityIs": "majorité à",
@ -205,6 +207,7 @@
"reference": { "reference": {
"title": "Réference de rôles", "title": "Réference de rôles",
"jinxed": "Jinx", "jinxed": "Jinx",
"notfirstnight": "* Pas la première nuit",
"teamNames": { "teamNames": {
"townsfolk": "villageois", "townsfolk": "villageois",
"outsider": "étranger", "outsider": "étranger",

View file

@ -5,22 +5,22 @@ const NEWPLAYER = {
reminders: [], reminders: [],
isVoteless: false, isVoteless: false,
isDead: false, isDead: false,
pronouns: "" pronouns: "",
}; };
const state = () => ({ const state = () => ({
players: [], players: [],
fabled: [], fabled: [],
bluffs: [] bluffs: [],
}); });
const getters = { const getters = {
alive({ players }) { alive({ players }) {
return players.filter(player => !player.isDead).length; return players.filter((player) => !player.isDead).length;
}, },
nonTravelers({ players }) { nonTravelers({ players }) {
const nonTravelers = players.filter( const nonTravelers = players.filter(
player => player.role.team !== "traveler" (player) => player.role.team !== "traveler",
); );
return Math.min(nonTravelers.length, 15); return Math.min(nonTravelers.length, 15);
}, },
@ -36,7 +36,7 @@ const getters = {
otherNight.push(role.otherNight); otherNight.push(role.otherNight);
} }
}); });
fabled.forEach(role => { fabled.forEach((role) => {
if (role.firstNight && !firstNight.includes(role.firstNight)) { if (role.firstNight && !firstNight.includes(role.firstNight)) {
firstNight.push(role.firstNight); firstNight.push(role.firstNight);
} }
@ -47,32 +47,32 @@ const getters = {
firstNight.sort((a, b) => a - b); firstNight.sort((a, b) => a - b);
otherNight.sort((a, b) => a - b); otherNight.sort((a, b) => a - b);
const nightOrder = new Map(); const nightOrder = new Map();
players.forEach(player => { players.forEach((player) => {
const first = Math.max(firstNight.indexOf(player.role.firstNight), 0); const first = Math.max(firstNight.indexOf(player.role.firstNight), 0);
const other = Math.max(otherNight.indexOf(player.role.otherNight), 0); const other = Math.max(otherNight.indexOf(player.role.otherNight), 0);
nightOrder.set(player, { first, other }); nightOrder.set(player, { first, other });
}); });
fabled.forEach(role => { fabled.forEach((role) => {
const first = Math.max(firstNight.indexOf(role.firstNight), 0); const first = Math.max(firstNight.indexOf(role.firstNight), 0);
const other = Math.max(otherNight.indexOf(role.otherNight), 0); const other = Math.max(otherNight.indexOf(role.otherNight), 0);
nightOrder.set(role, { first, other }); nightOrder.set(role, { first, other });
}); });
return nightOrder; return nightOrder;
} },
}; };
const actions = { const actions = {
randomize({ state, commit }) { randomize({ state, commit }) {
const players = state.players const players = state.players
.map(a => [Math.random(), a]) .map((a) => [Math.random(), a])
.sort((a, b) => a[0] - b[0]) .sort((a, b) => a[0] - b[0])
.map(a => a[1]); .map((a) => a[1]);
commit("set", players); commit("set", players);
}, },
clearRoles({ state, commit, rootState }) { clearRoles({ state, commit, rootState }) {
let players; let players;
if (rootState.session.isSpectator) { if (rootState.session.isSpectator) {
players = state.players.map(player => { players = state.players.map((player) => {
if (player.role.team !== "traveler") { if (player.role.team !== "traveler") {
player.role = {}; player.role = {};
} }
@ -84,13 +84,13 @@ const actions = {
...NEWPLAYER, ...NEWPLAYER,
name, name,
id, id,
pronouns pronouns,
})); }));
commit("setFabled", { fabled: [] }); commit("setFabled", { fabled: [] });
} }
commit("set", players); commit("set", players);
commit("setBluff"); commit("setBluff");
} },
}; };
const mutations = { const mutations = {
@ -119,7 +119,7 @@ const mutations = {
add(state, name) { add(state, name) {
state.players.push({ state.players.push({
...NEWPLAYER, ...NEWPLAYER,
name name,
}); });
}, },
remove(state, index) { remove(state, index) {
@ -128,7 +128,7 @@ const mutations = {
swap(state, [from, to]) { swap(state, [from, to]) {
[state.players[from], state.players[to]] = [ [state.players[from], state.players[to]] = [
state.players[to], state.players[to],
state.players[from] state.players[from],
]; ];
// hack: "modify" the array so that Vue notices something changed // hack: "modify" the array so that Vue notices something changed
state.players.splice(0, 0); state.players.splice(0, 0);
@ -153,7 +153,7 @@ const mutations = {
state.fabled = fabled; state.fabled = fabled;
} }
} }
} },
}; };
export default { export default {
@ -161,5 +161,5 @@ export default {
state, state,
getters, getters,
actions, actions,
mutations mutations,
}; };

View file

@ -29,7 +29,7 @@ const state = () => ({
voteHistory: [], voteHistory: [],
markedPlayer: -1, markedPlayer: -1,
isVoteHistoryAllowed: true, isVoteHistoryAllowed: true,
isRolesDistributed: false isRolesDistributed: false,
}); });
const getters = {}; const getters = {};
@ -37,7 +37,7 @@ const getters = {};
const actions = {}; const actions = {};
// mutations helper functions // mutations helper functions
const set = key => (state, val) => { const set = (key) => (state, val) => {
state[key] = val; state[key] = val;
}; };
@ -62,7 +62,7 @@ const mutations = {
}, },
nomination( nomination(
state, state,
{ nomination, votes, votingSpeed, lockedVote, isVoteInProgress } = {} { nomination, votes, votingSpeed, lockedVote, isVoteInProgress } = {},
) { ) {
state.nomination = nomination || false; state.nomination = nomination || false;
state.votes = votes || []; state.votes = votes || [];
@ -91,14 +91,14 @@ const mutations = {
: gameInfo.state.locale.modal.voteHistory.execution + : gameInfo.state.locale.modal.voteHistory.execution +
(organGrinder && !state.isSpectator ? "*" : ""), (organGrinder && !state.isSpectator ? "*" : ""),
majority: Math.ceil( majority: Math.ceil(
players.filter(player => !player.isDead || isExile).length / 2 players.filter((player) => !player.isDead || isExile).length / 2,
), ),
votes: votes:
organGrinder && state.isSpectator organGrinder && state.isSpectator
? null ? null
: players : players
.filter((player, index) => state.votes[index]) .filter((player, index) => state.votes[index])
.map(({ name }) => name) .map(({ name }) => name),
}); });
}, },
clearVoteHistory(state) { clearVoteHistory(state) {
@ -114,7 +114,7 @@ const mutations = {
voteSync: handleVote, voteSync: handleVote,
lockVote(state, lock) { lockVote(state, lock) {
state.lockedVote = lock !== undefined ? lock : state.lockedVote + 1; state.lockedVote = lock !== undefined ? lock : state.lockedVote + 1;
} },
}; };
export default { export default {
@ -122,5 +122,5 @@ export default {
state, state,
getters, getters,
actions, actions,
mutations mutations,
}; };

View file

@ -1,5 +1,5 @@
module.exports = store => { module.exports = (store) => {
const updatePagetitle = isPublic => const updatePagetitle = (isPublic) =>
(document.title = `Blood on the Clocktower ${ (document.title = `Blood on the Clocktower ${
isPublic ? "Town Square" : "Grimoire" isPublic ? "Town Square" : "Grimoire"
}`); }`);
@ -39,27 +39,27 @@ module.exports = store => {
JSON.parse(localStorage.bluffs).forEach((role, index) => { JSON.parse(localStorage.bluffs).forEach((role, index) => {
store.commit("players/setBluff", { store.commit("players/setBluff", {
index, index,
role: store.state.roles.get(role) || {} role: store.state.roles.get(role) || {},
}); });
}); });
} }
if (localStorage.fabled !== undefined) { if (localStorage.fabled !== undefined) {
store.commit("players/setFabled", { store.commit("players/setFabled", {
fabled: JSON.parse(localStorage.fabled).map( fabled: JSON.parse(localStorage.fabled).map(
fabled => store.state.fabled.get(fabled.id) || fabled (fabled) => store.state.fabled.get(fabled.id) || fabled,
) ),
}); });
} }
if (localStorage.players) { if (localStorage.players) {
store.commit( store.commit(
"players/set", "players/set",
JSON.parse(localStorage.players).map(player => ({ JSON.parse(localStorage.players).map((player) => ({
...player, ...player,
role: role:
store.state.roles.get(player.role) || store.state.roles.get(player.role) ||
store.getters.rolesJSONbyId.get(player.role) || store.getters.rolesJSONbyId.get(player.role) ||
{} {},
})) })),
); );
} }
/**** Session related data *****/ /**** Session related data *****/
@ -141,17 +141,17 @@ module.exports = store => {
case "players/setBluff": case "players/setBluff":
localStorage.setItem( localStorage.setItem(
"bluffs", "bluffs",
JSON.stringify(state.players.bluffs.map(({ id }) => id)) JSON.stringify(state.players.bluffs.map(({ id }) => id)),
); );
break; break;
case "players/setFabled": case "players/setFabled":
localStorage.setItem( localStorage.setItem(
"fabled", "fabled",
JSON.stringify( JSON.stringify(
state.players.fabled.map(fabled => state.players.fabled.map((fabled) =>
fabled.isCustom ? fabled : { id: fabled.id } fabled.isCustom ? fabled : { id: fabled.id },
) ),
) ),
); );
break; break;
case "players/add": case "players/add":
@ -165,12 +165,12 @@ module.exports = store => {
localStorage.setItem( localStorage.setItem(
"players", "players",
JSON.stringify( JSON.stringify(
state.players.players.map(player => ({ state.players.players.map((player) => ({
...player, ...player,
// simplify the stored data // simplify the stored data
role: player.role.id || {} role: player.role.id || {},
})) })),
) ),
); );
} else { } else {
localStorage.removeItem("players"); localStorage.removeItem("players");
@ -180,7 +180,7 @@ module.exports = store => {
if (payload) { if (payload) {
localStorage.setItem( localStorage.setItem(
"session", "session",
JSON.stringify([state.session.isSpectator, payload]) JSON.stringify([state.session.isSpectator, payload]),
); );
} else { } else {
localStorage.removeItem("session"); localStorage.removeItem("session");

View file

@ -28,11 +28,11 @@ class LiveSession {
this._wss + this._wss +
channel + channel +
"/" + "/" +
(this._isSpectator ? this._store.state.session.playerId : "host") (this._isSpectator ? this._store.state.session.playerId : "host"),
); );
this._socket.addEventListener("message", this._handleMessage.bind(this)); this._socket.addEventListener("message", this._handleMessage.bind(this));
this._socket.onopen = this._onOpen.bind(this); this._socket.onopen = this._onOpen.bind(this);
this._socket.onclose = err => { this._socket.onclose = (err) => {
this._socket = null; this._socket = null;
clearInterval(this._pingTimer); clearInterval(this._pingTimer);
this._pingTimer = null; this._pingTimer = null;
@ -41,7 +41,7 @@ class LiveSession {
this._store.commit("session/setReconnecting", true); this._store.commit("session/setReconnecting", true);
this._reconnectTimer = setTimeout( this._reconnectTimer = setTimeout(
() => this.connect(channel), () => this.connect(channel),
3 * 1000 3 * 1000,
); );
} else { } else {
this._store.commit("session/setSessionId", ""); this._store.commit("session/setSessionId", "");
@ -87,7 +87,7 @@ class LiveSession {
this._sendDirect( this._sendDirect(
"host", "host",
"getGamestate", "getGamestate",
this._store.state.session.playerId this._store.state.session.playerId,
); );
} else { } else {
this.sendGamestate(); this.sendGamestate();
@ -105,7 +105,7 @@ class LiveSession {
this._isSpectator this._isSpectator
? this._store.state.session.playerId ? this._store.state.session.playerId
: Object.keys(this._players).length, : Object.keys(this._players).length,
"latency" "latency",
]); ]);
clearTimeout(this._pingTimer); clearTimeout(this._pingTimer);
this._pingTimer = setTimeout(this._ping.bind(this), this._pingInterval); this._pingTimer = setTimeout(this._ping.bind(this), this._pingInterval);
@ -151,7 +151,7 @@ class LiveSession {
// create vote history record // create vote history record
this._store.commit( this._store.commit(
"session/addHistory", "session/addHistory",
this._store.state.players.players this._store.state.players.players,
); );
} }
this._store.commit("session/nomination", { nomination: params }); this._store.commit("session/nomination", { nomination: params });
@ -229,9 +229,7 @@ class LiveSession {
if (!this._store.state.session.playerId) { if (!this._store.state.session.playerId) {
this._store.commit( this._store.commit(
"session/setPlayerId", "session/setPlayerId",
Math.random() Math.random().toString(36).substr(2),
.toString(36)
.substr(2)
); );
} }
this._pings = {}; this._pings = {};
@ -267,7 +265,7 @@ class LiveSession {
*/ */
sendGamestate(playerId = "", isLightweight = false) { sendGamestate(playerId = "", isLightweight = false) {
if (this._isSpectator) return; if (this._isSpectator) return;
this._gamestate = this._store.state.players.players.map(player => ({ this._gamestate = this._store.state.players.players.map((player) => ({
name: player.name, name: player.name,
id: player.id, id: player.id,
isDead: player.isDead, isDead: player.isDead,
@ -275,12 +273,12 @@ class LiveSession {
pronouns: player.pronouns, pronouns: player.pronouns,
...(player.role && player.role.team === "traveler" ...(player.role && player.role.team === "traveler"
? { roleId: player.role.id } ? { roleId: player.role.id }
: {}) : {}),
})); }));
if (isLightweight) { if (isLightweight) {
this._sendDirect(playerId, "gs", { this._sendDirect(playerId, "gs", {
gamestate: this._gamestate, gamestate: this._gamestate,
isLightweight isLightweight,
}); });
} else { } else {
const { session, grimoire } = this._store.state; const { session, grimoire } = this._store.state;
@ -298,8 +296,8 @@ class LiveSession {
lockedVote: session.lockedVote, lockedVote: session.lockedVote,
isVoteInProgress: session.isVoteInProgress, isVoteInProgress: session.isVoteInProgress,
markedPlayer: session.markedPlayer, markedPlayer: session.markedPlayer,
fabled: fabled.map(f => (f.isCustom ? f : { id: f.id })), fabled: fabled.map((f) => (f.isCustom ? f : { id: f.id })),
...(session.nomination ? { votes: session.votes } : {}) ...(session.nomination ? { votes: session.votes } : {}),
}); });
} }
} }
@ -325,7 +323,7 @@ class LiveSession {
lockedVote, lockedVote,
isVoteInProgress, isVoteInProgress,
markedPlayer, markedPlayer,
fabled fabled,
} = data; } = data;
const players = this._store.state.players.players; const players = this._store.state.players.players;
// adjust number of players // adjust number of players
@ -343,7 +341,7 @@ class LiveSession {
const player = players[x]; const player = players[x];
const { roleId } = state; const { roleId } = state;
// update relevant properties // update relevant properties
["name", "id", "isDead", "isVoteless", "pronouns"].forEach(property => { ["name", "id", "isDead", "isVoteless", "pronouns"].forEach((property) => {
const value = state[property]; const value = state[property];
if (player[property] !== value) { if (player[property] !== value) {
this._store.commit("players/update", { player, property, value }); this._store.commit("players/update", { player, property, value });
@ -358,14 +356,14 @@ class LiveSession {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "role", property: "role",
value: role value: role,
}); });
} }
} else if (!roleId && player.role.team === "traveler") { } else if (!roleId && player.role.team === "traveler") {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "role", property: "role",
value: {} value: {},
}); });
} }
}); });
@ -380,11 +378,11 @@ class LiveSession {
votes, votes,
votingSpeed, votingSpeed,
lockedVote, lockedVote,
isVoteInProgress isVoteInProgress,
}); });
this._store.commit("session/setMarkedPlayer", markedPlayer); this._store.commit("session/setMarkedPlayer", markedPlayer);
this._store.commit("players/setFabled", { this._store.commit("players/setFabled", {
fabled: fabled.map(f => this._store.state.fabled.get(f.id) || f) fabled: fabled.map((f) => this._store.state.fabled.get(f.id) || f),
}); });
} }
} }
@ -402,7 +400,7 @@ class LiveSession {
} }
this._sendDirect(playerId, "edition", { this._sendDirect(playerId, "edition", {
edition: edition.isOfficial ? { id: edition.id } : edition, edition: edition.isOfficial ? { id: edition.id } : edition,
...(roles ? { roles } : {}) ...(roles ? { roles } : {}),
}); });
} }
@ -427,7 +425,7 @@ class LiveSession {
alert( alert(
`This session contains custom characters that can't be found. ` + `This session contains custom characters that can't be found. ` +
`Please load them before joining! ` + `Please load them before joining! ` +
`Missing roles: ${missing.join(", ")}` `Missing roles: ${missing.join(", ")}`,
); );
this.disconnect(); this.disconnect();
this._store.commit("toggleModal", "edition"); this._store.commit("toggleModal", "edition");
@ -443,7 +441,7 @@ class LiveSession {
const { fabled } = this._store.state.players; const { fabled } = this._store.state.players;
this._send( this._send(
"fabled", "fabled",
fabled.map(f => (f.isCustom ? f : { id: f.id })) fabled.map((f) => (f.isCustom ? f : { id: f.id })),
); );
} }
@ -455,7 +453,7 @@ class LiveSession {
_updateFabled(fabled) { _updateFabled(fabled) {
if (!this._isSpectator) return; if (!this._isSpectator) return;
this._store.commit("players/setFabled", { this._store.commit("players/setFabled", {
fabled: fabled.map(f => this._store.state.fabled.get(f.id) || f) fabled: fabled.map((f) => this._store.state.fabled.get(f.id) || f),
}); });
} }
@ -475,7 +473,7 @@ class LiveSession {
this._send("player", { this._send("player", {
index, index,
property, property,
value: value.id value: value.id,
}); });
} else if (this._gamestate[index].roleId) { } else if (this._gamestate[index].roleId) {
// player was previously a traveler // player was previously a traveler
@ -505,7 +503,7 @@ class LiveSession {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "role", property: "role",
value: {} value: {},
}); });
} else { } else {
// load role, first from session, the global, then fail gracefully // load role, first from session, the global, then fail gracefully
@ -516,7 +514,7 @@ class LiveSession {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "role", property: "role",
value: role value: role,
}); });
} }
} else { } else {
@ -556,7 +554,7 @@ class LiveSession {
player, player,
property: "pronouns", property: "pronouns",
value, value,
isFromSockets: true isFromSockets: true,
}); });
} }
@ -577,12 +575,12 @@ class LiveSession {
} }
} }
// remove claimed seats from players that are no longer connected // remove claimed seats from players that are no longer connected
this._store.state.players.players.forEach(player => { this._store.state.players.players.forEach((player) => {
if (player.id && !this._players[player.id]) { if (player.id && !this._players[player.id]) {
this._store.commit("players/update", { this._store.commit("players/update", {
player, player,
property: "id", property: "id",
value: "" value: "",
}); });
} }
}); });
@ -596,7 +594,7 @@ class LiveSession {
const pings = Object.values(this._pings); const pings = Object.values(this._pings);
this._store.commit( this._store.commit(
"session/setPing", "session/setPing",
Math.round(pings.reduce((a, b) => a + b, 0) / pings.length) Math.round(pings.reduce((a, b) => a + b, 0) / pings.length),
); );
} }
} }
@ -608,7 +606,7 @@ class LiveSession {
if (!this._isSpectator || playerIdOrCount) { if (!this._isSpectator || playerIdOrCount) {
this._store.commit( this._store.commit(
"session/setPlayerCount", "session/setPlayerCount",
this._isSpectator ? playerIdOrCount : Object.keys(this._players).length this._isSpectator ? playerIdOrCount : Object.keys(this._players).length,
); );
} }
} }
@ -623,7 +621,7 @@ class LiveSession {
delete this._players[playerId]; delete this._players[playerId];
this._store.commit( this._store.commit(
"session/setPlayerCount", "session/setPlayerCount",
Object.keys(this._players).length Object.keys(this._players).length,
); );
} }
@ -656,7 +654,7 @@ class LiveSession {
this._store.commit("players/update", { this._store.commit("players/update", {
player: players[oldIndex], player: players[oldIndex],
property, property,
value: "" value: "",
}); });
} }
// add playerId to new seat // add playerId to new seat
@ -680,7 +678,7 @@ class LiveSession {
if (player.id && player.role) { if (player.id && player.role) {
message[player.id] = [ message[player.id] = [
"player", "player",
{ index, property: "role", value: player.role.id } { index, property: "role", value: player.role.id },
]; ];
} }
}); });
@ -755,7 +753,7 @@ class LiveSession {
if (this._isSpectator) return; if (this._isSpectator) return;
this._send( this._send(
"isVoteHistoryAllowed", "isVoteHistoryAllowed",
this._store.state.session.isVoteHistoryAllowed this._store.state.session.isVoteHistoryAllowed,
); );
} }
@ -802,7 +800,7 @@ class LiveSession {
this._send("vote", [ this._send("vote", [
index, index,
this._store.state.session.votes[index], this._store.state.session.votes[index],
!this._isSpectator !this._isSpectator,
]); ]);
} }
} }
@ -881,7 +879,7 @@ class LiveSession {
} }
} }
export default store => { export default (store) => {
// setup // setup
const session = new LiveSession(store); const session = new LiveSession(store);

View file

@ -1,5 +1,5 @@
module.exports = { module.exports = {
// if the app is supposed to run on Github Pages in a subfolder, use the following config: // if the app is supposed to run on Github Pages in a subfolder, use the following config:
// publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/" // publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/"
publicPath: process.env.NODE_ENV === "production" ? "/" : "/" publicPath: process.env.NODE_ENV === "production" ? "/" : "/",
}; };