Skip to content

Commit ca8fb21

Browse files
Merge branch 'master' into public-release
2 parents a49ec40 + 32a36a1 commit ca8fb21

File tree

21 files changed

+238
-149
lines changed

21 files changed

+238
-149
lines changed

client/package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
"pako": "^2.1.0",
139139
"path": "^0.12.7",
140140
"process": "^0.11.10",
141+
"re-resizable": "^6.10.3",
141142
"react": "^18.2.0",
142143
"react-color": "^2.19.3",
143144
"react-custom-scrollbars-2": "^4.5.0",

client/src/app.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ import '../style.css'
88

99
const elem = document.getElementById('root')
1010
const root = ReactDom.createRoot(elem!)
11-
root.render(<BrowserRouter><AppContextProvider><MainPage /></AppContextProvider></BrowserRouter>)
11+
root.render(
12+
<BrowserRouter>
13+
<AppContextProvider>
14+
<MainPage />
15+
</AppContextProvider>
16+
</BrowserRouter>
17+
)

client/src/components/sidebar/game/game.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export const GamePage: React.FC<Props> = React.memo((props) => {
122122
<ResourceGraph active={showStats} property="paintPercent" propertyDisplayName="Coverage %" />
123123
<br />
124124
<ResourceGraph active={showStats} property="moneyAmount" propertyDisplayName="Chips" />
125+
<br />
126+
<ResourceGraph active={showStats} property="totalPaint" propertyDisplayName="Paint" />
125127
</div>
126128
) : (
127129
<div>Select a game to see stats</div>

client/src/components/sidebar/game/quick-line-chart.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,14 @@ export const QuickLineChart: React.FC<LineChartProps> = ({
4040

4141
setCanvasResolution(canvas, width, height, resolution)
4242

43-
const max = Math.max(...data.map((d) => Math.max(d.team0, d.team1)))
44-
const { xScale, yScale, innerWidth, innerHeight } = getAxes(width, height, margin, { x: data.length, y: max })
43+
let maxX = -9999999
44+
let maxY = -9999999
45+
for (const d of data) {
46+
maxX = Math.max(maxX, d.round)
47+
maxY = Math.max(maxY, Math.max(d.team0, d.team1))
48+
}
49+
50+
const { xScale, yScale, innerWidth, innerHeight } = getAxes(width, height, margin, { x: maxX, y: maxY })
4551

4652
context.clearRect(0, 0, width, height)
4753

@@ -65,11 +71,11 @@ export const QuickLineChart: React.FC<LineChartProps> = ({
6571
height,
6672
margin,
6773
{
68-
range: { min: 0, max: data.length },
74+
range: { min: 0, max: maxX },
6975
options: { textColor: 'white', lineColor: 'white' }
7076
},
7177
{
72-
range: { min: 0, max: max },
78+
range: { min: 0, max: maxY },
7379
options: { textColor: 'white', lineColor: 'white' }
7480
}
7581
)
Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
11
import React from 'react'
2-
import assert from 'assert'
32
import { useRound } from '../../../playback/GameRunner'
43
import Round from '../../../playback/Round'
54
import { LineChartDataPoint, QuickLineChart } from './quick-line-chart'
5+
import { TeamRoundStat } from '../../../playback/RoundStat'
66

77
interface Props {
88
active: boolean
9-
property: string
9+
property: keyof TeamRoundStat
1010
propertyDisplayName: string
1111
}
12-
function hasKey<O extends Object>(obj: O, key: PropertyKey): key is keyof O {
13-
return key in obj
14-
}
1512

16-
function getChartData(round: Round, property: string): LineChartDataPoint[] {
17-
const values = [0, 1].map((index) =>
18-
round.match.stats.map((roundStat) => {
19-
const teamStat = roundStat.getTeamStat(round.match.game.teams[index])
20-
assert(hasKey(teamStat, property), `TeamStat missing property '${property}' when rendering chart`)
21-
return teamStat[property]
13+
function getChartData(round: Round, property: keyof TeamRoundStat): LineChartDataPoint[] {
14+
const teams = round.match.game.teams
15+
16+
const result: LineChartDataPoint[] = []
17+
18+
// Sparser graph as datapoints increase
19+
const interval = Math.ceil(round.roundNumber / 500)
20+
21+
for (let i = 0; i < round.roundNumber; i += interval) {
22+
const roundStat = round.match.stats[i]
23+
24+
const team0Stat = roundStat.getTeamStat(teams[0])
25+
const team1Stat = roundStat.getTeamStat(teams[1])
26+
27+
result.push({
28+
round: i + 1,
29+
team0: team0Stat[property] as number,
30+
team1: team1Stat[property] as number
2231
})
23-
)
32+
}
2433

25-
return values[0].slice(0, round.roundNumber).map((value, index) => {
26-
return {
27-
round: index + 1,
28-
team0: value as number,
29-
team1: values[1][index] as number
30-
}
31-
})
34+
return result
3235
}
3336

3437
export const ResourceGraph: React.FC<Props> = (props: Props) => {
@@ -38,7 +41,7 @@ export const ResourceGraph: React.FC<Props> = (props: Props) => {
3841
return (
3942
<div className="mt-2 w-full">
4043
<h2 className="mx-auto text-center mb-2">{props.propertyDisplayName}</h2>
41-
<QuickLineChart data={data} width={350} height={170} margin={{ top: 2, right: 20, bottom: 17, left: 30 }} />
44+
<QuickLineChart data={data} width={350} height={170} margin={{ top: 2, right: 20, bottom: 17, left: 40 }} />
4245
</div>
4346
)
4447
}

client/src/components/sidebar/game/team-table.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,23 @@ export const UnitsTable: React.FC<UnitsTableProps> = ({ teamStat, teamIdx }) =>
110110
['Mopper', <UnitsIcon teamIdx={teamIdx} robotType="mopper" key="5" />]
111111
]
112112

113-
let data: [string, number[]][] = [['Count', [0, 0, 0, 0, 0, 0]]]
113+
let data: [string, number[]][] = [
114+
['Count', [0, 0, 0, 0, 0, 0]],
115+
['Paint', [0, 0, 0, 0, 0, 0]]
116+
]
114117
if (teamStat) {
115118
data = [
116119
[
117120
'Count',
118121
Object.values(schema.RobotType)
119122
.filter((k) => typeof k === 'number' && k !== schema.RobotType.NONE)
120123
.map((k) => teamStat.robotCounts[k as schema.RobotType])
124+
],
125+
[
126+
'Paint',
127+
Object.values(schema.RobotType)
128+
.filter((k) => typeof k === 'number' && k !== schema.RobotType.NONE)
129+
.map((k) => teamStat.robotPaints[k as schema.RobotType])
121130
]
122131
]
123132
}

client/src/components/sidebar/map-editor/map-editor-field.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const MapEditorBrushRowField: React.FC<Props> = (props: Props) => {
4646
case MapEditorBrushFieldType.TEAM:
4747
field = (
4848
<Toggle
49+
initialValue={props.field.value}
4950
options={{
5051
[TEAM_COLOR_NAMES[0]]: { value: 0, selectedClass: 'bg-team0 text-black' },
5152
[TEAM_COLOR_NAMES[1]]: { value: 1, selectedClass: 'bg-team1 text-black' }

client/src/components/sidebar/runner/runner.tsx

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { RingBuffer } from '../../../util/ring-buffer'
1313
import { ProfilerDialog } from './profiler'
1414
import { GameRenderer } from '../../../playback/GameRenderer'
1515
import GameRunner from '../../../playback/GameRunner'
16+
import { Resizable } from 're-resizable'
1617

1718
type RunnerPageProps = {
1819
open: boolean
@@ -91,14 +92,20 @@ export const RunnerPage: React.FC<RunnerPageProps> = ({ open, scaffold }) => {
9192

9293
const MemoConsole = React.useMemo(() => <Console lines={consoleLines} />, [consoleLines.effectiveLength()])
9394

94-
if (!open) return null
95+
if (open && !nativeAPI) return <>Run the client locally to use the runner</>
9596

96-
if (!nativeAPI) return <>Run the client locally to use the runner</>
97+
/* Keep the component mounted but hidden when !open so we retain any locally resized/edited elements */
9798

9899
const lastLogLine = consoleLines.get(consoleLines.length() - 1)
99100
const runDisabled = !teamA || !teamB || maps.size === 0 || !langVersion
100101
return (
101-
<div className={'flex flex-col grow ' + (scaffoldLoading ? 'opacity-50 pointer-events-none' : '')}>
102+
<div
103+
className={
104+
'flex flex-col grow' +
105+
(scaffoldLoading ? ' opacity-50 pointer-events-none' : '') +
106+
(open ? '' : ' hidden')
107+
}
108+
>
102109
{!setup ? (
103110
<>
104111
{error && <div className="text-red">{`Setup Error: ${error}`}</div>}
@@ -286,7 +293,7 @@ const TeamSelector: React.FC<TeamSelectorProps> = ({ teamA, teamB, options, onCh
286293
<label>Team A</label>
287294
<Select className="w-full" value={teamA ?? 'NONE'} onChange={(e) => onChangeA(e)}>
288295
{teamA === undefined && <option value={'NONE'}>Select a team</option>}
289-
{[...options].map((t) => (
296+
{[...options].sort().map((t) => (
290297
<option key={t} value={t}>
291298
{t}
292299
</option>
@@ -309,7 +316,7 @@ const TeamSelector: React.FC<TeamSelectorProps> = ({ teamA, teamB, options, onCh
309316
<label className="ml-auto">Team B</label>
310317
<Select className="w-full" value={teamB ?? 'NONE'} onChange={(e) => onChangeB(e)}>
311318
{teamB === undefined && <option value={'NONE'}>Select a team</option>}
312-
{[...options].map((t) => (
319+
{[...options].sort().map((t) => (
313320
<option key={t} value={t}>
314321
{t}
315322
</option>
@@ -331,21 +338,36 @@ const MapSelector: React.FC<MapSelectorProps> = ({ maps, availableMaps, onSelect
331338
return (
332339
<div className="flex flex-col mt-3">
333340
<label>Maps</label>
334-
<div className="flex flex-col border border-white py-1 px-1 rounded-md max-h-[190px] overflow-y-auto">
335-
{[...availableMaps].map((m) => {
336-
const selected = maps.has(m)
337-
return (
338-
<div
339-
key={m}
340-
className={'cursor-pointer hover:bg-lightHighlight flex items-center justify-between'}
341-
onClick={() => (maps.has(m) ? onDeselect(m) : onSelect(m))}
342-
>
343-
{m}
344-
<input type={'checkbox'} readOnly checked={selected} className="pointer-events-none mr-2" />
345-
</div>
346-
)
347-
})}
348-
</div>
341+
<Resizable
342+
minWidth="100%"
343+
maxWidth="100%"
344+
minHeight={50}
345+
defaultSize={{
346+
width: '100%',
347+
height: 120
348+
}}
349+
>
350+
<div className="flex flex-col border border-white py-1 px-1 h-full rounded-md overflow-y-auto">
351+
{[...availableMaps].sort().map((m) => {
352+
const selected = maps.has(m)
353+
return (
354+
<div
355+
key={m}
356+
className={'cursor-pointer hover:bg-lightHighlight flex items-center justify-between'}
357+
onClick={() => (maps.has(m) ? onDeselect(m) : onSelect(m))}
358+
>
359+
{m}
360+
<input
361+
type={'checkbox'}
362+
readOnly
363+
checked={selected}
364+
className="pointer-events-none mr-2"
365+
/>
366+
</div>
367+
)
368+
})}
369+
</div>
370+
</Resizable>
349371
</div>
350372
)
351373
}
@@ -377,6 +399,11 @@ export const Console: React.FC<Props> = ({ lines }) => {
377399
setPopout(false)
378400
GameRunner.jumpToRound(round)
379401
GameRenderer.setSelectedRobot(id)
402+
403+
// If turn playback is enabled, focus the robot's exact turn as well
404+
if (GameRunner.match?.playbackPerTurn) {
405+
GameRunner.jumpToRobotTurn(id)
406+
}
380407
}
381408

382409
const ConsoleRow = (props: { index: number; style: any }) => {
@@ -390,7 +417,7 @@ export const Console: React.FC<Props> = ({ lines }) => {
390417
const team = found[1]
391418
const id = Number(found[2])
392419
const round = Number(found[3])
393-
const ogText = found[4]
420+
const ogText = found[4].replace(/\n/g, ' ')
394421

395422
return (
396423
<div className="flex items-center gap-1 sele" style={props.style}>
@@ -400,9 +427,7 @@ export const Console: React.FC<Props> = ({ lines }) => {
400427
>
401428
{`[Team ${team}, ID #${id}, Round ${round}]`}
402429
</span>
403-
<span className={getLineClass(lines.get(props.index)!) + ' text-xs whitespace-nowrap'}>
404-
{ogText}
405-
</span>
430+
<span className={getLineClass(lines.get(props.index)!) + ' text-xs whitespace-pre'}>{ogText}</span>
406431
</div>
407432
)
408433
}

client/src/components/sidebar/sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export const Sidebar: React.FC = () => {
213213
</div>
214214
</>
215215
)}
216-
<div className="h-full overflow-y-scroll overflow-x-hidden">
216+
<div className="h-full overflow-y-auto overflow-x-hidden">
217217
<GamePage open={open && page == PageType.GAME} />
218218
<QueuePage open={open && page == PageType.QUEUE} />
219219
<RunnerPage open={open && page == PageType.RUNNER} scaffold={scaffold} />

0 commit comments

Comments
 (0)