Skip to content

Commit

Permalink
feat(Ship Map): Creates the ECS components and systems for entities t…
Browse files Browse the repository at this point in the history
…o traverse a ship map. Closes #265
  • Loading branch information
alexanderson1993 committed Jun 16, 2022
1 parent 7fbe026 commit 43d0264
Show file tree
Hide file tree
Showing 16 changed files with 429 additions and 62 deletions.
1 change: 0 additions & 1 deletion server/src/classes/FlightDataModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import systems from "../systems";
import {FlightClient} from "./FlightClient";
import {FSDataStore, FSDataStoreOptions} from "@thorium/db-fs";
import ShipPlugin from "./Plugins/Ship";
import {spawnSolarSystem} from "../spawners/solarSystem";

export class FlightDataModel extends FSDataStore {
static INTERVAL = 1000 / 60;
Expand Down
3 changes: 3 additions & 0 deletions server/src/classes/Plugins/Ship/Deck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type DeckNodeId = Flavor<number, "deckNodeId">;
export class DeckNode {
id: DeckNodeId;
name: string;
/** Only used for in-flight use, not in plugin configuration */
deckIndex!: number;
x: number;
y: number;
isRoom: boolean;
Expand All @@ -28,6 +30,7 @@ export class DeckNode {
constructor(params: Partial<DeckNode>) {
this.id = params.id || 0;
this.name = params.name || "";
this.deckIndex = params.deckIndex!;
this.x = params.x || 0;
this.y = params.y || 0;
this.isRoom = params.isRoom || false;
Expand Down
1 change: 1 addition & 0 deletions server/src/components/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export * from "./satellite";
export * from "./temperature";
export * from "./population";
export * from "./shipMap";
export * from "./shipMap/passengerMovement";
5 changes: 5 additions & 0 deletions server/src/components/position.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@ export class PositionComponent extends Component {

x: number = 0;
y: number = 0;
/**
* For ship maps, the Z coordinate is used to represent which deck the crew member is on.
* Z is the index of the deck number. Decimal values are used when traveling between decks.
* The deck that the crew member is on is found by rounding the Z coordinate.
*/
z: number = 0;
}
44 changes: 0 additions & 44 deletions server/src/components/shipMap.ts

This file was deleted.

48 changes: 48 additions & 0 deletions server/src/components/shipMap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import DeckPlugin, {DeckEdge} from "../../classes/Plugins/Ship/Deck";
import {ShipMapGraph} from "../../utils/shipMapPathfinder";
import {Component} from "server/src/components/utils";

const roundTo1000 = (num: number) => Math.round(num * 1000) / 1000;
export class ShipMapComponent extends Component {
static id = "shipMap" as const;
static serialize(data: ShipMapComponent) {
return {
decks: data.decks.map(({backgroundUrl, name}) => ({
backgroundUrl,
name,
})),
deckNodes: data.deckNodes.map(
({flags, deckIndex, icon, id, isRoom, name, radius, x, y}) => {
const output: Partial<DeckPlugin["nodes"][0]> & {deckIndex: number} =
{
id,
deckIndex,
x: roundTo1000(x),
y: roundTo1000(y),
};
if (name) output.name = name;
if (icon) output.icon = icon;
if (isRoom) output.isRoom = isRoom;
if (radius) output.radius = radius;
if (flags?.length > 0) output.flags = flags;
return output;
}
),
deckEdges: data.deckEdges.map(({id, flags, from, to, isOpen, weight}) => {
const output: Partial<DeckEdge> = {
id,
from,
to,
};
if (weight !== 1) output.weight = weight;
if (!isOpen) output.isOpen = isOpen;
if (flags?.length > 0) output.flags = flags;
return output;
}),
};
}
decks: Omit<DeckPlugin, "nodes">[] = [];
deckNodes: (DeckPlugin["nodes"][0] & {deckIndex: number})[] = [];
deckEdges: DeckEdge[] = [];
graph: ShipMapGraph | null = null;
}
21 changes: 21 additions & 0 deletions server/src/components/shipMap/passengerMovement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {MetersPerSecond} from "server/src/utils/unitTypes";
import {Component} from "../utils";

/** This class represents entities that can move around a ship */
export class PassengerMovementComponent extends Component {
static id = "passengerMovement" as const;

/** TODO June 16, 2022 - Some day it should be possible to connect from one ship to another and have entities move between them. */
nodePath: number[] = [];
nextNodeIndex: number = 0;

movementMaxVelocity: {
x: MetersPerSecond;
y: MetersPerSecond;
z: MetersPerSecond;
} = {
x: 1.3,
y: 1.3,
z: 1.3 / 10, // The Z default is because decks are 10 meters high, so it should take 10x as long to move between decks.
};
}
2 changes: 1 addition & 1 deletion server/src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class Component {
init(params: any = {}) {
(Object.getOwnPropertyNames(this) as (keyof this)[]).forEach(key => {
if (key !== "init") {
this[key] = params[key] || this[key];
this[key] = params[key] ?? this[key];
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/inputs/__test__/flight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ describe("flight input", () => {
"type": "solar",
"x": -228630890,
"y": 0,
"z": 12500.000000027532,
"z": 12500.000000028002,
}
`);
});
Expand Down
4 changes: 4 additions & 0 deletions server/src/spawners/ship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ export function spawnShip(
if (entity.components.isPlayerShip) {
entity.addComponent("shipMap", {
decks: template.decks || [],
deckNodes:
template.decks?.flatMap((deck, i) =>
deck.nodes.map(n => ({...n, deckIndex: i}))
) || [],
deckEdges: template.deckEdges || [],
});
}
Expand Down
12 changes: 12 additions & 0 deletions server/src/systems/PassengerDestinationSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {Entity, System} from "../utils/ecs";

export class PassengerDestinationSystem extends System {
test(entity: Entity) {
return !!entity.components.passengerMovement;
}
frequency = 10;
update(entity: Entity, elapsed: number) {
const elapsedRatio = elapsed / 1000;
// console.log("Lets go!");
}
}
80 changes: 80 additions & 0 deletions server/src/systems/PassengerMovementSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import DeckPlugin, {DeckNode} from "../classes/Plugins/Ship/Deck";
import {Entity, System} from "../utils/ecs";
export class PassengerMovementSystem extends System {
test(entity: Entity) {
return !!(
entity.components.passengerMovement && entity.components.position
);
}
frequency = 20;
update(entity: Entity, elapsed: number) {
const elapsedRatio = elapsed / (1000 / this.frequency);
const {position, passengerMovement} = entity.components;
if (!position || !passengerMovement) return;
const {x, y, z, parentId} = position;
if (parentId === null) return;
const ship = this.ecs.getEntityById(parentId);
if (!ship) return;
const nextNode = ship.components.shipMap?.deckNodes.find(
node =>
node.id === passengerMovement.nodePath[passengerMovement.nextNodeIndex]
);
if (!nextNode) return;
const distanceToNext = Math.hypot(x - nextNode?.x, y - nextNode?.y);
// Increment to the next node
if (distanceToNext <= 0.1 && z === nextNode.deckIndex) {
passengerMovement.nextNodeIndex++;
if (
passengerMovement.nextNodeIndex >= passengerMovement.nodePath.length
) {
// We've reached the end of the path, so we're done.
entity.updateComponent("passengerMovement", {
nodePath: [],
nextNodeIndex: 0,
});
passengerMovement.nodePath = [];
passengerMovement.nextNodeIndex = 0;
entity.updateComponent("position", {
x: nextNode.x,
y: nextNode.y,
z: nextNode.deckIndex,
});
}
} else {
// Move towards the next node
const direction = Math.atan2(nextNode?.y - y, nextNode?.x - x);
const velocity = Math.min(
passengerMovement.movementMaxVelocity.x,
distanceToNext
);
const zVelocity = Math.min(
passengerMovement.movementMaxVelocity.z,
nextNode.deckIndex - z
);
const newX = x + velocity * Math.cos(direction) * elapsedRatio;
const newY = y + velocity * Math.sin(direction) * elapsedRatio;
let newZ = z + zVelocity * elapsedRatio;
if (Math.abs(newZ - nextNode.deckIndex) < 0.1) {
// We've reached the next deck
newZ = nextNode.deckIndex;
}

entity.updateComponent("position", {x: newX, y: newY, z: newZ});
}
}
}

export function findClosestNode(
nodes: DeckNode[],
{x, y, z}: {x: number; y: number; z: number}
) {
const node = nodes.reduce(
(acc: {distance3d: number; node: null | DeckNode}, node) => {
const distance3d = Math.hypot(node.x - x, node.y - y, node.deckIndex - z);
if (distance3d < acc.distance3d) return {distance3d, node};
return acc;
},
{distance3d: Infinity, node: null}
);
return node.node;
}
Loading

0 comments on commit 43d0264

Please sign in to comment.