Merge pull request #197 from traintastic/144-add-zone-support
144 add zone support
Dieser Commit ist enthalten in:
Commit
fabd0b2a17
@ -92,7 +92,9 @@
|
||||
<file>board_tile.rail.nx_button.svg</file>
|
||||
<file>board_tile.misc.switch.svg</file>
|
||||
<file>board_tile.misc.label.svg</file>
|
||||
<file>zone.svg</file>
|
||||
<file>clear_persistent_variables.svg</file>
|
||||
<file>highlight_zone.svg</file>
|
||||
<file>swap.svg</file>
|
||||
<file>circle/add.svg</file>
|
||||
</qresource>
|
||||
|
||||
111
client/gfx/dark/highlight_zone.svg
Normale Datei
111
client/gfx/dark/highlight_zone.svg
Normale Datei
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="highlight_zone.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="43.035714"
|
||||
inkscape:cy="51.964286"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1335"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="585"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<rect
|
||||
style="fill:#555555;stroke:#ffffff;stroke-width:2.11666667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||
id="rect3462"
|
||||
width="14.816667"
|
||||
height="14.816663"
|
||||
x="2.1166666"
|
||||
y="280.06665"
|
||||
ry="3.175" />
|
||||
<path
|
||||
id="path3640-5-2"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283333,283.24165 v -4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6-7"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 19.05,288.53332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="rect2600-2"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.4666664,277.94997 v -1.05833 c 0,-1.75895 1.4160506,-3.175 3.1750006,-3.175 v 0 H 12.7" />
|
||||
<path
|
||||
id="rect2600-3-9"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283331,277.94997 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3632-1"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.758333,273.71665 h 4.233333"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 4.2 KiB |
143
client/gfx/dark/zone.svg
Normale Datei
143
client/gfx/dark/zone.svg
Normale Datei
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="zone.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="43.035714"
|
||||
inkscape:cy="51.964286"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1335"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="585"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<path
|
||||
id="rect2600"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 7.9999994 48.000014 L 7.9999994 44.000014 C 7.9999994 37.352015 13.352 32.000014 20.000001 32.000014 L 20.000001 32.000014 L 24.000001 32.000014 "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" />
|
||||
<path
|
||||
id="path3640"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8,68.000001 0,-16"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 24.000001 88.000013 L 20.000001 88.000013 C 13.352 88.000013 7.9999995 82.648012 7.9999995 76.000011 L 7.9999995 72.000012 "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" />
|
||||
<path
|
||||
id="rect2600-3"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 16.933331,284.29998 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3640-5"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 16.933333,289.59165 0,-4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 12.699998,294.88332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="path3640-5-2"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283333,283.24165 v -4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6-7"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 19.05,288.53332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="path3632"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 28,32 H 44"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="rect2600-2"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.4666664,277.94997 v -1.05833 c 0,-1.75895 1.4160506,-3.175 3.1750006,-3.175 v 0 H 12.7" />
|
||||
<path
|
||||
id="rect2600-3-9"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283331,277.94997 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3632-1"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.758333,273.71665 h 4.233333"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3632-5"
|
||||
style="fill:none;stroke:#fffffe;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 7.4083332,294.88331 H 11.641667"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 6.4 KiB |
111
client/gfx/light/highlight_zone.svg
Normale Datei
111
client/gfx/light/highlight_zone.svg
Normale Datei
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="highlight_zone.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="43.035714"
|
||||
inkscape:cy="51.964286"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1335"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="585"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<rect
|
||||
style="fill:#aaaaaa;stroke:#000000;stroke-width:2.11666667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||
id="rect3462"
|
||||
width="14.816667"
|
||||
height="14.816663"
|
||||
x="2.1166666"
|
||||
y="280.06665"
|
||||
ry="3.175" />
|
||||
<path
|
||||
id="path3640-5-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283333,283.24165 v -4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 19.05,288.53332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="rect2600-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.4666664,277.94997 v -1.05833 c 0,-1.75895 1.4160506,-3.175 3.1750006,-3.175 v 0 H 12.7" />
|
||||
<path
|
||||
id="rect2600-3-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283331,277.94997 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3632-1"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.758333,273.71665 h 4.233333"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 4.2 KiB |
@ -66,7 +66,9 @@
|
||||
<file>board_tile.rail.nx_button.svg</file>
|
||||
<file>board_tile.misc.switch.svg</file>
|
||||
<file>board_tile.misc.label.svg</file>
|
||||
<file>zone.svg</file>
|
||||
<file>clear_persistent_variables.svg</file>
|
||||
<file>highlight_zone.svg</file>
|
||||
<file>decoder_function.mute.svg</file>
|
||||
<file>decoder_function.sound.svg</file>
|
||||
</qresource>
|
||||
|
||||
143
client/gfx/light/zone.svg
Normale Datei
143
client/gfx/light/zone.svg
Normale Datei
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="0 0 25.399999 25.400001"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
||||
sodipodi:docname="zone.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="43.035714"
|
||||
inkscape:cy="51.964286"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:window-width="1335"
|
||||
inkscape:window-height="1015"
|
||||
inkscape:window-x="585"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:pagecheckerboard="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3713"
|
||||
spacingx="0.26458333"
|
||||
empspacing="4" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by-nc/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by-nc/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:prohibits
|
||||
rdf:resource="http://creativecommons.org/ns#CommercialUse" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-271.59998)">
|
||||
<path
|
||||
id="rect2600"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 7.9999994 48.000014 L 7.9999994 44.000014 C 7.9999994 37.352015 13.352 32.000014 20.000001 32.000014 L 20.000001 32.000014 L 24.000001 32.000014 "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" />
|
||||
<path
|
||||
id="path3640"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8,68.000001 0,-16"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 24.000001 88.000013 L 20.000001 88.000013 C 13.352 88.000013 7.9999995 82.648012 7.9999995 76.000011 L 7.9999995 72.000012 "
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)" />
|
||||
<path
|
||||
id="rect2600-3"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 16.933331,284.29998 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3640-5"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 16.933333,289.59165 0,-4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 12.699998,294.88332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="path3640-5-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283333,283.24165 v -4.23334"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3637-6-7"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 19.05,288.53332 h 1.058333 c 1.75895,0 3.175,-1.41605 3.175,-3.175 v -1.05834" />
|
||||
<path
|
||||
id="path3632"
|
||||
style="fill:none;stroke:#000000;stroke-width:8.00001;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 28,32 H 44"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,0,271.59998)"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="rect2600-2"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 8.4666664,277.94997 v -1.05833 c 0,-1.75895 1.4160506,-3.175 3.1750006,-3.175 v 0 H 12.7" />
|
||||
<path
|
||||
id="rect2600-3-9"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 23.283331,277.94997 v -1.05833 c 0,-1.75895 -1.41605,-3.175 -3.175,-3.175 v 0 h -1.058333" />
|
||||
<path
|
||||
id="path3632-1"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.758333,273.71665 h 4.233333"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path3632-5"
|
||||
style="fill:none;stroke:#000000;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 7.4083332,294.88331 H 11.641667"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Nachher Breite: | Höhe: | Größe: 6.4 KiB |
55
client/src/board/blockhighlight.cpp
Normale Datei
55
client/src/board/blockhighlight.cpp
Normale Datei
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* client/src/board/blockhighlight.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "blockhighlight.hpp"
|
||||
|
||||
BlockHighlight::BlockHighlight(QObject* parent)
|
||||
: QObject(parent)
|
||||
, colorPool{{Color::Fuchsia, Color::Green, Color::Blue, Color::Lime, Color::Purple, Color::Red, Color::Aqua}}
|
||||
{
|
||||
}
|
||||
|
||||
void BlockHighlight::add(const QString& blockId, Color color)
|
||||
{
|
||||
auto& colors = m_blockColors[blockId];
|
||||
if(!colors.contains(color))
|
||||
{
|
||||
colors.append(color);
|
||||
emit colorsChanged(blockId, colors);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockHighlight::remove(const QString& blockId, Color color)
|
||||
{
|
||||
if(auto it = m_blockColors.find(blockId); it != m_blockColors.end())
|
||||
{
|
||||
if(int index = it->indexOf(color); index >= 0)
|
||||
{
|
||||
it->remove(index);
|
||||
emit colorsChanged(blockId, *it);
|
||||
if(it->isEmpty())
|
||||
{
|
||||
m_blockColors.remove(blockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
client/src/board/blockhighlight.hpp
Normale Datei
60
client/src/board/blockhighlight.hpp
Normale Datei
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* client/src/board/blockhighlight.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_BOARD_BLOCKHIGHLIGHT_HPP
|
||||
#define TRAINTASTIC_CLIENT_BOARD_BLOCKHIGHLIGHT_HPP
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QMap>
|
||||
#include <traintastic/enum/color.hpp>
|
||||
#include "../misc/colorpool.hpp"
|
||||
|
||||
class BlockHighlight : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using BlockColors = QMap<QString, QVector<Color>>;
|
||||
|
||||
private:
|
||||
BlockColors m_blockColors;
|
||||
|
||||
public:
|
||||
ColorPool colorPool;
|
||||
|
||||
explicit BlockHighlight(QObject* parent);
|
||||
|
||||
const BlockColors& blockColors() const
|
||||
{
|
||||
return m_blockColors;
|
||||
}
|
||||
|
||||
void add(const QString& blockId, Color color);
|
||||
void remove(const QString& blockId, Color color);
|
||||
|
||||
signals:
|
||||
void colorsChanged(const QString& blockId, const QVector<Color>& colors);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -31,6 +31,8 @@
|
||||
#include "boardwidget.hpp"
|
||||
#include "getboardcolorscheme.hpp"
|
||||
#include "tilepainter.hpp"
|
||||
#include "blockhighlight.hpp"
|
||||
#include "../mainwindow.hpp"
|
||||
#include "../network/board.hpp"
|
||||
#include "../network/callmethod.hpp"
|
||||
#include "../network/object.tpp"
|
||||
@ -76,6 +78,7 @@ BoardAreaWidget::BoardAreaWidget(std::shared_ptr<Board> board, QWidget* parent)
|
||||
m_boardBottom{m_board->getProperty("bottom")},
|
||||
m_grid{Grid::Dot},
|
||||
m_zoomLevel{0},
|
||||
m_blockHighlight{MainWindow::instance->blockHighlight()},
|
||||
m_mouseLeftButtonPressed{false},
|
||||
m_mouseRightButtonPressed{false},
|
||||
m_mouseMoveAction{MouseMoveAction::None},
|
||||
@ -99,6 +102,12 @@ BoardAreaWidget::BoardAreaWidget(std::shared_ptr<Board> board, QWidget* parent)
|
||||
|
||||
connect(&BoardSettings::instance(), &SettingsBase::changed, this, &BoardAreaWidget::settingsChanged);
|
||||
|
||||
connect(&m_blockHighlight, &BlockHighlight::colorsChanged, this,
|
||||
[this](const QString& /*blockId*/, const QVector<Color>& /*colors*/)
|
||||
{
|
||||
update();
|
||||
});
|
||||
|
||||
for(const auto& [l, object] : m_board->tileObjects())
|
||||
tileObjectAdded(l.x, l.y, object);
|
||||
|
||||
@ -731,9 +740,32 @@ void BoardAreaWidget::paintEvent(QPaintEvent* event)
|
||||
break;
|
||||
|
||||
case TileId::RailBlock:
|
||||
tilePainter.drawBlock(id, r, a, state & 0x01, state & 0x02, m_board->getTileObject(it.first));
|
||||
{
|
||||
auto block = m_board->getTileObject(it.first);
|
||||
tilePainter.drawBlock(id, r, a, state & 0x01, state & 0x02, block);
|
||||
if(auto itColors = m_blockHighlight.blockColors().find(block->getPropertyValueString("id"));
|
||||
itColors != m_blockHighlight.blockColors().end() && !itColors->isEmpty())
|
||||
{
|
||||
for(int i = 0; i < itColors->size(); ++i)
|
||||
{
|
||||
QColor color = toQColor((*itColors)[i]);
|
||||
painter.setPen({});
|
||||
color.setAlphaF(m_colorScheme->blockHighlightAlpha);
|
||||
painter.setBrush(color);
|
||||
if(a == TileRotate::Deg0)
|
||||
{
|
||||
const auto h = r.height() / itColors->size();
|
||||
painter.drawRect(r.left(), r.top() + i * h, r.width(), h);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto w = r.width() / itColors->size();
|
||||
painter.drawRect(r.left() + i * w, r.top(), w, r.height());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
case TileId::RailDirectionControl:
|
||||
tilePainter.drawDirectionControl(id, r, a, isReserved, getDirectionControlState(it.first));
|
||||
break;
|
||||
|
||||
@ -38,6 +38,8 @@
|
||||
#include "../network/abstractproperty.hpp"
|
||||
#include "../network/objectptr.hpp"
|
||||
|
||||
class BoardWidget;
|
||||
class BlockHighlight;
|
||||
class Board;
|
||||
|
||||
class BoardAreaWidget : public QWidget
|
||||
@ -74,6 +76,8 @@ class BoardAreaWidget : public QWidget
|
||||
Grid m_grid;
|
||||
int m_zoomLevel;
|
||||
|
||||
BlockHighlight& m_blockHighlight;
|
||||
|
||||
bool m_mouseLeftButtonPressed;
|
||||
TileLocation m_mouseLeftButtonPressedTileLocation;
|
||||
bool m_mouseRightButtonPressed;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -42,6 +42,7 @@ const BoardColorScheme BoardColorScheme::dark = {
|
||||
/*.turnoutState =*/ {Qt::blue},
|
||||
/*.decouplerDeactivated =*/ {0x10, 0x10, 0x10},
|
||||
/*.decouplerActivated =*/ {0x00, 0xBF, 0xFF},
|
||||
/*.blockHighlightAlpha =*/ 0.2,
|
||||
};
|
||||
|
||||
const BoardColorScheme BoardColorScheme::light = {
|
||||
@ -64,4 +65,5 @@ const BoardColorScheme BoardColorScheme::light = {
|
||||
/*.turnoutState =*/ {Qt::cyan},
|
||||
/*.decouplerDeactivated =*/ {0xF5, 0xF5, 0xF5},
|
||||
/*.decouplerActivated =*/ {0x00, 0xBF, 0xFF},
|
||||
/*.blockHighlightAlpha =*/ 0.3,
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021,2023 Reinder Feenstra
|
||||
* Copyright (C) 2021,2023-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -49,6 +49,7 @@ struct BoardColorScheme
|
||||
const QColor turnoutState;
|
||||
const QColor decouplerDeactivated;
|
||||
const QColor decouplerActivated;
|
||||
const qreal blockHighlightAlpha;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
#include <traintastic/utils/standardpaths.hpp>
|
||||
#include "mdiarea.hpp"
|
||||
#include "mainwindow/mainwindowstatusbar.hpp"
|
||||
#include "board/blockhighlight.hpp"
|
||||
#include "clock/clock.hpp"
|
||||
#include "dialog/connectdialog.hpp"
|
||||
#include "settings/settingsdialog.hpp"
|
||||
@ -111,6 +112,7 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
m_mdiArea{new MdiArea(m_splitter)},
|
||||
m_statusBar{new MainWindowStatusBar(*this)},
|
||||
m_serverLog{nullptr},
|
||||
m_blockHighlight{new BlockHighlight(this)},
|
||||
m_toolbar{new QToolBar(this)}
|
||||
{
|
||||
instance = this;
|
||||
@ -406,6 +408,14 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
menu->addAction(Locale::tr("world:outputs") + "...", [this](){ showObject("world.outputs", Locale::tr("world:outputs")); });
|
||||
menu->addAction(Locale::tr("hardware:identifications") + "...", [this](){ showObject("world.identifications", Locale::tr("hardware:identifications")); });
|
||||
boardsAction = m_menuObjects->addAction(Theme::getIcon("board"), Locale::tr("world:boards") + "...", [this](){ showObject("world.boards", Locale::tr("world:boards")); });
|
||||
m_menuObjects->addAction(
|
||||
Theme::getIcon("zone"),
|
||||
Locale::tr("world:zones") + "...",
|
||||
[this]()
|
||||
{
|
||||
showObject("world.zones", Locale::tr("world:zones"));
|
||||
}
|
||||
);
|
||||
m_menuObjects->addAction(Theme::getIcon("clock"), Locale::tr("world:clock") + "...", [this](){ showObject("world.clock", Locale::tr("world:clock")); });
|
||||
trainsAction = m_menuObjects->addAction(Theme::getIcon("train"), Locale::tr("world:trains") + "...",
|
||||
[this]()
|
||||
|
||||
@ -47,6 +47,7 @@ class IntroductionWizard;
|
||||
class AddInterfaceWizard;
|
||||
class NewBoardWizard;
|
||||
class WorldListDialog;
|
||||
class BlockHighlight;
|
||||
|
||||
class MainWindow final : public QMainWindow
|
||||
{
|
||||
@ -71,6 +72,7 @@ class MainWindow final : public QMainWindow
|
||||
QMdiSubWindow* m_clockWindow = nullptr;
|
||||
QMap<QString, SubWindow*> m_subWindows;
|
||||
QMdiSubWindow* m_trainAndRailVehiclesSubWindow = nullptr;
|
||||
BlockHighlight* m_blockHighlight;
|
||||
// Main menu:
|
||||
QAction* m_actionConnectToServer;
|
||||
QAction* m_actionDisconnectFromServer;
|
||||
@ -138,6 +140,11 @@ class MainWindow final : public QMainWindow
|
||||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() final;
|
||||
|
||||
BlockHighlight& blockHighlight()
|
||||
{
|
||||
return *m_blockHighlight;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Connection>& connection() { return m_connection; }
|
||||
|
||||
const ObjectPtr& world() const;
|
||||
|
||||
57
client/src/misc/colorpool.cpp
Normale Datei
57
client/src/misc/colorpool.cpp
Normale Datei
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* client/src/misc/colorpool.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "colorpool.hpp"
|
||||
|
||||
ColorPool::ColorPool(std::initializer_list<Color> colors)
|
||||
{
|
||||
m_colors.reserve(colors.size());
|
||||
for(auto color : colors)
|
||||
{
|
||||
m_colors.emplace_back(std::make_pair(color, false));
|
||||
}
|
||||
}
|
||||
|
||||
Color ColorPool::aquire()
|
||||
{
|
||||
for(auto& item : m_colors)
|
||||
{
|
||||
if(!item.second)
|
||||
{
|
||||
item.second = true;
|
||||
return item.first;
|
||||
}
|
||||
}
|
||||
return Color::None;
|
||||
}
|
||||
|
||||
void ColorPool::release(Color color)
|
||||
{
|
||||
for(auto& item: m_colors)
|
||||
{
|
||||
if(item.first == color)
|
||||
{
|
||||
item.second = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
/**
|
||||
* server/test/main.cpp
|
||||
* client/src/misc/colorpool.hpp
|
||||
*
|
||||
* This file is part of the traintastic test suite.
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021 Reinder Feenstra
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -19,3 +19,25 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_MISC_COLORPOOL_HPP
|
||||
#define TRAINTASTIC_CLIENT_MISC_COLORPOOL_HPP
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <traintastic/enum/color.hpp>
|
||||
|
||||
class ColorPool
|
||||
{
|
||||
private:
|
||||
std::vector<std::pair<Color, bool>> m_colors;
|
||||
|
||||
public:
|
||||
explicit ColorPool(std::initializer_list<Color> colors);
|
||||
|
||||
Color aquire();
|
||||
void release(Color color);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -69,6 +69,8 @@ QIcon Theme::getIconForClassId(const QString& classId)
|
||||
return getIcon("clock");
|
||||
else if(classId == "train" || classId == "list.train")
|
||||
return getIcon("train");
|
||||
else if(classId == "zone" || classId == "list.zone")
|
||||
return getIcon("zone");
|
||||
else
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "objectlist/interfacelistwidget.hpp"
|
||||
#include "objectlist/throttleobjectlistwidget.hpp"
|
||||
#include "objectlist/trainlistwidget.hpp"
|
||||
#include "objectlist/zoneblocklistwidget.hpp"
|
||||
#include "object/luascripteditwidget.hpp"
|
||||
#include "object/objecteditwidget.hpp"
|
||||
#include "object/itemseditwidget.hpp"
|
||||
@ -70,6 +71,10 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
|
||||
{
|
||||
return new TrainListWidget(object, parent);
|
||||
}
|
||||
if(classId == "list.zone_block")
|
||||
{
|
||||
return new ZoneBlockListWidget(object, parent);
|
||||
}
|
||||
else if(object->classId().startsWith("list."))
|
||||
return new ObjectListWidget(object, parent);
|
||||
else if(classId == "lua.script")
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2023 Reinder Feenstra
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -53,20 +53,7 @@ ListWidget::ListWidget(const ObjectPtr& object, QWidget* parent)
|
||||
if(tableModel)
|
||||
{
|
||||
m_requestId = Connection::invalidRequestId;
|
||||
|
||||
m_tableWidget->setTableModel(tableModel);
|
||||
connect(m_tableWidget, &TableWidget::doubleClicked, this,
|
||||
[this](const QModelIndex& index)
|
||||
{
|
||||
tableDoubleClicked(index);
|
||||
});
|
||||
connect(m_tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
[this](const QItemSelection&, const QItemSelection&)
|
||||
{
|
||||
tableSelectionChanged();
|
||||
});
|
||||
tableSelectionChanged();
|
||||
|
||||
setTableModel(tableModel);
|
||||
delete spinner;
|
||||
}
|
||||
else if(error)
|
||||
@ -84,3 +71,19 @@ ListWidget::~ListWidget()
|
||||
{
|
||||
object()->connection()->cancelRequest(m_requestId);
|
||||
}
|
||||
|
||||
void ListWidget::setTableModel(const TableModelPtr& tableModel)
|
||||
{
|
||||
m_tableWidget->setTableModel(tableModel);
|
||||
connect(m_tableWidget, &TableWidget::doubleClicked, this,
|
||||
[this](const QModelIndex& index)
|
||||
{
|
||||
tableDoubleClicked(index);
|
||||
});
|
||||
connect(m_tableWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
[this](const QItemSelection&, const QItemSelection&)
|
||||
{
|
||||
tableSelectionChanged();
|
||||
});
|
||||
tableSelectionChanged();
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include "../../network/objectptr.hpp"
|
||||
#include "../../network/tablemodelptr.hpp"
|
||||
|
||||
class TableWidget;
|
||||
|
||||
@ -39,6 +40,7 @@ protected:
|
||||
|
||||
const ObjectPtr& object() const { return m_object; }
|
||||
|
||||
virtual void setTableModel(const TableModelPtr& tableModel);
|
||||
virtual void tableSelectionChanged() {}
|
||||
virtual void tableDoubleClicked(const QModelIndex& /*index*/) {}
|
||||
|
||||
|
||||
102
client/src/widget/objectlist/zoneblocklistwidget.cpp
Normale Datei
102
client/src/widget/objectlist/zoneblocklistwidget.cpp
Normale Datei
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* client/src/widget/objectlist/zoneblocklistwidget.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "zoneblocklistwidget.hpp"
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
#include <traintastic/locale/locale.hpp>
|
||||
#include "../../theme/theme.hpp"
|
||||
#include "../../network/object.hpp"
|
||||
#include "../../network/connection.hpp"
|
||||
#include "../../network/tablemodel.hpp"
|
||||
#include "../../board/blockhighlight.hpp"
|
||||
#include "../../widget/tablewidget.hpp"
|
||||
#include "../../mainwindow.hpp"
|
||||
|
||||
ZoneBlockListWidget::ZoneBlockListWidget(const ObjectPtr& object, QWidget* parent)
|
||||
: ObjectListWidget(object, parent)
|
||||
{
|
||||
m_tableWidget->setFetchAll(true);
|
||||
toolbar()->addSeparator();
|
||||
m_actionHighlight = toolbar()->addAction(Theme::getIcon("highlight_zone"), Locale::tr("list.zone:highlight_zone"),
|
||||
[this]()
|
||||
{
|
||||
updateHighlight();
|
||||
});
|
||||
m_actionHighlight->setCheckable(true);
|
||||
}
|
||||
|
||||
ZoneBlockListWidget::~ZoneBlockListWidget()
|
||||
{
|
||||
if(m_actionHighlight->isChecked())
|
||||
{
|
||||
m_actionHighlight->setChecked(false);
|
||||
updateHighlight();
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneBlockListWidget::setTableModel(const TableModelPtr& tableModel)
|
||||
{
|
||||
ObjectListWidget::setTableModel(tableModel);
|
||||
connect(tableModel.get(), &TableModel::modelReset, this,
|
||||
[this]()
|
||||
{
|
||||
if(m_actionHighlight->isChecked())
|
||||
{
|
||||
updateHighlight();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ZoneBlockListWidget::updateHighlight()
|
||||
{
|
||||
const auto highlight = m_actionHighlight->isChecked() ? m_tableWidget->getObjectIds() : QStringList();
|
||||
|
||||
if(!highlight.isEmpty() && m_highlightColor == Color::None)
|
||||
{
|
||||
m_highlightColor = MainWindow::instance->blockHighlight().colorPool.aquire();
|
||||
}
|
||||
|
||||
for(const auto& blockId : m_highlight)
|
||||
{
|
||||
if(!highlight.contains(blockId))
|
||||
{
|
||||
MainWindow::instance->blockHighlight().remove(blockId, m_highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto& blockId : highlight)
|
||||
{
|
||||
if(!m_highlight.contains(blockId))
|
||||
{
|
||||
MainWindow::instance->blockHighlight().add(blockId, m_highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
m_highlight = highlight;
|
||||
|
||||
if(m_highlight.isEmpty() && m_highlightColor != Color::None)
|
||||
{
|
||||
MainWindow::instance->blockHighlight().colorPool.release(m_highlightColor);
|
||||
m_highlightColor = Color::None;
|
||||
}
|
||||
}
|
||||
46
client/src/widget/objectlist/zoneblocklistwidget.hpp
Normale Datei
46
client/src/widget/objectlist/zoneblocklistwidget.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* client/src/widget/objectlist/zoneblocklistwidget.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_ZONEBLOCKLISTWIDGET_HPP
|
||||
#define TRAINTASTIC_CLIENT_WIDGET_OBJECTLIST_ZONEBLOCKLISTWIDGET_HPP
|
||||
|
||||
#include "objectlistwidget.hpp"
|
||||
#include <traintastic/enum/color.hpp>
|
||||
|
||||
class ZoneBlockListWidget : public ObjectListWidget
|
||||
{
|
||||
private:
|
||||
Color m_highlightColor = Color::None;
|
||||
QStringList m_highlight;
|
||||
QAction* m_actionHighlight;
|
||||
|
||||
void updateHighlight();
|
||||
|
||||
protected:
|
||||
void setTableModel(const TableModelPtr& tableModel) override;
|
||||
|
||||
public:
|
||||
explicit ZoneBlockListWidget(const ObjectPtr& object, QWidget* parent = nullptr);
|
||||
~ZoneBlockListWidget() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -54,6 +54,21 @@ QString TableWidget::getRowObjectId(int row) const
|
||||
return m_model ? m_model->getRowObjectId(row) : "";
|
||||
}
|
||||
|
||||
QStringList TableWidget::getObjectIds() const
|
||||
{
|
||||
assert(m_fetchAll);
|
||||
QStringList ids;
|
||||
if(m_model)
|
||||
{
|
||||
ids.reserve(m_model->rowCount());
|
||||
for(int row = 0; row < m_model->rowCount(); ++row)
|
||||
{
|
||||
ids.append(m_model->getRowObjectId(row));
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
void TableWidget::setTableModel(const TableModelPtr& model)
|
||||
{
|
||||
Q_ASSERT(!m_model);
|
||||
@ -88,11 +103,34 @@ void TableWidget::setTableModel(const TableModelPtr& model)
|
||||
updateRegion();
|
||||
}
|
||||
|
||||
void TableWidget::setFetchAll(bool value)
|
||||
{
|
||||
if(m_fetchAll != value)
|
||||
{
|
||||
m_fetchAll = value;
|
||||
if(m_model)
|
||||
{
|
||||
updateRegion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TableWidget::updateRegion()
|
||||
{
|
||||
assert(m_model);
|
||||
|
||||
const int columnCount = m_model->columnCount();
|
||||
const int rowCount = m_model->rowCount();
|
||||
|
||||
if(columnCount == 0 || rowCount == 0)
|
||||
return;
|
||||
|
||||
if(m_fetchAll)
|
||||
{
|
||||
m_model->setRegion(0, columnCount - 1, 0, rowCount - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const QRect r = viewport()->rect();
|
||||
const QModelIndex topLeft = indexAt(r.topLeft());
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ class TableWidget : public QTableView
|
||||
TableModelPtr m_model;
|
||||
int m_selectedRow = -1;
|
||||
QPoint m_dragStartPosition;
|
||||
bool m_fetchAll = false;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
@ -46,8 +47,10 @@ class TableWidget : public QTableView
|
||||
~TableWidget() override;
|
||||
|
||||
QString getRowObjectId(int row) const;
|
||||
QStringList getObjectIds() const;
|
||||
|
||||
void setTableModel(const TableModelPtr& model);
|
||||
void setFetchAll(bool value);
|
||||
|
||||
signals:
|
||||
void rowDragged(int row);
|
||||
|
||||
@ -202,6 +202,10 @@
|
||||
"TRAIN_LIST": {
|
||||
"type": "constant"
|
||||
},
|
||||
"TRAIN_ZONE_STATUS": {
|
||||
"type": "constant",
|
||||
"since": "0.3"
|
||||
},
|
||||
"WORLD": {
|
||||
"type": "constant",
|
||||
"since": "0.1"
|
||||
@ -212,5 +216,9 @@
|
||||
"NX_BUTTON_RAIL_TILE": {
|
||||
"type": "constant",
|
||||
"since": "0.3"
|
||||
},
|
||||
"ZONE": {
|
||||
"type": "constant",
|
||||
"since": "0.3"
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,6 @@
|
||||
{
|
||||
"active_train": {},
|
||||
"mute": {},
|
||||
"no_smoke": {},
|
||||
"trains": {}
|
||||
}
|
||||
|
||||
1
manual/luadoc/object/stateobject.json
Normale Datei
1
manual/luadoc/object/stateobject.json
Normale Datei
@ -0,0 +1 @@
|
||||
{}
|
||||
@ -6,9 +6,12 @@
|
||||
"powered": {},
|
||||
"active": {},
|
||||
"mode": {},
|
||||
"blocks": {},
|
||||
"mute": {},
|
||||
"no_smoke": {},
|
||||
"has_throttle": {},
|
||||
"throttle_name": {},
|
||||
"blocks": {},
|
||||
"zones": {},
|
||||
"on_block_assigned": {
|
||||
"parameters": [
|
||||
{
|
||||
@ -72,5 +75,71 @@
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_assigned": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_entering": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_entered": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_leaving": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_left": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
},
|
||||
"on_zone_removed": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
],
|
||||
"since": "0.3"
|
||||
}
|
||||
}
|
||||
|
||||
11
manual/luadoc/object/trainzonestatus.json
Normale Datei
11
manual/luadoc/object/trainzonestatus.json
Normale Datei
@ -0,0 +1,11 @@
|
||||
{
|
||||
"state": {
|
||||
"since": "0.3"
|
||||
},
|
||||
"train": {
|
||||
"since": "0.3"
|
||||
},
|
||||
"zone": {
|
||||
"since": "0.3"
|
||||
}
|
||||
}
|
||||
@ -46,5 +46,6 @@
|
||||
}
|
||||
],
|
||||
"since": "0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"zones": {}
|
||||
}
|
||||
|
||||
66
manual/luadoc/object/zone.json
Normale Datei
66
manual/luadoc/object/zone.json
Normale Datei
@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": {},
|
||||
"mute": {},
|
||||
"no_smoke": {},
|
||||
"trains": {},
|
||||
"on_train_assigned": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
},
|
||||
"on_train_entering": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
},
|
||||
"on_train_entered": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
},
|
||||
"on_train_leaving": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
},
|
||||
"on_train_left": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
},
|
||||
"on_train_removed": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "train"
|
||||
},
|
||||
{
|
||||
"name": "zone"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -2031,6 +2031,194 @@
|
||||
"term": "object.turnoutrailtile.set_position:return_values",
|
||||
"definition": "`true` if the position is changed, `false` if position value is invalid or turnout is locked e.g. due to a reserved path."
|
||||
},
|
||||
{
|
||||
"term": "object.zone:title",
|
||||
"definition": "Zone"
|
||||
},
|
||||
{
|
||||
"term": "object.zone:description",
|
||||
"definition": "A group of blocks, this can for example be used to create a station or a staging area."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.name:description",
|
||||
"definition": "Zone name."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entering:description",
|
||||
"definition": "Fired when a train reserves or enters the first block in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entering.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that is entering the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entering.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_assigned:description",
|
||||
"definition": "Fired when a train is assigned to the first block in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_assigned.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that is assigned to a block in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_assigned.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_leaving:description",
|
||||
"definition": "Fired when a train reserves or enters a block that is not part of the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_leaving.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that is leaving the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_leaving.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entered:description",
|
||||
"definition": "Fired when a train is in the zone, a train is in the zone when it only occupies blocks that are part of the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entered.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that is in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_entered.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_left:description",
|
||||
"definition": "Fired when a train is no longer in the zone, a train is not longer in the zone when it doesn't occupy any block that are part of the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_left.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that has left the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_left.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_removed:description",
|
||||
"definition": "Fired when a train is removed from the last block in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_removed.parameter.train:description",
|
||||
"definition": "The {ref:object.train|train} that is removed from the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.on_train_removed.parameter.zone:description",
|
||||
"definition": "The zone."
|
||||
},
|
||||
{
|
||||
"term": "object.trainzonestatus:title",
|
||||
"definition": "Train zone status"
|
||||
},
|
||||
{
|
||||
"term": "object.trainzonestatus.state:description",
|
||||
"definition": "State of the train in the zone, a {ref:enum.zone_train_state} value."
|
||||
},
|
||||
{
|
||||
"term": "object.trainzonestatus.train:description",
|
||||
"definition": "The {ref:object.train|train}."
|
||||
},
|
||||
{
|
||||
"term": "object.trainzonestatus.zone:description",
|
||||
"definition": "The {ref:object.zone|zone}."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state:title",
|
||||
"definition": "Zone train state"
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_assigned:description",
|
||||
"definition": "Fired when the train is assigned to the first block of a zone."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_assigned.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_assigned.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train is assigned to."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entered:description",
|
||||
"definition": "Fired when the train is in a zone, the train is in a zone when it only occupies blocks that are part of a zone."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entered.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entered.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train has entered."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entering:description",
|
||||
"definition": "Fired when the train reserves or enters the first block of a zone it currently isn't in."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entering.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_entering.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train is entering."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_leaving:description",
|
||||
"definition": "Fired when the train reserves or enters a block that is not part of a zone it currently is in."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_leaving.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_leaving.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train is leaving."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_left:description",
|
||||
"definition": "Fired when the train is no longer in a zone, the train is not longer in a zone when it doesn't occupy any block that is part of a zone."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_left.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_left.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train left."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_removed:description",
|
||||
"definition": "Fired when the train is removed from the last block of a zone."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_removed.parameter.train:description",
|
||||
"definition": "The train."
|
||||
},
|
||||
{
|
||||
"term": "object.train.on_zone_removed.parameter.zone:description",
|
||||
"definition": "The {ref:object.zone|zone} that the train is removed from."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.mute:description",
|
||||
"definition": "If `true`, all trains in the zone are muted."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.no_smoke:description",
|
||||
"definition": "If `true`, smoke generaters are disabled of all trains in the zone."
|
||||
},
|
||||
{
|
||||
"term": "object.zone.trains:description",
|
||||
"definition": "List of {ref:object.trainzonestatus|train zone status} objects of all trains that are entering, entered or leaving the zone."
|
||||
},
|
||||
{
|
||||
"term": "globals.pv:description",
|
||||
"definition": "The {ref:pv|persistent variable} table."
|
||||
@ -2078,5 +2266,41 @@
|
||||
{
|
||||
"term": "pv.checking:paragraph_1",
|
||||
"definition": "To determine if a persistent variable has been set, use an `if` statement with `nil` checks. Variables in {ref:globals#pv|`pv`} that haven't been initialized or have been deleted will return `nil`. This pattern is useful for initializing default values or handling cases where the persistent variables are cleared."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state:description",
|
||||
"definition": "Enum value represeting the state of a {ref:object.train|train} related to a {ref:object.zone|zone}. For long trains and/or short zones it is possible that a train is `LEAVING` before it fully `ENTERED` the zone, in that case `LEAVING` takes preference over `ENTERING`."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state.entered:description",
|
||||
"definition": "The {ref:object.train|train} is in the {ref:object.zone|zone}, it does not occupy any {ref:object.blockrailtile|block} outside the zone."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state.entering:description",
|
||||
"definition": "The front of the {ref:object.train|train} is in or has reserved at least one {ref:object.blockrailtile|block} of the {ref:object.zone|zone}."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state.leaving:description",
|
||||
"definition": "The front of the {ref:object.train|train} is in or has reserved at least one {ref:object.blockrailtile|block} that is not of the {ref:object.zone|zone}."
|
||||
},
|
||||
{
|
||||
"term": "enum.zone_train_state.unknown:description",
|
||||
"definition": "The state is unknown, this value isn't used, it can be used for initialization purposes."
|
||||
},
|
||||
{
|
||||
"term": "object.trainzonestatus:description",
|
||||
"definition": "Object represeting the state of a train related to a zone."
|
||||
},
|
||||
{
|
||||
"term": "object.train.mute:description",
|
||||
"definition": "`true` if the train is currently muted by the {ref:object.world|world} mute or a muted {ref:object.zone|zone}, `false` otherwise."
|
||||
},
|
||||
{
|
||||
"term": "object.train.no_smoke:description",
|
||||
"definition": "`true` if the train's smoke generators are disabled by the {ref:object.world|world} no smoke or a non smoking {ref:object.zone|zone}, `false` otherwise."
|
||||
},
|
||||
{
|
||||
"term": "object.train.zones:description",
|
||||
"definition": "List of {ref:object.trainzonestatus|train zone status} objects of all zones that the train is entering, has entered and/or is leaving."
|
||||
}
|
||||
]
|
||||
|
||||
40
manual/traintasticmanual/en-us/zone.md
Normale Datei
40
manual/traintasticmanual/en-us/zone.md
Normale Datei
@ -0,0 +1,40 @@
|
||||
# Zones {#zones}
|
||||
|
||||
Zones allow you to apply common rules or restrictions to a group of blocks.
|
||||
For example, you can create a **Staging zone** that mutes trains, or a **Shunting zone** with a speed limit.
|
||||
|
||||
- A block can belong to multiple zones.
|
||||
- A zone can include any blocks, even if they are not physically adjacent.
|
||||
|
||||
## Zone list {#zone-list}
|
||||
|
||||
Open the zone list from the main menu: *Objects* → *Zones*.
|
||||
From here you can manage all zones in your world:
|
||||
|
||||
- **Add a new zone** — Click the **+** button to create a new zone.
|
||||
- **Remove a zone** — Select a zone and click the **–** button to delete it.
|
||||
- **Edit a zone** — Double-click a zone in the list or use the **pencil** button to open its properties.
|
||||
|
||||
## Zone properties {#zone-properties}
|
||||
|
||||
Each zone has a set of properties that define its behavior.
|
||||
|
||||
### Identification
|
||||
- **Name** — A short description of the zone, e.g. *Shunting Area* or *Staging Area*.
|
||||
- **ID** — Unique identifier of the zone.
|
||||
- Must be unique among all objects.
|
||||
- Used for accessing the zone from the [Lua scripting engine](lua.md).
|
||||
- Must start with a letter (`a–z`).
|
||||
- May only contain lowercase letters (`a–z`), digits (`0–9`), and underscores (`_`).
|
||||
|
||||
### Block membership
|
||||
- **Blocks** — List of blocks included in the zone.
|
||||
Use the **+** and **–** buttons to add or remove blocks.
|
||||
The **Highlight** button shows all blocks of the zone on opened boards.
|
||||
|
||||
### Behavioral settings
|
||||
- **Mute** — When enabled, all trains in or entering the zone are muted (default: off).
|
||||
- **No smoke** — When enabled, all smoke generators of trains in or entering the zone are disabled (default: off).
|
||||
- **Speed limit** — Defines the maximum speed for trains in the zone.
|
||||
This does **not** affect manually controlled trains.
|
||||
By default, there is no speed limit.
|
||||
@ -60,6 +60,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "chapter",
|
||||
"markdown": "zone.md"
|
||||
},
|
||||
{
|
||||
"type": "chapter",
|
||||
"markdown": "clock.md"
|
||||
|
||||
@ -40,7 +40,9 @@ if(BUILD_TESTING)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(Catch2 PRIVATE -Wno-restrict) # workaround GCC bug
|
||||
endif()
|
||||
add_executable(traintastic-server-test test/main.cpp)
|
||||
add_executable(traintastic-server-test
|
||||
test/zone.cpp
|
||||
)
|
||||
add_dependencies(traintastic-server-test traintastic-lang)
|
||||
target_compile_definitions(traintastic-server-test PRIVATE -DTRAINTASTIC_TEST)
|
||||
set_target_properties(traintastic-server-test PROPERTIES
|
||||
@ -183,6 +185,8 @@ file(GLOB SOURCES
|
||||
"src/utils/*.cpp"
|
||||
"src/world/*.hpp"
|
||||
"src/world/*.cpp"
|
||||
"src/zone/*.hpp"
|
||||
"src/zone/*.cpp"
|
||||
"../shared/src/traintastic/locale/locale.cpp"
|
||||
"../shared/src/traintastic/utils/standardpaths.cpp")
|
||||
|
||||
|
||||
39
server/src/board/list/blockrailtilelist.cpp
Normale Datei
39
server/src/board/list/blockrailtilelist.cpp
Normale Datei
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* server/src/board/list/blockrailtilelist.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "blockrailtilelist.hpp"
|
||||
#include "blockrailtilelisttablemodel.hpp"
|
||||
|
||||
BlockRailTileList::BlockRailTileList(Object& _parent, std::string_view parentPropertyName)
|
||||
: ObjectList<BlockRailTile>(_parent, parentPropertyName)
|
||||
{
|
||||
}
|
||||
|
||||
TableModelPtr BlockRailTileList::getModel()
|
||||
{
|
||||
return std::make_shared<BlockRailTileListTableModel>(*this);
|
||||
}
|
||||
|
||||
bool BlockRailTileList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return BlockRailTileListTableModel::isListedProperty(name);
|
||||
}
|
||||
42
server/src/board/list/blockrailtilelist.hpp
Normale Datei
42
server/src/board/list/blockrailtilelist.hpp
Normale Datei
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* server/src/board/list/blockrailtilelist.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_BOARD_LIST_BLOCKRAILTILELIST_HPP
|
||||
#define TRAINTASTIC_SERVER_BOARD_LIST_BLOCKRAILTILELIST_HPP
|
||||
|
||||
#include "../../core/objectlist.hpp"
|
||||
#include "../tile/rail/blockrailtile.hpp"
|
||||
|
||||
class BlockRailTileList : public ObjectList<BlockRailTile>
|
||||
{
|
||||
CLASS_ID("list.block_rail_tile")
|
||||
|
||||
protected:
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
BlockRailTileList(Object& _parent, std::string_view parentPropertyName);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
};
|
||||
|
||||
#endif
|
||||
75
server/src/board/list/blockrailtilelisttablemodel.cpp
Normale Datei
75
server/src/board/list/blockrailtilelisttablemodel.cpp
Normale Datei
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* server/src/board/list/blockrailtilelisttablemodel.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "blockrailtilelisttablemodel.hpp"
|
||||
#include "blockrailtilelist.hpp"
|
||||
#include "../../utils/displayname.hpp"
|
||||
|
||||
constexpr uint32_t columnId = 0;
|
||||
constexpr uint32_t columnName = 1;
|
||||
|
||||
bool BlockRailTileListTableModel::isListedProperty(std::string_view name)
|
||||
{
|
||||
return
|
||||
name == "id" ||
|
||||
name == "name";
|
||||
}
|
||||
|
||||
BlockRailTileListTableModel::BlockRailTileListTableModel(ObjectList<BlockRailTile>& list) :
|
||||
ObjectListTableModel<BlockRailTile>(list)
|
||||
{
|
||||
setColumnHeaders({
|
||||
DisplayName::Object::id,
|
||||
DisplayName::Object::name,
|
||||
});
|
||||
}
|
||||
|
||||
std::string BlockRailTileListTableModel::getText(uint32_t column, uint32_t row) const
|
||||
{
|
||||
if(row < rowCount())
|
||||
{
|
||||
const BlockRailTile& blockrailtile = getItem(row);
|
||||
|
||||
switch(column)
|
||||
{
|
||||
case columnId:
|
||||
return blockrailtile.id;
|
||||
|
||||
case columnName:
|
||||
return blockrailtile.name;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void BlockRailTileListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
|
||||
{
|
||||
if(property.name() == "id")
|
||||
changed(row, columnId);
|
||||
else if(property.name() == "name")
|
||||
changed(row, columnName);
|
||||
}
|
||||
46
server/src/board/list/blockrailtilelisttablemodel.hpp
Normale Datei
46
server/src/board/list/blockrailtilelisttablemodel.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* server/src/board/list/blockrailtilelisttablemodel.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_BOARD_LIST_BLOCKRAILTILELISTTABLEMODEL_HPP
|
||||
#define TRAINTASTIC_SERVER_BOARD_LIST_BLOCKRAILTILELISTTABLEMODEL_HPP
|
||||
|
||||
#include "../../core/objectlisttablemodel.hpp"
|
||||
#include "../tile/rail/blockrailtile.hpp"
|
||||
|
||||
class BlockRailTileList;
|
||||
|
||||
class BlockRailTileListTableModel : public ObjectListTableModel<BlockRailTile>
|
||||
{
|
||||
protected:
|
||||
void propertyChanged(BaseProperty& property, uint32_t row) final;
|
||||
|
||||
public:
|
||||
CLASS_ID("table_model.blockrailtile_list")
|
||||
|
||||
static bool isListedProperty(std::string_view name);
|
||||
|
||||
BlockRailTileListTableModel(ObjectList<BlockRailTile>& list);
|
||||
|
||||
std::string getText(uint32_t column, uint32_t row) const final;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -32,6 +32,10 @@
|
||||
#include "../../../train/trainblockstatus.hpp"
|
||||
#include "../../../train/traintracking.hpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
#include "../../../zone/blockzonelist.hpp"
|
||||
#include "../../../zone/zoneblocklist.hpp"
|
||||
#include "../../list/blockrailtilelist.hpp"
|
||||
#include "../../list/blockrailtilelisttablemodel.hpp"
|
||||
#include "../../map/blockpath.hpp"
|
||||
|
||||
constexpr uint8_t toMask(BlockSide side)
|
||||
@ -56,6 +60,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
|
||||
state{this, "state", BlockState::Unknown, PropertyFlags::ReadOnly | PropertyFlags::StoreState},
|
||||
sensorStates{*this, "sensor_states", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
|
||||
, trains{*this, "trains", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, zones{this, "zones", {}, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
, assignTrain{*this, "assign_train",
|
||||
[this](const std::shared_ptr<Train>& newTrain)
|
||||
{
|
||||
@ -113,8 +118,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
|
||||
}
|
||||
}
|
||||
|
||||
newTrain->fireBlockAssigned(shared_ptr<BlockRailTile>());
|
||||
fireEvent(onTrainAssigned, newTrain, self);
|
||||
TrainTracking::assigned(newTrain, self);
|
||||
}
|
||||
}}
|
||||
, removeTrain{*this, "remove_train",
|
||||
@ -171,6 +175,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
|
||||
, onTrainRemoved{*this, "on_train_removed", EventFlags::Scriptable}
|
||||
{
|
||||
inputMap.setValueInternal(std::make_shared<BlockInputMap>(*this, inputMap.name()));
|
||||
zones.setValueInternal(std::make_shared<BlockZoneList>(*this, zones.name()));
|
||||
|
||||
const bool editable = contains(m_world.state.value(), WorldState::Edit);
|
||||
|
||||
@ -191,6 +196,8 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
|
||||
Attributes::addObjectEditor(trains, false);
|
||||
m_interfaceItems.add(trains);
|
||||
|
||||
m_interfaceItems.add(zones);
|
||||
|
||||
Attributes::addEnabled(assignTrain, true);
|
||||
Attributes::addObjectEditor(assignTrain, false);
|
||||
Attributes::addObjectList(assignTrain, world.trains);
|
||||
@ -480,8 +487,7 @@ bool BlockRailTile::removeTrainInternal(const std::shared_ptr<TrainBlockStatus>
|
||||
}
|
||||
}
|
||||
|
||||
oldTrain->fireBlockRemoved(shared_ptr<BlockRailTile>());
|
||||
fireEvent(onTrainRemoved, oldTrain, self);
|
||||
TrainTracking::removed(oldTrain, shared_ptr<BlockRailTile>());
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -541,6 +547,13 @@ void BlockRailTile::worldEvent(WorldState worldState, WorldEvent worldEvent)
|
||||
Attributes::setEnabled(name, editable);
|
||||
}
|
||||
|
||||
void BlockRailTile::addToWorld()
|
||||
{
|
||||
RailTile::addToWorld();
|
||||
|
||||
m_world.blockRailTiles->addObject(shared_ptr<BlockRailTile>());
|
||||
}
|
||||
|
||||
void BlockRailTile::loaded()
|
||||
{
|
||||
RailTile::loaded();
|
||||
@ -555,6 +568,11 @@ void BlockRailTile::destroying()
|
||||
{
|
||||
trains.back()->destroy();
|
||||
}
|
||||
while(!zones->empty())
|
||||
{
|
||||
zones->back()->blocks->remove(self);
|
||||
}
|
||||
m_world.blockRailTiles->removeObject(self);
|
||||
RailTile::destroying();
|
||||
}
|
||||
|
||||
@ -653,6 +671,11 @@ void BlockRailTile::updateHeightWidthMax()
|
||||
Attributes::setMax<uint8_t>(width, !vertical ? TileData::widthMax : 1);
|
||||
}
|
||||
|
||||
void BlockRailTile::fireTrainAssigned(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainAssigned, train, shared_ptr<BlockRailTile>());
|
||||
}
|
||||
|
||||
void BlockRailTile::fireTrainReserved(const std::shared_ptr<Train>& train, BlockTrainDirection trainDirection)
|
||||
{
|
||||
fireEvent(
|
||||
@ -679,3 +702,8 @@ void BlockRailTile::fireTrainLeft(const std::shared_ptr<Train>& train, BlockTrai
|
||||
shared_ptr<BlockRailTile>(),
|
||||
trainDirection);
|
||||
}
|
||||
|
||||
void BlockRailTile::fireTrainRemoved(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainRemoved, train, shared_ptr<BlockRailTile>());
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ class Train;
|
||||
class TrainBlockStatus;
|
||||
class BlockInputMapItem;
|
||||
class BlockPath;
|
||||
class BlockZoneList;
|
||||
|
||||
class BlockRailTile : public RailTile
|
||||
{
|
||||
@ -60,12 +61,15 @@ class BlockRailTile : public RailTile
|
||||
void updatePaths();
|
||||
void updateHeightWidthMax();
|
||||
|
||||
void fireTrainAssigned(const std::shared_ptr<Train>& train);
|
||||
void fireTrainReserved(const std::shared_ptr<Train>& train, BlockTrainDirection trainDirection);
|
||||
void fireTrainEntered(const std::shared_ptr<Train>& train, BlockTrainDirection trainDirection);
|
||||
void fireTrainLeft(const std::shared_ptr<Train>& train, BlockTrainDirection trainDirection);
|
||||
void fireTrainRemoved(const std::shared_ptr<Train>& train);
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState worldState, WorldEvent worldEvent) final;
|
||||
void addToWorld() final;
|
||||
void loaded() final;
|
||||
void destroying() final;
|
||||
void setRotate(TileRotate value) final;
|
||||
@ -83,6 +87,7 @@ class BlockRailTile : public RailTile
|
||||
Property<BlockState> state;
|
||||
VectorProperty<SensorState> sensorStates;
|
||||
ObjectVectorProperty<TrainBlockStatus> trains;
|
||||
ObjectProperty<BlockZoneList> zones;
|
||||
Method<void(std::shared_ptr<Train>)> assignTrain;
|
||||
Method<void(std::shared_ptr<Train>)> removeTrain;
|
||||
Method<void()> flipTrain;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -125,6 +125,8 @@ class ObjectList : public AbstractObjectList
|
||||
inline const_iterator end() const noexcept { return m_items.end(); }
|
||||
inline const std::shared_ptr<T>& front() const noexcept { return m_items.front(); }
|
||||
inline std::shared_ptr<T>& front() noexcept { return m_items.front(); }
|
||||
inline const std::shared_ptr<T>& back() const noexcept { return m_items.back(); }
|
||||
inline std::shared_ptr<T>& back() noexcept { return m_items.back(); }
|
||||
inline bool empty() const noexcept { return m_items.empty(); }
|
||||
|
||||
ObjectPtr getObject(uint32_t index) final
|
||||
|
||||
@ -39,6 +39,11 @@ public:
|
||||
|
||||
SpeedLimitProperty(Object& object, std::string_view name, double value, SpeedUnit unit, PropertyFlags flags);
|
||||
SpeedLimitProperty(Object& object, std::string_view name, double value, SpeedUnit unit, PropertyFlags flags, OnChanged onChanged);
|
||||
|
||||
inline bool hasLimit() const
|
||||
{
|
||||
return (m_value < noLimitValue);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -115,9 +115,6 @@ Decoder::Decoder(World& world, std::string_view _id) :
|
||||
{
|
||||
functions.setValueInternal(std::make_shared<DecoderFunctions>(*this, functions.name()));
|
||||
|
||||
m_worldMute = contains(m_world.state.value(), WorldState::Mute);
|
||||
m_worldNoSmoke = contains(m_world.state.value(), WorldState::NoSmoke);
|
||||
|
||||
Attributes::addDisplayName(name, DisplayName::Object::name);
|
||||
Attributes::addEnabled(name, false);
|
||||
m_interfaceItems.add(name);
|
||||
@ -169,6 +166,8 @@ Decoder::Decoder(World& world, std::string_view _id) :
|
||||
m_interfaceItems.add(notes);
|
||||
|
||||
updateEditable();
|
||||
updateMute();
|
||||
updateNoSmoke();
|
||||
}
|
||||
|
||||
void Decoder::addToWorld()
|
||||
@ -258,15 +257,15 @@ bool Decoder::getFunctionValue(const std::shared_ptr<const DecoderFunction>& fun
|
||||
|
||||
assert(this == &function->decoder());
|
||||
|
||||
// Apply mute/noSmoke world states:
|
||||
if(m_worldMute)
|
||||
// Apply mute/noSmoke:
|
||||
if(m_mute)
|
||||
{
|
||||
if(function->function == DecoderFunctionFunction::Mute)
|
||||
return true;
|
||||
if(function->function == DecoderFunctionFunction::Sound && !getFunction(DecoderFunctionFunction::Mute))
|
||||
return false;
|
||||
}
|
||||
if(m_worldNoSmoke)
|
||||
if(m_noSmoke)
|
||||
{
|
||||
if(function->function == DecoderFunctionFunction::Smoke)
|
||||
return false;
|
||||
@ -307,6 +306,54 @@ void Decoder::release(Throttle& driver)
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void Decoder::updateMute()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::Mute);
|
||||
if(!value && vehicle)
|
||||
{
|
||||
value |= vehicle->mute;
|
||||
}
|
||||
if(value != m_mute)
|
||||
{
|
||||
m_mute = value;
|
||||
|
||||
if(auto muteFn = getFunction(DecoderFunctionFunction::Mute))
|
||||
{
|
||||
if(muteFn->value != m_mute)
|
||||
{
|
||||
changed(DecoderChangeFlags::FunctionValue, muteFn->number);
|
||||
}
|
||||
}
|
||||
else if(auto soundFn = getFunction(DecoderFunctionFunction::Sound))
|
||||
{
|
||||
if(soundFn->value == m_mute)
|
||||
{
|
||||
changed(DecoderChangeFlags::FunctionValue, soundFn->number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::updateNoSmoke()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::NoSmoke);
|
||||
if(!value && vehicle)
|
||||
{
|
||||
value |= vehicle->noSmoke;
|
||||
}
|
||||
if(value != m_noSmoke)
|
||||
{
|
||||
m_noSmoke = value;
|
||||
if(auto smokeFn = getFunction(DecoderFunctionFunction::Smoke))
|
||||
{
|
||||
if(smokeFn->value == m_noSmoke)
|
||||
{
|
||||
changed(DecoderChangeFlags::FunctionValue, smokeFn->number);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::destroying()
|
||||
{
|
||||
if(m_driver) // release driver throttle
|
||||
@ -325,34 +372,20 @@ void Decoder::worldEvent(WorldState state, WorldEvent event)
|
||||
IdObject::worldEvent(state, event);
|
||||
updateEditable(contains(state, WorldState::Edit));
|
||||
|
||||
// Handle mute/noSmoke world states:
|
||||
m_worldMute = contains(state, WorldState::Mute);
|
||||
m_worldNoSmoke = contains(state, WorldState::NoSmoke);
|
||||
|
||||
if(event == WorldEvent::Mute || event == WorldEvent::Unmute)
|
||||
switch(event)
|
||||
{
|
||||
bool hasMute = false;
|
||||
case WorldEvent::Mute:
|
||||
case WorldEvent::Unmute:
|
||||
updateMute();
|
||||
break;
|
||||
|
||||
for(const auto& f : *functions)
|
||||
if(f->function == DecoderFunctionFunction::Mute)
|
||||
{
|
||||
if(!f->value)
|
||||
changed(DecoderChangeFlags::FunctionValue, f->number);
|
||||
hasMute = true;
|
||||
}
|
||||
case WorldEvent::NoSmoke:
|
||||
case WorldEvent::Smoke:
|
||||
updateNoSmoke();
|
||||
break;
|
||||
|
||||
if(!hasMute)
|
||||
{
|
||||
for(const auto& f : *functions)
|
||||
if(f->function == DecoderFunctionFunction::Sound && f->value)
|
||||
changed(DecoderChangeFlags::FunctionValue, f->number);
|
||||
}
|
||||
}
|
||||
else if(event == WorldEvent::NoSmoke || event == WorldEvent::Smoke)
|
||||
{
|
||||
for(const auto& f : *functions)
|
||||
if(f->function == DecoderFunctionFunction::Smoke && f->value)
|
||||
changed(DecoderChangeFlags::FunctionValue, f->number);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -45,8 +45,8 @@ class Decoder : public IdObject
|
||||
friend class DecoderFunction;
|
||||
|
||||
private:
|
||||
bool m_worldMute;
|
||||
bool m_worldNoSmoke;
|
||||
bool m_mute = false;
|
||||
bool m_noSmoke = false;
|
||||
std::shared_ptr<Throttle> m_driver;
|
||||
|
||||
protected:
|
||||
@ -131,6 +131,9 @@ class Decoder : public IdObject
|
||||
|
||||
bool acquire(Throttle& driver, bool steal = false);
|
||||
void release(Throttle& driver);
|
||||
|
||||
void updateMute();
|
||||
void updateNoSmoke();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -104,9 +104,12 @@
|
||||
|
||||
#include "../train/train.hpp"
|
||||
#include "../train/trainlist.hpp"
|
||||
#include "../train/trainzonestatus.hpp"
|
||||
|
||||
#include "../world/world.hpp"
|
||||
|
||||
#include "../zone/zone.hpp"
|
||||
|
||||
namespace Lua {
|
||||
|
||||
static const char* metaTableName = "class";
|
||||
@ -228,8 +231,11 @@ void Class::registerValues(lua_State* L)
|
||||
|
||||
registerValue<Train>(L, "TRAIN");
|
||||
registerValue<TrainList>(L, "TRAIN_LIST");
|
||||
registerValue<TrainZoneStatus>(L, "TRAIN_ZONE_STATUS");
|
||||
|
||||
registerValue<World>(L, "WORLD");
|
||||
|
||||
registerValue<Zone>(L, "ZONE");
|
||||
}
|
||||
|
||||
void Class::push(lua_State* L, std::string_view classId)
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
#include "../../src/enum/signalaspect.hpp"
|
||||
#include <traintastic/enum/worldevent.hpp>
|
||||
#include "../../src/enum/worldscale.hpp"
|
||||
#include <traintastic/enum/zonetrainstate.hpp>
|
||||
|
||||
#define LUA_ENUMS \
|
||||
BlockTrainDirection, \
|
||||
@ -60,7 +61,8 @@
|
||||
TurnoutPosition, \
|
||||
SignalAspect, \
|
||||
WorldEvent, \
|
||||
WorldScale
|
||||
WorldScale, \
|
||||
ZoneTrainState
|
||||
|
||||
namespace Lua {
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#include "../throttle/throttle.hpp"
|
||||
#include "../utils/almostzero.hpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
#include "../zone/zone.hpp"
|
||||
|
||||
CREATE_IMPL(Train)
|
||||
|
||||
@ -78,6 +79,7 @@ Train::Train(World& world, std::string_view _id) :
|
||||
isStopped{this, "is_stopped", true, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
speed{*this, "speed", 0, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadOnly | PropertyFlags::NoStore},
|
||||
speedMax{*this, "speed_max", 0, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadWrite | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
speedLimit{*this, "speed_limit", SpeedLimitProperty::noLimitValue, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
throttleSpeed{*this, "throttle_speed", 0, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadWrite | PropertyFlags::StoreState,
|
||||
[this](double value, SpeedUnit unit)
|
||||
{
|
||||
@ -148,17 +150,26 @@ Train::Train(World& world, std::string_view _id) :
|
||||
m_throttle->release();
|
||||
}
|
||||
},
|
||||
std::bind(&Train::setTrainActive, this, std::placeholders::_1)},
|
||||
mode{this, "mode", TrainMode::ManualUnprotected, PropertyFlags::ReadWrite | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly},
|
||||
hasThrottle{this, "has_throttle", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
throttleName{this, "throttle_name", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
blocks{*this, "blocks", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly},
|
||||
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
std::bind(&Train::setTrainActive, this, std::placeholders::_1)}
|
||||
, mode{this, "mode", TrainMode::ManualUnprotected, PropertyFlags::ReadWrite | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, mute{this, "mute", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, noSmoke{this, "no_smoke", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, hasThrottle{this, "has_throttle", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, throttleName{this, "throttle_name", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, blocks{*this, "blocks", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, zones{*this, "zones", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
|
||||
, onBlockAssigned{*this, "on_block_assigned", EventFlags::Scriptable}
|
||||
, onBlockReserved{*this, "on_block_reserved", EventFlags::Scriptable}
|
||||
, onBlockEntered{*this, "on_block_entered", EventFlags::Scriptable}
|
||||
, onBlockLeft{*this, "on_block_left", EventFlags::Scriptable}
|
||||
, onBlockRemoved{*this, "on_block_removed", EventFlags::Scriptable}
|
||||
, onZoneAssigned{*this, "on_zone_assigned", EventFlags::Scriptable}
|
||||
, onZoneEntering{*this, "on_zone_entering", EventFlags::Scriptable}
|
||||
, onZoneEntered{*this, "on_zone_entered", EventFlags::Scriptable}
|
||||
, onZoneLeaving{*this, "on_zone_leaving", EventFlags::Scriptable}
|
||||
, onZoneLeft{*this, "on_zone_left", EventFlags::Scriptable}
|
||||
, onZoneRemoved{*this, "on_zone_removed", EventFlags::Scriptable}
|
||||
{
|
||||
vehicles.setValueInternal(std::make_shared<TrainVehicleList>(*this, vehicles.name()));
|
||||
|
||||
@ -180,6 +191,10 @@ Train::Train(World& world, std::string_view _id) :
|
||||
Attributes::addMinMax(speed, 0., 0., SpeedUnit::KiloMeterPerHour);
|
||||
m_interfaceItems.add(speed);
|
||||
m_interfaceItems.add(speedMax);
|
||||
|
||||
Attributes::addObjectEditor(speedLimit, false);
|
||||
m_interfaceItems.add(speedLimit);
|
||||
|
||||
Attributes::addMinMax(throttleSpeed, 0., 0., SpeedUnit::KiloMeterPerHour);
|
||||
Attributes::addEnabled(throttleSpeed, false);
|
||||
Attributes::addObjectEditor(throttleSpeed, false);
|
||||
@ -204,6 +219,12 @@ Train::Train(World& world, std::string_view _id) :
|
||||
Attributes::addValues(mode, trainModeValues);
|
||||
m_interfaceItems.add(mode);
|
||||
|
||||
Attributes::addObjectEditor(mute, false);
|
||||
m_interfaceItems.add(mute);
|
||||
|
||||
Attributes::addObjectEditor(noSmoke, false);
|
||||
m_interfaceItems.add(noSmoke);
|
||||
|
||||
Attributes::addObjectEditor(hasThrottle, false);
|
||||
m_interfaceItems.add(hasThrottle);
|
||||
|
||||
@ -213,6 +234,9 @@ Train::Train(World& world, std::string_view _id) :
|
||||
Attributes::addObjectEditor(blocks, false);
|
||||
m_interfaceItems.add(blocks);
|
||||
|
||||
Attributes::addObjectEditor(zones, false);
|
||||
m_interfaceItems.add(zones);
|
||||
|
||||
Attributes::addObjectEditor(powered, false);
|
||||
m_interfaceItems.add(powered);
|
||||
Attributes::addDisplayName(notes, DisplayName::Object::notes);
|
||||
@ -223,8 +247,90 @@ Train::Train(World& world, std::string_view _id) :
|
||||
m_interfaceItems.add(onBlockEntered);
|
||||
m_interfaceItems.add(onBlockLeft);
|
||||
m_interfaceItems.add(onBlockRemoved);
|
||||
m_interfaceItems.add(onZoneAssigned);
|
||||
m_interfaceItems.add(onZoneEntering);
|
||||
m_interfaceItems.add(onZoneEntered);
|
||||
m_interfaceItems.add(onZoneLeaving);
|
||||
m_interfaceItems.add(onZoneLeft);
|
||||
m_interfaceItems.add(onZoneRemoved);
|
||||
|
||||
updateEnabled();
|
||||
updateMute();
|
||||
updateNoSmoke();
|
||||
}
|
||||
|
||||
void Train::updateMute()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::Mute);
|
||||
if(!value)
|
||||
{
|
||||
for(const auto& zoneStatus : zones)
|
||||
{
|
||||
if(zoneStatus->zone->mute)
|
||||
{
|
||||
value = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(value != mute)
|
||||
{
|
||||
mute.setValueInternal(value);
|
||||
if(active)
|
||||
{
|
||||
for(const auto& vehicle : *vehicles)
|
||||
{
|
||||
vehicle->updateMute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Train::updateNoSmoke()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::NoSmoke);
|
||||
if(!value)
|
||||
{
|
||||
for(const auto& zoneStatus : zones)
|
||||
{
|
||||
if(zoneStatus->zone->noSmoke)
|
||||
{
|
||||
value = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(value != noSmoke)
|
||||
{
|
||||
noSmoke.setValueInternal(value);
|
||||
if(active)
|
||||
{
|
||||
for(const auto& vehicle : *vehicles)
|
||||
{
|
||||
vehicle->updateNoSmoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Train::updateSpeedLimit()
|
||||
{
|
||||
double value = SpeedLimitProperty::noLimitValue;
|
||||
SpeedUnit unit = speedLimit.unit();
|
||||
for(const auto& zoneStatus : zones)
|
||||
{
|
||||
const auto& zoneSpeedLimit = zoneStatus->zone->speedLimit;
|
||||
if(zoneSpeedLimit.getValue(unit) < value)
|
||||
{
|
||||
unit = zoneSpeedLimit.unit();
|
||||
value = zoneSpeedLimit.getValue(unit);
|
||||
}
|
||||
}
|
||||
if(value != speedLimit.getValue(unit))
|
||||
{
|
||||
speedLimit.setValueInternal(value, unit);
|
||||
//! \todo Apply to train
|
||||
}
|
||||
}
|
||||
|
||||
void Train::addToWorld()
|
||||
@ -258,13 +364,34 @@ void Train::loaded()
|
||||
}
|
||||
|
||||
vehiclesChanged();
|
||||
updateMute();
|
||||
updateNoSmoke();
|
||||
}
|
||||
|
||||
void Train::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
IdObject::worldEvent(state, event);
|
||||
|
||||
updateEnabled();
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::EditEnabled:
|
||||
case WorldEvent::EditDisabled:
|
||||
updateEnabled();
|
||||
break;
|
||||
|
||||
case WorldEvent::Mute:
|
||||
case WorldEvent::Unmute:
|
||||
updateMute();
|
||||
break;
|
||||
|
||||
case WorldEvent::NoSmoke:
|
||||
case WorldEvent::Smoke:
|
||||
updateNoSmoke();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Train::setSpeed(const double kmph)
|
||||
@ -438,7 +565,7 @@ bool Train::setTrainActive(bool val)
|
||||
//Register this train as activeTrain
|
||||
for(const auto& vehicle : *vehicles)
|
||||
{
|
||||
vehicle->activeTrain.setValueInternal(self);
|
||||
vehicle->setActiveTrain(self);
|
||||
}
|
||||
|
||||
//Sync Emergency Stop state
|
||||
@ -456,7 +583,7 @@ bool Train::setTrainActive(bool val)
|
||||
for(const auto& vehicle : *vehicles)
|
||||
{
|
||||
assert(vehicle->activeTrain.value() == self);
|
||||
vehicle->activeTrain.setValueInternal(nullptr);
|
||||
vehicle->setActiveTrain(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -605,3 +732,33 @@ void Train::fireBlockRemoved(const std::shared_ptr<BlockRailTile>& block)
|
||||
shared_ptr<Train>(),
|
||||
block);
|
||||
}
|
||||
|
||||
void Train::fireZoneAssigned(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneAssigned, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
void Train::fireZoneEntering(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneEntering, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
void Train::fireZoneEntered(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneEntered, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
void Train::fireZoneLeaving(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneLeaving, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
void Train::fireZoneLeft(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneLeft, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
void Train::fireZoneRemoved(const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
fireEvent(onZoneRemoved, shared_ptr<Train>(), zone);
|
||||
}
|
||||
|
||||
@ -38,8 +38,10 @@
|
||||
|
||||
class TrainVehicleList;
|
||||
class TrainBlockStatus;
|
||||
class TrainZoneStatus;
|
||||
class BlockRailTile;
|
||||
class PoweredRailVehicle;
|
||||
class Zone;
|
||||
class Throttle;
|
||||
|
||||
class Train : public IdObject
|
||||
@ -76,6 +78,13 @@ class Train : public IdObject
|
||||
void fireBlockEntered(const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection trainDirection);
|
||||
void fireBlockLeft(const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection trainDirection);
|
||||
|
||||
void fireZoneAssigned(const std::shared_ptr<Zone>& zone);
|
||||
void fireZoneEntering(const std::shared_ptr<Zone>& zone);
|
||||
void fireZoneEntered(const std::shared_ptr<Zone>& zone);
|
||||
void fireZoneLeaving(const std::shared_ptr<Zone>& zone);
|
||||
void fireZoneLeft(const std::shared_ptr<Zone>& zone);
|
||||
void fireZoneRemoved(const std::shared_ptr<Zone>& zone);
|
||||
|
||||
protected:
|
||||
void addToWorld() override;
|
||||
void destroying() override;
|
||||
@ -93,6 +102,7 @@ class Train : public IdObject
|
||||
Property<bool> isStopped;
|
||||
SpeedProperty speed;
|
||||
SpeedProperty speedMax;
|
||||
SpeedProperty speedLimit;
|
||||
SpeedProperty throttleSpeed;
|
||||
Method<void()> stop;
|
||||
Property<bool> emergencyStop;
|
||||
@ -102,6 +112,8 @@ class Train : public IdObject
|
||||
Property<bool> powered;
|
||||
Property<bool> active;
|
||||
Property<TrainMode> mode;
|
||||
Property<bool> mute;
|
||||
Property<bool> noSmoke;
|
||||
Property<bool> hasThrottle;
|
||||
Property<std::string> throttleName;
|
||||
|
||||
@ -109,15 +121,26 @@ class Train : public IdObject
|
||||
//! Index 0 is the block where the head of the train is.
|
||||
//! If the train changes direction this list will be reversed.
|
||||
ObjectVectorProperty<TrainBlockStatus> blocks;
|
||||
ObjectVectorProperty<TrainZoneStatus> zones;
|
||||
Property<std::string> notes;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&> onBlockAssigned;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&, BlockTrainDirection> onBlockReserved;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&, BlockTrainDirection> onBlockEntered;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&, BlockTrainDirection> onBlockLeft;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&> onBlockRemoved;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneAssigned;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneEntering;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneEntered;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneLeaving;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneLeft;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onZoneRemoved;
|
||||
|
||||
Train(World& world, std::string_view _id);
|
||||
|
||||
void updateMute();
|
||||
void updateNoSmoke();
|
||||
void updateSpeedLimit();
|
||||
|
||||
std::error_code acquire(Throttle& throttle, bool steal = false);
|
||||
std::error_code release(Throttle& throttle);
|
||||
std::error_code setSpeed(Throttle& throttle, double value);
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
* Copyright (C) 2024-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -26,19 +26,41 @@
|
||||
#include "../board/tile/rail/blockrailtile.hpp"
|
||||
#include "../board/map/blockpath.hpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../zone/blockzonelist.hpp"
|
||||
#include "../world/world.hpp"
|
||||
|
||||
void TrainTracking::assigned(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
checkZoneAssigned(train, block);
|
||||
train->fireBlockAssigned(block);
|
||||
block->fireTrainAssigned(train);
|
||||
}
|
||||
|
||||
void TrainTracking::reserve(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection direction)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
checkZoneLeaving(train, block);
|
||||
|
||||
block->trains.appendInternal(TrainBlockStatus::create(*block, *train, direction));
|
||||
block->updateTrainMethodEnabled();
|
||||
|
||||
checkZoneEntering(train, block);
|
||||
|
||||
train->fireBlockReserved(block, direction);
|
||||
block->fireTrainReserved(train, direction);
|
||||
}
|
||||
|
||||
void TrainTracking::enter(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection direction)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
checkZoneLeaving(train, block);
|
||||
|
||||
auto status = TrainBlockStatus::create(*block, *train, direction);
|
||||
block->trains.appendInternal(status);
|
||||
|
||||
@ -47,14 +69,17 @@ void TrainTracking::enter(const std::shared_ptr<Train>& train, const std::shared
|
||||
enter(status);
|
||||
}
|
||||
|
||||
void TrainTracking::enter(const std::shared_ptr<TrainBlockStatus>& status)
|
||||
void TrainTracking::enter(const std::shared_ptr<TrainBlockStatus>& blockStatus)
|
||||
{
|
||||
assert(status);
|
||||
const auto& train = status->train.value();
|
||||
const auto& block = status->block.value();
|
||||
const auto direction = status->direction.value();
|
||||
assert(blockStatus);
|
||||
const auto& train = blockStatus->train.value();
|
||||
const auto& block = blockStatus->block.value();
|
||||
const auto direction = blockStatus->direction.value();
|
||||
|
||||
status->train->blocks.insertInternal(0, status); // head of train
|
||||
blockStatus->train->blocks.insertInternal(0, blockStatus); // head of train
|
||||
|
||||
checkZoneEntering(train, block);
|
||||
checkZoneEntered(train, block);
|
||||
|
||||
train->fireBlockEntered(block, direction);
|
||||
block->fireTrainEntered(train, direction);
|
||||
@ -77,18 +102,22 @@ void TrainTracking::enter(const std::shared_ptr<TrainBlockStatus>& status)
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::left(std::shared_ptr<TrainBlockStatus> status)
|
||||
void TrainTracking::left(std::shared_ptr<TrainBlockStatus> blockStatus)
|
||||
{
|
||||
const auto& train = status->train.value();
|
||||
const auto& block = status->block.value();
|
||||
const auto direction = status->direction.value();
|
||||
assert(blockStatus);
|
||||
const auto& train = blockStatus->train.value();
|
||||
const auto& block = blockStatus->block.value();
|
||||
const auto direction = blockStatus->direction.value();
|
||||
|
||||
train->blocks.removeInternal(status);
|
||||
block->trains.removeInternal(status);
|
||||
train->blocks.removeInternal(blockStatus);
|
||||
block->trains.removeInternal(blockStatus);
|
||||
|
||||
block->updateTrainMethodEnabled();
|
||||
block->updateState();
|
||||
|
||||
checkZoneEntered(train, block);
|
||||
checkZoneLeft(train, block);
|
||||
|
||||
train->fireBlockLeft(block, direction);
|
||||
block->fireTrainLeft(train, direction);
|
||||
|
||||
@ -107,10 +136,201 @@ void TrainTracking::left(std::shared_ptr<TrainBlockStatus> status)
|
||||
pathB->delayedRelease(block->world().pathReleaseDelay);
|
||||
}
|
||||
|
||||
status->destroy();
|
||||
blockStatus->destroy();
|
||||
#ifndef NDEBUG
|
||||
std::weak_ptr<TrainBlockStatus> statusWeak = status;
|
||||
status.reset();
|
||||
assert(statusWeak.expired());
|
||||
std::weak_ptr<TrainBlockStatus> blockStatusWeak = blockStatus;
|
||||
blockStatus.reset();
|
||||
assert(blockStatusWeak.expired());
|
||||
#endif
|
||||
}
|
||||
|
||||
void TrainTracking::removed(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
checkZoneRemoved(train, block);
|
||||
train->fireBlockRemoved(block);
|
||||
block->fireTrainRemoved(train);
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneAssigned(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(auto& zone : *block->zones)
|
||||
{
|
||||
auto zoneStatus = zone->getTrainZoneStatus(train);
|
||||
if(zoneStatus)
|
||||
{
|
||||
continue; // train already known
|
||||
}
|
||||
|
||||
zoneStatus = TrainZoneStatus::create(*zone, *train, ZoneTrainState::Entered);
|
||||
zone->trains.appendInternal(zoneStatus);
|
||||
train->zones.appendInternal(zoneStatus);
|
||||
|
||||
trainEnteredOrLeftZone(*train, *zone);
|
||||
|
||||
train->fireZoneAssigned(zone);
|
||||
zone->fireTrainAssigned(train);
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneEntering(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(auto& zone : *block->zones)
|
||||
{
|
||||
auto zoneStatus = zone->getTrainZoneStatus(train);
|
||||
if(zoneStatus && (zoneStatus->state == ZoneTrainState::Entering || zoneStatus->state == ZoneTrainState::Entered))
|
||||
{
|
||||
continue; // train already in or entering zone
|
||||
}
|
||||
|
||||
if(!zoneStatus)
|
||||
{
|
||||
zoneStatus = TrainZoneStatus::create(*zone, *train, ZoneTrainState::Entering);
|
||||
zone->trains.appendInternal(zoneStatus);
|
||||
train->zones.appendInternal(zoneStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(zoneStatus->state.value() == ZoneTrainState::Leaving);
|
||||
zoneStatus->state.setValueInternal(ZoneTrainState::Entering);
|
||||
}
|
||||
|
||||
zone->fireTrainEntering(train);
|
||||
train->fireZoneEntering(zone);
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneEntered(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(auto& zone : *block->zones)
|
||||
{
|
||||
auto zoneStatus = zone->getTrainZoneStatus(train);
|
||||
if(zoneStatus && zoneStatus->state == ZoneTrainState::Entered)
|
||||
{
|
||||
continue; // train already in zone
|
||||
}
|
||||
|
||||
if(!zoneStatus)
|
||||
{
|
||||
zoneStatus = TrainZoneStatus::create(*zone, *train, ZoneTrainState::Entered);
|
||||
zone->trains.appendInternal(zoneStatus);
|
||||
train->zones.appendInternal(zoneStatus);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(zoneStatus->state == ZoneTrainState::Entering || zoneStatus->state.value() == ZoneTrainState::Leaving);
|
||||
zoneStatus->state.setValueInternal(ZoneTrainState::Entered);
|
||||
}
|
||||
|
||||
trainEnteredOrLeftZone(*train, *zone);
|
||||
|
||||
zone->fireTrainEntered(train);
|
||||
train->fireZoneEntered(zone);
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneLeaving(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(const auto& zoneStatus : *train->zones)
|
||||
{
|
||||
const auto& zone = zoneStatus->zone.value();
|
||||
if(!block->zones->containsObject(zone))
|
||||
{
|
||||
zoneStatus->state.setValueInternal(ZoneTrainState::Leaving);
|
||||
zone->fireTrainLeaving(train);
|
||||
train->fireZoneLeaving(zone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneLeft(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(const auto& zone : *block->zones)
|
||||
{
|
||||
if(removeTrainIfNotInZone(train, zone))
|
||||
{
|
||||
trainEnteredOrLeftZone(*train, *zone);
|
||||
|
||||
zone->fireTrainLeft(train);
|
||||
train->fireZoneLeft(zone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrainTracking::checkZoneRemoved(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
assert(train);
|
||||
assert(block);
|
||||
|
||||
for(auto& zone : *block->zones)
|
||||
{
|
||||
if(removeTrainIfNotInZone(train, zone))
|
||||
{
|
||||
trainEnteredOrLeftZone(*train, *zone);
|
||||
|
||||
train->fireZoneRemoved(zone);
|
||||
zone->fireTrainRemoved(train);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TrainTracking::removeTrainIfNotInZone(const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
assert(train);
|
||||
assert(zone);
|
||||
|
||||
if(std::find_if(train->blocks.begin(), train->blocks.end(),
|
||||
[&zone](const auto& trainBlockStatus)
|
||||
{
|
||||
return trainBlockStatus->block->zones->containsObject(zone);
|
||||
}) == train->blocks.end())
|
||||
{
|
||||
auto zoneStatus = zone->getTrainZoneStatus(train);
|
||||
|
||||
zone->trains.removeInternal(zoneStatus);
|
||||
train->zones.removeInternal(zoneStatus);
|
||||
|
||||
zoneStatus->destroy();
|
||||
#ifndef NDEBUG
|
||||
std::weak_ptr<TrainZoneStatus> zoneStatusWeak = zoneStatus;
|
||||
zoneStatus.reset();
|
||||
assert(zoneStatusWeak.expired());
|
||||
#endif
|
||||
|
||||
return true; // removed
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrainTracking::trainEnteredOrLeftZone(Train& train, Zone& zone)
|
||||
{
|
||||
// Re-evaluate zone limitations:
|
||||
if(zone.mute)
|
||||
{
|
||||
train.updateMute();
|
||||
}
|
||||
if(zone.noSmoke)
|
||||
{
|
||||
train.updateNoSmoke();
|
||||
}
|
||||
if(zone.speedLimit.hasLimit())
|
||||
{
|
||||
train.updateSpeedLimit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
class Train;
|
||||
class BlockRailTile;
|
||||
class TrainBlockStatus;
|
||||
class Zone;
|
||||
|
||||
class TrainTracking final
|
||||
{
|
||||
@ -37,11 +38,24 @@ private:
|
||||
TrainTracking(const TrainTracking&) = delete;
|
||||
TrainTracking& operator=(const TrainTracking&) = delete;
|
||||
|
||||
static void checkZoneAssigned(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void checkZoneEntering(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void checkZoneEntered(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void checkZoneLeaving(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void checkZoneLeft(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void checkZoneRemoved(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
|
||||
static bool removeTrainIfNotInZone(const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone);
|
||||
|
||||
static void trainEnteredOrLeftZone(Train& train, Zone& zone);
|
||||
|
||||
public:
|
||||
static void assigned(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
static void reserve(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection direction);
|
||||
static void enter(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block, BlockTrainDirection direction); // enter unreserved block
|
||||
static void enter(const std::shared_ptr<TrainBlockStatus>& status); // enter reserved block
|
||||
static void left(std::shared_ptr<TrainBlockStatus> status);
|
||||
static void enter(const std::shared_ptr<TrainBlockStatus>& blockStatus); // enter reserved block
|
||||
static void left(std::shared_ptr<TrainBlockStatus> blockStatus);
|
||||
static void removed(const std::shared_ptr<Train>& train, const std::shared_ptr<BlockRailTile>& block);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -47,13 +47,13 @@ TrainVehicleList::TrainVehicleList(Train& train_, std::string_view parentPropert
|
||||
vehicle->trains.appendInternal(parent().shared_ptr<Train>());
|
||||
|
||||
if(train().active)
|
||||
vehicle->activeTrain.setValueInternal(parent().shared_ptr<Train>());
|
||||
vehicle->setActiveTrain(parent().shared_ptr<Train>());
|
||||
}}
|
||||
, remove{*this, "remove",
|
||||
[this](const std::shared_ptr<RailVehicle>& vehicle)
|
||||
{
|
||||
if(vehicle->activeTrain.value() == train().shared_ptr<Train>())
|
||||
vehicle->activeTrain.setValueInternal(nullptr);
|
||||
vehicle->setActiveTrain(nullptr);
|
||||
|
||||
vehicle->trains.removeInternal(parent().shared_ptr<Train>());
|
||||
removeObject(vehicle);
|
||||
|
||||
64
server/src/train/trainzonestatus.cpp
Normale Datei
64
server/src/train/trainzonestatus.cpp
Normale Datei
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* server/src/train/trainzonestatus.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "trainzonestatus.hpp"
|
||||
#include "train.hpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../world/world.hpp"
|
||||
#include "../zone/zone.hpp"
|
||||
|
||||
std::shared_ptr<TrainZoneStatus> TrainZoneStatus::create(Zone& zone_, Train& train_, ZoneTrainState state_, std::string_view id)
|
||||
{
|
||||
World& world = zone_.world();
|
||||
auto p = std::make_shared<TrainZoneStatus>(id.empty() ? world.getUniqueId(TrainZoneStatus::classId) : std::string{id});
|
||||
p->zone.setValueInternal(zone_.shared_ptr<Zone>());
|
||||
p->train.setValueInternal(train_.shared_ptr<Train>());
|
||||
p->state.setValueInternal(state_);
|
||||
TrainZoneStatus::addToWorld(world, *p);
|
||||
return p;
|
||||
}
|
||||
|
||||
TrainZoneStatus::TrainZoneStatus(std::string id)
|
||||
: StateObject(std::move(id))
|
||||
, zone{this, "zone", nullptr, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, train{this, "train", nullptr, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, state{this, "state", ZoneTrainState::Unknown, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
{
|
||||
m_interfaceItems.add(zone);
|
||||
|
||||
m_interfaceItems.add(train);
|
||||
|
||||
Attributes::addValues(state, zoneTrainStateValues);
|
||||
m_interfaceItems.add(state);
|
||||
}
|
||||
|
||||
void TrainZoneStatus::destroying()
|
||||
{
|
||||
auto self = shared_ptr<TrainZoneStatus>();
|
||||
if(zone)
|
||||
zone->trains.removeInternal(self);
|
||||
if(train)
|
||||
train->zones.removeInternal(self);
|
||||
removeFromWorld(zone->world(), *this);
|
||||
StateObject::destroying();
|
||||
}
|
||||
52
server/src/train/trainzonestatus.hpp
Normale Datei
52
server/src/train/trainzonestatus.hpp
Normale Datei
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* server/src/train/trainzonestatus.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_TRAIN_TRAINZONESTATUS_HPP
|
||||
#define TRAINTASTIC_SERVER_TRAIN_TRAINZONESTATUS_HPP
|
||||
|
||||
#include "../core/stateobject.hpp"
|
||||
#include "../core/property.hpp"
|
||||
#include "../core/objectproperty.hpp"
|
||||
#include <traintastic/enum/zonetrainstate.hpp>
|
||||
|
||||
class Zone;
|
||||
class Train;
|
||||
|
||||
//! \brief Represents the state of a train within a zone
|
||||
class TrainZoneStatus final : public StateObject
|
||||
{
|
||||
CLASS_ID("train_zone_status");
|
||||
|
||||
protected:
|
||||
void destroying() final;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<TrainZoneStatus> create(Zone& zone_, Train& train_, ZoneTrainState state_, std::string_view id = {});
|
||||
|
||||
ObjectProperty<Zone> zone;
|
||||
ObjectProperty<Train> train;
|
||||
Property<ZoneTrainState> state;
|
||||
|
||||
TrainZoneStatus(std::string id);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -55,8 +55,10 @@ RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
lob{*this, "lob", 0, LengthUnit::MilliMeter, PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
speedMax{*this, "speed_max", 0, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadWrite | PropertyFlags::Store},
|
||||
weight{*this, "weight", 0, WeightUnit::Ton, PropertyFlags::ReadWrite | PropertyFlags::Store, [this](double /*value*/, WeightUnit /*unit*/){ updateTotalWeight(); }},
|
||||
totalWeight{*this, "total_weight", 0, WeightUnit::Ton, PropertyFlags::ReadOnly | PropertyFlags::NoStore},
|
||||
activeTrain{this, "active_train", nullptr, PropertyFlags::ReadOnly | PropertyFlags::ScriptReadOnly | PropertyFlags::StoreState}
|
||||
totalWeight{*this, "total_weight", 0, WeightUnit::Ton, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
|
||||
, mute{this, "mute", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, noSmoke{this, "no_smoke", false, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, activeTrain{this, "active_train", nullptr, PropertyFlags::ReadOnly | PropertyFlags::ScriptReadOnly | PropertyFlags::StoreState}
|
||||
, trains{*this, "trains", {}, PropertyFlags::ReadOnly | PropertyFlags::ScriptReadOnly | PropertyFlags::NoStore}
|
||||
{
|
||||
const bool editable = contains(m_world.state.value(), WorldState::Edit);
|
||||
@ -82,6 +84,12 @@ RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
Attributes::addDisplayName(totalWeight, DisplayName::Vehicle::Rail::totalWeight);
|
||||
m_interfaceItems.insertBefore(totalWeight, notes);
|
||||
|
||||
Attributes::addObjectEditor(mute, false);
|
||||
m_interfaceItems.add(mute);
|
||||
|
||||
Attributes::addObjectEditor(noSmoke, false);
|
||||
m_interfaceItems.add(noSmoke);
|
||||
|
||||
Attributes::addDisplayName(activeTrain, DisplayName::Vehicle::Rail::train); //TODO: "Active"
|
||||
Attributes::addEnabled(activeTrain, true);
|
||||
m_interfaceItems.insertBefore(activeTrain, notes);
|
||||
@ -90,6 +98,47 @@ RailVehicle::RailVehicle(World& world, std::string_view _id) :
|
||||
m_interfaceItems.insertBefore(trains, notes);
|
||||
}
|
||||
|
||||
void RailVehicle::setActiveTrain(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
activeTrain.setValueInternal(train);
|
||||
updateMute();
|
||||
updateNoSmoke();
|
||||
}
|
||||
|
||||
void RailVehicle::updateMute()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::NoSmoke);
|
||||
if(!value && activeTrain)
|
||||
{
|
||||
value |= activeTrain->mute;
|
||||
}
|
||||
if(value != mute)
|
||||
{
|
||||
mute.setValueInternal(value);
|
||||
if(decoder)
|
||||
{
|
||||
decoder->updateMute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RailVehicle::updateNoSmoke()
|
||||
{
|
||||
bool value = contains(m_world.state, WorldState::NoSmoke);
|
||||
if(!value && activeTrain)
|
||||
{
|
||||
value |= activeTrain->noSmoke;
|
||||
}
|
||||
if(value != noSmoke)
|
||||
{
|
||||
noSmoke.setValueInternal(value);
|
||||
if(decoder)
|
||||
{
|
||||
decoder->updateNoSmoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RailVehicle::addToWorld()
|
||||
{
|
||||
Vehicle::addToWorld();
|
||||
@ -125,12 +174,31 @@ void RailVehicle::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
Vehicle::worldEvent(state, event);
|
||||
|
||||
const bool editable = contains(state, WorldState::Edit);
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::EditEnabled:
|
||||
case WorldEvent::EditDisabled:
|
||||
{
|
||||
const bool editable = contains(state, WorldState::Edit);
|
||||
Attributes::setEnabled(decoder, editable);
|
||||
Attributes::setEnabled(lob, editable);
|
||||
Attributes::setEnabled(speedMax, editable);
|
||||
Attributes::setEnabled(weight, editable);
|
||||
break;
|
||||
}
|
||||
case WorldEvent::Mute:
|
||||
case WorldEvent::Unmute:
|
||||
updateMute();
|
||||
break;
|
||||
|
||||
Attributes::setEnabled(decoder, editable);
|
||||
Attributes::setEnabled(lob, editable);
|
||||
Attributes::setEnabled(speedMax, editable);
|
||||
Attributes::setEnabled(weight, editable);
|
||||
case WorldEvent::NoSmoke:
|
||||
case WorldEvent::Smoke:
|
||||
updateNoSmoke();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double RailVehicle::calcTotalWeight(WeightUnit unit) const
|
||||
|
||||
@ -52,9 +52,16 @@ class RailVehicle : public Vehicle
|
||||
SpeedProperty speedMax;
|
||||
WeightProperty weight;
|
||||
WeightProperty totalWeight;
|
||||
Property<bool> mute;
|
||||
Property<bool> noSmoke;
|
||||
|
||||
ObjectProperty<Train> activeTrain;
|
||||
ObjectVectorProperty<Train> trains;
|
||||
|
||||
void setActiveTrain(const std::shared_ptr<Train>& train);
|
||||
|
||||
void updateMute();
|
||||
void updateNoSmoke();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -59,10 +59,14 @@
|
||||
|
||||
#include "../board/board.hpp"
|
||||
#include "../board/boardlist.hpp"
|
||||
#include "../board/list/blockrailtilelist.hpp"
|
||||
#include "../board/list/linkrailtilelist.hpp"
|
||||
#include "../board/nx/nxmanager.hpp"
|
||||
#include "../board/tile/rail/nxbuttonrailtile.hpp"
|
||||
|
||||
#include "../zone/zone.hpp"
|
||||
#include "../zone/zonelist.hpp"
|
||||
|
||||
#include "../throttle/list/throttlelist.hpp"
|
||||
#include "../train/train.hpp"
|
||||
#include "../train/trainlist.hpp"
|
||||
@ -126,12 +130,14 @@ void World::init(World& world)
|
||||
world.outputs.setValueInternal(std::make_shared<OutputList>(world, world.outputs.name(), outputListColumns));
|
||||
world.identifications.setValueInternal(std::make_shared<IdentificationList>(world, world.outputs.name(), identificationListColumns));
|
||||
world.boards.setValueInternal(std::make_shared<BoardList>(world, world.boards.name()));
|
||||
world.zones.setValueInternal(std::make_shared<ZoneList>(world, world.zones.name()));
|
||||
world.clock.setValueInternal(std::make_shared<Clock>(world, world.clock.name()));
|
||||
world.throttles.setValueInternal(std::make_shared<ThrottleList>(world, world.throttles.name(), throttleListColumns));
|
||||
world.trains.setValueInternal(std::make_shared<TrainList>(world, world.trains.name()));
|
||||
world.railVehicles.setValueInternal(std::make_shared<RailVehicleList>(world, world.railVehicles.name()));
|
||||
world.luaScripts.setValueInternal(std::make_shared<Lua::ScriptList>(world, world.luaScripts.name()));
|
||||
|
||||
world.blockRailTiles.setValueInternal(std::make_shared<BlockRailTileList>(world, world.blockRailTiles.name()));
|
||||
world.linkRailTiles.setValueInternal(std::make_shared<LinkRailTileList>(world, world.linkRailTiles.name()));
|
||||
world.nxManager.setValueInternal(std::make_shared<NXManager>(world, world.nxManager.name()));
|
||||
|
||||
@ -174,11 +180,13 @@ World::World(Private /*unused*/) :
|
||||
outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
identifications{this, "identifications", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
boards{this, "boards", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
zones{this, "zones", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
clock{this, "clock", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::Store | PropertyFlags::ScriptReadOnly},
|
||||
throttles{this, "throttles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
trains{this, "trains", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
railVehicles{this, "rail_vehicles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
luaScripts{this, "lua_scripts", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
blockRailTiles{this, "block_rail_tiles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
linkRailTiles{this, "link_rail_tiles", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
nxManager{this, "nx_manager", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
statuses(*this, "statuses", {}, PropertyFlags::ReadOnly | PropertyFlags::Store),
|
||||
@ -377,6 +385,10 @@ World::World(Private /*unused*/) :
|
||||
m_interfaceItems.add(throttles);
|
||||
Attributes::addObjectEditor(boards, false);
|
||||
m_interfaceItems.add(boards);
|
||||
|
||||
Attributes::addObjectEditor(zones, false);
|
||||
m_interfaceItems.add(zones);
|
||||
|
||||
Attributes::addObjectEditor(clock, false);
|
||||
m_interfaceItems.add(clock);
|
||||
Attributes::addObjectEditor(trains, false);
|
||||
@ -386,6 +398,9 @@ World::World(Private /*unused*/) :
|
||||
Attributes::addObjectEditor(luaScripts, false);
|
||||
m_interfaceItems.add(luaScripts);
|
||||
|
||||
Attributes::addObjectEditor(blockRailTiles, false);
|
||||
m_interfaceItems.add(blockRailTiles);
|
||||
|
||||
Attributes::addObjectEditor(linkRailTiles, false);
|
||||
m_interfaceItems.add(linkRailTiles);
|
||||
Attributes::addObjectEditor(nxManager, false);
|
||||
@ -443,6 +458,7 @@ World::~World()
|
||||
deleteAll(*inputs);
|
||||
deleteAll(*identifications);
|
||||
deleteAll(*boards);
|
||||
deleteAll(*zones);
|
||||
deleteAll(*throttles);
|
||||
deleteAll(*trains);
|
||||
deleteAll(*railVehicles);
|
||||
|
||||
@ -50,6 +50,8 @@ class InputList;
|
||||
class OutputList;
|
||||
class IdentificationList;
|
||||
class BoardList;
|
||||
class ZoneList;
|
||||
class BlockRailTileList;
|
||||
class LinkRailTileList;
|
||||
class NXManager;
|
||||
class Clock;
|
||||
@ -122,12 +124,14 @@ class World : public Object
|
||||
ObjectProperty<OutputList> outputs;
|
||||
ObjectProperty<IdentificationList> identifications;
|
||||
ObjectProperty<BoardList> boards;
|
||||
ObjectProperty<ZoneList> zones;
|
||||
ObjectProperty<Clock> clock;
|
||||
ObjectProperty<ThrottleList> throttles;
|
||||
ObjectProperty<TrainList> trains;
|
||||
ObjectProperty<RailVehicleList> railVehicles;
|
||||
ObjectProperty<Lua::ScriptList> luaScripts;
|
||||
|
||||
ObjectProperty<BlockRailTileList> blockRailTiles;
|
||||
ObjectProperty<LinkRailTileList> linkRailTiles;
|
||||
ObjectProperty<NXManager> nxManager;
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
#include "../train/train.hpp"
|
||||
#include "../train/trainblockstatus.hpp"
|
||||
#include "../lua/script.hpp"
|
||||
#include "../zone/zone.hpp"
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
@ -276,8 +277,20 @@ void WorldLoader::createObject(ObjectData& objectData)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(classId == TrainZoneStatus::classId)
|
||||
{
|
||||
if(auto zone = std::dynamic_pointer_cast<Zone>(getObject(objectData.json["zone"].get<std::string_view>()))) /*[[likely]]*/
|
||||
{
|
||||
auto train = std::dynamic_pointer_cast<Train>(getObject(objectData.json["train"].get<std::string_view>()));
|
||||
objectData.object = TrainZoneStatus::create(*zone, *train, to<ZoneTrainState>(objectData.json["state"]), id);
|
||||
}
|
||||
}
|
||||
else if(classId == Lua::Script::classId)
|
||||
objectData.object = Lua::Script::create(*m_world, id);
|
||||
else if(classId == Zone::classId)
|
||||
{
|
||||
objectData.object = Zone::create(*m_world, id);
|
||||
}
|
||||
|
||||
if(!objectData.object)
|
||||
throw LogMessageException(LogMessage::C1012_UNKNOWN_CLASS_X_CANT_RECREATE_OBJECT_X, classId, id);
|
||||
|
||||
86
server/src/zone/blockzonelist.cpp
Normale Datei
86
server/src/zone/blockzonelist.cpp
Normale Datei
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* server/src/zone/blockzonelist.cpp
|
||||
*
|
||||
* This file is part of the zonetastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "blockzonelist.hpp"
|
||||
#include "zoneblocklist.hpp"
|
||||
#include "zonelisttablemodel.hpp"
|
||||
#include "zone.hpp"
|
||||
#include "../world/getworld.hpp"
|
||||
#include "../world/world.hpp"
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../core/method.tpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
|
||||
BlockZoneList::BlockZoneList(BlockRailTile& block_, std::string_view parentPropertyName)
|
||||
: ObjectList<Zone>(block_, parentPropertyName)
|
||||
, add{*this, "add",
|
||||
[this](const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
if(!containsObject(zone))
|
||||
{
|
||||
addObject(zone);
|
||||
zone->blocks->add(parent().shared_ptr<BlockRailTile>());
|
||||
}
|
||||
}}
|
||||
, remove{*this, "remove",
|
||||
[this](const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
if(containsObject(zone))
|
||||
{
|
||||
removeObject(zone);
|
||||
zone->blocks->remove(parent().shared_ptr<BlockRailTile>());
|
||||
}
|
||||
}}
|
||||
{
|
||||
const auto& world = getWorld(parent());
|
||||
|
||||
Attributes::addDisplayName(add, DisplayName::List::add);
|
||||
Attributes::addEnabled(add, false);
|
||||
Attributes::addObjectList(add, world.zones);
|
||||
m_interfaceItems.add(add);
|
||||
|
||||
Attributes::addDisplayName(remove, DisplayName::List::remove);
|
||||
Attributes::addEnabled(remove, false);
|
||||
m_interfaceItems.add(remove);
|
||||
}
|
||||
|
||||
TableModelPtr BlockZoneList::getModel()
|
||||
{
|
||||
return std::make_shared<ZoneListTableModel>(*this);
|
||||
}
|
||||
|
||||
void BlockZoneList::worldEvent(WorldState worldState, WorldEvent worldEvent)
|
||||
{
|
||||
ObjectList<Zone>::worldEvent(worldState, worldEvent);
|
||||
Attributes::setEnabled({add, remove}, contains(worldState, WorldState::Edit) && !contains(worldState, WorldState::Run));
|
||||
}
|
||||
|
||||
bool BlockZoneList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return ZoneListTableModel::isListedProperty(name);
|
||||
}
|
||||
|
||||
BlockRailTile& BlockZoneList::block()
|
||||
{
|
||||
return static_cast<BlockRailTile&>(parent());
|
||||
}
|
||||
53
server/src/zone/blockzonelist.hpp
Normale Datei
53
server/src/zone/blockzonelist.hpp
Normale Datei
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* server/src/zone/blockzonelist.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ZONE_BLOCKZONELIST_HPP
|
||||
#define TRAINTASTIC_SERVER_ZONE_BLOCKZONELIST_HPP
|
||||
|
||||
#include "../core/method.hpp"
|
||||
#include "../core/objectlist.hpp"
|
||||
#include "zone.hpp"
|
||||
|
||||
class BlockRailTile;
|
||||
class Zone;
|
||||
|
||||
class BlockZoneList : public ObjectList<Zone>
|
||||
{
|
||||
CLASS_ID("list.block_zone")
|
||||
|
||||
private:
|
||||
inline BlockRailTile& block();
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState worldState, WorldEvent worldEvent) override;
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
Method<void(const std::shared_ptr<Zone>&)> add;
|
||||
Method<void(const std::shared_ptr<Zone>&)> remove;
|
||||
|
||||
BlockZoneList(BlockRailTile& block_, std::string_view parentPropertyName);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
};
|
||||
|
||||
#endif
|
||||
170
server/src/zone/zone.cpp
Normale Datei
170
server/src/zone/zone.cpp
Normale Datei
@ -0,0 +1,170 @@
|
||||
/**
|
||||
* server/src/zone/zone.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "zone.hpp"
|
||||
#include "zoneblocklist.hpp"
|
||||
#include "zonelist.hpp"
|
||||
#include "zonelisttablemodel.hpp"
|
||||
#include "blockzonelist.hpp"
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../core/method.tpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../core/objectvectorproperty.tpp"
|
||||
#include "../train/train.hpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
#include "../world/world.hpp"
|
||||
|
||||
CREATE_IMPL(Zone)
|
||||
|
||||
Zone::Zone(World& world, std::string_view id_)
|
||||
: IdObject(world, id_)
|
||||
, name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly}
|
||||
, mute{this, "mute", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly,
|
||||
[this](bool /*value*/)
|
||||
{
|
||||
for(auto& status : trains)
|
||||
{
|
||||
status->train->updateMute();
|
||||
}
|
||||
}}
|
||||
, noSmoke{this, "no_smoke", false, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly,
|
||||
[this](bool /*value*/)
|
||||
{
|
||||
for(auto& status : trains)
|
||||
{
|
||||
status->train->updateNoSmoke();
|
||||
}
|
||||
}}
|
||||
, speedLimit{*this, "speed_limit", SpeedLimitProperty::noLimitValue, SpeedUnit::KiloMeterPerHour, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly,
|
||||
[this](double /*value*/, SpeedUnit /*unit*/)
|
||||
{
|
||||
for(auto& status : trains)
|
||||
{
|
||||
status->train->updateSpeedLimit();
|
||||
}
|
||||
}}
|
||||
, blocks{this, "blocks", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
, trains{*this, "trains", {}, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}
|
||||
, onTrainAssigned{*this, "on_train_assigned", EventFlags::Scriptable}
|
||||
, onTrainEntering{*this, "on_train_entering", EventFlags::Scriptable}
|
||||
, onTrainEntered{*this, "on_train_entered", EventFlags::Scriptable}
|
||||
, onTrainLeaving{*this, "on_train_leaving", EventFlags::Scriptable}
|
||||
, onTrainLeft{*this, "on_train_left", EventFlags::Scriptable}
|
||||
, onTrainRemoved{*this, "on_train_removed", EventFlags::Scriptable}
|
||||
{
|
||||
blocks.setValueInternal(std::make_shared<ZoneBlockList>(*this, blocks.name()));
|
||||
|
||||
const bool editable = contains(world.state.value(), WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(name, DisplayName::Object::name);
|
||||
Attributes::addEnabled(name, editable);
|
||||
m_interfaceItems.add(name);
|
||||
|
||||
Attributes::addEnabled(mute, editable);
|
||||
m_interfaceItems.add(mute);
|
||||
|
||||
Attributes::addEnabled(noSmoke, editable);
|
||||
m_interfaceItems.add(noSmoke);
|
||||
|
||||
Attributes::addEnabled(speedLimit, editable);
|
||||
m_interfaceItems.add(speedLimit);
|
||||
|
||||
m_interfaceItems.add(blocks);
|
||||
|
||||
Attributes::addObjectEditor(trains, false);
|
||||
m_interfaceItems.add(trains);
|
||||
|
||||
m_interfaceItems.add(onTrainAssigned);
|
||||
m_interfaceItems.add(onTrainEntering);
|
||||
m_interfaceItems.add(onTrainEntered);
|
||||
m_interfaceItems.add(onTrainLeaving);
|
||||
m_interfaceItems.add(onTrainLeft);
|
||||
m_interfaceItems.add(onTrainRemoved);
|
||||
}
|
||||
|
||||
std::shared_ptr<TrainZoneStatus> Zone::getTrainZoneStatus(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
auto it = std::find_if(trains.begin(), trains.end(),
|
||||
[&train](const auto& status)
|
||||
{
|
||||
return status->train.value() == train;
|
||||
});
|
||||
|
||||
return (it != trains.end()) ? *it : std::shared_ptr<TrainZoneStatus>{};
|
||||
}
|
||||
|
||||
void Zone::worldEvent(WorldState worldState, WorldEvent worldEvent)
|
||||
{
|
||||
IdObject::worldEvent(worldState, worldEvent);
|
||||
|
||||
const bool editable = contains(worldState, WorldState::Edit);
|
||||
|
||||
Attributes::setEnabled({name, mute, noSmoke, speedLimit}, editable);
|
||||
}
|
||||
|
||||
void Zone::addToWorld()
|
||||
{
|
||||
IdObject::addToWorld();
|
||||
|
||||
m_world.zones->addObject(shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::destroying()
|
||||
{
|
||||
auto self = shared_ptr<Zone>();
|
||||
while(!blocks->empty())
|
||||
{
|
||||
blocks->back()->zones->remove(self);
|
||||
}
|
||||
m_world.zones->removeObject(self);
|
||||
IdObject::destroying();
|
||||
}
|
||||
|
||||
void Zone::fireTrainAssigned(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainAssigned, train, shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::fireTrainEntering(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainEntering, train, shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::fireTrainEntered(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainEntered, train, shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::fireTrainLeaving(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainLeaving, train, shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::fireTrainLeft(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainLeft, train, shared_ptr<Zone>());
|
||||
}
|
||||
|
||||
void Zone::fireTrainRemoved(const std::shared_ptr<Train>& train)
|
||||
{
|
||||
fireEvent(onTrainRemoved, train, shared_ptr<Zone>());
|
||||
}
|
||||
75
server/src/zone/zone.hpp
Normale Datei
75
server/src/zone/zone.hpp
Normale Datei
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* server/src/zone/zone.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ZONE_ZONE_HPP
|
||||
#define TRAINTASTIC_SERVER_ZONE_ZONE_HPP
|
||||
|
||||
#include "../core/idobject.hpp"
|
||||
#include "../core/event.hpp"
|
||||
#include "../core/objectproperty.hpp"
|
||||
#include "../core/objectvectorproperty.hpp"
|
||||
#include "../core/speedlimitproperty.hpp"
|
||||
#include "../train/trainzonestatus.hpp"
|
||||
|
||||
class ZoneBlockList;
|
||||
class Train;
|
||||
|
||||
class Zone : public IdObject
|
||||
{
|
||||
friend class TrainTracking;
|
||||
|
||||
CLASS_ID("zone")
|
||||
CREATE_DEF(Zone)
|
||||
DEFAULT_ID("zone")
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState worldState, WorldEvent worldEvent) override;
|
||||
void addToWorld() override;
|
||||
void destroying() override;
|
||||
|
||||
void fireTrainAssigned(const std::shared_ptr<Train>& train);
|
||||
void fireTrainEntering(const std::shared_ptr<Train>& train);
|
||||
void fireTrainEntered(const std::shared_ptr<Train>& train);
|
||||
void fireTrainLeaving(const std::shared_ptr<Train>& train);
|
||||
void fireTrainLeft(const std::shared_ptr<Train>& train);
|
||||
void fireTrainRemoved(const std::shared_ptr<Train>& train);
|
||||
|
||||
public:
|
||||
Zone(World& world, std::string_view id_);
|
||||
|
||||
std::shared_ptr<TrainZoneStatus> getTrainZoneStatus(const std::shared_ptr<Train>& train);
|
||||
|
||||
Property<std::string> name;
|
||||
Property<bool> mute;
|
||||
Property<bool> noSmoke;
|
||||
SpeedLimitProperty speedLimit;
|
||||
ObjectProperty<ZoneBlockList> blocks;
|
||||
ObjectVectorProperty<TrainZoneStatus> trains;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainAssigned;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainEntering;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainEntered;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainLeaving;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainLeft;
|
||||
Event<const std::shared_ptr<Train>&, const std::shared_ptr<Zone>&> onTrainRemoved;
|
||||
};
|
||||
|
||||
#endif
|
||||
85
server/src/zone/zoneblocklist.cpp
Normale Datei
85
server/src/zone/zoneblocklist.cpp
Normale Datei
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* server/src/zone/zoneblocklist.cpp
|
||||
*
|
||||
* This file is part of the zonetastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "zoneblocklist.hpp"
|
||||
#include "blockzonelist.hpp"
|
||||
#include "../board/list/blockrailtilelisttablemodel.hpp"
|
||||
#include "../world/getworld.hpp"
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../core/method.tpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
|
||||
ZoneBlockList::ZoneBlockList(Zone& zone_, std::string_view parentPropertyName)
|
||||
: ObjectList<BlockRailTile>(zone_, parentPropertyName)
|
||||
, add{*this, "add",
|
||||
[this](const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
if(!containsObject(block))
|
||||
{
|
||||
addObject(block);
|
||||
block->zones->add(parent().shared_ptr<Zone>());
|
||||
}
|
||||
}}
|
||||
, remove{*this, "remove",
|
||||
[this](const std::shared_ptr<BlockRailTile>& block)
|
||||
{
|
||||
if(containsObject(block))
|
||||
{
|
||||
removeObject(block);
|
||||
block->zones->remove(parent().shared_ptr<Zone>());
|
||||
}
|
||||
}}
|
||||
{
|
||||
const auto& world = getWorld(parent());
|
||||
const bool editable = contains(world.state.value(), WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(add, DisplayName::List::add);
|
||||
Attributes::addEnabled(add, editable);
|
||||
Attributes::addObjectList(add, world.blockRailTiles);
|
||||
m_interfaceItems.add(add);
|
||||
|
||||
Attributes::addDisplayName(remove, DisplayName::List::remove);
|
||||
Attributes::addEnabled(remove, editable);
|
||||
m_interfaceItems.add(remove);
|
||||
}
|
||||
|
||||
TableModelPtr ZoneBlockList::getModel()
|
||||
{
|
||||
return std::make_shared<BlockRailTileListTableModel>(*this);
|
||||
}
|
||||
|
||||
void ZoneBlockList::worldEvent(WorldState worldState, WorldEvent worldEvent)
|
||||
{
|
||||
ObjectList<BlockRailTile>::worldEvent(worldState, worldEvent);
|
||||
Attributes::setEnabled({add, remove}, contains(worldState, WorldState::Edit) && !contains(worldState, WorldState::Run));
|
||||
}
|
||||
|
||||
bool ZoneBlockList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return BlockRailTileListTableModel::isListedProperty(name);
|
||||
}
|
||||
|
||||
Zone& ZoneBlockList::zone()
|
||||
{
|
||||
return static_cast<Zone&>(parent());
|
||||
}
|
||||
53
server/src/zone/zoneblocklist.hpp
Normale Datei
53
server/src/zone/zoneblocklist.hpp
Normale Datei
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* server/src/zone/zoneblocklist.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ZONE_ZONEBLOCKLIST_HPP
|
||||
#define TRAINTASTIC_SERVER_ZONE_ZONEBLOCKLIST_HPP
|
||||
|
||||
#include "../core/method.hpp"
|
||||
#include "../core/objectlist.hpp"
|
||||
#include "../board/tile/rail/blockrailtile.hpp"
|
||||
|
||||
class Zone;
|
||||
class BlockRailTile;
|
||||
|
||||
class ZoneBlockList : public ObjectList<BlockRailTile>
|
||||
{
|
||||
private:
|
||||
inline Zone& zone();
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState worldState, WorldEvent worldEvent) override;
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
CLASS_ID("list.zone_block")
|
||||
|
||||
Method<void(const std::shared_ptr<BlockRailTile>&)> add;
|
||||
Method<void(const std::shared_ptr<BlockRailTile>&)> remove;
|
||||
|
||||
ZoneBlockList(Zone& zone_, std::string_view parentPropertyName);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
};
|
||||
|
||||
#endif
|
||||
75
server/src/zone/zonelist.cpp
Normale Datei
75
server/src/zone/zonelist.cpp
Normale Datei
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* server/src/zone/zonelist.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "zone.hpp"
|
||||
#include "zonelist.hpp"
|
||||
#include "zonelisttablemodel.hpp"
|
||||
#include "../world/world.hpp"
|
||||
#include "../world/getworld.hpp"
|
||||
#include "../core/attributes.hpp"
|
||||
#include "../core/method.tpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
|
||||
ZoneList::ZoneList(Object& _parent, std::string_view parentPropertyName)
|
||||
: ObjectList<Zone>(_parent, parentPropertyName)
|
||||
, create{*this, "create",
|
||||
[this]()
|
||||
{
|
||||
auto& world = getWorld(parent());
|
||||
return Zone::create(world, world.getUniqueId(Zone::defaultId));
|
||||
}}
|
||||
, delete_{*this, "delete",
|
||||
[this](const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
deleteMethodHandler(zone);
|
||||
}}
|
||||
{
|
||||
const bool editable = contains(getWorld(parent()).state.value(), WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(create, DisplayName::List::create);
|
||||
Attributes::addEnabled(create, editable);
|
||||
m_interfaceItems.add(create);
|
||||
|
||||
Attributes::addDisplayName(delete_, DisplayName::List::delete_);
|
||||
Attributes::addEnabled(delete_, editable);
|
||||
m_interfaceItems.add(delete_);
|
||||
}
|
||||
|
||||
TableModelPtr ZoneList::getModel()
|
||||
{
|
||||
return std::make_shared<ZoneListTableModel>(*this);
|
||||
}
|
||||
|
||||
void ZoneList::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
ObjectList<Zone>::worldEvent(state, event);
|
||||
|
||||
const bool editable = contains(state, WorldState::Edit);
|
||||
|
||||
Attributes::setEnabled(create, editable);
|
||||
Attributes::setEnabled(delete_, editable);
|
||||
}
|
||||
|
||||
bool ZoneList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return ZoneListTableModel::isListedProperty(name);
|
||||
}
|
||||
48
server/src/zone/zonelist.hpp
Normale Datei
48
server/src/zone/zonelist.hpp
Normale Datei
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* server/src/zone/zonelist.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ZONE_ZONELIST_HPP
|
||||
#define TRAINTASTIC_SERVER_ZONE_ZONELIST_HPP
|
||||
|
||||
#include "../core/objectlist.hpp"
|
||||
#include "../core/method.hpp"
|
||||
|
||||
class Zone;
|
||||
|
||||
class ZoneList : public ObjectList<Zone>
|
||||
{
|
||||
CLASS_ID("list.zone")
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
Method<std::shared_ptr<Zone>()> create;
|
||||
Method<void(const std::shared_ptr<Zone>&)> delete_;
|
||||
|
||||
ZoneList(Object& _parent, std::string_view parentPropertyName);
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
};
|
||||
|
||||
#endif
|
||||
77
server/src/zone/zonelisttablemodel.cpp
Normale Datei
77
server/src/zone/zonelisttablemodel.cpp
Normale Datei
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* server/src/zone/zonelisttablemodel.cpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "zonelisttablemodel.hpp"
|
||||
#include "zone.hpp"
|
||||
#include "zonelist.hpp"
|
||||
#include "../core/objectproperty.tpp"
|
||||
#include "../utils/displayname.hpp"
|
||||
|
||||
constexpr uint32_t columnId = 0;
|
||||
constexpr uint32_t columnName = 1;
|
||||
|
||||
bool ZoneListTableModel::isListedProperty(std::string_view name)
|
||||
{
|
||||
return
|
||||
name == "id" ||
|
||||
name == "name";
|
||||
}
|
||||
|
||||
ZoneListTableModel::ZoneListTableModel(ObjectList<Zone>& list) :
|
||||
ObjectListTableModel<Zone>(list)
|
||||
{
|
||||
setColumnHeaders({
|
||||
DisplayName::Object::id,
|
||||
DisplayName::Object::name,
|
||||
});
|
||||
}
|
||||
|
||||
std::string ZoneListTableModel::getText(uint32_t column, uint32_t row) const
|
||||
{
|
||||
if(row < rowCount())
|
||||
{
|
||||
const Zone& zone = getItem(row);
|
||||
|
||||
switch(column)
|
||||
{
|
||||
case columnId:
|
||||
return zone.id;
|
||||
|
||||
case columnName:
|
||||
return zone.name;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ZoneListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
|
||||
{
|
||||
if(property.name() == "id")
|
||||
changed(row, columnId);
|
||||
else if(property.name() == "name")
|
||||
changed(row, columnName);
|
||||
}
|
||||
46
server/src/zone/zonelisttablemodel.hpp
Normale Datei
46
server/src/zone/zonelisttablemodel.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* server/src/zone/zonelisttablemodel.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_ZONE_ZONELISTTABLEMODEL_HPP
|
||||
#define TRAINTASTIC_SERVER_ZONE_ZONELISTTABLEMODEL_HPP
|
||||
|
||||
#include "../core/objectlisttablemodel.hpp"
|
||||
|
||||
class Zone;
|
||||
class ZoneList;
|
||||
|
||||
class ZoneListTableModel : public ObjectListTableModel<Zone>
|
||||
{
|
||||
CLASS_ID("zone_list_table_model")
|
||||
|
||||
protected:
|
||||
void propertyChanged(BaseProperty& property, uint32_t row) final;
|
||||
|
||||
public:
|
||||
static bool isListedProperty(std::string_view name);
|
||||
|
||||
ZoneListTableModel(ObjectList<Zone>& list);
|
||||
|
||||
std::string getText(uint32_t column, uint32_t row) const final;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -26,6 +26,7 @@
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
#include "../src/board/board.hpp"
|
||||
#include "../src/board/boardlist.hpp"
|
||||
#include "../src/board/tile/rail/blockrailtile.hpp"
|
||||
#include "../src/hardware/decoder/list/decoderlist.hpp"
|
||||
#include "../src/hardware/interface/interfacelist.hpp"
|
||||
#include "../src/hardware/input/input.hpp"
|
||||
@ -36,6 +37,10 @@
|
||||
#include "vehicle/rail/railvehicles.hpp"
|
||||
#include "../src/train/trainlist.hpp"
|
||||
#include "../src/train/train.hpp"
|
||||
#include "../src/zone/zonelist.hpp"
|
||||
#include "../src/zone/zone.hpp"
|
||||
#include "../src/zone/zoneblocklist.hpp"
|
||||
#include "../src/zone/blockzonelist.hpp"
|
||||
|
||||
TEST_CASE("Create world => destroy world", "[object-create-destroy]")
|
||||
{
|
||||
@ -443,3 +448,194 @@ TEST_CASE("Create world and train => destroy train", "[object-create-destroy]")
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world and zone => destroy world", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->getClassId() == Zone::classId);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world and zone => destroy zone", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 1);
|
||||
|
||||
world->zones->delete_(zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, board, block and zone => destroy world", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 0);
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 1);
|
||||
|
||||
REQUIRE(blockWeak.lock()->zones->length == 0);
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(blockWeak.lock()->zones->length == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->front() == zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->blocks->front() == blockWeak.lock());
|
||||
|
||||
world.reset();
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, board, block and zone => destroy board", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 0);
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 1);
|
||||
|
||||
REQUIRE(blockWeak.lock()->zones->length == 0);
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(blockWeak.lock()->zones->length == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->front() == zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->blocks->front() == blockWeak.lock());
|
||||
|
||||
world->boards->delete_(boardWeak.lock());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
world.reset();
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, board, block and zone => destroy block", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 0);
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 1);
|
||||
|
||||
REQUIRE(blockWeak.lock()->zones->length == 0);
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(blockWeak.lock()->zones->length == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->front() == zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->blocks->front() == blockWeak.lock());
|
||||
|
||||
REQUIRE(boardWeak.lock()->deleteTile(0, 0));
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
world.reset();
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Create world, board, block and zone => destroy zone", "[object-create-destroy]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 0);
|
||||
REQUIRE(worldWeak.lock()->zones->length == 0);
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg0, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(worldWeak.lock()->zones->length == 1);
|
||||
|
||||
REQUIRE(blockWeak.lock()->zones->length == 0);
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(blockWeak.lock()->zones->length == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->front() == zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->blocks->front() == blockWeak.lock());
|
||||
|
||||
world->zones->delete_(zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
REQUIRE(blockWeak.lock()->zones->length == 0);
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
world.reset();
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(worldWeak.expired());
|
||||
}
|
||||
|
||||
766
server/test/zone.cpp
Normale Datei
766
server/test/zone.cpp
Normale Datei
@ -0,0 +1,766 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_approx.hpp>
|
||||
#include "../src/core/attributes.hpp"
|
||||
#include "../src/core/method.tpp"
|
||||
#include "../src/core/objectproperty.tpp"
|
||||
#include "../src/board/board.hpp"
|
||||
#include "../src/board/boardlist.hpp"
|
||||
#include "../src/board/tile/rail/blockrailtile.hpp"
|
||||
#include "../src/world/world.hpp"
|
||||
#include "../src/vehicle/rail/railvehiclelist.hpp"
|
||||
#include "../src/vehicle/rail/railvehiclelisttablemodel.hpp"
|
||||
#include "../src/vehicle/rail/locomotive.hpp"
|
||||
#include "../src/train/trainlist.hpp"
|
||||
#include "../src/train/train.hpp"
|
||||
#include "../src/train/trainvehiclelist.hpp"
|
||||
#include "../src/hardware/decoder/decoder.hpp"
|
||||
#include "../src/zone/blockzonelist.hpp"
|
||||
#include "../src/zone/zonelist.hpp"
|
||||
#include "../src/zone/zonelisttablemodel.hpp"
|
||||
#include "../src/zone/zone.hpp"
|
||||
#include "../src/zone/zoneblocklist.hpp"
|
||||
|
||||
TEST_CASE("Zone: Assign/remove train to/from muted, no smoke and speed limited zone", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
REQUIRE(world->railVehicles->length == 0);
|
||||
std::weak_ptr<RailVehicle> locomotiveWeak = world->railVehicles->create(Locomotive::classId);
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
REQUIRE(world->trains->length == 0);
|
||||
std::weak_ptr<Train> trainWeak = world->trains->create();
|
||||
REQUIRE_FALSE(trainWeak.expired());
|
||||
REQUIRE(world->trains->length == 1);
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 0);
|
||||
trainWeak.lock()->vehicles->add(locomotiveWeak.lock());
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 1);
|
||||
|
||||
REQUIRE(world->boards->length == 0);
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(world->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE_FALSE(zoneWeak.lock()->mute.value());
|
||||
REQUIRE_FALSE(zoneWeak.lock()->noSmoke.value());
|
||||
zoneWeak.lock()->mute = true;
|
||||
zoneWeak.lock()->noSmoke = true;
|
||||
zoneWeak.lock()->speedLimit.setValue(100.0);
|
||||
REQUIRE(zoneWeak.lock()->mute);
|
||||
REQUIRE(zoneWeak.lock()->noSmoke);
|
||||
REQUIRE(zoneWeak.lock()->speedLimit.value() == Catch::Approx(100.0));
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 0);
|
||||
|
||||
// Assign train to block in muted, no smoke and speed limited zone:
|
||||
REQUIRE_FALSE(trainWeak.lock()->active);
|
||||
REQUIRE_FALSE(trainWeak.lock()->mute);
|
||||
REQUIRE_FALSE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE(std::isinf(trainWeak.lock()->speedLimit.value()));
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->mute);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->noSmoke);
|
||||
blockWeak.lock()->assignTrain(trainWeak.lock());
|
||||
REQUIRE(trainWeak.lock()->active);
|
||||
REQUIRE(trainWeak.lock()->blocks.size() == 1);
|
||||
REQUIRE(trainWeak.lock()->zones.size() == 1);
|
||||
REQUIRE(blockWeak.lock()->trains.size() == 1);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 1);
|
||||
REQUIRE(trainWeak.lock()->mute);
|
||||
REQUIRE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE(trainWeak.lock()->speedLimit.value() == Catch::Approx(100.0));
|
||||
REQUIRE(locomotiveWeak.lock()->mute);
|
||||
REQUIRE(locomotiveWeak.lock()->noSmoke);
|
||||
|
||||
// Remove train from block in muted, no smoke and speed limited zone:
|
||||
blockWeak.lock()->removeTrain(trainWeak.lock());
|
||||
REQUIRE_FALSE(trainWeak.lock()->active);
|
||||
REQUIRE(trainWeak.lock()->blocks.size() == 0);
|
||||
REQUIRE(trainWeak.lock()->zones.size() == 0);
|
||||
REQUIRE(blockWeak.lock()->trains.size() == 0);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 0);
|
||||
REQUIRE_FALSE(trainWeak.lock()->mute);
|
||||
REQUIRE_FALSE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE(std::isinf(trainWeak.lock()->speedLimit.value()));
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->mute);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->noSmoke);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(trainWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: Assign/remove events", "[zone]")
|
||||
{
|
||||
size_t trainZoneAssignedEventCount = 0;
|
||||
size_t trainZoneEnteringEventCount = 0;
|
||||
size_t trainZoneEnteredEventCount = 0;
|
||||
size_t trainZoneLeavingEventCount = 0;
|
||||
size_t trainZoneLeftEventCount = 0;
|
||||
size_t trainZoneRemovedEventCount = 0;
|
||||
size_t zoneTrainAssignedEventCount = 0;
|
||||
size_t zoneTrainEnteringEventCount = 0;
|
||||
size_t zoneTrainEnteredEventCount = 0;
|
||||
size_t zoneTrainLeavingEventCount = 0;
|
||||
size_t zoneTrainLeftEventCount = 0;
|
||||
size_t zoneTrainRemovedEventCount = 0;
|
||||
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
REQUIRE(world->railVehicles->length == 0);
|
||||
std::weak_ptr<RailVehicle> locomotiveWeak = world->railVehicles->create(Locomotive::classId);
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
REQUIRE(world->trains->length == 0);
|
||||
std::weak_ptr<Train> trainWeak = world->trains->create();
|
||||
REQUIRE_FALSE(trainWeak.expired());
|
||||
REQUIRE(world->trains->length == 1);
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 0);
|
||||
trainWeak.lock()->vehicles->add(locomotiveWeak.lock());
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 1);
|
||||
|
||||
REQUIRE(world->boards->length == 0);
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(world->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 0);
|
||||
|
||||
// Setup events:
|
||||
trainWeak.lock()->onZoneAssigned.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneAssignedEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
trainWeak.lock()->onZoneEntering.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneEnteringEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
trainWeak.lock()->onZoneEntered.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneEnteredEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
trainWeak.lock()->onZoneLeaving.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneLeavingEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
trainWeak.lock()->onZoneLeft.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneLeftEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
trainWeak.lock()->onZoneRemoved.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
trainZoneRemovedEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainAssigned.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainAssignedEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainEntering.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainEnteringEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainEntered.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainEnteredEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainLeaving.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainLeavingEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainLeft.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainLeftEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
zoneWeak.lock()->onTrainRemoved.connect(
|
||||
[&](const std::shared_ptr<Train>& train, const std::shared_ptr<Zone>& zone)
|
||||
{
|
||||
zoneTrainRemovedEventCount++;
|
||||
REQUIRE(train == trainWeak.lock());
|
||||
REQUIRE(zone == zoneWeak.lock());
|
||||
});
|
||||
|
||||
REQUIRE(trainZoneAssignedEventCount == 0);
|
||||
REQUIRE(trainZoneEnteringEventCount == 0);
|
||||
REQUIRE(trainZoneEnteredEventCount == 0);
|
||||
REQUIRE(trainZoneLeavingEventCount == 0);
|
||||
REQUIRE(trainZoneLeftEventCount == 0);
|
||||
REQUIRE(trainZoneRemovedEventCount == 0);
|
||||
REQUIRE(zoneTrainAssignedEventCount == 0);
|
||||
REQUIRE(zoneTrainEnteringEventCount == 0);
|
||||
REQUIRE(zoneTrainEnteredEventCount == 0);
|
||||
REQUIRE(zoneTrainLeavingEventCount == 0);
|
||||
REQUIRE(zoneTrainLeftEventCount == 0);
|
||||
REQUIRE(zoneTrainRemovedEventCount == 0);
|
||||
|
||||
blockWeak.lock()->assignTrain(trainWeak.lock());
|
||||
|
||||
REQUIRE(trainZoneAssignedEventCount == 1);
|
||||
REQUIRE(trainZoneEnteringEventCount == 0);
|
||||
REQUIRE(trainZoneEnteredEventCount == 0);
|
||||
REQUIRE(trainZoneLeavingEventCount == 0);
|
||||
REQUIRE(trainZoneLeftEventCount == 0);
|
||||
REQUIRE(trainZoneRemovedEventCount == 0);
|
||||
REQUIRE(zoneTrainAssignedEventCount == 1);
|
||||
REQUIRE(zoneTrainEnteringEventCount == 0);
|
||||
REQUIRE(zoneTrainEnteredEventCount == 0);
|
||||
REQUIRE(zoneTrainLeavingEventCount == 0);
|
||||
REQUIRE(zoneTrainLeftEventCount == 0);
|
||||
REQUIRE(zoneTrainRemovedEventCount == 0);
|
||||
|
||||
blockWeak.lock()->removeTrain(trainWeak.lock());
|
||||
|
||||
REQUIRE(trainZoneAssignedEventCount == 1);
|
||||
REQUIRE(trainZoneEnteringEventCount == 0);
|
||||
REQUIRE(trainZoneEnteredEventCount == 0);
|
||||
REQUIRE(trainZoneLeavingEventCount == 0);
|
||||
REQUIRE(trainZoneLeftEventCount == 0);
|
||||
REQUIRE(trainZoneRemovedEventCount == 1);
|
||||
REQUIRE(zoneTrainAssignedEventCount == 1);
|
||||
REQUIRE(zoneTrainEnteringEventCount == 0);
|
||||
REQUIRE(zoneTrainEnteredEventCount == 0);
|
||||
REQUIRE(zoneTrainLeavingEventCount == 0);
|
||||
REQUIRE(zoneTrainLeftEventCount == 0);
|
||||
REQUIRE(zoneTrainRemovedEventCount == 1);
|
||||
|
||||
world.reset();
|
||||
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(trainWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(trainZoneAssignedEventCount == 1);
|
||||
REQUIRE(trainZoneEnteringEventCount == 0);
|
||||
REQUIRE(trainZoneEnteredEventCount == 0);
|
||||
REQUIRE(trainZoneLeavingEventCount == 0);
|
||||
REQUIRE(trainZoneLeftEventCount == 0);
|
||||
REQUIRE(trainZoneRemovedEventCount == 1);
|
||||
REQUIRE(zoneTrainAssignedEventCount == 1);
|
||||
REQUIRE(zoneTrainEnteringEventCount == 0);
|
||||
REQUIRE(zoneTrainEnteredEventCount == 0);
|
||||
REQUIRE(zoneTrainLeavingEventCount == 0);
|
||||
REQUIRE(zoneTrainLeftEventCount == 0);
|
||||
REQUIRE(zoneTrainRemovedEventCount == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: Toggle mute/noSmoke/speedLimit with train in zone", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
REQUIRE(world->railVehicles->length == 0);
|
||||
std::weak_ptr<RailVehicle> locomotiveWeak = world->railVehicles->create(Locomotive::classId);
|
||||
REQUIRE_FALSE(locomotiveWeak.expired());
|
||||
REQUIRE(world->railVehicles->length == 1);
|
||||
|
||||
REQUIRE(world->trains->length == 0);
|
||||
std::weak_ptr<Train> trainWeak = world->trains->create();
|
||||
REQUIRE_FALSE(trainWeak.expired());
|
||||
REQUIRE(world->trains->length == 1);
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 0);
|
||||
trainWeak.lock()->vehicles->add(locomotiveWeak.lock());
|
||||
REQUIRE(trainWeak.lock()->vehicles->length == 1);
|
||||
|
||||
REQUIRE(world->boards->length == 0);
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(world->boards->length == 1);
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 0);
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length == 1);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 0);
|
||||
|
||||
REQUIRE_FALSE(trainWeak.lock()->active);
|
||||
blockWeak.lock()->assignTrain(trainWeak.lock());
|
||||
REQUIRE(trainWeak.lock()->active);
|
||||
REQUIRE(trainWeak.lock()->blocks.size() == 1);
|
||||
REQUIRE(trainWeak.lock()->zones.size() == 1);
|
||||
REQUIRE(blockWeak.lock()->trains.size() == 1);
|
||||
REQUIRE(zoneWeak.lock()->trains.size() == 1);
|
||||
|
||||
REQUIRE_FALSE(trainWeak.lock()->mute);
|
||||
REQUIRE_FALSE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->mute);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->noSmoke);
|
||||
|
||||
zoneWeak.lock()->mute = true;
|
||||
REQUIRE(trainWeak.lock()->mute);
|
||||
REQUIRE(locomotiveWeak.lock()->mute);
|
||||
|
||||
zoneWeak.lock()->mute = false;
|
||||
REQUIRE_FALSE(trainWeak.lock()->mute);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->mute);
|
||||
|
||||
zoneWeak.lock()->noSmoke = true;
|
||||
REQUIRE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE(locomotiveWeak.lock()->noSmoke);
|
||||
|
||||
zoneWeak.lock()->noSmoke = false;
|
||||
REQUIRE_FALSE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->noSmoke);
|
||||
|
||||
zoneWeak.lock()->speedLimit.setValue(100.0);
|
||||
REQUIRE(trainWeak.lock()->speedLimit.value() == Catch::Approx(100.0));
|
||||
|
||||
zoneWeak.lock()->speedLimit.setValue(std::numeric_limits<double>::infinity());
|
||||
REQUIRE(std::isinf(trainWeak.lock()->speedLimit.value()));
|
||||
|
||||
zoneWeak.lock()->mute = true;
|
||||
zoneWeak.lock()->noSmoke = true;
|
||||
zoneWeak.lock()->speedLimit.setValue(100.0);
|
||||
|
||||
blockWeak.lock()->removeTrain(trainWeak.lock());
|
||||
|
||||
REQUIRE_FALSE(trainWeak.lock()->mute);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->mute);
|
||||
REQUIRE_FALSE(trainWeak.lock()->noSmoke);
|
||||
REQUIRE_FALSE(locomotiveWeak.lock()->noSmoke);
|
||||
REQUIRE(std::isinf(trainWeak.lock()->speedLimit.value()));
|
||||
|
||||
world.reset();
|
||||
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(locomotiveWeak.expired());
|
||||
REQUIRE(trainWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: Check class id's", "[zone]")
|
||||
{
|
||||
// class id's may NOT be changed, it will break saved worlds and might break client stuff.
|
||||
|
||||
REQUIRE(BlockZoneList::classId == "list.block_zone");
|
||||
REQUIRE(Zone::classId == "zone");
|
||||
REQUIRE(ZoneBlockList::classId == "list.zone_block");
|
||||
REQUIRE(ZoneList::classId == "list.zone");
|
||||
REQUIRE(ZoneListTableModel::classId == "zone_list_table_model");
|
||||
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
REQUIRE(world->zones->getClassId() == "list.zone");
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
REQUIRE(blockWeak.lock()->zones->getClassId() == "list.block_zone");
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->getClassId() == "zone");
|
||||
REQUIRE(zoneWeak.lock()->blocks->getClassId() == "list.zone_block");
|
||||
|
||||
world.reset();
|
||||
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: Check enabled attribute", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
|
||||
REQUIRE(world->state.value() == static_cast<WorldState>(0));
|
||||
REQUIRE_FALSE(world->edit);
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->id));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->name));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->mute));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->noSmoke));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->speedLimit));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->add));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->remove));
|
||||
|
||||
world->edit = true;
|
||||
|
||||
REQUIRE(world->state.value() == WorldState::Edit);
|
||||
REQUIRE(world->edit);
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->id));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->name));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->mute));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->noSmoke));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->speedLimit));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->blocks->add));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->blocks->remove));
|
||||
|
||||
world->run();
|
||||
|
||||
REQUIRE(world->state.value() == (WorldState::Edit | WorldState::PowerOn | WorldState::Run));
|
||||
REQUIRE(world->edit);
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->id));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->name));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->mute));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->noSmoke));
|
||||
REQUIRE(Attributes::getEnabled(zoneWeak.lock()->speedLimit));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->add));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->remove));
|
||||
|
||||
world->edit = false;
|
||||
|
||||
REQUIRE(world->state.value() == (WorldState::PowerOn | WorldState::Run));
|
||||
REQUIRE_FALSE(world->edit);
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->id));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->name));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->mute));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->noSmoke));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->speedLimit));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->add));
|
||||
REQUIRE_FALSE(Attributes::getEnabled(zoneWeak.lock()->blocks->remove));
|
||||
|
||||
world.reset();
|
||||
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: zone list table model", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
auto zoneListModel = world->zones->getModel();
|
||||
REQUIRE(zoneListModel);
|
||||
REQUIRE(zoneListModel->getClassId() == "zone_list_table_model");
|
||||
REQUIRE(zoneListModel->rowCount() == 0);
|
||||
|
||||
std::weak_ptr<Zone> zone1 = world->zones->create();
|
||||
REQUIRE_FALSE(zone1.expired());
|
||||
|
||||
REQUIRE(zoneListModel->rowCount() == 1);
|
||||
REQUIRE(zoneListModel->getText(0, 0) == zone1.lock()->id.value());
|
||||
REQUIRE(zoneListModel->getText(1, 0) == zone1.lock()->name.value());
|
||||
|
||||
zone1.lock()->id = "zone_one";
|
||||
REQUIRE(zoneListModel->getText(0, 0) == "zone_one");
|
||||
zone1.lock()->name = "Zone One";
|
||||
REQUIRE(zoneListModel->getText(1, 0) == "Zone One");
|
||||
|
||||
std::weak_ptr<Zone> zone2 = world->zones->create();
|
||||
REQUIRE_FALSE(zone2.expired());
|
||||
|
||||
REQUIRE(zoneListModel->rowCount() == 2);
|
||||
REQUIRE(zoneListModel->getText(0, 1) == zone2.lock()->id.value());
|
||||
REQUIRE(zoneListModel->getText(1, 1) == zone2.lock()->name.value());
|
||||
|
||||
zone2.lock()->id = "zone_two";
|
||||
REQUIRE(zoneListModel->getText(0, 1) == "zone_two");
|
||||
zone2.lock()->name = "Zone Two";
|
||||
REQUIRE(zoneListModel->getText(1, 1) == "Zone Two");
|
||||
|
||||
world->zones->delete_(zone1.lock());
|
||||
REQUIRE(zone1.expired());
|
||||
|
||||
REQUIRE(zoneListModel->rowCount() == 1);
|
||||
REQUIRE(zoneListModel->getText(0, 0) == "zone_two");
|
||||
REQUIRE(zoneListModel->getText(1, 0) == "Zone Two");
|
||||
REQUIRE(zoneListModel->getText(0, 1) == "");
|
||||
REQUIRE(zoneListModel->getText(1, 1) == "");
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(zone2.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: block zone list table model", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zone1 = world->zones->create();
|
||||
REQUIRE_FALSE(zone1.expired());
|
||||
|
||||
std::weak_ptr<Zone> zone2 = world->zones->create();
|
||||
REQUIRE_FALSE(zone2.expired());
|
||||
|
||||
auto blockZoneList = blockWeak.lock()->zones->getModel();
|
||||
REQUIRE(blockZoneList->rowCount() == 0);
|
||||
|
||||
blockWeak.lock()->zones->add(zone1.lock());
|
||||
REQUIRE(blockZoneList->rowCount() == 1);
|
||||
REQUIRE(blockZoneList->getText(0, 0) == zone1.lock()->id.value());
|
||||
REQUIRE(blockZoneList->getText(1, 0) == zone1.lock()->name.value());
|
||||
|
||||
zone1.lock()->id = "zone_one";
|
||||
zone1.lock()->name = "Zone One";
|
||||
REQUIRE(blockZoneList->getText(0, 0) == "zone_one");
|
||||
REQUIRE(blockZoneList->getText(1, 0) == "Zone One");
|
||||
|
||||
zone2.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(blockZoneList->rowCount() == 2);
|
||||
REQUIRE(blockZoneList->getText(0, 1) == zone2.lock()->id.value());
|
||||
REQUIRE(blockZoneList->getText(1, 1) == zone2.lock()->name.value());
|
||||
|
||||
zone2.lock()->id = "zone_two";
|
||||
zone2.lock()->name = "Zone Two";
|
||||
REQUIRE(blockZoneList->getText(0, 1) == "zone_two");
|
||||
REQUIRE(blockZoneList->getText(1, 1) == "Zone Two");
|
||||
|
||||
blockWeak.lock()->zones->remove(zone1.lock());
|
||||
REQUIRE(blockZoneList->rowCount() == 1);
|
||||
REQUIRE(blockZoneList->getText(0, 0) == "zone_two");
|
||||
REQUIRE(blockZoneList->getText(1, 0) == "Zone Two");
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zone1.expired());
|
||||
REQUIRE(zone2.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: zone block list table model", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
|
||||
std::weak_ptr<BlockRailTile> block1 = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(block1.expired());
|
||||
REQUIRE(boardWeak.lock()->addTile(1, 1, TileRotate::Deg0, BlockRailTile::classId, false));
|
||||
|
||||
std::weak_ptr<BlockRailTile> block2 = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({1, 1}));
|
||||
REQUIRE_FALSE(block2.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
|
||||
auto zoneBlockList = zoneWeak.lock()->blocks->getModel();
|
||||
REQUIRE(zoneBlockList->rowCount() == 0);
|
||||
|
||||
zoneWeak.lock()->blocks->add(block1.lock());
|
||||
REQUIRE(zoneBlockList->rowCount() == 1);
|
||||
REQUIRE(zoneBlockList->getText(0, 0) == block1.lock()->id.value());
|
||||
REQUIRE(zoneBlockList->getText(1, 0) == block1.lock()->name.value());
|
||||
|
||||
block1.lock()->id = "block_one";
|
||||
block1.lock()->name = "Block One";
|
||||
REQUIRE(zoneBlockList->getText(0, 0) == "block_one");
|
||||
REQUIRE(zoneBlockList->getText(1, 0) == "Block One");
|
||||
|
||||
zoneWeak.lock()->blocks->add(block2.lock());
|
||||
REQUIRE(zoneBlockList->rowCount() == 2);
|
||||
REQUIRE(zoneBlockList->getText(0, 1) == block2.lock()->id.value());
|
||||
REQUIRE(zoneBlockList->getText(1, 1) == block2.lock()->name.value());
|
||||
|
||||
block2.lock()->id = "block_two";
|
||||
block2.lock()->name = "Block Two";
|
||||
REQUIRE(zoneBlockList->getText(0, 1) == "block_two");
|
||||
REQUIRE(zoneBlockList->getText(1, 1) == "Block Two");
|
||||
|
||||
zoneWeak.lock()->blocks->remove(block1.lock());
|
||||
REQUIRE(zoneBlockList->rowCount() == 1);
|
||||
REQUIRE(zoneBlockList->getText(0, 0) == "block_two");
|
||||
REQUIRE(zoneBlockList->getText(1, 0) == "Block Two");
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(block1.expired());
|
||||
REQUIRE(block2.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: delete zone with block assigned", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length.value() == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->length.value() == 1);
|
||||
|
||||
world->zones->delete_(zoneWeak.lock());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
REQUIRE(world->zones->length.value() == 0);
|
||||
REQUIRE(blockWeak.lock()->zones->length.value() == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: delete block with zone assigned", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length.value() == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->length.value() == 1);
|
||||
|
||||
boardWeak.lock()->deleteTile(0, 0);
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length.value() == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
|
||||
TEST_CASE("Zone: delete board with block with zone assigned", "[zone]")
|
||||
{
|
||||
auto world = World::create();
|
||||
std::weak_ptr<World> worldWeak = world;
|
||||
REQUIRE_FALSE(worldWeak.expired());
|
||||
|
||||
std::weak_ptr<Board> boardWeak = world->boards->create();
|
||||
REQUIRE_FALSE(boardWeak.expired());
|
||||
|
||||
REQUIRE(boardWeak.lock()->addTile(0, 0, TileRotate::Deg90, BlockRailTile::classId, false));
|
||||
std::weak_ptr<BlockRailTile> blockWeak = std::dynamic_pointer_cast<BlockRailTile>(boardWeak.lock()->getTile({0, 0}));
|
||||
REQUIRE_FALSE(blockWeak.expired());
|
||||
|
||||
std::weak_ptr<Zone> zoneWeak = world->zones->create();
|
||||
REQUIRE_FALSE(zoneWeak.expired());
|
||||
|
||||
zoneWeak.lock()->blocks->add(blockWeak.lock());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length.value() == 1);
|
||||
REQUIRE(blockWeak.lock()->zones->length.value() == 1);
|
||||
|
||||
world->boards->delete_(boardWeak.lock());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(blockWeak.expired());
|
||||
REQUIRE(zoneWeak.lock()->blocks->length.value() == 0);
|
||||
|
||||
world.reset();
|
||||
REQUIRE(worldWeak.expired());
|
||||
REQUIRE(boardWeak.expired());
|
||||
REQUIRE(zoneWeak.expired());
|
||||
}
|
||||
54
shared/src/traintastic/enum/zonetrainstate.hpp
Normale Datei
54
shared/src/traintastic/enum/zonetrainstate.hpp
Normale Datei
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* shared/src/traintastic/enum/zonetrainstate.hpp
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2024 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_ZONETRAINSTATE_HPP
|
||||
#define TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_ZONETRAINSTATE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include "enum.hpp"
|
||||
|
||||
enum class ZoneTrainState : uint8_t
|
||||
{
|
||||
Unknown = 0,
|
||||
Entering = 1,
|
||||
Entered = 2,
|
||||
Leaving = 3,
|
||||
};
|
||||
|
||||
TRAINTASTIC_ENUM(ZoneTrainState, "zone_train_state", 4,
|
||||
{
|
||||
{ZoneTrainState::Unknown, "unknown"},
|
||||
{ZoneTrainState::Entering, "entering"},
|
||||
{ZoneTrainState::Entered, "entered"},
|
||||
{ZoneTrainState::Leaving, "leaving"}
|
||||
});
|
||||
|
||||
constexpr std::array<ZoneTrainState, 4> zoneTrainStateValues
|
||||
{
|
||||
ZoneTrainState::Unknown,
|
||||
ZoneTrainState::Entering,
|
||||
ZoneTrainState::Entered,
|
||||
ZoneTrainState::Leaving,
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -6542,6 +6542,30 @@
|
||||
"comment": "",
|
||||
"fuzzy": 0
|
||||
},
|
||||
{
|
||||
"term": "world:zones",
|
||||
"definition": "Zones"
|
||||
},
|
||||
{
|
||||
"term": "zone:blocks",
|
||||
"definition": "Blocks"
|
||||
},
|
||||
{
|
||||
"term": "zone:mute",
|
||||
"definition": "Mute"
|
||||
},
|
||||
{
|
||||
"term": "zone:no_smoke",
|
||||
"definition": "No smoke"
|
||||
},
|
||||
{
|
||||
"term": "list.zone:highlight_zone",
|
||||
"definition": "Highlight zone"
|
||||
},
|
||||
{
|
||||
"term": "zone:speed_limit",
|
||||
"definition": "Speed limit"
|
||||
},
|
||||
{
|
||||
"term": "message:I9003",
|
||||
"definition": "Cleared persistent variables",
|
||||
@ -6785,5 +6809,9 @@
|
||||
{
|
||||
"term": "throttle.controlled_by_x",
|
||||
"definition": "Controlled by %1"
|
||||
},
|
||||
{
|
||||
"term": "board_tile.rail.block:zones",
|
||||
"definition": "Zones"
|
||||
}
|
||||
]
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren