mirror of
https://github.com/project-slippi/slippi-js.git
synced 2025-10-06 00:32:40 +02:00
Heck yeah back to double quotes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: 'all',
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
singleQuote: false,
|
||||
printWidth: 120,
|
||||
tabWidth: 2,
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import _ from 'lodash';
|
||||
import { openSlpFile, closeSlpFile, iterateEvents, getMetadata, SlpInputSource, SlpReadInput } from './utils/slpReader';
|
||||
import _ from "lodash";
|
||||
import { openSlpFile, closeSlpFile, iterateEvents, getMetadata, SlpInputSource, SlpReadInput } from "./utils/slpReader";
|
||||
|
||||
// Type imports
|
||||
import { MetadataType, GameStartType, GameEndType } from './types';
|
||||
import { SlpParser, SlpParserEvent } from './utils/slpParser';
|
||||
import { MetadataType, GameStartType, GameEndType } from "./types";
|
||||
import { SlpParser, SlpParserEvent } from "./utils/slpParser";
|
||||
import {
|
||||
StockComputer,
|
||||
ComboComputer,
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
getSinglesPlayerPermutationsFromSettings,
|
||||
generateOverallStats,
|
||||
StatOptions,
|
||||
} from './stats';
|
||||
} from "./stats";
|
||||
|
||||
/**
|
||||
* Slippi Game class that wraps a file
|
||||
@@ -48,7 +48,7 @@ export class SlippiGame {
|
||||
buffer: input,
|
||||
};
|
||||
} else {
|
||||
throw new Error('Cannot create SlippiGame with input of that type');
|
||||
throw new Error("Cannot create SlippiGame with input of that type");
|
||||
}
|
||||
|
||||
// Set up stats calculation
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { decode, encode } from '@shelacek/ubjson';
|
||||
import { decode, encode } from "@shelacek/ubjson";
|
||||
|
||||
export enum CommunicationType {
|
||||
HANDSHAKE = 1,
|
||||
|
@@ -1,20 +1,20 @@
|
||||
import net from 'net';
|
||||
import { EventEmitter } from 'events';
|
||||
import net from "net";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import inject from 'reconnect-core';
|
||||
import inject from "reconnect-core";
|
||||
|
||||
import { ConsoleCommunication, CommunicationType, CommunicationMessage } from './communication';
|
||||
import { ConsoleCommunication, CommunicationType, CommunicationMessage } from "./communication";
|
||||
|
||||
export const NETWORK_MESSAGE = 'HELO\0';
|
||||
export const NETWORK_MESSAGE = "HELO\0";
|
||||
|
||||
const DEFAULT_CONNECTION_TIMEOUT_MS = 20000;
|
||||
|
||||
export enum ConnectionEvent {
|
||||
HANDSHAKE = 'handshake',
|
||||
STATUS_CHANGE = 'statusChange',
|
||||
DATA = 'data',
|
||||
INFO = 'loginfo',
|
||||
WARN = 'logwarn',
|
||||
HANDSHAKE = "handshake",
|
||||
STATUS_CHANGE = "statusChange",
|
||||
DATA = "data",
|
||||
INFO = "loginfo",
|
||||
WARN = "logwarn",
|
||||
}
|
||||
|
||||
export enum ConnectionStatus {
|
||||
@@ -43,15 +43,15 @@ export interface ConnectionSettings {
|
||||
}
|
||||
|
||||
enum CommunicationState {
|
||||
INITIAL = 'initial',
|
||||
LEGACY = 'legacy',
|
||||
NORMAL = 'normal',
|
||||
INITIAL = "initial",
|
||||
LEGACY = "legacy",
|
||||
NORMAL = "normal",
|
||||
}
|
||||
|
||||
const defaultConnectionDetails: ConnectionDetails = {
|
||||
consoleNick: 'unknown',
|
||||
consoleNick: "unknown",
|
||||
gameDataCursor: Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
version: '',
|
||||
version: "",
|
||||
clientToken: 0,
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ export class ConsoleConnection extends EventEmitter {
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this.ipAddress = '0.0.0.0';
|
||||
this.ipAddress = "0.0.0.0";
|
||||
this.port = Ports.DEFAULT;
|
||||
this.clientsByPort = [];
|
||||
this.connectionsByPort = [];
|
||||
@@ -160,19 +160,19 @@ export class ConsoleConnection extends EventEmitter {
|
||||
{
|
||||
initialDelay: 2000,
|
||||
maxDelay: 10000,
|
||||
strategy: 'fibonacci',
|
||||
strategy: "fibonacci",
|
||||
failAfter: Infinity,
|
||||
},
|
||||
(client) => {
|
||||
this.clientsByPort[port] = client;
|
||||
|
||||
let commState: CommunicationState = CommunicationState.INITIAL;
|
||||
client.on('data', (data) => {
|
||||
client.on("data", (data) => {
|
||||
if (commState === CommunicationState.INITIAL) {
|
||||
commState = this._getInitialCommState(data);
|
||||
console.log(`Connected to ${this.ipAddress}:${this.port} with type: ${commState}`);
|
||||
this._setStatus(ConnectionStatus.CONNECTED);
|
||||
console.log(data.toString('hex'));
|
||||
console.log(data.toString("hex"));
|
||||
}
|
||||
|
||||
if (commState === CommunicationState.LEGACY) {
|
||||
@@ -185,7 +185,7 @@ export class ConsoleConnection extends EventEmitter {
|
||||
try {
|
||||
consoleComms.receive(data);
|
||||
} catch (err) {
|
||||
console.warn('Failed to process new data from server...', {
|
||||
console.warn("Failed to process new data from server...", {
|
||||
error: err,
|
||||
prevDataBuf: consoleComms.getReceiveBuffer(),
|
||||
rcvData: data,
|
||||
@@ -206,19 +206,19 @@ export class ConsoleConnection extends EventEmitter {
|
||||
}
|
||||
});
|
||||
|
||||
client.on('timeout', () => {
|
||||
client.on("timeout", () => {
|
||||
// const previouslyConnected = this.connectionStatus === ConnectionStatus.CONNECTED;
|
||||
console.warn(`Attempted connection to ${this.ipAddress}:${this.port} timed out after ${timeout}ms`);
|
||||
client.destroy();
|
||||
});
|
||||
|
||||
client.on('end', () => {
|
||||
console.log('disconnect');
|
||||
client.on("end", () => {
|
||||
console.log("disconnect");
|
||||
client.destroy();
|
||||
});
|
||||
|
||||
client.on('close', () => {
|
||||
console.log('connection was closed');
|
||||
client.on("close", () => {
|
||||
console.log("connection was closed");
|
||||
});
|
||||
|
||||
const handshakeMsgOut = consoleComms.genHandshakeOut(
|
||||
@@ -235,10 +235,10 @@ export class ConsoleConnection extends EventEmitter {
|
||||
this._setStatus(ConnectionStatus.CONNECTING);
|
||||
};
|
||||
|
||||
connection.on('connect', setConnectingStatus);
|
||||
connection.on('reconnect', setConnectingStatus);
|
||||
connection.on("connect", setConnectingStatus);
|
||||
connection.on("reconnect", setConnectingStatus);
|
||||
|
||||
connection.on('disconnect', () => {
|
||||
connection.on("disconnect", () => {
|
||||
// If one of the connections was successful, we no longer need to try connecting this one
|
||||
this.connectionsByPort.forEach((iConn, iPort) => {
|
||||
if (iPort === port || !iConn.connected) {
|
||||
@@ -255,12 +255,12 @@ export class ConsoleConnection extends EventEmitter {
|
||||
// TODO: Connecting... forever
|
||||
});
|
||||
|
||||
connection.on('error', (error) => {
|
||||
connection.on("error", (error) => {
|
||||
console.error(`Connection on port ${port} encountered an error.`, error);
|
||||
});
|
||||
|
||||
this.connectionsByPort[port] = connection;
|
||||
console.log('Starting connection');
|
||||
console.log("Starting connection");
|
||||
connection.connect(port);
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ export class ConsoleConnection extends EventEmitter {
|
||||
* Terminate the current connection.
|
||||
*/
|
||||
public disconnect(): void {
|
||||
console.log('Disconnect request');
|
||||
console.log("Disconnect request");
|
||||
|
||||
this.connectionsByPort.forEach((connection) => {
|
||||
// Prevent reconnections and disconnect
|
||||
@@ -312,19 +312,19 @@ export class ConsoleConnection extends EventEmitter {
|
||||
const cmp = Buffer.compare(this.connDetails.gameDataCursor, readPos);
|
||||
if (!message.payload.forcePos && cmp !== 0) {
|
||||
console.warn(
|
||||
'Position of received data is not what was expected. Expected, Received:',
|
||||
"Position of received data is not what was expected. Expected, Received:",
|
||||
this.connDetails.gameDataCursor,
|
||||
readPos,
|
||||
);
|
||||
|
||||
// The readPos is not the one we are waiting on, throw error
|
||||
throw new Error('Position of received data is incorrect.');
|
||||
throw new Error("Position of received data is incorrect.");
|
||||
}
|
||||
|
||||
if (message.payload.forcePos) {
|
||||
console.warn(
|
||||
'Overflow occured in Nintendont, data has likely been skipped and replay corrupted. ' +
|
||||
'Expected, Received:',
|
||||
"Overflow occured in Nintendont, data has likely been skipped and replay corrupted. " +
|
||||
"Expected, Received:",
|
||||
this.connDetails.gameDataCursor,
|
||||
readPos,
|
||||
);
|
||||
|
@@ -1 +1 @@
|
||||
export * from './connection';
|
||||
export * from "./connection";
|
||||
|
18
src/index.ts
18
src/index.ts
@@ -1,21 +1,21 @@
|
||||
import { SlippiGame } from './SlippiGame';
|
||||
import { SlippiGame } from "./SlippiGame";
|
||||
|
||||
// Export melee util types
|
||||
export * from './melee';
|
||||
export * from "./melee";
|
||||
|
||||
// Export types
|
||||
export * from './types';
|
||||
export * from './stats';
|
||||
export * from "./types";
|
||||
export * from "./stats";
|
||||
|
||||
// Utils
|
||||
export * from './utils/slpFile';
|
||||
export * from './utils/slpStream';
|
||||
export * from './utils/slpParser';
|
||||
export * from "./utils/slpFile";
|
||||
export * from "./utils/slpStream";
|
||||
export * from "./utils/slpParser";
|
||||
|
||||
// Console networking
|
||||
export * from './console';
|
||||
export * from "./console";
|
||||
|
||||
// Support both named and default exports
|
||||
export * from './SlippiGame';
|
||||
export * from "./SlippiGame";
|
||||
|
||||
export default SlippiGame;
|
||||
|
@@ -6,12 +6,12 @@ export function getDeathDirection(actionStateId: number) {
|
||||
|
||||
switch (actionStateId) {
|
||||
case 0:
|
||||
return 'down';
|
||||
return "down";
|
||||
case 1:
|
||||
return 'left';
|
||||
return "left";
|
||||
case 2:
|
||||
return 'right';
|
||||
return "right";
|
||||
default:
|
||||
return 'up';
|
||||
return "up";
|
||||
}
|
||||
}
|
||||
|
@@ -10,159 +10,159 @@ export interface CharacterInfo {
|
||||
const externalCharacters: CharacterInfo[] = [
|
||||
{
|
||||
id: 0,
|
||||
name: 'Captain Falcon',
|
||||
shortName: 'Falcon',
|
||||
colors: ['Default', 'Black', 'Red', 'White', 'Green', 'Blue'],
|
||||
name: "Captain Falcon",
|
||||
shortName: "Falcon",
|
||||
colors: ["Default", "Black", "Red", "White", "Green", "Blue"],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'Donkey Kong',
|
||||
shortName: 'DK',
|
||||
colors: ['Default', 'Black', 'Red', 'Blue', 'Green'],
|
||||
name: "Donkey Kong",
|
||||
shortName: "DK",
|
||||
colors: ["Default", "Black", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Fox',
|
||||
shortName: 'Fox',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green'],
|
||||
name: "Fox",
|
||||
shortName: "Fox",
|
||||
colors: ["Default", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Mr. Game & Watch',
|
||||
shortName: 'G&W',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green'],
|
||||
name: "Mr. Game & Watch",
|
||||
shortName: "G&W",
|
||||
colors: ["Default", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Kirby',
|
||||
shortName: 'Kirby',
|
||||
colors: ['Default', 'Yellow', 'Blue', 'Red', 'Green', 'White'],
|
||||
name: "Kirby",
|
||||
shortName: "Kirby",
|
||||
colors: ["Default", "Yellow", "Blue", "Red", "Green", "White"],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Bowser',
|
||||
shortName: 'Bowser',
|
||||
colors: ['Default', 'Red', 'Blue', 'Black'],
|
||||
name: "Bowser",
|
||||
shortName: "Bowser",
|
||||
colors: ["Default", "Red", "Blue", "Black"],
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Link',
|
||||
shortName: 'Link',
|
||||
colors: ['Default', 'Red', 'Blue', 'Black', 'White'],
|
||||
name: "Link",
|
||||
shortName: "Link",
|
||||
colors: ["Default", "Red", "Blue", "Black", "White"],
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Luigi',
|
||||
shortName: 'Luigi',
|
||||
colors: ['Default', 'White', 'Blue', 'Red'],
|
||||
name: "Luigi",
|
||||
shortName: "Luigi",
|
||||
colors: ["Default", "White", "Blue", "Red"],
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Mario',
|
||||
shortName: 'Mario',
|
||||
colors: ['Default', 'Yellow', 'Black', 'Blue', 'Green'],
|
||||
name: "Mario",
|
||||
shortName: "Mario",
|
||||
colors: ["Default", "Yellow", "Black", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Marth',
|
||||
shortName: 'Marth',
|
||||
colors: ['Default', 'Red', 'Green', 'Black', 'White'],
|
||||
name: "Marth",
|
||||
shortName: "Marth",
|
||||
colors: ["Default", "Red", "Green", "Black", "White"],
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Mewtwo',
|
||||
shortName: 'Mewtwo',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green'],
|
||||
name: "Mewtwo",
|
||||
shortName: "Mewtwo",
|
||||
colors: ["Default", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Ness',
|
||||
shortName: 'Ness',
|
||||
colors: ['Default', 'Yellow', 'Blue', 'Green'],
|
||||
name: "Ness",
|
||||
shortName: "Ness",
|
||||
colors: ["Default", "Yellow", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Peach',
|
||||
shortName: 'Peach',
|
||||
colors: ['Default', 'Daisy', 'White', 'Blue', 'Green'],
|
||||
name: "Peach",
|
||||
shortName: "Peach",
|
||||
colors: ["Default", "Daisy", "White", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Pikachu',
|
||||
shortName: 'Pikachu',
|
||||
colors: ['Default', 'Red', 'Party Hat', 'Cowboy Hat'],
|
||||
name: "Pikachu",
|
||||
shortName: "Pikachu",
|
||||
colors: ["Default", "Red", "Party Hat", "Cowboy Hat"],
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Ice Climbers',
|
||||
shortName: 'ICs',
|
||||
colors: ['Default', 'Green', 'Orange', 'Red'],
|
||||
name: "Ice Climbers",
|
||||
shortName: "ICs",
|
||||
colors: ["Default", "Green", "Orange", "Red"],
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'Jigglypuff',
|
||||
shortName: 'Puff',
|
||||
colors: ['Default', 'Red', 'Blue', 'Headband', 'Crown'],
|
||||
name: "Jigglypuff",
|
||||
shortName: "Puff",
|
||||
colors: ["Default", "Red", "Blue", "Headband", "Crown"],
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: 'Samus',
|
||||
shortName: 'Samus',
|
||||
colors: ['Default', 'Pink', 'Black', 'Green', 'Purple'],
|
||||
name: "Samus",
|
||||
shortName: "Samus",
|
||||
colors: ["Default", "Pink", "Black", "Green", "Purple"],
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
name: 'Yoshi',
|
||||
shortName: 'Yoshi',
|
||||
colors: ['Default', 'Red', 'Blue', 'Yellow', 'Pink', 'Cyan'],
|
||||
name: "Yoshi",
|
||||
shortName: "Yoshi",
|
||||
colors: ["Default", "Red", "Blue", "Yellow", "Pink", "Cyan"],
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
name: 'Zelda',
|
||||
shortName: 'Zelda',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green', 'White'],
|
||||
name: "Zelda",
|
||||
shortName: "Zelda",
|
||||
colors: ["Default", "Red", "Blue", "Green", "White"],
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
name: 'Sheik',
|
||||
shortName: 'Sheik',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green', 'White'],
|
||||
name: "Sheik",
|
||||
shortName: "Sheik",
|
||||
colors: ["Default", "Red", "Blue", "Green", "White"],
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
name: 'Falco',
|
||||
shortName: 'Falco',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green'],
|
||||
name: "Falco",
|
||||
shortName: "Falco",
|
||||
colors: ["Default", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
name: 'Young Link',
|
||||
shortName: 'YLink',
|
||||
colors: ['Default', 'Red', 'Blue', 'White', 'Black'],
|
||||
name: "Young Link",
|
||||
shortName: "YLink",
|
||||
colors: ["Default", "Red", "Blue", "White", "Black"],
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
name: 'Dr. Mario',
|
||||
shortName: 'Doc',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green', 'Black'],
|
||||
name: "Dr. Mario",
|
||||
shortName: "Doc",
|
||||
colors: ["Default", "Red", "Blue", "Green", "Black"],
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
name: 'Roy',
|
||||
shortName: 'Roy',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green', 'Yellow'],
|
||||
name: "Roy",
|
||||
shortName: "Roy",
|
||||
colors: ["Default", "Red", "Blue", "Green", "Yellow"],
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
name: 'Pichu',
|
||||
shortName: 'Pichu',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green'],
|
||||
name: "Pichu",
|
||||
shortName: "Pichu",
|
||||
colors: ["Default", "Red", "Blue", "Green"],
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
name: 'Ganondorf',
|
||||
shortName: 'Ganon',
|
||||
colors: ['Default', 'Red', 'Blue', 'Green', 'Purple'],
|
||||
name: "Ganondorf",
|
||||
shortName: "Ganon",
|
||||
colors: ["Default", "Red", "Blue", "Green", "Purple"],
|
||||
},
|
||||
];
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as animations from './animations';
|
||||
import * as characters from './characters';
|
||||
import * as moves from './moves';
|
||||
import * as stages from './stages';
|
||||
import * as animations from "./animations";
|
||||
import * as characters from "./characters";
|
||||
import * as moves from "./moves";
|
||||
import * as stages from "./stages";
|
||||
|
||||
export { animations, characters, moves, stages };
|
||||
|
@@ -6,161 +6,161 @@ export interface Move {
|
||||
|
||||
export const UnknownMove: Move = {
|
||||
id: -1,
|
||||
name: 'Unknown Move',
|
||||
shortName: 'unknown',
|
||||
name: "Unknown Move",
|
||||
shortName: "unknown",
|
||||
};
|
||||
|
||||
const moves: { [id: number]: Move } = {
|
||||
1: {
|
||||
// This includes all thrown items, zair, luigi's taunt, samus bombs, etc
|
||||
id: 1,
|
||||
name: 'Miscellaneous',
|
||||
shortName: 'misc',
|
||||
name: "Miscellaneous",
|
||||
shortName: "misc",
|
||||
},
|
||||
2: {
|
||||
id: 2,
|
||||
name: 'Jab',
|
||||
shortName: 'jab',
|
||||
name: "Jab",
|
||||
shortName: "jab",
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
name: 'Jab',
|
||||
shortName: 'jab',
|
||||
name: "Jab",
|
||||
shortName: "jab",
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
name: 'Jab',
|
||||
shortName: 'jab',
|
||||
name: "Jab",
|
||||
shortName: "jab",
|
||||
},
|
||||
5: {
|
||||
id: 5,
|
||||
name: 'Rapid Jabs',
|
||||
shortName: 'rapid-jabs',
|
||||
name: "Rapid Jabs",
|
||||
shortName: "rapid-jabs",
|
||||
},
|
||||
6: {
|
||||
id: 6,
|
||||
name: 'Dash Attack',
|
||||
shortName: 'dash',
|
||||
name: "Dash Attack",
|
||||
shortName: "dash",
|
||||
},
|
||||
7: {
|
||||
id: 7,
|
||||
name: 'Forward Tilt',
|
||||
shortName: 'ftilt',
|
||||
name: "Forward Tilt",
|
||||
shortName: "ftilt",
|
||||
},
|
||||
8: {
|
||||
id: 8,
|
||||
name: 'Up Tilt',
|
||||
shortName: 'utilt',
|
||||
name: "Up Tilt",
|
||||
shortName: "utilt",
|
||||
},
|
||||
9: {
|
||||
id: 9,
|
||||
name: 'Down Tilt',
|
||||
shortName: 'dtilt',
|
||||
name: "Down Tilt",
|
||||
shortName: "dtilt",
|
||||
},
|
||||
10: {
|
||||
id: 10,
|
||||
name: 'Forward Smash',
|
||||
shortName: 'fsmash',
|
||||
name: "Forward Smash",
|
||||
shortName: "fsmash",
|
||||
},
|
||||
11: {
|
||||
id: 11,
|
||||
name: 'Up Smash',
|
||||
shortName: 'usmash',
|
||||
name: "Up Smash",
|
||||
shortName: "usmash",
|
||||
},
|
||||
12: {
|
||||
id: 12,
|
||||
name: 'Down Smash',
|
||||
shortName: 'dsmash',
|
||||
name: "Down Smash",
|
||||
shortName: "dsmash",
|
||||
},
|
||||
13: {
|
||||
id: 13,
|
||||
name: 'Neutral Air',
|
||||
shortName: 'nair',
|
||||
name: "Neutral Air",
|
||||
shortName: "nair",
|
||||
},
|
||||
14: {
|
||||
id: 14,
|
||||
name: 'Forward Air',
|
||||
shortName: 'fair',
|
||||
name: "Forward Air",
|
||||
shortName: "fair",
|
||||
},
|
||||
15: {
|
||||
id: 15,
|
||||
name: 'Back Air',
|
||||
shortName: 'bair',
|
||||
name: "Back Air",
|
||||
shortName: "bair",
|
||||
},
|
||||
16: {
|
||||
id: 16,
|
||||
name: 'Up Air',
|
||||
shortName: 'uair',
|
||||
name: "Up Air",
|
||||
shortName: "uair",
|
||||
},
|
||||
17: {
|
||||
id: 17,
|
||||
name: 'Down Air',
|
||||
shortName: 'dair',
|
||||
name: "Down Air",
|
||||
shortName: "dair",
|
||||
},
|
||||
18: {
|
||||
id: 18,
|
||||
name: 'Neutral B',
|
||||
shortName: 'neutral-b',
|
||||
name: "Neutral B",
|
||||
shortName: "neutral-b",
|
||||
},
|
||||
19: {
|
||||
id: 19,
|
||||
name: 'Side B',
|
||||
shortName: 'side-b',
|
||||
name: "Side B",
|
||||
shortName: "side-b",
|
||||
},
|
||||
20: {
|
||||
id: 20,
|
||||
name: 'Up B',
|
||||
shortName: 'up-b',
|
||||
name: "Up B",
|
||||
shortName: "up-b",
|
||||
},
|
||||
21: {
|
||||
id: 21,
|
||||
name: 'Down B',
|
||||
shortName: 'down-b',
|
||||
name: "Down B",
|
||||
shortName: "down-b",
|
||||
},
|
||||
50: {
|
||||
id: 50,
|
||||
name: 'Getup Attack',
|
||||
shortName: 'getup',
|
||||
name: "Getup Attack",
|
||||
shortName: "getup",
|
||||
},
|
||||
51: {
|
||||
id: 51,
|
||||
name: 'Getup Attack (Slow)',
|
||||
shortName: 'getup-slow',
|
||||
name: "Getup Attack (Slow)",
|
||||
shortName: "getup-slow",
|
||||
},
|
||||
52: {
|
||||
id: 52,
|
||||
name: 'Grab Pummel',
|
||||
shortName: 'pummel',
|
||||
name: "Grab Pummel",
|
||||
shortName: "pummel",
|
||||
},
|
||||
53: {
|
||||
id: 53,
|
||||
name: 'Forward Throw',
|
||||
shortName: 'fthrow',
|
||||
name: "Forward Throw",
|
||||
shortName: "fthrow",
|
||||
},
|
||||
54: {
|
||||
id: 54,
|
||||
name: 'Back Throw',
|
||||
shortName: 'bthrow',
|
||||
name: "Back Throw",
|
||||
shortName: "bthrow",
|
||||
},
|
||||
55: {
|
||||
id: 55,
|
||||
name: 'Up Throw',
|
||||
shortName: 'uthrow',
|
||||
name: "Up Throw",
|
||||
shortName: "uthrow",
|
||||
},
|
||||
56: {
|
||||
id: 56,
|
||||
name: 'Down Throw',
|
||||
shortName: 'dthrow',
|
||||
name: "Down Throw",
|
||||
shortName: "dthrow",
|
||||
},
|
||||
61: {
|
||||
id: 61,
|
||||
name: 'Edge Attack (Slow)',
|
||||
shortName: 'edge-slow',
|
||||
name: "Edge Attack (Slow)",
|
||||
shortName: "edge-slow",
|
||||
},
|
||||
62: {
|
||||
id: 62,
|
||||
name: 'Edge Attack',
|
||||
shortName: 'edge',
|
||||
name: "Edge Attack",
|
||||
shortName: "edge",
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -6,11 +6,11 @@ export interface Stage {
|
||||
const stages: { [id: number]: Stage } = {
|
||||
2: {
|
||||
id: 2,
|
||||
name: 'Fountain of Dreams',
|
||||
name: "Fountain of Dreams",
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
name: 'Pokémon Stadium',
|
||||
name: "Pokémon Stadium",
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
@@ -18,15 +18,15 @@ const stages: { [id: number]: Stage } = {
|
||||
},
|
||||
5: {
|
||||
id: 5,
|
||||
name: 'Kongo Jungle',
|
||||
name: "Kongo Jungle",
|
||||
},
|
||||
6: {
|
||||
id: 6,
|
||||
name: 'Brinstar',
|
||||
name: "Brinstar",
|
||||
},
|
||||
7: {
|
||||
id: 7,
|
||||
name: 'Corneria',
|
||||
name: "Corneria",
|
||||
},
|
||||
8: {
|
||||
id: 8,
|
||||
@@ -34,31 +34,31 @@ const stages: { [id: number]: Stage } = {
|
||||
},
|
||||
9: {
|
||||
id: 9,
|
||||
name: 'Onett',
|
||||
name: "Onett",
|
||||
},
|
||||
10: {
|
||||
id: 10,
|
||||
name: 'Mute City',
|
||||
name: "Mute City",
|
||||
},
|
||||
11: {
|
||||
id: 11,
|
||||
name: 'Rainbow Cruise',
|
||||
name: "Rainbow Cruise",
|
||||
},
|
||||
12: {
|
||||
id: 12,
|
||||
name: 'Jungle Japes',
|
||||
name: "Jungle Japes",
|
||||
},
|
||||
13: {
|
||||
id: 13,
|
||||
name: 'Great Bay',
|
||||
name: "Great Bay",
|
||||
},
|
||||
14: {
|
||||
id: 14,
|
||||
name: 'Hyrule Temple',
|
||||
name: "Hyrule Temple",
|
||||
},
|
||||
15: {
|
||||
id: 15,
|
||||
name: 'Brinstar Depths',
|
||||
name: "Brinstar Depths",
|
||||
},
|
||||
16: {
|
||||
id: 16,
|
||||
@@ -66,47 +66,47 @@ const stages: { [id: number]: Stage } = {
|
||||
},
|
||||
17: {
|
||||
id: 17,
|
||||
name: 'Green Greens',
|
||||
name: "Green Greens",
|
||||
},
|
||||
18: {
|
||||
id: 18,
|
||||
name: 'Fourside',
|
||||
name: "Fourside",
|
||||
},
|
||||
19: {
|
||||
id: 19,
|
||||
name: 'Mushroom Kingdom I',
|
||||
name: "Mushroom Kingdom I",
|
||||
},
|
||||
20: {
|
||||
id: 20,
|
||||
name: 'Mushroom Kingdom II',
|
||||
name: "Mushroom Kingdom II",
|
||||
},
|
||||
22: {
|
||||
id: 22,
|
||||
name: 'Venom',
|
||||
name: "Venom",
|
||||
},
|
||||
23: {
|
||||
id: 23,
|
||||
name: 'Poké Floats',
|
||||
name: "Poké Floats",
|
||||
},
|
||||
24: {
|
||||
id: 24,
|
||||
name: 'Big Blue',
|
||||
name: "Big Blue",
|
||||
},
|
||||
25: {
|
||||
id: 25,
|
||||
name: 'Icicle Mountain',
|
||||
name: "Icicle Mountain",
|
||||
},
|
||||
26: {
|
||||
id: 26,
|
||||
name: 'Icetop',
|
||||
name: "Icetop",
|
||||
},
|
||||
27: {
|
||||
id: 27,
|
||||
name: 'Flat Zone',
|
||||
name: "Flat Zone",
|
||||
},
|
||||
28: {
|
||||
id: 28,
|
||||
name: 'Dream Land N64',
|
||||
name: "Dream Land N64",
|
||||
},
|
||||
29: {
|
||||
id: 29,
|
||||
@@ -114,15 +114,15 @@ const stages: { [id: number]: Stage } = {
|
||||
},
|
||||
30: {
|
||||
id: 30,
|
||||
name: 'Kongo Jungle N64',
|
||||
name: "Kongo Jungle N64",
|
||||
},
|
||||
31: {
|
||||
id: 31,
|
||||
name: 'Battlefield',
|
||||
name: "Battlefield",
|
||||
},
|
||||
32: {
|
||||
id: 32,
|
||||
name: 'Final Destination',
|
||||
name: "Final Destination",
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import _ from 'lodash';
|
||||
import { State, PlayerIndexedType, FrameEntryType, ActionCountsType } from './common';
|
||||
import { StatComputer } from './stats';
|
||||
import _ from "lodash";
|
||||
import { State, PlayerIndexedType, FrameEntryType, ActionCountsType } from "./common";
|
||||
import { StatComputer } from "./stats";
|
||||
|
||||
// Frame pattern that indicates a dash dance turn was executed
|
||||
const dashDanceAnimations = [State.DASH, State.TURN, State.DASH];
|
||||
@@ -114,19 +114,19 @@ function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedTyp
|
||||
|
||||
// Increment counts based on conditions
|
||||
const didDashDance = _.isEqual(last3Frames, dashDanceAnimations);
|
||||
incrementCount('dashDanceCount', didDashDance);
|
||||
incrementCount("dashDanceCount", didDashDance);
|
||||
|
||||
const didRoll = didStartRoll(currentAnimation, prevAnimation);
|
||||
incrementCount('rollCount', didRoll);
|
||||
incrementCount("rollCount", didRoll);
|
||||
|
||||
const didSpotDodge = didStartSpotDodge(currentAnimation, prevAnimation);
|
||||
incrementCount('spotDodgeCount', didSpotDodge);
|
||||
incrementCount("spotDodgeCount", didSpotDodge);
|
||||
|
||||
const didAirDodge = didStartAirDodge(currentAnimation, prevAnimation);
|
||||
incrementCount('airDodgeCount', didAirDodge);
|
||||
incrementCount("airDodgeCount", didAirDodge);
|
||||
|
||||
const didGrabLedge = didStartLedgegrab(currentAnimation, prevAnimation);
|
||||
incrementCount('ledgegrabCount', didGrabLedge);
|
||||
incrementCount("ledgegrabCount", didGrabLedge);
|
||||
|
||||
// Handles wavedash detection (and waveland)
|
||||
handleActionWavedash(state.playerCounts, state.animations);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
import { PostFrameUpdateType } from '../types';
|
||||
import { FrameEntryType, FramesType, MoveLandedType, ComboType, PlayerIndexedType } from './common';
|
||||
import { isDamaged, isGrabbed, calcDamageTaken, isTeching, didLoseStock, Timers, isDown, isDead } from './common';
|
||||
import { StatComputer } from './stats';
|
||||
import _ from "lodash";
|
||||
import { PostFrameUpdateType } from "../types";
|
||||
import { FrameEntryType, FramesType, MoveLandedType, ComboType, PlayerIndexedType } from "./common";
|
||||
import { isDamaged, isGrabbed, calcDamageTaken, isTeching, didLoseStock, Timers, isDown, isDead } from "./common";
|
||||
import { StatComputer } from "./stats";
|
||||
|
||||
interface ComboState {
|
||||
combo: ComboType | null;
|
||||
@@ -51,11 +51,11 @@ function handleComboCompute(
|
||||
const playerFrame: PostFrameUpdateType = frame.players[indices.playerIndex].post;
|
||||
// FIXME: use type PostFrameUpdateType instead of any
|
||||
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.playerIndex, 'post'], {});
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.playerIndex, "post"], {});
|
||||
const opponentFrame: PostFrameUpdateType = frame.players[indices.opponentIndex].post;
|
||||
// FIXME: use type PostFrameUpdateType instead of any
|
||||
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
|
||||
const prevOpponentFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.opponentIndex, 'post'], {});
|
||||
const prevOpponentFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.opponentIndex, "post"], {});
|
||||
|
||||
const opntIsDamaged = isDamaged(opponentFrame.actionStateId);
|
||||
const opntIsGrabbed = isGrabbed(opponentFrame.actionStateId);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import { PostFrameUpdateType, GameStartType, PreFrameUpdateType, ItemUpdateType } from '../types';
|
||||
import _ from "lodash";
|
||||
import { PostFrameUpdateType, GameStartType, PreFrameUpdateType, ItemUpdateType } from "../types";
|
||||
|
||||
export interface FrameEntryType {
|
||||
frame: number;
|
||||
@@ -214,8 +214,8 @@ export function isDead(state: number): boolean {
|
||||
}
|
||||
|
||||
export function calcDamageTaken(frame: PostFrameUpdateType, prevFrame: PostFrameUpdateType): number {
|
||||
const percent = _.get(frame, 'percent', 0);
|
||||
const prevPercent = _.get(prevFrame, 'percent', 0);
|
||||
const percent = _.get(frame, "percent", 0);
|
||||
const prevPercent = _.get(prevFrame, "percent", 0);
|
||||
|
||||
return percent - prevPercent;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
import { PostFrameUpdateType } from '../types';
|
||||
import { FrameEntryType, FramesType, MoveLandedType, ConversionType, PlayerIndexedType } from './common';
|
||||
import { isDamaged, isGrabbed, calcDamageTaken, isInControl, didLoseStock, Timers } from './common';
|
||||
import { StatComputer } from './stats';
|
||||
import _ from "lodash";
|
||||
import { PostFrameUpdateType } from "../types";
|
||||
import { FrameEntryType, FramesType, MoveLandedType, ConversionType, PlayerIndexedType } from "./common";
|
||||
import { isDamaged, isGrabbed, calcDamageTaken, isInControl, didLoseStock, Timers } from "./common";
|
||||
import { StatComputer } from "./stats";
|
||||
|
||||
interface PlayerConversionState {
|
||||
conversion: ConversionType | null;
|
||||
@@ -57,13 +57,13 @@ export class ConversionComputer implements StatComputer<ConversionType[]> {
|
||||
private _populateConversionTypes(): void {
|
||||
// Post-processing step: set the openingTypes
|
||||
const conversionsToHandle = _.filter(this.conversions, (conversion) => {
|
||||
return conversion.openingType === 'unknown';
|
||||
return conversion.openingType === "unknown";
|
||||
});
|
||||
|
||||
// Group new conversions by startTime and sort
|
||||
const sortedConversions: ConversionType[][] = _.chain(conversionsToHandle)
|
||||
.groupBy('startFrame')
|
||||
.orderBy((conversions) => _.get(conversions, [0, 'startFrame']))
|
||||
.groupBy("startFrame")
|
||||
.orderBy((conversions) => _.get(conversions, [0, "startFrame"]))
|
||||
.value();
|
||||
|
||||
// Set the opening types on the conversions we need to handle
|
||||
@@ -75,14 +75,14 @@ export class ConversionComputer implements StatComputer<ConversionType[]> {
|
||||
|
||||
if (isTrade) {
|
||||
// If trade, just short-circuit
|
||||
conversion.openingType = 'trade';
|
||||
conversion.openingType = "trade";
|
||||
return;
|
||||
}
|
||||
|
||||
// If not trade, check the opponent endFrame
|
||||
const oppEndFrame = this.metadata.lastEndFrameByOppIdx[conversion.opponentIndex];
|
||||
const isCounterAttack = oppEndFrame && oppEndFrame > conversion.startFrame;
|
||||
conversion.openingType = isCounterAttack ? 'counter-attack' : 'neutral-win';
|
||||
conversion.openingType = isCounterAttack ? "counter-attack" : "neutral-win";
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -98,11 +98,11 @@ function handleConversionCompute(
|
||||
const playerFrame: PostFrameUpdateType = frame.players[indices.playerIndex].post;
|
||||
// FIXME: use type PostFrameUpdateType instead of any
|
||||
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.playerIndex, 'post'], {});
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.playerIndex, "post"], {});
|
||||
const opponentFrame: PostFrameUpdateType = frame.players[indices.opponentIndex].post;
|
||||
// FIXME: use type PostFrameUpdateType instead of any
|
||||
// This is because the default value {} should not be casted as a type of PostFrameUpdateType
|
||||
const prevOpponentFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.opponentIndex, 'post'], {});
|
||||
const prevOpponentFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.opponentIndex, "post"], {});
|
||||
|
||||
const opntIsDamaged = isDamaged(opponentFrame.actionStateId);
|
||||
const opntIsGrabbed = isGrabbed(opponentFrame.actionStateId);
|
||||
@@ -136,7 +136,7 @@ function handleConversionCompute(
|
||||
endPercent: null,
|
||||
moves: [],
|
||||
didKill: false,
|
||||
openingType: 'unknown', // Will be updated later
|
||||
openingType: "unknown", // Will be updated later
|
||||
};
|
||||
|
||||
conversions.push(state.conversion);
|
||||
|
@@ -1,9 +1,9 @@
|
||||
export * from './common';
|
||||
export * from "./common";
|
||||
|
||||
export { ActionsComputer } from './actions';
|
||||
export { ConversionComputer } from './conversions';
|
||||
export { ComboComputer } from './combos';
|
||||
export { StockComputer } from './stocks';
|
||||
export { InputComputer } from './inputs';
|
||||
export { Stats, StatComputer, StatOptions } from './stats';
|
||||
export { generateOverallStats } from './overall';
|
||||
export { ActionsComputer } from "./actions";
|
||||
export { ConversionComputer } from "./conversions";
|
||||
export { ComboComputer } from "./combos";
|
||||
export { StockComputer } from "./stocks";
|
||||
export { InputComputer } from "./inputs";
|
||||
export { Stats, StatComputer, StatOptions } from "./stats";
|
||||
export { generateOverallStats } from "./overall";
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import { FramesType, FrameEntryType, Frames, PlayerIndexedType } from './common';
|
||||
import _ from "lodash";
|
||||
import { FramesType, FrameEntryType, Frames, PlayerIndexedType } from "./common";
|
||||
|
||||
import { StatComputer } from './stats';
|
||||
import { StatComputer } from "./stats";
|
||||
|
||||
enum JoystickRegion {
|
||||
DZ = 0,
|
||||
@@ -58,7 +58,7 @@ function handleInputCompute(
|
||||
const playerFrame = frame.players[indices.playerIndex].pre;
|
||||
// FIXME: use PreFrameUpdateType instead of any
|
||||
// This is because the default value {} should not be casted as a type of PreFrameUpdateType
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.playerIndex, 'pre'], {});
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.playerIndex, "pre"], {});
|
||||
|
||||
if (playerFrame.frame < Frames.FIRST_PLAYABLE) {
|
||||
// Don't count inputs until the game actually starts
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import { ConversionType, PlayerIndexedType, StockType, OverallType, RatioType } from './common';
|
||||
import { PlayerInput } from './inputs';
|
||||
import _ from "lodash";
|
||||
import { ConversionType, PlayerIndexedType, StockType, OverallType, RatioType } from "./common";
|
||||
import { PlayerInput } from "./inputs";
|
||||
|
||||
interface ConversionsByPlayerByOpening {
|
||||
[playerIndex: string]: {
|
||||
@@ -15,11 +15,11 @@ export function generateOverallStats(
|
||||
conversions: ConversionType[],
|
||||
playableFrameCount: number,
|
||||
): OverallType[] {
|
||||
const inputsByPlayer = _.keyBy(inputs, 'playerIndex');
|
||||
const stocksByPlayer = _.groupBy(stocks, 'playerIndex');
|
||||
const conversionsByPlayer = _.groupBy(conversions, 'playerIndex');
|
||||
const inputsByPlayer = _.keyBy(inputs, "playerIndex");
|
||||
const stocksByPlayer = _.groupBy(stocks, "playerIndex");
|
||||
const conversionsByPlayer = _.groupBy(conversions, "playerIndex");
|
||||
const conversionsByPlayerByOpening: ConversionsByPlayerByOpening = _.mapValues(conversionsByPlayer, (conversions) =>
|
||||
_.groupBy(conversions, 'openingType'),
|
||||
_.groupBy(conversions, "openingType"),
|
||||
);
|
||||
|
||||
const gameMinutes = playableFrameCount / 3600;
|
||||
@@ -28,15 +28,15 @@ export function generateOverallStats(
|
||||
const playerIndex = indices.playerIndex;
|
||||
const opponentIndex = indices.opponentIndex;
|
||||
|
||||
const inputCount = _.get(inputsByPlayer, [playerIndex, 'inputCount']) || 0;
|
||||
const inputCount = _.get(inputsByPlayer, [playerIndex, "inputCount"]) || 0;
|
||||
const conversions = _.get(conversionsByPlayer, playerIndex) || [];
|
||||
const successfulConversions = conversions.filter((conversion) => conversion.moves.length > 1);
|
||||
const opponentStocks = _.get(stocksByPlayer, opponentIndex) || [];
|
||||
const opponentEndedStocks = _.filter(opponentStocks, 'endFrame');
|
||||
const opponentEndedStocks = _.filter(opponentStocks, "endFrame");
|
||||
|
||||
const conversionCount = conversions.length;
|
||||
const successfulConversionCount = successfulConversions.length;
|
||||
const totalDamage = _.sumBy(opponentStocks, 'currentPercent') || 0;
|
||||
const totalDamage = _.sumBy(opponentStocks, "currentPercent") || 0;
|
||||
const killCount = opponentEndedStocks.length;
|
||||
|
||||
return {
|
||||
@@ -51,8 +51,8 @@ export function generateOverallStats(
|
||||
inputsPerMinute: getRatio(inputCount, gameMinutes),
|
||||
openingsPerKill: getRatio(conversionCount, killCount),
|
||||
damagePerOpening: getRatio(totalDamage, conversionCount),
|
||||
neutralWinRatio: getOpeningRatio(conversionsByPlayerByOpening, playerIndex, opponentIndex, 'neutral-win'),
|
||||
counterHitRatio: getOpeningRatio(conversionsByPlayerByOpening, playerIndex, opponentIndex, 'counter-attack'),
|
||||
neutralWinRatio: getOpeningRatio(conversionsByPlayerByOpening, playerIndex, opponentIndex, "neutral-win"),
|
||||
counterHitRatio: getOpeningRatio(conversionsByPlayerByOpening, playerIndex, opponentIndex, "counter-attack"),
|
||||
beneficialTradeRatio: getBeneficialTradeRatio(conversionsByPlayerByOpening, playerIndex, opponentIndex),
|
||||
};
|
||||
});
|
||||
@@ -86,8 +86,8 @@ function getBeneficialTradeRatio(
|
||||
playerIndex: number,
|
||||
opponentIndex: number,
|
||||
): RatioType {
|
||||
const playerTrades = _.get(conversionsByPlayerByOpening, [playerIndex, 'trade']) || [];
|
||||
const opponentTrades = _.get(conversionsByPlayerByOpening, [opponentIndex, 'trade']) || [];
|
||||
const playerTrades = _.get(conversionsByPlayerByOpening, [playerIndex, "trade"]) || [];
|
||||
const opponentTrades = _.get(conversionsByPlayerByOpening, [opponentIndex, "trade"]) || [];
|
||||
|
||||
const benefitsPlayer = [];
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import _ from "lodash";
|
||||
|
||||
import { FrameEntryType, PlayerIndexedType, Frames, FramesType } from './common';
|
||||
import { FrameEntryType, PlayerIndexedType, Frames, FramesType } from "./common";
|
||||
|
||||
export interface StatComputer<T> {
|
||||
setPlayerPermutations(indices: PlayerIndexedType[]): void;
|
||||
@@ -68,8 +68,8 @@ function isCompletedFrame(playerPermutations: PlayerIndexedType[], frame: FrameE
|
||||
// follower frames are not used for any stat calculations so this doesn't matter
|
||||
// for our purposes.
|
||||
const indices = _.first(playerPermutations);
|
||||
const playerPostFrame = _.get(frame, ['players', indices.playerIndex, 'post']);
|
||||
const oppPostFrame = _.get(frame, ['players', indices.opponentIndex, 'post']);
|
||||
const playerPostFrame = _.get(frame, ["players", indices.playerIndex, "post"]);
|
||||
const oppPostFrame = _.get(frame, ["players", indices.opponentIndex, "post"]);
|
||||
|
||||
return Boolean(playerPostFrame && oppPostFrame);
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
// @flow
|
||||
import _ from 'lodash';
|
||||
import _ from "lodash";
|
||||
|
||||
import { FrameEntryType, FramesType, isDead, didLoseStock, PlayerIndexedType, StockType } from './common';
|
||||
import { StatComputer } from './stats';
|
||||
import { FrameEntryType, FramesType, isDead, didLoseStock, PlayerIndexedType, StockType } from "./common";
|
||||
import { StatComputer } from "./stats";
|
||||
|
||||
interface StockState {
|
||||
stock?: StockType | null;
|
||||
@@ -44,7 +44,7 @@ function handleStockCompute(
|
||||
): void {
|
||||
const playerFrame = frame.players[indices.playerIndex].post;
|
||||
// FIXME: use PostFrameUpdateType instead of any
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, 'players', indices.playerIndex, 'post'], {});
|
||||
const prevPlayerFrame: any = _.get(frames, [playerFrame.frame - 1, "players", indices.playerIndex, "post"], {});
|
||||
|
||||
// If there is currently no active stock, wait until the player is no longer spawning.
|
||||
// Once the player is no longer spawning, start the stock
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import _ from "lodash";
|
||||
|
||||
export function toHalfwidth(str: string): string {
|
||||
// Code reference from https://github.com/sampathsris/ascii-fullwidth-halfwidth-convert
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { forEach } from 'lodash';
|
||||
import fs, { WriteStream } from 'fs';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { Writable, WritableOptions } from 'stream';
|
||||
import { forEach } from "lodash";
|
||||
import fs, { WriteStream } from "fs";
|
||||
import moment, { Moment } from "moment";
|
||||
import { Writable, WritableOptions } from "stream";
|
||||
|
||||
const DEFAULT_NICKNAME = 'unknown';
|
||||
const DEFAULT_NICKNAME = "unknown";
|
||||
|
||||
export interface SlpFileMetadata {
|
||||
startTime: Moment;
|
||||
@@ -42,10 +42,10 @@ export class SlpFile extends Writable {
|
||||
};
|
||||
|
||||
this._initializeNewGame(this.filePath);
|
||||
this.on('finish', () => {
|
||||
this.on("finish", () => {
|
||||
// Write bytes written
|
||||
const fd = fs.openSync(this.filePath, 'r+');
|
||||
(fs as any).writeSync(fd, createUInt32Buffer(this.rawDataLength), 0, 'binary', 11);
|
||||
const fd = fs.openSync(this.filePath, "r+");
|
||||
(fs as any).writeSync(fd, createUInt32Buffer(this.rawDataLength), 0, "binary", 11);
|
||||
fs.closeSync(fd);
|
||||
});
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export class SlpFile extends Writable {
|
||||
}
|
||||
|
||||
public _write(chunk: Uint8Array, encoding: string, callback: (error?: Error | null) => void): void {
|
||||
if (encoding !== 'buffer') {
|
||||
if (encoding !== "buffer") {
|
||||
throw new Error(`Unsupported stream encoding. Expected 'buffer' got '${encoding}'.`);
|
||||
}
|
||||
this.fileStream.write(chunk);
|
||||
@@ -81,28 +81,28 @@ export class SlpFile extends Writable {
|
||||
|
||||
private _initializeNewGame(filePath: string): void {
|
||||
this.fileStream = fs.createWriteStream(filePath, {
|
||||
encoding: 'binary',
|
||||
encoding: "binary",
|
||||
});
|
||||
|
||||
const header = Buffer.concat([
|
||||
Buffer.from('{U'),
|
||||
Buffer.from("{U"),
|
||||
Buffer.from([3]),
|
||||
Buffer.from('raw[$U#l'),
|
||||
Buffer.from("raw[$U#l"),
|
||||
Buffer.from([0, 0, 0, 0]),
|
||||
]);
|
||||
this.fileStream.write(header);
|
||||
}
|
||||
|
||||
public _final(callback: (error?: Error | null) => void): void {
|
||||
let footer = Buffer.concat([Buffer.from('U'), Buffer.from([8]), Buffer.from('metadata{')]);
|
||||
let footer = Buffer.concat([Buffer.from("U"), Buffer.from([8]), Buffer.from("metadata{")]);
|
||||
|
||||
// Write game start time
|
||||
const startTimeStr = this.metadata.startTime.toISOString();
|
||||
footer = Buffer.concat([
|
||||
footer,
|
||||
Buffer.from('U'),
|
||||
Buffer.from("U"),
|
||||
Buffer.from([7]),
|
||||
Buffer.from('startAtSU'),
|
||||
Buffer.from("startAtSU"),
|
||||
Buffer.from([startTimeStr.length]),
|
||||
Buffer.from(startTimeStr),
|
||||
]);
|
||||
@@ -112,9 +112,9 @@ export class SlpFile extends Writable {
|
||||
const lastFrame = this.metadata.lastFrame;
|
||||
footer = Buffer.concat([
|
||||
footer,
|
||||
Buffer.from('U'),
|
||||
Buffer.from("U"),
|
||||
Buffer.from([9]),
|
||||
Buffer.from('lastFramel'),
|
||||
Buffer.from("lastFramel"),
|
||||
createInt32Buffer(lastFrame),
|
||||
]);
|
||||
|
||||
@@ -122,29 +122,29 @@ export class SlpFile extends Writable {
|
||||
const consoleNick = this.metadata.consoleNickname || DEFAULT_NICKNAME;
|
||||
footer = Buffer.concat([
|
||||
footer,
|
||||
Buffer.from('U'),
|
||||
Buffer.from("U"),
|
||||
Buffer.from([11]),
|
||||
Buffer.from('consoleNickSU'),
|
||||
Buffer.from("consoleNickSU"),
|
||||
Buffer.from([consoleNick.length]),
|
||||
Buffer.from(consoleNick),
|
||||
]);
|
||||
|
||||
// Start writting player specific data
|
||||
footer = Buffer.concat([footer, Buffer.from('U'), Buffer.from([7]), Buffer.from('players{')]);
|
||||
footer = Buffer.concat([footer, Buffer.from("U"), Buffer.from([7]), Buffer.from("players{")]);
|
||||
const players = this.metadata.players;
|
||||
forEach(players, (player, index) => {
|
||||
// Start player obj with index being the player index
|
||||
footer = Buffer.concat([footer, Buffer.from('U'), Buffer.from([index.length]), Buffer.from(`${index}{`)]);
|
||||
footer = Buffer.concat([footer, Buffer.from("U"), Buffer.from([index.length]), Buffer.from(`${index}{`)]);
|
||||
|
||||
// Start characters key for this player
|
||||
footer = Buffer.concat([footer, Buffer.from('U'), Buffer.from([10]), Buffer.from('characters{')]);
|
||||
footer = Buffer.concat([footer, Buffer.from("U"), Buffer.from([10]), Buffer.from("characters{")]);
|
||||
|
||||
// Write character usage
|
||||
forEach(player.characterUsage, (usage, internalId) => {
|
||||
// Write this character
|
||||
footer = Buffer.concat([
|
||||
footer,
|
||||
Buffer.from('U'),
|
||||
Buffer.from("U"),
|
||||
Buffer.from([internalId.length]),
|
||||
Buffer.from(`${internalId}l`),
|
||||
createUInt32Buffer(usage),
|
||||
@@ -152,24 +152,24 @@ export class SlpFile extends Writable {
|
||||
});
|
||||
|
||||
// Close characters and player
|
||||
footer = Buffer.concat([footer, Buffer.from('}}')]);
|
||||
footer = Buffer.concat([footer, Buffer.from("}}")]);
|
||||
});
|
||||
|
||||
// Close players
|
||||
footer = Buffer.concat([footer, Buffer.from('}')]);
|
||||
footer = Buffer.concat([footer, Buffer.from("}")]);
|
||||
|
||||
// Write played on
|
||||
footer = Buffer.concat([
|
||||
footer,
|
||||
Buffer.from('U'),
|
||||
Buffer.from("U"),
|
||||
Buffer.from([8]),
|
||||
Buffer.from('playedOnSU'),
|
||||
Buffer.from("playedOnSU"),
|
||||
Buffer.from([7]),
|
||||
Buffer.from('network'),
|
||||
Buffer.from("network"),
|
||||
]);
|
||||
|
||||
// Close metadata and file
|
||||
footer = Buffer.concat([footer, Buffer.from('}}')]);
|
||||
footer = Buffer.concat([footer, Buffer.from("}}")]);
|
||||
|
||||
// End the stream
|
||||
this.fileStream.write(footer, callback);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import semver from 'semver';
|
||||
import _ from "lodash";
|
||||
import semver from "semver";
|
||||
|
||||
import {
|
||||
PostFrameUpdateType,
|
||||
@@ -10,17 +10,17 @@ import {
|
||||
ItemUpdateType,
|
||||
FrameBookendType,
|
||||
GameMode,
|
||||
} from '../types';
|
||||
import { FramesType, FrameEntryType, Frames } from '../stats';
|
||||
import { EventEmitter } from 'events';
|
||||
} from "../types";
|
||||
import { FramesType, FrameEntryType, Frames } from "../stats";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
export const MAX_ROLLBACK_FRAMES = 7;
|
||||
|
||||
export enum SlpParserEvent {
|
||||
SETTINGS = 'settings',
|
||||
END = 'end',
|
||||
FRAME = 'frame', // Emitted for every frame
|
||||
FINALIZED_FRAME = 'finalized-frame', // Emitted for only finalized frames
|
||||
SETTINGS = "settings",
|
||||
END = "end",
|
||||
FRAME = "frame", // Emitted for every frame
|
||||
FINALIZED_FRAME = "finalized-frame", // Emitted for only finalized frames
|
||||
}
|
||||
|
||||
export class SlpParser extends EventEmitter {
|
||||
@@ -122,7 +122,7 @@ export class SlpParser extends EventEmitter {
|
||||
|
||||
// Check to see if the file was created after the sheik fix so we know
|
||||
// we don't have to process the first frame of the game for the full settings
|
||||
if (semver.gte(payload.slpVersion, '1.6.0')) {
|
||||
if (semver.gte(payload.slpVersion, "1.6.0")) {
|
||||
this._completeSettings();
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export class SlpParser extends EventEmitter {
|
||||
// Finish calculating settings
|
||||
if (payload.frame <= Frames.FIRST) {
|
||||
const playerIndex = payload.playerIndex;
|
||||
const playersByIndex = _.keyBy(this.settings.players, 'playerIndex');
|
||||
const playersByIndex = _.keyBy(this.settings.players, "playerIndex");
|
||||
|
||||
switch (payload.internalCharacterId) {
|
||||
case 0x7:
|
||||
@@ -153,35 +153,35 @@ export class SlpParser extends EventEmitter {
|
||||
|
||||
private _handleFrameUpdate(command: Command, payload: PreFrameUpdateType | PostFrameUpdateType): void {
|
||||
payload = payload as PostFrameUpdateType;
|
||||
const location = command === Command.PRE_FRAME_UPDATE ? 'pre' : 'post';
|
||||
const field = payload.isFollower ? 'followers' : 'players';
|
||||
const location = command === Command.PRE_FRAME_UPDATE ? "pre" : "post";
|
||||
const field = payload.isFollower ? "followers" : "players";
|
||||
this.latestFrameIndex = payload.frame;
|
||||
_.set(this.frames, [payload.frame, field, payload.playerIndex, location], payload);
|
||||
_.set(this.frames, [payload.frame, 'frame'], payload.frame);
|
||||
_.set(this.frames, [payload.frame, "frame"], payload.frame);
|
||||
|
||||
// If file is from before frame bookending, add frame to stats computer here. Does a little
|
||||
// more processing than necessary, but it works
|
||||
const settings = this.getSettings();
|
||||
if (!settings || semver.lte(settings.slpVersion, '2.2.0')) {
|
||||
if (!settings || semver.lte(settings.slpVersion, "2.2.0")) {
|
||||
this.emit(SlpParserEvent.FRAME, this.frames[payload.frame]);
|
||||
// Finalize the previous frame since no bookending exists
|
||||
this._finalizeFrames(payload.frame - 1);
|
||||
} else {
|
||||
_.set(this.frames, [payload.frame, 'isTransferComplete'], false);
|
||||
_.set(this.frames, [payload.frame, "isTransferComplete"], false);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleItemUpdate(payload: ItemUpdateType): void {
|
||||
const items = _.get(this.frames, [payload.frame, 'items'], []);
|
||||
const items = _.get(this.frames, [payload.frame, "items"], []);
|
||||
items.push(payload);
|
||||
|
||||
// Set items with newest
|
||||
_.set(this.frames, [payload.frame, 'items'], items);
|
||||
_.set(this.frames, [payload.frame, "items"], items);
|
||||
}
|
||||
|
||||
private _handleFrameBookend(payload: FrameBookendType): void {
|
||||
const { frame, latestFinalizedFrame } = payload;
|
||||
_.set(this.frames, [frame, 'isTransferComplete'], true);
|
||||
_.set(this.frames, [frame, "isTransferComplete"], true);
|
||||
// Fire off a normal frame event
|
||||
this.emit(SlpParserEvent.FRAME, this.frames[frame]);
|
||||
|
||||
|
@@ -1,14 +1,14 @@
|
||||
import _ from 'lodash';
|
||||
import fs from 'fs';
|
||||
import iconv from 'iconv-lite';
|
||||
import { decode } from '@shelacek/ubjson';
|
||||
import _ from "lodash";
|
||||
import fs from "fs";
|
||||
import iconv from "iconv-lite";
|
||||
import { decode } from "@shelacek/ubjson";
|
||||
|
||||
import { toHalfwidth } from './fullwidth';
|
||||
import { Command, EventCallbackFunc, EventPayloadTypes, MetadataType, PlayerType } from '../types';
|
||||
import { toHalfwidth } from "./fullwidth";
|
||||
import { Command, EventCallbackFunc, EventPayloadTypes, MetadataType, PlayerType } from "../types";
|
||||
|
||||
export enum SlpInputSource {
|
||||
BUFFER = 'buffer',
|
||||
FILE = 'file',
|
||||
BUFFER = "buffer",
|
||||
FILE = "file",
|
||||
}
|
||||
|
||||
export interface SlpReadInput {
|
||||
@@ -43,7 +43,7 @@ export interface SlpBufferSourceRef {
|
||||
function getRef(input: SlpReadInput): SlpRefType {
|
||||
switch (input.source) {
|
||||
case SlpInputSource.FILE:
|
||||
const fd = fs.openSync(input.filePath, 'r');
|
||||
const fd = fs.openSync(input.filePath, "r");
|
||||
return {
|
||||
source: input.source,
|
||||
fileDescriptor: fd,
|
||||
@@ -54,7 +54,7 @@ function getRef(input: SlpReadInput): SlpRefType {
|
||||
buffer: input.buffer,
|
||||
} as SlpBufferSourceRef;
|
||||
default:
|
||||
throw new Error('Source type not supported');
|
||||
throw new Error("Source type not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ function readRef(ref: SlpRefType, buffer: Uint8Array, offset: number, length: nu
|
||||
case SlpInputSource.BUFFER:
|
||||
return (ref as SlpBufferSourceRef).buffer.copy(buffer, offset, position, position + length);
|
||||
default:
|
||||
throw new Error('Source type not supported');
|
||||
throw new Error("Source type not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ function getLenRef(ref: SlpRefType): number {
|
||||
case SlpInputSource.BUFFER:
|
||||
return (ref as SlpBufferSourceRef).buffer.length;
|
||||
default:
|
||||
throw new Error('Source type not supported');
|
||||
throw new Error("Source type not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ function getRawDataPosition(ref: SlpRefType): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buffer[0] !== '{'.charCodeAt(0)) {
|
||||
if (buffer[0] !== "{".charCodeAt(0)) {
|
||||
return 0; // return error?
|
||||
}
|
||||
|
||||
@@ -244,13 +244,13 @@ export function parseMessage(command: Command, payload: Uint8Array): EventPayloa
|
||||
const cfOffset = playerIndex * 0x8;
|
||||
const dashback = readUint32(view, 0x141 + cfOffset);
|
||||
const shieldDrop = readUint32(view, 0x145 + cfOffset);
|
||||
let cfOption = 'None';
|
||||
let cfOption = "None";
|
||||
if (dashback !== shieldDrop) {
|
||||
cfOption = 'Mixed';
|
||||
cfOption = "Mixed";
|
||||
} else if (dashback === 1) {
|
||||
cfOption = 'UCF';
|
||||
cfOption = "UCF";
|
||||
} else if (dashback === 2) {
|
||||
cfOption = 'Dween';
|
||||
cfOption = "Dween";
|
||||
}
|
||||
|
||||
// Nametag stuff
|
||||
@@ -259,8 +259,8 @@ export function parseMessage(command: Command, payload: Uint8Array): EventPayloa
|
||||
const nametagBuf = payload.slice(nametagStart, nametagStart + 16);
|
||||
const nametag = toHalfwidth(
|
||||
iconv
|
||||
.decode(nametagBuf as Buffer, 'Shift_JIS')
|
||||
.split('\0')
|
||||
.decode(nametagBuf as Buffer, "Shift_JIS")
|
||||
.split("\0")
|
||||
.shift(),
|
||||
);
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { Writable, WritableOptions } from 'stream';
|
||||
import { Command, EventPayloadTypes } from '../types';
|
||||
import { parseMessage } from './slpReader';
|
||||
import { NETWORK_MESSAGE } from '../console/connection';
|
||||
import { Writable, WritableOptions } from "stream";
|
||||
import { Command, EventPayloadTypes } from "../types";
|
||||
import { parseMessage } from "./slpReader";
|
||||
import { NETWORK_MESSAGE } from "../console/connection";
|
||||
|
||||
export enum SlpStreamMode {
|
||||
AUTO = 'AUTO', // Always reading data, but errors on invalid command
|
||||
MANUAL = 'MANUAL', // Stops parsing inputs after a valid game end command, requires manual restarting
|
||||
AUTO = "AUTO", // Always reading data, but errors on invalid command
|
||||
MANUAL = "MANUAL", // Stops parsing inputs after a valid game end command, requires manual restarting
|
||||
}
|
||||
|
||||
const defaultSettings = {
|
||||
@@ -28,8 +28,8 @@ export interface SlpRawEventPayload {
|
||||
}
|
||||
|
||||
export enum SlpStreamEvent {
|
||||
RAW = 'slp-raw',
|
||||
COMMAND = 'slp-command',
|
||||
RAW = "slp-raw",
|
||||
COMMAND = "slp-command",
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ export class SlpStream extends Writable {
|
||||
}
|
||||
|
||||
public _write(newData: Buffer, encoding: string, callback: (error?: Error | null, data?: any) => void): void {
|
||||
if (encoding !== 'buffer') {
|
||||
if (encoding !== "buffer") {
|
||||
throw new Error(`Unsupported stream encoding. Expected 'buffer' got '${encoding}'.`);
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,19 @@
|
||||
import _ from 'lodash';
|
||||
import _ from "lodash";
|
||||
// import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { SlippiGame } from '../src';
|
||||
import fs from "fs";
|
||||
import { SlippiGame } from "../src";
|
||||
|
||||
it('should correctly return game settings', () => {
|
||||
const game = new SlippiGame('slp/sheik_vs_ics_yoshis.slp');
|
||||
it("should correctly return game settings", () => {
|
||||
const game = new SlippiGame("slp/sheik_vs_ics_yoshis.slp");
|
||||
const settings = game.getSettings();
|
||||
expect(settings.stageId).toBe(8);
|
||||
expect(_.first(settings.players).characterId).toBe(0x13);
|
||||
expect(_.last(settings.players).characterId).toBe(0xe);
|
||||
expect(settings.slpVersion).toBe('0.1.0');
|
||||
expect(settings.slpVersion).toBe("0.1.0");
|
||||
});
|
||||
|
||||
it('should correctly return stats', () => {
|
||||
const game = new SlippiGame('slp/test.slp');
|
||||
it("should correctly return stats", () => {
|
||||
const game = new SlippiGame("slp/test.slp");
|
||||
const stats = game.getStats();
|
||||
expect(stats.lastFrame).toBe(3694);
|
||||
|
||||
@@ -39,56 +39,56 @@ it('should correctly return stats', () => {
|
||||
expect(stats.overall[0].inputCount).toBe(494);
|
||||
});
|
||||
|
||||
it('should correctly return metadata', () => {
|
||||
const game = new SlippiGame('slp/test.slp');
|
||||
it("should correctly return metadata", () => {
|
||||
const game = new SlippiGame("slp/test.slp");
|
||||
const metadata = game.getMetadata();
|
||||
expect(metadata.startAt).toBe('2017-12-18T21:14:14Z');
|
||||
expect(metadata.playedOn).toBe('dolphin');
|
||||
expect(metadata.startAt).toBe("2017-12-18T21:14:14Z");
|
||||
expect(metadata.playedOn).toBe("dolphin");
|
||||
});
|
||||
|
||||
it('should be able to read incomplete SLP files', () => {
|
||||
const game = new SlippiGame('slp/incomplete.slp');
|
||||
it("should be able to read incomplete SLP files", () => {
|
||||
const game = new SlippiGame("slp/incomplete.slp");
|
||||
const settings = game.getSettings();
|
||||
expect(settings.players.length).toBe(2);
|
||||
game.getMetadata();
|
||||
game.getStats();
|
||||
});
|
||||
|
||||
it('should be able to read nametags', () => {
|
||||
const game = new SlippiGame('slp/nametags.slp');
|
||||
it("should be able to read nametags", () => {
|
||||
const game = new SlippiGame("slp/nametags.slp");
|
||||
const settings = game.getSettings();
|
||||
expect(settings.players[0].nametag).toBe('AMNイ');
|
||||
expect(settings.players[1].nametag).toBe('');
|
||||
expect(settings.players[0].nametag).toBe("AMNイ");
|
||||
expect(settings.players[1].nametag).toBe("");
|
||||
|
||||
const game2 = new SlippiGame('slp/nametags2.slp');
|
||||
const game2 = new SlippiGame("slp/nametags2.slp");
|
||||
const settings2 = game2.getSettings();
|
||||
expect(settings2.players[0].nametag).toBe('A1=$');
|
||||
expect(settings2.players[1].nametag).toBe('か、9@');
|
||||
expect(settings2.players[0].nametag).toBe("A1=$");
|
||||
expect(settings2.players[1].nametag).toBe("か、9@");
|
||||
|
||||
const game3 = new SlippiGame('slp/nametags3.slp');
|
||||
const game3 = new SlippiGame("slp/nametags3.slp");
|
||||
const settings3 = game3.getSettings();
|
||||
expect(settings3.players[0].nametag).toBe('B R');
|
||||
expect(settings3.players[1].nametag).toBe('. 。');
|
||||
expect(settings3.players[0].nametag).toBe("B R");
|
||||
expect(settings3.players[1].nametag).toBe(". 。");
|
||||
});
|
||||
|
||||
it('should support PAL version', () => {
|
||||
const palGame = new SlippiGame('slp/pal.slp');
|
||||
const ntscGame = new SlippiGame('slp/ntsc.slp');
|
||||
it("should support PAL version", () => {
|
||||
const palGame = new SlippiGame("slp/pal.slp");
|
||||
const ntscGame = new SlippiGame("slp/ntsc.slp");
|
||||
|
||||
expect(palGame.getSettings().isPAL).toBe(true);
|
||||
expect(ntscGame.getSettings().isPAL).toBe(false);
|
||||
});
|
||||
|
||||
it('should correctly distinguish between different controller fixes', () => {
|
||||
const game = new SlippiGame('slp/controllerFixes.slp');
|
||||
it("should correctly distinguish between different controller fixes", () => {
|
||||
const game = new SlippiGame("slp/controllerFixes.slp");
|
||||
const settings = game.getSettings();
|
||||
expect(settings.players[0].controllerFix).toBe('Dween');
|
||||
expect(settings.players[1].controllerFix).toBe('UCF');
|
||||
expect(settings.players[2].controllerFix).toBe('None');
|
||||
expect(settings.players[0].controllerFix).toBe("Dween");
|
||||
expect(settings.players[1].controllerFix).toBe("UCF");
|
||||
expect(settings.players[2].controllerFix).toBe("None");
|
||||
});
|
||||
|
||||
it('should be able to support reading from a buffer input', () => {
|
||||
const buf = fs.readFileSync('slp/sheik_vs_ics_yoshis.slp');
|
||||
it("should be able to support reading from a buffer input", () => {
|
||||
const buf = fs.readFileSync("slp/sheik_vs_ics_yoshis.slp");
|
||||
const game = new SlippiGame(buf);
|
||||
const settings = game.getSettings();
|
||||
expect(settings.stageId).toBe(8);
|
||||
@@ -96,7 +96,7 @@ it('should be able to support reading from a buffer input', () => {
|
||||
expect(_.last(settings.players).characterId).toBe(0xe);
|
||||
});
|
||||
|
||||
it.skip('should support item information extraction', () => {
|
||||
it.skip("should support item information extraction", () => {
|
||||
// const game = new SlippiGame("slp/itemExport.slp");
|
||||
// const frames = game.getFrames();
|
||||
// TODO: Add test
|
||||
@@ -121,8 +121,8 @@ it.skip('should support item information extraction', () => {
|
||||
// console.log(frames[429].items);
|
||||
});
|
||||
|
||||
it('should support realtime parsing', () => {
|
||||
const fullData = fs.readFileSync('slp/realtimeTest.slp');
|
||||
it("should support realtime parsing", () => {
|
||||
const fullData = fs.readFileSync("slp/realtimeTest.slp");
|
||||
const buf = Buffer.alloc(100e6); // Allocate 100 MB of space
|
||||
const game = new SlippiGame(buf);
|
||||
|
||||
@@ -175,7 +175,7 @@ it('should support realtime parsing', () => {
|
||||
// Load metadata
|
||||
copyBuf(0xa7);
|
||||
data = getData();
|
||||
expect(data.metadata.playedOn).toBe('network');
|
||||
expect(data.metadata.playedOn).toBe("network");
|
||||
});
|
||||
|
||||
// it('test speedReadTest', () => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import fs from 'fs';
|
||||
import { Writable } from 'stream';
|
||||
import fs from "fs";
|
||||
import { Writable } from "stream";
|
||||
|
||||
import SlippiGame, {
|
||||
Command,
|
||||
@@ -14,11 +14,11 @@ import SlippiGame, {
|
||||
FrameEntryType,
|
||||
MAX_ROLLBACK_FRAMES,
|
||||
GameMode,
|
||||
} from '../src';
|
||||
} from "../src";
|
||||
|
||||
describe('when reading last finalised frame from SlpStream', () => {
|
||||
it('should never decrease', async () => {
|
||||
const testFile = 'slp/finalizedFrame.slp';
|
||||
describe("when reading last finalised frame from SlpStream", () => {
|
||||
it("should never decrease", async () => {
|
||||
const testFile = "slp/finalizedFrame.slp";
|
||||
const stream = new SlpStream({
|
||||
mode: SlpStreamMode.MANUAL,
|
||||
});
|
||||
@@ -51,9 +51,9 @@ describe('when reading last finalised frame from SlpStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when reading finalised frames from SlpParser', () => {
|
||||
it('should support older SLP files without frame bookend', async () => {
|
||||
const testFile = 'slp/sheik_vs_ics_yoshis.slp';
|
||||
describe("when reading finalised frames from SlpParser", () => {
|
||||
it("should support older SLP files without frame bookend", async () => {
|
||||
const testFile = "slp/sheik_vs_ics_yoshis.slp";
|
||||
const stream = new SlpStream({
|
||||
mode: SlpStreamMode.MANUAL,
|
||||
});
|
||||
@@ -87,8 +87,8 @@ describe('when reading finalised frames from SlpParser', () => {
|
||||
expect(lastFrame).toEqual(lastFinalizedFrame);
|
||||
});
|
||||
|
||||
it('should only increase', async () => {
|
||||
const testFile = 'slp/finalizedFrame.slp';
|
||||
it("should only increase", async () => {
|
||||
const testFile = "slp/finalizedFrame.slp";
|
||||
const stream = new SlpStream({
|
||||
mode: SlpStreamMode.MANUAL,
|
||||
});
|
||||
@@ -123,10 +123,10 @@ describe('when reading finalised frames from SlpParser', () => {
|
||||
const pipeFileContents = async (filename: string, destination: Writable, options?: any): Promise<void> => {
|
||||
return new Promise((resolve): void => {
|
||||
const readStream = fs.createReadStream(filename);
|
||||
readStream.on('open', () => {
|
||||
readStream.on("open", () => {
|
||||
readStream.pipe(destination, options);
|
||||
});
|
||||
readStream.on('close', () => {
|
||||
readStream.on("close", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user