0
0
Fork 0

Merge pull request #78 from Oliver-Akins/dev

v1.2.6
This commit is contained in:
Oliver 2021-03-01 16:46:03 -07:00 committed by GitHub
commit de2f02b321
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 296 additions and 104 deletions

View file

@ -122,14 +122,24 @@ export default (io: Server, socket: Socket, data: JoinGame) => {
let rooms: string[] = [game.id]; let rooms: string[] = [game.id];
game.log.info(`Player Reconnected to the game (name=${data.name})`); game.log.info(`Player Reconnected to the game (name=${data.name})`);
// Get the hand of the player's team
let hand: string[] = []; let hand: string[] = [];
if (sameName.team && sameName.role == `guesser`) {
hand = game.teams[sameName.team - 1].hand; // Ensure that the user has a role before connecting them to
// the websocket rooms
if (sameName.role && sameName.team) {
rooms.push( rooms.push(
`${game.id}:*:${sameName.role}`, `${game.id}:*:${sameName.role}`,
`${game.id}:${sameName.team}:${sameName.role}` `${game.id}:${sameName.team}:${sameName.role}`
); );
switch (sameName.role) {
case "guesser":
hand = game.teams[sameName.team - 1].hand;
break;
case "writer":
hand = game.teams[sameName.team - 1].spiritHand;
break;
};
}; };
socket.join(rooms); socket.join(rooms);

View file

@ -0,0 +1,63 @@
import { games, log } from '../main';
import { Server, Socket } from 'socket.io';
export default (io: Server, socket: Socket, data: RandomizeTeams) => {
try {
// Assert game exists
if (!games[data.game_code]) {
log.debug(`Can't find game with code: ${data.game_code}`);
socket.emit(`RandomizedTeams`, {
status: 404,
message: `Game with code ${data.game_code} could not be found`,
source: `RandomizeTeams`
});
return;
};
let game = games[data.game_code];
let players = [...game.players];
// game.log.info(players);
let new_team: 1|2 = 1;
while (players.length > 0) {
let player_index = Math.floor(Math.random() * players.length);
let player = players[player_index];
players.splice(player_index, 1);
game.log.debug(`Randomized ${player.name} onto team ${new_team}`);
// Move the socket rooms that the player's socket is in
player.socket?.leave(`${game.id}:*:${player.role}`);
player.socket?.leave(`${game.id}:${player.team}:${player.role}`);
player.socket?.join([
`${game.id}:*:guesser`,
`${game.id}:${new_team}:guesser`
]);
// Update the player's object
player.role = `guesser`;
player.team = new_team;
// Add the next player to the other team
new_team = new_team == 1 ? 2 : 1;
// Alert all connected clients that they need to update the UI
io.to(game.id).emit(`PlayerUpdate`, {
status: 200,
action: `modify`,
name: player.name,
role: player.role,
team: player.team,
});
};
}
catch (err) {
log.prettyError(err);
socket.emit(`RandomizedTeams`, {
status: 500,
message: `${err.name}: ${err.message}`,
source: `RandomizeTeams`,
});
};
};

View file

@ -50,7 +50,7 @@ export default (io: Server, socket: Socket, data: SendCard) => {
game.log.debug(`Guesser is sending a card to the writer`); game.log.debug(`Guesser is sending a card to the writer`);
// Update the team's hand // Update the team's hand
team.removeCard(data.text); team.askSpirit(data.text);
// send the question text to the writer player // send the question text to the writer player
io.to(`${game.id}:${team.id}:writer`).emit(`UpdateHand`, { io.to(`${game.id}:${team.id}:writer`).emit(`UpdateHand`, {

View file

@ -38,11 +38,15 @@ export default (io: Server, socket: Socket, data: UpdatePlayer) => {
message: `${err.name}: ${err.message}`, message: `${err.name}: ${err.message}`,
source: `UpdatePlayer`, source: `UpdatePlayer`,
}); });
} };
}; };
const modifyPlayer = (io: Server, socket: Socket, data: UpdatePlayer): void => { export const modifyPlayer = (
io: Server,
socket: Socket,
data: UpdatePlayer
): void => {
let game = games[data.game_code]; let game = games[data.game_code];
let player = game.players.find(x => x.name === data.name); let player = game.players.find(x => x.name === data.name);

View file

@ -14,14 +14,14 @@ export class Deck<T> {
get size(): number { return this._deck.length; } get size(): number { return this._deck.length; }
/**
* Draws X cards from the deck
*
* @param quantity The number of cards to draw
* @throws Error If quantity is <= 0
* @throws Error If quantity > size
*/
public draw(quantity: number): T[] { public draw(quantity: number): T[] {
/**
* Draws X cards from the deck
*
* @param quantity -> The number of cards to draw
* @throws Error -> If quantity is <= 0
* @throws Error -> If quantity > size
*/
if (quantity <= 0) { if (quantity <= 0) {
throw new Error(`Cannot get ${quantity} cards.`); throw new Error(`Cannot get ${quantity} cards.`);
} else if (quantity > this.size) { } else if (quantity > this.size) {
@ -47,12 +47,12 @@ export class Deck<T> {
}; };
/**
* Adds the specific card to the discard pile
*
* @param card The card to add to the discard pile
*/
public discard(card: T) { public discard(card: T) {
/**
* Adds the specific card to the discard pile
*
* @param card -> The card to add to the discard pile
*/
this._unknown = this._unknown.filter(x => x != card); this._unknown = this._unknown.filter(x => x != card);
this._discard.push(card); this._discard.push(card);
}; };
@ -65,10 +65,10 @@ export class Deck<T> {
}; };
/**
* Converts this Deck into a JSON-compatible object
*/
public toJSON(): datastoreDeck<T> { public toJSON(): datastoreDeck<T> {
/**
* Converts this Deck into a JSON-compatible object
*/
return { return {
deck: this._deck, deck: this._deck,
unknown: this._unknown, unknown: this._unknown,
@ -76,10 +76,10 @@ export class Deck<T> {
}; };
}; };
/**
* Converts the JSON representation of a deck into a Deck
*/
public static fromJSON<A>(data: datastoreDeck<A>): Deck<A> { public static fromJSON<A>(data: datastoreDeck<A>): Deck<A> {
/**
* Converts the JSON representation of a deck into a Deck
*/
let d = new Deck(data.deck); let d = new Deck(data.deck);
d._discard = data.discard; d._discard = data.discard;
d._unknown = data.unknown; d._unknown = data.unknown;

View file

@ -46,10 +46,10 @@ export class Game {
get questions() { return this._questions; }; get questions() { return this._questions; };
/**
* Return the objects that the spirits can choose from for the game.
*/
get objects() { get objects() {
/**
* Return the objects that the spirits can choose from for the game.
*/
if (!this._objectCard) { if (!this._objectCard) {
this._objectCard = this._objects.draw(1)[0]; this._objectCard = this._objects.draw(1)[0];
}; };
@ -69,11 +69,11 @@ export class Game {
} }
/**
* Parses out the CSV files and creates the decks for the game to run
* on.
*/
private parseDeckCSV() { private parseDeckCSV() {
/**
* Parses out the CSV files and creates the decks for the game to run
* on.
*/
// parse the questions from the CSV // parse the questions from the CSV
readFile(conf.game.cards.questions.fingerprint, `utf-8`, (err, filebuffer) => { readFile(conf.game.cards.questions.fingerprint, `utf-8`, (err, filebuffer) => {
@ -102,11 +102,11 @@ export class Game {
}); });
}; };
/**
* Fetches and parses the CSV data from Google Sheets instead of local
* CSV files.
*/
private parseDeckGoogleSheets() { private parseDeckGoogleSheets() {
/**
* Fetches and parses the CSV data from Google Sheets instead of local
* CSV files.
*/
let key = conf.game.cards.key as string; let key = conf.game.cards.key as string;
let questions_id = conf.game.cards.questions.fingerprint; let questions_id = conf.game.cards.questions.fingerprint;
let objects_id = conf.game.cards.objects.fingerprint; let objects_id = conf.game.cards.objects.fingerprint;
@ -160,10 +160,10 @@ export class Game {
}; };
/**
* Resets the objects card, for restarting the game
*/
public resetObject() { public resetObject() {
/**
* Resets the objects card, for restarting the game
*/
if (this._objectCard) { if (this._objectCard) {
this._objects.discard(this._objectCard); this._objects.discard(this._objectCard);
this._objectCard = null; this._objectCard = null;
@ -172,10 +172,10 @@ export class Game {
}; };
/**
* Returns a JSON representation of the game.
*/
public toJSON(): datastoreGame { public toJSON(): datastoreGame {
/**
* Returns a JSON representation of the game.
*/
return { return {
players: this.players.map(p => p.toJSON()), players: this.players.map(p => p.toJSON()),
teams: this.teams.map(t => t.toJSON()), teams: this.teams.map(t => t.toJSON()),
@ -190,10 +190,10 @@ export class Game {
}; };
}; };
/**
* Converts a JSON representation into a Game object
*/
public static fromJSON(host: Player, data: datastoreGame): Game { public static fromJSON(host: Player, data: datastoreGame): Game {
/**
* Converts a JSON representation into a Game object
*/
let game = new this(host, { id: data.id }); let game = new this(host, { id: data.id });
// Re-create the deck objects // Re-create the deck objects
@ -217,12 +217,12 @@ export class Game {
}; };
/**
* Generates a game code with the given length
*
* @param length The length of the code we want to generate
*/
public static generateID(length: number): string { public static generateID(length: number): string {
/**
* Generates a game code with the given length
*
* @param length -> The length of the code we want to generate
*/
let code: string; let code: string;
// Generate a code until we don't have a collision // Generate a code until we don't have a collision

View file

@ -13,6 +13,9 @@ export class Player {
this.isHost = isHost; this.isHost = isHost;
}; };
/**
* Converts the Player into a JSON-compatible representation of the player
*/
public toJSON(): datastorePlayer { public toJSON(): datastorePlayer {
return { return {
name: this.name, name: this.name,
@ -22,6 +25,11 @@ export class Player {
}; };
}; };
/**
* Converts JSON-compatible player data into a Player object.
*
* @param data The player data to convert
*/
public static fromJSON(data: datastorePlayer): Player { public static fromJSON(data: datastorePlayer): Player {
let player = new this(data.name, null, data.host); let player = new this(data.name, null, data.host);
player.role = data.role; player.role = data.role;

View file

@ -4,7 +4,7 @@ export class Team {
readonly id: team; readonly id: team;
public guessers: Player[]; public guessers: Player[];
public writer: Player | null; public writer: Player | null;
private _hand: string[]; private _hand: team_hands;
private _questions: string[]; private _questions: string[];
private _answers: string[]; private _answers: string[];
@ -12,68 +12,81 @@ export class Team {
this.id = id; this.id = id;
this._answers = new Array<string>(8).fill(``); this._answers = new Array<string>(8).fill(``);
this._questions = []; this._questions = [];
this._hand = []; this._hand = {
guesser: [],
writer: []
};
this.guessers = []; this.guessers = [];
}; };
/* /* The getters for the various class properties */
* The getters for the various class properties get hand(): string[] { return this._hand.guesser; };
*/ get spiritHand(): string[] { return this._hand.writer };
get hand(): string[] { return this._hand; };
get answers(): string[] { return this._answers; }; get answers(): string[] { return this._answers; };
get questions(): string[] { return this._questions; }; get questions(): string[] { return this._questions; };
/**
* Adds the question(s) to the medium's hand
*
* @param questions The array of question text to add the medium's hand.
*/
public addCardsToHand(questions: string[]): void { public addCardsToHand(questions: string[]): void {
/** this._hand.guesser.push(...questions);
* Adds the question(s) to the medium's hand
*
* @param questions -> The array of question text to add the medium's
* hand.
*/
this._hand.push(...questions);
}; };
/**
* Resets all the per-game data related to this team
*/
public reset(): void { public reset(): void {
/** this._hand.guesser = [];
* Resets all the per-game data related to this team
*/
this._hand = [];
this._questions = []; this._questions = [];
this._answers = new Array<string>(8).fill(``); this._answers = new Array<string>(8).fill(``);
} }
/**
* Removes a card from the medium's hand
*
* @param question The card to remove
*/
public removeCard(question: string) { public removeCard(question: string) {
/** this._hand.guesser = this._hand.guesser.filter(x => x != question);
* Removes the given question from the medium's hand
*
* @param question -> The card text to remove from the hand.
*/
this._hand = this._hand.filter(x => x != question);
}; };
/**
* Asks the spirit a question, removing it from the medium's hands
*
* @param question The question that is being asked
*/
public askSpirit(question: string) {
this._hand.writer.push(question);
this.removeCard(question);
};
/**
* Adds the given question to the history of the questions.
*
* @param question The question the spirit is answering
*/
public selectQuestion(question: string) { public selectQuestion(question: string) {
/**
* Adds the given question to the history of the questions.
*
* @param question -> The question the spirit is answering
*/
this._questions.push(question); this._questions.push(question);
this._hand.writer = [];
}; };
/**
* Takes the value of an answer and modifies in the storage.
*
* @param answerIndex The value of the answer between 1 and 8 (inclusive)
* @param answer The new answer for that index
* @throws Error If the answerIndex is not in range
*/
public modifyAnswer(answerIndex: answer, answer: string) { public modifyAnswer(answerIndex: answer, answer: string) {
/**
* Takes the value of an answer and modifies in the storage.
*
* @param answerIndex -> The value of the answer between 1 and 8 (inclusive)
* @param answer -> The new answer for that index
* @throws Error -> If the answerIndex is not in range
*/
if (answerIndex > this._answers.length || answerIndex <= 0) { if (answerIndex > this._answers.length || answerIndex <= 0) {
throw new Error(`Cannot set answer at index ${answerIndex}.`) throw new Error(`Cannot set answer at index ${answerIndex}.`)
}; };
@ -81,26 +94,26 @@ export class Team {
}; };
/**
* Converts the given object into a JSON representation of the data
*/
public toJSON(): datastoreTeam { public toJSON(): datastoreTeam {
/**
* Converts the given object into a JSON representation of the data
*/
return { return {
questions: this._questions, questions: this._questions,
answers: this._answers, answers: this._answers,
hand: this._hand, hands: this._hand,
id: this.id, id: this.id,
}; };
}; };
/**
* Converts a team JSON object back into a Team object.
*/
public static fromJSON(data: datastoreTeam): Team { public static fromJSON(data: datastoreTeam): Team {
/**
* Converts a team JSON object back into a Team object.
*/
let t = new Team(data.id); let t = new Team(data.id);
t._questions = data.questions; t._questions = data.questions;
t._answers = data.answers; t._answers = data.answers;
t._hand = data.hand; t._hand = data.hands;
return t; return t;
}; };
}; };

View file

@ -72,6 +72,14 @@ interface GameRejoined extends response {
} }
interface RandomizeTeam {
game_code: string;
}
interface TeamsRandomized extends response {
players: player[];
}
interface GetHand { interface GetHand {
game_code: string; game_code: string;
team: team; team: team;

View file

@ -10,7 +10,7 @@ type datastoreObjectCard = string[];
interface datastoreTeam { interface datastoreTeam {
questions: datastoreQuestionCard[]; questions: datastoreQuestionCard[];
hand: datastoreQuestionCard[]; hands: team_hands;
answers: string[]; answers: string[];
id: team; id: team;
} }

4
server/src/types/team_hands.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
interface team_hands {
guesser: question_deck[];
writer: question_deck[];
}

View file

@ -15,6 +15,7 @@ import UpdatePlayer from "./events/UpdatePlayer";
import SelectObject from "./events/SelectObject"; import SelectObject from "./events/SelectObject";
import UpdateAnswer from "./events/UpdateAnswer"; import UpdateAnswer from "./events/UpdateAnswer";
import GetPastQuestions from "./events/GetPastQuestions"; import GetPastQuestions from "./events/GetPastQuestions";
import RandomizeTeams from "./events/RandomizeTeams";
export default async (conf: config) => { export default async (conf: config) => {
@ -42,6 +43,7 @@ export default async (conf: config) => {
socket.on(`JoinGame`, (data: JoinGame) => JoinGame(io, socket, data)); socket.on(`JoinGame`, (data: JoinGame) => JoinGame(io, socket, data));
socket.on(`UpdatePlayer`, (data: UpdatePlayer) => UpdatePlayer(io, socket, data)); socket.on(`UpdatePlayer`, (data: UpdatePlayer) => UpdatePlayer(io, socket, data));
socket.on(`LeaveGame`, (data: LeaveGame) => LeaveGame(io, socket, data)); socket.on(`LeaveGame`, (data: LeaveGame) => LeaveGame(io, socket, data));
socket.on(`RandomizeTeams`, (data: RandomizeTeams) => RandomizeTeams(io, socket, data));
// Game Mechanisms // Game Mechanisms

View file

@ -1,17 +1,56 @@
<template> <template>
<div id="TeamReminder"> <div id="TeamReminder">
<img <div
:class="`team_${teamNumber}`" class="container clickable"
:src="`/assets/${teamIcon}`" @click="modal_visible = true"
:alt="`${teamName} Team Icon`"
> >
<img
:class="`team_${teamNumber}`"
:src="`/assets/${teamIcon}`"
:alt="`${teamName} Team Icon`"
>
</div>
<ModalAnimation
:show="modal_visible"
@closed="modal_visible = false"
>
<h1 class="centre">Players</h1>
<div class="team-list">
<div
class="team centre"
v-for="teamID in 2"
:key="`team-${teamID}`"
>
<h2>{{$store.state[`team_${teamID}`].name}} Team:</h2>
<div class="players">
<p
class="player"
v-for="p in getTeamPlayers(teamID)"
:key="p.name"
>
{{p.name}}
<span v-if="p.role == 'writer'">
<b>({{$store.state.writer_name}})</b>
</span>
</p>
</div>
</div>
</div>
</ModalAnimation>
</div> </div>
</template> </template>
<script> <script>
import ModalAnimation from "./Modal";
export default { export default {
name: `TeamReminder`, name: `TeamReminder`,
components: {}, components: {
ModalAnimation: ModalAnimation
},
data() {return {
modal_visible: false
}},
computed: { computed: {
teamNumber() { teamNumber() {
return this.$store.state.team; return this.$store.state.team;
@ -23,7 +62,11 @@ export default {
return this.$store.state[`team_${this.teamNumber}`].icon; return this.$store.state[`team_${this.teamNumber}`].icon;
}, },
}, },
methods: {}, methods: {
getTeamPlayers(teamID) {
return this.$store.state.players.filter(p => p.team == teamID);
}
},
} }
</script> </script>
@ -31,7 +74,7 @@ export default {
@import "../css/theme.css"; @import "../css/theme.css";
@import "../css/style.css"; @import "../css/style.css";
#TeamReminder { #TeamReminder > .container {
background-color: var(--background3); background-color: var(--background3);
border-radius: 0 100% 0 0; border-radius: 0 100% 0 0;
height: var(--size); height: var(--size);
@ -46,7 +89,7 @@ img.team_1 {
width: calc(var(--size) / 1.5); width: calc(var(--size) / 1.5);
position: absolute; position: absolute;
bottom: 7px; bottom: 7px;
left: 7px;; left: 7px;
} }
img.team_2 { img.team_2 {
@ -55,4 +98,19 @@ img.team_2 {
left: calc(var(--size) / 8); left: calc(var(--size) / 8);
position: absolute; position: absolute;
} }
h1, h2, p {
margin: 5px 0;
}
.team-list {
justify-content: center;
flex-direction: row;
display: flex;
}
.team {
width: 40%;
}
</style> </style>

View file

@ -24,9 +24,10 @@
</div> </div>
<div class="flex-row"> <div class="flex-row">
<button <button
@click.stop="exitGame()" class="clickable"
@click.stop="randomizeTeams()"
> >
{{ $store.state.is_host ? `Delete` : `Leave`}} Game Randomize Teams
</button> </button>
<button <button
class="clickable" class="clickable"
@ -34,6 +35,12 @@
> >
Click to Start the Game Click to Start the Game
</button> </button>
<div class="new-line"></div>
<button
@click.stop="exitGame()"
>
{{ $store.state.is_host ? `Delete` : `Leave`}} Game
</button>
</div> </div>
</div> </div>
</template> </template>
@ -61,6 +68,9 @@ export default {
gameCode() { gameCode() {
return this.$store.state.game_code; return this.$store.state.game_code;
}, },
canRandomize() {
return this.$store.state.is_host;
},
}, },
methods: { methods: {
copySuccess() { copySuccess() {
@ -92,7 +102,12 @@ export default {
startGame() { startGame() {
this.$socket.client.emit(`StartGame`, { this.$socket.client.emit(`StartGame`, {
game_code: this.gameCode game_code: this.gameCode
}) });
},
randomizeTeams() {
this.$socket.client.emit(`RandomizeTeams`, {
game_code: this.gameCode
});
}, },
}, },
sockets: { sockets: {
@ -126,6 +141,7 @@ export default {
justify-content: center; justify-content: center;
align-items: stretch; align-items: stretch;
display: flex; display: flex;
flex-wrap: wrap;
} }
button { button {
@ -139,4 +155,10 @@ button {
} }
button:hover { background-color: var(--background2-darken); } button:hover { background-color: var(--background2-darken); }
button:focus { background-color: var(--background2-lighten); } button:focus { background-color: var(--background2-lighten); }
div.new-line {
width: 100% !important;
height: 0px;
}
</style> </style>