diff --git a/TODO b/TODO
index b71f99822e..9196b3c548 100644
--- a/TODO
+++ b/TODO
@@ -12,6 +12,11 @@ FBGM show field during game
- some minimum height of field, but expand dynamically for long drives
- draw vertical lines for scrimmage and togo
- light mode
+ - how to update numPlays
+ - accepted penalties only count as a play if they are added on to the end of the play. this means we sometimes need to decrease numPlays when a penalty gets accepted, but not always
+ - how does drive tracker handle turnover reversed on penalty?
+ - need to set sportState.drive to undefined when doing a kickoff at start of half/overtime
+ - unify logic with kickoffAfterEndOfPeriod
use team colors as foreground/background in UI in some places - already guaranteed contrast bc used for jersey numbers
- box scores
diff --git a/src/ui/components/BoxScore.football.tsx b/src/ui/components/BoxScore.football.tsx
index 337126a833..8a00ffe0da 100644
--- a/src/ui/components/BoxScore.football.tsx
+++ b/src/ui/components/BoxScore.football.tsx
@@ -15,6 +15,7 @@ import updateSortBys from "./DataTable/updateSortBys";
import { getSortClassName } from "./DataTable/Header";
import range from "lodash-es/range";
import classNames from "classnames";
+import type { SportState } from "../util/processLiveGameEvents.football";
type Quarter = `Q${number}` | "OT";
@@ -386,20 +387,31 @@ const ScoringSummary = memo(
},
);
-const FieldAndDrive = ({ boxScore }: { boxScore: BoxScore }) => {
- const t = 0;
- const t2 = 1;
+const FieldAndDrive = ({
+ boxScore,
+ sportState,
+}: {
+ boxScore: BoxScore;
+ sportState: SportState;
+}) => {
+ const t = sportState.t;
+ const t2 = t === 0 ? 1 : 0;
// 12 is for 2 endzones and 10 10-yard areas in between
const NUM_SECTIONS = 12;
-
const DEFAULT_HEIGHT = 200;
- console.log(boxScore);
+
+ const yards = sportState.scrimmage - sportState.initialScrimmage;
+
return (
-
- LOGO 1st & 10, own 20
-
Drive: 5 plays, 62 yards
+
+ {sportState.text}
+
+ Drive: {sportState.numPlays} play
+ {sportState.numPlays === 1 ? "" : "s"}, {yards} yard
+ {yards === 1 ? "" : "s"}
+
{
);
};
-const BoxScore = ({ boxScore, Row }: { boxScore: BoxScore; Row: any }) => {
+const BoxScore = ({
+ boxScore,
+ sportState,
+ Row,
+}: {
+ boxScore: BoxScore;
+ sportState: SportState;
+ Row: any;
+}) => {
const liveGameSim = (boxScore as any).won?.name === undefined;
return (
- {liveGameSim ?
: undefined}
+ {liveGameSim ? (
+
+ ) : undefined}
Scoring Summary
{
let stop = false;
let text;
@@ -86,8 +106,9 @@ const processLiveGameEvents = ({
boxScore.time = e.time;
stop = true;
} else if (e.type === "clock") {
- if (typeof e.awaitingKickoff === "number") {
- text = `${e.time} - ${boxScore.teams[actualT].abbrev} kicking off`;
+ let textWithoutTime;
+ if (e.awaitingKickoff !== undefined) {
+ textWithoutTime = `${boxScore.teams[actualT].abbrev} kicking off`;
} else {
let fieldPos = "";
if (e.scrimmage === 50) {
@@ -98,13 +119,26 @@ const processLiveGameEvents = ({
fieldPos = `own ${e.scrimmage}`;
}
- text = `${e.time} - ${
+ textWithoutTime = `${
boxScore.teams[actualT].abbrev
} ball, ${helpers.ordinal(e.down)} & ${e.toGo}, ${fieldPos}`;
}
+ text = `${e.time} - ${textWithoutTime}`;
boxScore.time = e.time;
stop = true;
+
+ if (e.awaitingKickoff !== undefined || sportState.t !== actualT) {
+ sportState.t = actualT;
+ sportState.numPlays = 0;
+ sportState.initialScrimmage = e.scrimmage;
+ sportState.scrimmage = e.scrimmage;
+ sportState.plays = [];
+ sportState.text = textWithoutTime;
+ } else {
+ sportState.text = textWithoutTime;
+ sportState.scrimmage = e.scrimmage;
+ }
} else if (e.type === "stat") {
// Quarter-by-quarter score
if (e.s === "pts") {
@@ -148,6 +182,7 @@ const processLiveGameEvents = ({
overtimes,
possessionChange,
quarters,
+ sportState,
text,
};
};
diff --git a/src/ui/util/processLiveGameEvents.ts b/src/ui/util/processLiveGameEvents.ts
index 6cc96d50a9..7441551204 100644
--- a/src/ui/util/processLiveGameEvents.ts
+++ b/src/ui/util/processLiveGameEvents.ts
@@ -25,7 +25,7 @@ const processLiveGameEvents = ({
overtimes,
quarters,
sportState,
- }) as any,
+ }),
basketball: processLiveGameEventsBasketball({
events,
boxScore,
@@ -37,6 +37,7 @@ const processLiveGameEvents = ({
boxScore,
overtimes,
quarters,
+ sportState,
}),
hockey: processLiveGameEventsHockey({
events,
diff --git a/src/ui/views/LiveGame.tsx b/src/ui/views/LiveGame.tsx
index baab22b7f9..8e51c6a77e 100644
--- a/src/ui/views/LiveGame.tsx
+++ b/src/ui/views/LiveGame.tsx
@@ -19,7 +19,8 @@ import { helpers, processLiveGameEvents, toWorker } from "../util";
import type { View } from "../../common/types";
import { bySport, getPeriodName, isSport } from "../../common";
import useLocalStorageState from "use-local-storage-state";
-import type { SportState } from "../util/processLiveGameEvents.baseball";
+import { DEFAULT_SPORT_STATE as DEFAULT_SPORT_STATE_BASEBALL } from "../util/processLiveGameEvents.baseball";
+import { DEFAULT_SPORT_STATE as DEFAULT_SPORT_STATE_FOOTBALL } from "../util/processLiveGameEvents.football";
import { HeadlineScore } from "../components/BoxScoreWrapper";
type PlayerRowProps = {
@@ -92,20 +93,12 @@ const getSeconds = (time: string | undefined) => {
return min * 60 + sec;
};
-export const DEFAULT_SPORT_STATE = isSport("baseball")
- ? ({
- bases: [undefined, undefined, undefined] as [
- number | undefined,
- number | undefined,
- number | undefined,
- ],
- outs: 0,
- balls: 0,
- strikes: 0,
- batterPid: -1,
- pitcherPid: -1,
- } as SportState)
- : undefined;
+const DEFAULT_SPORT_STATE = bySport({
+ baseball: DEFAULT_SPORT_STATE_BASEBALL,
+ basketball: undefined,
+ football: DEFAULT_SPORT_STATE_FOOTBALL,
+ hockey: undefined,
+});
export const LiveGame = (props: View<"liveGame">) => {
const [paused, setPaused] = useState(false);
@@ -251,10 +244,13 @@ export const LiveGame = (props: View<"liveGame">) => {
if (events.current && events.current.length > 0) {
if (!pausedRef.current) {
- setTimeout(() => {
- processToNextPause();
- setPlayIndex(prev => prev + 1);
- }, 4000 / 1.2 ** speedRef.current);
+ setTimeout(
+ () => {
+ processToNextPause();
+ setPlayIndex(prev => prev + 1);
+ },
+ 4000 / 1.2 ** speedRef.current,
+ );
}
} else {
boxScore.current.time = "0:00";
@@ -547,7 +543,9 @@ export const LiveGame = (props: View<"liveGame">) => {
label: "Next baserunner",
key: "T",
onClick: () => {
- const initialBases = sportState.current?.bases ?? [];
+ const sportStateBaseball =
+ sportState.current as typeof DEFAULT_SPORT_STATE_BASEBALL;
+ const initialBases = sportStateBaseball.bases ?? [];
const initialBaserunners = new Set(
initialBases.filter(pid => pid !== undefined),
);
@@ -562,7 +560,7 @@ export const LiveGame = (props: View<"liveGame">) => {
numPlays += 1;
// Any new baserunner -> stop
- const baserunners = (sportState.current?.bases ?? []).filter(
+ const baserunners = (sportStateBaseball.bases ?? []).filter(
pid => pid !== undefined,
);
if (baserunners.length === 0) {