Added SignalBoxes, fixed bugs

+Added Misc objects parser
+Added SignalBoxRenderer
*Fixed Switch and TrackObject double apply protection
*Updated scp.py to not exclude Signalboxes
This commit is contained in:
izawartka 2025-06-01 17:45:18 +02:00
parent 0b77a5a782
commit 7f279bf35d
11 changed files with 124 additions and 3 deletions

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100mm" height="100mm" version="1.1" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="m4 19v62h92v-62zm8 8h76v46h-76z" color="#000000" fill-rule="evenodd"/>
<circle cx="50" cy="50" r="6"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 279 B

View File

@ -46,3 +46,12 @@
fill: #eee;
font-size: 72mm;
}
.map svg g.signalbox {
fill: #eee;
}
.map svg g.signalbox text {
fill: #eee;
font-size: 4px;
}

View File

@ -4,6 +4,7 @@ import IsolationIdRenderer from './object-renderers/IsolationIdRenderer';
import TrackObjectRenderer from './object-renderers/TrackObjectRenderer';
import SignalRenderer from './object-renderers/SignalRenderer';
import RouteRenderer from './object-renderers/RouteRenderer';
import SignalBoxRenderer from './object-renderers/SignalBoxRenderer';
const ObjectRendererQueue = [
{
@ -38,6 +39,12 @@ const ObjectRendererQueue = [
'renderer': SignalRenderer,
'cond': (settings) => settings.layers['signals']
},
{
'name': 'signalboxes',
'category': 'misc',
'renderer': SignalBoxRenderer,
'cond': (settings) => settings.layers['signalboxes']
},
{
'name': 'routes',
'category': 'routes',

View File

@ -0,0 +1,34 @@
import { ReactSVG } from 'react-svg';
import AngleHelper from '../../../helpers/angleHelper';
export default function SignalBoxRenderer(props) {
const { object } = props;
if (!object.isSignalBox) return null;
const [x, y] = object.pos.toSVGCoords();
const text = `"${object.name}"`;
const rot = AngleHelper.normalizeDegAngle(object.rot.y);
const upsideDown = rot >= 90 && rot < 270;
const upsideDownRot = upsideDown ? 180 : 0;
return (
<g className="signalbox" transform={`translate(${x}, ${y}) rotate(${object.rot.y}) `}>
<g className="signalbox-icon" transform={`translate(-4.725, -4.725)`}>
<ReactSVG
src="/assets/signalbox.svg"
wrapper='svg'
beforeInjection={(svg) => {
svg.setAttribute('width', '2.5mm');
svg.setAttribute('height', '2.5mm');
}}
/>
</g>
<g transform={`translate(0, 5) rotate(${upsideDownRot})`}>
<text x="0" y="0" textAnchor="middle" dominantBaseline="middle">
{text}
</text>
</g>
</g>
);
}

View File

@ -36,6 +36,11 @@ const Constants = {
name: 'Signals',
default: true,
},
{
id: 'signalboxes',
name: 'Signal boxes',
default: true,
},
{
id: 'routes',
name: 'Routes',

35
src/model/misc.js Normal file
View File

@ -0,0 +1,35 @@
import SceneryObject from "./scenery-object";
import Vector3 from "./vector3";
export default class Misc extends SceneryObject {
prefab_name;
name;
category = "misc";
type = "Misc";
isSignalBox;
constructor(id, misc_id, prefab_name, pos, rot, name) {
super(id, pos, rot);
Object.assign(this, {
misc_id,
prefab_name,
name,
});
this.isSignalBox = prefab_name.startsWith("SignalBox");
}
static fromText(id, text) {
const values = text.split(";");
const object = new Misc(
id, // id
values[1], // misc_id
values[2], // prefab_name
Vector3.fromValuesArray(values, 3), // pos
Vector3.fromValuesArray(values, 6), // rot
values[9] // name
);
return object;
}
}

View File

@ -4,12 +4,14 @@ import BezierTrack from './bezier-track';
import TrackObject from './track-object';
import Signal from './signal';
import Route from './route';
import Misc from './misc';
import SceneryParserLog from './scenery-parser-log';
export default class Scenery
{
objects = {};
bounds = { minX: Infinity, minZ: Infinity, maxX: -Infinity, maxZ: -Infinity };
static nextMiscId = 1;
getBounds () {
return { ...this.bounds };
@ -71,6 +73,8 @@ export default class Scenery
return Scenery._parseTrackObject(text);
case 'Route':
return Route.fromText(text);
case 'Misc':
return Misc.fromText(Scenery.nextMiscId++, text);
case 'Empty':
case 'EndRoute':
return null;

View File

@ -1,3 +1,4 @@
import SceneryParserLog from "./scenery-parser-log";
import SpawnInfo from "./spawn-info";
import TrackObject from "./track-object";
import Vector3 from "./vector3";
@ -16,6 +17,10 @@ export default class Signal extends TrackObject {
spawn_info: is_spawn ? spawn_info : null,
signal_name
});
if (this.is_spawn && !this.spawn_info) {
SceneryParserLog.warn('signalWithoutSpawnInfo', `Signal ${this.id} is marked as spawn but has no spawn info`);
}
}
getPrintableSignalName() {

View File

@ -47,6 +47,7 @@ export default class Switch extends SceneryObject {
applyObject(scenery) {
if(this.applied) return; // already applied
this.applied = true;
this.outs = Switch.getOutsFromData(this.data);
const r = Switch.getRadiusFromModel(this.model);

View File

@ -9,6 +9,7 @@ export default class TrackObject extends SceneryObject {
category = "track-objects";
type = "TrackObject";
track;
applied = false;
constructor(id, prefab_name, pos, rot, track_id, name) {
super(id, pos, rot);
@ -34,6 +35,9 @@ export default class TrackObject extends SceneryObject {
}
applyObject(scenery) {
if(this.applied) return; // already applied
this.applied = true;
const track = scenery.getObject('tracks', this.track_id);
if (!track) {
SceneryParserLog.warn('trackObjectCannotBeApplied', `TrackObject ${this.id} cannot be applied: track ${this.track_id} not found`)

View File

@ -4,19 +4,29 @@ import sys
#
# [ TD2 SCENERY PROCESSOR ]
# by masuo
# v1.2
# v1.3
#
BAD_WORDS = ['Misc', 'Fence', 'TerrainPoint', 'Wires']
BAD_WORDS = ['Empty,Empty', 'Forest Start,Forest Start', 'EndMiscGroup', 'Fence', 'TerrainPoint', 'Wires']
def should_exclude(line: str) -> bool:
return any(bad_word in line for bad_word in BAD_WORDS)
for word in BAD_WORDS:
if line.startswith(word):
return True
if line.startswith("Misc"):
values = line.split(';')
return len(values[9].strip()) == 0 or not values[2].startswith("SignalBox")
def process_file(file_path: str):
if not file_path.endswith(".sc"):
print(f"Skipping non-.sc file: {file_path}")
return
if file_path.endswith(".lite.sc"):
print(f"Skipping already processed file: {file_path}")
return
output_path = file_path[:-3] + ".lite.sc"
print(f"Processing file: {file_path} -> {output_path}")