Add SCUM entity catalog with extracted map and icons

- Import 2492 entity records (2391 items, 54 vehicles, 25 zombies, 12 animals, 10 armed NPCs) with spawn commands and icon names from the SCUM Admin Helper 0.4.0.5 payload.
- Ship the 2604 extracted item/NPC icons plus 4096 terrain, topo, night and building-zone map layers.
- Resolve vehicle icons through the catalog so live-map and trajectory markers cover every known vehicle class.
- Point map geometry and the live map background at the 4096 terrain layer.
This commit is contained in:
npc0-hue
2026-09-15 15:56:44 +08:00
parent 05f5a97ba9
commit 59a8657297
2614 changed files with 2580 additions and 27 deletions
@@ -19,6 +19,7 @@ import {
type SCUMSurfaceData,
type SCUMWorkspaceActions
} from "./page-data.js";
import { scumVehicleIconUrl } from "./scum-catalog.js";
type StateSetter<T> = (next: T | ((previous: T) => T)) => void;
type InputEvent = { target?: { value?: string; checked?: boolean; open?: boolean }; stopPropagation?: () => void };
@@ -26,25 +27,9 @@ type GiftTab = "definitions" | "claims" | "deliveries" | "timed";
type MapLayer = "players" | "vehicles" | "flags" | "regions" | "other";
type PlayerPanelKind = "closed" | "gifts" | "items" | "history" | "trajectory";
type PlayerPanelState = { kind: PlayerPanelKind; playerId: string };
const scumMapBackground = new URL("../assets/map/scum-map-overview.jpg", import.meta.url).href;
const scumMapSize = 256;
const scumMapBackground = new URL("../assets/map/scum-map-terrain-4096.webp", import.meta.url).href;
const scumMapSize = 4096;
const scumSurfaceRefreshMs = 15000;
const vehicleIconByClass: Record<string, string> = {
BPC_Barba: new URL("../assets/vehicles/vehicle-BPC_Barba.webp", import.meta.url).href,
BPC_CityBike: new URL("../assets/vehicles/vehicle-BPC_CityBike.webp", import.meta.url).href,
BPC_Cruiser: new URL("../assets/vehicles/vehicle-BPC_Cruiser.webp", import.meta.url).href,
BPC_Dirtbike: new URL("../assets/vehicles/vehicle-BPC_Dirtbike.webp", import.meta.url).href,
BPC_Kinglet_Duster: new URL("../assets/vehicles/vehicle-BPC_Kinglet_Duster.webp", import.meta.url).href,
BPC_Kinglet_Mariner: new URL("../assets/vehicles/vehicle-BPC_Kinglet_Mariner.webp", import.meta.url).href,
BPC_Laika: new URL("../assets/vehicles/vehicle-BPC_Laika.webp", import.meta.url).href,
BPC_MountainBike: new URL("../assets/vehicles/vehicle-BPC_MountainBike.webp", import.meta.url).href,
BPC_Rager: new URL("../assets/vehicles/vehicle-BPC_Rager.webp", import.meta.url).href,
BPC_RIS: new URL("../assets/vehicles/vehicle-BPC_RIS.webp", import.meta.url).href,
BPC_Tractor: new URL("../assets/vehicles/vehicle-BPC_Tractor.webp", import.meta.url).href,
BPC_WolfsWagen: new URL("../assets/vehicles/vehicle-BPC_WolfsWagen.webp", import.meta.url).href,
BP_WheelBarrow_Improvised: new URL("../assets/vehicles/vehicle-BP_WheelBarrow_Improvised.webp", import.meta.url).href,
BP_WheelBarrow_Metal: new URL("../assets/vehicles/vehicle-BP_WheelBarrow_Metal.webp", import.meta.url).href
};
export type ReactLike = {
createElement: (...args: any[]) => any;
@@ -624,7 +609,7 @@ function trajectoryRecordsForPoint(rows: RecordMap[], point: RecordMap): RecordM
function vehicleLockRecordsForPoint(rows: RecordMap[], point: RecordMap): RecordMap[] { const ids = new Set([textField(point, "id"), textField(point, "scumVehicleId"), textField(point, "vehicleId"), textField(point, "gameVehicleId"), textField(point, "entityId"), textField(point, "subjectId")].filter(Boolean)); return rows.filter((row) => [textField(row, "scumVehicleId"), textField(row, "vehicleId"), textField(row, "gameVehicleId")].some((identity) => ids.has(identity))); }
function trajectoryIdentity(row: RecordMap): string[] { return [textField(row, "subjectId"), textField(row, "steamId"), textField(row, "gamePlayerId"), textField(row, "vehicleId"), textField(row, "id")].filter(Boolean); }
function trajectoryOrder(row: RecordMap): number { const stamp = Date.parse(textField(row, "sampledAt", "observedAt", "createdAt")); return Number.isNaN(stamp) ? 0 : stamp; }
function vehicleIconFor(point: RecordMap): string { const explicit = textField(point, "imagePath", "image_path"); if (explicit) return explicit.startsWith("/") ? explicit : `/${explicit}`; return vehicleIconByClass[normalizedVehicleClass(textField(point, "className", "vehicleClass", "entityClass", "vehicleType"))] ?? ""; }
function vehicleIconFor(point: RecordMap): string { const explicit = textField(point, "imagePath", "image_path"); if (explicit) return explicit.startsWith("/") ? explicit : `/${explicit}`; return scumVehicleIconUrl(normalizedVehicleClass(textField(point, "className", "vehicleClass", "entityClass", "vehicleType"))); }
function vehicleClassKey(value: string): string { return normalizedVehicleClass(value.replace(/^#spawnvehicle\s+/i, "")); }
function normalizedVehicleClass(value: string): string { return value.replace(/_C$/i, "").split(".").pop()?.trim() ?? value.trim(); }
function shortHash(value: string): string { return value ? `${value.slice(0, 10)}${value.slice(-6)}` : ""; }