Skip to content

Commit

Permalink
Revert "Doubles Stats (#54)"
Browse files Browse the repository at this point in the history
This reverts commit 3de9791.
  • Loading branch information
vinceau committed May 10, 2021
1 parent f0509ee commit 5c55aa8
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 256 deletions.
4 changes: 3 additions & 1 deletion src/SlippiGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ComboComputer,
ConversionComputer,
generateOverallStats,
getSinglesPlayerPermutationsFromSettings,
InputComputer,
StatOptions,
Stats,
Expand Down Expand Up @@ -130,8 +131,9 @@ export class SlippiGame {
const inputs = this.inputComputer.fetch();
const stocks = this.stockComputer.fetch();
const conversions = this.conversionComputer.fetch();
const indices = getSinglesPlayerPermutationsFromSettings(settings);
const playableFrames = this.parser.getPlayableFrameCount();
const overall = generateOverallStats(settings, inputs, stocks, conversions, playableFrames);
const overall = generateOverallStats(indices, inputs, stocks, conversions, playableFrames);

const stats = {
lastFrame: this.parser.getLatestFrameNumber(),
Expand Down
27 changes: 13 additions & 14 deletions src/stats/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import _ from "lodash";

import { FrameEntryType, GameStartType } from "../types";
import { ActionCountsType, State } from "./common";
import { ActionCountsType, getSinglesPlayerPermutationsFromSettings, PlayerIndexedType, State } from "./common";
import { StatComputer } from "./stats";

// Frame pattern that indicates a dash dance turn was executed
Expand All @@ -13,17 +13,16 @@ interface PlayerActionState {
}

export class ActionsComputer implements StatComputer<ActionCountsType[]> {
private playerIndices: number[] = [];
private state = new Map<number, PlayerActionState>();
private playerPermutations = new Array<PlayerIndexedType>();
private state = new Map<PlayerIndexedType, PlayerActionState>();

public setup(settings: GameStartType): void {
// Reset the state
this.state = new Map();

this.playerIndices = settings.players.map((p) => p.playerIndex);
this.playerIndices.forEach((playerIndex) => {
this.playerPermutations = getSinglesPlayerPermutationsFromSettings(settings);
this.playerPermutations.forEach((indices) => {
const playerCounts: ActionCountsType = {
playerIndex,
playerIndex: indices.playerIndex,
opponentIndex: indices.opponentIndex,
wavedashCount: 0,
wavelandCount: 0,
airDodgeCount: 0,
Expand All @@ -50,15 +49,15 @@ export class ActionsComputer implements StatComputer<ActionCountsType[]> {
playerCounts: playerCounts,
animations: [],
};
this.state.set(playerIndex, playerState);
this.state.set(indices, playerState);
});
}

public processFrame(frame: FrameEntryType): void {
this.playerIndices.forEach((index) => {
const state = this.state.get(index);
this.playerPermutations.forEach((indices) => {
const state = this.state.get(indices);
if (state) {
handleActionCompute(state, index, frame);
handleActionCompute(state, indices, frame);
}
});
}
Expand Down Expand Up @@ -123,8 +122,8 @@ function didStartLedgegrab(currentAnimation: State, previousAnimation: State): b
return isCurrentlyGrabbingLedge && !wasPreviouslyGrabbingLedge;
}

function handleActionCompute(state: PlayerActionState, playerIndex: number, frame: FrameEntryType): void {
const playerFrame = frame.players[playerIndex]!.post;
function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedType, frame: FrameEntryType): void {
const playerFrame = frame.players[indices.playerIndex]!.post;
const incrementCount = (field: string, condition: boolean): void => {
if (!condition) {
return;
Expand Down
99 changes: 45 additions & 54 deletions src/stats/combos.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { EventEmitter } from "events";
import { last } from "lodash";
import _ from "lodash";

import { FrameEntryType, FramesType, GameStartType, PostFrameUpdateType } from "../types";
import {
calcDamageTaken,
ComboType,
didLoseStock,
getSinglesPlayerPermutationsFromSettings,
isCommandGrabbed,
isDamaged,
isDead,
isDown,
isGrabbed,
isTeching,
MoveLandedType,
PlayerIndexedType,
Timers,
} from "./common";
import { StatComputer } from "./stats";
Expand All @@ -32,19 +34,19 @@ interface ComboState {
}

export class ComboComputer extends EventEmitter implements StatComputer<ComboType[]> {
private playerIndices: number[] = [];
private combos: ComboType[] = [];
private state = new Map<number, ComboState>();
private playerPermutations = new Array<PlayerIndexedType>();
private state = new Map<PlayerIndexedType, ComboState>();
private combos = new Array<ComboType>();
private settings: GameStartType | null = null;

public setup(settings: GameStartType): void {
// Reset the state
this.settings = settings;
this.state = new Map();
this.combos = [];
this.playerPermutations = getSinglesPlayerPermutationsFromSettings(settings);

this.playerIndices = settings.players.map((p) => p.playerIndex);
this.playerIndices.forEach((indices) => {
this.playerPermutations.forEach((indices) => {
const playerState: ComboState = {
combo: null,
move: null,
Expand All @@ -57,15 +59,15 @@ export class ComboComputer extends EventEmitter implements StatComputer<ComboTyp
}

public processFrame(frame: FrameEntryType, allFrames: FramesType): void {
this.playerIndices.forEach((index) => {
const state = this.state.get(index);
this.playerPermutations.forEach((indices) => {
const state = this.state.get(indices);
if (state) {
handleComboCompute(allFrames, state, index, frame, this.combos);
handleComboCompute(allFrames, state, indices, frame, this.combos);

// Emit an event for the new combo
if (state.event !== null) {
this.emit(state.event, {
combo: last(this.combos),
combo: _.last(this.combos),
settings: this.settings,
});
state.event = null;
Expand All @@ -82,20 +84,29 @@ export class ComboComputer extends EventEmitter implements StatComputer<ComboTyp
function handleComboCompute(
frames: FramesType,
state: ComboState,
playerIndex: number,
indices: PlayerIndexedType,
frame: FrameEntryType,
combos: ComboType[],
): void {
const currentFrameNumber = frame.frame;
const playerFrame = frame.players[playerIndex]!.post;
const playerFrame = frame.players[indices.playerIndex]!.post;
const opponentFrame = frame.players[indices.opponentIndex]!.post;

const prevFrameNumber = currentFrameNumber - 1;
let prevPlayerFrame: PostFrameUpdateType | null = null;
let prevOpponentFrame: PostFrameUpdateType | null = null;

if (frames[prevFrameNumber]) {
prevPlayerFrame = frames[prevFrameNumber].players[playerIndex]!.post;
prevPlayerFrame = frames[prevFrameNumber].players[indices.playerIndex]!.post;
prevOpponentFrame = frames[prevFrameNumber].players[indices.opponentIndex]!.post;
}

const oppActionStateId = opponentFrame.actionStateId!;
const opntIsDamaged = isDamaged(oppActionStateId);
const opntIsGrabbed = isGrabbed(oppActionStateId);
const opntIsCommandGrabbed = isCommandGrabbed(oppActionStateId);
const opntDamageTaken = prevOpponentFrame ? calcDamageTaken(opponentFrame, prevOpponentFrame) : 0;

// Keep track of whether actionState changes after a hit. Used to compute move count
// When purely using action state there was a bug where if you did two of the same
// move really fast (such as ganon's jab), it would count as one move. Added
Expand All @@ -110,27 +121,21 @@ function handleComboCompute(
state.lastHitAnimation = null;
}

const playerActionStateId = playerFrame.actionStateId!;
const playerIsDamaged = isDamaged(playerActionStateId);
const playerIsGrabbed = isGrabbed(playerActionStateId);
const playerIsCommandGrabbed = isCommandGrabbed(playerActionStateId);

// If the player took damage and was put in some kind of stun this frame, either
// If opponent took damage and was put in some kind of stun this frame, either
// start a combo or count the moves for the existing combo
if (playerIsDamaged || playerIsGrabbed || playerIsCommandGrabbed) {
if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed) {
let comboStarted = false;

if (!state.combo) {
state.combo = {
playerIndex,
playerIndex: indices.playerIndex,
opponentIndex: indices.opponentIndex,
startFrame: currentFrameNumber,
endFrame: null,
startPercent: prevPlayerFrame ? prevPlayerFrame.percent ?? 0 : 0,
currentPercent: playerFrame.percent ?? 0,
startPercent: prevOpponentFrame ? prevOpponentFrame.percent ?? 0 : 0,
currentPercent: opponentFrame.percent ?? 0,
endPercent: null,
moves: [],
didKill: false,
lastHitBy: null,
};

combos.push(state.combo);
Expand All @@ -139,22 +144,15 @@ function handleComboCompute(
comboStarted = true;
}

const playerDamageTaken = prevPlayerFrame ? calcDamageTaken(playerFrame, prevPlayerFrame) : 0;
const lastHitBy = playerFrame.lastHitBy;
const validLastHitBy = lastHitBy !== null && lastHitBy >= 0 && lastHitBy <= 3;
if (playerDamageTaken && lastHitBy !== null && validLastHitBy) {
// Update who hit us last
state.combo.lastHitBy = lastHitBy;

if (opntDamageTaken) {
// If animation of last hit has been cleared that means this is a new move. This
// prevents counting multiple hits from the same move such as fox's drill
if (state.lastHitAnimation === null) {
state.move = {
frame: currentFrameNumber,
moveId: frame.players[lastHitBy]!.post!.lastAttackLanded!,
moveId: playerFrame.lastAttackLanded!,
hitCount: 0,
damage: 0,
playerIndex: lastHitBy,
};

state.combo.moves.push(state.move);
Expand All @@ -167,7 +165,7 @@ function handleComboCompute(

if (state.move) {
state.move.hitCount += 1;
state.move.damage += playerDamageTaken;
state.move.damage += opntDamageTaken;
}

// Store previous frame animation to consider the case of a trade, the previous
Expand All @@ -186,34 +184,27 @@ function handleComboCompute(
return;
}

const playerIsTeching = isTeching(playerActionStateId);
const playerIsDowned = isDown(playerActionStateId);
const playerDidLoseStock = prevPlayerFrame && didLoseStock(playerFrame, prevPlayerFrame);
const playerIsDying = isDead(playerActionStateId);
const opntIsTeching = isTeching(oppActionStateId);
const opntIsDowned = isDown(oppActionStateId);
const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame);
const opntIsDying = isDead(oppActionStateId);

// Update percent if the player didn't lose stock
if (!playerDidLoseStock) {
state.combo.currentPercent = playerFrame.percent ?? 0;
// Update percent if opponent didn't lose stock
if (!opntDidLoseStock) {
state.combo.currentPercent = opponentFrame.percent ?? 0;
}

if (
playerIsDamaged ||
playerIsGrabbed ||
playerIsCommandGrabbed ||
playerIsTeching ||
playerIsDowned ||
playerIsDying
) {
// If the player got grabbed or damaged, reset the reset counter
if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed || opntIsTeching || opntIsDowned || opntIsDying) {
// If opponent got grabbed or damaged, reset the reset counter
state.resetCounter = 0;
} else {
state.resetCounter += 1;
}

let shouldTerminate = false;

// Termination condition 1 - player was killed
if (playerDidLoseStock) {
// Termination condition 1 - player kills opponent
if (opntDidLoseStock) {
state.combo.didKill = true;
shouldTerminate = true;
}
Expand All @@ -226,7 +217,7 @@ function handleComboCompute(
// If combo should terminate, mark the end states and add it to list
if (shouldTerminate) {
state.combo.endFrame = playerFrame.frame;
state.combo.endPercent = prevPlayerFrame ? prevPlayerFrame.percent ?? 0 : 0;
state.combo.endPercent = prevOpponentFrame ? prevOpponentFrame.percent ?? 0 : 0;
state.event = ComboEvent.COMBO_END;

state.combo = null;
Expand Down
Loading

0 comments on commit 5c55aa8

Please sign in to comment.