diff --git a/public/assets/signal-test-new.svg b/public/assets/signal-test-new.svg new file mode 100644 index 0000000..61add39 --- /dev/null +++ b/public/assets/signal-test-new.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 2e4706f..8b18860 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -36,6 +36,8 @@ const Constants = { electrificationMissingRouteTracks: true, electrificationResolverError: true, electrificationResolverWarnings: true, + signalElemsUnknownPrefab: true, + signalElemsRecognizedUnknownPrefab: true, }, errors: { invalidSceneryInfo: true diff --git a/src/model/signal_elements/enums.js b/src/model/signal_elements/enums.js new file mode 100644 index 0000000..6791a25 --- /dev/null +++ b/src/model/signal_elements/enums.js @@ -0,0 +1,39 @@ +const SignalElementsEnums = { + Type: { + UNKNOWN: 0, + STANDARD: 1, + DWARF: 2, + TOP: 4, + }, + HeadPosition: { + UNKNOWN: 0, + STANDARD: 1, + RIGHT: 2, + LEFT: 3, + }, + UnitType: { + UNKNOWN: 0, + WHITE: 1, + RED: 2, + BLUE: 3, + YELLOW_UPPER: 4, + YELLOW_LOWER: 5, + DWARF_YELLOW_S13: 6, + INNER: 7, + DWARF_W24: 8, + GREEN: 9, + OLD_GREEN: 10, + UNUSED: 11, + BROKEN: 12, + NONE: 13, + }, + BarType: { + UNKNOWN: 0, + NONE: 1, + YELLOW: 2, + GREEN: 3, + YELLOW_GREEN: 4, + } +} + +export default SignalElementsEnums; diff --git a/src/model/signal_elements/parsers-list.js b/src/model/signal_elements/parsers-list.js new file mode 100644 index 0000000..ab78127 --- /dev/null +++ b/src/model/signal_elements/parsers-list.js @@ -0,0 +1,15 @@ +import SignalElementsParserEhaEriDwarf from "./parsers/eha-eri-dwarf"; +import SignalElementsParserEhaEriStandard from "./parsers/eha-eri-standard"; + +const SignalElementsParsersList = { + 'eha-eri': { + name: 'EHA/ERI', + regex: /^(eha_|eri_)/, + subtypes: [ + SignalElementsParserEhaEriStandard, + SignalElementsParserEhaEriDwarf + ] + } +}; + +export default SignalElementsParsersList; diff --git a/src/model/signal_elements/parsers/common.js b/src/model/signal_elements/parsers/common.js new file mode 100644 index 0000000..125a26c --- /dev/null +++ b/src/model/signal_elements/parsers/common.js @@ -0,0 +1,94 @@ +import SignalElementsEnums from '../enums'; + +export default class SignalElementsParserCommon { + static getAfterLastUnderscoreNoVersion(entry) { + entry = entry?.trim(); + entry = entry?.replace(/_(old|new)$/, ''); + const afterLastUnderscore = entry?.lastIndexOf('_') + 1; + return entry?.substring(afterLastUnderscore) || null; + } + + static getHeadPosition(poleString) { + const headPosition = this.getAfterLastUnderscoreNoVersion(poleString); + if (!headPosition) return SignalElementsEnums.HeadPosition.UNKNOWN; + + switch (headPosition) { + case 'p': + return SignalElementsEnums.HeadPosition.STANDARD; + case 'r': + return SignalElementsEnums.HeadPosition.RIGHT; + case 'l': + return SignalElementsEnums.HeadPosition.LEFT; + default: + return SignalElementsEnums.HeadPosition.UNKNOWN; + } + } + + static getUnitTypeFromTypeString(typeString) { + typeString = typeString?.trim(); + if(!typeString) return SignalElementsEnums.UnitType.NONE; + + switch (typeString) { + case 'b': + return SignalElementsEnums.UnitType.WHITE; + case 'c': + return SignalElementsEnums.UnitType.RED; + case 'n': + return SignalElementsEnums.UnitType.BLUE; + case 'p1': + return SignalElementsEnums.UnitType.YELLOW_UPPER; + case 'p2': + return SignalElementsEnums.UnitType.YELLOW_LOWER; + case 'p3': + return SignalElementsEnums.UnitType.DWARF_YELLOW_S13; + case 'wew': + return SignalElementsEnums.UnitType.INNER; + case 'w24': + return SignalElementsEnums.UnitType.DWARF_W24; + case 'z': + return SignalElementsEnums.UnitType.GREEN; + case 'z2': + return SignalElementsEnums.UnitType.OLD_GREEN; + case 'zas': + return SignalElementsEnums.UnitType.UNUSED; + case 'zb': + return SignalElementsEnums.UnitType.BROKEN; + default: + return SignalElementsEnums.UnitType.UNKNOWN; + } + } + + static getUnits(entries, start, count, step) { + const units = []; + + for (let i = 0; i < count; i++) { + const index = start + i * step; + if (index >= entries.length) break; + const typeString = this.getAfterLastUnderscoreNoVersion(entries[index]); + const type = this.getUnitTypeFromTypeString(typeString); + + units[i] = type; + } + + while (units.length > 0 && units[units.length - 1] === SignalElementsEnums.UnitType.NONE) { + units.pop(); + } + + return units; + } + + static getBarType(barString) { + const typeString = this.getAfterLastUnderscoreNoVersion(barString); + + switch (typeString) { + case 'p': + return SignalElementsEnums.BarType.YELLOW; + case 'z': + return SignalElementsEnums.BarType.GREEN; + case 'pz': + return SignalElementsEnums.BarType.YELLOW_GREEN; + default: + return SignalElementsEnums.BarType.UNKNOWN; + } + } +} \ No newline at end of file diff --git a/src/model/signal_elements/parsers/eha-eri-dwarf.js b/src/model/signal_elements/parsers/eha-eri-dwarf.js new file mode 100644 index 0000000..de040db --- /dev/null +++ b/src/model/signal_elements/parsers/eha-eri-dwarf.js @@ -0,0 +1,24 @@ +import SignalElementsEnums from "../enums"; +import SignalElements from "../signal-elements"; +import SignalElementsParserCommon from "./common"; + +export default class SignalElementsParserEhaEriDwarf { + static parsers = []; + + static isAppliable(prefabName) { + const regex = /^(eha|eri)_(sp|tm|to)_k$/; + return regex.test(prefabName); + } + + static fromPrefabText(id, text) { + const entries = text.split(","); + + return new SignalElements( + SignalElementsEnums.Type.DWARF, + SignalElementsEnums.HeadPosition.STANDARD, + SignalElementsParserCommon.getUnits(entries, 2, 3, 2), + SignalElementsEnums.BarType.NONE, + [] + ); + } +} diff --git a/src/model/signal_elements/parsers/eha-eri-standard.js b/src/model/signal_elements/parsers/eha-eri-standard.js new file mode 100644 index 0000000..7cbf520 --- /dev/null +++ b/src/model/signal_elements/parsers/eha-eri-standard.js @@ -0,0 +1,24 @@ +import SignalElementsEnums from "../enums"; +import SignalElements from "../signal-elements"; +import SignalElementsParserCommon from "./common"; + +export default class SignalElementsParserEhaEriStandard { + static parsers = []; + + static isAppliable(prefabName) { + const regex = /^(eha|eri)_(ps|sbl|sp|tm|to)_(p|r|l)$/; + return regex.test(prefabName); + } + + static fromPrefabText(id, text) { + const entries = text.split(","); + + return new SignalElements( + SignalElementsEnums.Type.STANDARD, + SignalElementsParserCommon.getHeadPosition(entries[1]), + SignalElementsParserCommon.getUnits(entries, 6, 6, 1), + SignalElementsParserCommon.getBarType(entries[12]), + [] + ); + } +} diff --git a/src/model/signal_elements/signal-elements.js b/src/model/signal_elements/signal-elements.js new file mode 100644 index 0000000..61b27f9 --- /dev/null +++ b/src/model/signal_elements/signal-elements.js @@ -0,0 +1,41 @@ +import SceneryParserLog from "../scenery-parser-log"; +import SignalElementsEnums from "./enums"; +import SignalElementsParsersList from "./parsers-list"; + +export default class SignalElements { + type = SignalElementsEnums.Type.UNKNOWN; + headPosition = SignalElementsEnums.HeadPosition.UNKNOWN; + units = []; + bar; + signs = []; + + constructor(type, headPosition, units = [], bar = null, signs = []) { + Object.assign(this, { + type, + headPosition, + units, + bar, + signs + }); + } + + static fromPrefabText(id, text) { + const prefabName = text.split(",", 2)[0].trim(); + + for(const parser of Object.values(SignalElementsParsersList)) { + if (!parser.regex.test(prefabName)) continue; + + for(const subtype of parser.subtypes) { + if (subtype.isAppliable(prefabName)) { + return subtype.fromPrefabText(id, text); + } + } + + SceneryParserLog.warn('signalElemsRecognizedUnknownPrefab', `Signal ${id} recognized as ${parser.name} but has unknown prefab name: ${prefabName}`); + return null; + } + + SceneryParserLog.warn('signalElemsUnknownPrefab', `Signal ${id} has unknown prefab name: ${prefabName}`); + return null; + } +} diff --git a/src/model/track-objects/signal.js b/src/model/track-objects/signal.js index a4a40d2..07563be 100644 --- a/src/model/track-objects/signal.js +++ b/src/model/track-objects/signal.js @@ -2,21 +2,24 @@ import SceneryParserLog from "../scenery-parser-log"; import SpawnInfo from "../spawn-info"; import TrackObject from "./track-object"; import Vector3 from "../vector3"; +import SignalElements from "../signal_elements/signal-elements"; export default class Signal extends TrackObject { is_spawn; spawn_info; signal_name; + signal_elements; type = "Signal"; applied = false; - constructor(id, prefab_name, pos, rot, track_id, name, is_spawn, spawn_info, signal_name) { + constructor(id, prefab_name, pos, rot, track_id, name, is_spawn, spawn_info, signal_name, signal_elements) { super(id, prefab_name, pos, rot, track_id, name); Object.assign(this, { is_spawn, spawn_info: is_spawn ? spawn_info : null, - signal_name + signal_name, + signal_elements }); if (this.is_spawn && !this.spawn_info) { @@ -75,7 +78,8 @@ export default class Signal extends TrackObject { values[11], // name, values[12] === "Spawn Signal", // is_spawn SpawnInfo.fromText(values[15]), // spawn_info - values[29] + values[29], + SignalElements.fromPrefabText(values[1], values[2]) // signal_elements ); return signal;