146 lines
3.7 KiB
TypeScript
146 lines
3.7 KiB
TypeScript
import { drawLine } from "../drawing/line.ts";
|
|
import { ComplexPath, PathSegment } from "../math/path.ts";
|
|
import { Vector } from "doodler";
|
|
import { Follower } from "../physics/follower.ts";
|
|
import { Mover } from "../physics/mover.ts";
|
|
import { Spline, Track } from "../track.ts";
|
|
|
|
export class Train {
|
|
nodes: Vector[] = [];
|
|
|
|
cars: TrainCar[] = [];
|
|
|
|
path: Spline<Track>;
|
|
t: number;
|
|
|
|
engineLength = 40;
|
|
spacing = 30;
|
|
|
|
speed = 0;
|
|
|
|
constructor(track: Spline<Track>, cars: TrainCar[] = []) {
|
|
this.path = track;
|
|
this.t = 0;
|
|
this.nodes.push(this.path.followEvenPoints(this.t));
|
|
this.nodes.push(this.path.followEvenPoints(this.t - this.real2Track(40)));
|
|
const engineSprites = document.getElementById(
|
|
"engine-sprites",
|
|
)! as HTMLImageElement;
|
|
this.cars.push(
|
|
new TrainCar(
|
|
55,
|
|
engineSprites,
|
|
80,
|
|
20,
|
|
{ at: new Vector(0, 60), width: 80, height: 20 },
|
|
),
|
|
new TrainCar(
|
|
25,
|
|
engineSprites,
|
|
40,
|
|
20,
|
|
{ at: new Vector(80, 0), width: 40, height: 20 },
|
|
),
|
|
);
|
|
this.cars[0].points = this.nodes.map((n) => n) as [Vector, Vector];
|
|
this.cars[1].points = this.nodes.map((n) => n) as [Vector, Vector];
|
|
let currentOffset = 40;
|
|
for (const car of cars) {
|
|
currentOffset += this.spacing;
|
|
const a = this.path.followEvenPoints(this.t - currentOffset);
|
|
currentOffset += car.length;
|
|
const b = this.path.followEvenPoints(this.t - currentOffset);
|
|
car.points = [a, b];
|
|
this.cars.push(car);
|
|
}
|
|
}
|
|
|
|
move(dTime: number) {
|
|
this.t = (this.t + this.speed * dTime * 10) % this.path.evenPoints.length;
|
|
// console.log(this.t);
|
|
let currentOffset = 0;
|
|
for (const car of this.cars) {
|
|
if (!car.points) return;
|
|
const [a, b] = car.points;
|
|
a.set(this.path.followEvenPoints(this.t - currentOffset));
|
|
currentOffset += car.length;
|
|
b.set(this.path.followEvenPoints(this.t - currentOffset));
|
|
currentOffset += this.spacing;
|
|
car.draw();
|
|
}
|
|
// this.draw();
|
|
}
|
|
|
|
// draw() {
|
|
// for (const [i, node] of this.nodes.entries()) {
|
|
// doodler.drawCircle(node.point, 10, { color: 'purple', weight: 3 })
|
|
// // const next = this.nodes[i + 1];
|
|
// // if (next) {
|
|
// // const to = Vector.sub(node.point, next.point);
|
|
// // to.setMag(40);
|
|
// // doodler.line(next.point, Vector.add(to, next.point))
|
|
// // }
|
|
// }
|
|
// }
|
|
|
|
real2Track(length: number) {
|
|
return length / this.path.pointSpacing;
|
|
}
|
|
}
|
|
|
|
export class TrainCar {
|
|
img: HTMLImageElement;
|
|
imgWidth: number;
|
|
imgHeight: number;
|
|
sprite?: ISprite;
|
|
|
|
points?: [Vector, Vector];
|
|
length: number;
|
|
|
|
constructor(
|
|
length: number,
|
|
img: HTMLImageElement,
|
|
w: number,
|
|
h: number,
|
|
sprite?: ISprite,
|
|
) {
|
|
this.img = img;
|
|
this.sprite = sprite;
|
|
this.imgWidth = w;
|
|
this.imgHeight = h;
|
|
this.length = length;
|
|
}
|
|
|
|
draw() {
|
|
if (!this.points) return;
|
|
const [a, b] = this.points;
|
|
const origin = Vector.add(Vector.sub(a, b).div(2), b);
|
|
const angle = Vector.sub(b, a).heading();
|
|
|
|
doodler.drawCircle(origin, 4, { color: "blue" });
|
|
|
|
doodler.drawRotated(origin, angle, () => {
|
|
this.sprite
|
|
? doodler.drawSprite(
|
|
this.img,
|
|
this.sprite.at,
|
|
this.sprite.width,
|
|
this.sprite.height,
|
|
origin.copy().sub(this.imgWidth / 2, this.imgHeight / 2),
|
|
this.imgWidth,
|
|
this.imgHeight,
|
|
)
|
|
: doodler.drawImage(
|
|
this.img,
|
|
origin.copy().sub(this.imgWidth / 2, this.imgHeight / 2),
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
interface ISprite {
|
|
at: Vector;
|
|
width: number;
|
|
height: number;
|
|
}
|