td2-visualizer/src/model/switch.js
2025-07-12 16:58:33 +02:00

164 lines
5.5 KiB
JavaScript

import PointTrack from "./tracks/point-track.js";
import SceneryObject from "./scenery-object.js";
import SceneryParserLog from "./scenery-parser-log.js";
import Vector3 from "./vector3.js";
import DefinedSwitches from "./defs/defined-switches.js";
import {SwitchTrackConnectionType} from "./switch-descriptions/switch-prefab-track";
import TrackConnection, {TrackConnectionEnd} from "./track-connection";
export default class Switch extends SceneryObject {
model;
bare_model;
data;
id_isolation;
id_switch;
maxspeed;
derailspeed;
category = "switches";
type = "Switch";
applied = false;
track_prefab_name;
tracks = [];
def = null;
constructor(id, model, pos, rot, data, id_isolation, id_switch, maxspeed, derailspeed, track_prefab_name) {
super(id, pos, rot);
Object.assign(this, {
model,
data,
id_switch, id_isolation,
maxspeed, derailspeed,
track_prefab_name
});
this.bare_model = model.split(',')[0]?.trim() || model.trim();
}
static fromText(text) {
const values = text.split(";");
const sw = new Switch(
values[1], // id
values[2], // model
Vector3.fromValuesArray(values, 3), // pos
Vector3.fromValuesArray(values, 6), // rot
values[9], // data
values[10], // id_isolation
values[11], // id_switch
parseFloat(values[13]) || 0, // maxspeed
parseFloat(values[14]) || 0, // derailspeed
values[15] // track_prefab_name
);
return sw;
}
applyObject(scenery) {
if(this.applied) return; // already applied
this.applied = true;
if(!this.bare_model) {
SceneryParserLog.warn('switchNoModel', `Switch ${this.id} has no model defined, apply`);
return;
}
let def = DefinedSwitches[this.bare_model] || null;
if (!def) {
SceneryParserLog.warn('switchUndefinedModel', `Switch ${this.id} has an undefined model "${this.bare_model}"`);
return;
}
this._applySwitchFromDef(scenery, def);
}
_applySwitchFromDef(scenery, def) {
const trackIds = this._getTrackIds();
if (!trackIds) return;
const tracks = Object.entries(def.tracks).map(([key, track]) => {
return this._createSwitchTrackFromDef(scenery, def, track, trackIds);
});
if (tracks.some(track => !track)) return;
this.tracks = tracks;
this.def = def;
}
_getTrackIds() {
const ids = this.data
.split(",")
.filter(part => part.trim() !== '')
.map(
(value) => value.split(":").map((part) => part.trim()),
);
if (ids.some((part) => part.length !== 3)) {
SceneryParserLog.warn('switchInvalidDataFormat', `Switch ${this.id} has invalid data: ${this.data}`);
}
return ids;
}
_createSwitchTrackFromDef(scenery, switchDef, trackDef, ids) {
const rotRad = this.rot.multiply(Math.PI / 180);
if (trackDef.dataIndex >= ids.length) {
SceneryParserLog.warn(
'switchMissingTrackId',
`Switch ${this.id} with model ${this.bare_model} is missing an id for the track at index ${trackDef.dataIndex}`,
);
return null;
}
const [trackId, prevId, nextId] = ids[trackDef.dataIndex];
const connections = [];
trackDef.connections.forEach((connection) => {
if (connection.type === SwitchTrackConnectionType.INTERNAL) {
const otherTrackDef = switchDef.tracks[connection.internalId];
if (!otherTrackDef) {
SceneryParserLog.warn(
'switchMissingTrackId',
`Switch prefab for model with model ${this.bare_model} of track ${this.id} does not have the internal track ${connection.internalId}`,
);
return;
}
if (otherTrackDef.dataIndex >= ids.length) {
SceneryParserLog.warn(
'switchMissingTrackId',
`Switch ${this.id} with model ${this.bare_model} is missing an id for the track at index ${otherTrackDef.dataIndex}`,
);
return;
}
const otherTrackId = ids[otherTrackDef.dataIndex][0];
if (otherTrackId) {
connections.push(new TrackConnection(otherTrackId, connection.end));
}
} else if (connection.type === SwitchTrackConnectionType.EXTERNAL) {
const otherTrackId = connection.end === TrackConnectionEnd.START ? prevId : nextId;
if (otherTrackId) {
connections.push(new TrackConnection(otherTrackId, connection.end));
}
}
});
const trackObj = new PointTrack(
trackId,
this.pos.add(trackDef.startPos.rotate(rotRad)),
this.pos.add(trackDef.endPos.rotate(rotRad)),
trackDef.radius,
connections,
this.id_switch,
0, // start_slope
0, // end_slope
this.id_isolation,
this.track_prefab_name,
this.maxspeed,
this.derailspeed
);
trackObj.switch = this;
scenery.addObject(trackObj);
return trackObj;
}
}