Skip to content

Commit

Permalink
Merge 2c7d56b into 8534e7f
Browse files Browse the repository at this point in the history
  • Loading branch information
NickCondron committed May 3, 2022
2 parents 8534e7f + 2c7d56b commit 701e1d6
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 82 deletions.
Binary file added slp/actionEdgeCases.slp
Binary file not shown.
Binary file added slp/gnwActions.slp
Binary file not shown.
182 changes: 102 additions & 80 deletions src/stats/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const dashDanceAnimations = [State.DASH, State.TURN, State.DASH];
interface PlayerActionState {
playerCounts: ActionCountsType;
animations: number[];
actionFrameCounters: number[];
}

export class ActionsComputer implements StatComputer<ActionCountsType[]> {
Expand All @@ -34,6 +35,24 @@ export class ActionsComputer implements StatComputer<ActionCountsType[]> {
success: 0,
fail: 0,
},
attackCount: {
jab1: 0,
jab2: 0,
jab3: 0,
jabm: 0,
dash: 0,
ftilt: 0,
utilt: 0,
dtilt: 0,
fsmash: 0,
usmash: 0,
dsmash: 0,
nair: 0,
fair: 0,
bair: 0,
uair: 0,
dair: 0,
},
grabCount: {
success: 0,
fail: 0,
Expand All @@ -59,6 +78,7 @@ export class ActionsComputer implements StatComputer<ActionCountsType[]> {
const playerState: PlayerActionState = {
playerCounts: playerCounts,
animations: [],
actionFrameCounters: [],
};
this.state.set(indices, playerState);
});
Expand All @@ -78,63 +98,33 @@ export class ActionsComputer implements StatComputer<ActionCountsType[]> {
}
}

function didMissGroundTech(animation: State): boolean {
function isMissGroundTech(animation: State): boolean {
return animation === State.TECH_MISS_DOWN || animation === State.TECH_MISS_UP;
}

function isRolling(animation: State): boolean {
return animation === State.ROLL_BACKWARD || animation === State.ROLL_FORWARD;
}

function didStartRoll(currentAnimation: number, previousAnimation: number): boolean {
const isCurrentlyRolling = isRolling(currentAnimation);
const wasPreviouslyRolling = isRolling(previousAnimation);

return isCurrentlyRolling && !wasPreviouslyRolling;
}

function isSpotDodging(animation: State): boolean {
return animation === State.SPOT_DODGE;
}

function didStartGrabSuccess(currentAnimation: State, previousAnimation: State): boolean {
return previousAnimation === State.GRAB && currentAnimation <= State.GRAB_WAIT && currentAnimation > State.GRAB;
}
function didStartGrabFail(currentAnimation: State, previousAnimation: State): boolean {
return previousAnimation === State.GRAB && (currentAnimation > State.GRAB_WAIT || currentAnimation < State.GRAB);
}

function didStartSpotDodge(currentAnimation: State, previousAnimation: State): boolean {
const isCurrentlyDodging = isSpotDodging(currentAnimation);
const wasPreviouslyDodging = isSpotDodging(previousAnimation);

return isCurrentlyDodging && !wasPreviouslyDodging;
}

function isAirDodging(animation: State): boolean {
return animation === State.AIR_DODGE;
}

function didStartAirDodge(currentAnimation: State, previousAnimation: State): boolean {
const isCurrentlyDodging = isAirDodging(currentAnimation);
const wasPreviouslyDodging = isAirDodging(previousAnimation);

return isCurrentlyDodging && !wasPreviouslyDodging;
function isGrabAction(animation: State): boolean {
// Includes Grab pull, wait, pummel, and throws
return animation > State.GRAB && animation <= State.THROW_DOWN && animation !== State.DASH_GRAB;
}

function isGrabbingLedge(animation: State): boolean {
return animation === State.CLIFF_CATCH;
function isGrabbing(animation: State): boolean {
return animation === State.GRAB || animation === State.DASH_GRAB;
}

function isAerialAttack(animation: State): boolean {
return animation >= State.AERIAL_ATTACK_START && animation <= State.AERIAL_ATTACK_END;
}

function didStartLedgegrab(currentAnimation: State, previousAnimation: State): boolean {
const isCurrentlyGrabbingLedge = isGrabbingLedge(currentAnimation);
const wasPreviouslyGrabbingLedge = isGrabbingLedge(previousAnimation);
function isForwardTilt(animation: State): boolean {
return animation >= State.ATTACK_FTILT_START && animation <= State.ATTACK_FTILT_END;
}

return isCurrentlyGrabbingLedge && !wasPreviouslyGrabbingLedge;
function isForwardSmash(animation: State): boolean {
return animation >= State.ATTACK_FSMASH_START && animation <= State.ATTACK_FSMASH_END;
}

function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedType, frame: FrameEntryType): void {
Expand All @@ -152,60 +142,92 @@ function handleActionCompute(state: PlayerActionState, indices: PlayerIndexedTyp
// Manage animation state
const currentAnimation = playerFrame.actionStateId!;
state.animations.push(currentAnimation);
const currentFrameCounter = playerFrame.actionStateCounter!;
state.actionFrameCounters.push(currentFrameCounter);

// Grab last 3 frames
const last3Frames = state.animations.slice(-3);
const prevAnimation = last3Frames[last3Frames.length - 2] as number;
const newAnimation = currentAnimation !== prevAnimation;
const prevFrameCounter = state.actionFrameCounters[state.actionFrameCounters.length - 2] as number;

// New action if new animation or frame counter goes back down (repeated action)
const isNewAction =
currentAnimation !== prevAnimation ||
(currentAnimation === prevAnimation && prevFrameCounter > currentFrameCounter);

if (!isNewAction) {
return;
}

// Increment counts based on conditions
const didDashDance = isEqual(last3Frames, dashDanceAnimations);
incrementCount("dashDanceCount", didDashDance);

const didRoll = didStartRoll(currentAnimation, prevAnimation);
incrementCount("rollCount", didRoll);

const didSpotDodge = didStartSpotDodge(currentAnimation, prevAnimation);
incrementCount("spotDodgeCount", didSpotDodge);

const didAirDodge = didStartAirDodge(currentAnimation, prevAnimation);
incrementCount("airDodgeCount", didAirDodge);
incrementCount("rollCount", isRolling(currentAnimation));
incrementCount("spotDodgeCount", currentAnimation === State.SPOT_DODGE);
incrementCount("airDodgeCount", currentAnimation === State.AIR_DODGE);
incrementCount("ledgegrabCount", currentAnimation === State.CLIFF_CATCH);

const didGrabLedge = didStartLedgegrab(currentAnimation, prevAnimation);
incrementCount("ledgegrabCount", didGrabLedge);
// Grabs
incrementCount("grabCount.success", isGrabbing(prevAnimation) && isGrabAction(currentAnimation));
incrementCount("grabCount.fail", isGrabbing(prevAnimation) && !isGrabAction(currentAnimation));
if (currentAnimation === State.DASH_GRAB && prevAnimation === State.ATTACK_DASH) {
state.playerCounts.attackCount.dash -= 1; // subtract from dash attack if boost grab
}

const didGrabSucceed = didStartGrabSuccess(currentAnimation, prevAnimation);
incrementCount("grabCount.success", didGrabSucceed);
const didGrabFail = didStartGrabFail(currentAnimation, prevAnimation);
incrementCount("grabCount.fail", didGrabFail);
// Basic attacks
incrementCount("attackCount.jab1", currentAnimation === State.ATTACK_JAB1);
incrementCount("attackCount.jab2", currentAnimation === State.ATTACK_JAB2);
incrementCount("attackCount.jab3", currentAnimation === State.ATTACK_JAB3);
incrementCount("attackCount.jabm", currentAnimation === State.ATTACK_JABM);
incrementCount("attackCount.dash", currentAnimation === State.ATTACK_DASH);
incrementCount("attackCount.ftilt", isForwardTilt(currentAnimation));
incrementCount("attackCount.utilt", currentAnimation === State.ATTACK_UTILT);
incrementCount("attackCount.dtilt", currentAnimation === State.ATTACK_DTILT);
incrementCount("attackCount.fsmash", isForwardSmash(currentAnimation));
incrementCount("attackCount.usmash", currentAnimation === State.ATTACK_USMASH);
incrementCount("attackCount.dsmash", currentAnimation === State.ATTACK_DSMASH);
incrementCount("attackCount.nair", currentAnimation === State.AERIAL_NAIR);
incrementCount("attackCount.fair", currentAnimation === State.AERIAL_FAIR);
incrementCount("attackCount.bair", currentAnimation === State.AERIAL_BAIR);
incrementCount("attackCount.uair", currentAnimation === State.AERIAL_UAIR);
incrementCount("attackCount.dair", currentAnimation === State.AERIAL_DAIR);

// GnW is weird and has unique IDs for some moves
if (playerFrame.internalCharacterId === 0x18) {
incrementCount("attackCount.jab1", currentAnimation === State.GNW_JAB1);
incrementCount("attackCount.jabm", currentAnimation === State.GNW_JABM);
incrementCount("attackCount.dtilt", currentAnimation === State.GNW_DTILT);
incrementCount("attackCount.fsmash", currentAnimation === State.GNW_FSMASH);
incrementCount("attackCount.nair", currentAnimation === State.GNW_NAIR);
incrementCount("attackCount.bair", currentAnimation === State.GNW_BAIR);
incrementCount("attackCount.uair", currentAnimation === State.GNW_UAIR);
}

incrementCount("throwCount.up", currentAnimation === State.THROW_UP && newAnimation);
incrementCount("throwCount.forward", currentAnimation === State.THROW_FORWARD && newAnimation);
incrementCount("throwCount.down", currentAnimation === State.THROW_DOWN && newAnimation);
incrementCount("throwCount.back", currentAnimation === State.THROW_BACK && newAnimation);
// Throws
incrementCount("throwCount.up", currentAnimation === State.THROW_UP);
incrementCount("throwCount.forward", currentAnimation === State.THROW_FORWARD);
incrementCount("throwCount.down", currentAnimation === State.THROW_DOWN);
incrementCount("throwCount.back", currentAnimation === State.THROW_BACK);

if (newAnimation) {
const didMissTech = didMissGroundTech(currentAnimation);
incrementCount("groundTechCount.fail", didMissTech);
let opponentDir = 1;
let facingOpponent = false;
// Techs
incrementCount("groundTechCount.fail", isMissGroundTech(currentAnimation));
let opponentDir = 1;
let facingOpponent = false;

if (playerFrame.positionX! > opponentFrame.positionX!) {
opponentDir = -1;
}
if (playerFrame.facingDirection == opponentDir) {
facingOpponent = true;
}

incrementCount("groundTechCount.in", currentAnimation === State.FORWARD_TECH && facingOpponent);
incrementCount("groundTechCount.in", currentAnimation === State.BACKWARD_TECH && !facingOpponent);
incrementCount("groundTechCount.neutral", currentAnimation === State.NEUTRAL_TECH);
incrementCount("groundTechCount.away", currentAnimation === State.BACKWARD_TECH && facingOpponent);
incrementCount("groundTechCount.away", currentAnimation === State.FORWARD_TECH && !facingOpponent);

incrementCount("wallTechCount.success", currentAnimation === State.WALL_TECH);
incrementCount("wallTechCount.fail", currentAnimation === State.MISSED_WALL_TECH);
if (playerFrame.positionX! > opponentFrame.positionX!) {
opponentDir = -1;
}
if (playerFrame.facingDirection == opponentDir) {
facingOpponent = true;
}
incrementCount("groundTechCount.in", currentAnimation === State.FORWARD_TECH && facingOpponent);
incrementCount("groundTechCount.in", currentAnimation === State.BACKWARD_TECH && !facingOpponent);
incrementCount("groundTechCount.neutral", currentAnimation === State.NEUTRAL_TECH);
incrementCount("groundTechCount.away", currentAnimation === State.BACKWARD_TECH && facingOpponent);
incrementCount("groundTechCount.away", currentAnimation === State.FORWARD_TECH && !facingOpponent);
incrementCount("wallTechCount.success", currentAnimation === State.WALL_TECH);
incrementCount("wallTechCount.fail", currentAnimation === State.MISSED_WALL_TECH);

if (isAerialAttack(currentAnimation)) {
incrementCount("lCancelCount.success", playerFrame.lCancelStatus === 1);
Expand Down
46 changes: 46 additions & 0 deletions src/stats/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@ export interface ActionCountsType {
success: number;
fail: number;
};
attackCount: {
jab1: number;
jab2: number;
jab3: number;
jabm: number;
dash: number;
ftilt: number;
utilt: number;
dtilt: number;
fsmash: number;
usmash: number;
dsmash: number;
nair: number;
fair: number;
bair: number;
uair: number;
dair: number;
};
grabCount: {
success: number;
fail: number;
Expand Down Expand Up @@ -142,6 +160,10 @@ export enum State {
GROUND_ATTACK_END = 0x40,
AERIAL_ATTACK_START = 0x41,
AERIAL_ATTACK_END = 0x4a,
ATTACK_FTILT_START = 0x33,
ATTACK_FTILT_END = 0x37,
ATTACK_FSMASH_START = 0x3a,
ATTACK_FSMASH_END = 0x3e,

// Animation ID specific
ROLL_FORWARD = 0xe9,
Expand All @@ -167,6 +189,7 @@ export enum State {
FALL_FORWARD = 0x1e,
FALL_BACKWARD = 0x1f,
GRAB = 0xd4,
DASH_GRAB = 0xd6,
GRAB_WAIT = 0xd8,
PUMMEL = 0xd9,
CLIFF_CATCH = 0xfc,
Expand All @@ -175,6 +198,29 @@ export enum State {
THROW_DOWN = 0xde,
THROW_BACK = 0xdc,
DAMAGE_FALL = 0x26,
ATTACK_JAB1 = 0x2c,
ATTACK_JAB2 = 0x2d,
ATTACK_JAB3 = 0x2e,
ATTACK_JABM = 0x2f,
ATTACK_DASH = 0x32,
ATTACK_UTILT = 0x38,
ATTACK_DTILT = 0x39,
ATTACK_USMASH = 0x3f,
ATTACK_DSMASH = 0x40,
AERIAL_NAIR = 0x41,
AERIAL_FAIR = 0x42,
AERIAL_BAIR = 0x43,
AERIAL_UAIR = 0x44,
AERIAL_DAIR = 0x45,

// Weird GnW IDs
GNW_JAB1 = 0x155,
GNW_JABM = 0x156,
GNW_DTILT = 0x159,
GNW_FSMASH = 0x15a,
GNW_NAIR = 0x15b,
GNW_BAIR = 0x15c,
GNW_UAIR = 0x15d,

// Command Grabs
BARREL_WAIT = 0x125,
Expand Down
6 changes: 6 additions & 0 deletions test/game.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ it("should correctly return stats", () => {
expect(stats.actionCounts[0].wavelandCount).toBe(1);
expect(stats.actionCounts[0].airDodgeCount).toBe(3);

// Test attack counts
expect(stats.actionCounts[0].attackCount.ftilt).toBe(3);
expect(stats.actionCounts[0].attackCount.dash).toBe(1);
expect(stats.actionCounts[0].attackCount.fsmash).toBe(4);
expect(stats.actionCounts[0].attackCount.bair).toBe(4);

// Test overall
expect(stats.overall[0].inputCounts.total).toBe(494);
});
Expand Down
Loading

0 comments on commit 701e1d6

Please sign in to comment.