Something went wrong. Try again.
Starship Simulator Controls thoriumsim.com
Something went wrong. Try again.
10 kB · 268 lines
TypeScript
at develop
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269import { pubsub } from "@thorium/.server/init/pubsub";import { type Entity, System } from "@thorium/utils/ecs";import { getOrbitPosition } from "@thorium/utils/starmap/getOrbitPosition";import { pathfinder } from "@thorium/utils/starmap/pathfinder.server";import { clearObjectSystem } from "@thorium/utils/starmap/position";import { lightMinuteToLightYear } from "@thorium/utils/unitTypes";import { Matrix4, Quaternion, Vector3 } from "three";
const MIN_SYSTEM_SIZE = 10_000;const SYSTEM_PADDING = 1.05;
// Key is system ID, value is the max distance to leaveconst maxDistanceCache = new Map<number, number>();// Get the maximum distance necessary to leave or enter// This requires getting the distance of all of the objects from the center of the solar system.// We don't care about moons thoughfunction getMaxDistance(entities: Entity[], systemId: number) { if (maxDistanceCache.has(systemId)) { return maxDistanceCache.get(systemId)!; } const distance = entities.reduce((prev, next) => { if (next.components.satellite?.parentId === systemId) { // It's a star or planet const position = getOrbitPosition(next.components.satellite); const distance = Math.hypot(position.x, position.y, position.z);
if (distance > prev) return distance; } else if ( next.components.position && next.components.position.parentId === systemId && next.components.isStatic ) { // It's a ship! const distance = Math.hypot( next.components.position.x, next.components.position.y, next.components.position.z, ); if (distance > prev) return distance; } return prev; }, 0); // Multiply the max distance by a constant to give a bit of extra room around the edges const properDistance = Math.max(MIN_SYSTEM_SIZE, distance * SYSTEM_PADDING); maxDistanceCache.set(systemId, properDistance); return properDistance;}
const direction = new Vector3();const systemPosition = new Vector3();const shipPosition = new Vector3();const desiredDestination = new Vector3();const desiredRotationQuat = new Quaternion();const up = new Vector3(0, 1, 0);const matrix = new Matrix4();const rotationMatrix = new Matrix4().makeRotationY(-Math.PI);
export class InterstellarTransitionSystem extends System { static flightMode = ["nova"]; test(entity: Entity) { return !!(entity.components.isShip && entity.components.position && entity.components.velocity); } update(entity: Entity) { // const warpSystem = this.ecs.systems.find(sys =>sys.constructor.name === "WarpSystem") if (!entity.components.position) return; const system = this.ecs.getEntityById(entity.components.position?.parentId || -1); const autopilot = entity.components.autopilot;
if (system) { // Transition from inside a solar system to interstellar space. // If the entity's destination is inside the current solar system, don't leave it if (system.id === autopilot?.desiredSolarSystemId) return; // Check if the ship is on the outskirts of the solar system const entitiesWithPosition = [ ...(this.ecs.componentCache.get("position") || []), ...(this.ecs.componentCache.get("satellite") || []), ]; const maxDistance = getMaxDistance(entitiesWithPosition, system.id); const entityDistance = Math.hypot( entity.components.position.x, entity.components.position.y, entity.components.position.z, );
if (maxDistance - entityDistance < 0 && system.components.position) { // Transition time! const { x, y, z } = system.components.position; entity.updateComponent("position", { x, y, z, type: "interstellar", parentId: null, }); entity.addComponent("snapInterpolation"); clearObjectSystem(entity); // We also need to update the warp engines so they go faster.
// const warpEngines = warpSystem?.entities.find(e => entity.components.shipSystems?.shipSystemIds.includes(e.id) && e.components.isWarpEngines) // if (warpEngines?.components.isWarpEngines) { // const { // interstellarCruisingSpeed, // minSpeedMultiplier, // warpFactorCount, // currentWarpFactor, // } = warpEngines.components.isWarpEngines;
// const cruisingSpeed = interstellarCruisingSpeed;
// const minWarp = cruisingSpeed * minSpeedMultiplier;
// // Calculate max warp speed based on the factor and the number of warp factors // let warpSpeed = 0; // if (currentWarpFactor === 1) { // warpSpeed = minWarp; // } else if (currentWarpFactor > 1) { // warpSpeed = // (cruisingSpeed - minWarp) * // ((currentWarpFactor - 1) / (warpFactorCount - 1)); // }
// warpEngines.updateComponent("isWarpEngines", { // maxVelocity: warpSpeed, // }); // outfitPublish({plugin: undefined, ship: entity, outfit: warpEngines}); // } pubsub.publish.starmapCore.ships({ systemId: system.id }); pubsub.publish.starmapCore.ships({ systemId: null }); pubsub.publish.shortRangeComm.hailableEntities({ systemId: null }); pubsub.publish.shortRangeComm.hailableEntities({ systemId: system.id }); pubsub.publish.starmapCore.ship({ shipId: entity.id });
if (entity.components.isPlayerShip) { pubsub.publish.navigation.ship({ shipId: entity.id }); pubsub.publish.ship.player({ shipId: entity.id }); pubsub.publish.ship.players(); } } } else { // Transition from interstellar space to within a solar system // Check if the ship has locked course on to a solar system, and is within range const destinationWaypoint = this.ecs.getEntityById(autopilot?.destinationWaypointId || -1); const destinationSystem = this.ecs.getEntityById(autopilot?.desiredSolarSystemId || -1); if (!destinationSystem?.components.position || !entity.components.position) return; const distance = Math.hypot( entity.components.position.x - destinationSystem.components.position.x, entity.components.position.y - destinationSystem.components.position.y, entity.components.position.z - destinationSystem.components.position.z, ); // 1/100th of a lightyear if (lightMinuteToLightYear(distance) < 0.01) { const entitiesWithPosition = [ ...(this.ecs.componentCache.get("position") || []), ...(this.ecs.componentCache.get("satellite") || []), ];
// create a vector of the ship from the center of the system, and position it outside the heliopause const maxDistance = getMaxDistance(entitiesWithPosition, destinationSystem.id); shipPosition.set( entity.components.position.x, entity.components.position.y, entity.components.position.z, ); systemPosition.set( destinationSystem.components.position.x, destinationSystem.components.position.y, destinationSystem.components.position.z, ); direction.subVectors(shipPosition, systemPosition).normalize().multiplyScalar(maxDistance); entity.updateComponent("position", { x: direction.x, y: direction.y, z: direction.z, parentId: destinationSystem.id, type: "solar", }); entity.addComponent("snapInterpolation"); clearObjectSystem(entity);
// Also set the rotation to the destination to be spot-on to the destination if ( destinationWaypoint?.components.isWaypoint?.attachedObjectId !== destinationSystem.id && destinationWaypoint?.components.position ) { desiredDestination.set( destinationWaypoint.components.position.x, destinationWaypoint.components.position.y, destinationWaypoint.components.position.z, );
up.set(0, 1, 0); matrix.lookAt(direction, desiredDestination, up).multiply(rotationMatrix); desiredRotationQuat.setFromRotationMatrix(matrix); entity.updateComponent("rotation", { x: desiredRotationQuat.x, y: desiredRotationQuat.y, z: desiredRotationQuat.z, w: desiredRotationQuat.w, }); }
// Generate a path to get to the destination if (entity.components.autopilot?.desiredCoordinates) { const { x, y, z } = entity.components.autopilot.desiredCoordinates; const path = pathfinder(entity, new Vector3(x, y, z)); const nextCoordinates = path?.shift(); entity.updateComponent("autopilot", { path, nextCoordinates, desiredRotation: null, }); } pubsub.publish.starmapCore.ships({ systemId: destinationSystem.id }); pubsub.publish.starmapCore.ships({ systemId: null }); pubsub.publish.shortRangeComm.hailableEntities({ systemId: null }); pubsub.publish.shortRangeComm.hailableEntities({ systemId: destinationSystem.id, }); pubsub.publish.starmapCore.ship({ shipId: entity.id });
if (entity.components.isPlayerShip) { pubsub.publish.navigation.ship({ shipId: entity.id }); pubsub.publish.ship.player({ shipId: entity.id }); pubsub.publish.ship.players(); } // // Update the warp engines // const warpEngines = this.ecs.entities.find( // e => // e.isOutfit && // e.warpEngines && // e.shipAssignment?.shipId === entity.id // ); // if (warpEngines?.warpEngines) { // const { // planetaryCruisingSpeed, // minSpeedMultiplier, // warpFactorCount, // } = warpEngines.warpEngines; // const currentWarpFactor = 1; // const cruisingSpeed = planetaryCruisingSpeed; // const minWarp = cruisingSpeed * minSpeedMultiplier;
// // Calculate max warp speed based on the factor and the number of warp factors // let warpSpeed = 0; // if (currentWarpFactor === 1) { // warpSpeed = minWarp; // } else if (currentWarpFactor > 1) { // warpSpeed = // (cruisingSpeed - minWarp) * // ((currentWarpFactor - 1) / (warpFactorCount - 1)); // }
// warpEngines.updateComponent("warpEngines", { // forwardAcceleration: 0, // forwardVelocity: warpSpeed, // maxVelocity: warpSpeed, // currentWarpFactor: 1, // }); // outfitPublish({ // plugin: undefined, // ship: entity, // outfit: warpEngines, // }); // } } } }}