Skip to content

Commit

Permalink
Good start to sensors.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderson1993 committed Feb 20, 2025
1 parent 66bb1f0 commit 6ebd7c7
Show file tree
Hide file tree
Showing 29 changed files with 1,333 additions and 260 deletions.
7 changes: 7 additions & 0 deletions app/.server/init/dataStreamEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ export function dataStreamEntity(e: Entity) {
};
}

if (e.components.scan) {
return {
id: e.id.toString(),
x: e.components.scan.progress,
};
}

const { parentId, type, ...position } = e.components.position || {};
const shouldSnap = e.components.snapInterpolation ? 1 : 0;
e.removeComponent("snapInterpolation");
Expand Down
63 changes: 41 additions & 22 deletions app/.server/systems/SensorScanSystem.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { pubsub } from "@thorium/.server/init/pubsub";
import { getPhaserCharge } from "@thorium/.server/systems/PhasersSystem";
import { getClassification } from "@thorium/cards/Navigation/getObjectClassification.server";
import { efficiency } from "@thorium/ecs-components/efficiency";
import { getShipSystems } from "@thorium/utils/.server/ship/getShipSystem";
import { type ECS, type Entity, System } from "@thorium/utils/ecs";
import type { scanRecord } from "@thorium/utils/flags/scanTypes";
import { getOrbitPosition } from "@thorium/utils/starmap/getOrbitPosition";
import type { KiloWattHour } from "@thorium/utils/unitTypes";
import { capitalCase } from "change-case";
import type { z } from "zod";

export class SensorScanSystem extends System {
/** The number of concurrent sensor scans */
Expand Down Expand Up @@ -33,17 +38,17 @@ export class SensorScanSystem extends System {
if (!scan) return;
if (scan.progress >= 1) {
// Handle repeat scans
if (scan.repeatInterval === null) this.ecs.removeEntity(entity);
else {
const intervalTime = scan.intervalTime + elapsedTimeSeconds;
if (scan.repeatInterval === null) {
return;
}
const intervalTime = scan.intervalTime + elapsedTimeSeconds;
entity.updateComponent("scan", {
intervalTime,
});
if (intervalTime > scan.repeatInterval) {
entity.updateComponent("scan", {
intervalTime,
progress: 0,
});
if (intervalTime > scan.repeatInterval) {
entity.updateComponent("scan", {
progress: 0,
});
}
}

return;
Expand All @@ -68,8 +73,9 @@ export class SensorScanSystem extends System {
}
}
const sensorSystem = sensors?.components.isSensors;
if (!sensors || !sensorSystem) return;
const scanCount = this.sensorsScanCount.get(sensors.id);
const shipId = sensors?.components.isShipSystem?.shipId;
if (!sensors || !sensorSystem || !shipId) return;
const scanCount = this.sensorsScanCount.get(shipId);
if (!scanCount) return;

const shipPosition = parent.components.position;
Expand All @@ -93,16 +99,16 @@ export class SensorScanSystem extends System {
shieldPenaltyMultiplier,
} = sensorSystem;
let totalRequiredEnergy: KiloWattHour = Number.POSITIVE_INFINITY;
if (distance <= activeRange)
if (distance <= activeRange) {
totalRequiredEnergy =
(maxScanEnergyCost - minScanEnergyCost) * (distance / activeRange) +
minScanEnergyCost;
else if (distance <= passiveRange) {
} else if (distance <= passiveRange) {
const addedDistance = distance - activeRange;
const distanceRatio = passiveRange / activeRange;
// Exponential increase
totalRequiredEnergy =
maxScanEnergyCost + Math.E ** (addedDistance * distanceRatio) - 1;
maxScanEnergyCost + addedDistance * distanceRatio - 1;
}

// Multiply by the target's shields strength if shields are raised
Expand All @@ -118,20 +124,26 @@ export class SensorScanSystem extends System {
shieldStrength += shield.strength / shield.maxStrength / shields.length;
shieldStatus = shield.state === "up" ? "up" : shieldStatus;
}
totalRequiredEnergy *= shieldStrength * shieldPenaltyMultiplier;
totalRequiredEnergy *= 1 + shieldStrength * shieldPenaltyMultiplier;

const currentPower = sensors.components.power?.currentPower || 0;
const powerProvided = currentPower / scanCount;
// The energy provided in kilowatt hours, by converting from megawatts
const energyProvided: KiloWattHour =
powerProvided * elapsedTimeHours * 1000;

const progress = energyProvided / totalRequiredEnergy;
const progress = Math.min(
1,
scan.progress + energyProvided / (totalRequiredEnergy || Number.EPSILON),
);
entity.updateComponent("scan", { progress });

if (scan.progress >= 1) {
// The scan is complete! Let's put some data in the database
const currentResults = sensorSystem.resultsDatabase.get(object.id) || {};
entity.updateComponent("scan", { timestamp: Date.now() });
const currentResults =
sensorSystem.resultsDatabase.get(object.id) ||
({} as z.infer<typeof scanRecord>);
switch (scan.type) {
case "cargo": {
const output: Record<string, number> = {};
Expand Down Expand Up @@ -159,11 +171,13 @@ export class SensorScanSystem extends System {
[]) {
const system = this.ecs.getEntityById(systemId);
if (!system) continue;
const efficiency = system.components.efficiency?.efficiency || 1;
if (efficiency > 0.9) continue;
systems.push({
name:
system?.components.identity?.name ||
system.components.isShipSystem?.type,
efficiency: system.components.efficiency?.efficiency || 1,
efficiency,
});
}
systems.sort((a, b) => a.efficiency - b.efficiency);
Expand All @@ -174,12 +188,14 @@ export class SensorScanSystem extends System {
}
break;
}
case "iff": {
case "identification": {
const faction = this.ecs.getEntityById(
object.components.faction?.factionId || -1,
);

currentResults.iff = {
currentResults.identification = {
name: object.components.identity?.name || "Unknown",
classification: getClassification(object) || "Unknown",
factionName: faction?.components.identity?.name || "Unknown",
};
break;
Expand All @@ -200,6 +216,8 @@ export class SensorScanSystem extends System {
);
currentResults.targeting = {
targetName: target?.components.identity?.name || "None",
// TODO February 18, 2025 - Add proper support for this once we have individual weapons targeting
targetedSystem: "General",
};
break;
}
Expand All @@ -225,7 +243,7 @@ export class SensorScanSystem extends System {
type: "torpedoes" as const,
loaded:
t.components.isTorpedoLauncher?.status === "loaded"
? torpedo?.components.identity?.name || "Unknown"
? `${capitalCase(torpedo?.components.identity?.name || "Unknown")} Loaded`
: "Unloaded",
};
}),
Expand All @@ -235,7 +253,8 @@ export class SensorScanSystem extends System {
}

sensorSystem.resultsDatabase.set(object.id, currentResults);
if (scan.repeatInterval === null) this.ecs.removeEntity(entity);
pubsub.publish.sensors.scanResult({ shipId, objectId: object.id });
pubsub.publish.sensors.scans({ shipId });
}
}
}
2 changes: 2 additions & 0 deletions app/.server/systems/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { PowerDistributionSystem } from "./PowerDistributionSystem";
import { ShieldsSystem } from "./ShieldsSystem";
import { PhasersSystem } from "./PhasersSystem";
import { SolarSystemPositionSystem } from "./SolarSystemPositionSystem";
import { SensorScanSystem } from "@thorium/.server/systems/SensorScanSystem";

const systems = [
FilterInventorySystem,
Expand All @@ -59,6 +60,7 @@ const systems = [
TorpedoMovementSystem,
PhysicsWorldPositionSystem,
PhysicsMovementSystem,
SensorScanSystem,
WaypointRemoveSystem,
HeatToCoolantSystem,
HeatDispersionSystem,
Expand Down
19 changes: 2 additions & 17 deletions app/cards/Navigation/data.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getObjectSystem,
} from "@thorium/utils/starmap/position";
import type { DataContext } from "@thorium/.server/DataContext";
import { getClassification } from "@thorium/cards/Navigation/getObjectClassification.server";

type Waypoint = {
id: number;
Expand Down Expand Up @@ -75,7 +76,7 @@ export const navigation = t.router({
object: {
position,
name: object.components.identity?.name,
classification: getClassification(),
classification: getClassification(object),
type: object.components.isShip
? "ship"
: object.components.isPlanet
Expand All @@ -99,22 +100,6 @@ export const navigation = t.router({
? { id: shipSystem?.id, ...shipSystem.components.position }
: null,
};

function getClassification() {
if (!object) return "";
if (object.components.isPlanet)
return `Class ${object.components.isPlanet.classification} Planet`;
if (object.components.isStar)
return `Class ${object.components.isStar.spectralType} Star`;
if (object.components.isShip)
return `${
object.components.isShip.shipClass
? `${object.components.isShip.shipClass} Class `
: ""
}${object.components.isShip.category}`;
if (object.components.isSolarSystem) return "Solar System";
return "";
}
}),

search: t.procedure
Expand Down
17 changes: 17 additions & 0 deletions app/cards/Navigation/getObjectClassification.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Entity } from "@thorium/utils/ecs";

export function getClassification(object: Entity) {
if (!object) return "";
if (object.components.isPlanet)
return `Class ${object.components.isPlanet.classification} Planet`;
if (object.components.isStar)
return `Class ${object.components.isStar.spectralType} Star`;
if (object.components.isShip)
return `${
object.components.isShip.shipClass
? `${object.components.isShip.shipClass} Class `
: ""
}${object.components.isShip.category}`;
if (object.components.isSolarSystem) return "Solar System";
return "";
}
1 change: 0 additions & 1 deletion app/cards/OfficersLog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export default function OfficersLog() {
.concat()
.reverse()
.map((log, i) => (
// biome-ignore lint/a11y/useKeyWithClickEvents:
<li
key={`${log.timestamp}-${i}`}
className={`list-group-item ${
Expand Down
4 changes: 3 additions & 1 deletion app/cards/Pilot/CircleGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ export function GridCanvas({
onContextMenu={(e) => {
e.preventDefault();
}}
onPointerDown={onBackgroundClick}
onPointerDown={() => {
onBackgroundClick?.();
}}
>
<CameraEffects />
<ContextBridge>
Expand Down
20 changes: 13 additions & 7 deletions app/cards/Pilot/DistanceCircle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import type { Line2 } from "three-stdlib";
import FONT_URL from "./Teko-Light.ttf";

export const DistanceCircle: FC<
{ radius?: number } & ThreeElements["object3D"]
> = ({ radius = 1 }) => {
{
radius?: number;
color?: string | number;
label?: string;
} & ThreeElements["object3D"]
> = ({ radius = 1, color = 0x6666666, label }) => {
const points = useMemo(() => {
const curve = new EllipseCurve(
0,
Expand Down Expand Up @@ -65,22 +69,24 @@ export const DistanceCircle: FC<
return (
<group ref={groupRef} rotation={[-Math.PI / 2, 0, 0]}>
<Text
color="#666" // default
color={color} // default
anchorX="center" // default
anchorY="bottom-baseline" // default
fontSize={0.075}
font={FONT_URL}
position={[0, radius * 1.03, 0]}
ref={textRef}
>
{radius < 1
? `${(radius * 1000).toLocaleString()}m`
: `${radius.toLocaleString()}km`}
{label
? label
: radius < 1
? `${(radius * 1000).toLocaleString()}m`
: `${radius.toLocaleString()}km`}
</Text>
<Line
ref={lineRef}
points={points} // Array of points
color={0x666666} // Default
color={color} // Default
lineWidth={1} // In pixels (default)
/>
</group>
Expand Down
Loading

0 comments on commit 6ebd7c7

Please sign in to comment.