Skip to content

Commit b64b976

Browse files
committed
Add sector names and corp-member ship map glows
Every sector now has a human-readable name (e.g. "Iron Gate", "Grand Nexus Station"). Names flow from the DB through edge functions to the client banner, sector panel, and map-node popover. Agent tool schemas accept either a sector number or a name; the resolver is consolidated into `resolveSectorParam`. Also adds purple glow + pilot-name labels for active corporation members' ships on the big map, powered by a new corp_member_ships field on the ships-list payload. - universe_structure gains a `name` column (migration + LOWER(name) index) - scripts/generate_sector_names.py seeds 5000 adjective/noun names (shuffled, seed=42) with grand names for the 3 megaports - SectorSnapshot / LocalMapSector / MapSectorNode carry name through to the client; new `formatSectorLabel` util used in 3 UI surfaces - MOVE, PLOT_COURSE, PATH_WITH_REGION, LOCAL_MAP_REGION, and COMBAT_ACTION accept sector names in addition to IDs - Voice/task agent prompts teach "ID - Name" display format and megaport grand names - list_user_ships returns `corp_member_ships` for same-corp active pilots - SectorMapFX adds renderCorpMemberGlows + renderCorpMemberLabels passes
1 parent df0b2ec commit b64b976

25 files changed

Lines changed: 674 additions & 56 deletions

File tree

client/app/src/GameContext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,7 @@ export function GameProvider({ children }: GameProviderProps) {
16211621
console.debug("[GAME EVENT] Ships list", e.payload)
16221622
const data = e.payload as Msg.ShipsListMessage
16231623
useGameStore.getState().setShips(data.ships)
1624+
useGameStore.getState().setCorpMemberShips(data.corp_member_ships ?? [])
16241625
useGameStore.getState().resolveFetchPromise("get-my-ships")
16251626
break
16261627
}

client/app/src/components/SectorMap.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ interface MapProps {
6060
showLegend?: boolean
6161
coursePlot?: CoursePlot | null
6262
ships?: Array<{ sector: number; ship_name: string; ship_type: string }>
63+
corp_member_ships?: Array<{ sector: number; character_name: string; ship_name: string }>
6364
onNodeClick?: (node: MapSectorNode | null) => void
6465
onNodeEnter?: (node: MapSectorNode) => void
6566
onNodeExit?: (node: MapSectorNode) => void
@@ -129,6 +130,7 @@ const MapComponent = ({
129130
maxDistance = 2,
130131
coursePlot,
131132
ships,
133+
corp_member_ships,
132134
onNodeClick,
133135
onNodeEnter,
134136
onNodeExit,
@@ -155,6 +157,19 @@ const MapComponent = ({
155157
return map
156158
}, [ships])
157159

160+
const corpMemberShipsKey =
161+
corp_member_ships?.map((s) => `${s.sector}:${s.character_name}`).join(",") ?? ""
162+
const corpMemberShipsMap = useMemo(() => {
163+
if (!corp_member_ships || corp_member_ships.length === 0) return undefined
164+
const map = new Map<number, Array<{ character_name: string; ship_name: string }>>()
165+
for (const entry of corp_member_ships) {
166+
const existing = map.get(entry.sector) ?? []
167+
existing.push({ character_name: entry.character_name, ship_name: entry.ship_name })
168+
map.set(entry.sector, existing)
169+
}
170+
return map
171+
}, [corp_member_ships])
172+
158173
// Default center_sector_id to current_sector_id if not provided
159174
const center_sector_id = center_sector_id_prop ?? current_sector_id ?? 0
160175

@@ -180,6 +195,7 @@ const MapComponent = ({
180195
const lastConfigRef = useRef<Omit<SectorMapConfigBase, "center_sector_id"> | null>(null)
181196
const lastCoursePlotRef = useRef<CoursePlot | null | undefined>(coursePlot)
182197
const lastShipsKeyRef = useRef<string>(shipsKey)
198+
const lastCorpMemberShipsKeyRef = useRef<string>(corpMemberShipsKey)
183199
const lastCenterWorldRef = useRef<[number, number] | undefined>(center_world)
184200
const lastFitBoundsWorldRef = useRef<[number, number, number, number] | undefined>(
185201
fit_bounds_world
@@ -374,6 +390,7 @@ const MapComponent = ({
374390
maxDistance,
375391
coursePlot,
376392
ships: shipsMap,
393+
corp_member_ships: corpMemberShipsMap,
377394
})
378395
controllerRef.current = controller
379396
prevCenterSectorIdRef.current = center_sector_id
@@ -383,6 +400,7 @@ const MapComponent = ({
383400
lastConfigRef.current = baseConfig
384401
lastCoursePlotRef.current = coursePlot
385402
lastShipsKeyRef.current = shipsKey
403+
lastCorpMemberShipsKeyRef.current = corpMemberShipsKey
386404
lastCenterWorldRef.current = center_world
387405
lastFitBoundsWorldRef.current = fit_bounds_world
388406
lastMapFitEpochRef.current = mapFitEpoch
@@ -402,6 +420,7 @@ const MapComponent = ({
402420
const configChanged = lastConfigRef.current !== baseConfig
403421
const coursePlotChanged = !courseplotsEqual(lastCoursePlotRef.current, coursePlot)
404422
const shipsChanged = lastShipsKeyRef.current !== shipsKey
423+
const corpMemberShipsChanged = lastCorpMemberShipsKeyRef.current !== corpMemberShipsKey
405424
const centerWorldChanged = !tuplesEqual(lastCenterWorldRef.current, center_world)
406425
const fitBoundsWorldChanged = !tuplesEqual(lastFitBoundsWorldRef.current, fit_bounds_world)
407426
const mapFitEpochChanged = lastMapFitEpochRef.current !== mapFitEpoch
@@ -416,6 +435,7 @@ const MapComponent = ({
416435
!configChanged &&
417436
!coursePlotChanged &&
418437
!shipsChanged &&
438+
!corpMemberShipsChanged &&
419439
!centerWorldChanged &&
420440
!fitBoundsWorldChanged &&
421441
!mapFitEpochChanged &&
@@ -447,6 +467,7 @@ const MapComponent = ({
447467
data: normalizedMapData,
448468
coursePlot,
449469
ships: shipsMap,
470+
corp_member_ships: corpMemberShipsMap,
450471
})
451472

452473
// Determine if a camera reframe is needed
@@ -481,6 +502,7 @@ const MapComponent = ({
481502
maxDistanceOnly ||
482503
needsConfigUpdate ||
483504
shipsChanged ||
505+
corpMemberShipsChanged ||
484506
coursePlotChanged ||
485507
topologyChanged
486508
) {
@@ -499,6 +521,7 @@ const MapComponent = ({
499521
lastConfigRef.current = baseConfig
500522
lastCoursePlotRef.current = coursePlot
501523
lastShipsKeyRef.current = shipsKey
524+
lastCorpMemberShipsKeyRef.current = corpMemberShipsKey
502525
lastCenterWorldRef.current = center_world
503526
lastFitBoundsWorldRef.current = fit_bounds_world
504527
lastMapFitEpochRef.current = mapFitEpoch
@@ -511,6 +534,8 @@ const MapComponent = ({
511534
coursePlot,
512535
shipsKey,
513536
shipsMap,
537+
corpMemberShipsKey,
538+
corpMemberShipsMap,
514539
onMapFetch,
515540
effectiveWidth,
516541
effectiveHeight,
@@ -643,6 +668,16 @@ const areMapPropsEqual = (prevProps: MapProps, nextProps: MapProps): boolean =>
643668
}
644669
}
645670

671+
if (prevProps.corp_member_ships !== nextProps.corp_member_ships) {
672+
const prevKey =
673+
prevProps.corp_member_ships?.map((s) => `${s.sector}:${s.character_name}`).join(",") ?? ""
674+
const nextKey =
675+
nextProps.corp_member_ships?.map((s) => `${s.sector}:${s.character_name}`).join(",") ?? ""
676+
if (prevKey !== nextKey) {
677+
return false
678+
}
679+
}
680+
646681
// World-coordinate overrides (zoomMode)
647682
if (!tuplesEqual(prevProps.center_world, nextProps.center_world)) return false
648683
if (!tuplesEqual(prevProps.fit_bounds_world, nextProps.fit_bounds_world)) return false

client/app/src/components/SectorTitleBanner.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AnimatePresence, motion } from "motion/react"
66
import { ScrambleText, type ScrambleTextRef } from "@/fx/ScrambleText"
77
import useAudioStore from "@/stores/audio"
88
import useGameStore from "@/stores/game"
9+
import { formatSectorLabel } from "@/utils/formatting"
910
import { getPortCode } from "@/utils/port"
1011
import { cn } from "@/utils/tailwind"
1112

@@ -51,7 +52,7 @@ export const SectorTitleBanner = () => {
5152
)
5253

5354
const shouldDisplay = sector?.id !== undefined && !hasActiveTask && uiState !== "combat"
54-
const sectorText = `SECTOR ${sector?.id ?? "unknown"}`
55+
const sectorText = formatSectorLabel(sector).toUpperCase()
5556

5657
// Timer management
5758
const clearTimers = useCallback(() => {

client/app/src/components/panels/BigMapPanel.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SectorMap, { type MapConfig } from "@/components/SectorMap"
1414
import { NeuroSymbolicsIcon, QuantumFoamIcon, RetroOrganicsIcon } from "@/icons"
1515
import useGameStore from "@/stores/game"
1616
import { formatTimeAgoOrDate } from "@/utils/date"
17+
import { formatSectorLabel } from "@/utils/formatting"
1718
import { getFetchBounds } from "@/utils/map"
1819
import { getPortCode } from "@/utils/port"
1920
import { cn } from "@/utils/tailwind"
@@ -86,7 +87,7 @@ const MapNodeDetails = ({ node }: { node?: MapSectorNode | null }) => {
8687
className="h-auto w-3 self-stretch text-accent"
8788
/>
8889
<div className="flex flex-col gap-2 flex-1">
89-
<DottedTitle title={`Sector ${node.id.toString()}`} textColor="text-foreground" />
90+
<DottedTitle title={formatSectorLabel(node)} textColor="text-foreground" />
9091
<dl className="flex flex-col gap-2 uppercase text-xxs text-foreground">
9192
<div className="flex flex-row justify-between gap-2">
9293
<dt className="font-bold">Region</dt>
@@ -133,6 +134,7 @@ export const BigMapPanel = ({ config }: { config?: MapConfig }) => {
133134
const mapData = useGameStore.use.regional_map_data?.()
134135
const coursePlot = useGameStore.use.course_plot?.()
135136
const ships = useGameStore.use.ships?.()
137+
const corpMemberShips = useGameStore.use.corpMemberShips?.()
136138
const mapCenterSector = useGameStore((state) => state.mapCenterSector)
137139
const mapZoomLevel = useGameStore((state) => state.mapZoomLevel)
138140
const mapCenterWorld = useGameStore((state) => state.mapCenterWorld)
@@ -165,6 +167,12 @@ export const BigMapPanel = ({ config }: { config?: MapConfig }) => {
165167
ship_type: s.ship_type,
166168
}))
167169

170+
const corpMemberShipSectors = corpMemberShips?.data?.map((s) => ({
171+
sector: s.sector,
172+
character_name: s.character_name,
173+
ship_name: s.ship_name,
174+
}))
175+
168176
// Initial fetch of map data
169177
useEffect(() => {
170178
if (initialFetchRef.current) return
@@ -274,6 +282,7 @@ export const BigMapPanel = ({ config }: { config?: MapConfig }) => {
274282
onMapFetch={handleMapFetch}
275283
coursePlot={coursePlot ?? null}
276284
ships={shipSectors}
285+
corp_member_ships={corpMemberShipSectors}
277286
center_world={mapCenterWorld}
278287
fit_bounds_world={mapFitBoundsWorld}
279288
mapFitEpoch={mapFitEpoch}

client/app/src/components/panels/SectorPanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { GarrisonPanel } from "@/components/panels/GarrisonPanel"
1616
import { ShipCatalogue } from "@/components/panels/ShipCatalogue"
1717
import { SalvageIcon } from "@/icons"
1818
import useGameStore from "@/stores/game"
19+
import { formatSectorLabel } from "@/utils/formatting"
1920
import { getPortCode } from "@/utils/port"
2021
import { cn } from "@/utils/tailwind"
2122

@@ -96,7 +97,7 @@ export const SectorPanel = () => {
9697
)}
9798
>
9899
<CardHeader className="gap-0">
99-
<CardTitle>Sector {sector?.id}</CardTitle>
100+
<CardTitle>{formatSectorLabel(sector)}</CardTitle>
100101
</CardHeader>
101102

102103
<CardContent className="flex flex-row gap-ui-sm pr-0">

0 commit comments

Comments
 (0)