mirror of
				https://github.com/bra1n/townsquare.git
				synced 2025-10-21 16:55:12 +00:00 
			
		
		
		
	
						commit
						ef4241308e
					
				
					 25 changed files with 1028 additions and 988 deletions
				
			
		
							
								
								
									
										3
									
								
								.github/workflows/linter.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/linter.yml
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -18,6 +18,9 @@ on:
 | 
				
			||||||
  push:
 | 
					  push:
 | 
				
			||||||
    branches-ignore:
 | 
					    branches-ignore:
 | 
				
			||||||
      - 'gh-pages'
 | 
					      - 'gh-pages'
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    # The branches below must be a subset of the branches above
 | 
				
			||||||
 | 
					    branches: [ main ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
###############
 | 
					###############
 | 
				
			||||||
# Set the Job #
 | 
					# Set the Job #
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										47
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								CHANGELOG.md
									
										
									
									
									
								
							| 
						 | 
					@ -1,10 +1,57 @@
 | 
				
			||||||
# Release Notes
 | 
					# Release Notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.6.0
 | 
				
			||||||
 | 
					- night mode can be toggeled with [S] now (thanks @davotronic5000)
 | 
				
			||||||
 | 
					- night order shows which players are dead
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.5.0
 | 
				
			||||||
 | 
					- all travelers from the base editions are now optionally available (thanks @davotronic5000)
 | 
				
			||||||
 | 
					- night order shows player names near roles now
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.4.0
 | 
				
			||||||
 | 
					- added spoiler role (Pixie!)
 | 
				
			||||||
 | 
					- fixed bug with ST sending out roles that are not part of the current edition / script (ie. travelers or base set roles)
 | 
				
			||||||
 | 
					- better Lycanthrope icon (thanks @AWConant)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.3.1
 | 
				
			||||||
 | 
					- better vote history design and added timestamps
 | 
				
			||||||
 | 
					- adjusted player menu styling on smaller screens
 | 
				
			||||||
 | 
					- improved CONTRIBUTING.md description of hosting your own copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.3.0
 | 
				
			||||||
 | 
					- added spoiler role (Lycanthrope!)
 | 
				
			||||||
 | 
					- fixed copy to clipboard in Firefox
 | 
				
			||||||
 | 
					- fixed non-countdown votes still playing countdown sound for a split second
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.2.1
 | 
				
			||||||
 | 
					- clearing players / roles now also clears Fabled
 | 
				
			||||||
 | 
					- fix list of locked votes showing unlocked votes sometimes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Version 2.2.0
 | 
				
			||||||
 | 
					- added [V] hotkey to open nomination history (thanks @lilserf)
 | 
				
			||||||
 | 
					- updated roles according to official Wiki changes
 | 
				
			||||||
 | 
					- adjusted roles night order
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Version 2.1.1
 | 
					## Version 2.1.1
 | 
				
			||||||
- show vote results at the end of a vote
 | 
					- show vote results at the end of a vote
 | 
				
			||||||
- fixed global reminders not showing up anymore when the associated role is assigned to a player
 | 
					- fixed global reminders not showing up anymore when the associated role is assigned to a player
 | 
				
			||||||
- adjusted backend metrics
 | 
					- adjusted backend metrics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Version 2.1.0
 | 
					## Version 2.1.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,20 @@ $ npm install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The development server can be started with `npm run serve`.
 | 
					The development server can be started with `npm run serve`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Deploying to GitHub pages or with a non-root path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Deploying a forked version to GitHub Pages or running your local
 | 
				
			||||||
 | 
					development copy in a sub-path (instead of the web root) will require you to modify
 | 
				
			||||||
 | 
					the `vue.config.js` and configure the path at which the website will be served.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For example, deploying your forked `townsquare` project to GitHub pages would need the following
 | 
				
			||||||
 | 
					`vue.config.js` changes: 
 | 
				
			||||||
 | 
					```js
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  publicPath: process.env.NODE_ENV === "production" ? "/townsquare/" : "/"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Committing Changes
 | 
					### Committing Changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Commit messages should be verbose enough to allow someone else to follow your changes and should include references to issues that are being worked on.
 | 
					Commit messages should be verbose enough to allow someone else to follow your changes and should include references to issues that are being worked on.
 | 
				
			||||||
| 
						 | 
					@ -64,6 +78,10 @@ $ npm run lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **`dist`**: contains built files for distribution.
 | 
					- **`dist`**: contains built files for distribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **`public`**: static assets and templates that don't need to be built dynamically.
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					- **`server`**: NodeJS code for the live session backend server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- **`src`**: contains the source code. The codebase is written in ES2015.
 | 
					- **`src`**: contains the source code. The codebase is written in ES2015.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - **`assets`**: contains all graphical assets like images, fonts, icons, etc.
 | 
					  - **`assets`**: contains all graphical assets like images, fonts, icons, etc.
 | 
				
			||||||
| 
						 | 
					@ -73,9 +91,13 @@ $ npm run lint
 | 
				
			||||||
    - **`fonts`**: webfonts used on the page
 | 
					    - **`fonts`**: webfonts used on the page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - **`icons`**: character token icons
 | 
					    - **`icons`**: character token icons
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    - **`sounds`**: sound effects used on the page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - **`components`**: the internal components used in the project
 | 
					  - **`components`**: the internal components used in the project
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
    - **`modals`**: the modals have a separate subfolder
 | 
					    - **`modals`**: the modals have a separate subfolder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  - **`store`**: the VueX data store and modules
 | 
					  - **`store`**: the VueX data store and modules
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    - **`modules`**: VueX modules that live in their own namespace
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "townsquare",
 | 
					  "name": "townsquare",
 | 
				
			||||||
  "version": "2.1.1",
 | 
					  "version": "2.6.0",
 | 
				
			||||||
  "lockfileVersion": 1,
 | 
					  "lockfileVersion": 1,
 | 
				
			||||||
  "requires": true,
 | 
					  "requires": true,
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "townsquare",
 | 
					  "name": "townsquare",
 | 
				
			||||||
  "version": "2.1.1",
 | 
					  "version": "2.6.0",
 | 
				
			||||||
  "description": "Blood on the Clocktower Town Square",
 | 
					  "description": "Blood on the Clocktower Town Square",
 | 
				
			||||||
  "author": "Steffen Baumgart",
 | 
					  "author": "Steffen Baumgart",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,6 +102,15 @@ export default {
 | 
				
			||||||
          if (this.session.isSpectator) return;
 | 
					          if (this.session.isSpectator) return;
 | 
				
			||||||
          this.$store.commit("toggleModal", "roles");
 | 
					          this.$store.commit("toggleModal", "roles");
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
 | 
					        case "v":
 | 
				
			||||||
 | 
					          if (this.session.voteHistory.length) {
 | 
				
			||||||
 | 
					            this.$store.commit("toggleModal", "voteHistory");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case "s":
 | 
				
			||||||
 | 
					          if (this.session.isSpectator) return;
 | 
				
			||||||
 | 
					          this.$store.commit("toggleNight");
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
        case "escape":
 | 
					        case "escape":
 | 
				
			||||||
          this.$store.commit("toggleModal");
 | 
					          this.$store.commit("toggleModal");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 95 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/lycanthrope.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/icons/lycanthrope.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 86 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/assets/icons/pixie.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/icons/pixie.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 151 KiB  | 
| 
						 | 
					@ -43,10 +43,7 @@
 | 
				
			||||||
          <li @click="toggleNight" v-if="!session.isSpectator">
 | 
					          <li @click="toggleNight" v-if="!session.isSpectator">
 | 
				
			||||||
            <template v-if="!grimoire.isNight">Switch to Night</template>
 | 
					            <template v-if="!grimoire.isNight">Switch to Night</template>
 | 
				
			||||||
            <template v-if="grimoire.isNight">Switch to Day</template>
 | 
					            <template v-if="grimoire.isNight">Switch to Day</template>
 | 
				
			||||||
            <em
 | 
					            <em>[S]</em>
 | 
				
			||||||
              ><font-awesome-icon
 | 
					 | 
				
			||||||
                :icon="['fas', grimoire.isNight ? 'sun' : 'cloud-moon']"
 | 
					 | 
				
			||||||
            /></em>
 | 
					 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
          <li @click="toggleNightOrder" v-if="players.length">
 | 
					          <li @click="toggleNightOrder" v-if="players.length">
 | 
				
			||||||
            Night order
 | 
					            Night order
 | 
				
			||||||
| 
						 | 
					@ -114,8 +111,7 @@
 | 
				
			||||||
            v-if="session.voteHistory.length"
 | 
					            v-if="session.voteHistory.length"
 | 
				
			||||||
            @click="toggleModal('voteHistory')"
 | 
					            @click="toggleModal('voteHistory')"
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            Nomination history
 | 
					            Nomination history<em>[V]</em>
 | 
				
			||||||
            <em><font-awesome-icon icon="hand-paper"/></em>
 | 
					 | 
				
			||||||
          </li>
 | 
					          </li>
 | 
				
			||||||
          <li @click="leaveSession" v-if="session.sessionId">
 | 
					          <li @click="leaveSession" v-if="session.sessionId">
 | 
				
			||||||
            Leave Session
 | 
					            Leave Session
 | 
				
			||||||
| 
						 | 
					@ -239,16 +235,9 @@ export default {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    copySessionUrl() {
 | 
					    copySessionUrl() {
 | 
				
			||||||
      // check for clipboard permissions
 | 
					      const url = window.location.href.split("#")[0];
 | 
				
			||||||
      navigator.permissions
 | 
					      const link = url + "#" + this.session.sessionId;
 | 
				
			||||||
        .query({ name: "clipboard-write" })
 | 
					      navigator.clipboard.writeText(link);
 | 
				
			||||||
        .then(({ state }) => {
 | 
					 | 
				
			||||||
          if (state === "granted" || state === "prompt") {
 | 
					 | 
				
			||||||
            const url = window.location.href.split("#")[0];
 | 
					 | 
				
			||||||
            const link = url + "#" + this.session.sessionId;
 | 
					 | 
				
			||||||
            navigator.clipboard.writeText(link);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    distributeRoles() {
 | 
					    distributeRoles() {
 | 
				
			||||||
      if (this.session.isSpectator) return;
 | 
					      if (this.session.isSpectator) return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -675,7 +675,7 @@ li.move:not(.from) .player .overlay svg.move {
 | 
				
			||||||
    border: 10px solid transparent;
 | 
					    border: 10px solid transparent;
 | 
				
			||||||
    border-right-color: black;
 | 
					    border-right-color: black;
 | 
				
			||||||
    right: 100%;
 | 
					    right: 100%;
 | 
				
			||||||
    bottom: 7px;
 | 
					    bottom: 5px;
 | 
				
			||||||
    margin-right: 2px;
 | 
					    margin-right: 2px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,11 +161,13 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    voters: function() {
 | 
					    voters: function() {
 | 
				
			||||||
      const nomination = this.session.nomination[1];
 | 
					      const nomination = this.session.nomination[1];
 | 
				
			||||||
      const voters = this.session.votes.map((vote, index) =>
 | 
					      const voters = Array(this.players.length)
 | 
				
			||||||
        vote ? this.players[index].name : ""
 | 
					        .fill("")
 | 
				
			||||||
      );
 | 
					        .map((x, index) =>
 | 
				
			||||||
 | 
					          this.session.votes[index] ? this.players[index].name : ""
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
      const reorder = [
 | 
					      const reorder = [
 | 
				
			||||||
        ...voters.slice(nomination + 1, this.players.length),
 | 
					        ...voters.slice(nomination + 1),
 | 
				
			||||||
        ...voters.slice(0, nomination + 1)
 | 
					        ...voters.slice(0, nomination + 1)
 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
      return reorder.slice(0, this.session.lockedVote - 1).filter(n => !!n);
 | 
					      return reorder.slice(0, this.session.lockedVote - 1).filter(n => !!n);
 | 
				
			||||||
| 
						 | 
					@ -178,15 +180,15 @@ export default {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    countdown() {
 | 
					    countdown() {
 | 
				
			||||||
      this.$store.commit("session/setVoteInProgress", true);
 | 
					 | 
				
			||||||
      this.$store.commit("session/lockVote", 0);
 | 
					      this.$store.commit("session/lockVote", 0);
 | 
				
			||||||
 | 
					      this.$store.commit("session/setVoteInProgress", true);
 | 
				
			||||||
      this.voteTimer = setInterval(() => {
 | 
					      this.voteTimer = setInterval(() => {
 | 
				
			||||||
        this.start();
 | 
					        this.start();
 | 
				
			||||||
      }, 4000);
 | 
					      }, 4000);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    start() {
 | 
					    start() {
 | 
				
			||||||
      this.$store.commit("session/setVoteInProgress", true);
 | 
					 | 
				
			||||||
      this.$store.commit("session/lockVote", 1);
 | 
					      this.$store.commit("session/lockVote", 1);
 | 
				
			||||||
 | 
					      this.$store.commit("session/setVoteInProgress", true);
 | 
				
			||||||
      clearInterval(this.voteTimer);
 | 
					      clearInterval(this.voteTimer);
 | 
				
			||||||
      this.voteTimer = setInterval(() => {
 | 
					      this.voteTimer = setInterval(() => {
 | 
				
			||||||
        this.$store.commit("session/lockVote");
 | 
					        this.$store.commit("session/lockVote");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,14 +54,7 @@ export default {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    copy: function() {
 | 
					    copy: function() {
 | 
				
			||||||
      // check for clipboard permissions
 | 
					      navigator.clipboard.writeText(this.input || this.gamestate);
 | 
				
			||||||
      navigator.permissions
 | 
					 | 
				
			||||||
        .query({ name: "clipboard-write" })
 | 
					 | 
				
			||||||
        .then(({ state }) => {
 | 
					 | 
				
			||||||
          if (state === "granted" || state === "prompt") {
 | 
					 | 
				
			||||||
            navigator.clipboard.writeText(this.input || this.gamestate);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    load: function() {
 | 
					    load: function() {
 | 
				
			||||||
      if (this.session.isSpectator) return;
 | 
					      if (this.session.isSpectator) return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,17 @@
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span class="name">
 | 
					          <span class="name">
 | 
				
			||||||
            {{ role.name }}
 | 
					            {{ role.name }}
 | 
				
			||||||
 | 
					            <template v-if="role.players.length">
 | 
				
			||||||
 | 
					              <br />
 | 
				
			||||||
 | 
					              <small
 | 
				
			||||||
 | 
					                v-for="(player, index) in role.players"
 | 
				
			||||||
 | 
					                :class="{ dead: player.isDead }"
 | 
				
			||||||
 | 
					                :key="index"
 | 
				
			||||||
 | 
					                >{{
 | 
				
			||||||
 | 
					                  player.name + (role.players.length > index + 1 ? "," : "")
 | 
				
			||||||
 | 
					                }}</small
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
          </span>
 | 
					          </span>
 | 
				
			||||||
          <span
 | 
					          <span
 | 
				
			||||||
            class="icon"
 | 
					            class="icon"
 | 
				
			||||||
| 
						 | 
					@ -53,6 +64,17 @@
 | 
				
			||||||
          ></span>
 | 
					          ></span>
 | 
				
			||||||
          <span class="name">
 | 
					          <span class="name">
 | 
				
			||||||
            {{ role.name }}
 | 
					            {{ role.name }}
 | 
				
			||||||
 | 
					            <template v-if="role.players.length">
 | 
				
			||||||
 | 
					              <br />
 | 
				
			||||||
 | 
					              <small
 | 
				
			||||||
 | 
					                v-for="(player, index) in role.players"
 | 
				
			||||||
 | 
					                :class="{ dead: player.isDead }"
 | 
				
			||||||
 | 
					                :key="index"
 | 
				
			||||||
 | 
					                >{{
 | 
				
			||||||
 | 
					                  player.name + (role.players.length > index + 1 ? "," : "")
 | 
				
			||||||
 | 
					                }}</small
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					            </template>
 | 
				
			||||||
          </span>
 | 
					          </span>
 | 
				
			||||||
        </li>
 | 
					        </li>
 | 
				
			||||||
      </ul>
 | 
					      </ul>
 | 
				
			||||||
| 
						 | 
					@ -68,11 +90,6 @@ export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
    Modal
 | 
					    Modal
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  data: function() {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      roleSelection: {}
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
    rolesFirstNight: function() {
 | 
					    rolesFirstNight: function() {
 | 
				
			||||||
      const rolesFirstNight = [];
 | 
					      const rolesFirstNight = [];
 | 
				
			||||||
| 
						 | 
					@ -83,23 +100,22 @@ export default {
 | 
				
			||||||
            id: "evil",
 | 
					            id: "evil",
 | 
				
			||||||
            name: "Minion info",
 | 
					            name: "Minion info",
 | 
				
			||||||
            firstNight: 2,
 | 
					            firstNight: 2,
 | 
				
			||||||
            team: "minion"
 | 
					            team: "minion",
 | 
				
			||||||
 | 
					            players: this.players.filter(p => p.role.team === "minion")
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            id: "evil",
 | 
					            id: "evil",
 | 
				
			||||||
            name: "Demon info & bluffs",
 | 
					            name: "Demon info & bluffs",
 | 
				
			||||||
            firstNight: 4,
 | 
					            firstNight: 4,
 | 
				
			||||||
            team: "demon"
 | 
					            team: "demon",
 | 
				
			||||||
 | 
					            players: this.players.filter(p => p.role.team === "demon")
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.roles.forEach(role => {
 | 
					      this.roles.forEach(role => {
 | 
				
			||||||
        if (
 | 
					        const players = this.players.filter(p => p.role.id === role.id);
 | 
				
			||||||
          role.firstNight &&
 | 
					        if (role.firstNight && (role.team !== "traveler" || players.length)) {
 | 
				
			||||||
          (role.team !== "traveler" ||
 | 
					          rolesFirstNight.push(Object.assign({ players }, role));
 | 
				
			||||||
            this.players.some(p => p.role.id === role.id))
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
          rolesFirstNight.push(role);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      this.fabled
 | 
					      this.fabled
 | 
				
			||||||
| 
						 | 
					@ -113,12 +129,9 @@ export default {
 | 
				
			||||||
    rolesOtherNight: function() {
 | 
					    rolesOtherNight: function() {
 | 
				
			||||||
      const rolesOtherNight = [];
 | 
					      const rolesOtherNight = [];
 | 
				
			||||||
      this.roles.forEach(role => {
 | 
					      this.roles.forEach(role => {
 | 
				
			||||||
        if (
 | 
					        const players = this.players.filter(p => p.role.id === role.id);
 | 
				
			||||||
          role.otherNight &&
 | 
					        if (role.otherNight && (role.team !== "traveler" || players.length)) {
 | 
				
			||||||
          (role.team !== "traveler" ||
 | 
					          rolesOtherNight.push(Object.assign({ players }, role));
 | 
				
			||||||
            this.players.some(p => p.role.id === role.id))
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
          rolesOtherNight.push(role);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      this.fabled
 | 
					      this.fabled
 | 
				
			||||||
| 
						 | 
					@ -179,57 +192,42 @@ h4 {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fabled {
 | 
					.fabled {
 | 
				
			||||||
  .name,
 | 
					  .name {
 | 
				
			||||||
  .player,
 | 
					    background: linear-gradient(90deg, $fabled, transparent 35%);
 | 
				
			||||||
  h4 {
 | 
					    .night .other & {
 | 
				
			||||||
    color: $fabled;
 | 
					      background: linear-gradient(-90deg, $fabled, transparent 35%);
 | 
				
			||||||
    &:before,
 | 
					 | 
				
			||||||
    &:after {
 | 
					 | 
				
			||||||
      background-color: $fabled;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.townsfolk {
 | 
					.townsfolk {
 | 
				
			||||||
  .name,
 | 
					  .name {
 | 
				
			||||||
  .player,
 | 
					    background: linear-gradient(90deg, $townsfolk, transparent 35%);
 | 
				
			||||||
  h4 {
 | 
					    .night .other & {
 | 
				
			||||||
    color: $townsfolk;
 | 
					      background: linear-gradient(-90deg, $townsfolk, transparent 35%);
 | 
				
			||||||
    &:before,
 | 
					 | 
				
			||||||
    &:after {
 | 
					 | 
				
			||||||
      background-color: $townsfolk;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.outsider {
 | 
					.outsider {
 | 
				
			||||||
  .name,
 | 
					  .name {
 | 
				
			||||||
  .player,
 | 
					    background: linear-gradient(90deg, $outsider, transparent 35%);
 | 
				
			||||||
  h4 {
 | 
					    .night .other & {
 | 
				
			||||||
    color: $outsider;
 | 
					      background: linear-gradient(-90deg, $outsider, transparent 35%);
 | 
				
			||||||
    &:before,
 | 
					 | 
				
			||||||
    &:after {
 | 
					 | 
				
			||||||
      background-color: $outsider;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.minion {
 | 
					.minion {
 | 
				
			||||||
  .name,
 | 
					  .name {
 | 
				
			||||||
  .player,
 | 
					    background: linear-gradient(90deg, $minion, transparent 35%);
 | 
				
			||||||
  h4 {
 | 
					    .night .other & {
 | 
				
			||||||
    color: $minion;
 | 
					      background: linear-gradient(-90deg, $minion, transparent 35%);
 | 
				
			||||||
    &:before,
 | 
					 | 
				
			||||||
    &:after {
 | 
					 | 
				
			||||||
      background-color: $minion;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.demon {
 | 
					.demon {
 | 
				
			||||||
  .name,
 | 
					  .name {
 | 
				
			||||||
  .player,
 | 
					    background: linear-gradient(90deg, $demon, transparent 35%);
 | 
				
			||||||
  h4 {
 | 
					    .night .other & {
 | 
				
			||||||
    color: $demon;
 | 
					      background: linear-gradient(-90deg, $demon, transparent 35%);
 | 
				
			||||||
    &:before,
 | 
					 | 
				
			||||||
    &:after {
 | 
					 | 
				
			||||||
      background-color: $demon;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -237,20 +235,15 @@ ul {
 | 
				
			||||||
  li {
 | 
					  li {
 | 
				
			||||||
    display: flex;
 | 
					    display: flex;
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
    align-items: center;
 | 
					    margin-bottom: 3px;
 | 
				
			||||||
    align-content: center;
 | 
					 | 
				
			||||||
    /*background: linear-gradient(0deg, #ffffff0f, transparent);*/
 | 
					 | 
				
			||||||
    border-radius: 10px;
 | 
					 | 
				
			||||||
    .icon {
 | 
					    .icon {
 | 
				
			||||||
      width: 6vh;
 | 
					      width: 6vh;
 | 
				
			||||||
      background-size: cover;
 | 
					      background-size: cover;
 | 
				
			||||||
      background-position: 0 -5px;
 | 
					      background-position: 0 0;
 | 
				
			||||||
      flex-grow: 0;
 | 
					      flex-grow: 0;
 | 
				
			||||||
      flex-shrink: 0;
 | 
					      flex-shrink: 0;
 | 
				
			||||||
      margin: 0 10px;
 | 
					 | 
				
			||||||
      text-align: center;
 | 
					      text-align: center;
 | 
				
			||||||
      border-left: 1px solid #ffffff1f;
 | 
					      margin: 0 2px;
 | 
				
			||||||
      border-right: 1px solid #ffffff1f;
 | 
					 | 
				
			||||||
      &:after {
 | 
					      &:after {
 | 
				
			||||||
        content: " ";
 | 
					        content: " ";
 | 
				
			||||||
        display: block;
 | 
					        display: block;
 | 
				
			||||||
| 
						 | 
					@ -261,19 +254,18 @@ ul {
 | 
				
			||||||
      flex-grow: 0;
 | 
					      flex-grow: 0;
 | 
				
			||||||
      flex-shrink: 0;
 | 
					      flex-shrink: 0;
 | 
				
			||||||
      width: 15%;
 | 
					      width: 15%;
 | 
				
			||||||
      font-weight: bold;
 | 
					 | 
				
			||||||
      text-align: right;
 | 
					      text-align: right;
 | 
				
			||||||
      font-family: "Papyrus", sans-serif;
 | 
					 | 
				
			||||||
      font-size: 110%;
 | 
					      font-size: 110%;
 | 
				
			||||||
    }
 | 
					      padding: 5px;
 | 
				
			||||||
    .player {
 | 
					      border-left: 1px solid rgba(255, 255, 255, 0.4);
 | 
				
			||||||
      flex-grow: 0;
 | 
					      border-right: 1px solid rgba(255, 255, 255, 0.4);
 | 
				
			||||||
      flex-shrink: 1;
 | 
					      small {
 | 
				
			||||||
      text-align: right;
 | 
					        color: #888;
 | 
				
			||||||
      margin: 0 10px;
 | 
					        margin-right: 5px;
 | 
				
			||||||
    }
 | 
					        &.dead {
 | 
				
			||||||
    .ability {
 | 
					          text-decoration: line-through;
 | 
				
			||||||
      flex-grow: 1;
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  &.legend {
 | 
					  &.legend {
 | 
				
			||||||
| 
						 | 
					@ -307,28 +299,23 @@ ul {
 | 
				
			||||||
  .headline {
 | 
					  .headline {
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
    font-weight: bold;
 | 
					    font-weight: bold;
 | 
				
			||||||
    border-bottom: 1px solid white;
 | 
					    border-bottom: 1px solid rgba(255, 255, 255, 0.4);
 | 
				
			||||||
    padding: 5px 10px;
 | 
					    padding: 5px 10px;
 | 
				
			||||||
    border-radius: 0;
 | 
					    border-radius: 0;
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  .icon {
 | 
					 | 
				
			||||||
    border-color: white;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  .name {
 | 
					  .name {
 | 
				
			||||||
    flex-grow: 1;
 | 
					    flex-grow: 1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  .first {
 | 
					  .first {
 | 
				
			||||||
    .icon {
 | 
					    .name {
 | 
				
			||||||
      border-right: 0;
 | 
					      border-left: 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  .other {
 | 
					  .other {
 | 
				
			||||||
    li .name {
 | 
					    li .name {
 | 
				
			||||||
      text-align: left;
 | 
					      text-align: left;
 | 
				
			||||||
    }
 | 
					      border-right: 0;
 | 
				
			||||||
    .icon {
 | 
					 | 
				
			||||||
      border-left: 0;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,11 +56,6 @@ export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
    Modal
 | 
					    Modal
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  data: function() {
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
      roleSelection: {}
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
    rolesGrouped: function() {
 | 
					    rolesGrouped: function() {
 | 
				
			||||||
      const rolesGrouped = {};
 | 
					      const rolesGrouped = {};
 | 
				
			||||||
| 
						 | 
					@ -136,7 +131,6 @@ h4 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.townsfolk {
 | 
					.townsfolk {
 | 
				
			||||||
  .name,
 | 
					  .name,
 | 
				
			||||||
  .player,
 | 
					 | 
				
			||||||
  h4 {
 | 
					  h4 {
 | 
				
			||||||
    color: $townsfolk;
 | 
					    color: $townsfolk;
 | 
				
			||||||
    &:before,
 | 
					    &:before,
 | 
				
			||||||
| 
						 | 
					@ -147,7 +141,6 @@ h4 {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.outsider {
 | 
					.outsider {
 | 
				
			||||||
  .name,
 | 
					  .name,
 | 
				
			||||||
  .player,
 | 
					 | 
				
			||||||
  h4 {
 | 
					  h4 {
 | 
				
			||||||
    color: $outsider;
 | 
					    color: $outsider;
 | 
				
			||||||
    &:before,
 | 
					    &:before,
 | 
				
			||||||
| 
						 | 
					@ -158,7 +151,6 @@ h4 {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.minion {
 | 
					.minion {
 | 
				
			||||||
  .name,
 | 
					  .name,
 | 
				
			||||||
  .player,
 | 
					 | 
				
			||||||
  h4 {
 | 
					  h4 {
 | 
				
			||||||
    color: $minion;
 | 
					    color: $minion;
 | 
				
			||||||
    &:before,
 | 
					    &:before,
 | 
				
			||||||
| 
						 | 
					@ -169,7 +161,6 @@ h4 {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
.demon {
 | 
					.demon {
 | 
				
			||||||
  .name,
 | 
					  .name,
 | 
				
			||||||
  .player,
 | 
					 | 
				
			||||||
  h4 {
 | 
					  h4 {
 | 
				
			||||||
    color: $demon;
 | 
					    color: $demon;
 | 
				
			||||||
    &:before,
 | 
					    &:before,
 | 
				
			||||||
| 
						 | 
					@ -208,7 +199,6 @@ ul {
 | 
				
			||||||
      width: 15%;
 | 
					      width: 15%;
 | 
				
			||||||
      font-weight: bold;
 | 
					      font-weight: bold;
 | 
				
			||||||
      text-align: right;
 | 
					      text-align: right;
 | 
				
			||||||
      font-family: "Papyrus", sans-serif;
 | 
					 | 
				
			||||||
      font-size: 110%;
 | 
					      font-size: 110%;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    .player {
 | 
					    .player {
 | 
				
			||||||
| 
						 | 
					@ -216,6 +206,8 @@ ul {
 | 
				
			||||||
      flex-shrink: 1;
 | 
					      flex-shrink: 1;
 | 
				
			||||||
      text-align: right;
 | 
					      text-align: right;
 | 
				
			||||||
      margin: 0 10px;
 | 
					      margin: 0 10px;
 | 
				
			||||||
 | 
					      color: #888;
 | 
				
			||||||
 | 
					      font-size: smaller;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    .ability {
 | 
					    .ability {
 | 
				
			||||||
      flex-grow: 1;
 | 
					      flex-grow: 1;
 | 
				
			||||||
| 
						 | 
					@ -230,6 +222,7 @@ ul {
 | 
				
			||||||
      height: auto;
 | 
					      height: auto;
 | 
				
			||||||
      font-family: inherit;
 | 
					      font-family: inherit;
 | 
				
			||||||
      font-size: inherit;
 | 
					      font-size: inherit;
 | 
				
			||||||
 | 
					      color: #fff;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    .icon:after {
 | 
					    .icon:after {
 | 
				
			||||||
      padding-top: 0;
 | 
					      padding-top: 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,21 @@ export default {
 | 
				
			||||||
          }))
 | 
					          }))
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // add out of script traveler reminders
 | 
				
			||||||
 | 
					      this.$store.state.otherTravelers.forEach(role => {
 | 
				
			||||||
 | 
					        if (players.some(p => p.role.id === role.id)) {
 | 
				
			||||||
 | 
					          reminders = [
 | 
				
			||||||
 | 
					            ...reminders,
 | 
				
			||||||
 | 
					            ...role.reminders.map(name => ({
 | 
				
			||||||
 | 
					              role: role.id,
 | 
				
			||||||
 | 
					              image: role.image,
 | 
				
			||||||
 | 
					              name
 | 
				
			||||||
 | 
					            }))
 | 
				
			||||||
 | 
					          ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      reminders.push({ role: "good", name: "Good" });
 | 
					      reminders.push({ role: "good", name: "Good" });
 | 
				
			||||||
      reminders.push({ role: "evil", name: "Evil" });
 | 
					      reminders.push({ role: "evil", name: "Evil" });
 | 
				
			||||||
      reminders.push({ role: "custom", name: "Custom note" });
 | 
					      reminders.push({ role: "custom", name: "Custom note" });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,5 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <Modal
 | 
					  <Modal v-if="modals.role && availableRoles.length" @close="close">
 | 
				
			||||||
    v-if="modals.role && availableRoles.length"
 | 
					 | 
				
			||||||
    @close="toggleModal('role')"
 | 
					 | 
				
			||||||
  >
 | 
					 | 
				
			||||||
    <h3>
 | 
					    <h3>
 | 
				
			||||||
      Choose a new character for
 | 
					      Choose a new character for
 | 
				
			||||||
      {{
 | 
					      {{
 | 
				
			||||||
| 
						 | 
					@ -11,7 +8,7 @@
 | 
				
			||||||
          : "bluffing"
 | 
					          : "bluffing"
 | 
				
			||||||
      }}
 | 
					      }}
 | 
				
			||||||
    </h3>
 | 
					    </h3>
 | 
				
			||||||
    <ul class="tokens">
 | 
					    <ul class="tokens" v-if="tab === 'editionRoles' || !otherTravelers.size">
 | 
				
			||||||
      <li
 | 
					      <li
 | 
				
			||||||
        v-for="role in availableRoles"
 | 
					        v-for="role in availableRoles"
 | 
				
			||||||
        :class="[role.team]"
 | 
					        :class="[role.team]"
 | 
				
			||||||
| 
						 | 
					@ -21,6 +18,33 @@
 | 
				
			||||||
        <Token :role="role" />
 | 
					        <Token :role="role" />
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
 | 
					    <ul class="tokens" v-if="tab === 'otherTravelers' && otherTravelers.size">
 | 
				
			||||||
 | 
					      <li
 | 
				
			||||||
 | 
					        v-for="role in otherTravelers.values()"
 | 
				
			||||||
 | 
					        :class="[role.team]"
 | 
				
			||||||
 | 
					        :key="role.id"
 | 
				
			||||||
 | 
					        @click="setRole(role)"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <Token :role="role" />
 | 
				
			||||||
 | 
					      </li>
 | 
				
			||||||
 | 
					    </ul>
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      class="button-group"
 | 
				
			||||||
 | 
					      v-if="playerIndex >= 0 && otherTravelers.size && !session.isSpectator"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        class="button"
 | 
				
			||||||
 | 
					        :class="{ townsfolk: tab === 'editionRoles' }"
 | 
				
			||||||
 | 
					        @click="tab = 'editionRoles'"
 | 
				
			||||||
 | 
					        >Edtition Roles</span
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        class="button"
 | 
				
			||||||
 | 
					        :class="{ townsfolk: tab === 'otherTravelers' }"
 | 
				
			||||||
 | 
					        @click="tab = 'otherTravelers'"
 | 
				
			||||||
 | 
					        >Other Travelers</span
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
  </Modal>
 | 
					  </Modal>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +74,13 @@ export default {
 | 
				
			||||||
      return availableRoles;
 | 
					      return availableRoles;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    ...mapState(["modals", "roles", "session"]),
 | 
					    ...mapState(["modals", "roles", "session"]),
 | 
				
			||||||
    ...mapState("players", ["players"])
 | 
					    ...mapState("players", ["players"]),
 | 
				
			||||||
 | 
					    ...mapState(["otherTravelers"])
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      tab: "editionRoles"
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    setRole(role) {
 | 
					    setRole(role) {
 | 
				
			||||||
| 
						 | 
					@ -72,6 +102,10 @@ export default {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.$store.commit("toggleModal", "role");
 | 
					      this.$store.commit("toggleModal", "role");
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    close() {
 | 
				
			||||||
 | 
					      this.tab = "editionRoles";
 | 
				
			||||||
 | 
					      this.toggleModal("role");
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    ...mapMutations(["toggleModal"])
 | 
					    ...mapMutations(["toggleModal"])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,22 +15,50 @@
 | 
				
			||||||
    <table>
 | 
					    <table>
 | 
				
			||||||
      <thead>
 | 
					      <thead>
 | 
				
			||||||
        <tr>
 | 
					        <tr>
 | 
				
			||||||
 | 
					          <td>Time</td>
 | 
				
			||||||
          <td>Nominator</td>
 | 
					          <td>Nominator</td>
 | 
				
			||||||
          <td>Nominee</td>
 | 
					          <td>Nominee</td>
 | 
				
			||||||
          <td>Type</td>
 | 
					          <td>Type</td>
 | 
				
			||||||
 | 
					          <td>Votes</td>
 | 
				
			||||||
          <td>Majority</td>
 | 
					          <td>Majority</td>
 | 
				
			||||||
          <td><font-awesome-icon icon="hand-paper" /> Hand up</td>
 | 
					          <td>
 | 
				
			||||||
 | 
					            <font-awesome-icon icon="user-friends" />
 | 
				
			||||||
 | 
					            Voters
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
      </thead>
 | 
					      </thead>
 | 
				
			||||||
      <tbody>
 | 
					      <tbody>
 | 
				
			||||||
        <tr v-for="(vote, index) in session.voteHistory" :key="index">
 | 
					        <tr v-for="(vote, index) in session.voteHistory" :key="index">
 | 
				
			||||||
 | 
					          <td>
 | 
				
			||||||
 | 
					            {{
 | 
				
			||||||
 | 
					              vote.timestamp
 | 
				
			||||||
 | 
					                .getHours()
 | 
				
			||||||
 | 
					                .toString()
 | 
				
			||||||
 | 
					                .padStart(2, "0")
 | 
				
			||||||
 | 
					            }}:{{
 | 
				
			||||||
 | 
					              vote.timestamp
 | 
				
			||||||
 | 
					                .getMinutes()
 | 
				
			||||||
 | 
					                .toString()
 | 
				
			||||||
 | 
					                .padStart(2, "0")
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
          <td>{{ vote.nominator }}</td>
 | 
					          <td>{{ vote.nominator }}</td>
 | 
				
			||||||
          <td>{{ vote.nominee }}</td>
 | 
					          <td>{{ vote.nominee }}</td>
 | 
				
			||||||
          <td>{{ vote.type }}</td>
 | 
					          <td>{{ vote.type }}</td>
 | 
				
			||||||
          <td>{{ vote.majority }}</td>
 | 
					 | 
				
			||||||
          <td>
 | 
					          <td>
 | 
				
			||||||
            {{ vote.votes.length }}
 | 
					            {{ vote.votes.length }}
 | 
				
			||||||
            <font-awesome-icon icon="user-friends" />
 | 
					            <font-awesome-icon icon="hand-paper" />
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td>
 | 
				
			||||||
 | 
					            {{ vote.majority }}
 | 
				
			||||||
 | 
					            <font-awesome-icon
 | 
				
			||||||
 | 
					              :icon="[
 | 
				
			||||||
 | 
					                'fas',
 | 
				
			||||||
 | 
					                vote.votes.length >= vote.majority ? 'check-square' : 'square'
 | 
				
			||||||
 | 
					              ]"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </td>
 | 
				
			||||||
 | 
					          <td>
 | 
				
			||||||
            {{ vote.votes.join(", ") }}
 | 
					            {{ vote.votes.join(", ") }}
 | 
				
			||||||
          </td>
 | 
					          </td>
 | 
				
			||||||
        </tr>
 | 
					        </tr>
 | 
				
			||||||
| 
						 | 
					@ -89,13 +117,16 @@ thead td {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tbody {
 | 
					tbody {
 | 
				
			||||||
  td:nth-child(1) {
 | 
					  td:nth-child(2) {
 | 
				
			||||||
    color: $townsfolk;
 | 
					    color: $townsfolk;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  td:nth-child(2) {
 | 
					  td:nth-child(3) {
 | 
				
			||||||
    color: $demon;
 | 
					    color: $demon;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  td:nth-child(4) {
 | 
					  td:nth-child(5) {
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  td:nth-child(6) {
 | 
				
			||||||
    text-align: center;
 | 
					    text-align: center;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,6 @@ const faIcons = [
 | 
				
			||||||
  "SearchMinus",
 | 
					  "SearchMinus",
 | 
				
			||||||
  "SearchPlus",
 | 
					  "SearchPlus",
 | 
				
			||||||
  "Square",
 | 
					  "Square",
 | 
				
			||||||
  "Sun",
 | 
					 | 
				
			||||||
  "TheaterMasks",
 | 
					  "TheaterMasks",
 | 
				
			||||||
  "Times",
 | 
					  "Times",
 | 
				
			||||||
  "TimesCircle",
 | 
					  "TimesCircle",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,12 @@
 | 
				
			||||||
  .player > .name {
 | 
					  .player > .name {
 | 
				
			||||||
    top: 0;
 | 
					    top: 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  .player > .menu {
 | 
				
			||||||
 | 
					    bottom: 0;
 | 
				
			||||||
 | 
					    &:before {
 | 
				
			||||||
 | 
					      bottom: 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Old phones
 | 
					// Old phones
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1578
									
								
								src/roles.json
									
										
									
									
									
								
							
							
						
						
									
										1578
									
								
								src/roles.json
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -25,6 +25,19 @@ const getRolesByEdition = (edition = editionJSON[0]) => {
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getTravelersNotInEdition = (edition = editionJSON[0]) => {
 | 
				
			||||||
 | 
					  return new Map(
 | 
				
			||||||
 | 
					    rolesJSON
 | 
				
			||||||
 | 
					      .filter(
 | 
				
			||||||
 | 
					        r =>
 | 
				
			||||||
 | 
					          r.team === "traveler" &&
 | 
				
			||||||
 | 
					          r.edition !== edition.id &&
 | 
				
			||||||
 | 
					          !edition.roles.includes(r.id)
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      .map(role => [role.id, role])
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// base definition for custom roles
 | 
					// base definition for custom roles
 | 
				
			||||||
const imageBase =
 | 
					const imageBase =
 | 
				
			||||||
  "https://raw.githubusercontent.com/bra1n/townsquare/main/src/assets/icons/";
 | 
					  "https://raw.githubusercontent.com/bra1n/townsquare/main/src/assets/icons/";
 | 
				
			||||||
| 
						 | 
					@ -70,6 +83,7 @@ export default new Vuex.Store({
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    edition: editionJSONbyId.get("tb"),
 | 
					    edition: editionJSONbyId.get("tb"),
 | 
				
			||||||
    roles: getRolesByEdition(),
 | 
					    roles: getRolesByEdition(),
 | 
				
			||||||
 | 
					    otherTravelers: getTravelersNotInEdition(),
 | 
				
			||||||
    fabled
 | 
					    fabled
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  getters: {
 | 
					  getters: {
 | 
				
			||||||
| 
						 | 
					@ -180,11 +194,18 @@ export default new Vuex.Store({
 | 
				
			||||||
          // convert to Map
 | 
					          // convert to Map
 | 
				
			||||||
          .map(role => [role.id, role])
 | 
					          .map(role => [role.id, role])
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					      // update extraTravelers map to only show travelers not in this script
 | 
				
			||||||
 | 
					      state.otherTravelers = new Map(
 | 
				
			||||||
 | 
					        rolesJSON
 | 
				
			||||||
 | 
					          .filter(r => r.team === "traveler" && !roles.some(i => i.id === r.id))
 | 
				
			||||||
 | 
					          .map(role => [role.id, role])
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    setEdition(state, edition) {
 | 
					    setEdition(state, edition) {
 | 
				
			||||||
      if (editionJSONbyId.has(edition.id)) {
 | 
					      if (editionJSONbyId.has(edition.id)) {
 | 
				
			||||||
        state.edition = editionJSONbyId.get(edition.id);
 | 
					        state.edition = editionJSONbyId.get(edition.id);
 | 
				
			||||||
        state.roles = getRolesByEdition(state.edition);
 | 
					        state.roles = getRolesByEdition(state.edition);
 | 
				
			||||||
 | 
					        state.otherTravelers = getTravelersNotInEdition(state.edition);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        state.edition = edition;
 | 
					        state.edition = edition;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,6 +84,7 @@ const actions = {
 | 
				
			||||||
        name,
 | 
					        name,
 | 
				
			||||||
        id
 | 
					        id
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
 | 
					      commit("setFabled", { fabled: [] });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    commit("set", players);
 | 
					    commit("set", players);
 | 
				
			||||||
    commit("setBluff");
 | 
					    commit("setBluff");
 | 
				
			||||||
| 
						 | 
					@ -94,6 +95,7 @@ const mutations = {
 | 
				
			||||||
  clear(state) {
 | 
					  clear(state) {
 | 
				
			||||||
    state.players = [];
 | 
					    state.players = [];
 | 
				
			||||||
    state.bluffs = [];
 | 
					    state.bluffs = [];
 | 
				
			||||||
 | 
					    state.fabled = [];
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  set(state, players = []) {
 | 
					  set(state, players = []) {
 | 
				
			||||||
    state.players = players;
 | 
					    state.players = players;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,8 +72,8 @@ const mutations = {
 | 
				
			||||||
  addHistory(state, players) {
 | 
					  addHistory(state, players) {
 | 
				
			||||||
    if (!state.nomination || state.lockedVote <= players.length) return;
 | 
					    if (!state.nomination || state.lockedVote <= players.length) return;
 | 
				
			||||||
    const isBanishment = players[state.nomination[1]].role.team === "traveler";
 | 
					    const isBanishment = players[state.nomination[1]].role.team === "traveler";
 | 
				
			||||||
    console.log(isBanishment);
 | 
					 | 
				
			||||||
    state.voteHistory.push({
 | 
					    state.voteHistory.push({
 | 
				
			||||||
 | 
					      timestamp: new Date(),
 | 
				
			||||||
      nominator: players[state.nomination[0]].name,
 | 
					      nominator: players[state.nomination[0]].name,
 | 
				
			||||||
      nominee: players[state.nomination[1]].name,
 | 
					      nominee: players[state.nomination[1]].name,
 | 
				
			||||||
      type: isBanishment ? "Banishment" : "Execution",
 | 
					      type: isBanishment ? "Banishment" : "Execution",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -443,8 +443,11 @@ class LiveSession {
 | 
				
			||||||
          value: {}
 | 
					          value: {}
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // load role
 | 
					        // load role, first from session, the global, then fail gracefully
 | 
				
			||||||
        const role = this._store.state.roles.get(value);
 | 
					        const role =
 | 
				
			||||||
 | 
					          this._store.state.roles.get(value) ||
 | 
				
			||||||
 | 
					          this._store.getters.rolesJSONbyId.get(value) ||
 | 
				
			||||||
 | 
					          {};
 | 
				
			||||||
        this._store.commit("players/update", {
 | 
					        this._store.commit("players/update", {
 | 
				
			||||||
          player,
 | 
					          player,
 | 
				
			||||||
          property: "role",
 | 
					          property: "role",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue