143 lines
5.1 KiB
TypeScript
143 lines
5.1 KiB
TypeScript
import { type MapScene } from './mapRenderer'
|
|
import { type ControlPointStyleEntry, type CourseLegStyleEntry, type ScoreBandStyleEntry } from '../../game/presentation/courseStyleConfig'
|
|
|
|
export type RgbaColor = [number, number, number, number]
|
|
|
|
export interface ResolvedControlStyle {
|
|
entry: ControlPointStyleEntry
|
|
color: RgbaColor
|
|
}
|
|
|
|
export interface ResolvedLegStyle {
|
|
entry: CourseLegStyleEntry
|
|
color: RgbaColor
|
|
}
|
|
|
|
export function hexToRgbaColor(hex: string, alphaOverride?: number): RgbaColor {
|
|
const fallback: RgbaColor = [1, 1, 1, alphaOverride !== undefined ? alphaOverride : 1]
|
|
if (typeof hex !== 'string' || !hex || hex.charAt(0) !== '#') {
|
|
return fallback
|
|
}
|
|
|
|
const normalized = hex.slice(1)
|
|
if (normalized.length !== 6 && normalized.length !== 8) {
|
|
return fallback
|
|
}
|
|
|
|
const red = parseInt(normalized.slice(0, 2), 16)
|
|
const green = parseInt(normalized.slice(2, 4), 16)
|
|
const blue = parseInt(normalized.slice(4, 6), 16)
|
|
const alpha = normalized.length === 8 ? parseInt(normalized.slice(6, 8), 16) : 255
|
|
if (!Number.isFinite(red) || !Number.isFinite(green) || !Number.isFinite(blue) || !Number.isFinite(alpha)) {
|
|
return fallback
|
|
}
|
|
|
|
return [
|
|
red / 255,
|
|
green / 255,
|
|
blue / 255,
|
|
alphaOverride !== undefined ? alphaOverride : alpha / 255,
|
|
]
|
|
}
|
|
|
|
function resolveScoreBandStyle(scene: MapScene, sequence: number): ScoreBandStyleEntry | null {
|
|
const score = scene.controlScoresBySequence[sequence]
|
|
if (score === undefined) {
|
|
return null
|
|
}
|
|
|
|
const bands = scene.courseStyleConfig.scoreO.controls.scoreBands
|
|
for (let index = 0; index < bands.length; index += 1) {
|
|
const band = bands[index]
|
|
if (score >= band.min && score <= band.max) {
|
|
return band
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function resolveControlStyle(scene: MapScene, kind: 'start' | 'control' | 'finish', sequence: number | null, index?: number): ResolvedControlStyle {
|
|
if (kind === 'start') {
|
|
if (index !== undefined && scene.startStyleOverrides[index]) {
|
|
const entry = scene.startStyleOverrides[index]
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
const entry = scene.gameMode === 'score-o'
|
|
? scene.courseStyleConfig.scoreO.controls.start
|
|
: scene.courseStyleConfig.sequential.controls.start
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (kind === 'finish') {
|
|
if (index !== undefined && scene.finishStyleOverrides[index]) {
|
|
const entry = scene.finishStyleOverrides[index]
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
const entry = scene.gameMode === 'score-o'
|
|
? scene.courseStyleConfig.scoreO.controls.finish
|
|
: scene.courseStyleConfig.sequential.controls.finish
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (sequence === null) {
|
|
const entry = scene.courseStyleConfig.sequential.controls.default
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.controlStyleOverridesBySequence[sequence]) {
|
|
const entry = scene.controlStyleOverridesBySequence[sequence]
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.gameMode === 'score-o') {
|
|
if (scene.completedControlSequences.includes(sequence)) {
|
|
const entry = scene.courseStyleConfig.scoreO.controls.collected
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.focusedControlSequences.includes(sequence)) {
|
|
const entry = scene.courseStyleConfig.scoreO.controls.focused
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
const bandEntry = resolveScoreBandStyle(scene, sequence)
|
|
const entry = bandEntry || scene.courseStyleConfig.scoreO.controls.default
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.readyControlSequences.includes(sequence) || scene.activeControlSequences.includes(sequence)) {
|
|
const entry = scene.courseStyleConfig.sequential.controls.current
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.completedControlSequences.includes(sequence)) {
|
|
const entry = scene.courseStyleConfig.sequential.controls.completed
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.skippedControlSequences.includes(sequence)) {
|
|
const entry = scene.courseStyleConfig.sequential.controls.skipped
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
const entry = scene.courseStyleConfig.sequential.controls.default
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
export function resolveLegStyle(scene: MapScene, index: number): ResolvedLegStyle {
|
|
if (scene.legStyleOverridesByIndex[index]) {
|
|
const entry = scene.legStyleOverridesByIndex[index]
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
if (scene.gameMode === 'score-o') {
|
|
const entry = scene.courseStyleConfig.sequential.legs.default
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|
|
|
|
const completed = scene.completedLegIndices.includes(index)
|
|
const entry = completed ? scene.courseStyleConfig.sequential.legs.completed : scene.courseStyleConfig.sequential.legs.default
|
|
return { entry, color: hexToRgbaColor(entry.colorHex) }
|
|
}
|