| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- 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
- }
- function resolveCompletedBoundaryEntry(scene: MapScene, baseEntry: ControlPointStyleEntry): ControlPointStyleEntry {
- const completedPalette = scene.gameMode === 'score-o'
- ? scene.courseStyleConfig.scoreO.controls.collected
- : scene.courseStyleConfig.sequential.controls.completed
- return {
- ...baseEntry,
- colorHex: completedPalette.colorHex,
- labelColorHex: completedPalette.labelColorHex || baseEntry.labelColorHex,
- glowStrength: 0,
- }
- }
- function mergeControlStyleEntries(
- baseEntry: ControlPointStyleEntry,
- overrideEntry?: ControlPointStyleEntry | null,
- ): ControlPointStyleEntry {
- if (!overrideEntry) {
- return baseEntry
- }
- return {
- ...baseEntry,
- ...overrideEntry,
- }
- }
- function mergeLegStyleEntries(
- baseEntry: CourseLegStyleEntry,
- overrideEntry?: CourseLegStyleEntry | null,
- ): CourseLegStyleEntry {
- if (!overrideEntry) {
- return baseEntry
- }
- return {
- ...baseEntry,
- ...overrideEntry,
- }
- }
- 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') {
- const baseEntry = index !== undefined && scene.startStyleOverrides[index]
- ? scene.startStyleOverrides[index]
- : scene.gameMode === 'score-o'
- ? scene.courseStyleConfig.scoreO.controls.start
- : scene.courseStyleConfig.sequential.controls.start
- const entry = scene.completedStart
- ? resolveCompletedBoundaryEntry(scene, baseEntry)
- : baseEntry
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (kind === 'finish') {
- const baseEntry = index !== undefined && scene.finishStyleOverrides[index]
- ? scene.finishStyleOverrides[index]
- : scene.gameMode === 'score-o'
- ? scene.courseStyleConfig.scoreO.controls.finish
- : scene.courseStyleConfig.sequential.controls.finish
- const entry = scene.completedFinish
- ? resolveCompletedBoundaryEntry(scene, baseEntry)
- : baseEntry
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (sequence === null) {
- const entry = scene.courseStyleConfig.sequential.controls.default
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- const sequenceOverride = scene.controlStyleOverridesBySequence[sequence]
- const defaultOverride = scene.defaultControlStyleOverride
- if (scene.gameMode === 'score-o') {
- if (scene.completedControlSequences.includes(sequence)) {
- const entry = mergeControlStyleEntries(
- scene.courseStyleConfig.scoreO.controls.collected,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (scene.focusedControlSequences.includes(sequence)) {
- const bandEntry = resolveScoreBandStyle(scene, sequence)
- const baseEntry = bandEntry || scene.courseStyleConfig.scoreO.controls.default
- const focusedEntry = scene.courseStyleConfig.scoreO.controls.focused
- const focusedMergedEntry: ControlPointStyleEntry = {
- ...baseEntry,
- ...focusedEntry,
- colorHex: baseEntry.colorHex,
- }
- const entry = mergeControlStyleEntries(focusedMergedEntry, sequenceOverride || defaultOverride)
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- const bandEntry = resolveScoreBandStyle(scene, sequence)
- const entry = mergeControlStyleEntries(
- bandEntry || scene.courseStyleConfig.scoreO.controls.default,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (scene.readyControlSequences.includes(sequence) || scene.activeControlSequences.includes(sequence)) {
- const entry = mergeControlStyleEntries(
- scene.courseStyleConfig.sequential.controls.current,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (scene.completedControlSequences.includes(sequence)) {
- const entry = mergeControlStyleEntries(
- scene.courseStyleConfig.sequential.controls.completed,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- if (scene.skippedControlSequences.includes(sequence)) {
- const entry = mergeControlStyleEntries(
- scene.courseStyleConfig.sequential.controls.skipped,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- const entry = mergeControlStyleEntries(
- scene.courseStyleConfig.sequential.controls.default,
- sequenceOverride || defaultOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- export function resolveLegStyle(scene: MapScene, index: number): ResolvedLegStyle {
- if (scene.gameMode === 'score-o') {
- const entry = mergeLegStyleEntries(
- scene.courseStyleConfig.sequential.legs.default,
- scene.legStyleOverridesByIndex[index] || scene.defaultLegStyleOverride,
- )
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
- const completed = scene.completedLegIndices.includes(index)
- const baseEntry = completed ? scene.courseStyleConfig.sequential.legs.completed : scene.courseStyleConfig.sequential.legs.default
- const entry = mergeLegStyleEntries(baseEntry, scene.legStyleOverridesByIndex[index] || scene.defaultLegStyleOverride)
- return { entry, color: hexToRgbaColor(entry.colorHex) }
- }
|