Something went wrong. Try again.
Starship Simulator Controls thoriumsim.com
Something went wrong. Try again.
6.7 kB · 187 lines
TypeScript
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188import { pubsub } from "@thorium/.server/init/pubsub";import { findClosestSatellite, pickNextLongRangeMessageNode,} from "@thorium/cards/LongRangeComm/data.server";import { getShipSystem } from "@thorium/utils/.server/ship/getShipSystem";import { type Entity, System } from "@thorium/utils/ecs";import { getObjectSystem } from "@thorium/utils/starmap/position";import { lightYearToLightMinute } from "@thorium/utils/unitTypes";import { Vector3 } from "three";
export class CommSatelliteSystem extends System { static flightMode = ["nova"];
test(entity: Entity) { return !!entity.components.isLongRangeMessage; } update(entity: Entity, elapsed: number) { const elapsedRatio = elapsed / 1000; const { position, isLongRangeMessage } = entity.components; if (!position || !isLongRangeMessage) return; if (isLongRangeMessage.state !== "sending" && isLongRangeMessage.state !== "failing") return; const senderId = isLongRangeMessage.senderId;
const { x, y, z } = position;
const currentNode = this.ecs.getEntityById(isLongRangeMessage.nextNodeId); if (!currentNode) return; const nodePosition = getObjectSystem(currentNode)?.components.position || currentNode.components.position; if (!nodePosition) return;
const velocity = lightYearToLightMinute(isLongRangeMessage.transmissionSpeed) * elapsedRatio; const direction = new Vector3(nodePosition.x - x, nodePosition.y - y, nodePosition.z - z) .normalize() .multiplyScalar(velocity); const currentDistance = Math.hypot(x - nodePosition.x, y - nodePosition.y, z - nodePosition.z);
const newX = x + direction.x; const newY = y + direction.y; const newZ = z + direction.z;
const newDistance = Math.hypot( newX - nodePosition.x, newY - nodePosition.y, newZ - nodePosition.z, );
// If we begin moving _away_ from the node, then that means we overshot and should // move on to the next node if (newDistance >= currentDistance) { entity.updateComponent("position", { x: nodePosition.x, y: nodePosition.y, z: nodePosition.z, });
entity.updateComponent("isLongRangeMessage", { visitedNodeIds: [...isLongRangeMessage.visitedNodeIds, isLongRangeMessage.nextNodeId], });
// We'll do some special handling to hold intercepted messages const currentNode = this.ecs.getEntityById( entity.components.isLongRangeMessage?.nextNodeId || -1, ); if (currentNode?.components.isPlayerShip && !isLongRangeMessage.interceptorId) { entity.updateComponent("isLongRangeMessage", { interceptorId: currentNode.id, state: "intercepted", }); pubsub.publish.longRangeComm.outgoingMessages({ shipId: senderId }); pubsub.publish.longRangeComm.incomingMessages({ shipId: senderId }); pubsub.publish.longRangeComm.incomingMessages({ shipId: currentNode.id, });
return; }
// Regular comm satellites just send it along to the next satellite const satellites = Array.from(this.ecs.componentCache.get("isCommSatellite") || []); const destination = this.ecs.getEntityById(isLongRangeMessage.destinationId); if (!destination) { fail("Unable to determine destination"); return; } const closestEndNode = findClosestSatellite(satellites, destination); if (!closestEndNode) { fail("Unable to find route to destination through communications network"); return; }
// If we reach the final node, then the last step is moving to the destination entity if (closestEndNode.id === isLongRangeMessage.nextNodeId) { // If its already failing, we'll just mark it as "undeliverable" and call it a day if (isLongRangeMessage.state === "failing") { fail("Failed to reach destination"); return; }
// But if the destination entity isn't in range, then we need to mark the message as failing const finalNode = this.ecs.getEntityById(isLongRangeMessage.nextNodeId); if (!finalNode || !destination) { fail("Unable to find route to destination through communications network"); return; }
// Get the position of the final satellite and the ship the message is supposed to go to const nodePosition = getObjectSystem(finalNode)?.components.position || finalNode.components.position; const destinationPosition = getObjectSystem(destination)?.components.position || destination?.components.position; if (!nodePosition || !destinationPosition) { fail("Unable to determine destination position"); return; }
const distance = Math.hypot( nodePosition.x - destinationPosition.x, nodePosition.y - destinationPosition.y, nodePosition.z - destinationPosition.z, );
// The ship is outside the range of the final node if (distance > (finalNode.components.isCommSatellite?.radius || -1)) { // It failed! fail("Destination is outside communications network"); return; } else { // Consider it sent! entity.updateComponent("isLongRangeMessage", { state: "delivered" }); entity.removeComponent("position"); pubsub.publish.longRangeComm.outgoingMessages({ shipId: senderId }); pubsub.publish.longRangeComm.incomingMessages({ shipId: isLongRangeMessage.destinationId, });
// While we're at it, make sure the sender is in the address book of the recipient for replies try { const longRangeComm = getShipSystem(this.ecs, { shipId: destination.id, systemType: "longRangeComm", });
if (longRangeComm.components.isLongRangeComm) { if ( !longRangeComm.components.isLongRangeComm.addressBook.find( (c) => c.contactId === senderId, ) && longRangeComm.components.isShipSystem?.shipId !== senderId ) { longRangeComm.components.isLongRangeComm.addressBook.push({ actions: [], contactId: senderId, });
pubsub.publish.longRangeComm.addressBook({ shipId: destination.id }); } } } catch {} } } else { // Recalculate the graph and try again const nextNodeId = pickNextLongRangeMessageNode( this.ecs, isLongRangeMessage.nextNodeId, isLongRangeMessage.destinationId, isLongRangeMessage.visitedNodeIds, ); entity.updateComponent("isLongRangeMessage", { nextNodeId, }); } } else { entity.updateComponent("position", { x: newX, y: newY, z: newZ }); }
function fail(reason: string) { entity.updateComponent("isLongRangeMessage", { state: "undelivered", failureReason: reason, }); entity.removeComponent("position"); pubsub.publish.longRangeComm.outgoingMessages({ shipId: senderId }); } }}