feat: surface SCUM vehicle functional state

This commit is contained in:
npc0-hue
2026-09-18 16:56:36 +08:00
parent 156392df1e
commit 60d479db71
13 changed files with 62 additions and 17 deletions
@@ -708,7 +708,7 @@ function mapHoverCard(e: ReactLike["createElement"], point: RecordMap, data: SCU
} else if (layer === "vehicles") {
push("载具", vehicleLabelFor(point));
push("类名", textField(point, "className", "vehicleClass", "entityClass", "vehicleType"));
push("状态", textField(point, "status", "state") || (boolField(point, "exists", "existsInGame") ? "存在" : ""));
push("状态", textField(point, "status", "state") || vehicleFunctionalLabel(point) || (boolField(point, "exists", "existsInGame") ? "存在" : ""));
push("耐久", vehicleDurabilityLabel(point));
push("车锁", vehicleLockLabel(point, data));
pushDate("最后观测", "vehicleObservedAt", "lastObservedAt", "observedAt", "updatedAt");
@@ -760,6 +760,11 @@ function vehicleDurabilityLabel(point: RecordMap): string {
return value ? `${value}${textField(point, "durabilityMax", "maxHealth") ? ` / ${textField(point, "durabilityMax", "maxHealth")}` : ""}` : "";
}
function vehicleFunctionalLabel(point: RecordMap): string {
if (field(point, "functional", "isFunctional") === undefined) return "";
return boolField(point, "functional", "isFunctional") ? "可用" : "不可用";
}
function mapUserMenu(e: ReactLike["createElement"], view: ViewState, users: Array<{ key: string; label: string; count: number }>, labels: Map<string, string>) {
const selected = view.mapUsers;
const rows = [...users, ...(selected ?? []).filter((key) => !users.some((user) => user.key === key)).map((key) => ({ key, label: labels.get(key) || key, count: 0 }))];
@@ -1179,7 +1184,7 @@ function mapPointStrip(e: ReactLike["createElement"], point: RecordMap, trails:
e("span", { className: "provider-id" }, `ID ${textField(point, "subjectId", "id", "_recordKey") || "unknown"}`),
e("span", { className: "provider-id" }, `来源 ${textField(point, "source") || "平台表"}`),
e("span", { className: "provider-id" }, freshness(point)),
layerOf(point) === "vehicles" ? e("span", { className: "provider-id" }, `${textField(point, "className", "vehicleClass", "vehicleType") || "unknown"} · ${textField(point, "status", "state", "isFunctional") || "unknown"} · ${boolField(point, "locked") ? "已上锁" : "未上锁/未知"} · 访问 ${dateField(point, "lastAccessTime", "vehicleObservedAt", "sampledAt")}`) : null),
layerOf(point) === "vehicles" ? e("span", { className: "provider-id" }, `${textField(point, "className", "vehicleClass", "vehicleType") || "unknown"} · ${textField(point, "status", "state") || vehicleFunctionalLabel(point) || "unknown"} · ${boolField(point, "locked") ? "已上锁" : "未上锁/未知"} · 访问 ${dateField(point, "lastAccessTime", "vehicleObservedAt", "sampledAt")}`) : null),
locks.length ? e("div", { className: "console-row-list" }, locks.map((row, index) => e("div", { key: `selected-lock-${index}`, className: "console-row" }, e("span", null, dateField(row, "lockedAt", "createdAt")), e("strong", null, textField(row, "scumUserId") || "unknown"), e("strong", null, textField(row, "steamId") || "unknown")))) : null,
trails.length ? e("div", { className: "console-row-list" }, trails.map((row, index) => e("div", { key: `selected-trail-${index}`, className: "console-row" }, e("span", null, dateField(row, "sampledAt", "observedAt")), e("strong", null, coords(row)), e("strong", null, textField(row, "source") || "平台轨迹表")))) : null
);
@@ -370,7 +370,7 @@
},
{
"key": "scum.database.vehicles",
"title": "Read SCUM vehicle positions and classes",
"title": "Read SCUM vehicle positions, classes, and functional state",
"permission": "server.game-client.read",
"engine": "sqlite",
"transportKey": "scum-database",
@@ -392,6 +392,7 @@
"gameVehicleId": "gameVehicleId",
"vehicleClass": "vehicleClass",
"displayName": "displayName",
"functional": "functional",
"exists": "existsInGame",
"x": "x",
"y": "y",
@@ -2,6 +2,7 @@ SELECT
CAST(spawner.vehicle_entity_id AS TEXT) AS gameVehicleId,
entity.class AS vehicleClass,
spawner.vehicle_alias AS displayName,
spawner.is_vehicle_functional AS functional,
1 AS existsInGame,
entity.location_x AS x,
entity.location_y AS y,
+4 -2
View File
@@ -39,7 +39,7 @@ const surfaceData: SCUMSurfaceData = {
mapPoints: [{ id: "poi-1", name: "Airfield", layer: "other", x: 800, y: 900, z: 10, source: "plugin-map" }],
mapRegions: [{ id: "region-1", name: "Safe Zone", x: 500, y: 600, z: 0, source: "server-config" }],
mapSettings: [],
vehicles: [{ id: "scum-vehicle-1", vehicleId: "scum-vehicle-1", gameVehicleId: "veh-1", label: "Laika", className: "BPC_Laika_C", locked: true, position: { x: 400, y: 200, z: 0 }, freshness: { status: "fresh" } }],
vehicles: [{ id: "scum-vehicle-1", vehicleId: "scum-vehicle-1", gameVehicleId: "veh-1", label: "Laika", className: "BPC_Laika_C", functional: true, locked: true, position: { x: 400, y: 200, z: 0 }, freshness: { status: "fresh" } }],
vehicleLocks: [{ id: "lock-1", scumVehicleId: "scum-vehicle-1", gameVehicleId: "veh-1", scumUserId: "scum-user-1", steamId: "76561198000000001", lockedAt: "2026-08-10T00:00:04Z", source: "platform.scum_vehicle_lock" }],
trajectories: [
{ subjectType: "player", subjectId: "76561198000000001", steamId: "76561198000000001", displayName: "Mira", x: 10, y: 20, z: 3, sampledAt: "2026-08-10T00:00:03Z", source: "platform.scum_user_trajectory" },
@@ -297,7 +297,9 @@ describe("SCUM plugin feature module", () => {
expect(view.elements.find((element) => element.label === "SCUM 地图图层")?.style?.backgroundImage).toContain("scum-map-terrain-4096.webp");
expect(view.buttons.map((button) => button.label)).toEqual(expect.arrayContaining(["", "", "复位", "适应轨迹"]));
const vehicleView = renderAndCollect({ pageKey: "live-map", pageTitle: "实时地图", data: { ...surfaceData, players: [], mapPoints: [], flags: [], mapRegions: [] } });
expect(vehicleView.texts).toEqual(expect.arrayContaining(["BPC_Laika_C · unknown · 已上锁 · 访问 unknown", "scum-user-1", "76561198000000001"]));
expect(vehicleView.texts).toEqual(expect.arrayContaining(["BPC_Laika_C · 可用 · 已上锁 · 访问 unknown", "scum-user-1", "76561198000000001"]));
const unavailableVehicleView = renderAndCollect({ pageKey: "live-map", pageTitle: "实时地图", data: { ...surfaceData, vehicles: [{ ...surfaceData.vehicles[0], functional: false }], players: [], mapPoints: [], flags: [], mapRegions: [] } });
expect(unavailableVehicleView.texts).toContain("BPC_Laika_C · 不可用 · 已上锁 · 访问 unknown");
});
it("keeps the live map full width with one compact filter row and dropdown menus", () => {