0
0
Fork 0
Phantom-Ink-Online/server/src/objects/Game.ts
2021-02-28 18:23:26 -07:00

238 lines
No EOL
5.7 KiB
TypeScript

import axios from "axios";
import { Team } from "./Team";
import { Deck } from "./Deck";
import { readFile } from "fs";
import neatCSV from "neat-csv";
import { Logger } from "tslog";
import { Player } from "./Player";
import { games, hibernatedGames, conf, log } from "../main";
export class Game {
readonly id: string;
readonly host: Player;
public log: Logger;
public ingame: boolean;
public teams: Team[];
public players: Player[];
private _questions: Deck<question_deck>;
private _objects: Deck<object_deck>;
private _objectCard: string[]|null;
public object: string;
constructor(host: Player, options:any=null) {
this.host = host;
this.ingame = false;
this.players = [host];
this.id = options?.id || Game.generateID(conf.game.code_length);
// If the object is being instantiated from JSON we don't want to do
// any of the stuff that requires weird per-game stuff
if (!options) {
// Get the decks based on what type of data they are.
switch (conf.game.cards.type) {
case "csv":
this.parseDeckCSV();
break;
case "sheets":
this.parseDeckGoogleSheets();
break;
};
// Instantiate everything for the teams
this.teams = [ new Team(1), new Team(2) ];
};
};
get questions() { return this._questions; };
/**
* Return the objects that the spirits can choose from for the game.
*/
get objects() {
if (!this._objectCard) {
this._objectCard = this._objects.draw(1)[0];
};
return this._objectCard;
};
get playerData() {
let players: player[] = [];
for (var player of this.players) {
players.push({
name: player.name,
role: player.role,
team: player.team,
});
};
return players
}
/**
* Parses out the CSV files and creates the decks for the game to run
* on.
*/
private parseDeckCSV() {
// parse the questions from the CSV
readFile(conf.game.cards.questions.fingerprint, `utf-8`, (err, filebuffer) => {
if (err) throw err;
neatCSV(filebuffer)
.then((data) => {
let questions: question_deck[] = [];
for (var entry of data) {
questions.push(Object.values(entry)[conf.game.cards.questions.column]);
};
this._questions = new Deck(questions);
});
});
// Parse the object deck from CSV
readFile(conf.game.cards.objects.fingerprint, `utf-8`, (err, filebuffer) => {
if (err) throw err;
neatCSV(filebuffer)
.then((data) => {
let objects: object_deck[] = [];
for (var line of data) {
objects.push(Object.values(line));
};
this._objects = new Deck(objects);
})
});
};
/**
* Fetches and parses the CSV data from Google Sheets instead of local
* CSV files.
*/
private parseDeckGoogleSheets() {
let key = conf.game.cards.key as string;
let questions_id = conf.game.cards.questions.fingerprint;
let objects_id = conf.game.cards.objects.fingerprint;
// Get the questions deck
axios.get(`https://docs.google.com/spreadsheets/d/e/${key}/pub?gid=${questions_id}&single=true&output=csv`)
.then(response => {
// Ensure not errored
if (response.status !== 200) {
log.warn(`Error Downloading CSV: ${response.statusText}`);
return;
};
// Parse the loaded CSV
neatCSV(response.data)
.then((data) => {
let questions: question_deck[] = [];
for (var entry of data) {
questions.push(Object.values(entry)[conf.game.cards.questions.column]);
};
this._questions = new Deck(questions);
});
})
.catch(err => {
log.prettyError(err);
});
// Get the objects deck
axios.get(`https://docs.google.com/spreadsheets/d/e/${key}/pub?gid=${objects_id}&single=true&output=csv`)
.then(response => {
// Ensure not errored
if (response.status !== 200) {
log.warn(`Error Downloading CSV: ${response.statusText}`);
return;
};
// Parse the downloaded CSV
neatCSV(response.data)
.then((data) => {
let objects: object_deck[] = [];
for (var line of data) {
objects.push(Object.values(line));
};
this._objects = new Deck(objects);
});
})
.catch(err => {
log.prettyError(err);
});
};
/**
* Resets the objects card, for restarting the game
*/
public resetObject() {
if (this._objectCard) {
this._objects.discard(this._objectCard);
this._objectCard = null;
this.object = ``;
};
};
/**
* Returns a JSON representation of the game.
*/
public toJSON(): datastoreGame {
return {
players: this.players.map(p => p.toJSON()),
teams: this.teams.map(t => t.toJSON()),
decks: {
questions: this._questions.toJSON(),
objects: this._objects.toJSON(),
},
objectCard: this._objectCard,
object: this.object,
ingame: this.ingame,
id: this.id,
};
};
/**
* Converts a JSON representation into a Game object
*/
public static fromJSON(host: Player, data: datastoreGame): Game {
let game = new this(host, { id: data.id });
// Re-create the deck objects
game._questions = Deck.fromJSON<question_deck>(data.decks.questions);
game._objects = Deck.fromJSON<object_deck>(data.decks.objects);
game.teams = data.teams.map(t => Team.fromJSON(t));
// Re-instantiate all the players from the game.
for (var player of data.players) {
if (player.name !== host.name) {
player.host = false;
game.players.push(Player.fromJSON(player));
};
};
game._objectCard = data.objectCard;
game.object = data.object;
return 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 {
let code: string;
// Generate a code until we don't have a collision
do {
code = ``;
for (var i = 0; i < length; i++) {
code += `${Math.floor(Math.random() * 9)}`;
};
} while (games[code] || hibernatedGames.includes(code));
return code;
};
};