From 1a6b6a3a97482eadf2d1ded40bd1baf2c67c9f78 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Mon, 26 Dec 2022 01:46:24 -0600 Subject: [PATCH 1/9] Modified combo logic --- src/stats/combos.ts | 10 +++++++++- src/stats/common.ts | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/stats/combos.ts b/src/stats/combos.ts index 1716a37f..ce667dcc 100644 --- a/src/stats/combos.ts +++ b/src/stats/combos.ts @@ -7,6 +7,9 @@ import { calcDamageTaken, didLoseStock, getSinglesPlayerPermutationsFromSettings, + isOffstage, + isDodging, + isShielding, isCommandGrabbed, isDamaged, isDead, @@ -187,14 +190,19 @@ function handleComboCompute( const opntIsDowned = isDown(oppActionStateId); const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame); const opntIsDying = isDead(oppActionStateId); + const opntPosition = [opponentFrame.positionX, opponentFrame.positionY] + const opntIsOffstage = isOffstage(opntPosition, settings.stageId); + const opntIsDodging = isDodging(oppActionStateId); + const opntIsShielding = isShielding(oppActionStateId); // Update percent if opponent didn't lose stock if (!opntDidLoseStock) { state.combo.currentPercent = opponentFrame.percent ?? 0; } - if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed || opntIsTeching || opntIsDowned || opntIsDying) { + if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed || opntIsTeching || opntIsDowned || opntIsDying || opntIsOffstage || opntIsDodging || isShielding) { // If opponent got grabbed or damaged, reset the reset counter + //Additionally, reset if opponent is offstage, in shield, or rolling/spot dodging/air dodging state.resetCounter = 0; } else { state.resetCounter += 1; diff --git a/src/stats/common.ts b/src/stats/common.ts index bb1a16f9..bdc3a388 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -1,4 +1,5 @@ -import type { GameStartType, PostFrameUpdateType } from "../types"; +import type { GameStartType, PostFrameUpdateType, FrameType } from "../types"; +import Stage from "../melee/types.ts"; export interface StatsType { gameComplete: boolean; @@ -323,6 +324,46 @@ export function isCommandGrabbed(state: number): boolean { ); } +export function isOffstage(position:Array, currStage: number): boolean { + let stageBounds = [0, 0] + switch(currStage.name){ + case Stage.FOUNTAIN_OF_DREAMS: + stageBounds = [-64, 64] + break; + case Stage.YOSHIS_STORY: + stageBounds = [-56, 56] + break; + case Stage.DREAMLAND: + stageBounds = [-73, 73] + break; + case Stage.POKEMON_STADIUM: + stageBounds = [-88, 88] + break; + case Stage.BATTLEFIELD: + stageBounds = [-67, 67] + break; + case Stage.FINAL_DESTINATION: + stageBounds = [-89, 89] + break; + default: + return false; + } + + if(position[0] > stageBounds[0] && position[0] < stageBounds[1]){ + return true; + } + else return false; + +} + +export function isDodging(state:number):boolean { //not the greatest term, but captures rolling, spot dodging, and air dodging + return state == State.ROLL_FORWARD || state == State.ROLL_BACKWARD || state == State.AIR_DODGE || state == State.SPOT_DODGE; +} + +export function isShielding(state:number):boolean { + return state >= State.GUARD_START && state <= State.GUARD_END; +} + export function isDead(state: number): boolean { return state >= State.DYING_START && state <= State.DYING_END; } From 09611a2237aed44571af2822e71312bee1709bdb Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Tue, 3 Jan 2023 06:20:22 -0600 Subject: [PATCH 2/9] minor logic fixes --- src/stats/common.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/stats/common.ts b/src/stats/common.ts index bdc3a388..1e6080b5 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -185,6 +185,10 @@ export enum State { ATTACK_FTILT_END = 0x37, ATTACK_FSMASH_START = 0x3a, ATTACK_FSMASH_END = 0x3e, + GUARD_BREAK_START = 0xcd, + GUARD_BREAK_END = 0xd3, + DODGE_START = 0xe9, + DODGE_END = 0xec, // Animation ID specific ROLL_FORWARD = 0xe9, @@ -349,7 +353,7 @@ export function isOffstage(position:Array, currStage: number): boolean { return false; } - if(position[0] > stageBounds[0] && position[0] < stageBounds[1]){ + if(position[0] < stageBounds[0] && position[0] > stageBounds[1]){ return true; } else return false; @@ -357,7 +361,7 @@ export function isOffstage(position:Array, currStage: number): boolean { } export function isDodging(state:number):boolean { //not the greatest term, but captures rolling, spot dodging, and air dodging - return state == State.ROLL_FORWARD || state == State.ROLL_BACKWARD || state == State.AIR_DODGE || state == State.SPOT_DODGE; + return state >= State.DODGE_START && state <= State.DODGE_END; } export function isShielding(state:number):boolean { @@ -374,3 +378,7 @@ export function calcDamageTaken(frame: PostFrameUpdateType, prevFrame: PostFrame return percent - prevPercent; } + +export function isShieldBroken(state:number):boolean{ + return state >= State.GUARD_BREAK_START && state <= State.GUARD_BREAK_END; +} \ No newline at end of file From e2952f2dc28969482a9c4cced407594ef1245739 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:31:38 -0600 Subject: [PATCH 3/9] refined logic moved from combo to conversion --- src/stats/combos.ts | 10 +------ src/stats/common.ts | 61 +++++++++++++++++++--------------------- src/stats/conversions.ts | 49 +++++++++++++++++++++++--------- 3 files changed, 65 insertions(+), 55 deletions(-) diff --git a/src/stats/combos.ts b/src/stats/combos.ts index ce667dcc..1716a37f 100644 --- a/src/stats/combos.ts +++ b/src/stats/combos.ts @@ -7,9 +7,6 @@ import { calcDamageTaken, didLoseStock, getSinglesPlayerPermutationsFromSettings, - isOffstage, - isDodging, - isShielding, isCommandGrabbed, isDamaged, isDead, @@ -190,19 +187,14 @@ function handleComboCompute( const opntIsDowned = isDown(oppActionStateId); const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame); const opntIsDying = isDead(oppActionStateId); - const opntPosition = [opponentFrame.positionX, opponentFrame.positionY] - const opntIsOffstage = isOffstage(opntPosition, settings.stageId); - const opntIsDodging = isDodging(oppActionStateId); - const opntIsShielding = isShielding(oppActionStateId); // Update percent if opponent didn't lose stock if (!opntDidLoseStock) { state.combo.currentPercent = opponentFrame.percent ?? 0; } - if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed || opntIsTeching || opntIsDowned || opntIsDying || opntIsOffstage || opntIsDodging || isShielding) { + if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed || opntIsTeching || opntIsDowned || opntIsDying) { // If opponent got grabbed or damaged, reset the reset counter - //Additionally, reset if opponent is offstage, in shield, or rolling/spot dodging/air dodging state.resetCounter = 0; } else { state.resetCounter += 1; diff --git a/src/stats/common.ts b/src/stats/common.ts index 1e6080b5..4647f74c 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -1,5 +1,5 @@ -import type { GameStartType, PostFrameUpdateType, FrameType } from "../types"; -import Stage from "../melee/types.ts"; +import type { GameStartType, PostFrameUpdateType } from "../types"; +import { Stage } from "../melee/types"; export interface StatsType { gameComplete: boolean; @@ -175,6 +175,8 @@ export enum State { TECH_END = 0xcc, DYING_START = 0x0, DYING_END = 0xa, + LEDGE_ACTION_START = 0xfc, + LEDGE_ACTION_END = 0x107, CONTROLLED_JUMP_START = 0x18, CONTROLLED_JUMP_END = 0x22, GROUND_ATTACK_START = 0x2c, @@ -294,15 +296,6 @@ export function didLoseStock(frame: PostFrameUpdateType, prevFrame: PostFrameUpd return prevFrame.stocksRemaining! - frame.stocksRemaining! > 0; } -export function isInControl(state: number): boolean { - const ground = state >= State.GROUNDED_CONTROL_START && state <= State.GROUNDED_CONTROL_END; - const squat = state >= State.SQUAT_START && state <= State.SQUAT_END; - const groundAttack = state > State.GROUND_ATTACK_START && state <= State.GROUND_ATTACK_END; - const isGrab = state === State.GRAB; - // TODO: Add grounded b moves? - return ground || squat || groundAttack || isGrab; -} - export function isTeching(state: number): boolean { return state >= State.TECH_START && state <= State.TECH_END; } @@ -328,43 +321,43 @@ export function isCommandGrabbed(state: number): boolean { ); } -export function isOffstage(position:Array, currStage: number): boolean { - let stageBounds = [0, 0] - switch(currStage.name){ +export function isOffstage(position: Array, currStage: number | null): boolean { + if (position == null || currStage == null) { + return false; + } + + let stageBounds = [0, 0]; + switch (currStage) { case Stage.FOUNTAIN_OF_DREAMS: - stageBounds = [-64, 64] + stageBounds = [-64, 64]; break; case Stage.YOSHIS_STORY: - stageBounds = [-56, 56] + stageBounds = [-56, 56]; break; case Stage.DREAMLAND: - stageBounds = [-73, 73] + stageBounds = [-73, 73]; break; case Stage.POKEMON_STADIUM: - stageBounds = [-88, 88] + stageBounds = [-88, 88]; break; case Stage.BATTLEFIELD: - stageBounds = [-67, 67] + stageBounds = [-67, 67]; break; case Stage.FINAL_DESTINATION: - stageBounds = [-89, 89] + stageBounds = [-89, 89]; break; default: return false; } - - if(position[0] < stageBounds[0] && position[0] > stageBounds[1]){ - return true; - } - else return false; - + return position[0]! < stageBounds[0]! && position[0]! > stageBounds[1]!; } -export function isDodging(state:number):boolean { //not the greatest term, but captures rolling, spot dodging, and air dodging +export function isDodging(state: number): boolean { + //not the greatest term, but captures rolling, spot dodging, and air dodging return state >= State.DODGE_START && state <= State.DODGE_END; } -export function isShielding(state:number):boolean { +export function isShielding(state: number): boolean { return state >= State.GUARD_START && state <= State.GUARD_END; } @@ -372,13 +365,17 @@ export function isDead(state: number): boolean { return state >= State.DYING_START && state <= State.DYING_END; } +export function isShieldBroken(state: number): boolean { + return state >= State.GUARD_BREAK_START && state <= State.GUARD_BREAK_END; +} + +export function isLedgeAction(state: number): boolean { + return state >= State.LEDGE_ACTION_START && state <= State.LEDGE_ACTION_END; +} + export function calcDamageTaken(frame: PostFrameUpdateType, prevFrame: PostFrameUpdateType): number { const percent = frame.percent ?? 0; const prevPercent = prevFrame.percent ?? 0; return percent - prevPercent; } - -export function isShieldBroken(state:number):boolean{ - return state >= State.GUARD_BREAK_START && state <= State.GUARD_BREAK_END; -} \ No newline at end of file diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index b36ace1e..a99f71e5 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -2,15 +2,22 @@ import { EventEmitter } from "events"; import { filter, get, groupBy, last, orderBy } from "lodash"; import type { FrameEntryType, FramesType, GameStartType, PostFrameUpdateType } from "../types"; -import type { ConversionType, MoveLandedType, PlayerIndexedType } from "./common"; +import { ConversionType, MoveLandedType, PlayerIndexedType } from "./common"; import { calcDamageTaken, didLoseStock, getSinglesPlayerPermutationsFromSettings, + isShielding, + isShieldBroken, + isDodging, + isTeching, + isOffstage, + isDead, + isDown, + isLedgeAction, isCommandGrabbed, isDamaged, isGrabbed, - isInControl, Timers, } from "./common"; import type { StatComputer } from "./stats"; @@ -67,7 +74,7 @@ export class ConversionComputer extends EventEmitter implements StatComputer { const state = this.state.get(indices); if (state) { - const terminated = handleConversionCompute(allFrames, state, indices, frame, this.conversions); + const terminated = handleConversionCompute(allFrames, state, indices, frame, this.conversions, this.settings); if (terminated) { this.emit("CONVERSION", { combo: last(this.conversions), @@ -123,6 +130,7 @@ function handleConversionCompute( indices: PlayerIndexedType, frame: FrameEntryType, conversions: ConversionType[], + settings: GameStartType | null, ): boolean { const currentFrameNumber = frame.frame; const playerFrame: PostFrameUpdateType = frame.players[indices.playerIndex]!.post; @@ -158,7 +166,7 @@ function handleConversionCompute( } // If opponent took damage and was put in some kind of stun this frame, either - // start a conversion or + // start a conversion or continue the current conversion if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed) { if (!state.conversion) { state.conversion = { @@ -209,25 +217,38 @@ function handleConversionCompute( return false; } - const opntInControl = isInControl(oppActionStateId); const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame); + const opntPosition = [opponentFrame.positionX, opponentFrame.positionY]; + const opntIsOffstage = isOffstage(opntPosition, settings!.stageId); + const opntIsDodging = isDodging(oppActionStateId); + const opntIsShielding = isShielding(oppActionStateId); + const opntIsTeching = isTeching(oppActionStateId); + const opntIsDowned = isDown(oppActionStateId); + const opntIsShieldBroken = isShieldBroken(oppActionStateId); + const opntIsDying = isDead(oppActionStateId); + const opntIsLedgeAction = isLedgeAction(oppActionStateId); // Update percent if opponent didn't lose stock if (!opntDidLoseStock) { state.conversion.currentPercent = opponentFrame.percent ?? 0; } - if (opntIsDamaged || opntIsGrabbed || opntIsCommandGrabbed) { + if ( + opntIsDamaged || + opntIsGrabbed || + opntIsCommandGrabbed || + opntIsTeching || + opntIsDowned || + opntIsDying || + opntIsOffstage || + opntIsDodging || + opntIsShielding || + opntIsShieldBroken || + opntIsLedgeAction + ) { // If opponent got grabbed or damaged, reset the reset counter state.resetCounter = 0; - } - - const shouldStartResetCounter = state.resetCounter === 0 && opntInControl; - const shouldContinueResetCounter = state.resetCounter > 0; - if (shouldStartResetCounter || shouldContinueResetCounter) { - // This will increment the reset timer under the following conditions: - // 1) if we were punishing opponent but they have now entered an actionable state - // 2) if counter has already started counting meaning opponent has entered actionable state + } else { state.resetCounter += 1; } From d98a340ea190e2cdc93f1a84940f24f939dde8c5 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:40:55 -0600 Subject: [PATCH 4/9] minor consistency fix --- src/stats/conversions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index a99f71e5..0715037b 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -2,7 +2,7 @@ import { EventEmitter } from "events"; import { filter, get, groupBy, last, orderBy } from "lodash"; import type { FrameEntryType, FramesType, GameStartType, PostFrameUpdateType } from "../types"; -import { ConversionType, MoveLandedType, PlayerIndexedType } from "./common"; +import type { ConversionType, MoveLandedType, PlayerIndexedType } from "./common"; import { calcDamageTaken, didLoseStock, From 3cbde656dd5b314f11f9962b162ac68af5d58a65 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Thu, 26 Jan 2023 21:19:52 -0600 Subject: [PATCH 5/9] pass only stageid instead of all settings --- src/stats/common.ts | 4 ++-- src/stats/conversions.ts | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/stats/common.ts b/src/stats/common.ts index 4647f74c..d300fd9f 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -321,8 +321,8 @@ export function isCommandGrabbed(state: number): boolean { ); } -export function isOffstage(position: Array, currStage: number | null): boolean { - if (position == null || currStage == null) { +export function isOffstage(position: Array, currStage?: number | null): boolean { + if (!position || !currStage) { return false; } diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index 0715037b..22092f46 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -74,7 +74,14 @@ export class ConversionComputer extends EventEmitter implements StatComputer { const state = this.state.get(indices); if (state) { - const terminated = handleConversionCompute(allFrames, state, indices, frame, this.conversions, this.settings); + const terminated = handleConversionCompute( + allFrames, + state, + indices, + frame, + this.conversions, + this.settings?.stageId, + ); if (terminated) { this.emit("CONVERSION", { combo: last(this.conversions), @@ -130,7 +137,7 @@ function handleConversionCompute( indices: PlayerIndexedType, frame: FrameEntryType, conversions: ConversionType[], - settings: GameStartType | null, + stageId: number | null | undefined, ): boolean { const currentFrameNumber = frame.frame; const playerFrame: PostFrameUpdateType = frame.players[indices.playerIndex]!.post; @@ -219,7 +226,7 @@ function handleConversionCompute( const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame); const opntPosition = [opponentFrame.positionX, opponentFrame.positionY]; - const opntIsOffstage = isOffstage(opntPosition, settings!.stageId); + const opntIsOffstage = isOffstage(opntPosition, stageId); const opntIsDodging = isDodging(oppActionStateId); const opntIsShielding = isShielding(oppActionStateId); const opntIsTeching = isTeching(oppActionStateId); From 655eb745c0525d3c0ab7e52890ff2f21d300dfae Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Thu, 26 Jan 2023 21:23:11 -0600 Subject: [PATCH 6/9] added check for player death --- src/stats/conversions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index 22092f46..465b4445 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -225,6 +225,8 @@ function handleConversionCompute( } const opntDidLoseStock = prevOpponentFrame && didLoseStock(opponentFrame, prevOpponentFrame); + const playerDidLoseStock = prevPlayerFrame && didLoseStock(playerFrame, prevPlayerFrame); + const opntPosition = [opponentFrame.positionX, opponentFrame.positionY]; const opntIsOffstage = isOffstage(opntPosition, stageId); const opntIsDodging = isDodging(oppActionStateId); @@ -261,8 +263,8 @@ function handleConversionCompute( let shouldTerminate = false; - // Termination condition 1 - player kills opponent - if (opntDidLoseStock) { + // Termination condition 1 - player kills opponent or opponent kills player + if (opntDidLoseStock || playerDidLoseStock) { state.conversion.didKill = true; shouldTerminate = true; } From 4039fc002f2e1273254d6d1cd32afccbc12732fe Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Sat, 4 Feb 2023 18:15:00 -0600 Subject: [PATCH 7/9] player dying no longer marks combo as killing --- src/stats/conversions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index 465b4445..ee349d23 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -264,11 +264,15 @@ function handleConversionCompute( let shouldTerminate = false; // Termination condition 1 - player kills opponent or opponent kills player - if (opntDidLoseStock || playerDidLoseStock) { + if (opntDidLoseStock) { state.conversion.didKill = true; shouldTerminate = true; } + if (playerDidLoseStock) { + shouldTerminate = true; + } + // Termination condition 2 - conversion resets on time if (state.resetCounter > Timers.PUNISH_RESET_FRAMES) { shouldTerminate = true; From ceb1694f9fa9bd84cdf9d0f6720137ecc87f4881 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:22:40 -0600 Subject: [PATCH 8/9] added upb lag, special fall, and Y value checks --- src/stats/common.ts | 73 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/stats/common.ts b/src/stats/common.ts index d300fd9f..f265e206 100644 --- a/src/stats/common.ts +++ b/src/stats/common.ts @@ -191,6 +191,8 @@ export enum State { GUARD_BREAK_END = 0xd3, DODGE_START = 0xe9, DODGE_END = 0xec, + FALL_SPECIAL_START = 0x23, + FALL_SPECIAL_END = 0x25, // Animation ID specific ROLL_FORWARD = 0xe9, @@ -321,10 +323,19 @@ export function isCommandGrabbed(state: number): boolean { ); } -export function isOffstage(position: Array, currStage?: number | null): boolean { - if (!position || !currStage) { +export function isOffstage( + position: (number | null)[], + isAirborne: boolean | null, + currStage?: number | null, +): boolean { + if (!position || !currStage || isAirborne === false) { + //if isAirborne is null, run the check anyway for backwards compatibility return false; } + //-5 is below the main part of all legal stages. Just ignore the X value if the player is at or below this + if (position[1]! <= -5) { + return true; + } let stageBounds = [0, 0]; switch (currStage) { @@ -373,6 +384,64 @@ export function isLedgeAction(state: number): boolean { return state >= State.LEDGE_ACTION_START && state <= State.LEDGE_ACTION_END; } +export function isMaybeJuggled( + position: (number | null)[], + isAirborne: boolean | null, + currStage?: number | null, +): boolean { + if (!position || !currStage || !isAirborne) { + return false; + } + + let stageBounds = 0; + + switch (currStage) { + case Stage.FOUNTAIN_OF_DREAMS: + stageBounds = 42; + break; + case Stage.YOSHIS_STORY: + stageBounds = 41; + break; + case Stage.DREAMLAND: + stageBounds = 51; + break; + case Stage.POKEMON_STADIUM: + //similar side plat heights to yoshi's, so we can steal the top plat height as well + stageBounds = 41; + break; + case Stage.BATTLEFIELD: + stageBounds = 54; + break; + case Stage.FINAL_DESTINATION: + //No plats, so we'll just use a lower-than-average value + stageBounds = 10; // or 45 + break; + default: + return false; + } + return position[1]! >= stageBounds!; +} + +export function isSpecialFall(state: number): boolean { + return state >= State.FALL_SPECIAL_START && state <= State.FALL_SPECIAL_END; +} + +export function isUpBLag(state: number, prevState: number | null | undefined): boolean { + if (!state || !prevState) { + return false; + } + //allows resetting timer for land_fall_special without triggering due to wavedash/waveland + //specifically useful for up b's like sheik's that have a unique animation id for ~40 frames of the endlag + //rather than just going straight into fall_special -> land_fall_special + return ( + state == State.LANDING_FALL_SPECIAL && + prevState != State.LANDING_FALL_SPECIAL && + prevState != State.ACTION_KNEE_BEND && + prevState != State.AIR_DODGE && + (prevState <= State.CONTROLLED_JUMP_START || prevState >= State.CONTROLLED_JUMP_END) + ); +} + export function calcDamageTaken(frame: PostFrameUpdateType, prevFrame: PostFrameUpdateType): number { const percent = frame.percent ?? 0; const prevPercent = prevFrame.percent ?? 0; From 39165a94174d20394bc684d7585e20d66959b2ee Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Wed, 8 Feb 2023 07:24:19 -0600 Subject: [PATCH 9/9] updated conversion calc with new checks --- src/stats/conversions.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/stats/conversions.ts b/src/stats/conversions.ts index ee349d23..7e8629f0 100644 --- a/src/stats/conversions.ts +++ b/src/stats/conversions.ts @@ -18,6 +18,9 @@ import { isCommandGrabbed, isDamaged, isGrabbed, + isMaybeJuggled, + isSpecialFall, + isUpBLag, Timers, } from "./common"; import type { StatComputer } from "./stats"; @@ -228,7 +231,7 @@ function handleConversionCompute( const playerDidLoseStock = prevPlayerFrame && didLoseStock(playerFrame, prevPlayerFrame); const opntPosition = [opponentFrame.positionX, opponentFrame.positionY]; - const opntIsOffstage = isOffstage(opntPosition, stageId); + const opntIsOffstage = isOffstage(opntPosition, opponentFrame.isAirborne, stageId); const opntIsDodging = isDodging(oppActionStateId); const opntIsShielding = isShielding(oppActionStateId); const opntIsTeching = isTeching(oppActionStateId); @@ -236,6 +239,9 @@ function handleConversionCompute( const opntIsShieldBroken = isShieldBroken(oppActionStateId); const opntIsDying = isDead(oppActionStateId); const opntIsLedgeAction = isLedgeAction(oppActionStateId); + const opntIsMaybeJuggled = isMaybeJuggled(opntPosition, opponentFrame.isAirborne, stageId); + const opntIsSpecialFall = isSpecialFall(oppActionStateId); + const opntIsUpBLag = isUpBLag(oppActionStateId, prevOpponentFrame?.actionStateId); // Update percent if opponent didn't lose stock if (!opntDidLoseStock) { @@ -253,7 +259,10 @@ function handleConversionCompute( opntIsDodging || opntIsShielding || opntIsShieldBroken || - opntIsLedgeAction + opntIsLedgeAction || + opntIsMaybeJuggled || + opntIsSpecialFall || + opntIsUpBLag ) { // If opponent got grabbed or damaged, reset the reset counter state.resetCounter = 0;