Update viewer AI state panels
This commit is contained in:
@@ -85,6 +85,99 @@ const factionMap = computed(() =>
|
|||||||
new Map(gmStore.factions.map((f) => [f.id, f.label])),
|
new Map(gmStore.factions.map((f) => [f.id, f.label])),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function titleCaseToken(value: string | null | undefined) {
|
||||||
|
if (!value) return "—";
|
||||||
|
return value
|
||||||
|
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||||
|
.replace(/[-_]+/g, " ")
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.trim()
|
||||||
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactNumber(value: number | null | undefined, digits = 0) {
|
||||||
|
if (value == null || Number.isNaN(value)) return "—";
|
||||||
|
return value.toFixed(digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactRate(value: number | null | undefined) {
|
||||||
|
if (value == null || Number.isNaN(value)) return "—";
|
||||||
|
const sign = value > 0.001 ? "+" : "";
|
||||||
|
return `${sign}${value.toFixed(2)}/s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeadObjective(faction: FactionSnapshot) {
|
||||||
|
return [...(faction.objectives ?? [])]
|
||||||
|
.sort((left, right) => right.priority - left.priority)
|
||||||
|
.find((objective) => objective.state !== "Complete" && objective.state !== "Cancelled")
|
||||||
|
?? faction.objectives?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeadStep(faction: FactionSnapshot) {
|
||||||
|
const objective = getLeadObjective(faction);
|
||||||
|
return [...(objective?.steps ?? [])]
|
||||||
|
.sort((left, right) => right.priority - left.priority)
|
||||||
|
.find((step) => step.status !== "Complete" && step.status !== "Cancelled")
|
||||||
|
?? objective?.steps?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeadTask(faction: FactionSnapshot) {
|
||||||
|
return [...(faction.issuedTasks ?? [])]
|
||||||
|
.sort((left, right) => right.priority - left.priority)
|
||||||
|
.find((task) => task.state !== "Complete" && task.state !== "Cancelled")
|
||||||
|
?? faction.issuedTasks?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeCommodityState(faction: FactionSnapshot, itemId: string, shortLabel: string) {
|
||||||
|
const signal = faction.blackboard?.commoditySignals.find((entry) => entry.itemId === itemId);
|
||||||
|
if (!signal) return `${shortLabel} —`;
|
||||||
|
return `${shortLabel} ${titleCaseToken(signal.level)} ${compactRate(signal.projectedNetRatePerSecond)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionStrategicState(faction: FactionSnapshot) {
|
||||||
|
const objective = getLeadObjective(faction);
|
||||||
|
if (!objective) return "No objectives";
|
||||||
|
return `${titleCaseToken(objective.kind)} · ${titleCaseToken(objective.state)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionLeadStep(faction: FactionSnapshot) {
|
||||||
|
const step = getLeadStep(faction);
|
||||||
|
if (!step) return "No steps";
|
||||||
|
const target = step.commodityId ?? step.moduleId ?? step.targetFactionId ?? step.targetSiteId;
|
||||||
|
return target
|
||||||
|
? `${titleCaseToken(step.kind)} · ${titleCaseToken(step.status)} · ${target}`
|
||||||
|
: `${titleCaseToken(step.kind)} · ${titleCaseToken(step.status)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionLeadTask(faction: FactionSnapshot) {
|
||||||
|
const task = getLeadTask(faction);
|
||||||
|
if (!task) return "No tasks";
|
||||||
|
const target = task.shipRole ?? task.commodityId ?? task.moduleId ?? task.targetFactionId ?? task.targetSiteId;
|
||||||
|
return target
|
||||||
|
? `${titleCaseToken(task.kind)} · ${titleCaseToken(task.state)} · ${target}`
|
||||||
|
: `${titleCaseToken(task.kind)} · ${titleCaseToken(task.state)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionPriority(faction: FactionSnapshot) {
|
||||||
|
const priority = [...(faction.strategicPriorities ?? [])]
|
||||||
|
.sort((left, right) => right.priority - left.priority)[0];
|
||||||
|
return priority ? `${titleCaseToken(priority.goalName)} · ${compactNumber(priority.priority, 0)}` : "—";
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionEconomy(faction: FactionSnapshot) {
|
||||||
|
return [
|
||||||
|
describeCommodityState(faction, "refinedmetals", "RM"),
|
||||||
|
describeCommodityState(faction, "hullparts", "HP"),
|
||||||
|
describeCommodityState(faction, "claytronics", "CL"),
|
||||||
|
].join(" | ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function describeFactionThreat(faction: FactionSnapshot) {
|
||||||
|
const blackboard = faction.blackboard;
|
||||||
|
if (!blackboard) return "—";
|
||||||
|
return `Enemy ships ${blackboard.enemyShipCount} · stations ${blackboard.enemyStationCount}`;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Ships table ────────────────────────────────────────────────────────────
|
// ── Ships table ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
type ShipRow = {
|
type ShipRow = {
|
||||||
@@ -94,7 +187,10 @@ type ShipRow = {
|
|||||||
faction: string;
|
faction: string;
|
||||||
system: string;
|
system: string;
|
||||||
state: string;
|
state: string;
|
||||||
|
objective: string;
|
||||||
behavior: string;
|
behavior: string;
|
||||||
|
phase: string;
|
||||||
|
action: string;
|
||||||
task: string;
|
task: string;
|
||||||
cargo: number;
|
cargo: number;
|
||||||
health: number;
|
health: number;
|
||||||
@@ -107,9 +203,12 @@ const shipRows = computed<ShipRow[]>(() =>
|
|||||||
class: s.class,
|
class: s.class,
|
||||||
faction: factionMap.value.get(s.factionId) ?? s.factionId,
|
faction: factionMap.value.get(s.factionId) ?? s.factionId,
|
||||||
system: s.systemId,
|
system: s.systemId,
|
||||||
state: s.state,
|
state: titleCaseToken(s.state),
|
||||||
behavior: s.defaultBehaviorKind + (s.behaviorPhase ? ` · ${s.behaviorPhase}` : ""),
|
objective: s.commanderObjective ? titleCaseToken(s.commanderObjective) : "—",
|
||||||
task: s.controllerTaskKind,
|
behavior: titleCaseToken(s.defaultBehaviorKind),
|
||||||
|
phase: s.behaviorPhase ? titleCaseToken(s.behaviorPhase) : "—",
|
||||||
|
action: s.currentAction ? `${s.currentAction.label} ${Math.round(s.currentAction.progress * 100)}%` : "—",
|
||||||
|
task: titleCaseToken(s.controllerTaskKind),
|
||||||
cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
|
cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
|
||||||
health: Math.round(s.health),
|
health: Math.round(s.health),
|
||||||
})),
|
})),
|
||||||
@@ -121,8 +220,11 @@ const shipColumns = [
|
|||||||
shipColumnHelper.accessor("class", { header: "Class" }),
|
shipColumnHelper.accessor("class", { header: "Class" }),
|
||||||
shipColumnHelper.accessor("faction", { header: "Faction" }),
|
shipColumnHelper.accessor("faction", { header: "Faction" }),
|
||||||
shipColumnHelper.accessor("system", { header: "System" }),
|
shipColumnHelper.accessor("system", { header: "System" }),
|
||||||
shipColumnHelper.accessor("state", { header: "State" }),
|
shipColumnHelper.accessor("state", { header: "Ship State" }),
|
||||||
|
shipColumnHelper.accessor("objective", { header: "Commander Objective" }),
|
||||||
shipColumnHelper.accessor("behavior", { header: "Behavior" }),
|
shipColumnHelper.accessor("behavior", { header: "Behavior" }),
|
||||||
|
shipColumnHelper.accessor("phase", { header: "Phase" }),
|
||||||
|
shipColumnHelper.accessor("action", { header: "Current Action" }),
|
||||||
shipColumnHelper.accessor("task", { header: "Task" }),
|
shipColumnHelper.accessor("task", { header: "Task" }),
|
||||||
shipColumnHelper.accessor("cargo", { header: "Cargo" }),
|
shipColumnHelper.accessor("cargo", { header: "Cargo" }),
|
||||||
shipColumnHelper.accessor("health", { header: "HP" }),
|
shipColumnHelper.accessor("health", { header: "HP" }),
|
||||||
@@ -130,7 +232,7 @@ const shipColumns = [
|
|||||||
|
|
||||||
const shipFilter = ref("");
|
const shipFilter = ref("");
|
||||||
const shipSorting = ref<SortingState>([]);
|
const shipSorting = ref<SortingState>([]);
|
||||||
const shipOrder = useColumnOrder(["label", "class", "faction", "system", "state", "behavior", "task", "cargo", "health"]);
|
const shipOrder = useColumnOrder(["label", "class", "faction", "system", "state", "objective", "behavior", "phase", "action", "task", "cargo", "health"]);
|
||||||
|
|
||||||
const shipTable = useVueTable({
|
const shipTable = useVueTable({
|
||||||
get data() { return shipRows.value; },
|
get data() { return shipRows.value; },
|
||||||
@@ -156,11 +258,14 @@ type StationRow = {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
category: string;
|
category: string;
|
||||||
|
objective: string;
|
||||||
faction: string;
|
faction: string;
|
||||||
system: string;
|
system: string;
|
||||||
|
process: string;
|
||||||
|
workforce: string;
|
||||||
docked: string;
|
docked: string;
|
||||||
|
orders: number;
|
||||||
cargo: number;
|
cargo: number;
|
||||||
population: number;
|
|
||||||
modules: number;
|
modules: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -169,11 +274,16 @@ const stationRows = computed<StationRow[]>(() =>
|
|||||||
id: s.id,
|
id: s.id,
|
||||||
label: s.label,
|
label: s.label,
|
||||||
category: s.category,
|
category: s.category,
|
||||||
|
objective: titleCaseToken(s.objective),
|
||||||
faction: factionMap.value.get(s.factionId) ?? s.factionId,
|
faction: factionMap.value.get(s.factionId) ?? s.factionId,
|
||||||
system: s.systemId,
|
system: s.systemId,
|
||||||
|
process: s.currentProcesses.length > 0
|
||||||
|
? s.currentProcesses.map((process) => `${process.label} ${Math.round(process.progress * 100)}%`).join(" | ")
|
||||||
|
: "Idle",
|
||||||
|
workforce: `${Math.round(s.population)} / ${Math.round(s.populationCapacity)} · ${Math.round(s.workforceEffectiveRatio * 100)}%`,
|
||||||
docked: `${s.dockedShips} / ${s.dockingPads}`,
|
docked: `${s.dockedShips} / ${s.dockingPads}`,
|
||||||
|
orders: s.marketOrderIds.length,
|
||||||
cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
|
cargo: s.inventory.reduce((sum, e) => sum + e.amount, 0),
|
||||||
population: Math.round(s.population),
|
|
||||||
modules: s.installedModules.length,
|
modules: s.installedModules.length,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
@@ -182,17 +292,20 @@ const stationColumnHelper = createColumnHelper<StationRow>();
|
|||||||
const stationColumns = [
|
const stationColumns = [
|
||||||
stationColumnHelper.accessor("label", { header: "Name" }),
|
stationColumnHelper.accessor("label", { header: "Name" }),
|
||||||
stationColumnHelper.accessor("category", { header: "Category" }),
|
stationColumnHelper.accessor("category", { header: "Category" }),
|
||||||
|
stationColumnHelper.accessor("objective", { header: "Objective" }),
|
||||||
stationColumnHelper.accessor("faction", { header: "Faction" }),
|
stationColumnHelper.accessor("faction", { header: "Faction" }),
|
||||||
stationColumnHelper.accessor("system", { header: "System" }),
|
stationColumnHelper.accessor("system", { header: "System" }),
|
||||||
|
stationColumnHelper.accessor("process", { header: "Production" }),
|
||||||
|
stationColumnHelper.accessor("workforce", { header: "Workforce" }),
|
||||||
stationColumnHelper.accessor("docked", { header: "Docked" }),
|
stationColumnHelper.accessor("docked", { header: "Docked" }),
|
||||||
|
stationColumnHelper.accessor("orders", { header: "Orders" }),
|
||||||
stationColumnHelper.accessor("cargo", { header: "Cargo" }),
|
stationColumnHelper.accessor("cargo", { header: "Cargo" }),
|
||||||
stationColumnHelper.accessor("population", { header: "Pop" }),
|
|
||||||
stationColumnHelper.accessor("modules", { header: "Modules" }),
|
stationColumnHelper.accessor("modules", { header: "Modules" }),
|
||||||
];
|
];
|
||||||
|
|
||||||
const stationFilter = ref("");
|
const stationFilter = ref("");
|
||||||
const stationSorting = ref<SortingState>([]);
|
const stationSorting = ref<SortingState>([]);
|
||||||
const stationOrder = useColumnOrder(["label", "category", "faction", "system", "docked", "cargo", "population", "modules"]);
|
const stationOrder = useColumnOrder(["label", "category", "objective", "faction", "system", "process", "workforce", "docked", "orders", "cargo", "modules"]);
|
||||||
|
|
||||||
const stationTable = useVueTable({
|
const stationTable = useVueTable({
|
||||||
get data() { return stationRows.value; },
|
get data() { return stationRows.value; },
|
||||||
@@ -217,32 +330,41 @@ const stationTable = useVueTable({
|
|||||||
type FactionRow = {
|
type FactionRow = {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
planCycle: number;
|
||||||
|
priority: string;
|
||||||
|
strategicState: string;
|
||||||
|
leadStep: string;
|
||||||
|
leadTask: string;
|
||||||
|
warReadiness: string;
|
||||||
|
economy: string;
|
||||||
|
threat: string;
|
||||||
|
fleets: string;
|
||||||
|
systems: string;
|
||||||
credits: number;
|
credits: number;
|
||||||
population: number;
|
population: number;
|
||||||
military: number;
|
|
||||||
miners: number;
|
|
||||||
transport: number;
|
|
||||||
constructors: number;
|
|
||||||
systems: string;
|
|
||||||
ore: number;
|
|
||||||
shipsBuilt: number;
|
shipsBuilt: number;
|
||||||
shipsLost: number;
|
shipsLost: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const factionRows = computed<FactionRow[]>(() =>
|
const factionRows = computed<FactionRow[]>(() =>
|
||||||
gmStore.factions.map((f) => {
|
gmStore.factions.map((f) => {
|
||||||
const gs = f.goapState;
|
const assessment = f.strategicAssessment;
|
||||||
|
const blackboard = f.blackboard;
|
||||||
return {
|
return {
|
||||||
id: f.id,
|
id: f.id,
|
||||||
label: f.label,
|
label: f.label,
|
||||||
|
planCycle: blackboard?.planCycle ?? 0,
|
||||||
|
priority: describeFactionPriority(f),
|
||||||
|
strategicState: describeFactionStrategicState(f),
|
||||||
|
leadStep: describeFactionLeadStep(f),
|
||||||
|
leadTask: describeFactionLeadTask(f),
|
||||||
|
warReadiness: `Industry ${blackboard?.hasWarIndustrySupplyChain ? "yes" : "no"} · Shipyard ${blackboard?.hasShipyard ? "yes" : "no"}${blackboard?.hasActiveExpansionProject ? ` · Expanding ${blackboard.activeExpansionCommodityId ?? blackboard.activeExpansionModuleId ?? "site"}` : ""}`,
|
||||||
|
economy: describeFactionEconomy(f),
|
||||||
|
threat: describeFactionThreat(f),
|
||||||
|
fleets: assessment ? `M ${assessment.militaryShipCount}/${blackboard?.targetWarshipCount ?? 0} · Mn ${assessment.minerShipCount} · Tr ${assessment.transportShipCount} · Cn ${assessment.constructorShipCount}` : "—",
|
||||||
|
systems: assessment ? `${assessment.controlledSystemCount} / ${assessment.targetSystemCount}` : "—",
|
||||||
credits: Math.round(f.credits),
|
credits: Math.round(f.credits),
|
||||||
population: Math.round(f.populationTotal),
|
population: Math.round(f.populationTotal),
|
||||||
military: gs?.militaryShipCount ?? 0,
|
|
||||||
miners: gs?.minerShipCount ?? 0,
|
|
||||||
transport: gs?.transportShipCount ?? 0,
|
|
||||||
constructors: gs?.constructorShipCount ?? 0,
|
|
||||||
systems: gs ? `${gs.controlledSystemCount} / ${gs.targetSystemCount}` : "—",
|
|
||||||
ore: gs ? Math.round(gs.oreStockpile) : 0,
|
|
||||||
shipsBuilt: f.shipsBuilt,
|
shipsBuilt: f.shipsBuilt,
|
||||||
shipsLost: f.shipsLost,
|
shipsLost: f.shipsLost,
|
||||||
};
|
};
|
||||||
@@ -252,21 +374,25 @@ const factionRows = computed<FactionRow[]>(() =>
|
|||||||
const factionColumnHelper = createColumnHelper<FactionRow>();
|
const factionColumnHelper = createColumnHelper<FactionRow>();
|
||||||
const factionColumns = [
|
const factionColumns = [
|
||||||
factionColumnHelper.accessor("label", { header: "Faction" }),
|
factionColumnHelper.accessor("label", { header: "Faction" }),
|
||||||
|
factionColumnHelper.accessor("planCycle", { header: "Cycle" }),
|
||||||
|
factionColumnHelper.accessor("priority", { header: "Top Priority" }),
|
||||||
|
factionColumnHelper.accessor("strategicState", { header: "Objective" }),
|
||||||
|
factionColumnHelper.accessor("leadStep", { header: "Lead Step" }),
|
||||||
|
factionColumnHelper.accessor("leadTask", { header: "Issued Task" }),
|
||||||
|
factionColumnHelper.accessor("warReadiness", { header: "Campaign State" }),
|
||||||
|
factionColumnHelper.accessor("economy", { header: "Economy" }),
|
||||||
|
factionColumnHelper.accessor("threat", { header: "Threat" }),
|
||||||
|
factionColumnHelper.accessor("fleets", { header: "Fleets" }),
|
||||||
|
factionColumnHelper.accessor("systems", { header: "Systems" }),
|
||||||
factionColumnHelper.accessor("credits", { header: "Credits" }),
|
factionColumnHelper.accessor("credits", { header: "Credits" }),
|
||||||
factionColumnHelper.accessor("population", { header: "Pop" }),
|
factionColumnHelper.accessor("population", { header: "Pop" }),
|
||||||
factionColumnHelper.accessor("military", { header: "Military" }),
|
|
||||||
factionColumnHelper.accessor("miners", { header: "Miners" }),
|
|
||||||
factionColumnHelper.accessor("transport", { header: "Transport" }),
|
|
||||||
factionColumnHelper.accessor("constructors", { header: "Constructors" }),
|
|
||||||
factionColumnHelper.accessor("systems", { header: "Systems" }),
|
|
||||||
factionColumnHelper.accessor("ore", { header: "Ore" }),
|
|
||||||
factionColumnHelper.accessor("shipsBuilt", { header: "Built" }),
|
factionColumnHelper.accessor("shipsBuilt", { header: "Built" }),
|
||||||
factionColumnHelper.accessor("shipsLost", { header: "Lost" }),
|
factionColumnHelper.accessor("shipsLost", { header: "Lost" }),
|
||||||
];
|
];
|
||||||
|
|
||||||
const factionFilter = ref("");
|
const factionFilter = ref("");
|
||||||
const factionSorting = ref<SortingState>([]);
|
const factionSorting = ref<SortingState>([]);
|
||||||
const factionOrder = useColumnOrder(["label", "credits", "population", "military", "miners", "transport", "constructors", "systems", "ore", "shipsBuilt", "shipsLost"]);
|
const factionOrder = useColumnOrder(["label", "planCycle", "priority", "strategicState", "leadStep", "leadTask", "warReadiness", "economy", "threat", "fleets", "systems", "credits", "population", "shipsBuilt", "shipsLost"]);
|
||||||
|
|
||||||
const factionTable = useVueTable({
|
const factionTable = useVueTable({
|
||||||
get data() { return factionRows.value; },
|
get data() { return factionRows.value; },
|
||||||
@@ -350,7 +476,7 @@ function isStationSelected(id: string) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<GmWindow
|
<GmWindow
|
||||||
title="Ships"
|
title="AI States"
|
||||||
:initial-width="980"
|
:initial-width="980"
|
||||||
:initial-height="560"
|
:initial-height="560"
|
||||||
:initial-x="80"
|
:initial-x="80"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export interface FactionGoapState {
|
export interface FactionPlanningStateSnapshot {
|
||||||
militaryShipCount: number;
|
militaryShipCount: number;
|
||||||
minerShipCount: number;
|
minerShipCount: number;
|
||||||
transportShipCount: number;
|
transportShipCount: number;
|
||||||
@@ -7,14 +7,145 @@ export interface FactionGoapState {
|
|||||||
targetSystemCount: number;
|
targetSystemCount: number;
|
||||||
hasShipFactory: boolean;
|
hasShipFactory: boolean;
|
||||||
oreStockpile: number;
|
oreStockpile: number;
|
||||||
refinedMetalsStockpile: number;
|
refinedMetalsAvailableStock: number;
|
||||||
|
refinedMetalsUsageRate: number;
|
||||||
|
refinedMetalsProjectedProductionRate: number;
|
||||||
|
refinedMetalsProjectedNetRate: number;
|
||||||
|
refinedMetalsLevelSeconds: number;
|
||||||
|
refinedMetalsLevel: string;
|
||||||
|
hullpartsAvailableStock: number;
|
||||||
|
hullpartsUsageRate: number;
|
||||||
|
hullpartsProjectedProductionRate: number;
|
||||||
|
hullpartsProjectedNetRate: number;
|
||||||
|
hullpartsLevelSeconds: number;
|
||||||
|
hullpartsLevel: string;
|
||||||
|
claytronicsAvailableStock: number;
|
||||||
|
claytronicsUsageRate: number;
|
||||||
|
claytronicsProjectedProductionRate: number;
|
||||||
|
claytronicsProjectedNetRate: number;
|
||||||
|
claytronicsLevelSeconds: number;
|
||||||
|
claytronicsLevel: string;
|
||||||
|
waterAvailableStock: number;
|
||||||
|
waterUsageRate: number;
|
||||||
|
waterProjectedProductionRate: number;
|
||||||
|
waterProjectedNetRate: number;
|
||||||
|
waterLevelSeconds: number;
|
||||||
|
waterLevel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FactionGoapPriority {
|
export interface FactionStrategicPrioritySnapshot {
|
||||||
goalName: string;
|
goalName: string;
|
||||||
priority: number;
|
priority: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FactionCommoditySignalSnapshot {
|
||||||
|
itemId: string;
|
||||||
|
availableStock: number;
|
||||||
|
onHand: number;
|
||||||
|
productionRatePerSecond: number;
|
||||||
|
committedProductionRatePerSecond: number;
|
||||||
|
usageRatePerSecond: number;
|
||||||
|
netRatePerSecond: number;
|
||||||
|
projectedNetRatePerSecond: number;
|
||||||
|
levelSeconds: number;
|
||||||
|
level: string;
|
||||||
|
projectedProductionRatePerSecond: number;
|
||||||
|
buyBacklog: number;
|
||||||
|
reservedForConstruction: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FactionThreatSignalSnapshot {
|
||||||
|
scopeId: string;
|
||||||
|
scopeKind: string;
|
||||||
|
enemyShipCount: number;
|
||||||
|
enemyStationCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FactionBlackboardSnapshot {
|
||||||
|
planCycle: number;
|
||||||
|
updatedAtUtc: string;
|
||||||
|
targetWarshipCount: number;
|
||||||
|
hasWarIndustrySupplyChain: boolean;
|
||||||
|
hasShipyard: boolean;
|
||||||
|
hasActiveExpansionProject: boolean;
|
||||||
|
activeExpansionCommodityId?: string | null;
|
||||||
|
activeExpansionModuleId?: string | null;
|
||||||
|
activeExpansionSiteId?: string | null;
|
||||||
|
activeExpansionSystemId?: string | null;
|
||||||
|
enemyFactionCount: number;
|
||||||
|
enemyShipCount: number;
|
||||||
|
enemyStationCount: number;
|
||||||
|
militaryShipCount: number;
|
||||||
|
minerShipCount: number;
|
||||||
|
transportShipCount: number;
|
||||||
|
constructorShipCount: number;
|
||||||
|
controlledSystemCount: number;
|
||||||
|
commoditySignals: FactionCommoditySignalSnapshot[];
|
||||||
|
threatSignals: FactionThreatSignalSnapshot[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FactionPlanStepSnapshot {
|
||||||
|
id: string;
|
||||||
|
kind: string;
|
||||||
|
status: string;
|
||||||
|
priority: number;
|
||||||
|
commodityId?: string | null;
|
||||||
|
moduleId?: string | null;
|
||||||
|
targetFactionId?: string | null;
|
||||||
|
targetSiteId?: string | null;
|
||||||
|
blockingReason?: string | null;
|
||||||
|
notes?: string | null;
|
||||||
|
lastEvaluatedCycle: number;
|
||||||
|
dependencyStepIds: string[];
|
||||||
|
requiredFacts: string[];
|
||||||
|
producedFacts: string[];
|
||||||
|
assignedAssets: string[];
|
||||||
|
issuedTaskIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FactionIssuedTaskSnapshot {
|
||||||
|
id: string;
|
||||||
|
kind: string;
|
||||||
|
state: string;
|
||||||
|
objectiveId: string;
|
||||||
|
stepId: string;
|
||||||
|
priority: number;
|
||||||
|
shipRole?: string | null;
|
||||||
|
commodityId?: string | null;
|
||||||
|
moduleId?: string | null;
|
||||||
|
targetFactionId?: string | null;
|
||||||
|
targetSystemId?: string | null;
|
||||||
|
targetSiteId?: string | null;
|
||||||
|
createdAtCycle: number;
|
||||||
|
updatedAtCycle: number;
|
||||||
|
blockingReason?: string | null;
|
||||||
|
notes?: string | null;
|
||||||
|
assignedAssets: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FactionObjectiveSnapshot {
|
||||||
|
id: string;
|
||||||
|
kind: string;
|
||||||
|
state: string;
|
||||||
|
priority: number;
|
||||||
|
parentObjectiveId?: string | null;
|
||||||
|
targetFactionId?: string | null;
|
||||||
|
targetSystemId?: string | null;
|
||||||
|
targetSiteId?: string | null;
|
||||||
|
targetRegionId?: string | null;
|
||||||
|
commodityId?: string | null;
|
||||||
|
moduleId?: string | null;
|
||||||
|
budgetWeight: number;
|
||||||
|
slotCost: number;
|
||||||
|
createdAtCycle: number;
|
||||||
|
updatedAtCycle: number;
|
||||||
|
invalidationReason?: string | null;
|
||||||
|
blockingReason?: string | null;
|
||||||
|
prerequisiteObjectiveIds: string[];
|
||||||
|
assignedAssets: string[];
|
||||||
|
steps: FactionPlanStepSnapshot[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface FactionSnapshot {
|
export interface FactionSnapshot {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -26,8 +157,11 @@ export interface FactionSnapshot {
|
|||||||
shipsBuilt: number;
|
shipsBuilt: number;
|
||||||
shipsLost: number;
|
shipsLost: number;
|
||||||
defaultPolicySetId?: string | null;
|
defaultPolicySetId?: string | null;
|
||||||
goapState?: FactionGoapState | null;
|
strategicAssessment?: FactionPlanningStateSnapshot | null;
|
||||||
goapPriorities?: FactionGoapPriority[] | null;
|
strategicPriorities?: FactionStrategicPrioritySnapshot[] | null;
|
||||||
|
blackboard?: FactionBlackboardSnapshot | null;
|
||||||
|
objectives?: FactionObjectiveSnapshot[] | null;
|
||||||
|
issuedTasks?: FactionIssuedTaskSnapshot[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FactionDelta extends FactionSnapshot {}
|
export interface FactionDelta extends FactionSnapshot {}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface StationSnapshot {
|
|||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
category: string;
|
category: string;
|
||||||
|
objective: string;
|
||||||
systemId: string;
|
systemId: string;
|
||||||
localPosition: Vector3Dto;
|
localPosition: Vector3Dto;
|
||||||
celestialId?: string | null;
|
celestialId?: string | null;
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import { describeShipCurrentAction, describeShipLocation, describeShipObjective,
|
|||||||
import type { CameraMode, Selectable, WorldState, PovLevel } from "./viewerTypes";
|
import type { CameraMode, Selectable, WorldState, PovLevel } from "./viewerTypes";
|
||||||
|
|
||||||
function buildFactionCard(faction: FactionSnapshot): OpsFactionCardState {
|
function buildFactionCard(faction: FactionSnapshot): OpsFactionCardState {
|
||||||
const state = faction.goapState;
|
const state = faction.strategicAssessment;
|
||||||
|
const blackboard = faction.blackboard;
|
||||||
|
const leadTask = [...(faction.issuedTasks ?? [])]
|
||||||
|
.sort((left, right) => right.priority - left.priority)[0];
|
||||||
return {
|
return {
|
||||||
kind: "faction",
|
kind: "faction",
|
||||||
id: faction.id,
|
id: faction.id,
|
||||||
@@ -20,9 +23,10 @@ function buildFactionCard(faction: FactionSnapshot): OpsFactionCardState {
|
|||||||
`Military ${state.militaryShipCount} · Miners ${state.minerShipCount}`,
|
`Military ${state.militaryShipCount} · Miners ${state.minerShipCount}`,
|
||||||
`Transport ${state.transportShipCount} · Constructors ${state.constructorShipCount}`,
|
`Transport ${state.transportShipCount} · Constructors ${state.constructorShipCount}`,
|
||||||
`Systems ${state.controlledSystemCount} / ${state.targetSystemCount}`,
|
`Systems ${state.controlledSystemCount} / ${state.targetSystemCount}`,
|
||||||
`Factory ${state.hasShipFactory ? "yes" : "no"} · Ore ${state.oreStockpile.toFixed(0)}`,
|
`Shipyard ${blackboard?.hasShipyard ? "yes" : "no"} · War industry ${blackboard?.hasWarIndustrySupplyChain ? "yes" : "no"}`,
|
||||||
|
leadTask ? `Task ${leadTask.kind}${leadTask.shipRole ? ` · ${leadTask.shipRole}` : ""}` : `Ore ${state.oreStockpile.toFixed(0)}`,
|
||||||
] : [],
|
] : [],
|
||||||
priorities: (faction.goapPriorities ?? []).map((entry) => ({
|
priorities: (faction.strategicPriorities ?? []).map((entry) => ({
|
||||||
label: entry.goalName,
|
label: entry.goalName,
|
||||||
value: entry.priority.toFixed(0),
|
value: entry.priority.toFixed(0),
|
||||||
})),
|
})),
|
||||||
|
|||||||
Reference in New Issue
Block a user