diff options
Diffstat (limited to 'openttd-svn')
-rwxr-xr-x | openttd-svn/PKGBUILD | 90 | ||||
-rw-r--r-- | openttd-svn/clipboard.grf | bin | 0 -> 5405 bytes | |||
-rw-r--r-- | openttd-svn/everything.patch | 19875 |
3 files changed, 19965 insertions, 0 deletions
diff --git a/openttd-svn/PKGBUILD b/openttd-svn/PKGBUILD new file mode 100755 index 00000000..bc1fb07d --- /dev/null +++ b/openttd-svn/PKGBUILD @@ -0,0 +1,90 @@ +# Maintainer: Erich Eckner <arch at eckner dot net> +pkgname=openttd-svn +pkgver=27534 +pkgrel=1 +pkgdesc="A FOSS clone of Transport Tycoon Deluxe." +arch=('x86_64') +url="http://www.openttd.org" +license=('') +groups=() +depends=( + 'sdl' + 'libpng' + 'fontconfig' + 'lzo' +) +makedepends=() +checkdepends=() +optdepends=() +provides=() +conflicts=() +replaces=() +backup=() +options=() +gfxversion=0.5.2 +sfxversion=0.2.3 +msxversion=0.3.1 +source=( + "http://binaries.openttd.org/extra/opengfx/$gfxversion/opengfx-$gfxversion-all.zip" + "http://binaries.openttd.org/extra/opensfx/$sfxversion/opensfx-$sfxversion-all.zip" + "http://binaries.openttd.org/extra/openmsx/$msxversion/openmsx-$msxversion-all.zip" + "everything.patch" + "clipboard.grf" +) +sha256sums=('19be61f1cb04cbb3cb9602f0b8eb6e6f56ecbefbfdd6e0e03f9579e5a5c1cbc8' + '6831b651b3dc8b494026f7277989a1d757961b67c17b75d3c2e097451f75af02' + '92e293ae89f13ad679f43185e83fb81fb8cad47fe63f4af3d3d9f955130460f5' + '87c17a36bbc0f401e3aad88402e68cbc5ff743c728e403e5492879c931f08757' + '12b90fe53f2d61d2d45d74ecc6b97d3a5f041c4215a5c1b02a6b1ba162e4572a') + +package() { + + tar -xf opengfx-$gfxversion.tar + + git clone "https://git.openttd.org/trunk.git" $pkgname + + cd $pkgname + git checkout 76fc2426669d3a52d392ab6838ebf30326294589 + + patch -p1 < $srcdir/everything.patch + + ./configure --prefix-dir=/usr \ + --binary-dir=bin \ + --data-dir=share/openttd \ + --icon-dir=share/openttd \ + --man-dir=share/man \ + --personal-dir=.openttd \ + --install-dir=$pkgdir \ + + make + make DESTDIR=$pkgdir install + + # Install OpenGFX + install -d $pkgdir/usr/share/openttd/data/opengfx-$gfxversion + install -m 644 $srcdir/opengfx-$gfxversion/* $pkgdir/usr/share/openttd/data/opengfx-$gfxversion + chown -R root:root $pkgdir/usr/share/openttd/data/opengfx-$gfxversion + + # Install OpenSFX + install -d $pkgdir/usr/share/openttd/data/opensfx-$sfxversion + install -m 644 $srcdir/opensfx-$sfxversion/* $pkgdir/usr/share/openttd/data/opensfx-$sfxversion + chown -R root:root $pkgdir/usr/share/openttd/data/opensfx-$sfxversion + + # Install OpenMSX + install -d $pkgdir/usr/share/openttd/gm + install -m 644 $srcdir/openmsx-$msxversion/* $pkgdir/usr/share/openttd/gm + chown -R root:root $pkgdir/usr/share/openttd/gm/* + install -m644 $srcdir/clipboard.grf $pkgdir/usr/share/openttd/baseset/ + + # Remove unnecessary languages + cp $pkgdir/usr/share/openttd/lang/{english,german}.lng $srcdir + rm $pkgdir/usr/share/openttd/lang/* + install -m 644 $srcdir/{english,german}.lng $pkgdir/usr/share/openttd/lang + + # Remove junk + rm -rf $pkgdir/usr/share/doc + rm -rf $pkgdir/usr/share/openttd/scripts + rm $pkgdir/usr/share/openttd/data/opengfx-$gfxversion/{changelog,readme}.txt + rm $pkgdir/usr/share/openttd/data/opensfx-$sfxversion/{changelog,readme}.txt + rm $pkgdir/usr/share/openttd/gm/{changelog,readme}.txt + +} diff --git a/openttd-svn/clipboard.grf b/openttd-svn/clipboard.grf Binary files differnew file mode 100644 index 00000000..60ebb06a --- /dev/null +++ b/openttd-svn/clipboard.grf diff --git a/openttd-svn/everything.patch b/openttd-svn/everything.patch new file mode 100644 index 00000000..dda6939c --- /dev/null +++ b/openttd-svn/everything.patch @@ -0,0 +1,19875 @@ +diff --git a/.gitignore b/.gitignore +index cb1e9d1..e6d4893 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -6,6 +6,7 @@ bin/ai/* + !bin/data + bin/baseset/* + !bin/baseset/openttd.grf ++!bin/baseset/clipboard.grf + !bin/baseset/opntitle.dat + !bin/baseset/orig_*.obg + !bin/baseset/orig_*.obs +diff --git a/config.lib b/config.lib +index a8c6e12..5e96666 100644 +--- a/config.lib ++++ b/config.lib +@@ -1740,7 +1740,8 @@ make_cflags_and_ldflags() { + CFLAGS="$CFLAGS `$freetype_config --cflags | tr '\n\r' ' '`" + + if [ "$enable_static" != "0" ]; then +- LIBS="$LIBS `$freetype_config --libs --static | tr '\n\r' ' '`" ++ # Is it possible to do static with freetype, if so: how? ++ LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" + else + LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" + fi +diff --git a/docs/tile_index_transformations.svg b/docs/tile_index_transformations.svg +new file mode 100644 +index 0000000..47fffb2 +--- /dev/null ++++ b/docs/tile_index_transformations.svg +@@ -0,0 +1,1849 @@ ++<?xml version="1.0" encoding="UTF-8" standalone="no"?> ++<!-- Created with Inkscape (http://www.inkscape.org/) --> ++ ++<svg ++ xmlns:dc="http://purl.org/dc/elements/1.1/" ++ xmlns:cc="http://creativecommons.org/ns#" ++ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ++ xmlns:svg="http://www.w3.org/2000/svg" ++ xmlns="http://www.w3.org/2000/svg" ++ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" ++ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" ++ width="752.40002" ++ height="359.76001" ++ id="svg2" ++ version="1.1" ++ inkscape:version="0.48.3.1 r9886" ++ sodipodi:docname="tile_index_transformations.svg" ++ style="enable-background:new"> ++ <defs ++ id="defs4"> ++ <marker ++ inkscape:stockid="Arrow2Sstart" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Sstart" ++ style="overflow:visible"> ++ <path ++ id="path4778" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="matrix(0.3,0,0,0.3,-0.69,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow1Mstart" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow1Mstart" ++ style="overflow:visible"> ++ <path ++ id="path4754" ++ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" ++ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" ++ transform="matrix(0.4,0,0,0.4,4,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Mstart" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Mstart" ++ style="overflow:visible"> ++ <path ++ id="path4772" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="scale(0.6,0.6)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Mend" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Mend" ++ style="overflow:visible"> ++ <path ++ id="path4247" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="scale(-0.6,-0.6)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Send" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Send" ++ style="overflow:visible"> ++ <path ++ id="path4253" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="matrix(-0.3,0,0,-0.3,0.69,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Lend" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Lend" ++ style="overflow:visible"> ++ <path ++ id="path4241" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow1Send" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow1Send" ++ style="overflow:visible"> ++ <path ++ id="path4235" ++ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" ++ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" ++ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow1Lend" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow1Lend" ++ style="overflow:visible"> ++ <path ++ id="path4223" ++ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" ++ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" ++ transform="matrix(-0.8,0,0,-0.8,-10,0)" ++ inkscape:connector-curvature="0" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow1Lend" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow1Lend-0" ++ style="overflow:visible"> ++ <path ++ inkscape:connector-curvature="0" ++ id="path4223-2" ++ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" ++ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" ++ transform="matrix(-0.8,0,0,-0.8,-10,0)" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Mend" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Mend-2" ++ style="overflow:visible"> ++ <path ++ inkscape:connector-curvature="0" ++ id="path4247-6" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="scale(-0.6,-0.6)" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Sstart" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="Arrow2Sstart-8" ++ style="overflow:visible"> ++ <path ++ inkscape:connector-curvature="0" ++ id="path4778-8" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="matrix(0.3,0,0,0.3,-0.69,0)" /> ++ </marker> ++ <marker ++ inkscape:stockid="Arrow2Sstart" ++ orient="auto" ++ refY="0" ++ refX="0" ++ id="marker6497" ++ style="overflow:visible"> ++ <path ++ inkscape:connector-curvature="0" ++ id="path6499" ++ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round" ++ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" ++ transform="matrix(0.3,0,0,0.3,-0.69,0)" /> ++ </marker> ++ </defs> ++ <sodipodi:namedview ++ id="base" ++ pagecolor="#ffffff" ++ bordercolor="#666666" ++ borderopacity="1.0" ++ inkscape:pageopacity="0.0" ++ inkscape:pageshadow="2" ++ inkscape:zoom="0.99999999" ++ inkscape:cx="301.96543" ++ inkscape:cy="13.791855" ++ inkscape:document-units="px" ++ inkscape:current-layer="layer5" ++ showgrid="false" ++ inkscape:object-paths="false" ++ inkscape:snap-intersection-paths="false" ++ inkscape:object-nodes="false" ++ inkscape:snap-smooth-nodes="false" ++ inkscape:snap-midpoints="false" ++ inkscape:window-width="1280" ++ inkscape:window-height="775" ++ inkscape:window-x="0" ++ inkscape:window-y="24" ++ inkscape:window-maximized="1" ++ inkscape:snap-nodes="false" ++ inkscape:snap-center="false" ++ inkscape:snap-object-midpoints="false" ++ inkscape:snap-global="true" ++ inkscape:snap-grids="true" ++ inkscape:snap-to-guides="false" ++ inkscape:snap-bbox="true" ++ inkscape:bbox-paths="false" ++ inkscape:bbox-nodes="false" ++ inkscape:snap-bbox-edge-midpoints="false" ++ inkscape:snap-bbox-midpoints="false" ++ inkscape:snap-page="true"> ++ <inkscape:grid ++ type="xygrid" ++ id="grid3198" /> ++ </sodipodi:namedview> ++ <metadata ++ id="metadata7"> ++ <rdf:RDF> ++ <cc:Work ++ rdf:about=""> ++ <dc:format>image/svg+xml</dc:format> ++ <dc:type ++ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> ++ <dc:title /> ++ </cc:Work> ++ </rdf:RDF> ++ </metadata> ++ <g ++ inkscape:groupmode="layer" ++ id="layer7" ++ inkscape:label="background" ++ style="display:inline" ++ transform="translate(0,-692.60215)" ++ sodipodi:insensitive="true"> ++ <path ++ style="fill:#ffffff;stroke:none" ++ d="m 0,692.60215 0,359.76005 752.40002,0 0,-359.76005 z" ++ id="rect3861-5" ++ inkscape:connector-curvature="0" ++ sodipodi:nodetypes="ccccc" /> ++ </g> ++ <g ++ inkscape:groupmode="layer" ++ id="layer4" ++ inkscape:label="tile markers" ++ style="display:inline" ++ transform="translate(0,-692.60215)" ++ sodipodi:insensitive="true"> ++ <rect ++ style="fill:#ececec;stroke:none" ++ id="rect6565" ++ width="151.78932" ++ height="94.868332" ++ x="618.09161" ++ y="1213.2018" ++ transform="matrix(-0.89442719,0.4472136,0.89442719,0.4472136,0,0)" /> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="788.85455" ++ x="1023.465" ++ height="94.868332" ++ width="151.78932" ++ id="rect6563" ++ style="fill:#ececec;stroke:none" /> ++ <rect ++ style="fill:#ff2a2a;stroke:none" ++ id="rect3939" ++ width="18.973665" ++ height="18.973665" ++ x="1023.465" ++ y="788.85455" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <rect ++ style="fill:#7fff2a;stroke:none" ++ id="rect3941" ++ width="18.973665" ++ height="18.973665" ++ x="1099.3596" ++ y="807.82825" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <rect ++ y="-1308.0702" ++ x="618.09149" ++ height="18.973665" ++ width="18.973665" ++ id="rect4184" ++ style="fill:#ff2a2a;stroke:none" ++ transform="matrix(-0.89442719,0.4472136,-0.89442719,-0.4472136,0,0)" /> ++ <rect ++ y="-1289.0963" ++ x="693.98615" ++ height="18.973665" ++ width="18.973665" ++ id="rect4186" ++ style="fill:#7fff2a;stroke:none" ++ transform="matrix(-0.89442719,0.4472136,-0.89442719,-0.4472136,0,0)" /> ++ <rect ++ style="fill:#5599ff;fill-opacity:1;stroke:none" ++ id="rect4209" ++ width="18.973665" ++ height="18.973665" ++ x="1213.2018" ++ y="618.09155" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ </g> ++ <g ++ inkscape:groupmode="layer" ++ id="layer2" ++ inkscape:label="grid" ++ style="display:inline" ++ transform="translate(0,-692.60215)" ++ sodipodi:insensitive="true"> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 21.478511,717.99944 30.4056,-15.2028" ++ id="path6826" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.498719,734.95989 85.804991,702.80675" ++ id="path6824" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 21.473939,751.94285 98.252001,-49.126" ++ id="path6822" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.476615,768.91207 153.66589,702.81744" ++ id="path6820" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.484403,785.87875 187.64275,702.79958" ++ id="path6818" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.479351,802.85183 221.57491,702.80406" ++ id="path6816" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.479831,819.82215 255.48455,702.8198" ++ id="path6814" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.486815,836.78922 289.42022,702.82253" ++ id="path6812" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.490283,853.75806 323.40134,702.80253" ++ id="path6810" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.488111,870.72971 357.33068,702.80843" ++ id="path6808" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.485735,887.70146 391.24315,702.82275" ++ id="path6806" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.475955,904.6769 425.23908,702.79536" ++ id="path6804" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 21.496175,921.63735 459.15995,702.80547" ++ id="path6802" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 23.871443,937.42028 493.08089,702.81558" ++ id="path6800" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 57.860395,937.39638 527.00902,702.82206" ++ id="path6796" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 91.75825,937.418 560.9977,702.79829" ++ id="path6792" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 125.73238,937.4015 594.92198,702.8067" ++ id="path6788" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 159.70164,937.38744 628.83947,702.81853" ++ id="path6784" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 193.61767,937.39998 662.76335,702.82716" ++ id="path6780" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 227.58728,937.38572 696.7563,702.80123" ++ id="path6776" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 261.45812,937.42087 730.67773,702.81108" ++ id="path6772" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 295.46368,937.38867 730.9224,719.65931" ++ id="path6768" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 329.36915,937.40652 730.89763,736.64226" ++ id="path6764" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 363.30457,937.40933 730.89054,753.61638" ++ id="path6760" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 397.26559,937.39938 730.90807,770.57816" ++ id="path6756" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 431.21478,937.39542 730.88947,787.55803" ++ id="path6752" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 465.14321,937.40171 730.90356,804.52155" ++ id="path6748" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 499.04942,937.41919 730.8993,821.49425" ++ id="path6744" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 533.0223,937.40331 730.91399,838.45746" ++ id="path6740" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 566.95674,937.40664 730.89965,855.43518" ++ id="path6736" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 600.93184,937.38968 129.9776,-64.98881" ++ id="path6732" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 634.83216,937.41006 96.0675,-48.03375" ++ id="path6728" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 668.81856,937.3874 62.10125,-31.05061" ++ id="path6724" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 702.69631,937.41911 730.8951,923.31972" ++ id="path6720" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 56.355552,937.39751 21.479855,919.95966" ++ id="path6696" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 90.335997,937.41719 21.491231,902.99479" ++ id="path6692" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 124.23228,937.39475 21.475271,886.01624" ++ id="path6688" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 158.21008,937.41307 21.488099,869.05209" ++ id="path6684" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 192.11394,937.39447 21.482375,852.07867" ++ id="path6680" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 226.05024,937.39205 21.460943,835.09739" ++ id="path6676" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 260.02396,937.40835 21.481163,818.13693" ++ id="path6672" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 293.95187,937.40171 21.471383,801.16148" ++ id="path6668" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 327.88655,937.39854 21.485903,784.19819" ++ id="path6664" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 361.80511,937.3872 21.466823,767.21808" ++ id="path6660" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 395.80138,937.41476 21.484655,750.25644" ++ id="path6656" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 429.7105,937.3988 21.477287,733.28219" ++ id="path6652" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 463.64177,937.39382 21.496559,716.32125" ++ id="path6648" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 497.62729,937.41608 28.417835,702.81134" ++ id="path6644" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 531.55715,937.41039 62.337528,702.80061" ++ id="path6640" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 565.46932,937.39597 96.333707,702.82814" ++ id="path6636" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="m 599.44158,937.41151 -469.186,-234.593" ++ id="path6632" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 633.38754,937.41392 164.17308,702.80671" ++ id="path6628" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 667.30681,937.403 198.09737,702.79829" ++ id="path6624" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 701.26308,937.41057 232.08605,702.82206" ++ id="path6620" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91854,935.26775 266.01419,702.81558" ++ id="path6616" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.8993,918.28755 299.92884,702.80234" ++ id="path6614" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91399,901.32434 333.85601,702.79537" ++ id="path6612" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91114,884.35237 367.8442,702.8189" ++ id="path6610" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.92445,867.38844 401.77282,702.81264" ++ id="path6608" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.89965,850.40549 435.6937,702.80251" ++ id="path6606" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.92439,833.44731 469.65468,702.81246" ++ id="path6604" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91011,816.46958 503.61054,702.8198" ++ id="path6602" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.89586,799.49191 537.52014,702.80406" ++ id="path6600" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.90549,782.52618 571.45229,702.79957" ++ id="path6598" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.89911,765.55221 605.42957,702.81744" ++ id="path6596" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91633,748.59027 639.36948,702.81685" ++ id="path6594" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.90495,731.61404 673.27622,702.79967" ++ id="path6592" ++ inkscape:connector-curvature="0" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:0.36840001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" ++ d="M 730.91178,714.64687 707.21132,702.79664" ++ id="path6590" ++ inkscape:connector-curvature="0" /> ++ </g> ++ <g ++ inkscape:groupmode="layer" ++ id="layer3" ++ inkscape:label="tile areas" ++ style="display:inline" ++ transform="translate(0,-692.60215)"> ++ <rect ++ style="fill:none;stroke:#000000;stroke-width:3.79473329;stroke-miterlimit:4;stroke-dasharray:none" ++ id="rect3169" ++ width="151.78932" ++ height="94.868355" ++ x="1023.465" ++ y="788.85455" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <rect ++ y="-1308.0699" ++ x="618.09161" ++ height="94.868355" ++ width="151.78932" ++ id="rect4188" ++ style="fill:none;stroke:#000000;stroke-width:3.79473329;stroke-miterlimit:4;stroke-dasharray:none" ++ transform="matrix(-0.89442719,0.4472136,-0.89442719,-0.4472136,0,0)" /> ++ <rect ++ transform="matrix(0.89717184,0.44168167,-0.89717184,0.44168167,0,0)" ++ y="1248.6029" ++ x="1282.3198" ++ height="7.8371782" ++ width="12.53948" ++ id="rect3247" ++ style="fill:none;stroke:#000000;stroke-width:1.12329161;stroke-miterlimit:4;stroke-dasharray:none" /> ++ <rect ++ style="fill:none;stroke:#000000;stroke-width:1.12329161;stroke-miterlimit:4;stroke-dasharray:none" ++ id="rect4093" ++ width="12.53948" ++ height="7.8371782" ++ x="1045.6216" ++ y="1485.3011" ++ transform="matrix(-0.89717184,0.44168167,0.89717184,0.44168167,0,0)" /> ++ <rect ++ style="fill:none;stroke:#000000;stroke-width:1.12329161;stroke-miterlimit:4;stroke-dasharray:none" ++ id="rect4145" ++ width="12.53948" ++ height="7.8371782" ++ x="1353.5859" ++ y="1313.1813" ++ transform="matrix(0.89717184,0.44168167,-0.89717184,0.44168167,0,0)" /> ++ <rect ++ transform="matrix(0.89717184,0.44168167,-0.89717184,0.44168167,0,0)" ++ y="1313.1813" ++ x="1353.5859" ++ height="7.8371782" ++ width="12.53948" ++ id="rect3270" ++ style="fill:none;stroke:#000000;stroke-width:1.12329161;stroke-miterlimit:4;stroke-dasharray:none" /> ++ </g> ++ <g ++ inkscape:groupmode="layer" ++ id="layer5" ++ inkscape:label="labels" ++ style="display:inline" ++ transform="translate(0,-692.60215)"> ++ <text ++ xml:space="preserve" ++ style="font-size:34.48934937px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="123.57652" ++ y="797.86407" ++ id="text5637" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5639" ++ x="123.57652" ++ y="797.86407" ++ style="font-weight:bold">ROTATE 90° CW (example)</tspan></text> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:5.27452183;stroke-miterlimit:4;marker-end:url(#Arrow2Send)" ++ d="m 329.37463,828.07604 c 20.84196,-12.56761 54.63347,-12.56761 75.47543,0" ++ id="path5717" ++ inkscape:connector-curvature="0" ++ sodipodi:nodetypes="cc" /> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1042.3517" ++ x="1075.5519" ++ height="15.939886" ++ width="15.939886" ++ id="rect3131" ++ style="fill:#ff2a2a;stroke:none;display:inline" /> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1085.2841" ++ x="1118.4843" ++ height="15.939886" ++ width="15.939886" ++ id="rect3133" ++ style="fill:#7fff2a;stroke:none;display:inline" /> ++ <rect ++ transform="matrix(-0.89442719,0.4472136,-0.89442719,-0.4472136,0,0)" ++ style="fill:#7fff2a;stroke:none;display:inline" ++ id="rect3135" ++ width="15.939886" ++ height="15.939886" ++ x="1106.7502" ++ y="-1155.8905" /> ++ <rect ++ transform="matrix(0.8944272,0.44721358,-0.8944272,0.44721358,0,0)" ++ y="1128.2166" ++ x="1161.4166" ++ height="15.939887" ++ width="15.939887" ++ id="rect3137" ++ style="fill:#5599ff;fill-opacity:1;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3139" ++ y="958.45111" ++ x="25.466764" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="958.45111" ++ x="25.466764" ++ id="tspan3141" ++ sodipodi:role="line">1</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="25.466791" ++ y="996.85114" ++ id="text3143" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3145" ++ x="25.466791" ++ y="996.85114" ++ style="font-weight:bold">1</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3147" ++ y="1016.1264" ++ x="25.762766" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1016.1264" ++ x="25.762766" ++ id="tspan3149" ++ sodipodi:role="line">2</tspan></text> ++ <rect ++ transform="matrix(-0.89442719,0.4472136,-0.89442719,-0.4472136,0,0)" ++ style="fill:#ff2a2a;stroke:none;display:inline" ++ id="rect3231" ++ width="15.939886" ++ height="15.939886" ++ x="1063.8177" ++ y="-1112.9579" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="25.763147" ++ y="977.72668" ++ id="text3233" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3235" ++ x="25.763147" ++ y="977.72668" ++ style="font-weight:bold">2</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10.08127022px;font-style:normal;font-weight:normal;line-height:171.00000381%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="48.410496" ++ y="958.31488" ++ id="text3261" ++ sodipodi:linespacing="171%"><tspan ++ sodipodi:role="line" ++ id="tspan3263" ++ x="48.410496" ++ y="958.31488" ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:171.00000381%;font-family:Sans;-inkscape-font-specification:Sans">northern tile of the area (TileAreaT::tile)</tspan><tspan ++ sodipodi:role="line" ++ x="48.410496" ++ y="977.12488" ++ id="tspan3267" ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:171.00000381%;font-family:Sans;-inkscape-font-specification:Sans">transformed northern tile of the area (transformed_north)</tspan><tspan ++ sodipodi:role="line" ++ x="48.410496" ++ y="995.93488" ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:171.00000381%;font-family:Sans;-inkscape-font-specification:Sans" ++ id="tspan5100">tile of the area (tile)</tspan><tspan ++ sodipodi:role="line" ++ x="48.410496" ++ y="1014.7449" ++ id="tspan3271" ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:171.00000381%;font-family:Sans;-inkscape-font-specification:Sans">transformed tile of the area (transformed_tile)</tspan><tspan ++ sodipodi:role="line" ++ x="48.410496" ++ y="1033.5549" ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:171.00000381%;font-family:Sans;-inkscape-font-specification:Sans" ++ id="tspan5104">northern tile of the transformed area (dst_area_north)</tspan></text> ++ <rect ++ style="fill:#ff2a2a;stroke:none;display:inline" ++ id="rect3273" ++ width="15.939886" ++ height="15.939886" ++ x="-795.13434" ++ y="-1519.1414" ++ transform="matrix(0.89442719,-0.4472136,-0.89442719,-0.4472136,0,0)" /> ++ <text ++ sodipodi:linespacing="227%" ++ id="text3299" ++ y="958.31488" ++ x="406.78116" ++ style="font-size:10.08127022px;font-style:normal;font-weight:normal;line-height:226.99999809%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:226.99999809%;font-family:Sans;-inkscape-font-specification:Sans" ++ y="958.31488" ++ x="406.78116" ++ sodipodi:role="line" ++ id="tspan6555">TileAreaT::TransformTile:</tspan><tspan ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:226.99999809%;font-family:Sans;-inkscape-font-specification:Sans" ++ y="983.28485" ++ x="406.78116" ++ sodipodi:role="line" ++ id="tspan6557">TileAreaT::ReverseTransformTile:</tspan><tspan ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:226.99999809%;font-family:Sans;-inkscape-font-specification:Sans" ++ y="1008.2549" ++ x="406.78116" ++ sodipodi:role="line" ++ id="tspan6559">TileAreaT::TransformedNorth:</tspan><tspan ++ style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:226.99999809%;font-family:Sans;-inkscape-font-specification:Sans" ++ id="tspan3309" ++ y="1033.2249" ++ x="406.78116" ++ sodipodi:role="line">TileAreaT::ReverseTransformedNorth:</tspan></text> ++ <rect ++ transform="matrix(0.89442719,-0.44721359,-0.89442719,-0.44721359,0,0)" ++ y="-1548.417" ++ x="-765.8595" ++ height="15.939887" ++ width="15.939887" ++ id="rect3313" ++ style="fill:#5599ff;fill-opacity:1;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3315" ++ y="-1023.6799" ++ x="668.86713" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve" ++ transform="scale(1,-1)"><tspan ++ style="font-weight:bold" ++ y="-1023.6799" ++ x="668.86713" ++ id="tspan3317" ++ sodipodi:role="line">→</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="643.63861" ++ y="1032.0898" ++ id="text3327" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3329" ++ x="643.63861" ++ y="1032.0898" ++ style="font-weight:bold">2</tspan></text> ++ <rect ++ transform="matrix(-0.89442719,-0.4472136,0.89442719,-0.4472136,0,0)" ++ y="-740.44305" ++ x="-1522.8073" ++ height="15.939886" ++ width="15.939886" ++ id="rect3331" ++ style="fill:#ff2a2a;stroke:none;display:inline" /> ++ <rect ++ style="fill:#5599ff;fill-opacity:1;stroke:none;display:inline" ++ id="rect3333" ++ width="15.939887" ++ height="15.939887" ++ x="-1493.599" ++ y="-769.65118" ++ transform="matrix(-0.89442719,-0.44721359,0.89442719,-0.44721359,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="668.79767" ++ y="-1000.8605" ++ id="text3335" ++ sodipodi:linespacing="125%" ++ transform="scale(1,-1)"><tspan ++ sodipodi:role="line" ++ id="tspan3337" ++ x="668.79767" ++ y="-1000.8605" ++ style="font-weight:bold">→</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3339" ++ y="1009.2702" ++ x="695.83484" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1009.2702" ++ x="695.83484" ++ id="tspan3341" ++ sodipodi:role="line">2</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="710.38507" ++ x="1408.5416" ++ height="15.939886" ++ width="15.939886" ++ id="rect3343" ++ style="fill:#7fff2a;fill-opacity:1;stroke:none;display:inline" /> ++ <rect ++ style="fill:#7fff2a;fill-opacity:1;stroke:none;display:inline" ++ id="rect3345" ++ width="15.939887" ++ height="15.939887" ++ x="1464.4972" ++ y="654.42877" ++ transform="matrix(0.89442719,0.44721359,-0.89442719,0.44721359,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="694.79431" ++ y="958.90826" ++ id="text3347" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3349" ++ x="694.79431" ++ y="958.90826" ++ style="font-weight:bold">→</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3351" ++ y="958.98401" ++ x="620.51727" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="958.98401" ++ x="620.51727" ++ id="tspan3353" ++ sodipodi:role="line">1</tspan></text> ++ <rect ++ style="fill:#ff2a2a;stroke:none;display:inline" ++ id="rect3355" ++ width="15.939886" ++ height="15.939886" ++ x="1432.6057" ++ y="686.32031" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="663.5658" ++ y="958.98401" ++ id="text3357" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3359" ++ x="663.5658" ++ y="958.98401" ++ style="font-weight:bold">2</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3361" ++ y="958.90826" ++ x="643.74567" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="958.90826" ++ x="643.74567" ++ id="tspan3363" ++ sodipodi:role="line">,</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4175" ++ y="958.98364" ++ x="720.61444" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="958.98364" ++ x="720.61444" ++ id="tspan4177" ++ sodipodi:role="line">2</tspan></text> ++ <rect ++ style="fill:#7fff2a;fill-opacity:1;stroke:none;display:inline" ++ id="rect4179" ++ width="15.939886" ++ height="15.939886" ++ x="1435.2516" ++ y="737.54517" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <rect ++ transform="matrix(-0.89442719,0.44721359,0.89442719,0.44721359,0,0)" ++ y="1459.3159" ++ x="713.48022" ++ height="15.939887" ++ width="15.939887" ++ id="rect4181" ++ style="fill:#7fff2a;fill-opacity:1;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4183" ++ y="982.99976" ++ x="694.39142" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="982.99976" ++ x="694.39142" ++ id="tspan4185" ++ sodipodi:role="line">→</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="662.77679" ++ y="983.07538" ++ id="text4187" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4189" ++ x="662.77679" ++ y="983.07538" ++ style="font-weight:bold">2</tspan></text> ++ <rect ++ transform="matrix(-0.89442719,0.4472136,0.89442719,0.4472136,0,0)" ++ y="1491.2074" ++ x="681.58911" ++ height="15.939886" ++ width="15.939886" ++ id="rect4191" ++ style="fill:#ff2a2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4193" ++ y="983.07538" ++ x="720.21057" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="983.07538" ++ x="720.21057" ++ id="tspan4195" ++ sodipodi:role="line">2</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="643.34283" ++ y="982.99976" ++ id="text4197" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4199" ++ x="643.34283" ++ y="982.99976" ++ style="font-weight:bold">,</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="619.72919" ++ y="983.07507" ++ id="text4201" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4203" ++ x="619.72919" ++ y="983.07507" ++ style="font-weight:bold">1</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:26.54228592px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="95.972397" ++ y="746.4621" ++ id="text5106" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5108" ++ x="95.972397" ++ y="746.4621">Explanation on TileAreaT transformations</tspan></text> ++ <g ++ id="g6532" ++ transform="matrix(1.2,0,0,1.2,25.4513,-206.20779)"> ++ <path ++ inkscape:connector-curvature="0" ++ id="path4743" ++ d="m 38.351894,925.43359 33.224791,0" ++ style="fill:none;stroke:#000000;stroke-width:1.28295767;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Sstart);marker-end:url(#Arrow2Send)" /> ++ <path ++ style="fill:none;stroke:#000000;stroke-width:2.00492573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow2Sstart);marker-end:url(#Arrow2Send)" ++ d="m 54.96429,918.13257 0,14.60204" ++ id="path5805" ++ inkscape:connector-curvature="0" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text5824" ++ y="915.06653" ++ x="50.374447" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Serif;-inkscape-font-specification:Serif Bold" ++ y="915.06653" ++ x="50.374447" ++ id="tspan5826" ++ sodipodi:role="line">N</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="51.324154" ++ y="943.08575" ++ id="text5828" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5830" ++ x="51.324154" ++ y="943.08575" ++ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Serif;-inkscape-font-specification:Serif Bold">S</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text5832" ++ y="929.07861" ++ x="78.171951" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Serif;-inkscape-font-specification:Serif Bold" ++ y="929.07861" ++ x="78.171951" ++ id="tspan5834" ++ sodipodi:role="line">E</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="22.337685" ++ y="929.07861" ++ id="text5836" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5838" ++ x="22.337685" ++ y="929.07861" ++ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Serif;-inkscape-font-specification:Serif Bold">W</tspan></text> ++ <path ++ inkscape:connector-curvature="0" ++ id="path6075" ++ d="M 90.015437,942.72921 19.30476,907.37387" ++ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Sstart);marker-end:none;display:inline;enable-background:new" /> ++ <path ++ inkscape:connector-curvature="0" ++ id="path5870" ++ d="M 19.30476,942.72921 90.015437,907.37387" ++ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow2Sstart);marker-end:none;display:inline;enable-background:new" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text6477" ++ y="948.11798" ++ x="11.056497" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ xml:space="preserve"><tspan ++ y="948.11798" ++ x="11.056497" ++ id="tspan6479" ++ sodipodi:role="line">x</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text6481" ++ y="946.11218" ++ x="91.75" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ xml:space="preserve"><tspan ++ y="946.11218" ++ x="91.75" ++ id="tspan6483" ++ sodipodi:role="line">y</tspan></text> ++ </g> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="604.51727" ++ y="958.98401" ++ id="text3188" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3190" ++ x="604.51727" ++ y="958.98401" ++ style="font-weight:bold">(</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3192" ++ y="958.98401" ++ x="682.51727" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="958.98401" ++ x="682.51727" ++ id="tspan3194" ++ sodipodi:role="line">)</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3196" ++ y="982.98401" ++ x="604.51727" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="982.98401" ++ x="604.51727" ++ id="tspan3198" ++ sodipodi:role="line">(</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="682.51727" ++ y="982.98401" ++ id="text3200" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3202" ++ x="682.51727" ++ y="982.98401" ++ style="font-weight:bold">)</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:13.60865021px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ x="204.80836" ++ y="823.93964" ++ id="text5672" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5674" ++ x="204.80836" ++ y="823.93964" ++ style="font-weight:bold">1</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text5676" ++ y="866.36603" ++ x="255.71988" ++ style="font-size:13.60865021px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="866.36603" ++ x="255.71988" ++ id="tspan5678" ++ sodipodi:role="line">1</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:13.60865021px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ x="510.63068" ++ y="891.9115" ++ id="text5680" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan5682" ++ x="510.63068" ++ y="891.9115" ++ style="font-weight:bold">2</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text5684" ++ y="866.45581" ++ x="595.48383" ++ style="font-size:13.60865021px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;enable-background:new;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="866.45581" ++ x="595.48383" ++ id="tspan5686" ++ sodipodi:role="line">2</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="49.000977" ++ y="1067.2064" ++ id="text3205" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3207" ++ x="49.000977" ++ y="1067.2064" ++ style="font-size:11px">northern tile of the source area</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3213" ++ y="1087.2064" ++ x="49.000977" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1087.2064" ++ x="49.000977" ++ id="tspan3215" ++ sodipodi:role="line">tile of the source area</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="49.000977" ++ y="1107.2064" ++ id="text3217" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3219" ++ x="49.000977" ++ y="1107.2064" ++ style="font-size:11px">source tile of the northern tile of the transformed area </tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3221" ++ y="1127.2064" ++ x="49.000977" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1127.2064" ++ x="49.000977" ++ id="tspan3223" ++ sodipodi:role="line">source area</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="49.000977" ++ y="1147.2064" ++ id="text3225" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3227" ++ x="49.000977" ++ y="1147.2064" ++ style="font-size:11px">transformation</tspan></text> ++ <rect ++ style="fill:#ff2a2a;stroke:none;display:inline" ++ id="rect3229" ++ width="15.939886" ++ height="15.939886" ++ x="1198.6404" ++ y="1162.7954" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="27.83223" ++ y="1067.3622" ++ id="text3231" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3233" ++ x="27.83223" ++ y="1067.3622" ++ style="font-weight:bold">1</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1185.1561" ++ x="1221.0011" ++ height="15.939886" ++ width="15.939886" ++ id="rect3235" ++ style="fill:#7fff2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3237" ++ y="1087.3622" ++ x="27.83223" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1087.3622" ++ x="27.83223" ++ id="tspan3239" ++ sodipodi:role="line">1</tspan></text> ++ <rect ++ style="fill:#5599ff;stroke:none;display:inline" ++ id="rect3241" ++ width="15.939886" ++ height="15.939886" ++ x="1243.3618" ++ y="1207.5168" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="27.83223" ++ y="1107.3622" ++ id="text3243" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3245" ++ x="27.83223" ++ y="1107.3622" ++ style="font-weight:bold">1</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4089" ++ y="1147.3622" ++ x="27.83223" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1147.3622" ++ x="27.83223" ++ id="tspan4091" ++ sodipodi:role="line">↷</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4095" ++ y="1067.2064" ++ x="409.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1067.2064" ++ x="409.00098" ++ id="tspan4097" ++ sodipodi:role="line">transformed northern tile of the source area</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="409.00098" ++ y="1087.2064" ++ id="text4099" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4101" ++ x="409.00098" ++ y="1087.2064" ++ style="font-size:11px">transformed tile of the source area</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4103" ++ y="1107.2064" ++ x="409.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1107.2064" ++ x="409.00098" ++ id="tspan4105" ++ sodipodi:role="line">northern tile of the transformed area</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="409.00098" ++ y="1127.2064" ++ id="text4107" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4109" ++ x="409.00098" ++ y="1127.2064" ++ style="font-size:11px">transformed area</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4111" ++ y="1147.2064" ++ x="409.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1147.2064" ++ x="409.00098" ++ id="tspan4113" ++ sodipodi:role="line">inverted transformation</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="961.54974" ++ x="1399.8859" ++ height="15.939886" ++ width="15.939886" ++ id="rect4115" ++ style="fill:#ff2a2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4117" ++ y="1067.3622" ++ x="387.83221" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1067.3622" ++ x="387.83221" ++ id="tspan4119" ++ sodipodi:role="line">2</tspan></text> ++ <rect ++ style="fill:#7fff2a;stroke:none;display:inline" ++ id="rect4121" ++ width="15.939886" ++ height="15.939886" ++ x="1422.2466" ++ y="983.91058" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="387.83221" ++ y="1087.3622" ++ id="text4123" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4125" ++ x="387.83221" ++ y="1087.3622" ++ style="font-weight:bold">2</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1006.2711" ++ x="1444.6073" ++ height="15.939886" ++ width="15.939886" ++ id="rect4127" ++ style="fill:#5599ff;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4129" ++ y="1107.3622" ++ x="387.83221" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1107.3622" ++ x="387.83221" ++ id="tspan4131" ++ sodipodi:role="line">2</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="387.83221" ++ y="1147.3622" ++ id="text4133" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4135" ++ x="387.83221" ++ y="1147.3622" ++ style="font-weight:bold">↶</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="49.000977" ++ y="1187.2064" ++ id="text4147" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4149" ++ x="49.000977" ++ y="1187.2064" ++ style="font-size:11px">.TransformTile(</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4151" ++ y="1187.2064" ++ x="163.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1187.2064" ++ x="163.00098" ++ id="tspan4153" ++ sodipodi:role="line">,</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="199.00098" ++ y="1187.2064" ++ id="text4155" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4157" ++ x="199.00098" ++ y="1187.2064" ++ style="font-size:11px">,</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text4159" ++ y="1187.2064" ++ x="217.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1187.2064" ++ x="217.00098" ++ id="tspan4161" ++ sodipodi:role="line">) →</tspan></text> ++ <rect ++ style="fill:#7fff2a;stroke:none;display:inline" ++ id="rect4169" ++ width="15.939886" ++ height="15.939886" ++ x="1396.5323" ++ y="1233.2321" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="141.83223" ++ y="1187.3622" ++ id="text4171" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan4173" ++ x="141.83223" ++ y="1187.3622" ++ style="font-weight:bold">1</tspan></text> ++ <rect ++ style="fill:#ff2a2a;stroke:none;display:inline" ++ id="rect3253" ++ width="15.939886" ++ height="15.939886" ++ x="1417.7751" ++ y="1211.989" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="179.83221" ++ y="1187.3622" ++ id="text3255" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3257" ++ x="179.83221" ++ y="1187.3622" ++ style="font-weight:bold">2</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="205.83223" ++ y="1187.3622" ++ id="text3259" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3261" ++ x="205.83223" ++ y="1187.3622" ++ style="font-weight:bold">↷</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1173.9766" ++ x="1455.7881" ++ height="15.939886" ++ width="15.939886" ++ id="rect3263" ++ style="fill:#7fff2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3265" ++ y="1187.3622" ++ x="247.83221" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1187.3622" ++ x="247.83221" ++ id="tspan3268" ++ sodipodi:role="line">2</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3272" ++ y="1187.2064" ++ x="49.000977" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1187.2064" ++ x="49.000977" ++ id="tspan3274" ++ sodipodi:role="line">.TransformTile(</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="163.00098" ++ y="1187.2064" ++ id="text3276" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3278" ++ x="163.00098" ++ y="1187.2064" ++ style="font-size:11px">,</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3280" ++ y="1187.2064" ++ x="199.00098" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-size:11px" ++ y="1187.2064" ++ x="199.00098" ++ id="tspan3282" ++ sodipodi:role="line">,</tspan></text> ++ <text ++ xml:space="preserve" ++ style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" ++ x="217.00098" ++ y="1187.2064" ++ id="text3284" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3286" ++ x="217.00098" ++ y="1187.2064" ++ style="font-size:11px">) →</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1233.2321" ++ x="1396.5323" ++ height="15.939886" ++ width="15.939886" ++ id="rect3288" ++ style="fill:#7fff2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3290" ++ y="1187.3622" ++ x="141.83223" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1187.3622" ++ x="141.83223" ++ id="tspan3292" ++ sodipodi:role="line">1</tspan></text> ++ <rect ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" ++ y="1211.989" ++ x="1417.7751" ++ height="15.939886" ++ width="15.939886" ++ id="rect3294" ++ style="fill:#ff2a2a;stroke:none;display:inline" /> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3296" ++ y="1187.3622" ++ x="179.83221" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1187.3622" ++ x="179.83221" ++ id="tspan3298" ++ sodipodi:role="line">2</tspan></text> ++ <text ++ sodipodi:linespacing="125%" ++ id="text3300" ++ y="1187.3622" ++ x="205.83223" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ xml:space="preserve"><tspan ++ style="font-weight:bold" ++ y="1187.3622" ++ x="205.83223" ++ id="tspan3302" ++ sodipodi:role="line">↷</tspan></text> ++ <rect ++ style="fill:#7fff2a;stroke:none;display:inline" ++ id="rect3304" ++ width="15.939886" ++ height="15.939886" ++ x="1455.7881" ++ y="1173.9766" ++ transform="matrix(0.89442719,0.4472136,-0.89442719,0.4472136,0,0)" /> ++ <text ++ xml:space="preserve" ++ style="font-size:11.43270588px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" ++ x="247.83221" ++ y="1187.3622" ++ id="text3306" ++ sodipodi:linespacing="125%"><tspan ++ sodipodi:role="line" ++ id="tspan3308" ++ x="247.83221" ++ y="1187.3622" ++ style="font-weight:bold">2</tspan></text> ++ </g> ++</svg> +diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj +index 37f9948..5447805 100644 +--- a/projects/openttd_vs100.vcxproj ++++ b/projects/openttd_vs100.vcxproj +@@ -301,6 +301,7 @@ + <ClCompile Include="..\src\cargopacket.cpp" /> + <ClCompile Include="..\src\cargotype.cpp" /> + <ClCompile Include="..\src\cheat.cpp" /> ++ <ClCompile Include="..\src\clipboard.cpp" /> + <ClCompile Include="..\src\command.cpp" /> + <ClCompile Include="..\src\console.cpp" /> + <ClCompile Include="..\src\console_cmds.cpp" /> +@@ -412,6 +413,9 @@ + <ClInclude Include="..\src\cheat_func.h" /> + <ClInclude Include="..\src\cheat_type.h" /> + <ClInclude Include="..\src\clear_func.h" /> ++ <ClInclude Include="..\src\clipboard_func.h" /> ++ <ClInclude Include="..\src\clipboard_gui.h" /> ++ <ClInclude Include="..\src\clipboard_type.h" /> + <ClInclude Include="..\src\cmd_helper.h" /> + <ClInclude Include="..\src\command_func.h" /> + <ClInclude Include="..\src\command_type.h" /> +@@ -424,6 +428,7 @@ + <ClInclude Include="..\src\console_gui.h" /> + <ClInclude Include="..\src\console_internal.h" /> + <ClInclude Include="..\src\console_type.h" /> ++ <ClInclude Include="..\src\copypaste_cmd.h" /> + <ClInclude Include="..\src\cpu.h" /> + <ClInclude Include="..\src\crashlog.h" /> + <ClInclude Include="..\src\currency.h" /> +@@ -556,6 +561,8 @@ + <ClInclude Include="..\src\order_base.h" /> + <ClInclude Include="..\src\order_func.h" /> + <ClInclude Include="..\src\order_type.h" /> ++ <ClInclude Include="..\src\overlay.h" /> ++ <ClInclude Include="..\src\overlay_cmd.h" /> + <ClInclude Include="..\src\pbs.h" /> + <ClInclude Include="..\src\progress.h" /> + <ClInclude Include="..\src\querystring_gui.h" /> +@@ -621,6 +628,7 @@ + <ClInclude Include="..\src\tgp.h" /> + <ClInclude Include="..\src\tile_cmd.h" /> + <ClInclude Include="..\src\tile_type.h" /> ++ <ClInclude Include="..\src\tilearea_func.h" /> + <ClInclude Include="..\src\tilearea_type.h" /> + <ClInclude Include="..\src\tilehighlight_func.h" /> + <ClInclude Include="..\src\tilehighlight_type.h" /> +@@ -695,6 +703,7 @@ + <ClCompile Include="..\src\bridge_gui.cpp" /> + <ClCompile Include="..\src\build_vehicle_gui.cpp" /> + <ClCompile Include="..\src\cheat_gui.cpp" /> ++ <ClCompile Include="..\src\clipboard_gui.cpp" /> + <ClCompile Include="..\src\company_gui.cpp" /> + <ClCompile Include="..\src\console_gui.cpp" /> + <ClCompile Include="..\src\date_gui.cpp" /> +@@ -752,6 +761,7 @@ + <ClInclude Include="..\src\widgets\bridge_widget.h" /> + <ClInclude Include="..\src\widgets\build_vehicle_widget.h" /> + <ClInclude Include="..\src\widgets\cheat_widget.h" /> ++ <ClInclude Include="..\src\widgets\clipboard_widget.h" /> + <ClInclude Include="..\src\widgets\company_widget.h" /> + <ClInclude Include="..\src\widgets\console_widget.h" /> + <ClInclude Include="..\src\widgets\date_widget.h" /> +@@ -805,6 +815,7 @@ + <ClCompile Include="..\src\aircraft_cmd.cpp" /> + <ClCompile Include="..\src\autoreplace_cmd.cpp" /> + <ClCompile Include="..\src\clear_cmd.cpp" /> ++ <ClCompile Include="..\src\copypaste_cmd.cpp" /> + <ClCompile Include="..\src\company_cmd.cpp" /> + <ClCompile Include="..\src\depot_cmd.cpp" /> + <ClCompile Include="..\src\group_cmd.cpp" /> +@@ -812,6 +823,7 @@ + <ClCompile Include="..\src\misc_cmd.cpp" /> + <ClCompile Include="..\src\object_cmd.cpp" /> + <ClCompile Include="..\src\order_cmd.cpp" /> ++ <ClCompile Include="..\src\overlay_cmd.cpp" /> + <ClCompile Include="..\src\rail_cmd.cpp" /> + <ClCompile Include="..\src\road_cmd.cpp" /> + <ClCompile Include="..\src\roadveh_cmd.cpp" /> +diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters +index 06800ff..c561077 100644 +--- a/projects/openttd_vs100.vcxproj.filters ++++ b/projects/openttd_vs100.vcxproj.filters +@@ -132,6 +132,9 @@ + <ClCompile Include="..\src\cheat.cpp"> + <Filter>Source Files</Filter> + </ClCompile> ++ <ClCompile Include="..\src\clipboard.cpp"> ++ <Filter>Source Files</Filter> ++ </ClCompile> + <ClCompile Include="..\src\command.cpp"> + <Filter>Source Files</Filter> + </ClCompile> +@@ -465,6 +468,15 @@ + <ClInclude Include="..\src\clear_func.h"> + <Filter>Header Files</Filter> + </ClInclude> ++ <ClInclude Include="..\src\clipboard_func.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="..\src\clipboard_gui.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="..\src\clipboard_type.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> + <ClInclude Include="..\src\cmd_helper.h"> + <Filter>Header Files</Filter> + </ClInclude> +@@ -501,6 +513,9 @@ + <ClInclude Include="..\src\console_type.h"> + <Filter>Header Files</Filter> + </ClInclude> ++ <ClInclude Include="..\src\copypaste_cmd.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> + <ClInclude Include="..\src\cpu.h"> + <Filter>Header Files</Filter> + </ClInclude> +@@ -897,6 +912,12 @@ + <ClInclude Include="..\src\order_type.h"> + <Filter>Header Files</Filter> + </ClInclude> ++ <ClInclude Include="..\src\overlay.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> ++ <ClInclude Include="..\src\overlay_cmd.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> + <ClInclude Include="..\src\pbs.h"> + <Filter>Header Files</Filter> + </ClInclude> +@@ -1092,6 +1113,9 @@ + <ClInclude Include="..\src\tile_type.h"> + <Filter>Header Files</Filter> + </ClInclude> ++ <ClInclude Include="..\src\tilearea_func.h"> ++ <Filter>Header Files</Filter> ++ </ClInclude> + <ClInclude Include="..\src\tilearea_type.h"> + <Filter>Header Files</Filter> + </ClInclude> +@@ -1314,6 +1338,9 @@ + <ClCompile Include="..\src\cheat_gui.cpp"> + <Filter>GUI Source Code</Filter> + </ClCompile> ++ <ClCompile Include="..\src\clipboard_gui.cpp"> ++ <Filter>GUI Source Code</Filter> ++ </ClCompile> + <ClCompile Include="..\src\company_gui.cpp"> + <Filter>GUI Source Code</Filter> + </ClCompile> +@@ -1485,6 +1512,9 @@ + <ClInclude Include="..\src\widgets\cheat_widget.h"> + <Filter>Widgets</Filter> + </ClInclude> ++ <ClInclude Include="..\src\widgets\clipboard_widget.h"> ++ <Filter>Widgets</Filter> ++ </ClInclude> + <ClInclude Include="..\src\widgets\company_widget.h"> + <Filter>Widgets</Filter> + </ClInclude> +@@ -1644,6 +1674,9 @@ + <ClCompile Include="..\src\clear_cmd.cpp"> + <Filter>Command handlers</Filter> + </ClCompile> ++ <ClCompile Include="..\src\copypaste_cmd.cpp"> ++ <Filter>Command handlers</Filter> ++ </ClCompile> + <ClCompile Include="..\src\company_cmd.cpp"> + <Filter>Command handlers</Filter> + </ClCompile> +@@ -1665,6 +1698,9 @@ + <ClCompile Include="..\src\order_cmd.cpp"> + <Filter>Command handlers</Filter> + </ClCompile> ++ <ClCompile Include="..\src\overlay_cmd.cpp"> ++ <Filter>Command handlers</Filter> ++ </ClCompile> + <ClCompile Include="..\src\rail_cmd.cpp"> + <Filter>Command handlers</Filter> + </ClCompile> +diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj +index f859fcf..3842070 100644 +--- a/projects/openttd_vs80.vcproj ++++ b/projects/openttd_vs80.vcproj +@@ -475,6 +475,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\command.cpp" + > + </File> +@@ -923,6 +927,18 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard_func.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\clipboard_gui.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\clipboard_type.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\cmd_helper.h" + > + </File> +@@ -971,6 +987,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\copypaste_cmd.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\cpu.h" + > + </File> +@@ -1499,6 +1519,14 @@ + > + </File> + <File ++ RelativePath=".\..\src\overlay.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\overlay_cmd.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\pbs.h" + > + </File> +@@ -1759,6 +1787,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\tilearea_func.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\tilearea_type.h" + > + </File> +@@ -2063,6 +2095,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard_gui.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\company_gui.cpp" + > + </File> +@@ -2295,6 +2331,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\widgets\clipboard_widget.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\widgets\company_widget.h" + > + </File> +@@ -2511,6 +2551,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\copypaste_cmd.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\company_cmd.cpp" + > + </File> +@@ -2539,6 +2583,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\overlay_cmd.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\rail_cmd.cpp" + > + </File> +diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj +index 0cf6627..3df761e 100644 +--- a/projects/openttd_vs90.vcproj ++++ b/projects/openttd_vs90.vcproj +@@ -472,6 +472,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\command.cpp" + > + </File> +@@ -920,6 +924,18 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard_func.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\clipboard_gui.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\clipboard_type.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\cmd_helper.h" + > + </File> +@@ -968,6 +984,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\copypaste_cmd.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\cpu.h" + > + </File> +@@ -1496,6 +1516,14 @@ + > + </File> + <File ++ RelativePath=".\..\src\overlay.h" ++ > ++ </File> ++ <File ++ RelativePath=".\..\src\overlay_cmd.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\pbs.h" + > + </File> +@@ -1756,6 +1784,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\tilearea_func.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\tilearea_type.h" + > + </File> +@@ -2060,6 +2092,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\clipboard_gui.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\company_gui.cpp" + > + </File> +@@ -2292,6 +2328,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\widgets\clipboard_widget.h" ++ > ++ </File> ++ <File + RelativePath=".\..\src\widgets\company_widget.h" + > + </File> +@@ -2508,6 +2548,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\copypaste_cmd.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\company_cmd.cpp" + > + </File> +@@ -2536,6 +2580,10 @@ + > + </File> + <File ++ RelativePath=".\..\src\overlay_cmd.cpp" ++ > ++ </File> ++ <File + RelativePath=".\..\src\rail_cmd.cpp" + > + </File> +diff --git a/source.list b/source.list +index df35cdd..7a1c402 100644 +--- a/source.list ++++ b/source.list +@@ -9,6 +9,7 @@ cargomonitor.cpp + cargopacket.cpp + cargotype.cpp + cheat.cpp ++clipboard.cpp + command.cpp + console.cpp + console_cmds.cpp +@@ -151,6 +152,9 @@ cargotype.h + cheat_func.h + cheat_type.h + clear_func.h ++clipboard_func.h ++clipboard_gui.h ++clipboard_type.h + cmd_helper.h + command_func.h + command_type.h +@@ -163,6 +167,7 @@ console_func.h + console_gui.h + console_internal.h + console_type.h ++copypaste_cmd.h + cpu.h + crashlog.h + currency.h +@@ -295,6 +300,8 @@ order_backup.h + order_base.h + order_func.h + order_type.h ++overlay.h ++overlay_cmd.h + pbs.h + progress.h + querystring_gui.h +@@ -360,6 +367,7 @@ textfile_type.h + tgp.h + tile_cmd.h + tile_type.h ++tilearea_func.h + tilearea_type.h + tilehighlight_func.h + tilehighlight_type.h +@@ -453,6 +461,7 @@ bootstrap_gui.cpp + bridge_gui.cpp + build_vehicle_gui.cpp + cheat_gui.cpp ++clipboard_gui.cpp + company_gui.cpp + console_gui.cpp + date_gui.cpp +@@ -512,6 +521,7 @@ widgets/bootstrap_widget.h + widgets/bridge_widget.h + widgets/build_vehicle_widget.h + widgets/cheat_widget.h ++widgets/clipboard_widget.h + widgets/company_widget.h + widgets/console_widget.h + widgets/date_widget.h +@@ -567,6 +577,7 @@ widgets/waypoint_widget.h + aircraft_cmd.cpp + autoreplace_cmd.cpp + clear_cmd.cpp ++copypaste_cmd.cpp + company_cmd.cpp + depot_cmd.cpp + group_cmd.cpp +@@ -574,6 +585,7 @@ industry_cmd.cpp + misc_cmd.cpp + object_cmd.cpp + order_cmd.cpp ++overlay_cmd.cpp + rail_cmd.cpp + road_cmd.cpp + roadveh_cmd.cpp +diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp +index 6437f23..1ba9115 100644 +--- a/src/airport_gui.cpp ++++ b/src/airport_gui.cpp +@@ -57,7 +57,7 @@ void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 + static void PlaceAirport(TileIndex tile) + { + if (_selected_airport_index == -1) return; +- uint32 p2 = _ctrl_pressed; ++ uint32 p2 = _settings_game.station.adjacent_stations && _ctrl_pressed; // adjacent? + SB(p2, 16, 16, INVALID_STATION); // no station to join + + uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); +diff --git a/src/bridge.h b/src/bridge.h +index badf045..2c7fc08 100644 +--- a/src/bridge.h ++++ b/src/bridge.h +@@ -73,6 +73,7 @@ static inline const BridgeSpec *GetBridgeSpec(BridgeType i) + void DrawBridgeMiddle(const TileInfo *ti); + + CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlag flags = DC_NONE); ++BridgeType FastestAvailableBridgeType(uint bridge_len); + int CalcBridgeLenCostFactor(int x); + + void ResetBridges(); +diff --git a/src/bridge_map.cpp b/src/bridge_map.cpp +index d1e0d60..c763745 100644 +--- a/src/bridge_map.cpp ++++ b/src/bridge_map.cpp +@@ -21,9 +21,10 @@ + * @param tile the bridge tile to find the bridge ramp for + * @param dir the direction to search in + */ +-static TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) ++template <bool Tgeneric> ++static typename TileIndexT<Tgeneric>::T GetBridgeEnd(typename TileIndexT<Tgeneric>::T tile, DiagDirection dir) + { +- TileIndexDiff delta = TileOffsByDiagDir(dir); ++ TileIndexDiff delta = TileOffsByDiagDir<Tgeneric>(dir, MapOf(tile)); + + dir = ReverseDiagDir(dir); + do { +@@ -32,6 +33,10 @@ static TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) + + return tile; + } ++/** @copydoc GetBridgeEnd(TileIndexT<Tgeneric>::T,DiagDirection) */ ++static inline TileIndex GetBridgeEnd(TileIndex t, DiagDirection dir) { return GetBridgeEnd<false>(t, dir); } ++/** @copydoc GetBridgeEnd(TileIndexT<Tgeneric>::T,DiagDirection) */ ++static inline GenericTileIndex GetBridgeEnd(GenericTileIndex t, DiagDirection dir) { return GetBridgeEnd<true>(t, dir); } + + + /** +@@ -58,18 +63,23 @@ TileIndex GetSouthernBridgeEnd(TileIndex t) + * Starting at one bridge end finds the other bridge end + * @param t the bridge ramp tile to find the other bridge ramp for + */ +-TileIndex GetOtherBridgeEnd(TileIndex tile) ++template <bool Tgeneric> ++typename TileIndexT<Tgeneric>::T GetOtherBridgeEnd(typename TileIndexT<Tgeneric>::T tile) + { + assert(IsBridgeTile(tile)); + return GetBridgeEnd(tile, GetTunnelBridgeDirection(tile)); + } ++/* instantiate */ ++template TileIndex GetOtherBridgeEnd<false>(TileIndex tile); ++template GenericTileIndex GetOtherBridgeEnd<true>(GenericTileIndex tile); + + /** + * Get the height ('z') of a bridge. + * @param tile the bridge ramp tile to get the bridge height from + * @return the height of the bridge. + */ +-int GetBridgeHeight(TileIndex t) ++template <bool Tgeneric> ++int GetBridgeHeight(typename TileIndexT<Tgeneric>::T t) + { + int h; + Slope tileh = GetTileSlope(t, &h); +@@ -78,3 +88,6 @@ int GetBridgeHeight(TileIndex t) + /* one height level extra for the ramp */ + return h + 1 + ApplyFoundationToSlope(f, &tileh); + } ++/* instantiate */ ++template int GetBridgeHeight<false>(TileIndex t); ++template int GetBridgeHeight<true>(GenericTileIndex t); +diff --git a/src/bridge_map.h b/src/bridge_map.h +index 74c6974..c2e4d9b 100644 +--- a/src/bridge_map.h ++++ b/src/bridge_map.h +@@ -21,31 +21,46 @@ + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return true if the structure is a bridge one + */ +-static inline bool IsBridge(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBridge(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- return HasBit(_m[t].m5, 7); ++ return HasBit(GetTile(t)->m5, 7); + } ++/** @copydoc IsBridge(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridge(TileIndex t) { return IsBridge<false>(t); } ++/** @copydoc IsBridge(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridge(GenericTileIndex t) { return IsBridge<true>(t); } + + /** + * checks if there is a bridge on this tile + * @param t The tile to analyze + * @return true if a bridge is present + */ +-static inline bool IsBridgeTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBridgeTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_TUNNELBRIDGE) && IsBridge(t); + } ++/** @copydoc IsBridgeTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridgeTile(TileIndex t) { return IsBridgeTile<false>(t); } ++/** @copydoc IsBridgeTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridgeTile(GenericTileIndex t) { return IsBridgeTile<true>(t); } + + /** + * checks if a bridge is set above the ground of this tile + * @param t The tile to analyze + * @return true if a bridge is detected above + */ +-static inline bool IsBridgeAbove(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBridgeAbove(typename TileIndexT<Tgeneric>::T t) + { +- return GB(_m[t].type, 2, 2) != 0; ++ return GB(GetTile(t)->type, 2, 2) != 0; + } ++/** @copydoc IsBridgeAbove(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridgeAbove(TileIndex t) { return IsBridgeAbove<false>(t); } ++/** @copydoc IsBridgeAbove(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBridgeAbove(GenericTileIndex t) { return IsBridgeAbove<true>(t); } + + /** + * Determines the type of bridge on a tile +@@ -53,11 +68,16 @@ static inline bool IsBridgeAbove(TileIndex t) + * @pre IsBridgeTile(t) + * @return The bridge type + */ +-static inline BridgeType GetBridgeType(TileIndex t) ++template <bool Tgeneric> ++static inline BridgeType GetBridgeType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsBridgeTile(t)); +- return GB(_me[t].m6, 2, 4); ++ return GB(GetTileEx(t)->m6, 2, 4); + } ++/** @copydoc GetBridgeType(TileIndexT<Tgeneric>::T) */ ++static inline BridgeType GetBridgeType(TileIndex t) { return GetBridgeType<false>(t); } ++/** @copydoc GetBridgeType(TileIndexT<Tgeneric>::T) */ ++static inline BridgeType GetBridgeType(GenericTileIndex t) { return GetBridgeType<true>(t); } + + /** + * Get the axis of the bridge that goes over the tile. Not the axis or the ramp. +@@ -65,17 +85,34 @@ static inline BridgeType GetBridgeType(TileIndex t) + * @pre IsBridgeAbove(t) + * @return the above mentioned axis + */ +-static inline Axis GetBridgeAxis(TileIndex t) ++template <bool Tgeneric> ++static inline Axis GetBridgeAxis(typename TileIndexT<Tgeneric>::T t) + { + assert(IsBridgeAbove(t)); +- return (Axis)(GB(_m[t].type, 2, 2) - 1); ++ return (Axis)(GB(GetTile(t)->type, 2, 2) - 1); + } ++/** @copydoc GetBridgeAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetBridgeAxis(TileIndex t) { return GetBridgeAxis<false>(t); } ++/** @copydoc GetBridgeAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetBridgeAxis(GenericTileIndex t) { return GetBridgeAxis<true>(t); } + + TileIndex GetNorthernBridgeEnd(TileIndex t); + TileIndex GetSouthernBridgeEnd(TileIndex t); +-TileIndex GetOtherBridgeEnd(TileIndex t); + +-int GetBridgeHeight(TileIndex tile); ++template <bool Tgeneric> ++typename TileIndexT<Tgeneric>::T GetOtherBridgeEnd(typename TileIndexT<Tgeneric>::T t); ++/** @copydoc GetOtherBridgeEnd(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex GetOtherBridgeEnd(TileIndex t) { return GetOtherBridgeEnd<false>(t); } ++/** @copydoc GetOtherBridgeEnd(TileIndexT<Tgeneric>::T) */ ++static inline GenericTileIndex GetOtherBridgeEnd(GenericTileIndex t) { return GetOtherBridgeEnd<true>(t); } ++ ++template <bool Tgeneric> ++int GetBridgeHeight(typename TileIndexT<Tgeneric>::T tile); ++/** @copydoc GetBridgeHeight(TileIndexT<Tgeneric>::T) */ ++static inline int GetBridgeHeight(TileIndex t) { return GetBridgeHeight<false>(t); } ++/** @copydoc GetBridgeHeight(TileIndexT<Tgeneric>::T) */ ++static inline int GetBridgeHeight(GenericTileIndex t) { return GetBridgeHeight<true>(t); } ++ + /** + * Get the height ('z') of a bridge in pixels. + * @param tile the bridge ramp tile to get the bridge height from +@@ -93,7 +130,7 @@ static inline int GetBridgePixelHeight(TileIndex tile) + */ + static inline void ClearSingleBridgeMiddle(TileIndex t, Axis a) + { +- ClrBit(_m[t].type, 2 + a); ++ ClrBit(GetTile(t)->type, 2 + a); + } + + /** +@@ -111,10 +148,15 @@ static inline void ClearBridgeMiddle(TileIndex t) + * @param t the tile to add the bridge to + * @param a the axis of the bridge to add + */ +-static inline void SetBridgeMiddle(TileIndex t, Axis a) ++template <bool Tgeneric> ++static inline void SetBridgeMiddle(typename TileIndexT<Tgeneric>::T t, Axis a) + { +- SetBit(_m[t].type, 2 + a); ++ SetBit(GetTile(t)->type, 2 + a); + } ++/** @copydoc SetBridgeMiddle(TileIndexT<Tgeneric>::T,Axis) */ ++static inline void SetBridgeMiddle(TileIndex t, Axis a) { return SetBridgeMiddle<false>(t, a); } ++/** @copydoc SetBridgeMiddle(TileIndexT<Tgeneric>::T,Axis) */ ++static inline void SetBridgeMiddle(GenericTileIndex t, Axis a) { return SetBridgeMiddle<true>(t, a); } + + /** + * Generic part to make a bridge ramp for both roads and rails. +@@ -126,17 +168,22 @@ static inline void SetBridgeMiddle(TileIndex t, Axis a) + * @param rt the road or rail type + * @note this function should not be called directly. + */ +-static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) ++template <bool Tgeneric> ++static inline void MakeBridgeRamp(typename TileIndexT<Tgeneric>::T t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) + { + SetTileType(t, MP_TUNNELBRIDGE); + SetTileOwner(t, o); +- _m[t].m2 = 0; +- _m[t].m3 = rt; +- _m[t].m4 = 0; +- _m[t].m5 = 1 << 7 | tt << 2 | d; +- SB(_me[t].m6, 2, 4, bridgetype); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = rt; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = 1 << 7 | tt << 2 | d; ++ SB(GetTileEx(t)->m6, 2, 4, bridgetype); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeBridgeRamp(TileIndexT<Tgeneric>::T,Owner,BridgeType,DiagDirection,TransportType,uint)*/ ++static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) { return MakeBridgeRamp<false>(t, o, bridgetype, d, tt, rt); } ++/** @copydoc MakeBridgeRamp(TileIndexT<Tgeneric>::T,Owner,BridgeType,DiagDirection,TransportType,uint)*/ ++static inline void MakeBridgeRamp(GenericTileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) { return MakeBridgeRamp<true>(t, o, bridgetype, d, tt, rt); } + + /** + * Make a bridge ramp for roads. +@@ -148,13 +195,18 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D + * @param d the direction this ramp must be facing + * @param r the road type of the bridge + */ +-static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) ++template <bool Tgeneric> ++static inline void MakeRoadBridgeRamp(typename TileIndexT<Tgeneric>::T t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) + { + MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD, 0); + SetRoadOwner(t, ROADTYPE_ROAD, owner_road); + if (owner_tram != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, owner_tram); + SetRoadTypes(t, r); + } ++/** @copydoc MakeRoadBridgeRamp(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,BridgeType,DiagDirection,RoadTypes) */ ++static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) { return MakeRoadBridgeRamp<false>(t, o, owner_road, owner_tram, bridgetype, d, r); } ++/** @copydoc MakeRoadBridgeRamp(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,BridgeType,DiagDirection,RoadTypes) */ ++static inline void MakeRoadBridgeRamp(GenericTileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) { return MakeRoadBridgeRamp<true>(t, o, owner_road, owner_tram, bridgetype, d, r); } + + /** + * Make a bridge ramp for rails. +@@ -164,10 +216,15 @@ static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Ow + * @param d the direction this ramp must be facing + * @param r the rail type of the bridge + */ +-static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) ++template <bool Tgeneric> ++static inline void MakeRailBridgeRamp(typename TileIndexT<Tgeneric>::T t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) + { + MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL, r); + } ++/** @copydoc MakeRailBridgeRamp(TileIndexT<Tgeneric>::T,Owner,BridgeType,DiagDirection,RailType) */ ++static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) { return MakeRailBridgeRamp<false>(t, o, bridgetype, d, r); } ++/** @copydoc MakeRailBridgeRamp(TileIndexT<Tgeneric>::T,Owner,BridgeType,DiagDirection,RailType) */ ++static inline void MakeRailBridgeRamp(GenericTileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) { return MakeRailBridgeRamp<true>(t, o, bridgetype, d, r); } + + /** + * Make a bridge ramp for aqueducts. +@@ -175,9 +232,14 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp + * @param o the new owner of the bridge ramp + * @param d the direction this ramp must be facing + */ +-static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) ++template <bool Tgeneric> ++static inline void MakeAqueductBridgeRamp(typename TileIndexT<Tgeneric>::T t, Owner o, DiagDirection d) + { + MakeBridgeRamp(t, o, 0, d, TRANSPORT_WATER, 0); + } ++/** @copydoc MakeAqueductBridgeRamp(TileIndexT<Tgeneric>::T,Owner,DiagDirection) */ ++static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) { return MakeAqueductBridgeRamp<false>(t, o, d); } ++/** @copydoc MakeAqueductBridgeRamp(TileIndexT<Tgeneric>::T,Owner,DiagDirection) */ ++static inline void MakeAqueductBridgeRamp(GenericTileIndex t, Owner o, DiagDirection d) { return MakeAqueductBridgeRamp<true>(t, o, d); } + + #endif /* BRIDGE_MAP_H */ +diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp +index 2e9589a..a28d841 100644 +--- a/src/clear_cmd.cpp ++++ b/src/clear_cmd.cpp +@@ -126,6 +126,7 @@ static void DrawTile_Clear(TileInfo *ti) + break; + } + ++ DrawOverlay(ti, MP_CLEAR); + DrawBridgeMiddle(ti); + } + +@@ -399,4 +400,5 @@ extern const TileTypeProcs _tile_type_clear_procs = { + NULL, ///< vehicle_enter_tile_proc + GetFoundation_Clear, ///< get_foundation_proc + TerraformTile_Clear, ///< terraform_tile_proc ++ NULL, ///< copypaste_tile_proc + }; +diff --git a/src/clear_map.h b/src/clear_map.h +index 76b1e82..b8ddb34 100644 +--- a/src/clear_map.h ++++ b/src/clear_map.h +@@ -37,7 +37,7 @@ enum ClearGround { + static inline bool IsSnowTile(TileIndex t) + { + assert(IsTileType(t, MP_CLEAR)); +- return HasBit(_m[t].m3, 4); ++ return HasBit(GetTile(t)->m3, 4); + } + + /** +@@ -49,7 +49,7 @@ static inline bool IsSnowTile(TileIndex t) + static inline ClearGround GetRawClearGround(TileIndex t) + { + assert(IsTileType(t, MP_CLEAR)); +- return (ClearGround)GB(_m[t].m5, 2, 3); ++ return (ClearGround)GB(GetTile(t)->m5, 2, 3); + } + + /** +@@ -85,7 +85,7 @@ static inline bool IsClearGround(TileIndex t, ClearGround ct) + static inline uint GetClearDensity(TileIndex t) + { + assert(IsTileType(t, MP_CLEAR)); +- return GB(_m[t].m5, 0, 2); ++ return GB(GetTile(t)->m5, 0, 2); + } + + /** +@@ -97,7 +97,7 @@ static inline uint GetClearDensity(TileIndex t) + static inline void AddClearDensity(TileIndex t, int d) + { + assert(IsTileType(t, MP_CLEAR)); // XXX incomplete +- _m[t].m5 += d; ++ GetTile(t)->m5 += d; + } + + /** +@@ -109,7 +109,7 @@ static inline void AddClearDensity(TileIndex t, int d) + static inline void SetClearDensity(TileIndex t, uint d) + { + assert(IsTileType(t, MP_CLEAR)); +- SB(_m[t].m5, 0, 2, d); ++ SB(GetTile(t)->m5, 0, 2, d); + } + + +@@ -122,7 +122,7 @@ static inline void SetClearDensity(TileIndex t, uint d) + static inline uint GetClearCounter(TileIndex t) + { + assert(IsTileType(t, MP_CLEAR)); +- return GB(_m[t].m5, 5, 3); ++ return GB(GetTile(t)->m5, 5, 3); + } + + /** +@@ -134,7 +134,7 @@ static inline uint GetClearCounter(TileIndex t) + static inline void AddClearCounter(TileIndex t, int c) + { + assert(IsTileType(t, MP_CLEAR)); // XXX incomplete +- _m[t].m5 += c << 5; ++ GetTile(t)->m5 += c << 5; + } + + /** +@@ -146,7 +146,7 @@ static inline void AddClearCounter(TileIndex t, int c) + static inline void SetClearCounter(TileIndex t, uint c) + { + assert(IsTileType(t, MP_CLEAR)); // XXX incomplete +- SB(_m[t].m5, 5, 3, c); ++ SB(GetTile(t)->m5, 5, 3, c); + } + + +@@ -160,7 +160,7 @@ static inline void SetClearCounter(TileIndex t, uint c) + static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density) + { + assert(IsTileType(t, MP_CLEAR)); // XXX incomplete +- _m[t].m5 = 0 << 5 | type << 2 | density; ++ GetTile(t)->m5 = 0 << 5 | type << 2 | density; + } + + +@@ -173,7 +173,7 @@ static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint den + static inline uint GetFieldType(TileIndex t) + { + assert(GetClearGround(t) == CLEAR_FIELDS); +- return GB(_m[t].m3, 0, 4); ++ return GB(GetTile(t)->m3, 0, 4); + } + + /** +@@ -185,7 +185,7 @@ static inline uint GetFieldType(TileIndex t) + static inline void SetFieldType(TileIndex t, uint f) + { + assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete +- SB(_m[t].m3, 0, 4, f); ++ SB(GetTile(t)->m3, 0, 4, f); + } + + /** +@@ -197,7 +197,7 @@ static inline void SetFieldType(TileIndex t, uint f) + static inline IndustryID GetIndustryIndexOfField(TileIndex t) + { + assert(GetClearGround(t) == CLEAR_FIELDS); +- return(IndustryID) _m[t].m2; ++ return(IndustryID) GetTile(t)->m2; + } + + /** +@@ -209,7 +209,7 @@ static inline IndustryID GetIndustryIndexOfField(TileIndex t) + static inline void SetIndustryIndexOfField(TileIndex t, IndustryID i) + { + assert(GetClearGround(t) == CLEAR_FIELDS); +- _m[t].m2 = i; ++ GetTile(t)->m2 = i; + } + + +@@ -225,10 +225,10 @@ static inline uint GetFence(TileIndex t, DiagDirection side) + assert(IsClearGround(t, CLEAR_FIELDS)); + switch (side) { + default: NOT_REACHED(); +- case DIAGDIR_SE: return GB(_m[t].m4, 2, 3); +- case DIAGDIR_SW: return GB(_m[t].m4, 5, 3); +- case DIAGDIR_NE: return GB(_m[t].m3, 5, 3); +- case DIAGDIR_NW: return GB(_me[t].m6, 2, 3); ++ case DIAGDIR_SE: return GB(GetTile(t)->m4, 2, 3); ++ case DIAGDIR_SW: return GB(GetTile(t)->m4, 5, 3); ++ case DIAGDIR_NE: return GB(GetTile(t)->m3, 5, 3); ++ case DIAGDIR_NW: return GB(GetTileEx(t)->m6, 2, 3); + } + } + +@@ -244,10 +244,10 @@ static inline void SetFence(TileIndex t, DiagDirection side, uint h) + assert(IsClearGround(t, CLEAR_FIELDS)); + switch (side) { + default: NOT_REACHED(); +- case DIAGDIR_SE: SB(_m[t].m4, 2, 3, h); break; +- case DIAGDIR_SW: SB(_m[t].m4, 5, 3, h); break; +- case DIAGDIR_NE: SB(_m[t].m3, 5, 3, h); break; +- case DIAGDIR_NW: SB(_me[t].m6, 2, 3, h); break; ++ case DIAGDIR_SE: SB(GetTile(t)->m4, 2, 3, h); break; ++ case DIAGDIR_SW: SB(GetTile(t)->m4, 5, 3, h); break; ++ case DIAGDIR_NE: SB(GetTile(t)->m3, 5, 3, h); break; ++ case DIAGDIR_NW: SB(GetTileEx(t)->m6, 2, 3, h); break; + } + } + +@@ -261,14 +261,14 @@ static inline void SetFence(TileIndex t, DiagDirection side, uint h) + static inline void MakeClear(TileIndex t, ClearGround g, uint density) + { + SetTileType(t, MP_CLEAR); +- _m[t].m1 = 0; ++ GetTile(t)->m1 = 0; + SetTileOwner(t, OWNER_NONE); +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = 0 << 5 | 0 << 2; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0 << 5 | 0 << 2; + SetClearGroundDensity(t, g, density); // Sets m5 +- _me[t].m6 = 0; +- _me[t].m7 = 0; ++ GetTileEx(t)->m6 = 0; ++ GetTileEx(t)->m7 = 0; + } + + +@@ -281,14 +281,14 @@ static inline void MakeClear(TileIndex t, ClearGround g, uint density) + static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) + { + SetTileType(t, MP_CLEAR); +- _m[t].m1 = 0; ++ GetTile(t)->m1 = 0; + SetTileOwner(t, OWNER_NONE); +- _m[t].m2 = industry; +- _m[t].m3 = field_type; +- _m[t].m4 = 0 << 5 | 0 << 2; ++ GetTile(t)->m2 = industry; ++ GetTile(t)->m3 = field_type; ++ GetTile(t)->m4 = 0 << 5 | 0 << 2; + SetClearGroundDensity(t, CLEAR_FIELDS, 3); +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } + + /** +@@ -300,7 +300,7 @@ static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) + static inline void MakeSnow(TileIndex t, uint density = 0) + { + assert(GetClearGround(t) != CLEAR_SNOW); +- SetBit(_m[t].m3, 4); ++ SetBit(GetTile(t)->m3, 4); + if (GetRawClearGround(t) == CLEAR_FIELDS) { + SetClearGroundDensity(t, CLEAR_GRASS, density); + } else { +@@ -316,7 +316,7 @@ static inline void MakeSnow(TileIndex t, uint density = 0) + static inline void ClearSnow(TileIndex t) + { + assert(GetClearGround(t) == CLEAR_SNOW); +- ClrBit(_m[t].m3, 4); ++ ClrBit(GetTile(t)->m3, 4); + SetClearDensity(t, 3); + } + +diff --git a/src/clipboard.cpp b/src/clipboard.cpp +new file mode 100644 +index 0000000..4981a05 +--- /dev/null ++++ b/src/clipboard.cpp +@@ -0,0 +1,293 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard.cpp Implementaion of clipboard related to both copying and pasting. */ ++ ++#include "stdafx.h" ++#include "core/alloc_func.hpp" ++#include "core/mem_func.hpp" ++#include "clipboard_func.h" ++#include "tilearea_type.h" ++#include "station_map.h" ++#include "void_map.h" ++#include "newgrf_airport.h" ++ ++static Map _clipboard_buffers[NUM_CLIPBOARD_BUFFERS]; ++static ClipboardStationList _clipboard_stations[NUM_CLIPBOARD_BUFFERS]; ++ ++/** ++ * Get the list of stations associated to a given clipboard buffer. ++ * @param buffer the buffer ++ * @return the list ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++static ClipboardStationList GetClipboardStationList(Map *buffer) ++{ ++ uint index = GetClipboardBufferIndex(buffer); ++ assert(index < lengthof(_clipboard_stations)); ++ return _clipboard_stations[index]; ++} ++ ++/** ++ * Associate a list of stations to a given clipboard buffer. ++ * @param list the list ++ * @param buffer the buffer ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++static void SetClipboardStationList(ClipboardStationList list, Map *buffer) ++{ ++ uint index = GetClipboardBufferIndex(buffer); ++ assert(index < lengthof(_clipboard_stations)); ++ FreeClipboardStationList(&_clipboard_stations[index]); ++ _clipboard_stations[index] = list; ++} ++ ++/** ++ * Free a list of clipboard stations. ++ * @param list the list ++ */ ++void FreeClipboardStationList(ClipboardStationList *list) ++{ ++ for (ClipboardStation *item = *list, *next; item != NULL; item = next) { ++ next = item->next; ++ delete item; ++ } ++ *list = NULL; ++} ++ ++/** ++ * Test whether a given #Map is a clipboard buffer. ++ * @return if the map a clipboard buffer ++ */ ++bool IsClipboardBuffer(const Map *map) ++{ ++ return (size_t)(map - _clipboard_buffers) < NUM_CLIPBOARD_BUFFERS; ++} ++ ++/** ++ * Get a clipboard buffer by it's index. ++ * @param index the index ++ * @return the buffer ++ * ++ * @pre index < NUM_CLIPBOARD_BUFFERS ++ */ ++Map *GetClipboardBuffer(uint index) ++{ ++ assert(index < NUM_CLIPBOARD_BUFFERS); ++ return &_clipboard_buffers[index]; ++} ++ ++/** ++ * Get the index of a clipboard buffer. ++ * @param buffer the buffer ++ * @return the index ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++uint GetClipboardBufferIndex(const Map *buffer) ++{ ++ assert(IsClipboardBuffer(buffer)); ++ return buffer - _clipboard_buffers; ++} ++ ++/** ++ * Test if a clipboard buffer is empty. ++ * @param buffer the buffer ++ * @return true iff there is no content in the buffer ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++bool IsClipboardBufferEmpty(const Map *buffer) ++{ ++ assert(IsClipboardBuffer(buffer)); ++ return buffer->m == NULL; ++}; ++ ++/** ++ * Clear content of a clipboard buffer. ++ * @param buffer the buffer ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++void EmptyClipboardBuffer(Map *buffer) ++{ ++ if (IsClipboardBufferEmpty(buffer)) return; ++ ++ SetClipboardStationList(NULL, buffer); ++ ++ buffer->size_x = 0; ++ buffer->size_y = 0; ++ buffer->size = 0; ++ ++ free(buffer->m); ++ buffer->m = NULL; ++ free(buffer->me); ++ buffer->me = NULL; ++} ++ ++/** ++ * Allocate space in a clipboard buffer. ++ * @param buffer the buffer ++ * @param content_size_x X size of the content (excluding MP_VOID tiles on southern borders) ++ * @param content_size_y Y size of the content (excluding MP_VOID tiles on southern borders) ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++void AllocateClipboardBuffer(Map *buffer, uint content_size_x, uint content_size_y) ++{ ++ assert(IsClipboardBuffer(buffer)); ++ assert(IsInsideMM(content_size_x, 1, INT_MAX - 1)); ++ assert(IsInsideMM(content_size_y, 1, INT_MAX - 1)); ++ ++ SetClipboardStationList(NULL, buffer); ++ ++ buffer->size_x = content_size_x + 1; ++ buffer->size_y = content_size_y + 1; ++ buffer->size = buffer->size_x * buffer->size_y; ++ ++ free(buffer->m); ++ free(buffer->me); ++ buffer->m = CallocT<Tile>(buffer->size); ++ buffer->me = CallocT<TileExtended>(buffer->size); ++ ++ GENERIC_TILE_AREA_LOOP(iter, GenericTileArea(TileXY(buffer->size_x - 1, 0, buffer), 1, buffer->size_y)) { ++ MakeVoid(iter); ++ } ++ GENERIC_TILE_AREA_LOOP(iter, GenericTileArea(TileXY(0, buffer->size_y - 1, buffer), buffer->size_x - 1, 1)) { ++ MakeVoid(iter); ++ } ++} ++ ++/** ++ * Get #ClipboardStation by a given ID. ++ * @param id the ID of the station ++ * @param buffer clipboard buffer to get the station from ++ * ++ * @pre IsClipboardBuffer(buffer) ++ */ ++/* static */ ClipboardStation *ClipboardStation::Get(StationID id, Map *buffer) ++{ ++ for (ClipboardStation *ret = GetClipboardStationList(buffer); ret != NULL; ret = ret->next) { ++ if (ret->id == id) return ret; ++ } ++ return NULL; ++} ++ ++/** ++ * Get #ClipboardStation by a given tile. ++ * @param tile any tile that belongs to the station ++ * @return station pointer or NULL if the tile is not a station ++ * ++ * @pre IsClipboardBuffer(MapOf(tile)) ++ */ ++/* static */ ClipboardStation *ClipboardStation::GetByTile(GenericTileIndex tile) ++{ ++ return ClipboardStation::Get(GetStationIndex(tile), MapOf(tile)); ++} ++ ++ClipboardStation::ClipboardStation() ++{ ++ this->id = INVALID_STATION; ++ this->airport.tile = INVALID_TILE_INDEX; ++ this->airport.w = 0; ++ this->airport.h = 0; ++ this->airport.type = AT_INVALID; ++ this->airport.layout = 0; ++ this->num_specs = 0; ++ this->speclist = NULL; ++ this->next = NULL; ++} ++ ++ClipboardStation::~ClipboardStation() ++{ ++ free(this->speclist); ++} ++ ++ClipboardStation **ClipboardStationsBuilder::FindStation(StationID sid) ++{ ++ ClipboardStation **ret = &this->stations; ++ while (*ret != NULL) { ++ if ((*ret)->id == sid) break; ++ ret = &((*ret)->next); ++ } ++ return ret; ++} ++ ++ClipboardStation *ClipboardStationsBuilder::AddStation(StationID sid) ++{ ++ ClipboardStation **st_link = this->FindStation(sid); ++ ClipboardStation *st = *st_link; ++ if (st == NULL) { ++ st = new ClipboardStation; ++ st->id = sid; ++ *st_link = st; // put new item on the back of the list ++ } ++ return st; ++} ++ ++void ClipboardStationsBuilder::AddSpecToStation(ClipboardStation *st, StationClassID station_class, byte station_type, byte specindex) ++{ ++ assert(specindex != 0 || (station_type == 0 && (station_class == STAT_CLASS_DFLT || station_class == STAT_CLASS_WAYP))); ++ ++ if (specindex >= st->num_specs) { ++ /* Add "empty" placeholders. */ ++ st->speclist = ReallocT(st->speclist, specindex + 1); ++ for (int i = st->num_specs; i < specindex; i++) { ++ st->speclist[i].stat_class = STAT_CLASS_DFLT; ++ st->speclist[i].stat_type = 0; ++ } ++ st->num_specs = specindex + 1; ++ } else { ++ /* We can override an "empty" placeholder, but if the spec was added before, it shouldn't change. */ ++ assert((st->speclist[specindex].stat_class == station_class && st->speclist[specindex].stat_type == station_type) || ++ (st->speclist[specindex].stat_class == STAT_CLASS_DFLT && st->speclist[specindex].stat_type == 0)); ++ } ++ st->speclist[specindex].stat_class = station_class; ++ st->speclist[specindex].stat_type = station_type; ++} ++ ++/** ++ * Add an airport part. ++ * ++ * @param sid id of the station ++ * @param tile northern tile of the airport ++ * @param type airport type ++ * @param layout airport layout ++ */ ++void ClipboardStationsBuilder::AddAirportPart(StationID sid, RawTileIndex tile, AirportTypes type, byte layout) ++{ ++ ClipboardStation *st = this->AddStation(sid); ++ ++ assert(st->airport.type == AT_INVALID); // single airport per station! ++ const AirportSpec *spec = AirportSpec::Get(type); ++ st->airport.tile = tile; ++ if (spec->rotation[layout] != DIR_E && spec->rotation[layout] != DIR_W) { ++ st->airport.w = spec->size_x; ++ st->airport.h = spec->size_y; ++ } else { ++ st->airport.w = spec->size_y; ++ st->airport.h = spec->size_x; ++ } ++ st->airport.type = type; ++ st->airport.layout = layout; ++} ++ ++/** ++ * Finish building and store results. ++ * @param buffer clipboard buffer to store the list in ++ * ++ * @pre IsClipboardBuffer(MapOf(tile)) ++ */ ++void ClipboardStationsBuilder::BuildDone(Map *buffer) ++{ ++ SetClipboardStationList(this->stations, buffer); ++ this->stations = NULL; ++} +diff --git a/src/clipboard_func.h b/src/clipboard_func.h +new file mode 100644 +index 0000000..63582ce +--- /dev/null ++++ b/src/clipboard_func.h +@@ -0,0 +1,73 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_func.h Functions related to the clipboad. */ ++ ++#ifndef CLIPBOARD_FUNC_H ++#define CLIPBOARD_FUNC_H ++ ++#include "clipboard_type.h" ++ ++void FreeClipboardStationList(ClipboardStationList *list); ++ ++/** Helper class to build a station list while copying to the clipboard. */ ++class ClipboardStationsBuilder { ++protected: ++ ClipboardStationList stations; ///< the list of stations ++ ++ ClipboardStation **FindStation(StationID sid); ++ ClipboardStation *AddStation(StationID sid); ++ void AddSpecToStation(ClipboardStation *st, StationClassID station_class, byte station_type, byte specindex); ++ ++public: ++ ClipboardStationsBuilder() : stations(NULL) ++ { } ++ ++ ~ClipboardStationsBuilder() ++ { ++ FreeClipboardStationList(&this->stations); ++ } ++ ++ /** ++ * Add a "simple" station part (bus/truck/dock/buoy). ++ * @param sid id of the station ++ */ ++ inline void AddPart(StationID sid) ++ { ++ this->AddStation(sid); ++ } ++ ++ /** ++ * Add a rail station/waypoint part. ++ * @param sid id of the station ++ * @param station_class custom station class ++ * @param station_type type within the custom station class ++ * @param specindex index of the given station spec in the list of specs of this station (aka custom station spec index) ++ */ ++ inline void AddRailPart(StationID sid, StationClassID station_class, byte station_type, byte specindex) ++ { ++ this->AddSpecToStation(this->AddStation(sid), station_class, station_type, specindex); ++ } ++ ++ void AddAirportPart(StationID sid, RawTileIndex tile, AirportTypes type, byte layout); ++ ++ void BuildDone(Map *clipboard); ++}; ++ ++static const uint NUM_CLIPBOARD_BUFFERS = 5; ///< Total amount of clipboard buffers ++ ++bool IsClipboardBuffer(const Map *map); ++Map *GetClipboardBuffer(uint index); ++uint GetClipboardBufferIndex(const Map *clipboard); ++void AllocateClipboardBuffer(Map *clipboard, uint size_x, uint size_y); ++bool IsClipboardBufferEmpty(const Map *clipboard); ++void EmptyClipboardBuffer(Map *clipboard); ++void ClearClipboard(); ++ ++#endif /* CLIPBOARD_FUNC_H */ +diff --git a/src/clipboard_gui.cpp b/src/clipboard_gui.cpp +new file mode 100644 +index 0000000..50cdc96 +--- /dev/null ++++ b/src/clipboard_gui.cpp +@@ -0,0 +1,730 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_gui.cpp GUIs related to the clipboard. */ ++ ++#include "stdafx.h" ++#include "core/geometry_func.hpp" ++#include "network/network.h" ++#include "widgets/clipboard_widget.h" ++#include "clipboard_func.h" ++#include "clipboard_gui.h" ++#include "command_func.h" ++#include "company_func.h" ++#include "company_base.h" ++#include "copypaste_cmd.h" ++#include "direction_func.h" ++#include "error.h" ++#include "gfx_func.h" ++#include "gui.h" ++#include "hotkeys.h" ++#include "rail.h" ++#include "rail_gui.h" ++#include "rail_map.h" ++#include "road_map.h" ++#include "slope_func.h" ++#include "sound_func.h" ++#include "station_map.h" ++#include "strings_func.h" ++#include "terraform_gui.h" ++#include "tilearea_type.h" ++#include "tilehighlight_func.h" ++#include "track_func.h" ++#include "tunnelbridge_map.h" ++#include "viewport_func.h" ++#include "window_gui.h" ++#include "window_func.h" ++ ++#include "table/sprites.h" ++#include "table/strings.h" ++ ++static const int CLIPBOARD_ADDITIONAL_HEIGHT_MAX = 7; ++static const int CLIPBOARD_ADDITIONAL_HEIGHT_MIN = -7; ++static const uint NUM_USER_CLIPBOARDS = NUM_CLIPBOARD_BUFFERS - 1; ///< Number of clipboards available ++ ++/** Clipboard parameters. */ ++struct ClipboardProps { ++ TileArea copy_area; ///< Area on the main map selected as source of a copy operation ++ CopyPasteMode mode; ///< Various flags that will be applied when pasting ++ RailType railtype; ///< #Railtype to convert to when pasting ++ DirTransformation transformation; ///< Rotation/reflection to apply when pasting ++ int additional_height_delta; ///< Additional amount of tile heights to add when pasting ++ ++ ClipboardProps() ++ : copy_area(INVALID_TILE, 0, 0) ++ , mode(CPM_DEFAULT) ++ , railtype(INVALID_RAILTYPE) ++ , transformation(DTR_IDENTITY) ++ , additional_height_delta(0) ++ { } ++}; ++ ++ClipboardProps _clipboard_props[NUM_USER_CLIPBOARDS]; ///< Clipboard parameters selected via GUI ++ClipboardProps *_current_clipboard = &_clipboard_props[0]; ///< Currently selected clipboard ++static TileArea _clipboard_paste_area = TileArea(INVALID_TILE, 0, 0); ///< Area on the main map selected as destination for a paste operation ++ ++/** Clear entire clipboard. */ ++void ClearClipboard() ++{ ++ for (uint i = 0; i < NUM_CLIPBOARD_BUFFERS; i++) EmptyClipboardBuffer(GetClipboardBuffer(i)); ++ for (uint i = 0; i < NUM_USER_CLIPBOARDS; i++) _clipboard_props[i].copy_area = TileArea(INVALID_TILE, 0, 0); ++} ++ ++/** ++ * Whether the copy/paste operations are performed with the clipboard buffer, or instantantly. ++ * ++ * If true, clipboard buffer is on. Each "copy" user action moves selected area to the clipboard ++ * (to the buffer) and each "paste" tries to reproduce contents of the clipboard on the main map. ++ * ++ * If false, clipboard buffer is off. "copy" user action just selects area and ++ * "paste" makes an instant copy&paste from the selected area to pointed place. ++ * ++ * @return whether the clipboard buffer is available for local company ++ */ ++static inline bool IsClipboardBufferOn() { return !_networking; } ++ ++static inline Map *GetCurrentClipboardBuffer() ++{ ++ return IsClipboardBufferOn() ? GetClipboardBuffer(_current_clipboard - _clipboard_props) : NULL; ++} ++ ++static inline bool IsClipboardCopyAreaSelected() ++{ ++ return _current_clipboard->copy_area.tile != INVALID_TILE; ++} ++ ++static inline bool IsClipboardPasteSourceSet() ++{ ++ return IsClipboardBufferOn() ? !IsClipboardBufferEmpty(GetCurrentClipboardBuffer()) : IsClipboardCopyAreaSelected(); ++} ++ ++static void ClipboardRecalcPasteAreaSize() ++{ ++ assert(IsClipboardPasteSourceSet()); ++ ++ Dimension size; ++ if (IsClipboardBufferOn()) { ++ size.width = GetCurrentClipboardBuffer()->size_x - 1; ++ size.height = GetCurrentClipboardBuffer()->size_y - 1; ++ } else { ++ size.width = _current_clipboard->copy_area.w; ++ size.height = _current_clipboard->copy_area.h; ++ } ++ size = TransformDimension(size, _current_clipboard->transformation); ++ ++ _clipboard_paste_area.w = size.width; ++ _clipboard_paste_area.h = size.height; ++} ++ ++void CcPaste(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) ++{ ++ if (_paste_err_tile != INVALID_TILE) SetRedErrorSquare(_paste_err_tile); ++ ++ if (result.Succeeded()) { ++ if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile); ++ if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); ++ } ++} ++ ++void GetTilePastePreview(TileIndex tile, TilePastePreview *ret) ++{ ++ _clipboard_paste_area.tile = TileVirtXY(_thd.pos.x, _thd.pos.y); ++ ++ extern bool TestRailTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company, TileContentPastePreview *preview); ++ extern bool TestRoadTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company, TileContentPastePreview *preview); ++ extern bool TestWaterTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *object_rect, CompanyID company, TileContentPastePreview *preview); ++ extern bool TestTunnelBridgeTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileIndex *other_end, CompanyID company, TileContentPastePreview *preview); ++ extern bool TestStationTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *station_part_area, CompanyID company, TileContentPastePreview *preview); ++ ++ Map *clipboard = GetCurrentClipboardBuffer(); ++ ++ /* the area we are copying from */ ++ GenericTileArea src_area = IsClipboardBufferOn() ? ++ GenericTileArea(TileXY(0, 0, clipboard), clipboard->size_x - 1, clipboard->size_y - 1) : ++ GenericTileArea(_current_clipboard->copy_area); ++ ++ DirTransformation inv_dtr = InvertDirTransform(_current_clipboard->transformation); ++ /* area containing all tile corners (also those at SW and SE borders) */ ++ TileArea paste_area_corners(_clipboard_paste_area.tile, _clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); ++ /* source corner of the most norther corner */ ++ GenericTileIndex src_of_north_corner = paste_area_corners.TransformedNorth(src_area.tile, inv_dtr); ++ /* source corner of the tile corner (source of it's height) */ ++ GenericTileIndex src_of_tile_corner = paste_area_corners.TransformTile(tile, src_of_north_corner, inv_dtr); ++ /* calculate the height difference between areas */ ++ int height_delta = TileHeight(paste_area_corners.tile) - TileHeight(src_of_north_corner) + _current_clipboard->additional_height_delta; ++ ++ if (_clipboard_paste_area.Contains(tile)) { ++ /* source tile of the tile */ ++ GenericTileIndex src_tile = _clipboard_paste_area.TransformTile(tile, _clipboard_paste_area.TransformedNorth(src_area.tile, inv_dtr), inv_dtr); ++ ++ bool has_preview = false; ++ switch(GetTileType(src_tile)) { ++ case MP_RAILWAY: has_preview = TestRailTileCopyability(src_tile, _current_clipboard->mode, _local_company, ret); break; ++ case MP_ROAD: has_preview = TestRoadTileCopyability(src_tile, _current_clipboard->mode, _local_company, ret); break; ++ case MP_STATION: has_preview = TestStationTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; ++ case MP_WATER: has_preview = TestWaterTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; ++ case MP_TUNNELBRIDGE: has_preview = TestTunnelBridgeTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; ++ default: MemSetT(ret, 0); break; ++ } ++ ++ if (has_preview) ret->highlight_track_bits = TransformTrackBits(ret->highlight_track_bits, _current_clipboard->transformation); ++ } else { ++ assert(paste_area_corners.Contains(tile)); ++ MemSetT(ret, 0); ++ } ++ ++ ret->tile_height = TileHeight(src_of_tile_corner) + height_delta; ++} ++ ++struct ClipboardToolbarWindow : Window { ++ static HotkeyList hotkeys; ++ ++ static CopyPasteMode FlagButtonToFlagBit(int button) ++ { ++ switch (button) { ++ case WID_CT_WITH_RAIL: return CPM_WITH_RAIL_TRANSPORT; ++ case WID_CT_WITH_ROAD: return CPM_WITH_ROAD_TRANSPORT; ++ case WID_CT_WITH_WATER: return CPM_WITH_WATER_TRANSPORT; ++ case WID_CT_WITH_AIR: return CPM_WITH_AIR_TRANSPORT; ++ case WID_CT_MIRROR_SIGNALS: return CPM_MIRROR_SIGNALS; ++ case WID_CT_UPGRADE_BRIDGES: return CPM_UPGRADE_BRIDGES; ++ case WID_CT_WITH_STATIONS: return CPM_WITH_STATIONS; ++ default: NOT_REACHED(); break; ++ }; ++ return CPM_NONE; ++ } ++ ++ ClipboardToolbarWindow(WindowDesc *desc) : Window(desc) ++ { ++ this->InitNested(); ++ ++ if (!IsClipboardBufferOn()) { ++ NWidgetCore *button = this->GetWidget<NWidgetCore>(WID_CT_COPY); ++ button->widget_data = SPR_IMG_CLIPBOARD_SELECT_COPY_AREA; // instead of SPR_IMG_CLIPBOARD_COPY ++ button->tool_tip = STR_CLIPBOARD_TOOLTIP_SELECT_COPY_AREA; // instead of STR_CLIPBOARD_TOOLTIP_COPY ++ ++ button = this->GetWidget<NWidgetCore>(WID_CT_PASTE); ++ button->widget_data = SPR_IMG_CLIPBOARD_INSTANT_COPY_PASTE; // instead of SPR_IMG_CLIPBOARD_PASTE ++ button->tool_tip = STR_CLIPBOARD_TOOLTIP_INSTANT_COPY_PASTE; // instead of STR_CLIPBOARD_TOOLTIP_PASTE ++ } ++ ++ /* select another railtype if the one that was used last time is invalid/unavailable */ ++ for (uint i = 0; i < lengthof(_clipboard_props); i++) { ++ if (!IsInsideMM(_clipboard_props[i].railtype, RAILTYPE_BEGIN, RAILTYPE_END)) { ++ _clipboard_props[i].railtype = RAILTYPE_BEGIN; ++ } ++ RailType rt = _clipboard_props[i].railtype; ++ while (!HasRailtypeAvail(_local_company, rt)) { ++ rt++; ++ if (rt >= RAILTYPE_END) rt = RAILTYPE_BEGIN; ++ ++ if (rt == _clipboard_props[i].railtype) { // did we get back to the point where we started? ++ rt = INVALID_RAILTYPE; ++ _clipboard_props[i].mode &= ~CPM_CONVERT_RAILTYPE; ++ break; ++ } ++ } ++ _clipboard_props[i].railtype = rt; ++ } ++ ++ this->UpdateButtons(); ++ ++ if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); ++ } ++ ++ ~ClipboardToolbarWindow() ++ { ++ if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); ++ } ++ ++ void UpdateButtons() ++ { ++ /* lower clipboard index indicator */ ++ this->SetWidgetLoweredState(WID_CT_CLIPBOARD_1, _current_clipboard == &_clipboard_props[0]); ++ this->SetWidgetLoweredState(WID_CT_CLIPBOARD_2, _current_clipboard == &_clipboard_props[1]); ++ this->SetWidgetLoweredState(WID_CT_CLIPBOARD_3, _current_clipboard == &_clipboard_props[2]); ++ this->SetWidgetLoweredState(WID_CT_CLIPBOARD_4, _current_clipboard == &_clipboard_props[3]); ++ /* disable the paste button if there is nothing to paste */ ++ this->SetWidgetDisabledState(WID_CT_PASTE, !IsClipboardPasteSourceSet()); ++ /* lower on/off buttons */ ++ for (int widget = WID_CT_PASTE_FLAG_BUTTON_BEGIN; widget < WID_CT_PASTE_FLAG_BUTTON_END; widget++) { ++ this->SetWidgetLoweredState(widget, (_current_clipboard->mode & ClipboardToolbarWindow::FlagButtonToFlagBit(widget)) != 0); ++ } ++ this->SetWidgetLoweredState(WID_CT_TERRAFORM, (_current_clipboard->mode & CPM_TERRAFORM_MASK) != CPM_TERRAFORM_NONE); ++ /* set the sprite on the railtype button */ ++ this->GetWidget<NWidgetCore>(WID_CT_CONVERT_RAILTYPE)->widget_data = ++ (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? ++ GetRailTypeInfo(_current_clipboard->railtype)->gui_sprites.convert_rail : ++ SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION; ++ ++ this->SetDirty(); ++ } ++ ++ virtual void DrawWidget(const Rect &r, int widget) const ++ { ++ int offset = this->IsWidgetLowered(widget) ? 2 : 1; ++ switch (widget) { ++ case WID_CT_WITH_RAIL: ++ case WID_CT_WITH_ROAD: ++ case WID_CT_WITH_WATER: ++ case WID_CT_WITH_AIR: { ++ offset++; ++ DrawSprite(SPR_BLOT, this->IsWidgetLowered(widget) ? PALETTE_TO_GREEN : PALETTE_TO_RED, r.left + offset, r.top + offset); ++ break; ++ } ++ ++ case WID_CT_TERRAFORM: { ++ offset++; ++ PaletteID pal; ++ switch (_current_clipboard->mode & CPM_TERRAFORM_MASK) { ++ case CPM_TERRAFORM_FULL: pal = PALETTE_TO_GREEN; break; ++ case CPM_TERRAFORM_MINIMAL: pal = PALETTE_TO_YELLOW; break; ++ default: pal = PALETTE_TO_RED; break; ++ } ++ DrawSprite(SPR_BLOT, pal, r.left + offset, r.top + offset); ++ break; ++ } ++ ++ case WID_CT_TRANSFORMATION: ++ DrawSprite(SPR_IMG_TRANFORMATION_IDENTITY + _current_clipboard->transformation, PAL_NONE, r.left + offset, r.top + offset); ++ break; ++ ++ case WID_CT_HEIGHT_DIFF_GLYPH: ++ DrawSprite(SPR_IMG_CLIPBOARD_HEIGHT_PANEL, PAL_NONE, r.left, r.top); ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) ++ { ++ Dimension d; ++ ++ switch (widget) { ++ case WID_CT_CLIPBOARD_1: ++ case WID_CT_CLIPBOARD_2: ++ case WID_CT_CLIPBOARD_3: ++ case WID_CT_CLIPBOARD_4: ++ d.width = GetDigitWidth() + 4; ++ d.height = FONT_HEIGHT_NORMAL; ++ break; ++ ++ case WID_CT_HEIGHT_DIFF_GLYPH: ++ d = GetSpriteSize(SPR_IMG_CLIPBOARD_HEIGHT_PANEL); ++ break; ++ ++ case WID_CT_HEIGHT_DIFF: { ++ /* Backup the height delta. The variable will be used to calculate the size of the widget. */ ++ int backup = _current_clipboard->additional_height_delta; ++ /* calculate the size */ ++ d.width = d.height = 0; ++ for (_current_clipboard->additional_height_delta = CLIPBOARD_ADDITIONAL_HEIGHT_MIN; _current_clipboard->additional_height_delta <= CLIPBOARD_ADDITIONAL_HEIGHT_MAX; _current_clipboard->additional_height_delta++) { ++ this->SetStringParameters(WID_CT_HEIGHT_DIFF); // additional_height_delta will be used there ++ d = maxdim(d, GetStringBoundingBox(this->GetWidget<NWidgetCore>(WID_CT_HEIGHT_DIFF)->widget_data)); ++ } ++ d.width += 1; ++ /* restore */ ++ _current_clipboard->additional_height_delta = backup; ++ break; ++ } ++ ++ default: ++ return; ++ } ++ ++ d.width += padding.width; ++ d.height += padding.height; ++ *size = maxdim(*size, d); ++ } ++ ++ virtual void SetStringParameters(int widget) const ++ { ++ switch (widget) { ++ case WID_CT_CLIPBOARD_1: ++ case WID_CT_CLIPBOARD_2: ++ case WID_CT_CLIPBOARD_3: ++ case WID_CT_CLIPBOARD_4: ++ SetDParam(0, widget - WID_CT_CLIPBOARD_1 + 1); ++ break; ++ ++ case WID_CT_HEIGHT_DIFF: ++ SetDParam(0, (StringID)(STR_CLIPBOARD_HEIGHT_DIFF_NEUTRAL + sgn(_current_clipboard->additional_height_delta))); ++ SetDParam(1, abs(_current_clipboard->additional_height_delta)); ++ break; ++ } ++ } ++ ++ virtual void OnClick(Point pt, int widget, int click_count) ++ { ++ if (this->IsWidgetDisabled(widget)) return; ++ ++ DirTransformation add_clipboard_transformation = DTR_IDENTITY; // additional transformation ++ ++ switch (widget) { ++ case WID_CT_CLIPBOARD_1: ++ case WID_CT_CLIPBOARD_2: ++ case WID_CT_CLIPBOARD_3: ++ case WID_CT_CLIPBOARD_4: ++ /* switch to another clipboard */ ++ assert(IsInsideMM(widget - WID_CT_CLIPBOARD_1, 0, lengthof(_clipboard_props))); ++ _current_clipboard = &_clipboard_props[widget - WID_CT_CLIPBOARD_1]; ++ this->UpdateButtons(); ++ ++ if (this->IsWidgetLowered(WID_CT_PASTE)) { ++ if (IsClipboardPasteSourceSet()) { ++ /* update paste preview */ ++ ClipboardRecalcPasteAreaSize(); ++ SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); ++ UpdateTileSelection(); ++ MarkWholeScreenDirty(); ++ } else { ++ ResetObjectToPlace(); // current clipboard is empty! ++ } ++ } ++ break; ++ ++ case WID_CT_COPY: ++ if (HandlePlacePushButton(this, widget, SPR_CURSOR_COPY, HT_RECT)) { ++ this->SetWidgetDirty(widget); ++ } ++ return; ++ ++ case WID_CT_PASTE: ++ if (HandlePlacePushButton(this, widget, _ctrl_pressed ? SPR_CURSOR_ADJUST_HEIGHT : SPR_CURSOR_PASTE, HT_POINT | HT_PASTE_PREVIEW)) { ++ ClipboardRecalcPasteAreaSize(); ++ SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); ++ this->SetWidgetDirty(widget); ++ } ++ return; ++ ++ case WID_CT_TERRAFORM: { ++ switch (_current_clipboard->mode & CPM_TERRAFORM_MASK) { ++ case CPM_TERRAFORM_NONE: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_FULL; break; ++ case CPM_TERRAFORM_MINIMAL: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_NONE; break; ++ case CPM_TERRAFORM_FULL: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_MINIMAL; break; ++ default: NOT_REACHED(); ++ } ++ this->UpdateButtons(); ++ break; ++ } ++ ++ case WID_CT_TRANSFORMATION: ++ /* reset transformation - combined with its inversion will give identity */ ++ add_clipboard_transformation = InvertDirTransform(_current_clipboard->transformation); ++ break; ++ ++ case WID_CT_ROTATE_LEFT: add_clipboard_transformation = DTR_ROTATE_90_L; break; ++ case WID_CT_ROTATE_RIGHT: add_clipboard_transformation = DTR_ROTATE_90_R; break; ++ case WID_CT_REFLECT_NE_SW: add_clipboard_transformation = DTR_REFLECT_NE_SW; break; ++ case WID_CT_REFLECT_NW_SE: add_clipboard_transformation = DTR_REFLECT_NW_SE; break; ++ ++ case WID_CT_WITH_RAIL: ++ case WID_CT_WITH_ROAD: ++ case WID_CT_WITH_WATER: ++ case WID_CT_WITH_AIR: ++ case WID_CT_MIRROR_SIGNALS: ++ case WID_CT_UPGRADE_BRIDGES: ++ case WID_CT_WITH_STATIONS: ++ _current_clipboard->mode ^= ClipboardToolbarWindow::FlagButtonToFlagBit(widget); ++ this->UpdateButtons(); ++ break; ++ ++ case WID_CT_CONVERT_RAILTYPE: ++ ShowDropDownList(this, GetRailTypeDropDownList(), ++ (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? INVALID_RAILTYPE : _current_clipboard->railtype, ++ WID_CT_CONVERT_RAILTYPE, 140, true, true); ++ break; ++ ++ case WID_CT_HEIGHT_DIFF_INCREASE: this->ModifyAdditionalHeightDelta(+1); break; ++ case WID_CT_HEIGHT_DIFF_DECREASE: this->ModifyAdditionalHeightDelta(-1); break; ++ ++ default: ++ return; ++ } ++ ++ if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); ++ ++ if (add_clipboard_transformation != DTR_IDENTITY) { ++ _current_clipboard->transformation = CombineDirTransform(_current_clipboard->transformation, add_clipboard_transformation); ++ this->SetWidgetDirty(WID_CT_TRANSFORMATION); ++ if (this->IsWidgetLowered(WID_CT_PASTE)) { ++ ClipboardRecalcPasteAreaSize(); ++ SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); ++ } ++ } ++ } ++ ++ virtual EventState OnHotkey(int hotkey) ++ { ++ switch (hotkey) { ++ case WID_CT_CONVERT_RAILTYPE: ++ this->OnDropdownSelect(WID_CT_CONVERT_RAILTYPE, (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? INVALID_RAILTYPE : _current_clipboard->railtype); ++ this->SetWidgetDirty(WID_CT_CONVERT_RAILTYPE); ++ if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); ++ return ES_HANDLED; ++ ++ case WID_CT_WITH_RAIL: ++ case WID_CT_WITH_ROAD: ++ case WID_CT_WITH_WATER: ++ case WID_CT_WITH_AIR: ++ case WID_CT_TERRAFORM: ++ case WID_CT_WITH_STATIONS: ++ if (this->IsWidgetLowered(WID_CT_PASTE)) MarkWholeScreenDirty(); // redraw tile selection ++ break; ++ ++ default: ++ break; ++ } ++ ++ return this->Window::OnHotkey(hotkey); ++ } ++ ++ virtual void OnDropdownSelect(int widget, int index) ++ { ++ assert(widget == WID_CT_CONVERT_RAILTYPE); ++ if (index == INVALID_RAILTYPE) { ++ _current_clipboard->mode &= ~CPM_CONVERT_RAILTYPE; ++ } else { ++ _current_clipboard->mode |= CPM_CONVERT_RAILTYPE; ++ _current_clipboard->railtype = (RailType)index; ++ } ++ this->UpdateButtons(); ++ } ++ ++ virtual EventState OnCTRLStateChange() ++ { ++ if (this->IsWidgetLowered(WID_CT_PASTE)) SetMouseCursor(_ctrl_pressed ? SPR_CURSOR_ADJUST_HEIGHT : SPR_CURSOR_PASTE, PAL_NONE); ++ ++ return ES_NOT_HANDLED; ++ } ++ ++ virtual void OnPlaceObject(Point pt, TileIndex tile) ++ { ++ if (this->IsWidgetLowered(WID_CT_COPY)) { ++ /* start copy area dragging */ ++ VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_COPY_TO_CLIPBOARD); ++ VpSetPlaceSizingLimit(_settings_game.construction.clipboard_capacity); ++ } else { ++ _clipboard_paste_area.tile = tile; ++ ++ /* do paste */ ++ assert(IsClipboardPasteSourceSet()); ++ ++ uint32 p1 = 0, p2 = 0; ++ SB(p1, 28, 4, _current_clipboard->railtype); ++ SB(p2, 12, 4, _current_clipboard->additional_height_delta); ++ SB(p2, 16, 3, _current_clipboard->transformation); ++ SB(p2, 19, 10, _current_clipboard->mode); ++ if (IsClipboardBufferOn()) { ++ /* copy/paste clipboard-to-map */ ++ SB(p1, 0, 2, GetClipboardBufferIndex(GetCurrentClipboardBuffer())); ++ SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, STR_ERROR_CAN_T_PASTE_HERE); ++ DoCommandP(tile, p1, p2, CMD_PASTE_FROM_CLIPBOARD | CMD_MSG(STR_COPY_PASTE_ERROR_SUMMARY), CcPaste); ++ } else { ++ /* copy/paste map-to-map */ ++ SB(p1, 0, 28, _current_clipboard->copy_area.tile); ++ SB(p2, 0, 6, _current_clipboard->copy_area.w); ++ SB(p2, 6, 6, _current_clipboard->copy_area.h); ++ SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, STR_ERROR_CAN_T_PASTE_HERE); ++ DoCommandP(tile, p1, p2, CMD_INSTANT_COPY_PASTE | CMD_MSG(STR_COPY_PASTE_ERROR_SUMMARY), CcPaste); ++ } ++ ++ MarkWholeScreenDirty(); // redraw tile selection ++ } ++ } ++ ++ virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) ++ { ++ VpSelectTilesWithMethod(pt.x, pt.y, select_method); ++ } ++ ++ virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) ++ { ++ if (pt.x != -1) { ++ switch (select_proc) { ++ case DDSP_COPY_TO_CLIPBOARD: { ++ TileArea ta = TileArea(start_tile, end_tile); ++ ++ /* do copy */ ++ if (IsClipboardBufferOn()) { ++ /* copy into the buffer */ ++ uint32 p1 = 0, p2 = 0; ++ SB(p1, 0, 2, GetClipboardBufferIndex(GetCurrentClipboardBuffer())); ++ SB(p2, 0, 6, ta.w); // source area width ++ SB(p2, 6, 6, ta.h); // source area height ++ if (!DoCommandP(ta.tile, p1, p2, CMD_COPY_TO_CLIPBOARD) || _shift_pressed) return; // leave copy tool opened ++ } ++ ResetObjectToPlace(); ++ ++ /* select copy area */ ++ _current_clipboard->copy_area = ta; ++ ++ /* reset transformation and update buttons */ ++ _current_clipboard->transformation = DTR_IDENTITY; ++ this->ModifyAdditionalHeightDelta(-_current_clipboard->additional_height_delta); ++ this->UpdateButtons(); ++ break; ++ } ++ ++ default: ++ NOT_REACHED(); ++ } ++ } ++ } ++ ++ virtual void OnPlaceObjectAbort() ++ { ++ /* Unclick "copy" and "paste" buttons */ ++ this->RaiseWidget(WID_CT_COPY); ++ this->RaiseWidget(WID_CT_PASTE); ++ this->SetWidgetDirty(WID_CT_COPY); ++ this->SetWidgetDirty(WID_CT_PASTE); ++ } ++ ++ EventState OnPlaceMouseWheel(Point pt, int mousewheel) ++ { ++ if (mousewheel == 0 || !_ctrl_pressed || !this->IsWidgetLowered(WID_CT_PASTE)) return ES_NOT_HANDLED; ++ this->ModifyAdditionalHeightDelta(-sgn(mousewheel)); ++ return ES_HANDLED; ++ } ++ ++ void ModifyAdditionalHeightDelta(int diff) ++ { ++ _current_clipboard->additional_height_delta = Clamp(_current_clipboard->additional_height_delta + diff, CLIPBOARD_ADDITIONAL_HEIGHT_MIN, CLIPBOARD_ADDITIONAL_HEIGHT_MAX); ++ this->SetWidgetDirty(WID_CT_HEIGHT_DIFF); ++ } ++}; ++ ++EventState ClipboardGlobalHotkeys(int hotkey) ++{ ++ Window *w = ShowClipboardToolbar(); ++ if (w == NULL) return ES_NOT_HANDLED; ++ return w->OnHotkey(hotkey); ++} ++ ++static Hotkey _clipboard_hotkeys[] = { ++ Hotkey('C' | WKC_CTRL | WKC_GLOBAL_HOTKEY, "copy", WID_CT_COPY), ++ Hotkey('V' | WKC_CTRL | WKC_GLOBAL_HOTKEY, "paste", WID_CT_PASTE), ++ Hotkey('1', "clipboard1", WID_CT_CLIPBOARD_1), ++ Hotkey('2', "clipboard2", WID_CT_CLIPBOARD_2), ++ Hotkey('3', "clipboard3", WID_CT_CLIPBOARD_3), ++ Hotkey('4', "clipboard4", WID_CT_CLIPBOARD_4), ++ Hotkey('5', "rail", WID_CT_WITH_RAIL), ++ Hotkey('6', "road", WID_CT_WITH_ROAD), ++ Hotkey('7', "water", WID_CT_WITH_WATER), ++ Hotkey('8', "air", WID_CT_WITH_AIR), ++ Hotkey('9', "terrain", WID_CT_TERRAFORM), ++ Hotkey('0', "rail_conversion", WID_CT_CONVERT_RAILTYPE), ++ Hotkey('S', "signal_mirror", WID_CT_MIRROR_SIGNALS), ++ Hotkey('B', "bridge_upgrade", WID_CT_UPGRADE_BRIDGES), ++ Hotkey('N', "with_stations", WID_CT_WITH_STATIONS), ++ HOTKEY_LIST_END ++}; ++HotkeyList ClipboardToolbarWindow::hotkeys("clipboard", _clipboard_hotkeys, ClipboardGlobalHotkeys); ++ ++static const NWidgetPart _nested_clipboard_toolbar_widgets[] = { ++ NWidget(NWID_HORIZONTAL), ++ NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), ++ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_CLIPBOARD_TOOLBAR_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), ++ NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), ++ EndContainer(), ++ NWidget(NWID_HORIZONTAL), ++ /* CLIPBOARD INDEX BUTTONS */ ++ NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_1), ++ SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_1ST_CLIPBOARD), ++ NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_2), ++ SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_2ND_CLIPBOARD), ++ NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_3), ++ SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_3RD_CLIPBOARD), ++ NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_4), ++ SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_4TH_CLIPBOARD), ++ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), ++ SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), ++ ++ /* COPY / PASTE BUTTONS */ ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_COPY), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_COPY, STR_CLIPBOARD_TOOLTIP_COPY), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_PASTE), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_PASTE, STR_CLIPBOARD_TOOLTIP_PASTE), ++ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), ++ SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), ++ ++ /* COPY/PASTE MODE SELECTORS */ ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_RAIL), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDRAIL, STR_CLIPBOARD_TOOLTIP_COPY_WITH_RAIL_TRANSPORT), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_ROAD), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDROAD, STR_CLIPBOARD_TOOLTIP_COPY_WITH_ROAD_TRANSPORT), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_WATER), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDWATER, STR_CLIPBOARD_TOOLTIP_COPY_WITH_WATER_TRANSPORT), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_AIR), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDAIR, STR_CLIPBOARD_TOOLTIP_COPY_WITH_AIR_TRANSPORT), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_TERRAFORM), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_LANDSCAPING, STR_CLIPBOARD_TOOLTIP_TERRAFORM), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_CONVERT_RAILTYPE), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION, STR_CLIPBOARD_TOOLTIP_CONVERT_RAIL), ++ NWidget(WWT_IMGBTN_2, COLOUR_DARK_GREEN, WID_CT_MIRROR_SIGNALS), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_OFF, STR_CLIPBOARD_TOOLTIP_MIRROR_SIGNALS), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_UPGRADE_BRIDGES), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_UPGRADE_BRIDGES, STR_CLIPBOARD_TOOLTIP_UPGRADE_BRIDGES), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_STATIONS), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_COMPANY_LIST, STR_CLIPBOARD_TOOLTIP_WITH_STATIONS), ++ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), ++ SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), ++ ++ /* TRANSFORMATIONS */ ++ NWidget(WWT_PUSHBTN, COLOUR_DARK_GREEN, WID_CT_TRANSFORMATION), ++ SetFill(0, 1), SetMinimalSize(23, 22), SetDataTip(0, STR_CLIPBOARD_TOOLTIP_TRANSFORMATION), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_ROTATE_LEFT), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_ROTATE_LEFT, STR_CLIPBOARD_TOOLTIP_ROTATE_LEFT), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_ROTATE_RIGHT), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_ROTATE_RIGHT, STR_CLIPBOARD_TOOLTIP_ROTATE_RIGHT), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_REFLECT_NE_SW), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_REFLECT_NE_SW, STR_CLIPBOARD_TOOLTIP_REFLECT_NE_SW), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_REFLECT_NW_SE), ++ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_REFLECT_NW_SE, STR_CLIPBOARD_TOOLTIP_REFLECT_NW_SE), ++ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), ++ SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), ++ ++ /* HEIGHT MANIPULATOR */ ++ NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(0, 22), ++ NWidget(NWID_HORIZONTAL), ++ NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_CT_HEIGHT_DIFF_GLYPH), SetDataTip(STR_EMPTY, STR_NULL), SetFill(0, 1), ++ NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_CT_HEIGHT_DIFF), SetDataTip(STR_CLIPBOARD_HEIGHT_DIFF, STR_NULL), SetFill(0, 1), ++ NWidget(NWID_VERTICAL), SetPIP(3, 0, 3), ++ NWidget(NWID_HORIZONTAL), SetPIP(0, 1, 3), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CT_HEIGHT_DIFF_INCREASE), SetDataTip(SPR_ARROW_UP, STR_NULL), SetFill(0, 1), ++ NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CT_HEIGHT_DIFF_DECREASE), SetDataTip(SPR_ARROW_DOWN, STR_NULL), SetFill(0, 1), ++ EndContainer(), ++ EndContainer(), ++ EndContainer(), ++ EndContainer(), ++ EndContainer(), ++}; ++ ++static WindowDesc _clipboard_toolbar_desc( ++ WDP_ALIGN_TOOLBAR, "toolbar_clipboard", 0, 0, ++ WC_BUILD_TOOLBAR, WC_NONE, ++ WDF_CONSTRUCTION, ++ _nested_clipboard_toolbar_widgets, lengthof(_nested_clipboard_toolbar_widgets), ++ &ClipboardToolbarWindow::hotkeys ++); ++ ++ ++/** ++ * Open the clipboard toolbar to copy and paste map pieces. ++ * @return newly opened clipboard toolbar, or NULL if the toolbar could not be opened. ++ */ ++Window *ShowClipboardToolbar() ++{ ++ if (!Company::IsValidID(_local_company)) return NULL; ++ DeleteWindowByClass(WC_BUILD_TOOLBAR); ++ return new ClipboardToolbarWindow(&_clipboard_toolbar_desc); ++} +diff --git a/src/clipboard_gui.h b/src/clipboard_gui.h +new file mode 100644 +index 0000000..4cd2320 +--- /dev/null ++++ b/src/clipboard_gui.h +@@ -0,0 +1,30 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_gui.h GUIs related to the clipboard. */ ++ ++#ifndef CLIPBOARD_GUI_H ++#define CLIPBOARD_GUI_H ++ ++#include "tile_type.h" ++#include "road_type.h" ++#include "track_type.h" ++ ++struct TileContentPastePreview { ++ bool highlight_tile_rect; ///< Whether to highlight tile borders ++ TrackBitsByte highlight_track_bits; ///< Rail tracks to highlight ++}; ++ ++struct TilePastePreview : TileContentPastePreview { ++ int tile_height; ///< Destination height of the tile ++}; ++ ++void GetTilePastePreview(TileIndex tile, TilePastePreview *ret); ++ ++#endif /* CLIPBOARD_GUI_H */ +diff --git a/src/clipboard_type.h b/src/clipboard_type.h +new file mode 100644 +index 0000000..e3b1980 +--- /dev/null ++++ b/src/clipboard_type.h +@@ -0,0 +1,46 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_type.h Types related to the clipboard. */ ++ ++#ifndef CLIPBOARD_TYPE_H ++#define CLIPBOARD_TYPE_H ++ ++#include "airport.h" ++#include "newgrf_station.h" ++#include "station_type.h" ++#include "tilearea_type.h" ++ ++struct ClipboardStation { ++ struct Spec { ++ StationClassIDByte stat_class; ++ byte stat_type; ++ }; ++ ++ struct AirportPart : RawTileArea { ++ SimpleTinyEnumT<AirportTypes, byte> type; ///< Airport type ++ byte layout; ///< Airport layout ++ }; ++ ++ StationID id; ///< ID ++ AirportPart airport; ///< Airport details ++ uint8 num_specs; ///< Number of specs in the speclist ++ Spec *speclist; ///< List of station specs of this station ++ ClipboardStation *next; ///< "Next" pointer to make a linked list ++ ++ static ClipboardStation *Get(StationID id, Map *clipboard); ++ static ClipboardStation *GetByTile(GenericTileIndex tile); ++ ++ ClipboardStation(); ++ ~ClipboardStation(); ++}; ++ ++typedef ClipboardStation *ClipboardStationList; ++ ++#endif /* CLIPBOARD_TYPE_H */ +diff --git a/src/command.cpp b/src/command.cpp +index 959610c..58e7ed6 100644 +--- a/src/command.cpp ++++ b/src/command.cpp +@@ -34,6 +34,7 @@ + CommandProc CmdBuildRailroadTrack; + CommandProc CmdRemoveRailroadTrack; + CommandProc CmdBuildSingleRail; ++CommandProc CmdBuildSingleRails; + CommandProc CmdRemoveSingleRail; + + CommandProc CmdLandscapeClear; +@@ -198,6 +199,10 @@ CommandProc CmdSetTimetableStart; + + CommandProc CmdOpenCloseAirport; + ++CommandProc CmdInstantCopyPaste; ++CommandProc CmdCopyToClipboard; ++CommandProc CmdPasteFromClipboard; ++ + #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} + + /** +@@ -211,6 +216,7 @@ static const Command _command_proc_table[] = { + DEF_CMD(CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAILROAD_TRACK + DEF_CMD(CmdRemoveRailroadTrack, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_RAILROAD_TRACK + DEF_CMD(CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SINGLE_RAIL ++ DEF_CMD(CmdBuildSingleRails, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_MULTI_RAIL + DEF_CMD(CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SINGLE_RAIL + DEF_CMD(CmdLandscapeClear, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LANDSCAPE_CLEAR + DEF_CMD(CmdBuildBridge, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_BRIDGE +@@ -354,6 +360,10 @@ static const Command _command_proc_table[] = { + DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START + + DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT ++ ++ DEF_CMD(CmdCopyToClipboard, CMD_OFFLINE, CMDT_OTHER_MANAGEMENT ), // CMD_COPY_TO_CLIPBOARD ++ DEF_CMD(CmdPasteFromClipboard,CMD_OFFLINE|CMD_NO_TEST|CMD_AUTO,CMDT_LANDSCAPE_CONSTRUCTION),// CMD_PASTE_FROM_CLIPBOARD ++ DEF_CMD(CmdInstantCopyPaste, CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_INSTANT_COPY_PASTE + }; + + /*! +diff --git a/src/command_func.h b/src/command_func.h +index c4cc51e..f184b32 100644 +--- a/src/command_func.h ++++ b/src/command_func.h +@@ -125,4 +125,7 @@ CommandCallback CcFoundRandomTown; + CommandCallback CcBuildPrimaryVehicle; + CommandCallback CcStartStopVehicle; + ++/* clipboard_gui.cpp */ ++CommandCallback CcPaste; ++ + #endif /* COMMAND_FUNC_H */ +diff --git a/src/command_type.h b/src/command_type.h +index f318216..091585b 100644 +--- a/src/command_type.h ++++ b/src/command_type.h +@@ -162,6 +162,21 @@ public: + { + return !this->success; + } ++ ++ /** ++ * Compare equeal. ++ * ++ * In case of two successes, money and type of expenses are comapred. ++ * In case of two failures, error message is comapred. ++ * ++ * @param cost the other cost to compare to ++ * @return whether booth costs are equeal ++ */ ++ bool operator == (const CommandCost &cost) const ++ { ++ if (!this->success) return !cost.success && this->message == cost.message; ++ return cost.success && this->expense_type == cost.expense_type && this->cost == cost.cost; ++ } + }; + + /** +@@ -178,6 +193,7 @@ enum Commands { + CMD_BUILD_RAILROAD_TRACK, ///< build a rail track + CMD_REMOVE_RAILROAD_TRACK, ///< remove a rail track + CMD_BUILD_SINGLE_RAIL, ///< build a single rail track ++ CMD_BUILD_SINGLE_RAILS, ///< build a set of rail tracks on a single tile + CMD_REMOVE_SINGLE_RAIL, ///< remove a single rail track + CMD_LANDSCAPE_CLEAR, ///< demolish a tile + CMD_BUILD_BRIDGE, ///< build a bridge +@@ -329,6 +345,10 @@ enum Commands { + + CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft + ++ CMD_COPY_TO_CLIPBOARD, ///< copy selected tile area into the clipboard ++ CMD_PASTE_FROM_CLIPBOARD, ///< paste content of the clipboard onto the map ++ CMD_INSTANT_COPY_PASTE, ///< copy selected tile area and instantly paste it at a given location ++ + CMD_END, ///< Must ALWAYS be on the end of this list!! (period) + }; + +@@ -338,19 +358,20 @@ enum Commands { + * This enums defines some flags which can be used for the commands. + */ + enum DoCommandFlag { +- DC_NONE = 0x000, ///< no flag is set +- DC_EXEC = 0x001, ///< execute the given command +- DC_AUTO = 0x002, ///< don't allow building on structures +- DC_QUERY_COST = 0x004, ///< query cost only, don't build. +- DC_NO_WATER = 0x008, ///< don't allow building on water +- DC_NO_RAIL_OVERLAP = 0x010, ///< don't allow overlap of rails (used in buildrail) +- DC_NO_TEST_TOWN_RATING = 0x020, ///< town rating does not disallow you from building +- DC_BANKRUPT = 0x040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases +- DC_AUTOREPLACE = 0x080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) +- DC_NO_CARGO_CAP_CHECK = 0x100, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo +- DC_ALL_TILES = 0x200, ///< allow this command also on MP_VOID tiles +- DC_NO_MODIFY_TOWN_RATING = 0x400, ///< do not change town rating +- DC_FORCE_CLEAR_TILE = 0x800, ///< do not only remove the object on the tile, but also clear any water left on it ++ DC_NONE = 0x0000, ///< no flag is set ++ DC_EXEC = 0x0001, ///< execute the given command ++ DC_AUTO = 0x0002, ///< don't allow building on structures ++ DC_QUERY_COST = 0x0004, ///< query cost only, don't build. ++ DC_NO_WATER = 0x0008, ///< don't allow building on water ++ DC_NO_RAIL_OVERLAP = 0x0010, ///< don't allow overlap of rails (used in buildrail) ++ DC_NO_TEST_TOWN_RATING = 0x0020, ///< town rating does not disallow you from building ++ DC_BANKRUPT = 0x0040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases ++ DC_AUTOREPLACE = 0x0080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) ++ DC_NO_CARGO_CAP_CHECK = 0x0100, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo ++ DC_ALL_TILES = 0x0200, ///< allow this command also on MP_VOID tiles ++ DC_NO_MODIFY_TOWN_RATING = 0x0400, ///< do not change town rating ++ DC_FORCE_CLEAR_TILE = 0x0800, ///< do not only remove the object on the tile, but also clear any water left on it ++ DC_PASTE = 0x1000, ///< this command is a part of a paste process + }; + DECLARE_ENUM_AS_BIT_SET(DoCommandFlag) + +diff --git a/src/copypaste_cmd.cpp b/src/copypaste_cmd.cpp +new file mode 100644 +index 0000000..5b6acbe +--- /dev/null ++++ b/src/copypaste_cmd.cpp +@@ -0,0 +1,689 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_cmd.cpp Helper functions for copy/paste commands. */ ++ ++#include "stdafx.h" ++#include "core/alloc_func.hpp" ++#include "core/geometry_func.hpp" ++#include "network/network.h" ++#include "clear_map.h" ++#include "clipboard_func.h" ++#include "command_func.h" ++#include "company_base.h" ++#include "company_func.h" ++#include "copypaste_cmd.h" ++#include "error.h" ++#include "gui.h" ++#include "rail.h" ++#include "rail_map.h" ++#include "strings_func.h" ++#include "tile_cmd.h" ++#include "tilearea_func.h" ++#include "tunnelbridge_map.h" ++#include <cmath> ++ ++#include "table/strings.h" ++ ++static const uint INSTANT_COPY_PASTE_BUFFER = NUM_CLIPBOARD_BUFFERS - 1; ///< Index of the buffer reserved for the CmdInstantCopyPaste (temporary buffer) ++ ++PasteCmdHelper *_current_pasting = NULL; ///< State of currently executed paste command ++TileIndex _paste_err_tile = INVALID_TILE; ///< Tile where occured error of the last paste command ++ ++/** ++ * Importance of an error in context of pasting. Bigger value is bigger importance. ++ * ++ * Various command errors may be encountered when copy/pasting. The importance decides which one ++ * to show to the user - it will be one of most important errors, the one that was encountered ++ * first. Errors with importance PEI_CRITICAL cancel a paste operation e.g. company run out of money. ++ */ ++typedef int PasteErrorImportance; ++ ++static const PasteErrorImportance PEI_CRITICAL = 0x100; ///< Critical paste error ++ ++/** ++ * Get importance of a certain error message. ++ * ++ * @param error_msg The error. ++ * @return Importance of the error. ++ * ++ * @see PasteCmdHelper::DoCommand ++ * @see PasteCmdHelper::IsInterrupted ++ */ ++static PasteErrorImportance GetPasteErrorImportance(StringID error_msg) ++{ ++ switch (error_msg) { ++ /* Ignored errors, these will be newer stored as they are less important then the default error. */ ++ case STR_ERROR_ALREADY_LEVELLED: ++ case STR_ERROR_ALREADY_BUILT: ++ return -1; ++ ++ /* The default error which is set initially right before copy/pasting. */ ++ case STR_ERROR_NOTHING_TO_DO: ++ return 0; ++ ++ /* "Can't distant join" must be the least important error among all non-ignored and non-default ++ * errors. We must be able to reset it to the default one (see AfterPasteingStations). */ ++ case STR_ERROR_CAN_T_DISTANT_JOIN: ++ return 1; ++ ++ /* Messageless CMD_ERROR, it's not descriptive so it has a very low importance. */ ++ case INVALID_STRING_ID: ++ return 2; ++ ++ /* Low importance errors */ ++ case STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST: ++ case STR_ERROR_BUILDING_MUST_BE_DEMOLISHED: ++ case STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST: ++ case STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST: ++ case STR_ERROR_MUST_DEMOLISH_DOCK_FIRST: ++ case STR_ERROR_BUOY_IN_THE_WAY: ++ return 3; ++ ++ /* High importance errors */ ++ default: ++ return 4; ++ ++ /* Critical errors */ ++ case STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY: ++ return PEI_CRITICAL; ++ } ++} ++ ++/** ++ * Check if the current paste operations is interrupted. ++ * ++ * @return True if pasting is interrupted, false if we can continue pasting. ++ * ++ * @see PasteErrorImportance ++ * @see PasteCmdHelper::DoCommand ++ */ ++bool PasteCmdHelper::IsInterrupted() const ++{ ++ return GetPasteErrorImportance(this->err_message) >= PEI_CRITICAL; ++} ++ ++/** ++ * Call a given command as an ingredient of a paste operation. ++ * ++ * Costs and possible errors will be aggregated. After return, call PasteCmdHelper::IsInterrupted to ++ * test if the paste operation is disallowed to be continued. ++ * ++ * @param tile The tile to apply the command on. ++ * @param p1 Additional data for the command. ++ * @param p2 Additional data for the command. ++ * @param cmd The command-id to execute (a value of the CMD_* enums) and the error summary message (CMD_MSG). ++ * @return The cost of this operation or an error. ++ * ++ * @pre The command is not flagged with CMD_NO_TEST. ++ * @pre The type of the command is CMDT_LANDSCAPE_CONSTRUCTION. ++ * ++ * @see PasteCmdHelper::IsInterrupted ++ * @see PasteCmdHelper::CollectCost ++ * @see PasteCmdHelper::CollectError ++ */ ++void PasteCmdHelper::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) ++{ ++ /* make sure we are still allowed to paste */ ++ if (this->IsInterrupted()) { ++ this->last_result = CMD_ERROR; // mark that the command didn't succeed ++ return; ++ } ++ ++ /* PasteCmdHelper::DoCommand can handle only fully predictable commands, those without ++ * CMD_NO_TEST flag. Unpredictable command have to be handled separately. */ ++ assert(!(GetCommandFlags(cmd) & CMD_NO_TEST)); ++ ++ /* ignore some of the given flags, instead use those from the command proc table */ ++ DoCommandFlag flags = this->dc_flags; ++ flags &= ~DC_AUTO & ~DC_NO_WATER & ~DC_ALL_TILES; ++ flags |= CommandFlagsToDCFlags(GetCommandFlags(cmd)); ++ ++ /* use given error message or the default one */ ++ StringID summary_error_msg = GB(cmd, 16, 16); ++ if (summary_error_msg == 0) summary_error_msg = STR_ERROR_CAN_T_PASTE_HERE; ++ ++ /* test the command, output is the return value */ ++ CommandCost ret = ::DoCommand(tile, p1, p2, flags & ~DC_EXEC, cmd); ++ ++ /* apply if exec'ing */ ++ if (ret.Succeeded() && (flags & DC_EXEC)) { ++ /* check if there is enough money */ ++ if (ret.GetCost() > 0 && this->GetAvailableMoney() < ret.GetCost()) { ++ SetDParam(0, ret.GetCost()); ++ ret = CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); ++ } else { ++ CommandCost ret2 = ::DoCommand(tile, p1, p2, flags, cmd); ++ assert(ret == ret2); ++ } ++ } ++ ++ /* aggregate costs */ ++ this->CollectCost(ret, tile, summary_error_msg); ++} ++ ++/** ++ * Aggreagate paste command costs without calling PasteCmdHelper::DoCommand. ++ * ++ * The function works similarly to the PasteCmdHelper::DoCommand but doesn't actually execute any ++ * commands, it just collects a given result. ++ * ++ * When collecting a success, cost must be of type EXPENSES_CONSTRUCTION. A success also makes ++ * STR_ERROR_NOTHING_TO_DO no more applies (we "did" something). ++ * ++ * Call PasteCmdHelper::IsInterrupted to test whether the paste operation can be continued. ++ * ++ * @param cost The return value of the command, a cost or an error. ++ * @param tile The tile the error concerns. ++ * @param error_message Summary message of the error. ++ * ++ * @pre The company has enough money if DC_EXEC'ing. ++ * ++ * @see PasteCmdHelper::IsInterrupted ++ * @see PasteCmdHelper::CollectError ++ * @see PasteCmdHelper::DoCommand ++ */ ++void PasteCmdHelper::CollectCost(const CommandCost &cost, TileIndex tile, StringID error_summary) ++{ ++ if (cost.Succeeded()) { ++ assert(!this->IsInterrupted()); ++ /* Currently only EXPENSES_CONSTRUCTION expenses are allowed when copy/pasting. If this ++ * is not sufficient, some upgrade will be required. To allow proper update of finacial ++ * statistics, the overal cost of paste operation will have to be stored separatly for ++ * each supported type of expenses. */ ++ assert(cost.GetExpensesType() == EXPENSES_CONSTRUCTION); ++ ++ /* make sure we are not expending too much */ ++ assert(!(this->dc_flags & DC_EXEC) || cost.GetCost() <= 0 || this->GetAvailableMoney() >= 0); ++ ++ this->had_success = true; // mark that we had a succes and STR_ERROR_NOTHING_TO_DO no more applies ++ this->overal_cost += cost.GetCost(); ++ this->last_result = cost; ++ } else { ++ this->CollectError(tile, cost.GetErrorMessage(), error_summary); ++ } ++} ++ ++/** ++ * Collect a paste error without calling PasteCmdHelper::DoCommand or PasteCmdHelper::CollectCost. ++ * ++ * The function works similary to PasteCmdHelper::DoCommand and PasteCmdHelper::CollectCost, ++ * but it only generates an error. After return, call PasteCmdHelper::IsInterrupted to test whether ++ * the paste operation is allowd to be continued. ++ * ++ * @param tile The tile the error concerns. ++ * @param error_message Error message. ++ * @param error_message Summary error message. ++ * ++ * @see PasteCmdHelper::IsInterrupted ++ * @see PasteCmdHelper::CollectCost ++ * @see PasteCmdHelper::DoCommand ++ */ ++void PasteCmdHelper::CollectError(TileIndex tile, StringID error_message, StringID error_summary) ++{ ++ /* store the error only if it is more important then the previous one */ ++ if (GetPasteErrorImportance(error_message) > GetPasteErrorImportance(this->err_message)) { ++ this->err_tile = IsValidTile(tile) ? tile : INVALID_TILE; ++ this->err_message = error_message; ++ this->err_summary = error_summary; ++ CopyOutDParam(this->err_params, 0, lengthof(this->err_params)); ++ } ++ ++ this->last_result = CommandCost(error_message); ++} ++ ++/** ++ * Calculate how far tiles can be altered beyond a given paste area bound. ++ * ++ * When pasting, some tiles around the paste area may be altered (during terraforming). ++ * The function return the limit on how far it can happen. Calculations are not exact, ++ * the goal is to give a safe range that will include any possible case. ++ * ++ * Result is based on current and desired heights at neighbour corners of the paste area. ++ * ++ * @param curr_h1 Current height on the first corner. ++ * @param curr_h2 Current height on the second corner. ++ * @param new_h1 Desired height on the first corner. ++ * @param new_h2 Desired height on the second corner. ++ * @param length Distance (in tiles) between corners. ++ * @return How far (in tiles) terraforming can reach beyond the given bound. ++ * ++ * @pre Tile heights and the length can't create an impossible layout, heights can't differ ++ * too much: \n ++ * <tt> Delta(curr_h1, curr_h2) <= length </tt> \n ++ * <tt> Delta(new_h1, new_h2) <= length </tt> \n ++ * ++ * @see CopyPasteAreasMayColide ++ */ ++static uint CalcMaxPasteRange(uint curr_h1, uint new_h1, uint curr_h2, uint new_h2, uint length) ++{ ++ uint min_curr_h = CeilDiv(max<int>(curr_h1 + curr_h2 - length, 0), 2); ++ uint max_curr_h = min((curr_h1 + curr_h2 + length) / 2, MAX_TILE_HEIGHT); ++ uint min_new_h = CeilDiv(max<int>(new_h1 + new_h2 - length, 0), 2); ++ uint max_new_h = min((new_h1 + new_h2 + length) / 2, MAX_TILE_HEIGHT); ++ ++ return max(Delta(max_new_h, min_curr_h), Delta(max_curr_h, min_new_h)); ++} ++ ++/** ++ * Test if this is safe to copy and paste contents of the map instantly, without ++ * using an intermediate buffer. ++ * ++ * If the copy and the paste areas are close enough (especially when they intersect), ++ * sequential copy-pasting may alter at some point of time those tile of the copy ++ * area which hasn't been copied yet. In this case, further copy-pasting will read ++ * modified values, not the original and this is somthing we don't want to happen. ++ * We can deal with it by firstly copying all the content to the clipboard buffer and ++ * then pasting it onto the map. This function tels us whether we should use the ++ * clipboard as an intermediate buffer because there may happen such a colision. ++ * ++ * @param copy_paste What, where and how we are copying. ++ * @return \c true if intermediate buffer might be required, \c false if it's surely not required ++ * ++ * @pre booth the source area and the destination area are on the main map ++ * ++ * @see CalcMaxPasteRange ++ */ ++static bool CopyPasteAreasMayColide(const CopyPasteParams ©_paste) ++{ ++ /* No need to check surroundings if we are not terraforming. Just test for content intersection. */ ++ if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_NONE) return copy_paste.src_area.Intersects(copy_paste.dst_area); ++ ++ /* As we are interested in tile heights, increase areas to include all tile ++ * corners, also these at SW and SE borders. */ ++ TileArea src_corner_area(AsMainMapTile(copy_paste.src_area.tile), copy_paste.src_area.w + 1, copy_paste.src_area.h + 1); ++ TileArea dst_corner_area(AsMainMapTile(copy_paste.dst_area.tile), copy_paste.dst_area.w + 1, copy_paste.dst_area.h + 1); ++ ++ DirTransformation inv_transformation = InvertDirTransform(copy_paste.transformation); ++ /* source of the destination area most northern tile corner */ ++ TileIndex source_of_north = dst_corner_area.TransformedNorth(src_corner_area.tile, inv_transformation); ++ ++ /* calculate current and new heights on destination area corners */ ++ /* N */ ++ TileIndex dst_corner = dst_corner_area.tile; ++ TileIndex src_corner = source_of_north; ++ uint curr_n = TileHeight(dst_corner); ++ uint new_n = TileHeight(src_corner) + copy_paste.height_delta; ++ /* W */ ++ dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, 0); ++ src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); ++ uint curr_w = TileHeight(dst_corner); ++ uint new_w = TileHeight(src_corner) + copy_paste.height_delta; ++ /* S */ ++ dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, dst_corner_area.h); ++ src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); ++ uint curr_s = TileHeight(dst_corner); ++ uint new_s = TileHeight(src_corner) + copy_paste.height_delta; ++ /* E */ ++ dst_corner = TILE_ADDXY(dst_corner_area.tile, 0, dst_corner_area.h); ++ src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); ++ uint curr_e = TileHeight(dst_corner); ++ uint new_e = TileHeight(src_corner) + copy_paste.height_delta; ++ ++ /* calculate how far tiles can be altered beyon the paste area (safe approximation) */ ++ uint range_ne = CalcMaxPasteRange(curr_n, new_n, curr_e, new_e, dst_corner_area.h - 1); ++ uint range_sw = CalcMaxPasteRange(curr_s, new_s, curr_w, new_w, dst_corner_area.h - 1); ++ uint range_nw = CalcMaxPasteRange(curr_n, new_n, curr_w, new_w, dst_corner_area.w - 1); ++ uint range_se = CalcMaxPasteRange(curr_s, new_s, curr_e, new_e, dst_corner_area.w - 1); ++ ++ /* calculate the exact area which may be altered by the paste process */ ++ uint x = TileX(dst_corner_area.tile); ++ uint y = TileY(dst_corner_area.tile); ++ range_ne = max(range_ne, x); // cut the area at the NE border (don't let x to go below 0) ++ range_nw = max(range_nw, y); // cut the area at the NW border (don't let y to go below 0) ++ TileArea forbidden_area( ++ TileXY(x - range_ne, y - range_nw), ++ dst_corner_area.w + range_ne + range_sw, ++ dst_corner_area.h + range_nw + range_se); ++ ++ /* test if the source area is within the paste range */ ++ return src_corner_area.Intersects(forbidden_area); ++} ++ ++/** ++ * Calculate how much to add to each height of a tile while copy-pasteing. ++ * @param src_area Area we are copying from. ++ * @param dst_area Area we are pasting at. ++ * @param transformation Transformation to perform. ++ * @param additional_height Extra amount of units to add. ++ */ ++static inline int CalcCopyPasteHeightDelta(const GenericTileArea &src_area, const GenericTileArea &dst_area, DirTransformation transformation, int additional_height) ++{ ++ GenericTileArea dst_corners(dst_area.tile, dst_area.w + 1, dst_area.h + 1); ++ GenericTileIndex source_of_north = dst_corners.TransformedNorth(src_area.tile, InvertDirTransform(transformation)); ++ return TileHeight(dst_corners.tile) - TileHeight(source_of_north) + additional_height; ++} ++ ++/** ++ * Do a sequential copy-pasting by calling appropriate CopyPasteCommandProc on each selected tile. ++ * @param copy_paste What, where and how we are copying. ++ */ ++static inline void DoCopyPaste(const CopyPasteParams ©_paste) ++{ ++ /* Copying to the clipboard buffer should always success. ++ * Some content may be intransformable (e.g. airport) so we can't use any transformation. */ ++ assert(IsMainMapTile(copy_paste.dst_area.tile) || (copy_paste.transformation == DTR_IDENTITY && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_FULL)); ++ ++ if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_FULL) { ++ CopyPasteHeights(copy_paste.src_area, copy_paste.dst_area.tile, copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ } ++ ++ for (TransformationTileIteratorT<true, true> ti(copy_paste.src_area, copy_paste.src_area.TransformedNorth(copy_paste.dst_area.tile, copy_paste.transformation), copy_paste.transformation); IsValidTileIndex(ti); ++ti) { ++ CopyPasteTileProc *proc = _tile_type_procs[GetTileType(ti.SrcTile())]->copy_paste_tile_proc; ++ if (proc == NULL) continue; ++ proc(ti.SrcTile(), ti.DstTile(), copy_paste); ++ if (IsPastingInterrupted()) break; ++ } ++ ++ if (IsMainMapTile(copy_paste.dst_area.tile)) { ++ AfterPastingStations(copy_paste); ++ } else { ++ AfterCopyingStations(copy_paste); ++ } ++} ++ ++/** ++ * Test if a given TileArea is valid. ++ * @return \c true if the area is non-empty and fits inside the map, \c false otherwise. ++ */ ++static CommandCost ValParamCopyPasteArea(const GenericTileArea &ta) ++{ ++ if (!IsValidTile(ta.tile) || ++ !IsInsideBS(ta.w, 1, _settings_game.construction.clipboard_capacity) || ++ !IsInsideBS(ta.h, 1, _settings_game.construction.clipboard_capacity)) { ++ return CMD_ERROR; ++ } ++ ++ if (TileX(ta.tile) + ta.w > MapMaxX(MapOf(ta.tile)) || ++ TileY(ta.tile) + ta.h > MapMaxY(MapOf(ta.tile))) { ++ return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB); ++ } ++ ++ return CommandCost(); ++} ++ ++/** ++ * Test if a CopyPasteMode is valid. ++ * @return \c true if does, \c false otherwise ++ */ ++static bool ValParamCopyPasteMode(CopyPasteMode mode) ++{ ++ if (mode & ~CPM_MASK) return false; ++ mode &= CPM_TERRAFORM_MASK; ++ return mode == CPM_TERRAFORM_NONE || mode == CPM_TERRAFORM_MINIMAL || mode == CPM_TERRAFORM_FULL; ++} ++ ++/** ++ * Copy content of a given tile area into the clipboard buffer. ++ * @param buffer Clipboard buffer to copy into ++ * @param ta The area to copy ++ */ ++static void CopyToClipboard(Map *buffer, const TileArea &ta) ++{ ++ AllocateClipboardBuffer(buffer, ta.w, ta.h); ++ ++ CopyPasteParams copy_paste = { ++ GenericTileArea(ta), // src_area ++ GenericTileArea(TileXY(0, 0, buffer), ta.w, ta.h), // dst_area ++ CPM_COPY, // mode ++ INVALID_RAILTYPE, // railtype ++ DTR_IDENTITY, // transformation ++ 0 // height_delta ++ }; ++ ++ DoCopyPaste(copy_paste); ++} ++ ++/** ++ * Begin a paste process. ++ * @param flags Flags for the command. ++ * @param params What, where and how to paste. ++ */ ++static void InitializePasting(DoCommandFlag flags, const CopyPasteParams ¶ms) ++{ ++ assert(_current_pasting == NULL); ++ _current_pasting = new PasteCmdHelper; ++ ++ _current_pasting->dc_flags = flags | DC_PASTE; ++ _current_pasting->overal_cost = 0; ++ _current_pasting->had_success = false; ++ _current_pasting->err_summary = STR_ERROR_CAN_T_PASTE_HERE; ++ _current_pasting->err_message = STR_ERROR_NOTHING_TO_DO; ++ MemSetT(_current_pasting->err_params, 0, lengthof(_current_pasting->err_params)); ++ _current_pasting->err_tile = INVALID_TILE; ++ _current_pasting->last_result = CommandCost(STR_ERROR_NOTHING_TO_DO); ++} ++ ++/** ++ * Finish a paste process. ++ * @return Total cost. ++ */ ++static CommandCost FinalizePasting() ++{ ++ /* Set error string parameters */ ++ CopyInDParam(0, _current_pasting->err_params, lengthof(_current_pasting->err_params)); ++ /* Set error summary message (see COPY_PASTE_ERR_SUMMARY_PARAM for details). */ ++ SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, _current_pasting->err_summary); ++ /* Store the error tile so the GUI (CcPaste) can highlight it. */ ++ _paste_err_tile = _current_pasting->err_tile; ++ ++ CommandCost ret; ++ if (_current_pasting->had_success) { ++ /* Return overal cost of the operation */ ++ ret = CommandCost(EXPENSES_CONSTRUCTION, _current_pasting->overal_cost); ++ /* Here we are about to return a success. However, there could occured some meaningful ++ * errors (those except "already built", "already leveled" etc.) and we should inform ++ * the user that not everything went right. Show the message now. */ ++ if ((_current_pasting->dc_flags & DC_EXEC) && IsLocalCompany() && GetPasteErrorImportance(_current_pasting->err_message) > GetPasteErrorImportance(STR_ERROR_NOTHING_TO_DO)) { ++ ShowErrorMessage(_current_pasting->err_summary, _current_pasting->err_message, WL_INFO); ++ } else { ++ /* If we are not showing error message then clear the error tile to prevent GUI ++ * (CcPaste) from higlighting it. */ ++ _paste_err_tile = INVALID_TILE; ++ } ++ } else { ++ /* Return an error if we didn't have any success. */ ++ ret = CommandCost(_current_pasting->err_message); ++ } ++ ++ /* cleanup */ ++ delete _current_pasting; ++ _current_pasting = NULL; ++ ++ return ret; ++} ++ ++/** ++ * Paste onto the main map content of a clipboard buffer. ++ * @param buffer The buffer. ++ * @param tile Tile where to paste (most northern). ++ * @param flags Flags for the command. ++ * @param mode Copy-paste mode. ++ * @param transformation Transformation to perform. ++ * @param railtype #RailType to convert to. ++ * @param additional_height_delta Additional amount of units to add to each tile height. ++ * @return Total cost. ++ */ ++static CommandCost PasteFromClipboard(Map *buffer, TileIndex tile, DoCommandFlag flags, CopyPasteMode mode, DirTransformation transformation, RailType railtype, int additional_height_delta) ++{ ++ assert(!IsClipboardBufferEmpty(buffer)); ++ ++ CopyPasteParams copy_paste; ++ ++ /* calculate and validate copy/paste area */ ++ copy_paste.src_area = GenericTileArea(TileXY(0, 0, buffer), MapMaxX(buffer), MapMaxY(buffer)); ++ copy_paste.dst_area = TransformTileArea(copy_paste.src_area, GenericTileIndex(tile), transformation); ++ CommandCost ret = ValParamCopyPasteArea(copy_paste.dst_area); ++ if (ret.Failed()) return ret; ++ ++ copy_paste.mode = mode; ++ copy_paste.railtype = railtype; ++ copy_paste.transformation = transformation; ++ copy_paste.height_delta = CalcCopyPasteHeightDelta(copy_paste.src_area, copy_paste.dst_area, transformation, additional_height_delta); ++ ++ /* do sequential copy-pasting */ ++ InitializePasting(flags, copy_paste); ++ DoCopyPaste(copy_paste); ++ return FinalizePasting(); ++} ++ ++/** ++ * Copy tile area into clipboard. ++ * ++ * @param tile Northern tile of the area to copy. ++ * @param flags Command flags. ++ * @param p1 Various bits: ++ * \li bits 0..1 [2] - clipboard buffer index ++ * \li bits 2..31 [30] - [ unused ] ++ * @param p2 Various bits: ++ * \li bits 0..5 [6] - width of area to copy ++ * \li bits 6..11 [6] - height of area to copy ++ * \li bits 12..31 [20] - [ unused ] ++ * @param text Unused. ++ * @return The cost of this operation or an error. ++ */ ++CommandCost CmdCopyToClipboard(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) ++{ ++ /* clipboard is available only in a sigle player game and only to the local company */ ++ if (_networking || !IsLocalCompany()) return CMD_ERROR; ++ ++ /* extract and validate clipboard buffer index */ ++ uint index = GB(p1, 0, 2); ++ if (index >= NUM_CLIPBOARD_BUFFERS || index == INSTANT_COPY_PASTE_BUFFER) return CMD_ERROR; ++ ++ /* calculate and validate source area */ ++ TileArea src_area(tile, GB(p2, 0, 6), GB(p2, 6, 6)); ++ CommandCost ret = ValParamCopyPasteArea(src_area); ++ if (ret.Failed()) return ret; ++ ++ /* copy to clipboard only when executing (DC_EXEC) */ ++ if (flags & DC_EXEC) CopyToClipboard(GetClipboardBuffer(index), src_area); ++ ++ /* return the cost */ ++ return CommandCost(INVALID_EXPENSES, 0); ++} ++ ++/** ++ * Paste clipboard contents onto the map. ++ * ++ * @param tile Tile where to paste (northern). ++ * @param flags Command flags. ++ * @param p1 Various bits: ++ * \li bits 0..1 [2] - clipboard buffer index ++ * \li bits 2..27 [26] - [ unused ] ++ * \li bits 28..31 [4] - rail type (RailType) to convert to, ignored if CPM_CONVERT_RAILTYPE mode is off ++ * @param p2 Various bits: ++ * \li bits 0..11 [12] - [ unused ] ++ * \li bits 12..15 [4] - additional amount of tile heights to add to each tile (-8..7) ++ * \li bits 16..18 [3] - transformation to perform (DirTransformation) ++ * \li bits 19..28 [10] - mode (CopyPasteMode) ++ * \li bits 29..31 [3] - [ unused ] ++ * @param text Unused. ++ * @return The cost of this operation or an error. ++ */ ++CommandCost CmdPasteFromClipboard(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) ++{ ++ /* extract and validate clipboard buffer index */ ++ uint index = GB(p1, 0, 2); ++ if (index >= NUM_CLIPBOARD_BUFFERS || index == INSTANT_COPY_PASTE_BUFFER) return CMD_ERROR; ++ ++ /* clipboard is available only in a sigle player game and only to the local company */ ++ if (_networking || !IsLocalCompany() || IsClipboardBufferEmpty(GetClipboardBuffer(index))) return CMD_ERROR; ++ ++ /* extract and validate copy/paste mode */ ++ CopyPasteMode mode = (CopyPasteMode)GB(p2, 19, 10); ++ if (!ValParamCopyPasteMode(mode)) return CMD_ERROR; ++ ++ /* extract and validate rail type */ ++ RailType railtype = (RailType)GB(p1, 28, 4); ++ if (!ValParamRailtype(railtype)) return CMD_ERROR; ++ ++ /* extract transformation and additional height delta */ ++ DirTransformation transformation = (DirTransformation)GB(p2, 16, 3); ++ int additional_height_delta = GB(p2, 12, 4); ++ additional_height_delta |= -(additional_height_delta & (1 << 3)); // propagate sign bit ++ ++ return PasteFromClipboard(GetClipboardBuffer(index), tile, flags, mode, transformation, railtype, additional_height_delta); ++} ++ ++/** ++ * Copy a piece of map and instantly paste at given location. ++ * ++ * @param tile Tile where to paste (northern). ++ * @param flags Command flags. ++ * @param p1 Various bits: ++ * \li bits 0..27 [28] - northern tile of the source area ++ * \li bits 28..31 [4] - rail type (RailType) to convert to, ignored if CPM_CONVERT_RAILTYPE mode is off ++ * @param p2 Various bits: ++ * \li bits 0..5 [6] - source area width ++ * \li bits 6..11 [6] - source area height ++ * \li bits 12..15 [4] - additional amount of tile heights to add to each tile (-8..7) ++ * \li bits 16..18 [3] - transformation to perform (DirTransformation) ++ * \li bits 19..28 [10] - mode (CopyPasteMode) ++ * \li bits 29..31 [3] - [ unused ] ++ * @param text Unused. ++ * @return The cost of this operation or an error. ++ */ ++CommandCost CmdInstantCopyPaste(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) ++{ ++ CopyPasteParams copy_paste; ++ ++ /* extract and validate source area */ ++ copy_paste.src_area.tile = GenericTileIndex(TileIndex(GB(p1, 0, 28))); ++ copy_paste.src_area.w = GB(p2, 0, 6); ++ copy_paste.src_area.h = GB(p2, 6, 6); ++ CommandCost ret = ValParamCopyPasteArea(copy_paste.src_area); ++ if (ret.Failed()) return ret; ++ ++ /* calculate and validate destination area */ ++ copy_paste.dst_area = TransformTileArea(copy_paste.src_area, GenericTileIndex(tile), copy_paste.transformation); ++ ret = ValParamCopyPasteArea(copy_paste.dst_area); ++ if (ret.Failed()) return ret; ++ ++ /* extract and validate copy/paste mode */ ++ copy_paste.mode = (CopyPasteMode)GB(p2, 19, 10); ++ if (!ValParamCopyPasteMode(copy_paste.mode)) return CMD_ERROR; ++ ++ /* extract and validate rail type */ ++ copy_paste.railtype = (RailType)GB(p1, 28, 4); ++ if (!ValParamRailtype(copy_paste.railtype)) return CMD_ERROR; ++ ++ /* extract transformation */ ++ copy_paste.transformation = (DirTransformation)GB(p2, 16, 3); ++ ++ /* extract the additional number of height units */ ++ int additional_height_delta = GB(p2, 12, 4); // this is a 4-bit SIGNED integer (-8..7) ++ additional_height_delta |= -(additional_height_delta & (1 << 3)); // propagate the sign bit ++ ++ /* calculate the height */ ++ copy_paste.height_delta = CalcCopyPasteHeightDelta(copy_paste.src_area, copy_paste.dst_area, copy_paste.transformation, additional_height_delta); ++ ++ /* when copy and paste areas are too close each other, firstly ++ * copy to the clipboard and then from the clipboard to the map */ ++ if (CopyPasteAreasMayColide(copy_paste)) { ++ Map *clipboard = GetClipboardBuffer(INSTANT_COPY_PASTE_BUFFER); ++ /* Copy to a buffer, but only in the first stage of the command. ++ * In a single player game and also while we are a server, the first one is non-DC_EXEC ++ * stage (which is fallowed then by a DC_EXEC stage). When we are a client, there is only ++ * one stage which is either a single non-DC_EXEC stage (shift pressed), or a single DC_EXEC ++ * stage (command comming from the network). */ ++ if ((_networking && !_network_server) || !(flags & DC_EXEC)) { ++ CopyToClipboard(clipboard, copy_paste.src_area); ++ } ++ /* paste from the clipboard */ ++ ret = PasteFromClipboard(clipboard, tile, flags, copy_paste.mode, copy_paste.transformation, copy_paste.railtype, additional_height_delta); ++ } else { ++ /* copy/paste directly */ ++ InitializePasting(flags, copy_paste); ++ DoCopyPaste(copy_paste); ++ ret = FinalizePasting(); ++ } ++ return ret; ++} +diff --git a/src/copypaste_cmd.h b/src/copypaste_cmd.h +new file mode 100644 +index 0000000..66e8cf5 +--- /dev/null ++++ b/src/copypaste_cmd.h +@@ -0,0 +1,116 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file copypaste_cmd.h Helper functions for copy/paste commands. */ ++ ++#ifndef COPYPASTE_CMD_H ++#define COPYPASTE_CMD_H ++ ++#include "core/enum_type.hpp" ++#include "command_func.h" ++#include "tilearea_type.h" ++#include "station_map.h" ++#include "newgrf_station.h" ++ ++#include "table/strings.h" ++ ++/** Pasting modifiers. */ ++enum CopyPasteMode { ++ CPM_WITH_RAIL_TRANSPORT = 1 << 0, ///< copy-paste rail transport infrastructure ++ CPM_WITH_ROAD_TRANSPORT = 1 << 1, ///< copy-paste road transport infrastructure ++ CPM_WITH_WATER_TRANSPORT = 1 << 2, ///< copy-paste water transport infrastructure ++ CPM_WITH_AIR_TRANSPORT = 1 << 3, ///< copy-paste air transport infrastructure ++ CPM_ALL_TRANSPORT_MASK = 0xF << 0, ///< bitmask with all transport types ++ ++ CPM_TERRAFORM_NONE = 0 << 4, ///< do not alter tile heights ++ CPM_TERRAFORM_MINIMAL = 1 << 4, ///< terraform as less as possible to paste all objects at right heights ++ CPM_TERRAFORM_FULL = 2 << 4, ///< copy-paste all tile heights ++ CPM_TERRAFORM_MASK = 0x3 << 4, ///< bitmask to extract terraforming modes ++ ++ CPM_CONVERT_RAILTYPE = 1 << 6, ///< convert rails to a given rail type ++ CPM_MIRROR_SIGNALS = 1 << 7, ///< mirror signals direction ++ CPM_UPGRADE_BRIDGES = 1 << 8, ///< upgrade bridge types to fastes possible ++ CPM_WITH_STATIONS = 1 << 9, ///< also copy and pase stations and waypoints ++ CPM_FLAGS_MASK = 0xF << 6, ///< bitmask to mask all flag-like bits ++ ++ CPM_NONE = 0, ///< empty set of modes ++ CPM_MASK = CPM_ALL_TRANSPORT_MASK | CPM_FLAGS_MASK | CPM_TERRAFORM_MASK, ///< all possible bits ++ CPM_DEFAULT = CPM_ALL_TRANSPORT_MASK | CPM_TERRAFORM_MINIMAL | CPM_WITH_STATIONS, ///< defult paste mode ++ CPM_COPY = CPM_ALL_TRANSPORT_MASK | CPM_TERRAFORM_FULL | CPM_WITH_STATIONS, ///< mode used when copying to the clipboard ++}; ++DECLARE_ENUM_AS_BIT_SET(CopyPasteMode) ++ ++/** Land leveling type used in e.g. #LevelPasteLand. */ ++enum CopyPasteLevelVariant { ++ CPLV_LEVEL_ABOVE, ///< Lower the land until a given height reached. ++ CPLV_LEVEL_BELOW, ///< Raise the land until a given height reached. ++}; ++ ++/** Parameters of a copy/paste command. */ ++struct CopyPasteParams { ++ GenericTileArea src_area; ///< The area we are copying from ++ GenericTileArea dst_area; ///< The area we are pasting at ++ CopyPasteMode mode; ///< Various flags telling what to copy and how to paste ++ RailType railtype; ///< Convert all rails to a given rail type (only in #CPM_CONVERT_RAILTYPE mode) ++ DirTransformation transformation; ///< Transformation to perform on the content while copy-pasting ++ int height_delta; ///< Amount of units to add to the height of each tile (appropriate terraforming mode must be set e.g. #CPM_TERRAFORM_FULL) ++}; ++ ++/** Summary error message for copy/paste command may vary depending on encountered errors. ++ * While firing copy/paste command the summary messsage given with CMD_MSG is expected to ++ * be STR_COPY_PASTE_ERROR_SUMMARY (which is "{8:STRING}") so a true message can be set ++ * later through param #8. The constant below is the index of the param. */ ++static const int COPY_PASTE_ERR_SUMMARY_PARAM = 8; ++ ++/** Executes commands and gathers results of a paste process. */ ++struct PasteCmdHelper { ++ DoCommandFlag dc_flags; ///< Flags to use when executing commands ++ Money overal_cost; ///< Overal cost of currently executed paste command. ++ CommandCost last_result; ///< Result of the most recent PasteCmdHelper::DoCommand / PasteCmdHelper::CollectCost / PasteCmdHelper::CollectError. ++ bool had_success; ///< If currently executed paste command had a successful action (at least once). ++ StringID err_summary; ///< Summary message of the paste error. ++ StringID err_message; ///< Detailed message of the paste error. ++ TileIndex err_tile; ///< Tile where the last paste error occured. ++ uint64 err_params[COPY_PASTE_ERR_SUMMARY_PARAM]; ///< Parameters for the paste error ++ ++ void DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd); ++ void CollectCost(const CommandCost &cost, TileIndex tile, StringID error_summary = STR_ERROR_CAN_T_PASTE_HERE); ++ void CollectError(TileIndex tile, StringID error_message, StringID error_summary = STR_ERROR_CAN_T_PASTE_HERE); ++ bool IsInterrupted() const; ++ ++ inline Money GetAvailableMoney() const ++ { ++ return GetAvailableMoneyForCommand() - this->overal_cost; ++ } ++}; ++ ++extern PasteCmdHelper *_current_pasting; ++extern TileIndex _paste_err_tile; ++ ++/** ++ * Check if it is allowed to continue pasting. ++ * @return True if pasting is interrupted, false otherwise ++ */ ++static inline bool IsPastingInterrupted() ++{ ++ return _current_pasting != NULL && _current_pasting->IsInterrupted(); ++} ++ ++void LevelPasteLand(const TileArea &ta, uint height, CopyPasteLevelVariant variant); ++void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta); ++ ++void CopyPastePlaceTracks(GenericTileIndex tile, RailType railtype, TrackBits tracks); ++void CopyPastePlaceCannal(GenericTileIndex tile); ++void CopyPastePlaceRailWaypoint(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent); ++void CopyPastePlaceBuoy(GenericTileIndex tile, StationID sid, WaterClass wc); ++ ++void AfterCopyingStations(const CopyPasteParams ©_paste); ++void AfterPastingStations(const CopyPasteParams ©_paste); ++ ++#endif /* COPYPASTE_CMD_H */ +diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp +index 31e679b..af8015b 100644 +--- a/src/core/bitmath_func.hpp ++++ b/src/core/bitmath_func.hpp +@@ -321,6 +321,39 @@ static inline T ROR(const T x, const uint8 n) + } + + /** ++ * Swap odd and even bits in a variable. ++ * ++ * Bit 0 is swapped with bit 1, bit 2 with bit 3 and so on. ++ * ++ * @param x The value in which we want to swap bits ++ * @return The bit swapped value ++ */ ++template <typename T> ++static inline T SwapOddEvenBits(T x) ++{ ++ return (T)(((x >> 1) & (T)0x5555555555555555LL) | ((x & (T)0x5555555555555555LL) << 1)); ++} ++ ++/** ++ * Interleave bits (a.k.a. Morton code) of two 8-bit integers \c x and \c y. ++ * ++ * The result is computed by putting consecutive \c x bits at even positions and consecutive \c y ++ * bits at odd positions: ++ * ++ * <tt> (X7 X6 ... X1 X0, Y7 Y6 ... Y1 Y0) -> Y7 X7 Y6 X6 ... Y1 X1 Y0 X0 </tt> ++ * ++ * @param x The bits to put at even positions. ++ * @param y The bits to put at odd positions. ++ * @return The interleaved value. ++ */ ++static inline uint16 InterleaveBits8(uint8 x, uint8 y) ++{ ++ /* Found on the "Bit Twiddling Hacks" webpage: http://graphics.stanford.edu/~seander/bithacks.html */ ++ return (((x * 0x0101010101010101ULL & 0x8040201008040201ULL) * 0x0102040810204081ULL >> 49) & 0x5555) | ++ (((y * 0x0101010101010101ULL & 0x8040201008040201ULL) * 0x0102040810204081ULL >> 48) & 0xAAAA); ++} ++ ++/** + * Do an operation for each set bit in a value. + * + * This macros is used to do an operation for each set +diff --git a/src/core/geometry_func.cpp b/src/core/geometry_func.cpp +index 86f317a..9fc1465 100644 +--- a/src/core/geometry_func.cpp ++++ b/src/core/geometry_func.cpp +@@ -12,6 +12,7 @@ + #include "../stdafx.h" + #include "geometry_func.hpp" + #include "math_func.hpp" ++#include "../map_func.h" + + #include "../safeguards.h" + +@@ -28,3 +29,24 @@ Dimension maxdim(const Dimension &d1, const Dimension &d2) + d.height = max(d1.height, d2.height); + return d; + } ++ ++/** ++ * Transform a given Point. ++ * ++ * The center point of the transformation is (0, 0). ++ * For example, point (1, 2) rotated 90 degree left is (-2, 1). ++ * ++ * @param point The Point to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Point. ++ */ ++Point TransformPoint(Point point, DirTransformation transformation) ++{ ++ TileIndexDiffC diff_from_x = TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, transformation)); ++ TileIndexDiffC diff_from_y = TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SE, transformation)); ++ Point ret = { ++ point.x * diff_from_x.x + point.y * diff_from_y.x, ++ point.x * diff_from_x.y + point.y * diff_from_y.y ++ }; ++ return ret; ++} +diff --git a/src/core/geometry_func.hpp b/src/core/geometry_func.hpp +index e7c5325..8891e11 100644 +--- a/src/core/geometry_func.hpp ++++ b/src/core/geometry_func.hpp +@@ -13,7 +13,26 @@ + #define GEOMETRY_FUNC_HPP + + #include "geometry_type.hpp" ++#include "math_func.hpp" ++#include "../direction_func.h" + + Dimension maxdim(const Dimension &d1, const Dimension &d2); + ++/** ++ * Transform a given Dimension. ++ * ++ * The width and the height will be swapped or will stay unchanged based on used transformation. ++ * ++ * @param dim The Dimension to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Dimension. ++ */ ++static inline Dimension TransformDimension(Dimension dim, DirTransformation transformation) ++{ ++ if (TransformAxis(AXIS_X, transformation) != AXIS_X) Swap(dim.width, dim.height); ++ return dim; ++} ++ ++Point TransformPoint(Point point, DirTransformation transformation); ++ + #endif /* GEOMETRY_FUNC_HPP */ +diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp +index df91424..cb6c989 100644 +--- a/src/core/math_func.hpp ++++ b/src/core/math_func.hpp +@@ -86,6 +86,18 @@ static inline T abs(const T a) + } + + /** ++ * Returns the sign of a value. ++ * ++ * @param x The value ++ * @return -1 if the x is negative, +1 if positive, 0 otherwise ++ */ ++template <typename T> ++static inline int sgn(const T x) ++{ ++ return (int)(x > (T)0) - (int)(x < (T)0); ++} ++ ++/** + * Return the smallest multiple of n equal or greater than x + * + * @note n must be a power of 2 +diff --git a/src/crashlog.cpp b/src/crashlog.cpp +index 7ddc213..6bf7b06 100644 +--- a/src/crashlog.cpp ++++ b/src/crashlog.cpp +@@ -381,7 +381,7 @@ bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const + { + /* If the map array doesn't exist, saving will fail too. If the map got + * initialised, there is a big chance the rest is initialised too. */ +- if (_m == NULL) return false; ++ if (_main_map.m == NULL) return false; + + try { + GamelogEmergency(); +diff --git a/src/depot_map.h b/src/depot_map.h +index c304790..97012c1 100644 +--- a/src/depot_map.h ++++ b/src/depot_map.h +@@ -55,7 +55,7 @@ static inline DepotID GetDepotIndex(TileIndex t) + { + /* Hangars don't have a Depot class, thus store no DepotID. */ + assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)); +- return _m[t].m2; ++ return GetTile(t)->m2; + } + + /** +diff --git a/src/direction_func.h b/src/direction_func.h +index 12aee58..327c298 100644 +--- a/src/direction_func.h ++++ b/src/direction_func.h +@@ -13,6 +13,7 @@ + #define DIRECTION_FUNC_H + + #include "direction_type.h" ++#include "core/math_func.hpp" + + /** + * Checks if an integer value is a valid DiagDirection +@@ -278,4 +279,118 @@ static inline bool IsDiagonalDirection(Direction dir) + return (dir & 1) != 0; + } + ++/** ++ * Checks if an integer value is a valid DirTransformation. ++ * ++ * @param transformation The value to check. ++ * @return True if the value belongs to a DirTransformation, false otherwise. ++ */ ++static inline bool IsValidDirTransform(DirTransformation transformation) ++{ ++ return IsInsideMM(transformation, DTR_BEGIN, DTR_END); ++} ++ ++/** ++ * Combine two direction transformations into one. ++ * @param a First transformation. ++ * @param b Second transformation. ++ * @return Transformation that works like firstly applying the 'a' transformation and then the 'b' transformation. ++ */ ++static inline DirTransformation CombineDirTransform(DirTransformation a, DirTransformation b) ++{ ++ /* DirTransformation bit lauout: ++ * 0000irrr ++ * where: ++ * i - DTR_REFLECTION_BIT ++ * rrr - DTR_ROTATION_MASK ++ * ++ * DirTransformation transformation can be expressed as a function of an angle: ++ * f(x) = I * x + R ++ * where ++ * x - direction expressed in angle units (e.g. DiagDir) ++ * I - reflection, -1 to reflect before rotating (DTR_REFLECTION_BIT set), +1 otherwise ++ * R - rotation, number of angle units to add (bits of mask DTR_ROTATION_MASK) ++ * ++ * 1 angle unit is 90 degree. As we work on angles we must use modular arithmetic for ++ * calculations. Modulus is 4 because 360 degree is 4 our angle units. To apply ++ * modulus we can simply bitmask result with DTR_ROTATION_MASK. ++ * ++ * To combine two transformations ++ * a(x) = IA * x + RA ++ * b(x) = IB * x + RB ++ * into one ++ * c(x) = IC * x + RC ++ * we must compose functions ++ * c(x) = b(a(x)) = IB * (IA * x + RA) + RB = IA * IB * x + IB * RA + RB ++ * From above ++ * IC = IA * IB - so we can XOR reflection bits together to get resulting reflection bit ++ * RC = IB * RA + RB - so we evaluate RB+RA or RB-RA based on reflection bit of transformation B to get resulting rotation bits */ ++ return (DirTransformation)(((a ^ b) & DTR_REFLECTION_BIT) | // calculate reflection bit ++ (((b & DTR_REFLECTION_BIT) ? (b - a) : (b + a)) & DTR_ROTATION_MASK)); // calculate rotation bits ++} ++ ++/** ++ * Invert given transformation. ++ * @param transformation Tranformation to invert. ++ * @return Inverted transformation transformation. ++ */ ++static inline DirTransformation InvertDirTransform(DirTransformation transformation) ++{ ++ /* to revert a reflection reflect again, transformation is the same (involution) */ ++ if (transformation & DTR_REFLECTION_BIT) return transformation; ++ ++ /* to revert a rotation rotate in opposite direction */ ++ return (DirTransformation)((-transformation) & DTR_ROTATION_MASK); ++} ++ ++static inline DirTransformation DirRotation(DiagDirDiff angle) ++{ ++ return (DirTransformation)((uint)angle % DIAGDIR_END); ++} ++ ++static inline DirTransformation DirReflection(Direction axis) ++{ ++ return (DirTransformation)(((axis - DiagDirToDir(DIAGDIR_BEGIN)) & DTR_ROTATION_MASK) | DTR_REFLECTION_BIT); ++} ++ ++static inline DirTransformation DirReflection(Axis axis) ++{ ++ return (DirTransformation)((2 * axis) | DTR_REFLECTION_BIT); ++} ++ ++/** ++ * Transform Axis by a given transformation. ++ * @param axis Axis to transform. ++ * @param transformation Transformation to use. ++ * @return Transformed Axis. ++ */ ++static inline Axis TransformAxis(Axis axis, DirTransformation transformation) ++{ ++ return (Axis)(axis ^ (transformation & 1)); ++} ++ ++/** ++ * Transform Direction by given transformation. ++ * @param direction Direction to transform. ++ * @param transformation Transformation to use. ++ * @return Transformed Direction. ++ */ ++static inline Direction TransformDir(Direction direction, DirTransformation transformation) ++{ ++ if (transformation & DTR_REFLECTION_BIT) direction = (Direction)(2 * DIR_NE - direction); // reflect against X-axis ++ return ChangeDir(direction, (DirDiff)(2 * transformation)); // rotate and cut off overflowing bits ++} ++ ++/** ++ * Transform DiagDirection by a given transformation. ++ * @param diag_dir DiagDirection to transform. ++ * @param transformation Transformation to use. ++ * @return Transformed DiagDirection. ++ */ ++static inline DiagDirection TransformDiagDir(DiagDirection diag_dir, DirTransformation transformation) ++{ ++ if (transformation & DTR_REFLECTION_BIT) diag_dir = (DiagDirection)(2 * DIAGDIR_NE - diag_dir); // reflect against X-axis ++ return ChangeDiagDir(diag_dir, (DiagDirDiff)transformation); // rotate and cut off overflowing bits ++} ++ + #endif /* DIRECTION_FUNC_H */ +diff --git a/src/direction_type.h b/src/direction_type.h +index e6e08a1..9c3a5b1 100644 +--- a/src/direction_type.h ++++ b/src/direction_type.h +@@ -115,6 +115,25 @@ enum DiagDirDiff { + /** Allow incrementing of DiagDirDiff variables */ + DECLARE_POSTFIX_INCREMENT(DiagDirDiff) + ++/** Represents transformations to rotate/reflect directions. */ ++enum DirTransformation { ++ DTR_BEGIN = 0, ///< Used for iterations. ++ ++ DTR_IDENTITY = 0, ///< Identity transformation (no tranformation at all). ++ DTR_ROTATE_90_R = 1, ///< Rotate by 90 degree clockwise. ++ DTR_ROTATE_180 = 2, ///< Rotate by 180 degree. ++ DTR_ROTATE_90_L = 3, ///< Rotate by 90 degree counterclockwise. ++ ++ DTR_REFLECT_NE_SW = 4, ///< Reflect in respect to a Northeast-Southwest axis. ++ DTR_REFLECT_W_E = 5, ///< Reflect in respect to a West-East axis. ++ DTR_REFLECT_NW_SE = 6, ///< Reflect in respect to a Northwest-Southeast axis. ++ DTR_REFLECT_N_S = 7, ///< Reflect in respect to a North-South axis. ++ ++ DTR_END = 8, ///< Used for iterations. ++ ++ DTR_ROTATION_MASK = 0x03, ///< Rotation mask. The number formed by thiese bits tells how much to rotate object clockwise (in 90 degree units). ++ DTR_REFLECTION_BIT = 0x04, ///< Reflection bit. Indicates if to reflect object in respect to X-axis before rotating it. ++}; + + /** + * Enumeration for the two axis X and Y +diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp +index f6699da..7046127 100644 +--- a/src/dock_gui.cpp ++++ b/src/dock_gui.cpp +@@ -193,10 +193,11 @@ struct BuildDocksToolbarWindow : Window { + break; + + case WID_DT_STATION: { // Build station button ++ uint32 p1 = _settings_game.station.adjacent_stations && _ctrl_pressed; // adjacent? + uint32 p2 = (uint32)INVALID_STATION << 16; // no station to join + + /* tile is always the land tile, so need to evaluate _thd.pos */ +- CommandContainer cmdcont = { tile, _ctrl_pressed, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; ++ CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; + + /* Determine the watery part of the dock. */ + DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); +diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp +index ad64ae8..0bc0d70 100644 +--- a/src/genworld_gui.cpp ++++ b/src/genworld_gui.cpp +@@ -20,6 +20,7 @@ + #include "sound_func.h" + #include "fios.h" + #include "string_func.h" ++#include "gui.h" + #include "widgets/dropdown_type.h" + #include "widgets/dropdown_func.h" + #include "querystring_gui.h" +@@ -281,12 +282,37 @@ static void LandscapeGenerationCallback(Window *w, bool confirmed) + if (confirmed) StartGeneratingLandscape((GenerateLandscapeWindowMode)w->window_number); + } + +-static DropDownList *BuildMapsizeDropDown() ++/** ++ * Check if map size set lies in allowed boundaries. ++ * @param print_warning If set to true, messagebox with warning is printed out if size is outside limits. ++ * @return true if size is ok, false otherwise. ++ */ ++static bool CheckMapSize(bool print_warning = true) ++{ ++ uint64 tiles = 1ULL << (_settings_newgame.game_creation.map_x + _settings_newgame.game_creation.map_y); ++ ++ if (_settings_newgame.game_creation.map_x + _settings_newgame.game_creation.map_y > MAX_MAP_TILES_BITS) { ++ if (print_warning) { ++ SetDParam(0, MAX_MAP_TILES); ++ SetDParam(1, tiles); ++ ShowErrorMessage(STR_MAPGEN_TOO_MANY_TILES_MESSAGE, INVALID_STRING_ID, WL_ERROR, 0, 0); ++ } ++ return false; ++ } ++ return true; ++} ++ ++/** ++ * Build dropdown list with map sizes ++ * Dimension selected in the other dropdown is used to suggest which choices are 'valid' ++ * @param other_dimension Dimension specified by the second dropdown. ++ */ ++static DropDownList *BuildMapsizeDropDown(int other_dimension) + { + DropDownList *list = new DropDownList(); + + for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) { +- DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); ++ DropDownListParamStringItem *item = new DropDownListParamStringItem((i + other_dimension > MAX_MAP_TILES_BITS) ? STR_RED_INT : STR_JUST_INT, i, false); + item->SetParam(0, 1 << i); + *list->Append() = item; + } +@@ -314,6 +340,14 @@ struct GenerateLandscapeWindow : public Window { + char name[64]; + GenerateLandscapeWindowMode mode; + ++ void SetDropDownColor() ++ { ++ /* Draw sizes in mapsize selection dropdowns in red if too large size is selected */ ++ bool mapsize_valid = CheckMapSize(false); ++ this->GetWidget<NWidgetCore>(WID_GL_MAPSIZE_X_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; ++ this->GetWidget<NWidgetCore>(WID_GL_MAPSIZE_Y_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; ++ } ++ + GenerateLandscapeWindow(WindowDesc *desc, WindowNumber number = 0) : Window(desc) + { + this->InitNested(number); +@@ -322,6 +356,8 @@ struct GenerateLandscapeWindow : public Window { + + this->mode = (GenerateLandscapeWindowMode)this->window_number; + ++ SetDropDownColor(); ++ + /* Disable town, industry and trees in SE */ + this->SetWidgetDisabledState(WID_GL_TOWN_PULLDOWN, _game_mode == GM_EDITOR); + this->SetWidgetDisabledState(WID_GL_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR); +@@ -538,11 +574,11 @@ struct GenerateLandscapeWindow : public Window { + break; + + case WID_GL_MAPSIZE_X_PULLDOWN: // Mapsize X +- ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_x, WID_GL_MAPSIZE_X_PULLDOWN); ++ ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_y), _settings_newgame.game_creation.map_x, WID_GL_MAPSIZE_X_PULLDOWN); + break; + + case WID_GL_MAPSIZE_Y_PULLDOWN: // Mapsize Y +- ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_GL_MAPSIZE_Y_PULLDOWN); ++ ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_x), _settings_newgame.game_creation.map_y, WID_GL_MAPSIZE_Y_PULLDOWN); + break; + + case WID_GL_TOWN_PULLDOWN: // Number of towns +@@ -554,6 +590,7 @@ struct GenerateLandscapeWindow : public Window { + break; + + case WID_GL_GENERATE_BUTTON: { // Generate ++ if (!CheckMapSize()) break; + /* Get rotated map size. */ + uint map_x; + uint map_y; +@@ -716,8 +753,14 @@ struct GenerateLandscapeWindow : public Window { + virtual void OnDropdownSelect(int widget, int index) + { + switch (widget) { +- case WID_GL_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break; +- case WID_GL_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break; ++ case WID_GL_MAPSIZE_X_PULLDOWN: ++ _settings_newgame.game_creation.map_x = index; ++ SetDropDownColor(); ++ break; ++ case WID_GL_MAPSIZE_Y_PULLDOWN: ++ _settings_newgame.game_creation.map_y = index; ++ SetDropDownColor(); ++ break; + case WID_GL_TREE_PULLDOWN: _settings_newgame.game_creation.tree_placer = index; break; + case WID_GL_RIVER_PULLDOWN: _settings_newgame.game_creation.amount_of_rivers = index; break; + case WID_GL_SMOOTHNESS_PULLDOWN: _settings_newgame.game_creation.tgen_smoothness = index; break; +@@ -881,10 +924,19 @@ struct CreateScenarioWindow : public Window + { + uint widget_id; + ++ void SetDropDownColor() ++ { ++ /* Draw sizes in mapsize selection dropdowns in red if too large size is selected */ ++ bool mapsize_valid = CheckMapSize(false); ++ this->GetWidget<NWidgetCore>(WID_CS_MAPSIZE_X_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; ++ this->GetWidget<NWidgetCore>(WID_CS_MAPSIZE_Y_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; ++ } ++ + CreateScenarioWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) + { + this->InitNested(window_number); + this->LowerWidget(_settings_newgame.game_creation.landscape + WID_CS_TEMPERATE); ++ SetDropDownColor(); + } + + virtual void SetStringParameters(int widget) const +@@ -908,6 +960,8 @@ struct CreateScenarioWindow : public Window + } + } + ++ ++ + virtual void OnPaint() + { + this->SetWidgetDisabledState(WID_CS_START_DATE_DOWN, _settings_newgame.game_creation.starting_year <= MIN_YEAR); +@@ -961,18 +1015,20 @@ struct CreateScenarioWindow : public Window + break; + + case WID_CS_MAPSIZE_X_PULLDOWN: // Mapsize X +- ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_x, WID_CS_MAPSIZE_X_PULLDOWN); ++ ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_y), _settings_newgame.game_creation.map_x, WID_CS_MAPSIZE_X_PULLDOWN); + break; + + case WID_CS_MAPSIZE_Y_PULLDOWN: // Mapsize Y +- ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_CS_MAPSIZE_Y_PULLDOWN); ++ ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_x), _settings_newgame.game_creation.map_y, WID_CS_MAPSIZE_Y_PULLDOWN); + break; + + case WID_CS_EMPTY_WORLD: // Empty world / flat world ++ if (!CheckMapSize()) break; + StartGeneratingLandscape(GLWM_SCENARIO); + break; + + case WID_CS_RANDOM_WORLD: // Generate ++ if (!CheckMapSize()) break; + ShowGenerateLandscape(); + break; + +@@ -1031,6 +1087,8 @@ struct CreateScenarioWindow : public Window + case WID_CS_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break; + case WID_CS_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break; + } ++ SetDropDownColor(); ++ + this->SetDirty(); + } + +diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp +index 10bc0af..d170310 100644 +--- a/src/gfxinit.cpp ++++ b/src/gfxinit.cpp +@@ -193,6 +193,9 @@ static void LoadSpriteTables() + ); + } + ++ /* Load clipboard graphics */ ++ LoadGrfFile("clipboard.grf", SPR_CLIPBOARD_BASE, i++); ++ + /* Initialize the unicode to sprite mapping table */ + InitializeUnicodeGlyphMap(); + +diff --git a/src/group_gui.cpp b/src/group_gui.cpp +index 81fb120..c7ddde2 100644 +--- a/src/group_gui.cpp ++++ b/src/group_gui.cpp +@@ -74,6 +74,7 @@ static const NWidgetPart _nested_group_widgets[] = { + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA), + NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(), + EndContainer(), ++ NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_GROUP_INFO), SetMinimalSize(200, 25), SetFill(1, 0), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_VEHICLE), SetMinimalSize(248, 0), SetMatrixDataTip(1, 0, STR_NULL), SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_GL_LIST_VEHICLE_SCROLLBAR), +@@ -364,6 +365,7 @@ public: + + /* Minimum height is the height of the list widget minus all and default vehicles... */ + size->height = 4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - 2 * this->tiny_step_height; ++ size->height += 25; + + /* ... minus the buttons at the bottom ... */ + uint max_icon_height = GetSpriteSize(this->GetWidget<NWidgetCore>(WID_GL_CREATE_GROUP)->widget_data).height; +@@ -527,6 +529,29 @@ public: + DrawGroupInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, DEFAULT_GROUP); + break; + ++ case WID_GL_GROUP_INFO: { ++ Money this_year = 0; ++ Money last_year = 0; ++ ++ for (uint i = 0; i < this->vehicles.Length(); i++) { ++ const Vehicle *v = this->vehicles[i]; ++ ++ assert(v->owner == this->owner); ++ ++ if (this->vli.index == ALL_GROUP || v->group_id == this->vli.index) { ++ this_year += v->GetDisplayProfitThisYear(); ++ last_year += v->GetDisplayProfitLastYear(); ++ } ++ } ++ ++ SetDParam(0, this_year); ++ DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + 1, STR_GROUP_PROFIT_THIS_YEAR, TC_BLACK); ++ SetDParam(0, last_year); ++ DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + 2, STR_GROUP_PROFIT_LAST_YEAR, TC_BLACK); ++ ++ break; ++ } ++ + case WID_GL_LIST_GROUP: { + int y1 = r.top + WD_FRAMERECT_TOP; + int max = min(this->group_sb->GetPosition() + this->group_sb->GetCapacity(), this->groups.Length()); +@@ -885,7 +910,6 @@ public: + } + }; + +- + static WindowDesc _other_group_desc( + WDP_AUTO, "list_groups", 460, 246, + WC_INVALID, WC_NONE, +diff --git a/src/gui.h b/src/gui.h +index 39f1ea6..9a83d2c 100644 +--- a/src/gui.h ++++ b/src/gui.h +@@ -39,6 +39,9 @@ Window *ShowBuildDocksScenToolbar(); + /* airport_gui.cpp */ + Window *ShowBuildAirToolbar(); + ++/* clipboard_gui.cpp */ ++Window *ShowClipboardToolbar(); ++ + /* tgp_gui.cpp */ + void ShowGenerateLandscape(); + void ShowHeightmapLoad(); +diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp +index 5971964..47dc3e5 100644 +--- a/src/industry_cmd.cpp ++++ b/src/industry_cmd.cpp +@@ -343,6 +343,8 @@ static void DrawTile_Industry(TileInfo *ti) + DrawGroundSprite(image, GroundSpritePaletteTransform(image, dits->ground.pal, GENERAL_SPRITE_COLOUR(ind->random_colour))); + } + ++ DrawOverlay(ti, MP_INDUSTRY); ++ + /* If industries are transparent and invisible, do not draw the upper part */ + if (IsInvisibilitySet(TO_INDUSTRIES)) return; + +@@ -2859,4 +2861,5 @@ extern const TileTypeProcs _tile_type_industry_procs = { + NULL, // vehicle_enter_tile_proc + GetFoundation_Industry, // get_foundation_proc + TerraformTile_Industry, // terraform_tile_proc ++ NULL, // copypaste_tile_proc + }; +diff --git a/src/industry_map.h b/src/industry_map.h +index 9d2e3de..e0193f0 100644 +--- a/src/industry_map.h ++++ b/src/industry_map.h +@@ -65,7 +65,7 @@ enum IndustryGraphics { + static inline IndustryID GetIndustryIndex(TileIndex t) + { + assert(IsTileType(t, MP_INDUSTRY)); +- return _m[t].m2; ++ return GetTile(t)->m2; + } + + /** +@@ -77,7 +77,7 @@ static inline IndustryID GetIndustryIndex(TileIndex t) + static inline bool IsIndustryCompleted(TileIndex t) + { + assert(IsTileType(t, MP_INDUSTRY)); +- return HasBit(_m[t].m1, 7); ++ return HasBit(GetTile(t)->m1, 7); + } + + IndustryType GetIndustryType(TileIndex tile); +@@ -90,7 +90,7 @@ IndustryType GetIndustryType(TileIndex tile); + static inline void SetIndustryCompleted(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- SB(_m[tile].m1, 7, 1, 1); ++ SB(GetTile(tile)->m1, 7, 1, 1); + } + + /** +@@ -102,7 +102,7 @@ static inline void SetIndustryCompleted(TileIndex tile) + static inline byte GetIndustryConstructionStage(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(_m[tile].m1, 0, 2); ++ return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(GetTile(tile)->m1, 0, 2); + } + + /** +@@ -114,7 +114,7 @@ static inline byte GetIndustryConstructionStage(TileIndex tile) + static inline void SetIndustryConstructionStage(TileIndex tile, byte value) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- SB(_m[tile].m1, 0, 2, value); ++ SB(GetTile(tile)->m1, 0, 2, value); + } + + /** +@@ -127,7 +127,7 @@ static inline void SetIndustryConstructionStage(TileIndex tile, byte value) + static inline IndustryGfx GetCleanIndustryGfx(TileIndex t) + { + assert(IsTileType(t, MP_INDUSTRY)); +- return _m[t].m5 | (GB(_me[t].m6, 2, 1) << 8); ++ return GetTile(t)->m5 | (GB(GetTileEx(t)->m6, 2, 1) << 8); + } + + /** +@@ -151,8 +151,8 @@ static inline IndustryGfx GetIndustryGfx(TileIndex t) + static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) + { + assert(IsTileType(t, MP_INDUSTRY)); +- _m[t].m5 = GB(gfx, 0, 8); +- SB(_me[t].m6, 2, 1, GB(gfx, 8, 1)); ++ GetTile(t)->m5 = GB(gfx, 0, 8); ++ SB(GetTileEx(t)->m6, 2, 1, GB(gfx, 8, 1)); + } + + /** +@@ -164,7 +164,7 @@ static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) + static inline byte GetIndustryConstructionCounter(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- return GB(_m[tile].m1, 2, 2); ++ return GB(GetTile(tile)->m1, 2, 2); + } + + /** +@@ -176,7 +176,7 @@ static inline byte GetIndustryConstructionCounter(TileIndex tile) + static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- SB(_m[tile].m1, 2, 2, value); ++ SB(GetTile(tile)->m1, 2, 2, value); + } + + /** +@@ -189,8 +189,8 @@ static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) + static inline void ResetIndustryConstructionStage(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- SB(_m[tile].m1, 0, 4, 0); +- SB(_m[tile].m1, 7, 1, 0); ++ SB(GetTile(tile)->m1, 0, 4, 0); ++ SB(GetTile(tile)->m1, 7, 1, 0); + } + + /** +@@ -201,7 +201,7 @@ static inline void ResetIndustryConstructionStage(TileIndex tile) + static inline byte GetIndustryAnimationLoop(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- return _m[tile].m4; ++ return GetTile(tile)->m4; + } + + /** +@@ -213,7 +213,7 @@ static inline byte GetIndustryAnimationLoop(TileIndex tile) + static inline void SetIndustryAnimationLoop(TileIndex tile, byte count) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- _m[tile].m4 = count; ++ GetTile(tile)->m4 = count; + } + + /** +@@ -226,7 +226,7 @@ static inline void SetIndustryAnimationLoop(TileIndex tile, byte count) + static inline byte GetIndustryRandomBits(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- return _m[tile].m3; ++ return GetTile(tile)->m3; + } + + /** +@@ -239,7 +239,7 @@ static inline byte GetIndustryRandomBits(TileIndex tile) + static inline void SetIndustryRandomBits(TileIndex tile, byte bits) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- _m[tile].m3 = bits; ++ GetTile(tile)->m3 = bits; + } + + /** +@@ -252,7 +252,7 @@ static inline void SetIndustryRandomBits(TileIndex tile, byte bits) + static inline byte GetIndustryTriggers(TileIndex tile) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- return GB(_me[tile].m6, 3, 3); ++ return GB(GetTileEx(tile)->m6, 3, 3); + } + + +@@ -266,7 +266,7 @@ static inline byte GetIndustryTriggers(TileIndex tile) + static inline void SetIndustryTriggers(TileIndex tile, byte triggers) + { + assert(IsTileType(tile, MP_INDUSTRY)); +- SB(_me[tile].m6, 3, 3, triggers); ++ SB(GetTileEx(tile)->m6, 3, 3, triggers); + } + + /** +@@ -280,14 +280,14 @@ static inline void SetIndustryTriggers(TileIndex tile, byte triggers) + static inline void MakeIndustry(TileIndex t, IndustryID index, IndustryGfx gfx, uint8 random, WaterClass wc) + { + SetTileType(t, MP_INDUSTRY); +- _m[t].m1 = 0; +- _m[t].m2 = index; ++ GetTile(t)->m1 = 0; ++ GetTile(t)->m2 = index; + SetIndustryRandomBits(t, random); // m3 +- _m[t].m4 = 0; ++ GetTile(t)->m4 = 0; + SetIndustryGfx(t, gfx); // m5, part of m6 + SetIndustryTriggers(t, 0); // rest of m6 + SetWaterClass(t, wc); +- _me[t].m7 = 0; ++ GetTileEx(t)->m7 = 0; + } + + #endif /* INDUSTRY_MAP_H */ +diff --git a/src/landscape.cpp b/src/landscape.cpp +index d1c73fd..560be99 100644 +--- a/src/landscape.cpp ++++ b/src/landscape.cpp +@@ -723,12 +723,12 @@ void RunTileLoop() + * shift register (LFSR). This allows a deterministic pseudorandom ordering, but + * still with minimal state and fast iteration. */ + +- /* Maximal length LFSR feedback terms, from 12-bit (for 64x64 maps) to 24-bit (for 4096x4096 maps). ++ /* Maximal length LFSR feedback terms, from 12-bit (for 64x64 maps) to 26-bit. + * Extracted from http://www.ece.cmu.edu/~koopman/lfsr/ */ + static const uint32 feedbacks[] = { +- 0xD8F, 0x1296, 0x2496, 0x4357, 0x8679, 0x1030E, 0x206CD, 0x403FE, 0x807B8, 0x1004B2, 0x2006A8, 0x4004B2, 0x800B87 ++ 0xD8F, 0x1296, 0x2496, 0x4357, 0x8679, 0x1030E, 0x206CD, 0x403FE, 0x807B8, 0x1004B2, 0x2006A8, 0x4004B2, 0x800B87, 0x10004F3, 0x200072D + }; +- assert_compile(lengthof(feedbacks) == 2 * MAX_MAP_SIZE_BITS - 2 * MIN_MAP_SIZE_BITS + 1); ++ assert_compile(lengthof(feedbacks) == MAX_MAP_TILES_BITS - 2 * MIN_MAP_SIZE_BITS + 1); + const uint32 feedback = feedbacks[MapLogX() + MapLogY() - 2 * MIN_MAP_SIZE_BITS]; + + /* We update every tile every 256 ticks, so divide the map size by 2^8 = 256 */ +diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt +index 077092b..7419fbf 100644 +--- a/src/lang/afrikaans.txt ++++ b/src/lang/afrikaans.txt +@@ -4880,10 +4880,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Spektator, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/basque.txt b/src/lang/basque.txt +index ede8426..e03f226 100644 +--- a/src/lang/basque.txt ++++ b/src/lang/basque.txt +@@ -4726,10 +4726,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Ikuslea, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt +index d5b64be..702de7b 100644 +--- a/src/lang/catalan.txt ++++ b/src/lang/catalan.txt +@@ -4880,10 +4880,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :{G=Masculin}Espectador, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/danish.txt b/src/lang/danish.txt +index 175092f..010df52 100644 +--- a/src/lang/danish.txt ++++ b/src/lang/danish.txt +@@ -4879,10 +4879,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Tilskuer, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt +index 50470a2..b4af63f7 100644 +--- a/src/lang/dutch.txt ++++ b/src/lang/dutch.txt +@@ -4879,10 +4879,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Toeschouwer, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/english.txt b/src/lang/english.txt +index aaa794b..cd57a46 100644 +--- a/src/lang/english.txt ++++ b/src/lang/english.txt +@@ -232,6 +232,7 @@ STR_TOOLTIP_GROUP_ORDER :{BLACK}Select g + STR_TOOLTIP_SORT_ORDER :{BLACK}Select sorting order (descending/ascending) + STR_TOOLTIP_SORT_CRITERIA :{BLACK}Select sorting criteria + STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select filtering criteria ++STR_BUTTON_COVERAGE :{BLACK}Coverage + STR_BUTTON_SORT_BY :{BLACK}Sort by + STR_BUTTON_LOCATION :{BLACK}Location + STR_BUTTON_RENAME :{BLACK}Rename +@@ -453,6 +454,7 @@ STR_AIRCRAFT_MENU_AIRPORT_CONSTRUCTION :Airport constru + + ############ range for landscaping menu starts + STR_LANDSCAPING_MENU_LANDSCAPING :Landscaping ++STR_LANDSCAPING_MENU_CLIPBOARD :Clipboard + STR_LANDSCAPING_MENU_PLANT_TREES :Plant trees + STR_LANDSCAPING_MENU_PLACE_SIGN :Place sign + ############ range ends here +@@ -884,6 +886,8 @@ STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copy to + STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copy the location of the main view to this viewport + STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Paste from viewport + STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Paste the location of this viewport to the main view ++STR_EXTRA_VIEW_FOLLOW_CURSOR :{BLACK}Follow cursor ++STR_EXTRA_VIEW_FOLLOW_CURSOR_TT :{BLACK}Follow the position of the cursor in this view + + # Game options window + STR_GAME_OPTIONS_CAPTION :{WHITE}Game Options +@@ -910,7 +914,7 @@ STR_GAME_OPTIONS_CURRENCY_ISK :Icelandic Krona + STR_GAME_OPTIONS_CURRENCY_ITL :Italian Lira (ITL) + STR_GAME_OPTIONS_CURRENCY_NLG :Dutch Guilder (NLG) + STR_GAME_OPTIONS_CURRENCY_NOK :Norwegian Krone (NOK) +-STR_GAME_OPTIONS_CURRENCY_PLN :Polish Złoty (PLN) ++STR_GAME_OPTIONS_CURRENCY_PLN :Polish Z?oty (PLN) + STR_GAME_OPTIONS_CURRENCY_RON :Romanian Leu (RON) + STR_GAME_OPTIONS_CURRENCY_RUR :Russian Rubles (RUR) + STR_GAME_OPTIONS_CURRENCY_SIT :Slovenian Tolar (SIT) +@@ -1253,6 +1257,9 @@ STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Allow construct + STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Changing this setting is not possible when there are vehicles + STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastructure maintenance: {STRING2} + STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, infrastructure causes maintenance costs. The cost grows over-proportional with the network size, thus affecting bigger companies more than smaller ones ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY :Clipboard capacity: {STRING2} ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE :{0:NUM}x{0:NUM} tiles ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT :Maximum allowed width/height of area that can be copied into clipboard + + STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Airports never expire: {STRING2} + STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Enabling this setting makes each airport type stay available forever after its introduction +@@ -1287,6 +1294,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Show town popul + STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Display the population of towns in their label on the map + STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Thickness of lines in graphs: {STRING2} + STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Width of the line in the graphs. A thin line is more precisely readable, a thicker line is easier to see and colours are easier to distinguish ++STR_CONFIG_SETTING_FORECAST_DISPLAY :{LTBLUE}Display supply and demand forecasting on station building: {ORANGE}{STRING2} ++STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT :Display supply and demand forecasting on station building? + + STR_CONFIG_SETTING_LANDSCAPE :Landscape: {STRING2} + STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landscapes define basic gameplay scenarios with different cargos and town growth requirements. NewGRF and Game Scripts allow finer control though +@@ -1411,6 +1420,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Keep building t + STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Keep the building tools for bridges, tunnels, etc. open after use + STR_CONFIG_SETTING_EXPENSES_LAYOUT :Group expenses in company finance window: {STRING2} + STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Define the layout for the company expenses window ++STR_CONFIG_SETTING_TOWNRATING_INDICATOR :Show town rating indicators: {STRING2} + + STR_CONFIG_SETTING_SOUND_TICKER :News ticker: {STRING2} + STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Play sound for summarised news messages +@@ -1460,6 +1470,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Allow AI comput + STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#opcodes before scripts are suspended: {STRING2} + STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximum number of computation steps that a script can take in one turn + ++STR_CONFIG_SETTING_SIMULATE_SIGNALS :{LTBLUE}Simulate signals in tunnels, bridges every: {ORANGE}{STRING1} tile{P 0:1 "" s} + STR_CONFIG_SETTING_SERVINT_ISPERCENT :Service intervals are in percents: {STRING2} + STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Choose whether servicing of vehicles is triggered by the time passed since last service or by reliability dropping by a certain percentage of the maximum reliability + STR_CONFIG_SETTING_SERVINT_TRAINS :Default service interval for trains: {STRING2} +@@ -2275,6 +2286,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Toggle t + STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Toggle transparency for structures like lighthouses and antennas. Ctrl+Click to lock + STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Toggle transparency for catenary. Ctrl+Click to lock + STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Toggle transparency for loading indicators. Ctrl+Click to lock ++STR_TRANSPARENT_TUNNELS_TOOLTIP :{BLACK}Toggle transparency for vehicles in tunnels. Ctrl+Click to lock. + STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Set objects invisible instead of transparent + + # Linkgraph legend window +@@ -2385,6 +2397,35 @@ STR_BRIDGE_NAME_CONCRETE :Concrete + STR_BRIDGE_NAME_TUBULAR_STEEL :Tubular, Steel + STR_BRIDGE_TUBULAR_SILICON :Tubular, Silicon + ++# Clipboard toolbar window ++STR_CLIPBOARD_TOOLBAR_CAPTION :{WHITE}Clipboard ++STR_CLIPBOARD_TOOLTIP_SWITCH_TO_1ST_CLIPBOARD :{BLACK}Switch to first clipboard ++STR_CLIPBOARD_TOOLTIP_SWITCH_TO_2ND_CLIPBOARD :{BLACK}Switch to second clipboard ++STR_CLIPBOARD_TOOLTIP_SWITCH_TO_3RD_CLIPBOARD :{BLACK}Switch to third clipboard ++STR_CLIPBOARD_TOOLTIP_SWITCH_TO_4TH_CLIPBOARD :{BLACK}Switch to fourth clipboard ++STR_CLIPBOARD_TOOLTIP_COPY :{BLACK}Select area to copy ++STR_CLIPBOARD_TOOLTIP_PASTE :{BLACK}Paste selected area ++STR_CLIPBOARD_TOOLTIP_SELECT_COPY_AREA :{BLACK}Select source area to be copied ++STR_CLIPBOARD_TOOLTIP_INSTANT_COPY_PASTE :{BLACK}Copy previously selected area and past it at a given location ++STR_CLIPBOARD_TOOLTIP_COPY_WITH_RAIL_TRANSPORT :{BLACK}Paste rail transport infrastructure ++STR_CLIPBOARD_TOOLTIP_COPY_WITH_ROAD_TRANSPORT :{BLACK}Paste road transport infrastructure ++STR_CLIPBOARD_TOOLTIP_COPY_WITH_WATER_TRANSPORT :{BLACK}Paste water transport infrastructure ++STR_CLIPBOARD_TOOLTIP_COPY_WITH_AIR_TRANSPORT :{BLACK}Paste air transport infrastructure ++STR_CLIPBOARD_TOOLTIP_TERRAFORM :{BLACK}Terraform land when pasting{}(red) no terraforming{}(yellow) minimal terraforming{}(green) full terraforming ++STR_CLIPBOARD_TOOLTIP_CONVERT_RAIL :{BLACK}Converts rail infrastructure to{}current rail type when pasting ++STR_CLIPBOARD_TOOLTIP_MIRROR_SIGNALS :{BLACK}Mirror signals when pasting ++STR_CLIPBOARD_TOOLTIP_UPGRADE_BRIDGES :{BLACK}Upgrade bridges when pasting ++STR_CLIPBOARD_TOOLTIP_WITH_STATIONS :{BLACK}Paste stations and waypoints too ++STR_CLIPBOARD_TOOLTIP_STOP_ON_FAILURE :{BLACK}Stop pasting immidiately if something is failing ++STR_CLIPBOARD_TOOLTIP_TRANSFORMATION :{BLACK}Current transformation,{}click to reset ++STR_CLIPBOARD_TOOLTIP_ROTATE_LEFT :{BLACK}Rotate by 90 degree anticlockwise ++STR_CLIPBOARD_TOOLTIP_ROTATE_RIGHT :{BLACK}Rotate by 90 degree clockwise ++STR_CLIPBOARD_TOOLTIP_REFLECT_NE_SW :{BLACK}Reflect against NW-SE axis ++STR_CLIPBOARD_TOOLTIP_REFLECT_NW_SE :{BLACK}Reflect against NE-SW axis ++STR_CLIPBOARD_HEIGHT_DIFF :{GOLD}{STRING1} ++STR_CLIPBOARD_HEIGHT_DIFF_NEGATIVE :-{NUM} ++STR_CLIPBOARD_HEIGHT_DIFF_NEUTRAL : {NUM} ++STR_CLIPBOARD_HEIGHT_DIFF_POSITIVE :+{NUM} + + # Road construction toolbar + STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION :{WHITE}Road Construction +@@ -2474,6 +2515,7 @@ STR_LANDSCAPING_TOOLBAR :{WHITE}Landscap + STR_LANDSCAPING_TOOLTIP_LOWER_A_CORNER_OF_LAND :{BLACK}Lower a corner of land. Dragging lowers the first selected corner and levels the selected area to the new corner height. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate + STR_LANDSCAPING_TOOLTIP_RAISE_A_CORNER_OF_LAND :{BLACK}Raise a corner of land. Dragging raises the first selected corner and levels the selected area to the new corner height. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate + STR_LANDSCAPING_LEVEL_LAND_TOOLTIP :{BLACK}Level an area of land to the height of the first selected corner. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate ++STR_LANDSCAPING_TOOLTIP_SHOW_CLIPBOARD_TOOLBAR :{BLACK}Show clipboard toolbar + STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase land for future use. Shift toggles building/showing cost estimate + + # Object construction window +@@ -2550,6 +2592,12 @@ STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospect + STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Build + STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fund + ++# Town rating indicators ++STR_TOWN_RATING_INCREASED_TINY :{TINY_FONT}{GREEN}Rating increased to: {STRING} ++STR_TOWN_RATING_INCREASED :{GREEN}Rating increased to: {STRING} ++STR_TOWN_RATING_DECREASED_TINY :{TINY_FONT}{RED}Rating decreased to: {STRING} ++STR_TOWN_RATING_DECREASED :{RED}Rating decreased to: {STRING} ++ + # Industry cargoes window + STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Industry chain for {STRING} industry + STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Industry chain for {STRING} cargo +@@ -2658,8 +2706,10 @@ STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Ship depot + # Industries come directly from their industry names + + STR_LAI_TUNNEL_DESCRIPTION_RAILROAD :Railway tunnel ++STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL :Railway tunnel with signal simulation + STR_LAI_TUNNEL_DESCRIPTION_ROAD :Road tunnel + ++STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL :Railway bridge with signal simulation + STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :Steel suspension rail bridge + STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL :Steel girder rail bridge + STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL :Steel cantilever rail bridge +@@ -2753,6 +2803,7 @@ STR_MAPGEN_HEIGHTMAP_ROTATION :{BLACK}Heightma + STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Heightmap name: + STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Size: + STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} ++STR_MAPGEN_TOO_MANY_TILES_MESSAGE :{YELLOW}Too many tiles in map. Maximum number of tiles is {NUM}, you have selected {NUM} + + STR_MAPGEN_MAX_HEIGHTLEVEL_QUERY_CAPT :{WHITE}Change maximum map height + STR_MAPGEN_SNOW_LINE_QUERY_CAPT :{WHITE}Change snow line height +@@ -3159,6 +3210,8 @@ STR_CARGO_RATING_OUTSTANDING :Outstanding + STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Centre main view on station location. Ctrl+Click opens a new viewport on station location + STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Change name of station + ++STR_STATION_VIEW_COVERAGE :{BLACK}Coverage ++STR_STATION_VIEW_COVERAGE_TIP :{BLACK}Show station's area coverage + STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Show all trains which have this station on their schedule + STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP :{BLACK}Show all road vehicles which have this station on their schedule + STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP :{BLACK}Show all aircraft which have this station on their schedule +@@ -3363,6 +3416,9 @@ STR_GROUP_REMOVE_ALL_VEHICLES :Remove all vehi + + STR_GROUP_RENAME_CAPTION :{BLACK}Rename a group + ++STR_GROUP_PROFIT_THIS_YEAR :{BLACK}Profit this year: {CURRENCY_SHORT} ++STR_GROUP_PROFIT_LAST_YEAR :{BLACK}Profit last year: {CURRENCY_SHORT} ++ + # Build vehicle window + STR_BUY_VEHICLE_TRAIN_RAIL_CAPTION :New Rail Vehicles + STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION :New Electric Rail Vehicles +@@ -4128,6 +4184,8 @@ STR_ERROR_TREE_PLANT_LIMIT_REACHED :{WHITE}... tree + STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}Name must be unique + STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{1:STRING} in the way + STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}Not allowed while paused ++STR_ERROR_NOTHING_TO_DO :{WHITE}... there is nothing to do ++STR_ERROR_INAPPLICABLE_TRANSFORMATION :{WHITE}... inapplicable transformation + + # Local authority errors + STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}{TOWN} local authority refuses to allow this +@@ -4207,6 +4265,8 @@ STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Can't bu + STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Can't build airport here... + + STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Adjoins more than one existing station/loading area ++STR_ERROR_ADJOINS_OTHER_STATION :{WHITE}Adjoins other station/loading area ++STR_ERROR_CAN_T_DISTANT_JOIN :{WHITE}Can't distant-join station/loading area + STR_ERROR_STATION_TOO_SPREAD_OUT :{WHITE}... station too spread out + STR_ERROR_TOO_MANY_STATIONS_LOADING :{WHITE}Too many stations/loading areas + STR_ERROR_TOO_MANY_STATION_SPECS :{WHITE}Too many railway station parts +@@ -4356,6 +4416,10 @@ STR_ERROR_COMPANY_HEADQUARTERS_IN :{WHITE}... comp + STR_ERROR_CAN_T_PURCHASE_THIS_LAND :{WHITE}Can't purchase this land area... + STR_ERROR_YOU_ALREADY_OWN_IT :{WHITE}... you already own it! + ++# Clipboard related errors ++STR_COPY_PASTE_ERROR_SUMMARY :{WHITE}{8:STRING} ++STR_ERROR_CAN_T_PASTE_HERE :{WHITE}Can't paste here... ++ + # Group related errors + STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Can't create group... + STR_ERROR_GROUP_CAN_T_DELETE :{WHITE}Can't delete this group... +@@ -4879,10 +4943,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Spectator, {1:STRING1} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +@@ -4940,6 +5012,7 @@ STR_DATE_LONG_SMALL :{TINY_FONT}{BLA + STR_TINY_GROUP :{TINY_FONT}{GROUP} + STR_BLACK_INT :{BLACK}{NUM} + STR_ORANGE_INT :{ORANGE}{NUM} ++STR_RED_INT :{RED}{NUM} + STR_WHITE_SIGN :{WHITE}{SIGN} + STR_TINY_BLACK_STATION :{TINY_FONT}{BLACK}{STATION} + STR_BLACK_STRING :{BLACK}{STRING} +diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt +index fa7251c..e7455c5 100644 +--- a/src/lang/estonian.txt ++++ b/src/lang/estonian.txt +@@ -4937,10 +4937,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Vaatleja, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt +index a1eddbc..d05652a 100644 +--- a/src/lang/faroese.txt ++++ b/src/lang/faroese.txt +@@ -4383,10 +4383,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Eygleiðari, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/german.txt b/src/lang/german.txt +index 6843608..fe3f1d7 100644 +--- a/src/lang/german.txt ++++ b/src/lang/german.txt +@@ -233,6 +233,7 @@ STR_TOOLTIP_GROUP_ORDER :{BLACK}Gruppier + STR_TOOLTIP_SORT_ORDER :{BLACK}Sortierreihenfolge auswählen (absteigend/aufsteigend) + STR_TOOLTIP_SORT_CRITERIA :{BLACK}Sortierkriterien auswählen + STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Filterkriterien auswählen ++STR_BUTTON_COVERAGE :{BLACK}Einzugsbereich + STR_BUTTON_SORT_BY :{BLACK}Sortieren nach + STR_BUTTON_LOCATION :{BLACK}Standort + STR_BUTTON_RENAME :{BLACK}Umbenennen +@@ -885,6 +886,8 @@ STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}In Zusat + STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Aktuelle Position der Hauptansicht in diese Zusatzansicht kopieren + STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Aus Zusatzansicht einfügen + STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Hauptansicht zur Position dieser Zusatzansicht scrollen ++STR_EXTRA_VIEW_FOLLOW_CURSOR :{BLACK}Dem Mauszeiger folgen ++STR_EXTRA_VIEW_FOLLOW_CURSOR_TT :{BLACK}Dem Mauszeiger in diesem Fenster folgen + + # Game options window + STR_GAME_OPTIONS_CAPTION :{WHITE}Spieleinstellungen +@@ -1254,6 +1257,9 @@ STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Erlaube die Err + STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Diese Einstellung kann nicht geändert werden solange Fahrzeuge im Spiel sind + STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Instandhaltung der Infrastruktur: {STRING} + STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :Unterhaltskosten für Infrastruktur einschalten. Die Unterhaltskosten wachsen mit zunehmender Netzwerkgröße überproportional an, so dass sie größere Firmen stärker belasten als kleinere ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY :Clipboard Kapazität: {STRING} ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE :{0:NUM}x{0:NUM} Felder ++STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT :Maximal erlaubte Länge/Breite, die ins Clipboard kopiert werden kann + + STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Flughäfen veralten nie: {STRING} + STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Wird diese Option aktiviert, können Flughäfen, die einmal eingeführt wurden, das ganze Spiel über gebaut werden und veralten nie +@@ -1288,6 +1294,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Zeige die Einwo + STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Zeige die Einwohneranzahl neben den Städtenamen auf der Karte an + STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Linienstärke in Diagrammen: {STRING} + STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Strichdicke der Linien in Diagrammen. Dünnere Linien sind genauer ablesbar, dickere Linien sind besser sichtbar und erlauben es, Farben leichter zu unterscheiden ++STR_CONFIG_SETTING_FORECAST_DISPLAY :{LTBLUE}Angebots- und Bedarfsvorhersage für Station anzeigen: {ORANGE}{STRING} ++STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT :Angebots- und Bedarfsvorhersage für Station anzeigen? + + STR_CONFIG_SETTING_LANDSCAPE :Landschaftstyp: {STRING} + STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landschaftstype definiert grundlegende Spielscenarien in Bezug auf verfügbare Fracht und Wachstumsvoraussetzungen für Städte. NewGRFs und Spielskripte erlauben weitgehendere Kontrolle dieser Parameter +@@ -1412,6 +1420,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Belasse Bauwerk + STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Die Bauwerkzeuge für Brücken, Tunnel, etc. nach Benutzung weiter aktiviert lassen + STR_CONFIG_SETTING_EXPENSES_LAYOUT :Zwischensummen für Kategorien bei Firmenausgaben:{STRING} + STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Lege das Layout für das Fenster mit den Firmenausgaben fest ++STR_CONFIG_SETTING_TOWNRATING_INDICATOR :Zeige Indikatoren für Bewertung: {STRING} + + STR_CONFIG_SETTING_SOUND_TICKER :Nachrichtenticker: {STRING} + STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Soundeffekte für Kurzfassungen von Nachrichten abspielen (Ticker) +@@ -2276,6 +2285,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Transpar + STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Transparenz für Bauten wie Leuchttürme und Sendemasten einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben + STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Transparenz für Oberleitungen einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben + STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Transparenz für Ladestandsanzeige einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben ++STR_TRANSPARENT_TUNNELS_TOOLTIP :{BLACK}Transparenz für Fahrzeuge in Tunneln einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben + STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Objekte unsichtbar statt transparent machen + + # Linkgraph legend window +@@ -2551,6 +2561,12 @@ STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospekt + STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Bauen + STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finanzieren + ++# Town rating indicators ++STR_TOWN_RATING_INCREASED_TINY :{TINY_FONT}{GREEN}Bewertung verbessert zu: {STRING} ++STR_TOWN_RATING_INCREASED :{GREEN}Bewertung verbessert zu: {STRING} ++STR_TOWN_RATING_DECREASED_TINY :{TINY_FONT}{RED}Bewertung verschlechtert zu: {STRING} ++STR_TOWN_RATING_DECREASED :{RED}Bewertung verschlechtert zu: {STRING} ++ + # Industry cargoes window + STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Produktionskette für {STRING} + STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Produktionskette für {STRING} +@@ -3160,6 +3176,8 @@ STR_CARGO_RATING_OUTSTANDING :Hervorragend + STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Hauptansicht zur Station scrollen. Strg+Klick öffnet neue Zusatzansicht bei der Station + STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Namen der Station ändern + ++STR_STATION_VIEW_COVERAGE :{BLACK}Einzugsbereich ++STR_STATION_VIEW_COVERAGE_TIP :{BLACK}Einzugsbereich der Station anzeigen + STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Alle Züge, die diesen Bahnhof anfahren, anzeigen + STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP :{BLACK}Alle Straßenfahrzeuge, die diese Station anfahren, anzeigen + STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP :{BLACK}Alle Flugzeuge, die diesen Flughafen anfliegen, anzeigen +@@ -3364,6 +3382,9 @@ STR_GROUP_REMOVE_ALL_VEHICLES :Liste leeren + + STR_GROUP_RENAME_CAPTION :{BLACK}Gruppe umbenennen + ++STR_GROUP_PROFIT_THIS_YEAR :{BLACK}Profit im letzten Jahr: {CURRENCY_SHORT} ++STR_GROUP_PROFIT_LAST_YEAR :{BLACK}Profit in diesem Jahr: {CURRENCY_SHORT} ++ + # Build vehicle window + STR_BUY_VEHICLE_TRAIN_RAIL_CAPTION :Neue Schienenfahrzeuge + STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION :Neue elektrische Schienenfahrzeuge +@@ -4880,10 +4901,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Zuschauer, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt +index ee2f7b0..3963e4c 100644 +--- a/src/lang/icelandic.txt ++++ b/src/lang/icelandic.txt +@@ -4638,10 +4638,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Áhorfandi, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt +index 7e77758..aae1727 100644 +--- a/src/lang/indonesian.txt ++++ b/src/lang/indonesian.txt +@@ -4875,10 +4875,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Penonton, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/italian.txt b/src/lang/italian.txt +index 8f8d6cd..0518f1d 100644 +--- a/src/lang/italian.txt ++++ b/src/lang/italian.txt +@@ -4909,10 +4909,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Spettatore, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt +index b6d1b43..83503e2 100644 +--- a/src/lang/luxembourgish.txt ++++ b/src/lang/luxembourgish.txt +@@ -4879,10 +4879,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Zuschauer, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt +index 79cea5e..db74ac4 100644 +--- a/src/lang/norwegian_nynorsk.txt ++++ b/src/lang/norwegian_nynorsk.txt +@@ -4793,10 +4793,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Tilskuar, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt +index ec2a83c..91e7cfd 100644 +--- a/src/lang/portuguese.txt ++++ b/src/lang/portuguese.txt +@@ -4856,10 +4856,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Espectador, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt +index 89be4e4..a538a0d 100644 +--- a/src/lang/spanish.txt ++++ b/src/lang/spanish.txt +@@ -4880,10 +4880,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR + STR_SAVEGAME_NAME_SPECTATOR :Espectador, {1:STRING} + + # Viewport strings ++STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) ++STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) ++STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) ++STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) + STR_VIEWPORT_TOWN :{WHITE}{TOWN} + STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} ++STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} ++STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} + STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} ++STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} + + STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} + STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} +diff --git a/src/map.cpp b/src/map.cpp +index 252f20b..8fad25e 100644 +--- a/src/map.cpp ++++ b/src/map.cpp +@@ -22,16 +22,7 @@ + extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned); + #endif + +-uint _map_log_x; ///< 2^_map_log_x == _map_size_x +-uint _map_log_y; ///< 2^_map_log_y == _map_size_y +-uint _map_size_x; ///< Size of the map along the X +-uint _map_size_y; ///< Size of the map along the Y +-uint _map_size; ///< The number of tiles on the map +-uint _map_tile_mask; ///< _map_size - 1 (to mask the mapsize) +- +-Tile *_m = NULL; ///< Tiles of the map +-TileExtended *_me = NULL; ///< Extended Tiles of the map +- ++MainMap _main_map; ///< The main tile array. + + /** + * (Re)allocates a map with the given dimension +@@ -40,10 +31,14 @@ TileExtended *_me = NULL; ///< Extended Tiles of the map + */ + void AllocateMap(uint size_x, uint size_y) + { ++ DEBUG(map, 2, "Min/max map size %d/%d, max map tiles %d", MIN_MAP_SIZE, MAX_MAP_SIZE, MAX_MAP_TILES); ++ DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); ++ + /* Make sure that the map size is within the limits and that + * size of both axes is a power of 2. */ +- if (!IsInsideMM(size_x, MIN_MAP_SIZE, MAX_MAP_SIZE + 1) || +- !IsInsideMM(size_y, MIN_MAP_SIZE, MAX_MAP_SIZE + 1) || ++ if (size_x * size_y > MAX_MAP_TILES || ++ size_x < MIN_MAP_SIZE || ++ size_y < MIN_MAP_SIZE || + (size_x & (size_x - 1)) != 0 || + (size_y & (size_y - 1)) != 0) { + error("Invalid map size"); +@@ -51,18 +46,18 @@ void AllocateMap(uint size_x, uint size_y) + + DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); + +- _map_log_x = FindFirstBit(size_x); +- _map_log_y = FindFirstBit(size_y); +- _map_size_x = size_x; +- _map_size_y = size_y; +- _map_size = size_x * size_y; +- _map_tile_mask = _map_size - 1; ++ _main_map.log_x = FindFirstBit(size_x); ++ _main_map.log_y = FindFirstBit(size_y); ++ _main_map.size_x = size_x; ++ _main_map.size_y = size_y; ++ _main_map.size = size_x * size_y; ++ _main_map.tile_mask = _main_map.size - 1; + +- free(_m); +- free(_me); ++ free(_main_map.m); ++ free(_main_map.me); + +- _m = CallocT<Tile>(_map_size); +- _me = CallocT<TileExtended>(_map_size); ++ _main_map.m = CallocT<Tile>(_main_map.size); ++ _main_map.me = CallocT<TileExtended>(_main_map.size); + } + + +@@ -98,7 +93,25 @@ TileIndex TileAdd(TileIndex tile, TileIndexDiff add, + + return TileXY(x, y); + } ++ ++GenericTileIndex TileAddXY(GenericTileIndex tile, int dx, int dy, const char *exp, const char *file, int line) ++{ ++ uint x = TileX(tile) + dx; ++ uint y = TileY(tile) + dy; ++ ++ if (x >= MapSizeX(MapOf(tile)) || y >= MapSizeY(MapOf(tile))) { ++ char buf[512]; ++ snprintf(buf, lengthof(buf), "TILE_ADDXY(%s) when adding 0x%.4X and <0x%.4X, 0x%.4X> failed", exp, IndexOf(tile), dx, dy); ++#if !defined(_MSC_VER) || defined(WINCE) ++ fprintf(stderr, "%s:%d %s\n", file, line, buf); ++#else ++ _assert(buf, (char*)file, line); + #endif ++ } ++ ++ return TileXY<true>(x, y, MapOf(tile)); ++} ++#endif /* _DEBUG */ + + /** + * This function checks if we add addx/addy to tile, if we +diff --git a/src/map_func.h b/src/map_func.h +index 9198c2c..51b0cc2 100644 +--- a/src/map_func.h ++++ b/src/map_func.h +@@ -12,38 +12,122 @@ + #ifndef MAP_FUNC_H + #define MAP_FUNC_H + ++#include "core/bitmath_func.hpp" + #include "core/math_func.hpp" + #include "tile_type.h" + #include "map_type.h" + #include "direction_func.h" + +-extern uint _map_tile_mask; ++extern MainMap _main_map; + + /** + * 'Wraps' the given tile to it is within the map. It does + * this by masking the 'high' bits of. + * @param x the tile to 'wrap' + */ ++#define TILE_MASK(x) ((x) & _main_map.tile_mask) + +-#define TILE_MASK(x) ((x) & _map_tile_mask) ++void AllocateMap(uint size_x, uint size_y); + + /** +- * Pointer to the tile-array. +- * +- * This variable points to the tile-array which contains the tiles of +- * the map. ++ * Get the tile map that is bounded to a given tile index. ++ * @param tile tile index of a tile ++ * @return the map that contains the tile + */ +-extern Tile *_m; ++template <bool Tgeneric> ++static inline Map *MapOf(typename TileIndexT<Tgeneric>::T tile); ++ ++template <> ++inline Map *MapOf<false>(TileIndex tile) ++{ ++ return &_main_map; ++} ++ ++template <> ++inline Map *MapOf<true>(GenericTileIndex tile) ++{ ++ return tile.map; ++} ++ ++/** @copydoc MapOf(TileIndexT<Tgeneric>::T) */ ++static inline Map *MapOf(TileIndex tile) { return MapOf<false>(tile); } ++/** @copydoc MapOf(TileIndexT<Tgeneric>::T) */ ++static inline Map *MapOf(GenericTileIndex tile) { return MapOf<true>(tile); } + + /** +- * Pointer to the extended tile-array. +- * +- * This variable points to the extended tile-array which contains the tiles +- * of the map. ++ * Access the "raw" value (offset into map array) of a given tile index. ++ * @param tile tile index to query ++ * @return reference to the "raw" value of the index + */ +-extern TileExtended *_me; ++template <bool Tgeneric> ++static inline RawTileIndex &IndexOf(typename TileIndexT<Tgeneric>::T &tile); + +-void AllocateMap(uint size_x, uint size_y); ++/** @copydoc IndexOf(TileIndexT<Tgeneric>::T&) */ ++template <bool Tgeneric> ++static inline const RawTileIndex &IndexOf(const typename TileIndexT<Tgeneric>::T &tile); ++ ++template <> ++inline RawTileIndex &IndexOf<false>(TileIndex &tile) ++{ ++ return tile; ++} ++ ++template <> ++inline RawTileIndex &IndexOf<true>(GenericTileIndex &tile) ++{ ++ return tile.index; ++} ++ ++template <> ++inline const RawTileIndex &IndexOf<false>(const TileIndex &tile) ++{ ++ return tile; ++} ++ ++template <> ++inline const RawTileIndex &IndexOf<true>(const GenericTileIndex &tile) ++{ ++ return tile.index; ++} ++ ++/** @copydoc IndexOf(TileIndexT<Tgeneric>::T&) */ ++static inline RawTileIndex &IndexOf(TileIndex &tile) { return IndexOf<false>(tile); } ++/** @copydoc IndexOf(TileIndexT<Tgeneric>::T&) */ ++static inline RawTileIndex &IndexOf(GenericTileIndex &tile) { return IndexOf<true>(tile); } ++/** @copydoc IndexOf(TileIndexT<Tgeneric>::T&) */ ++static inline const RawTileIndex &IndexOf(const TileIndex &tile) { return IndexOf<false>(tile); } ++/** @copydoc IndexOf(TileIndexT<Tgeneric>::T&) */ ++static inline const RawTileIndex &IndexOf(const GenericTileIndex &tile) { return IndexOf<true>(tile); } ++ ++/** ++ * Get the data of a tile. ++ * @param tile index of the tile ++ * @return the tile data ++ */ ++template <bool Tgeneric> ++static inline Tile *GetTile(typename TileIndexT<Tgeneric>::T tile) ++{ ++ return &(MapOf(tile)->m[IndexOf(tile)]); ++} ++/** @copydoc GetTile(TileIndexT<Tgeneric>::T) */ ++static inline Tile *GetTile(TileIndex tile) { return GetTile<false>(tile); } ++/** @copydoc GetTile(TileIndexT<Tgeneric>::T) */ ++static inline Tile *GetTile(GenericTileIndex tile) { return GetTile<true>(tile); } ++ ++/** ++ * Get the extended data of a tile. ++ * @param tile index of the tile ++ * @return the extended tile data ++ */ ++template <bool Tgeneric> ++static inline TileExtended *GetTileEx(typename TileIndexT<Tgeneric>::T tile) ++{ ++ return &(MapOf(tile)->me[IndexOf(tile)]); ++} ++/** @copydoc GetTileEx(TileIndexT<Tgeneric>::T) */ ++static inline TileExtended *GetTileEx(TileIndex tile) { return GetTileEx<false>(tile); } ++/** @copydoc GetTileEx(TileIndexT<Tgeneric>::T) */ ++static inline TileExtended *GetTileEx(GenericTileIndex tile) { return GetTileEx<true>(tile); } + + /** + * Logarithm of the map size along the X side. +@@ -52,8 +136,7 @@ void AllocateMap(uint size_x, uint size_y); + */ + static inline uint MapLogX() + { +- extern uint _map_log_x; +- return _map_log_x; ++ return _main_map.log_x; + } + + /** +@@ -63,56 +146,57 @@ static inline uint MapLogX() + */ + static inline uint MapLogY() + { +- extern uint _map_log_y; +- return _map_log_y; ++ return _main_map.log_y; + } + + /** +- * Get the size of the map along the X ++ * Get the size of a map along the X ++ * @param map the map + * @return the number of tiles along the X of the map + */ +-static inline uint MapSizeX() ++static inline uint MapSizeX(Map *map = &_main_map) + { +- extern uint _map_size_x; +- return _map_size_x; ++ return map->size_x; + } + + /** +- * Get the size of the map along the Y ++ * Get the size of a map along the Y ++ * @param map the map + * @return the number of tiles along the Y of the map + */ +-static inline uint MapSizeY() ++static inline uint MapSizeY(Map *map = &_main_map) + { +- extern uint _map_size_y; +- return _map_size_y; ++ return map->size_y; + } + + /** +- * Get the size of the map ++ * Get the size of a map ++ * @param map the map + * @return the number of tiles of the map + */ +-static inline uint MapSize() ++static inline uint MapSize(Map *map = &_main_map) + { +- extern uint _map_size; +- return _map_size; ++ return map->size; + } + + /** +- * Gets the maximum X coordinate within the map, including MP_VOID ++ * Gets the maximum X coordinate within a map, including MP_VOID ++ * @param map the map + * @return the maximum X coordinate + */ +-static inline uint MapMaxX() ++static inline uint MapMaxX(Map *map = &_main_map) + { +- return MapSizeX() - 1; ++ return MapSizeX(map) - 1; + } + + /** +- * Gets the maximum Y coordinate within the map, including MP_VOID ++ * Gets the maximum Y coordinate within a map, including MP_VOID ++ * @param map the map + * @return the maximum Y coordinate + */ +-static inline uint MapMaxY() ++static inline uint MapMaxY(Map *map = &_main_map) + { +- return MapSizeY() - 1; ++ return MapSizeY(map) - 1; + } + + /** +@@ -151,11 +235,114 @@ static inline uint ScaleByMapSize1D(uint n) + * the resulting tileindex of the start tile applied + * with this saved difference. + * +- * @see TileDiffXY(int, int) ++ * @see TileDiffXY + */ + typedef int32 TileIndexDiff; + + /** ++ * Test if a given tile index is a main map tile index. ++ * @param tile the tile index to test ++ * @return \c true if the index points to the main map, \c false otherwise ++ */ ++template <bool Tgeneric> ++static inline bool IsMainMapTile(typename TileIndexT<Tgeneric>::T tile); ++ ++template <> ++inline bool IsMainMapTile<false>(TileIndex tile) ++{ ++ return true; ++} ++ ++template <> ++inline bool IsMainMapTile<true>(GenericTileIndex tile) ++{ ++ return MapOf<true>(tile) == &_main_map; ++} ++ ++/** @copydoc IsMainMapTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsMainMapTile(TileIndex tile) { return IsMainMapTile<false>(tile); } ++/** @copydoc IsMainMapTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsMainMapTile(GenericTileIndex tile) { return IsMainMapTile<true>(tile); } ++ ++/** ++ * Convert a given tile index to a main map tile index. ++ * ++ * @param tile the index to convert ++ * @return the converted index ++ * ++ * @pre IsMainMapTile(tile) ++ */ ++template <bool Tgeneric> ++static inline TileIndex AsMainMapTile(typename TileIndexT<Tgeneric>::T tile) ++{ ++ assert(IsMainMapTile(tile)); ++ return (TileIndex)IndexOf(tile); ++} ++/** @copydoc AsMainMapTile(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex AsMainMapTile(TileIndex tile) { return AsMainMapTile<false>(tile); } ++/** @copydoc AsMainMapTile(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex AsMainMapTile(GenericTileIndex tile) { return AsMainMapTile<true>(tile); } ++ ++/** ++ * Test whether two tile indices point to the same tile map. ++ * @param a the first tile ++ * @param b the second tile ++ * @return MapOf(a) == MapOf(b) ++ */ ++template <bool TgenericA, bool TgenericB> ++static inline bool IsSameMap(typename TileIndexT<TgenericA>::T a, typename TileIndexT<TgenericB>::T b) ++{ ++ return MapOf(a) == MapOf(b); ++} ++/** @copydoc IsSameMap(TileIndexT<TgenericA>::T,TileIndexT<TgenericB>::T) */ ++static inline bool IsSameMap(TileIndex a, TileIndex b) { return true; } ++/** @copydoc IsSameMap(TileIndexT<TgenericA>::T,TileIndexT<TgenericB>::T) */ ++static inline bool IsSameMap(GenericTileIndex a, GenericTileIndex b) { return IsSameMap<true, true>(a, b); } ++ ++/** ++ * Test if a given tile index points to an existing tile. ++ * ++ * @param tile the tile index ++ * @return whether we can access content of this tile ++ * ++ * @note the function returns \c true also for #MP_VOID tiles ++ * @see IsValidTile ++ * @see IsInnerTile ++ */ ++template <bool Tgeneric> ++static inline bool IsValidTileIndex(typename TileIndexT<Tgeneric>::T tile) ++{ ++ return MapOf(tile) != NULL && IndexOf(tile) < MapSize(MapOf(tile)); ++} ++/** @copydoc IsValidTileIndex(TileIndexT<Tgeneric>::T) */ ++static inline bool IsValidTileIndex(TileIndex tile) { return IsValidTileIndex<false>(tile); } ++/** @copydoc IsValidTileIndex(TileIndexT<Tgeneric>::T) */ ++static inline bool IsValidTileIndex(GenericTileIndex tile) { return IsValidTileIndex<true>(tile); } ++ ++/** ++ * Create a tile index. ++ * @param index the index of the tile ++ * @param map the map of the tile ++ * ++ * @pre Tgeneric || map == &_main_map ++ */ ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T MakeTileIndex(RawTileIndex index, Map *map); ++ ++template <> ++inline TileIndex MakeTileIndex<false>(RawTileIndex index, Map *map) ++{ ++ assert(map == &_main_map); ++ return TileIndex(index); ++} ++ ++template <> ++inline GenericTileIndex MakeTileIndex<true>(RawTileIndex index, Map *map) ++{ ++ return GenericTileIndex(index, map); ++} ++ ++/** + * Returns the TileIndex of a coordinate. + * + * @param x The x coordinate of the tile +@@ -168,6 +355,42 @@ static inline TileIndex TileXY(uint x, uint y) + } + + /** ++ * Returns the tile index of a coordinate. ++ * ++ * @param x The x coordinate of the tile ++ * @param y The y coordinate of the tile ++ * @param map The map of the tile ++ * @return The tile index calculated by the coordinate ++ * ++ * @pre Tgeneric || map == &_main_map ++ */ ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T TileXY(uint x, uint y, Map *map); ++ ++template <> ++inline TileIndex TileXY<false>(uint x, uint y, Map *map) ++{ ++ assert(map == &_main_map); ++ return TileXY(x, y); ++} ++ ++template <> ++inline GenericTileIndex TileXY<true>(uint x, uint y, Map *map) ++{ ++ return GenericTileIndex(y * MapSizeX(map) + x, map); ++} ++ ++/** ++ * Returns the tile index of a coordinate. ++ * ++ * @param x The x coordinate of the tile ++ * @param y The y coordinate of the tile ++ * @param map The map of the tile ++ * @return The tile index calculated by the coordinate ++ */ ++static inline GenericTileIndex TileXY(uint x, uint y, Map *map) { return TileXY<true>(x, y, map); } ++ ++/** + * Calculates an offset for the given coordinate(-offset). + * + * This function calculate an offset value which can be added to an +@@ -178,13 +401,13 @@ static inline TileIndex TileXY(uint x, uint y) + * @return The resulting offset value of the given coordinate + * @see ToTileIndexDiff(TileIndexDiffC) + */ +-static inline TileIndexDiff TileDiffXY(int x, int y) ++static inline TileIndexDiff TileDiffXY(int x, int y, Map *map = &_main_map) + { + /* Multiplication gives much better optimization on MSVC than shifting. + * 0 << shift isn't optimized to 0 properly. + * Typically x and y are constants, and then this doesn't result + * in any actual multiplication in the assembly code.. */ +- return (y * MapSizeX()) + x; ++ return (y * MapSizeX(map)) + x; + } + + /** +@@ -204,60 +427,152 @@ static inline TileIndex TileVirtXY(uint x, uint y) + * @param tile the tile to get the X component of + * @return the X component + */ +-static inline uint TileX(TileIndex tile) ++template <bool Tgeneric> ++static inline uint TileX(typename TileIndexT<Tgeneric>::T tile); ++ ++template <> ++inline uint TileX<false>(TileIndex tile) + { + return tile & MapMaxX(); + } + ++template <> ++inline uint TileX<true>(GenericTileIndex tile) ++{ ++ return IndexOf(tile) % MapSizeX(MapOf(tile)); ++} ++ ++/** @copydoc TileX(TileIndexT<Tgeneric>::T) */ ++static inline uint TileX(TileIndex tile) { return TileX<false>(tile); } ++/** @copydoc TileX(TileIndexT<Tgeneric>::T) */ ++static inline uint TileX(GenericTileIndex tile) { return TileX<true>(tile); } ++ + /** + * Get the Y component of a tile + * @param tile the tile to get the Y component of + * @return the Y component + */ +-static inline uint TileY(TileIndex tile) ++template <bool Tgeneric> ++static inline uint TileY(typename TileIndexT<Tgeneric>::T tile); ++ ++template <> ++inline uint TileY<false>(TileIndex tile) + { + return tile >> MapLogX(); + } + ++template <> ++inline uint TileY<true>(GenericTileIndex tile) ++{ ++ return IndexOf(tile) / MapSizeX(MapOf(tile)); ++} ++ ++/** @copydoc TileX(TileIndexT<Tgeneric>::T) */ ++static inline uint TileY(TileIndex tile) { return TileY<false>(tile); } ++/** @copydoc TileX(TileIndexT<Tgeneric>::T) */ ++static inline uint TileY(GenericTileIndex tile) { return TileY<true>(tile); } ++ + /** + * Return the offset between to tiles from a TileIndexDiffC struct. + * +- * This function works like #TileDiffXY(int, int) and returns the ++ * This function works like #TileDiffXY and returns the + * difference between two tiles. + * + * @param tidc The coordinate of the offset as TileIndexDiffC + * @return The difference between two tiles. +- * @see TileDiffXY(int, int) ++ * @see TileDiffXY + */ + static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc) + { + return (tidc.y << MapLogX()) + tidc.x; + } + ++/** ++ * Return the offset between two tiles from a TileIndexDiffC struct. ++ * ++ * This function works like #TileDiffXY and returns the ++ * difference between two tiles. ++ * ++ * @param tidc The coordinate of the offset as TileIndexDiffC ++ * @param map The map array of the tile ++ * @return The difference between two tiles. ++ * ++ * @pre Tgeneric || map == &_main_map ++ * ++ * @see TileDiffXY ++ */ ++template <bool Tgeneric> ++static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map); ++ ++template <> ++inline TileIndexDiff ToTileIndexDiff<false>(TileIndexDiffC tidc, Map *map) ++{ ++ assert(map == &_main_map); ++ return ToTileIndexDiff(tidc); ++} ++ ++template <> ++inline TileIndexDiff ToTileIndexDiff<true>(TileIndexDiffC tidc, Map *map) ++{ ++ return (tidc.y * MapSizeX(map)) + tidc.x; ++} ++ ++/** ++ * Return the offset between two tiles from a TileIndexDiffC struct. ++ * ++ * This function works like #TileDiffXY and returns the ++ * difference between two tiles. ++ * ++ * @param tidc The coordinate of the offset as TileIndexDiffC ++ * @param map The map array of the tile ++ * @return The difference between two tiles. ++ * ++ * @see TileDiffXY ++ */ ++static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map) { return ToTileIndexDiff<true>(tidc, map); } ++ + + #ifndef _DEBUG + /** +- * Adds to tiles together. ++ * Adds a given offset to a tile. ++ * ++ * @param tile The tile to add to ++ * @param delta The offset to add ++ * @return The resulting tile ++ */ ++ #define TILE_ADD(tile, delta) ((tile) + (delta)) ++ ++ /** ++ * Adds a given XY offset to a tile. + * +- * @param x One tile +- * @param y Another tile to add +- * @return The resulting tile(index) ++ * @param tile The tile to add an offset on it ++ * @param x The x offset to add to the tile ++ * @param y The y offset to add to the tile ++ * @return The resulting tile + */ +- #define TILE_ADD(x, y) ((x) + (y)) ++ #define TILE_ADDXY(tile, x, y) ((tile) + TileDiffXY(x, y, MapOf(tile))) + #else + extern TileIndex TileAdd(TileIndex tile, TileIndexDiff add, + const char *exp, const char *file, int line); +- #define TILE_ADD(x, y) (TileAdd((x), (y), #x " + " #y, __FILE__, __LINE__)) +-#endif + +-/** +- * Adds a given offset to a tile. +- * +- * @param tile The tile to add an offset on it +- * @param x The x offset to add to the tile +- * @param y The y offset to add to the tile +- */ +-#define TILE_ADDXY(tile, x, y) TILE_ADD(tile, TileDiffXY(x, y)) ++ /** ++ * Adds a given offset to a tile. ++ * ++ * @param tile The tile to add to ++ * @param delta The offset to add ++ * @return The resulting tile ++ */ ++ #define TILE_ADD(tile, delta) (TileAdd((tile), (delta), #tile " + " #delta, __FILE__, __LINE__)) ++ ++ GenericTileIndex TileAddXY(GenericTileIndex tile, int x, int y, const char *exp, const char *file, int line); ++ ++ static inline TileIndex TileAddXY(TileIndex tile, int x, int y, const char *exp, const char *file, int line) ++ { ++ return AsMainMapTile(TileAddXY(GenericTileIndex(tile), x, y, exp, file, line)); ++ } ++ ++ #define TILE_ADDXY(tile, ...) TileAddXY((tile), __VA_ARGS__, #tile " + <" #__VA_ARGS__ ">", __FILE__, __LINE__) ++#endif + + TileIndex TileAddWrap(TileIndex tile, int addx, int addy); + +@@ -325,6 +640,40 @@ static inline TileIndexDiffC TileIndexToTileIndexDiffC(TileIndex tile_a, TileInd + return difference; + } + ++/** ++ * Get the offset of transformed northern tile corner. ++ * ++ * When transforming a tile, it's nothern corner can move to other location. ++ * The function retuns difference (TileIndexDiffC) between new and old ++ * locations e.g. when rotating 90 degree left, northern corner becomes ++ * western and the difference is (1, 0). ++ * ++ * Scheme of a tile with corners and offsets: <tt><pre> ++ * N (0, 0) ++ * / \ ++ * (1, 0) W E (0, 1) ++ * \ / ++ * S (1, 1) ++ * </pre></tt> ++ * ++ * @param transformation The transformation. ++ * @return Offset to new location of northern corner. ++ * ++ * @see TileIndexTransformations ++ */ ++static inline TileIndexDiffC TransformedNorthCornerDiffC(DirTransformation transformation) ++{ ++ /* lookup tables (bit arrays) ++ * N E S W E S W N */ ++ static const uint8 DIFF_X = 0 << DTR_IDENTITY | 0 << DTR_ROTATE_90_R | 1 << DTR_ROTATE_180 | 1 << DTR_ROTATE_90_L | 0 << DTR_REFLECT_NE_SW | 1 << DTR_REFLECT_W_E | 1 << DTR_REFLECT_NW_SE | 0 << DTR_REFLECT_N_S; ++ static const uint8 DIFF_Y = 0 << DTR_IDENTITY | 1 << DTR_ROTATE_90_R | 1 << DTR_ROTATE_180 | 0 << DTR_ROTATE_90_L | 1 << DTR_REFLECT_NE_SW | 1 << DTR_REFLECT_W_E | 0 << DTR_REFLECT_NW_SE | 0 << DTR_REFLECT_N_S; ++ ++ assert(IsValidDirTransform(transformation)); ++ ++ TileIndexDiffC ret = { (int16)GB(DIFF_X, transformation, 1), (int16)GB(DIFF_Y, transformation, 1) }; ++ return ret; ++} ++ + /* Functions to calculate distances */ + uint DistanceManhattan(TileIndex, TileIndex); ///< also known as L1-Norm. Is the shortest distance one could go over diagonal tracks (or roads) + uint DistanceSquare(TileIndex, TileIndex); ///< euclidian- or L2-Norm squared +@@ -337,15 +686,32 @@ uint DistanceFromEdgeDir(TileIndex, DiagDirection); ///< distance from the map e + * Convert a DiagDirection to a TileIndexDiff + * + * @param dir The DiagDirection ++ * @param map The tile map (result will be valid only there) + * @return The resulting TileIndexDiff ++ * ++ * @pre Tgeneric || map == &_main_map ++ * + * @see TileIndexDiffCByDiagDir + */ +-static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir) ++template <bool Tgeneric> ++static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir, Map *map) + { + extern const TileIndexDiffC _tileoffs_by_diagdir[DIAGDIR_END]; + + assert(IsValidDiagDirection(dir)); +- return ToTileIndexDiff(_tileoffs_by_diagdir[dir]); ++ return ToTileIndexDiff<Tgeneric>(_tileoffs_by_diagdir[dir], map); ++} ++ ++/** ++ * Convert a DiagDirection to a TileIndexDiff ++ * ++ * @param dir The DiagDirection ++ * @return The resulting TileIndexDiff ++ * @see TileIndexDiffCByDiagDir ++ */ ++static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir) ++{ ++ return TileOffsByDiagDir<false>(dir, &_main_map); + } + + /** +@@ -362,6 +728,15 @@ static inline TileIndexDiff TileOffsByDir(Direction dir) + return ToTileIndexDiff(_tileoffs_by_dir[dir]); + } + ++template <bool Tgeneric> ++static inline TileIndexDiff TileOffsByDir(Direction dir, Map *map) ++{ ++ extern const TileIndexDiffC _tileoffs_by_dir[DIR_END]; ++ ++ assert(IsValidDirection(dir)); ++ return ToTileIndexDiff<Tgeneric>(_tileoffs_by_dir[dir], map); ++} ++ + /** + * Adds a DiagDir to a tile. + * +@@ -369,10 +744,15 @@ static inline TileIndexDiff TileOffsByDir(Direction dir) + * @param dir The direction in which we want to step + * @return the moved tile + */ +-static inline TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir) ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T TileAddByDiagDir(typename TileIndexT<Tgeneric>::T tile, DiagDirection dir) + { +- return TILE_ADD(tile, TileOffsByDiagDir(dir)); ++ return TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x, TileIndexDiffCByDiagDir(dir).y); + } ++/** @copydoc TileAddByDiagDir(TileIndexT<Tgeneric>::T,DiagDirection) */ ++static inline TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir) { return TileAddByDiagDir<false>(tile, dir); } ++/** @copydoc TileAddByDiagDir(TileIndexT<Tgeneric>::T,DiagDirection) */ ++static inline GenericTileIndex TileAddByDiagDir(GenericTileIndex tile, DiagDirection dir) { return TileAddByDiagDir<true>(tile, dir); } + + /** + * Determines the DiagDirection to get from one tile to another. +diff --git a/src/map_type.h b/src/map_type.h +index 620885e..ee419d1 100644 +--- a/src/map_type.h ++++ b/src/map_type.h +@@ -37,6 +37,22 @@ struct TileExtended { + byte m7; ///< Primarily used for newgrf support + }; + ++/** Tile array. */ ++struct Map { ++ uint size_x; ///< Size of the map along the X ++ uint size_y; ///< Size of the map along the Y ++ uint size; ///< The number of tiles on the map ++ Tile *m; ///< Tiles of the map ++ TileExtended *me; ///< Extended Tiles of the map ++}; ++ ++/** Main tile array. */ ++struct MainMap : Map { ++ uint log_x; ///< 2^log_x == size_x ++ uint log_y; ///< 2^log_y == size_y ++ uint tile_mask; ///< size - 1 (to mask the mapsize) ++}; ++ + /** + * An offset value between to tiles. + * +@@ -62,9 +78,11 @@ struct TileIndexDiffC { + + /** Minimal and maximal map width and height */ + static const uint MIN_MAP_SIZE_BITS = 6; ///< Minimal size of map is equal to 2 ^ MIN_MAP_SIZE_BITS +-static const uint MAX_MAP_SIZE_BITS = 12; ///< Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS ++static const uint MAX_MAP_SIZE_BITS = 20; ///< Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS ++static const uint MAX_MAP_TILES_BITS = 26; ///< Maximal number of tiles in a map is equal to 2 ^ MAX_MAP_TILES_BITS. + static const uint MIN_MAP_SIZE = 1 << MIN_MAP_SIZE_BITS; ///< Minimal map size = 64 +-static const uint MAX_MAP_SIZE = 1 << MAX_MAP_SIZE_BITS; ///< Maximal map size = 4096 ++static const uint MAX_MAP_SIZE = 1 << MAX_MAP_SIZE_BITS; ///< Maximal map size = 8192 ++static const uint MAX_MAP_TILES = 1 << MAX_MAP_TILES_BITS;///< Maximal number of tiles in a map = 2048 * 2048 + + /** + * Approximation of the length of a straight track, relative to a diagonal +diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp +index 5bb8c7e..6e73a4a 100644 +--- a/src/misc_gui.cpp ++++ b/src/misc_gui.cpp +@@ -123,15 +123,15 @@ public: + # define LANDINFOD_LEVEL 1 + #endif + DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile)); +- DEBUG(misc, LANDINFOD_LEVEL, "type = %#x", _m[tile].type); +- DEBUG(misc, LANDINFOD_LEVEL, "height = %#x", _m[tile].height); +- DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1); +- DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2); +- DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3); +- DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); +- DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); +- DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _me[tile].m6); +- DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); ++ DEBUG(misc, LANDINFOD_LEVEL, "type = %#x", GetTile(tile)->type); ++ DEBUG(misc, LANDINFOD_LEVEL, "height = %#x", GetTile(tile)->height); ++ DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", GetTile(tile)->m1); ++ DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", GetTile(tile)->m2); ++ DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", GetTile(tile)->m3); ++ DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", GetTile(tile)->m4); ++ DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", GetTile(tile)->m5); ++ DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", GetTileEx(tile)->m6); ++ DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", GetTileEx(tile)->m7); + #undef LANDINFOD_LEVEL + } + +diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp +index 6e5458f..31b7f5a 100644 +--- a/src/network/network_command.cpp ++++ b/src/network/network_command.cpp +@@ -51,6 +51,7 @@ static CommandCallback * const _callback_table[] = { + /* 0x19 */ CcStartStopVehicle, + /* 0x1A */ CcGame, + /* 0x1B */ CcAddVehicleNewGroup, ++ /* 0x1C */ CcPaste, + }; + + /** +diff --git a/src/newgrf_airport.h b/src/newgrf_airport.h +index 5a917c6..5ec7c42 100644 +--- a/src/newgrf_airport.h ++++ b/src/newgrf_airport.h +@@ -28,10 +28,14 @@ struct AirportTileTable { + }; + + /** Iterator to iterate over all tiles belonging to an airport spec. */ +-class AirportTileTableIterator : public TileIterator { ++template <bool Tgeneric> ++class AirportTileTableIteratorT : public TileIteratorT<Tgeneric> { ++public: ++ typedef typename TileIteratorT<Tgeneric>::TileIndexType TileIndexType; ++ + private: + const AirportTileTable *att; ///< The offsets. +- TileIndex base_tile; ///< The tile we base the offsets off. ++ TileIndexType base_tile; ///< The tile we base the offsets off. + + public: + /** +@@ -39,17 +43,17 @@ public: + * @param att The TileTable we want to iterate over. + * @param base_tile The basetile for all offsets. + */ +- AirportTileTableIterator(const AirportTileTable *att, TileIndex base_tile) : TileIterator(base_tile + ToTileIndexDiff(att->ti)), att(att), base_tile(base_tile) ++ AirportTileTableIteratorT(const AirportTileTable *att, TileIndexType base_tile) : TileIteratorT<Tgeneric>(base_tile + ToTileIndexDiff<Tgeneric>(att->ti, MapOf(base_tile))), att(att), base_tile(base_tile) + { + } + +- inline TileIterator& operator ++() ++ inline TileIteratorT<Tgeneric>& operator ++() + { + this->att++; + if (this->att->ti.x == -0x80) { +- this->tile = INVALID_TILE; ++ IndexOf(this->tile) = INVALID_TILE_INDEX; + } else { +- this->tile = this->base_tile + ToTileIndexDiff(this->att->ti); ++ this->tile = this->base_tile + ToTileIndexDiff<Tgeneric>(this->att->ti, MapOf(this->base_tile)); + } + return *this; + } +@@ -60,12 +64,14 @@ public: + return this->att->gfx; + } + +- virtual AirportTileTableIterator *Clone() const ++ virtual AirportTileTableIteratorT<Tgeneric> *Clone() const + { +- return new AirportTileTableIterator(*this); ++ return new AirportTileTableIteratorT<Tgeneric>(*this); + } + }; + ++typedef AirportTileTableIteratorT<false> AirportTileTableIterator; ++ + /** List of default airport classes. */ + enum AirportClassID { + APC_BEGIN = 0, ///< Lowest valid airport class id +diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp +index 75b0696..9069944 100644 +--- a/src/newgrf_debug_gui.cpp ++++ b/src/newgrf_debug_gui.cpp +@@ -56,7 +56,7 @@ NewGrfDebugSpritePicker _newgrf_debug_sprite_picker = { SPM_NONE, NULL, 0, Small + */ + static inline uint GetFeatureIndex(uint window_number) + { +- return GB(window_number, 0, 24); ++ return GB(window_number, 0, 27); + } + + /** +@@ -68,8 +68,8 @@ static inline uint GetFeatureIndex(uint window_number) + */ + static inline uint GetInspectWindowNumber(GrfSpecFeature feature, uint index) + { +- assert((index >> 24) == 0); +- return (feature << 24) | index; ++ assert((index >> 27) == 0); ++ return (feature << 27) | index; + } + + /** +@@ -246,7 +246,7 @@ struct NIFeature { + */ + static inline GrfSpecFeature GetFeatureNum(uint window_number) + { +- return (GrfSpecFeature)GB(window_number, 24, 8); ++ return (GrfSpecFeature)GB(window_number, 27, 5); + } + + /** +diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp +index 6c9c614..d0a0566 100644 +--- a/src/newgrf_house.cpp ++++ b/src/newgrf_house.cpp +@@ -469,6 +469,8 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou + DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette)); + } + ++ DrawOverlay(ti, MP_HOUSE); ++ + DrawNewGRFTileSeq(ti, dts, TO_HOUSES, stage, palette); + } + +diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp +index 90a1755..779b44e 100644 +--- a/src/newgrf_industrytiles.cpp ++++ b/src/newgrf_industrytiles.cpp +@@ -184,6 +184,8 @@ static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGro + } + } + ++ DrawOverlay(ti, MP_INDUSTRY); ++ + DrawNewGRFTileSeq(ti, dts, TO_INDUSTRIES, stage, GENERAL_SPRITE_COLOUR(rnd_colour)); + } + +diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp +index cd5dad7..8fd51a7 100644 +--- a/src/newgrf_station.cpp ++++ b/src/newgrf_station.cpp +@@ -676,6 +676,23 @@ CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_til + return GetErrorMessageFromLocationCallbackResult(cb_res, statspec->grf_prop.grffile, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + } + ++/** ++ * Find out whether a given station type has its own layout. ++ * ++ * The default TTD station layout can be overloaded by property 0E ("define custom layout"), ++ * property 0F ("copy custom layout") or by providing callback 24 ("custom station layout"). ++ * ++ * @param statspec Station spec. ++ * @return \c false if the station always uses the default TTD layout, \c true otherwise. ++ */ ++bool IsCustomLayoutStation(const StationSpec *statspec) ++{ ++ if (statspec->lengths != 0) return true; ++ ++ StationResolverObject object(statspec, NULL, INVALID_TILE, CBID_STATION_TILE_LAYOUT); ++ const SpriteGroup *group = object.Resolve(); ++ return group != NULL && group->type == SGT_CALLBACK; ++} + + /** + * Allocate a StationSpec to a Station. This is called once per build operation. +diff --git a/src/newgrf_station.h b/src/newgrf_station.h +index ffb827c..30b43a5 100644 +--- a/src/newgrf_station.h ++++ b/src/newgrf_station.h +@@ -175,6 +175,8 @@ SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseS + uint16 GetStationCallback(CallbackID callback, uint32 param1, uint32 param2, const StationSpec *statspec, BaseStation *st, TileIndex tile); + CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, byte plat_len, byte numtracks); + ++bool IsCustomLayoutStation(const StationSpec *statspec); ++ + /* Allocate a StationSpec to a Station. This is called once per build operation. */ + int AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec); + +diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp +index b5b9921..0db8cfe 100644 +--- a/src/object_cmd.cpp ++++ b/src/object_cmd.cpp +@@ -839,4 +839,5 @@ extern const TileTypeProcs _tile_type_object_procs = { + NULL, // vehicle_enter_tile_proc + GetFoundation_Object, // get_foundation_proc + TerraformTile_Object, // terraform_tile_proc ++ NULL, // copypaste_tile_proc + }; +diff --git a/src/object_map.h b/src/object_map.h +index 1aaf984..2258d9a 100644 +--- a/src/object_map.h ++++ b/src/object_map.h +@@ -49,7 +49,7 @@ static inline bool IsObjectTypeTile(TileIndex t, ObjectType type) + static inline ObjectID GetObjectIndex(TileIndex t) + { + assert(IsTileType(t, MP_OBJECT)); +- return _m[t].m2 | _m[t].m5 << 16; ++ return GetTile(t)->m2 | GetTile(t)->m5 << 16; + } + + /** +@@ -61,7 +61,7 @@ static inline ObjectID GetObjectIndex(TileIndex t) + static inline byte GetObjectRandomBits(TileIndex t) + { + assert(IsTileType(t, MP_OBJECT)); +- return _m[t].m3; ++ return GetTile(t)->m3; + } + + +@@ -78,12 +78,12 @@ static inline void MakeObject(TileIndex t, Owner o, ObjectID index, WaterClass w + SetTileType(t, MP_OBJECT); + SetTileOwner(t, o); + SetWaterClass(t, wc); +- _m[t].m2 = index; +- _m[t].m3 = random; +- _m[t].m4 = 0; +- _m[t].m5 = index >> 16; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = index; ++ GetTile(t)->m3 = random; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = index >> 16; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } + + #endif /* OBJECT_MAP_H */ +diff --git a/src/openttd.cpp b/src/openttd.cpp +index c149ebb..a5da97b 100644 +--- a/src/openttd.cpp ++++ b/src/openttd.cpp +@@ -57,6 +57,7 @@ + #include "hotkeys.h" + #include "newgrf.h" + #include "misc/getoptdata.h" ++#include "clipboard_func.h" + #include "game/game.hpp" + #include "game/game_config.hpp" + #include "town.h" +@@ -962,6 +963,24 @@ static void MakeNewGameDone() + MarkWholeScreenDirty(); + } + ++/* ++ * Too large size may be stored in settings (especially if switching between between OpenTTD ++ * versions with different map size limits), we have to check if it is valid before generating world. ++ * Simple separate checking of X and Y map sizes is not enough, as their sum is what counts for the limit. ++ * Check the size and decrease the larger of the sizes till the size is in limit. ++ */ ++static void FixConfigMapSize() ++{ ++ while (_settings_game.game_creation.map_x + _settings_game.game_creation.map_y > MAX_MAP_TILES_BITS) { ++ /* Repeat reducing larger of X/Y dimensions until the map size is within allowable limits */ ++ if (_settings_game.game_creation.map_x > _settings_game.game_creation.map_y) { ++ _settings_game.game_creation.map_x--; ++ } else { ++ _settings_game.game_creation.map_y--; ++ } ++ } ++} ++ + static void MakeNewGame(bool from_heightmap, bool reset_settings) + { + _game_mode = GM_NORMAL; +@@ -969,6 +988,7 @@ static void MakeNewGame(bool from_heightmap, bool reset_settings) + ResetGRFConfig(true); + + GenerateWorldSetCallback(&MakeNewGameDone); ++ FixConfigMapSize(); + GenerateWorld(from_heightmap ? GWM_HEIGHTMAP : GWM_NEWGAME, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y, reset_settings); + } + +@@ -984,6 +1004,7 @@ static void MakeNewEditorWorld() + ResetGRFConfig(true); + + GenerateWorldSetCallback(&MakeNewEditorWorldDone); ++ FixConfigMapSize(); + GenerateWorld(GWM_EMPTY, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); + } + +@@ -1071,8 +1092,13 @@ void SwitchToMode(SwitchMode new_mode) + } + } + #endif /* ENABLE_NETWORK */ +- /* Make sure all AI controllers are gone at quitting game */ +- if (new_mode != SM_SAVE_GAME) AI::KillAll(); ++ if (new_mode != SM_SAVE_GAME) { ++ /* Make sure all AI controllers are gone at quitting game */ ++ AI::KillAll(); ++ ++ /* Clear the clipboard */ ++ ClearClipboard(); ++ } + + switch (new_mode) { + case SM_EDITOR: // Switch to scenario editor +@@ -1129,6 +1155,7 @@ void SwitchToMode(SwitchMode new_mode) + case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor + SetLocalCompany(OWNER_NONE); + ++ FixConfigMapSize(); + GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); + MarkWholeScreenDirty(); + break; +@@ -1171,6 +1198,7 @@ void SwitchToMode(SwitchMode new_mode) + + case SM_GENRANDLAND: // Generate random land within scenario editor + SetLocalCompany(OWNER_NONE); ++ FixConfigMapSize(); + GenerateWorld(GWM_RANDOM, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); + /* XXX: set date */ + MarkWholeScreenDirty(); +diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp +index 57b29f3..18e6344 100644 +--- a/src/order_cmd.cpp ++++ b/src/order_cmd.cpp +@@ -1897,14 +1897,14 @@ restart: + + /** + * Checks if a vehicle has a depot in its order list. +- * @return True iff at least one order is a depot order. ++ * @return True iff at least one order is a depot order or a jump with condition on requires service. + */ + bool Vehicle::HasDepotOrder() const + { + const Order *order; + + FOR_VEHICLE_ORDERS(this, order) { +- if (order->IsType(OT_GOTO_DEPOT)) return true; ++ if (order->IsType(OT_GOTO_DEPOT) || (order->IsType(OT_CONDITIONAL) && (order->GetConditionVariable() == OCV_REQUIRES_SERVICE))) return true; + } + + return false; +diff --git a/src/overlay.h b/src/overlay.h +new file mode 100644 +index 0000000..f7d8e2d +--- /dev/null ++++ b/src/overlay.h +@@ -0,0 +1,73 @@ ++/* $Id$ */ ++ ++/** @file overlay.h Functions related to overlays. */ ++ ++#ifndef OVERLAY_H ++#define OVERLAY_H ++ ++#include "stdafx.h" ++#include "openttd.h" ++#include "core/bitmath_func.hpp" ++#include "gfx_func.h" ++ ++/** ++ * Transparency option bits: which position in _transparency_opt stands for which transparency. ++ * If you change the order, change the order of the ShowTransparencyToolbar() stuff in transparency_gui.cpp too. ++ * If you add or remove an option don't forget to change the transparency 'hot keys' in main_gui.cpp. ++ */ ++enum OverlayOption { ++ OO_COVERAGES = 0, ///< coverage ++ OO_END, ++}; ++ ++typedef uint OverlayOptionBits; ///< overlay option bits ++extern OverlayOptionBits _overlay_opt; ++extern OverlayOptionBits _overlay_lock; ++ ++/** ++ * Check if the overlay option bit is set ++ * and if we aren't in the game menu (there's no overlay) ++ * ++ * @param to the structure which overlay option is ask for ++ */ ++static inline bool IsOverlaySet(OverlayOption to) ++{ ++ return (HasBit(_overlay_opt, to) && _game_mode != GM_MENU); ++} ++ ++/** ++ * Toggle the overlay option bit ++ * ++ * @param to the overlay option to be toggled ++ */ ++static inline void ToggleOverlay(OverlayOption to) ++{ ++ ToggleBit(_overlay_opt, to); ++} ++ ++/** ++ * Toggle the overlay lock bit ++ * ++ * @param to the overlay option to be locked or unlocked ++ */ ++static inline void ToggleOverlayLock(OverlayOption to) ++{ ++ ToggleBit(_overlay_lock, to); ++} ++ ++/** Set or clear all non-locked overlay options */ ++static inline void ResetRestoreAllOverlays() ++{ ++ /* if none of the non-locked options are set */ ++ if ((_overlay_opt & ~_overlay_lock) == 0) { ++ /* set all non-locked options */ ++ _overlay_opt |= GB(~_overlay_lock, 0, OO_END); ++ } else { ++ /* clear all non-locked options */ ++ _overlay_opt &= _overlay_lock; ++ } ++ ++ MarkWholeScreenDirty(); ++} ++ ++#endif /* OVERLAY_H */ +\ No newline at end of file +diff --git a/src/overlay_cmd.cpp b/src/overlay_cmd.cpp +new file mode 100644 +index 0000000..059121d +--- /dev/null ++++ b/src/overlay_cmd.cpp +@@ -0,0 +1,65 @@ ++/* $Id$ */ ++ ++/** @file overlay_cmd.cpp Handling of overlays. */ ++ ++#include "stdafx.h" ++#include "tile_type.h" ++#include "tile_cmd.h" ++#include "overlay.h" ++#include "station_func.h" ++#include "viewport_func.h" ++#include "overlay_cmd.h" ++ ++Overlays* Overlays::instance = NULL; ++ ++Overlays* Overlays::Instance() ++{ ++ if (instance == NULL) ++ instance = new Overlays(); ++ return instance; ++}; ++ ++void Overlays::AddStation(const Station* st) ++{ ++ this->catchmentOverlay.insert(st); ++}; ++ ++void Overlays::RemoveStation(const Station* st) ++{ ++ this->catchmentOverlay.erase(st); ++}; ++ ++void Overlays::ToggleStation(const Station* st) ++{ ++ if(this->HasStation(st)) { ++ this->RemoveStation(st); ++ } else { ++ this->AddStation(st); ++ } ++}; ++ ++void Overlays::Clear() ++{ ++ this->catchmentOverlay.clear(); ++}; ++ ++bool Overlays::IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) ++{ ++ for(std::set<const Station *>::iterator iter = catchmentOverlay.begin();iter != catchmentOverlay.end();) { ++ const Station *st = *iter; ++ if( st->IsTileInCatchmentArea(ti, type)) ++ return true; ++ iter++; ++ } ++ return false; ++}; ++ ++bool Overlays::HasStation(const Station* st) ++{ ++ return (this->catchmentOverlay.find(st) != this->catchmentOverlay.end()); ++}; ++ ++Overlays::~Overlays() ++{ ++ this->catchmentOverlay.clear(); ++}; +\ No newline at end of file +diff --git a/src/overlay_cmd.h b/src/overlay_cmd.h +new file mode 100644 +index 0000000..a8b4383 +--- /dev/null ++++ b/src/overlay_cmd.h +@@ -0,0 +1,38 @@ ++/* $Id$ */ ++ ++/** @file overlay_cmd.h Functions related to overlays. */ ++ ++#ifndef OVERLAY_CMD_H ++#define OVERLAY_CMD_H ++ ++#include "tile_type.h" ++#include "tile_cmd.h" ++#include "station_base.h" ++#include <set> ++ ++class Overlays { ++ ++ std::set<const Station *> catchmentOverlay; ++ ++protected: ++ static Overlays* instance; ++ ++public: ++ static Overlays* Instance(); ++ ++ void AddStation(const Station* st); ++ ++ void RemoveStation(const Station *st); ++ ++ void ToggleStation(const Station* st); ++ ++ void Clear(); ++ ++ bool IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type); ++ ++ bool HasStation(const Station* st); ++ ++ virtual ~Overlays(); ++}; ++ ++#endif // OVERLAY_CMD_H +\ No newline at end of file +diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp +index 9f19b02..76f3340 100644 +--- a/src/pathfinder/follow_track.hpp ++++ b/src/pathfinder/follow_track.hpp +@@ -358,7 +358,7 @@ protected: + if (IsTunnel(m_new_tile)) { + if (!m_is_tunnel) { + DiagDirection tunnel_enterdir = GetTunnelBridgeDirection(m_new_tile); +- if (tunnel_enterdir != m_exitdir) { ++ if (tunnel_enterdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { + m_err = EC_NO_WAY; + return false; + } +@@ -366,7 +366,7 @@ protected: + } else { // IsBridge(m_new_tile) + if (!m_is_bridge) { + DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile); +- if (ramp_enderdir != m_exitdir) { ++ if (ramp_enderdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { + m_err = EC_NO_WAY; + return false; + } +diff --git a/src/rail.cpp b/src/rail.cpp +index 79598ec..d60cb87 100644 +--- a/src/rail.cpp ++++ b/src/rail.cpp +@@ -151,10 +151,23 @@ extern const TrackdirBits _uphill_trackdirs[] = { + TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE, ///< 30 SLOPE_STEEP_E -> inclined for diagonal track + }; + ++/** Lookup table to transform a Track by a given DirTransformation. */ ++extern const byte _track_transformation_map[DTR_END][TRACK_END] = { ++ { TRACK_X, TRACK_Y, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT }, // DTR_IDENTITY ++ { TRACK_Y, TRACK_X, TRACK_RIGHT, TRACK_LEFT, TRACK_UPPER, TRACK_LOWER }, // DTR_ROTATE_90_R ++ { TRACK_X, TRACK_Y, TRACK_LOWER, TRACK_UPPER, TRACK_RIGHT, TRACK_LEFT }, // DTR_ROTATE_180 ++ { TRACK_Y, TRACK_X, TRACK_LEFT, TRACK_RIGHT, TRACK_LOWER, TRACK_UPPER }, // DTR_ROTATE_90_L ++ { TRACK_X, TRACK_Y, TRACK_RIGHT, TRACK_LEFT, TRACK_LOWER, TRACK_UPPER }, // DTR_REFLECT_NE_SW ++ { TRACK_Y, TRACK_X, TRACK_LOWER, TRACK_UPPER, TRACK_LEFT, TRACK_RIGHT }, // DTR_REFLECT_W_E ++ { TRACK_X, TRACK_Y, TRACK_LEFT, TRACK_RIGHT, TRACK_UPPER, TRACK_LOWER }, // DTR_REFLECT_NW_SE ++ { TRACK_Y, TRACK_X, TRACK_UPPER, TRACK_LOWER, TRACK_RIGHT, TRACK_LEFT } // DTR_REFLECT_N_S ++}; ++ + /** + * Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile. + */ +-RailType GetTileRailType(TileIndex tile) ++template <bool Tgeneric> ++RailType GetTileRailType(typename TileIndexT<Tgeneric>::T tile) + { + switch (GetTileType(tile)) { + case MP_RAILWAY: +@@ -178,6 +191,9 @@ RailType GetTileRailType(TileIndex tile) + } + return INVALID_RAILTYPE; + } ++/* instantiate */ ++template RailType GetTileRailType<false>(TileIndex tile); ++template RailType GetTileRailType<true>(GenericTileIndex tile); + + /** + * Finds out if a company has a certain railtype available +diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp +index 2010f9b..ad0d98f 100644 +--- a/src/rail_cmd.cpp ++++ b/src/rail_cmd.cpp +@@ -11,6 +11,7 @@ + + #include "stdafx.h" + #include "cmd_helper.h" ++#include "copypaste_cmd.h" + #include "viewport_func.h" + #include "command_func.h" + #include "depot_base.h" +@@ -33,6 +34,7 @@ + #include "strings_func.h" + #include "company_gui.h" + #include "object_map.h" ++#include "clipboard_gui.h" + + #include "table/strings.h" + #include "table/railtypes.h" +@@ -423,39 +425,43 @@ static inline bool ValParamTrackOrientation(Track track) + } + + /** +- * Build a single piece of rail +- * @param tile tile to build on ++ * Build a set of rail tracks on a given tile ++ * @param tile tile to build on + * @param flags operation to perform + * @param p1 railtype of being built piece (normal, mono, maglev) +- * @param p2 rail track to build ++ * @param p2 TrackBits to build + * @param text unused + * @return the cost of this operation or an error + */ +-CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) ++CommandCost CmdBuildSingleRails(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) + { + RailType railtype = Extract<RailType, 0, 4>(p1); +- Track track = Extract<Track, 0, 3>(p2); ++ TrackBits trackbits = (TrackBits)GB(p2, 0, 6); + CommandCost cost(EXPENSES_CONSTRUCTION); + +- if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; ++ if (!ValParamRailtype(railtype)) return CMD_ERROR; + + Slope tileh = GetTileSlope(tile); +- TrackBits trackbit = TrackToTrackBits(track); ++ TrackBits currbits = TRACK_BIT_NONE; ++ ++ /* whether the tile needs to be cleared and built from scratch */ ++ bool make_new_rail_tile = true; + + switch (GetTileType(tile)) { + case MP_RAILWAY: { + CommandCost ret = CheckTileOwnership(tile); + if (ret.Failed()) return ret; + +- if (!IsPlainRail(tile)) return CMD_ERROR; ++ if (!IsPlainRail(tile)) break; + + if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION); + +- ret = CheckTrackCombination(tile, trackbit, flags); +- if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track); ++ currbits = GetTrackBits(tile); ++ ret = CheckTrackCombination(tile, trackbits, flags); ++ if (ret.Succeeded()) ret = EnsureNoTrainOnTrackBits(tile, trackbits & ~currbits); + if (ret.Failed()) return ret; + +- ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile); ++ ret = CheckRailSlope(tileh, trackbits, GetTrackBits(tile), tile); + if (ret.Failed()) return ret; + cost.AddCost(ret); + +@@ -472,20 +478,21 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u + } + } + +- if (flags & DC_EXEC) { ++ if ((trackbits & ~currbits) && (flags & DC_EXEC)) { + SetRailGroundType(tile, RAIL_GROUND_BARREN); +- TrackBits bits = GetTrackBits(tile); +- SetTrackBits(tile, bits | trackbit); ++ SetTrackBits(tile, currbits | trackbits); + /* Subtract old infrastructure count. */ +- uint pieces = CountBits(bits); +- if (TracksOverlap(bits)) pieces *= pieces; ++ uint pieces = CountBits(currbits); ++ if (TracksOverlap(currbits)) pieces *= pieces; + Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces; + /* Add new infrastructure count. */ +- pieces = CountBits(bits | trackbit); +- if (TracksOverlap(bits | trackbit)) pieces *= pieces; ++ pieces = CountBits(currbits | trackbits); ++ if (TracksOverlap(currbits | trackbits)) pieces *= pieces; + Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces; + DirtyCompanyInfrastructureWindows(GetTileOwner(tile)); + } ++ ++ make_new_rail_tile = false; + break; + } + +@@ -506,8 +513,8 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u + RoadTypes roadtypes = GetRoadTypes(tile); + RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); + RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); +- if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) || +- (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) { ++ if ((trackbits == TRACK_BIT_X && ((road | tram) & ROAD_X) == 0) || ++ (trackbits == TRACK_BIT_Y && ((road | tram) & ROAD_Y) == 0)) { + Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); + Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + /* Disallow breaking end-of-line of someone else +@@ -526,7 +533,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u + cost.AddCost((num_new_road_pieces + num_new_tram_pieces) * _price[PR_BUILD_ROAD]); + + if (flags & DC_EXEC) { +- MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); ++ MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (trackbits == TRACK_BIT_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); + UpdateLevelCrossing(tile, false); + Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR; + DirtyCompanyInfrastructureWindows(_current_company); +@@ -539,54 +546,76 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u + DirtyCompanyInfrastructureWindows(tram_owner); + } + } +- break; ++ make_new_rail_tile = false; + } ++ } else if (IsLevelCrossing(tile)) { ++ currbits = GetCrossingRailBits(tile); ++ if (trackbits == currbits) return_cmd_error(STR_ERROR_ALREADY_BUILT); + } +- +- if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) { +- return_cmd_error(STR_ERROR_ALREADY_BUILT); +- } +- /* FALL THROUGH */ ++ break; + } + +- default: { +- /* Will there be flat water on the lower halftile? */ +- bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); ++ default: ++ break; ++ } + +- CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); +- if (ret.Failed()) return ret; +- cost.AddCost(ret); ++ if (make_new_rail_tile) { ++ /* Will there be flat water on the lower halftile? */ ++ bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); + +- ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); +- if (ret.Failed()) return ret; +- cost.AddCost(ret); ++ CommandCost ret = CheckRailSlope(tileh, trackbits, TRACK_BIT_NONE, tile); ++ if (ret.Failed()) return ret; ++ cost.AddCost(ret); + +- if (water_ground) { +- cost.AddCost(-_price[PR_CLEAR_WATER]); +- cost.AddCost(_price[PR_CLEAR_ROUGH]); +- } ++ ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); ++ if (ret.Failed()) return ret; ++ cost.AddCost(ret); ++ currbits = TRACK_BIT_NONE; // the tile is clear now + +- if (flags & DC_EXEC) { +- MakeRailNormal(tile, _current_company, trackbit, railtype); +- if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); +- Company::Get(_current_company)->infrastructure.rail[railtype]++; +- DirtyCompanyInfrastructureWindows(_current_company); +- } +- break; ++ if (water_ground) { ++ cost.AddCost(-_price[PR_CLEAR_WATER]); ++ cost.AddCost(_price[PR_CLEAR_ROUGH]); ++ } ++ ++ if (flags & DC_EXEC) { ++ MakeRailNormal(tile, _current_company, trackbits, railtype); ++ if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); ++ Company::Get(_current_company)->infrastructure.rail[railtype]++; ++ DirtyCompanyInfrastructureWindows(_current_company); + } + } + + if (flags & DC_EXEC) { + MarkTileDirtyByTile(tile); +- AddTrackToSignalBuffer(tile, track, _current_company); +- YapfNotifyTrackLayoutChange(tile, track); ++ Track track; ++ FOR_EACH_SET_TRACK(track, trackbits & ~currbits) { ++ AddTrackToSignalBuffer(tile, track, _current_company); ++ YapfNotifyTrackLayoutChange(tile, track); ++ } + } + +- cost.AddCost(RailBuildCost(railtype)); ++ cost.AddCost(CountBits(trackbits & ~currbits) * RailBuildCost(railtype)); + return cost; + } + + /** ++ * Build a single piece of rail ++ * @param tile tile to build on ++ * @param flags operation to perform ++ * @param p1 railtype of being built piece (normal, mono, maglev) ++ * @param p2 rail track to build ++ * @param text unused ++ * @return the cost of this operation or an error ++ */ ++CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) ++{ ++ Track track = Extract<Track, 0, 3>(p2); ++ if (!ValParamTrackOrientation(track)) return CMD_ERROR; ++ ++ return CmdBuildSingleRails(tile, flags, p1, TrackToTrackBits(track), text); ++} ++ ++/** + * Remove a single piece of track + * @param tile tile to remove track from + * @param flags operation to perform +@@ -1035,9 +1064,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, + if (sigtype > SIGTYPE_LAST) return CMD_ERROR; + if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR; + +- /* You can only build signals on plain rail tiles, and the selected track must exist */ +- if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || +- !HasTrack(tile, track)) { ++ /* You can only build signals on plain rail tiles or tunnel/bridges, and the selected track must exist */ ++ if (IsTileType(tile, MP_TUNNELBRIDGE)) { ++ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR; ++ CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track); ++ if (ret.Failed()) return ret; ++ } else if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { + return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); + } + /* Protect against invalid signal copying */ +@@ -1046,6 +1078,52 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, + CommandCost ret = CheckTileOwnership(tile); + if (ret.Failed()) return ret; + ++ CommandCost cost(EXPENSES_CONSTRUCTION); ++ /* handle signals simulation on tunnel/bridge. */ ++ if (IsTileType(tile, MP_TUNNELBRIDGE)) { ++ TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile); ++ cost = CommandCost(); ++ if (!HasWormholeSignals(tile)) { // toggle signal zero costs. ++ if (p2 != 12) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2)); // minimal 1 ++ } ++ if (flags & DC_EXEC) { ++ if (p2 == 0 && HasWormholeSignals(tile)){ // Toggle signal if already signals present. ++ if (IsTunnelBridgeEntrance (tile)) { ++ ClrBitTunnelBridgeSignal(tile); ++ ClrBitTunnelBridgeExit(tile_exit); ++ SetBitTunnelBridgeExit(tile); ++ SetBitTunnelBridgeSignal(tile_exit); ++ } else { ++ ClrBitTunnelBridgeSignal(tile_exit); ++ ClrBitTunnelBridgeExit(tile); ++ SetBitTunnelBridgeExit(tile_exit); ++ SetBitTunnelBridgeSignal(tile); ++ } ++ } else{ ++ /* Create one direction tunnel/bridge if required. */ ++ if (p2 == 0) { ++ SetBitTunnelBridgeSignal(tile); ++ SetBitTunnelBridgeExit(tile_exit); ++ } else if (p2 == 4 || p2 == 8) { ++ DiagDirection tbdir = GetTunnelBridgeDirection(tile); ++ /* If signal only on one side build accoringly one-way tunnel/bridge. */ ++ if ((p2 == 8 && (tbdir == DIAGDIR_NE || tbdir == DIAGDIR_SE)) || ++ (p2 == 4 && (tbdir == DIAGDIR_SW || tbdir == DIAGDIR_NW))) { ++ SetBitTunnelBridgeSignal(tile); ++ SetBitTunnelBridgeExit(tile_exit); ++ } else { ++ SetBitTunnelBridgeSignal(tile_exit); ++ SetBitTunnelBridgeExit(tile); ++ } ++ } ++ } ++ MarkTileDirtyByTile(tile); ++ MarkTileDirtyByTile(tile_exit); ++ AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company); ++ YapfNotifyTrackLayoutChange(tile, track); ++ } ++ return cost; ++ } + /* See if this is a valid track combination for signals (no overlap) */ + if (TracksOverlap(GetTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); + +@@ -1055,28 +1133,25 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, + /* you can not convert a signal if no signal is on track */ + if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); + +- CommandCost cost; + if (!HasSignalOnTrack(tile, track)) { + /* build new signals */ +- cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); ++ cost.AddCost(_price[PR_BUILD_SIGNALS]); + } else { + if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) { + /* convert signals <-> semaphores */ +- cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); +- ++ cost.AddCost(_price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); + } else if (convert_signal) { + /* convert button pressed */ + if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) { + /* convert electric <-> semaphore */ +- cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); +- } else { ++ cost .AddCost(_price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); ++ } else if (GetSignalType(tile, track) != sigtype) { + /* it is free to change signal type: normal-pre-exit-combo */ +- cost = CommandCost(); ++ } else { ++ return_cmd_error(STR_ERROR_ALREADY_BUILT); + } +- + } else { + /* it is free to change orientation/pre-exit-combo signals */ +- cost = CommandCost(); + } + } + +@@ -1213,6 +1288,7 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal + return true; + + case MP_TUNNELBRIDGE: { ++ if (!remove && HasWormholeSignals(tile)) return false; + TileIndex orig_tile = tile; // backup old value + + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false; +@@ -1324,7 +1400,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin + bool had_success = false; + for (;;) { + /* only build/remove signals with the specified density */ +- if (remove || minimise_gaps || signal_ctr % signal_density == 0) { ++ if (remove || minimise_gaps || signal_ctr % signal_density == 0 || IsTileType(tile, MP_TUNNELBRIDGE)) { + uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3); + SB(p1, 3, 1, mode); + SB(p1, 4, 1, semaphores); +@@ -1363,6 +1439,14 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin + /* Be user-friendly and try placing signals as much as possible */ + if (ret.Succeeded()) { + had_success = true; ++ if (IsTileType(tile, MP_TUNNELBRIDGE)) { ++ if ((!autofill && GetTunnelBridgeDirection(tile) == TrackdirToExitdir(trackdir)) || ++ (autofill && GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir))) { ++ total_cost.AddCost(ret); ++ } ++ } else { ++ total_cost.AddCost(ret); ++ } + total_cost.AddCost(ret); + last_used_ctr = last_suitable_ctr; + last_suitable_tile = INVALID_TILE; +@@ -1437,12 +1521,26 @@ CommandCost CmdBuildSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, + CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) + { + Track track = Extract<Track, 0, 3>(p1); ++ Money cost = _price[PR_CLEAR_SIGNALS]; + +- if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { +- return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); +- } +- if (!HasSignalOnTrack(tile, track)) { +- return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); ++ if (IsTileType(tile, MP_TUNNELBRIDGE)) { ++ TileIndex end = GetOtherTunnelBridgeEnd(tile); ++ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); ++ if (!HasWormholeSignals(tile)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); ++ ++ cost *= ((GetTunnelBridgeLength(tile, end) + 4) >> 2); ++ ++ CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track); ++ if (ret.Failed()) return ret; ++ } else { ++ if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { ++ return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); ++ } ++ if (!HasSignalOnTrack(tile, track)) { ++ return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); ++ } ++ CommandCost ret = EnsureNoTrainOnTrack(tile, track); ++ if (ret.Failed()) return ret; + } + + /* Only water can remove signals from anyone */ +@@ -1453,6 +1551,20 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1 + + /* Do it? */ + if (flags & DC_EXEC) { ++ ++ if (HasWormholeSignals(tile)) { // handle tunnel/bridge signals. ++ TileIndex end = GetOtherTunnelBridgeEnd(tile); ++ ClrBitTunnelBridgeExit(tile); ++ ClrBitTunnelBridgeExit(end); ++ ClrBitTunnelBridgeSignal(tile); ++ ClrBitTunnelBridgeSignal(end); ++ GetTile(tile)->m2 = 0; ++ GetTile(end)->m2 = 0; ++ MarkTileDirtyByTile(tile); ++ MarkTileDirtyByTile(end); ++ return CommandCost(EXPENSES_CONSTRUCTION, cost); ++ } ++ + Train *v = NULL; + if (HasReservedTracks(tile, TrackToTrackBits(track))) { + v = GetTrainForReservation(tile, track); +@@ -1488,7 +1600,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1 + MarkTileDirtyByTile(tile); + } + +- return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]); ++ return CommandCost(EXPENSES_CONSTRUCTION, cost); + } + + /** +@@ -2394,6 +2506,8 @@ static void DrawTile_Track(TileInfo *ti) + + if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti); + ++ DrawOverlay(ti, MP_RAILWAY); ++ + if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); + + if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti); +@@ -2469,6 +2583,8 @@ static void DrawTile_Track(TileInfo *ti) + int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT); + relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset(); + ++ DrawOverlay(ti, MP_RAILWAY); ++ + if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); + + DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette); +@@ -3026,6 +3142,205 @@ static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + ++void CopyPastePlaceTracks(GenericTileIndex tile, RailType railtype, TrackBits tracks) ++{ ++ if (IsMainMapTile(tile)) { ++ _current_pasting->DoCommand(AsMainMapTile(tile), railtype, tracks, CMD_BUILD_SINGLE_RAILS | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK)); ++ } else { ++ MakeRailNormal(tile, OWNER_NONE, tracks, railtype); ++ } ++} ++ ++/** ++ * Estimate signal paste cost. ++ * @param tile The tile where to paste. ++ * @param tile The track where to put the signal (it may not exist, yet). ++ * @param variant The variant of the signal (semaphore/electric) ++ * @param type The type of the signal (normal/combo/pbs) ++ * @param no_sig_trackdir Signal orientation, the trackdir where a signal is NOT present (INVALID_TRACKDIR = two way signal) ++ */ ++static CommandCost EstimateSignalPasteCost(TileIndex tile, Track track, SignalVariant variant, SignalType type, Trackdir no_sig_trackdir) ++{ ++ switch (GetTileType(tile)) { ++ case MP_CLEAR: ++ case MP_TREES: ++ return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); ++ ++ case MP_RAILWAY: ++ if (!IsPlainRail(tile)) return CMD_ERROR; ++ if (TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); ++ if (!HasSignalOnTrack(tile, track)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); ++ if (GetSignalVariant(tile, track) != variant) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS] + _price[PR_BUILD_SIGNALS]); ++ if (GetSignalType(tile, track) != type) return CommandCost(EXPENSES_CONSTRUCTION, 0); ++ if (IsValidTrackdir(no_sig_trackdir)) { ++ assert(TrackdirToTrack(no_sig_trackdir) == track); ++ if (HasSignalOnTrackdir(tile, no_sig_trackdir)) return CommandCost(EXPENSES_CONSTRUCTION, 0); ++ } else { ++ assert(!IsPbsSignal(type)); ++ if (!HasSignalOnTrackdir(tile, TrackToTrackdir(track)) || !HasSignalOnTrackdir(tile, ReverseTrackdir(TrackToTrackdir(track)))) { ++ return CommandCost(EXPENSES_CONSTRUCTION, 0); ++ } ++ } ++ return_cmd_error(STR_ERROR_ALREADY_BUILT); ++ ++ case MP_ROAD: ++ return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST); ++ ++ default: ++ break; ++ } ++ return CMD_ERROR; ++} ++ ++static void CopyPastePlaceSignal(GenericTileIndex tile, Track track, SignalVariant variant, SignalType type, Trackdir no_sig_trackdir) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ if (!(_current_pasting->dc_flags & DC_EXEC)) { ++ _current_pasting->CollectCost(EstimateSignalPasteCost(t, track, variant, type, no_sig_trackdir), t, STR_ERROR_CAN_T_BUILD_SIGNALS_HERE); ++ } else { ++ /* build the signal */ ++ uint32 p1 = track | (variant << 4) | (type << 5); ++ if (IsTileType(t, MP_RAILWAY) && HasSignalOnTrack(t, track)) p1 |= (1 << 8); // convert existing ++ _current_pasting->DoCommand(t, p1, 0, CMD_BUILD_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_BUILD_SIGNALS_HERE)); ++ ++ /* cycle until the proper signal side */ ++ if ((_current_pasting->last_result.Succeeded() || _current_pasting->last_result.GetErrorMessage() == STR_ERROR_ALREADY_BUILT)) { ++ if (IsValidTrackdir(no_sig_trackdir)) { ++ assert(TrackdirToTrack(no_sig_trackdir) == track); ++ while (HasSignalOnTrackdir(t, no_sig_trackdir)) { ++ _current_pasting->DoCommand(t, track, 0, CMD_BUILD_SIGNALS); ++ if (_current_pasting->last_result.Failed()) break; ++ } ++ } else { ++ assert(!IsPbsSignal(GetSignalType(t, track))); ++ while (!HasSignalOnTrackdir(t, TrackToTrackdir(track)) || ++ !HasSignalOnTrackdir(t, ReverseTrackdir(TrackToTrackdir(track)))) { ++ _current_pasting->DoCommand(t, track, 0, CMD_BUILD_SIGNALS); ++ if (_current_pasting->last_result.Failed()) break; ++ } ++ } ++ } ++ } ++ } else { ++ SetHasSignals(tile, true); ++ SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track)); ++ SetSignalVariant(tile, track, variant); ++ SetSignalType(tile, track, type); ++ if (IsValidTrackdir(no_sig_trackdir)) { ++ assert(TrackdirToTrack(no_sig_trackdir) == track); ++ while (HasSignalOnTrackdir(tile, no_sig_trackdir)) CycleSignalSide(tile, track); ++ } ++ } ++} ++ ++static void CopyPastePlaceRailDepot(GenericTileIndex tile, RailType railtype, DiagDirection dir) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ if (IsRailDepotTile(t) && IsTileOwner(t, _current_company) && ++ GetRailDepotDirection(t) == dir && GetRailType(t) == railtype) { ++ _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT); ++ } else { ++ _current_pasting->DoCommand(t, railtype, dir, CMD_BUILD_TRAIN_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT)); ++ } ++ } else { ++ MakeRailDepot(tile, OWNER_NONE, 0, dir, railtype); ++ } ++} ++ ++/** ++ * Test a given rail tile if there is any contented to be copied from it. ++ * @param tile the tile to test ++ * @param mode copy-paste mode ++ * @param company the #Company to check ownership against to ++ * @param preview (out, may be NULL) information on how to higlight preview of the tile ++ * @return whether this tile needs to be copy-pasted ++ */ ++bool TestRailTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) ++{ ++ if (preview != NULL) MemSetT(preview, 0); ++ ++ if (!(mode & CPM_WITH_RAIL_TRANSPORT)) return false; ++ if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; ++ ++ if (preview != NULL) { ++ switch (GetRailTileType(tile)) { ++ case RAIL_TILE_NORMAL: ++ case RAIL_TILE_SIGNALS: ++ preview->highlight_track_bits = GetTrackBits(tile); ++ break; ++ ++ case RAIL_TILE_DEPOT: ++ preview->highlight_tile_rect = true; ++ preview->highlight_track_bits = TrackToTrackBits(GetRailDepotTrack(tile)); ++ break; ++ } ++ } ++ ++ return true; ++} ++ ++void CopyPasteTile_Rail(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) ++{ ++ if (!TestRailTileCopyability(src_tile, copy_paste.mode)) return; ++ ++ /* Terraform tiles if needed */ ++ if (IsMainMapTile(dst_tile) && ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL)) { ++ CopyPasteHeights(GenericTileArea(src_tile, 1, 1), dst_tile, copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ } ++ ++ RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); ++ ++ switch (GetRailTileType(src_tile)) { ++ case RAIL_TILE_NORMAL: ++ /* copy/paste tracks */ ++ CopyPastePlaceTracks(dst_tile, railtype, TransformTrackBits(GetTrackBits(src_tile), copy_paste.transformation)); ++ break; ++ ++ case RAIL_TILE_SIGNALS: { ++ /* copy/paste tracks */ ++ CopyPastePlaceTracks(dst_tile, railtype, TransformTrackBits(GetTrackBits(src_tile), copy_paste.transformation)); ++ if (IsPastingInterrupted()) return; ++ ++ /* copy/paste signals */ ++ Track src_track; ++ uint signal_bits = GetPresentSignals(src_tile); ++ FOR_EACH_SET_TRACK(src_track, GetTrackBits(src_tile)) { ++ /* extract signal bits related to curent track */ ++ uint track_signal_bits = signal_bits & SignalOnTrack(src_track); ++ if (track_signal_bits == 0) continue; ++ signal_bits &= ~track_signal_bits; ++ ++ /* calculate trackdir pointing to the back of the signal (INVALID_TRACKDIR for two-way signals) */ ++ Trackdir dst_no_sig_trackdir; ++ if (HasExactlyOneBit(track_signal_bits)) { ++ dst_no_sig_trackdir = TransformTrackdir(TrackToTrackdir(src_track), copy_paste.transformation); ++ if (HasSignalOnTrackdir(src_tile, TrackToTrackdir(src_track)) != ((copy_paste.mode & CPM_MIRROR_SIGNALS) != 0)) { ++ dst_no_sig_trackdir = ReverseTrackdir(dst_no_sig_trackdir); ++ } ++ } else { ++ dst_no_sig_trackdir = INVALID_TRACKDIR; ++ } ++ ++ /* place the signal */ ++ CopyPastePlaceSignal(dst_tile, TransformTrack(src_track, copy_paste.transformation), GetSignalVariant(src_tile, src_track), GetSignalType(src_tile, src_track), dst_no_sig_trackdir); ++ if (IsPastingInterrupted()) return; ++ ++ if (signal_bits == 0) break; // no more signals to paste ++ } ++ break; ++ } ++ ++ case RAIL_TILE_DEPOT: // Rail depot ++ CopyPastePlaceRailDepot(dst_tile, railtype, TransformDiagDir(GetRailDepotDirection(src_tile), copy_paste.transformation)); ++ break; ++ ++ default: ++ NOT_REACHED(); // corrupted tile data? ++ } ++} + + extern const TileTypeProcs _tile_type_rail_procs = { + DrawTile_Track, // draw_tile_proc +@@ -3042,4 +3357,5 @@ extern const TileTypeProcs _tile_type_rail_procs = { + VehicleEnter_Track, // vehicle_enter_tile_proc + GetFoundation_Track, // get_foundation_proc + TerraformTile_Track, // terraform_tile_proc ++ CopyPasteTile_Rail, // copypaste_tile_proc + }; +diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp +index a48abd2..73f2f12 100644 +--- a/src/rail_gui.cpp ++++ b/src/rail_gui.cpp +@@ -191,7 +191,7 @@ static void PlaceRail_Station(TileIndex tile) + VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); + VpSetPlaceSizingLimit(_settings_game.station.station_spread); + } else { +- uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | _ctrl_pressed << 24; ++ uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | (_settings_game.station.adjacent_stations && _ctrl_pressed) << 24; + uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; + + int w = _settings_client.gui.station_numtracks; +@@ -885,7 +885,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) + + if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); + +- uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | _ctrl_pressed << 24; ++ uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | (_settings_game.station.adjacent_stations && _ctrl_pressed) << 24; + uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; + + CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" }; +diff --git a/src/rail_map.h b/src/rail_map.h +index 2431a79..b185d15 100644 +--- a/src/rail_map.h ++++ b/src/rail_map.h +@@ -34,11 +34,16 @@ enum RailTileType { + * @pre IsTileType(t, MP_RAILWAY) + * @return the RailTileType + */ +-static inline RailTileType GetRailTileType(TileIndex t) ++template <bool Tgeneric> ++static inline RailTileType GetRailTileType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_RAILWAY)); +- return (RailTileType)GB(_m[t].m5, 6, 2); ++ return (RailTileType)GB(GetTile(t)->m5, 6, 2); + } ++/** @copydoc GetRailTileType(TileIndexT<Tgeneric>::T) */ ++static inline RailTileType GetRailTileType(TileIndex t) { return GetRailTileType<false>(t); } ++/** @copydoc GetRailTileType(TileIndexT<Tgeneric>::T) */ ++static inline RailTileType GetRailTileType(GenericTileIndex t) { return GetRailTileType<true>(t); } + + /** + * Returns whether this is plain rails, with or without signals. Iow, if this +@@ -47,21 +52,31 @@ static inline RailTileType GetRailTileType(TileIndex t) + * @pre IsTileType(t, MP_RAILWAY) + * @return true if and only if the tile is normal rail (with or without signals) + */ +-static inline bool IsPlainRail(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsPlainRail(typename TileIndexT<Tgeneric>::T t) + { + RailTileType rtt = GetRailTileType(t); + return rtt == RAIL_TILE_NORMAL || rtt == RAIL_TILE_SIGNALS; + } ++/** @copydoc IsPlainRail(TileIndexT<Tgeneric>::T) */ ++static inline bool IsPlainRail(TileIndex t) { return IsPlainRail<false>(t); } ++/** @copydoc IsPlainRail(TileIndexT<Tgeneric>::T) */ ++static inline bool IsPlainRail(GenericTileIndex t) { return IsPlainRail<true>(t); } + + /** + * Checks whether the tile is a rail tile or rail tile with signals. + * @param t the tile to get the information from + * @return true if and only if the tile is normal rail (with or without signals) + */ +-static inline bool IsPlainRailTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsPlainRailTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_RAILWAY) && IsPlainRail(t); + } ++/** @copydoc IsPlainRailTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsPlainRailTile(TileIndex t) { return IsPlainRailTile<false>(t); } ++/** @copydoc IsPlainRailTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsPlainRailTile(GenericTileIndex t) { return IsPlainRailTile<true>(t); } + + + /** +@@ -81,11 +96,16 @@ static inline bool HasSignals(TileIndex t) + * @param signals whether the rail tile should have signals or not + * @pre IsPlainRailTile(tile) + */ +-static inline void SetHasSignals(TileIndex tile, bool signals) ++template <bool Tgeneric> ++static inline void SetHasSignals(typename TileIndexT<Tgeneric>::T tile, bool signals) + { + assert(IsPlainRailTile(tile)); +- SB(_m[tile].m5, 6, 1, signals); ++ SB(GetTile(tile)->m5, 6, 1, signals); + } ++/** @copydoc SetHasSignals(TileIndexT<Tgeneric>::T,bool) */ ++static inline void SetHasSignals(TileIndex tile, bool signals) { SetHasSignals<false>(tile, signals); } ++/** @copydoc SetHasSignals(TileIndexT<Tgeneric>::T,bool) */ ++static inline void SetHasSignals(GenericTileIndex tile, bool signals) { SetHasSignals<true>(tile, signals); } + + /** + * Is this rail tile a rail depot? +@@ -93,40 +113,60 @@ static inline void SetHasSignals(TileIndex tile, bool signals) + * @pre IsTileType(t, MP_RAILWAY) + * @return true if and only if the tile is a rail depot + */ +-static inline bool IsRailDepot(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailDepot(typename TileIndexT<Tgeneric>::T t) + { + return GetRailTileType(t) == RAIL_TILE_DEPOT; + } ++/** @copydoc IsRailDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailDepot(TileIndex t) { return IsRailDepot<false>(t); } ++/** @copydoc IsRailDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailDepot(GenericTileIndex t) { return IsRailDepot<true>(t); } + + /** + * Is this tile rail tile and a rail depot? + * @param t the tile to get the information from + * @return true if and only if the tile is a rail depot + */ +-static inline bool IsRailDepotTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailDepotTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_RAILWAY) && IsRailDepot(t); + } ++/** @copydoc IsRailDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailDepotTile(TileIndex t) { return IsRailDepotTile<false>(t); } ++/** @copydoc IsRailDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailDepotTile(GenericTileIndex t) { return IsRailDepotTile<true>(t); } + + /** + * Gets the rail type of the given tile + * @param t the tile to get the rail type from + * @return the rail type of the tile + */ +-static inline RailType GetRailType(TileIndex t) ++template <bool Tgeneric> ++static inline RailType GetRailType(typename TileIndexT<Tgeneric>::T t) + { +- return (RailType)GB(_m[t].m3, 0, 4); ++ return (RailType)GB(GetTile(t)->m3, 0, 4); + } ++/** @copydoc GetRailType(TileIndexT<Tgeneric>::T) */ ++static inline RailType GetRailType(TileIndex t) { return GetRailType<false>(t); } ++/** @copydoc GetRailType(TileIndexT<Tgeneric>::T) */ ++static inline RailType GetRailType(GenericTileIndex t) { return GetRailType<true>(t); } + + /** + * Sets the rail type of the given tile + * @param t the tile to set the rail type of + * @param r the new rail type for the tile + */ +-static inline void SetRailType(TileIndex t, RailType r) ++template <bool Tgeneric> ++static inline void SetRailType(typename TileIndexT<Tgeneric>::T t, RailType r) + { +- SB(_m[t].m3, 0, 4, r); ++ SB(GetTile(t)->m3, 0, 4, r); + } ++/** @copydoc SetRailType(TileIndexT<Tgeneric>::T,RailType) */ ++static inline void SetRailType(TileIndex t, RailType r) { SetRailType<false>(t, r); } ++/** @copydoc SetRailType(TileIndexT<Tgeneric>::T,RailType) */ ++static inline void SetRailType(GenericTileIndex t, RailType r) { SetRailType<true>(t, r); } + + + /** +@@ -134,22 +174,32 @@ static inline void SetRailType(TileIndex t, RailType r) + * @param tile the tile to get the track bits from + * @return the track bits of the tile + */ +-static inline TrackBits GetTrackBits(TileIndex tile) ++template <bool Tgeneric> ++static inline TrackBits GetTrackBits(typename TileIndexT<Tgeneric>::T tile) + { + assert(IsPlainRailTile(tile)); +- return (TrackBits)GB(_m[tile].m5, 0, 6); ++ return (TrackBits)GB(GetTile(tile)->m5, 0, 6); + } ++/** @copydoc GetTrackBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetTrackBits(TileIndex tile) { return GetTrackBits<false>(tile); } ++/** @copydoc GetTrackBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetTrackBits(GenericTileIndex tile) { return GetTrackBits<true>(tile); } + + /** + * Sets the track bits of the given tile + * @param t the tile to set the track bits of + * @param b the new track bits for the tile + */ +-static inline void SetTrackBits(TileIndex t, TrackBits b) ++template <bool Tgeneric> ++static inline void SetTrackBits(typename TileIndexT<Tgeneric>::T t, TrackBits b) + { + assert(IsPlainRailTile(t)); +- SB(_m[t].m5, 0, 6, b); ++ SB(GetTile(t)->m5, 0, 6, b); + } ++/** @copydoc SetTrackBits(TileIndexT<Tgeneric>::T,TrackBits) */ ++static inline void SetTrackBits(TileIndex t, TrackBits b) { SetTrackBits<false>(t, b); } ++/** @copydoc SetTrackBits(TileIndexT<Tgeneric>::T,TrackBits) */ ++static inline void SetTrackBits(GenericTileIndex t, TrackBits b) { SetTrackBits<true>(t, b); } + + /** + * Returns whether the given track is present on the given tile. +@@ -158,10 +208,15 @@ static inline void SetTrackBits(TileIndex t, TrackBits b) + * @pre IsPlainRailTile(tile) + * @return true if and only if the given track exists on the tile + */ +-static inline bool HasTrack(TileIndex tile, Track track) ++template <bool Tgeneric> ++static inline bool HasTrack(typename TileIndexT<Tgeneric>::T tile, Track track) + { + return HasBit(GetTrackBits(tile), track); + } ++/** @copydoc HasTrack(TileIndexT<Tgeneric>::T,Track) */ ++static inline bool HasTrack(TileIndex tile, Track track) { return HasTrack<false>(tile, track); } ++/** @copydoc HasTrack(TileIndexT<Tgeneric>::T,Track) */ ++static inline bool HasTrack(GenericTileIndex tile, Track track) { return HasTrack<true>(tile, track); } + + /** + * Returns the direction the depot is facing to +@@ -169,10 +224,15 @@ static inline bool HasTrack(TileIndex tile, Track track) + * @pre IsRailDepotTile(t) + * @return the direction the depot is facing + */ +-static inline DiagDirection GetRailDepotDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetRailDepotDirection(typename TileIndexT<Tgeneric>::T t) + { +- return (DiagDirection)GB(_m[t].m5, 0, 2); ++ return (DiagDirection)GB(GetTile(t)->m5, 0, 2); + } ++/** @copydoc GetRailDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRailDepotDirection(TileIndex t) { return GetRailDepotDirection<false>(t); } ++/** @copydoc GetRailDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRailDepotDirection(GenericTileIndex t) { return GetRailDepotDirection<true>(t); } + + /** + * Returns the track of a depot, ignoring direction +@@ -180,10 +240,15 @@ static inline DiagDirection GetRailDepotDirection(TileIndex t) + * @param t the tile to get the depot track from + * @return the track of the depot + */ +-static inline Track GetRailDepotTrack(TileIndex t) ++template <bool Tgeneric> ++static inline Track GetRailDepotTrack(typename TileIndexT<Tgeneric>::T t) + { + return DiagDirToDiagTrack(GetRailDepotDirection(t)); + } ++/** @copydoc GetRailDepotTrack(TileIndexT<Tgeneric>::T) */ ++static inline Track GetRailDepotTrack(TileIndex t) { return GetRailDepotTrack<false>(t); } ++/** @copydoc GetRailDepotTrack(TileIndexT<Tgeneric>::T) */ ++static inline Track GetRailDepotTrack(GenericTileIndex t) { return GetRailDepotTrack<true>(t); } + + + /** +@@ -195,10 +260,10 @@ static inline Track GetRailDepotTrack(TileIndex t) + static inline TrackBits GetRailReservationTrackBits(TileIndex t) + { + assert(IsPlainRailTile(t)); +- byte track_b = GB(_m[t].m2, 8, 3); ++ byte track_b = GB(GetTile(t)->m2, 8, 3); + Track track = (Track)(track_b - 1); // map array saves Track+1 + if (track_b == 0) return TRACK_BIT_NONE; +- return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); ++ return (TrackBits)(TrackToTrackBits(track) | (HasBit(GetTile(t)->m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); + } + + /** +@@ -213,8 +278,8 @@ static inline void SetTrackReservation(TileIndex t, TrackBits b) + assert(b != INVALID_TRACK_BIT); + assert(!TracksOverlap(b)); + Track track = RemoveFirstTrack(&b); +- SB(_m[t].m2, 8, 3, track == INVALID_TRACK ? 0 : track + 1); +- SB(_m[t].m2, 11, 1, (byte)(b != TRACK_BIT_NONE)); ++ SB(GetTile(t)->m2, 8, 3, track == INVALID_TRACK ? 0 : track + 1); ++ SB(GetTile(t)->m2, 11, 1, (byte)(b != TRACK_BIT_NONE)); + } + + /** +@@ -259,7 +324,7 @@ static inline void UnreserveTrack(TileIndex tile, Track t) + static inline bool HasDepotReservation(TileIndex t) + { + assert(IsRailDepot(t)); +- return HasBit(_m[t].m5, 4); ++ return HasBit(GetTile(t)->m5, 4); + } + + /** +@@ -271,7 +336,7 @@ static inline bool HasDepotReservation(TileIndex t) + static inline void SetDepotReservation(TileIndex t, bool b) + { + assert(IsRailDepot(t)); +- SB(_m[t].m5, 4, 1, (byte)b); ++ SB(GetTile(t)->m5, 4, 1, (byte)b); + } + + /** +@@ -291,20 +356,30 @@ static inline bool IsPbsSignal(SignalType s) + return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY; + } + +-static inline SignalType GetSignalType(TileIndex t, Track track) ++template <bool Tgeneric> ++static inline SignalType GetSignalType(typename TileIndexT<Tgeneric>::T t, Track track) + { + assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); + byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; +- return (SignalType)GB(_m[t].m2, pos, 3); ++ return (SignalType)GB(GetTile(t)->m2, pos, 3); + } ++/** @copydoc GetSignalType(TileIndexT<Tgeneric>::T,Track) */ ++static inline SignalType GetSignalType(TileIndex t, Track track) { return GetSignalType<false>(t, track); } ++/** @copydoc GetSignalType(TileIndexT<Tgeneric>::T,Track) */ ++static inline SignalType GetSignalType(GenericTileIndex t, Track track) { return GetSignalType<true>(t, track); } + +-static inline void SetSignalType(TileIndex t, Track track, SignalType s) ++template <bool Tgeneric> ++static inline void SetSignalType(typename TileIndexT<Tgeneric>::T t, Track track, SignalType s) + { + assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); + byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; +- SB(_m[t].m2, pos, 3, s); +- if (track == INVALID_TRACK) SB(_m[t].m2, 4, 3, s); ++ SB(GetTile(t)->m2, pos, 3, s); ++ if (track == INVALID_TRACK) SB(GetTile(t)->m2, 4, 3, s); + } ++/** @copydoc SetSignalType(TileIndexT<Tgeneric>::T,Track,SignalType) */ ++static inline void SetSignalType(TileIndex t, Track track, SignalType s) { SetSignalType<false>(t, track, s); } ++/** @copydoc SetSignalType(TileIndexT<Tgeneric>::T,Track,SignalType) */ ++static inline void SetSignalType(GenericTileIndex t, Track track, SignalType s) { SetSignalType<true>(t, track, s); } + + static inline bool IsPresignalEntry(TileIndex t, Track track) + { +@@ -322,28 +397,43 @@ static inline bool IsOnewaySignal(TileIndex t, Track track) + return GetSignalType(t, track) != SIGTYPE_PBS; + } + +-static inline void CycleSignalSide(TileIndex t, Track track) ++template <bool Tgeneric> ++static inline void CycleSignalSide(typename TileIndexT<Tgeneric>::T t, Track track) + { + byte sig; + byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 6; + +- sig = GB(_m[t].m3, pos, 2); ++ sig = GB(GetTile(t)->m3, pos, 2); + if (--sig == 0) sig = IsPbsSignal(GetSignalType(t, track)) ? 2 : 3; +- SB(_m[t].m3, pos, 2, sig); ++ SB(GetTile(t)->m3, pos, 2, sig); + } ++/** @copydoc CycleSignalSide(TileIndexT<Tgeneric>::T,Track) */ ++static inline void CycleSignalSide(TileIndex t, Track track) { CycleSignalSide<false>(t, track); } ++/** @copydoc CycleSignalSide(TileIndexT<Tgeneric>::T,Track) */ ++static inline void CycleSignalSide(GenericTileIndex t, Track track) { CycleSignalSide<true>(t, track); } + +-static inline SignalVariant GetSignalVariant(TileIndex t, Track track) ++template <bool Tgeneric> ++static inline SignalVariant GetSignalVariant(typename TileIndexT<Tgeneric>::T t, Track track) + { + byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; +- return (SignalVariant)GB(_m[t].m2, pos, 1); ++ return (SignalVariant)GB(GetTile(t)->m2, pos, 1); + } ++/** @copydoc GetSignalVariant(TileIndexT<Tgeneric>::T,Track) */ ++static inline SignalVariant GetSignalVariant(TileIndex t, Track track) { return GetSignalVariant<false>(t, track); } ++/** @copydoc GetSignalVariant(TileIndexT<Tgeneric>::T,Track) */ ++static inline SignalVariant GetSignalVariant(GenericTileIndex t, Track track) { return GetSignalVariant<true>(t, track); } + +-static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) ++template <bool Tgeneric> ++static inline void SetSignalVariant(typename TileIndexT<Tgeneric>::T t, Track track, SignalVariant v) + { + byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; +- SB(_m[t].m2, pos, 1, v); +- if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v); ++ SB(GetTile(t)->m2, pos, 1, v); ++ if (track == INVALID_TRACK) SB(GetTile(t)->m2, 7, 1, v); + } ++/** @copydoc SetSignalVariant(TileIndexT<Tgeneric>::T,Track,SignalVariant) */ ++static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) { SetSignalVariant<false>(t, track, v); } ++/** @copydoc SetSignalVariant(TileIndexT<Tgeneric>::T,Track,SignalVariant) */ ++static inline void SetSignalVariant(GenericTileIndex t, Track track, SignalVariant v) { SetSignalVariant<true>(t, track, v); } + + /** + * Set the states of the signals (Along/AgainstTrackDir) +@@ -352,7 +442,7 @@ static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) + */ + static inline void SetSignalStates(TileIndex tile, uint state) + { +- SB(_m[tile].m4, 4, 4, state); ++ SB(GetTile(tile)->m4, 4, 4, state); + } + + /** +@@ -362,7 +452,7 @@ static inline void SetSignalStates(TileIndex tile, uint state) + */ + static inline uint GetSignalStates(TileIndex tile) + { +- return GB(_m[tile].m4, 4, 4); ++ return GB(GetTile(tile)->m4, 4, 4); + } + + /** +@@ -381,20 +471,30 @@ static inline SignalState GetSingleSignalState(TileIndex t, byte signalbit) + * @param tile the tile to set the present signals for + * @param signals the signals that have to be present + */ +-static inline void SetPresentSignals(TileIndex tile, uint signals) ++template <bool Tgeneric> ++static inline void SetPresentSignals(typename TileIndexT<Tgeneric>::T tile, uint signals) + { +- SB(_m[tile].m3, 4, 4, signals); ++ SB(GetTile(tile)->m3, 4, 4, signals); + } ++/** @copydoc SetPresentSignals(TileIndexT<Tgeneric>::T,uint) */ ++static inline void SetPresentSignals(TileIndex tile, uint signals) { SetPresentSignals<false>(tile, signals); } ++/** @copydoc SetPresentSignals(TileIndexT<Tgeneric>::T,uint) */ ++static inline void SetPresentSignals(GenericTileIndex tile, uint signals) { SetPresentSignals<true>(tile, signals); } + + /** + * Get whether the given signals are present (Along/AgainstTrackDir) + * @param tile the tile to get the present signals for + * @return the signals that are present + */ +-static inline uint GetPresentSignals(TileIndex tile) ++template <bool Tgeneric> ++static inline uint GetPresentSignals(typename TileIndexT<Tgeneric>::T tile) + { +- return GB(_m[tile].m3, 4, 4); ++ return GB(GetTile(tile)->m3, 4, 4); + } ++/** @copydoc GetPresentSignals(TileIndexT<Tgeneric>::T) */ ++static inline uint GetPresentSignals(TileIndex tile) { return GetPresentSignals<false>(tile); } ++/** @copydoc GetPresentSignals(TileIndexT<Tgeneric>::T) */ ++static inline uint GetPresentSignals(GenericTileIndex tile) { return GetPresentSignals<true>(tile); } + + /** + * Checks whether the given signals is present +@@ -411,11 +511,16 @@ static inline bool IsSignalPresent(TileIndex t, byte signalbit) + * Checks for the presence of signals (either way) on the given track on the + * given rail tile. + */ +-static inline bool HasSignalOnTrack(TileIndex tile, Track track) ++template <bool Tgeneric> ++static inline bool HasSignalOnTrack(typename TileIndexT<Tgeneric>::T tile, Track track) + { + assert(IsValidTrack(track)); + return GetRailTileType(tile) == RAIL_TILE_SIGNALS && (GetPresentSignals(tile) & SignalOnTrack(track)) != 0; + } ++/** @copydoc HasSignalOnTrack(TileIndexT<Tgeneric>::T,Track) */ ++static inline bool HasSignalOnTrack(TileIndex tile, Track track) { return HasSignalOnTrack<false>(tile, track); } ++/** @copydoc HasSignalOnTrack(TileIndexT<Tgeneric>::T,Track) */ ++static inline bool HasSignalOnTrack(GenericTileIndex tile, Track track) { return HasSignalOnTrack<true>(tile, track); } + + /** + * Checks for the presence of signals along the given trackdir on the given +@@ -424,11 +529,16 @@ static inline bool HasSignalOnTrack(TileIndex tile, Track track) + * Along meaning if you are currently driving on the given trackdir, this is + * the signal that is facing us (for which we stop when it's red). + */ +-static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) ++template <bool Tgeneric> ++static inline bool HasSignalOnTrackdir(typename TileIndexT<Tgeneric>::T tile, Trackdir trackdir) + { +- assert (IsValidTrackdir(trackdir)); ++ assert(IsValidTrackdir(trackdir)); + return GetRailTileType(tile) == RAIL_TILE_SIGNALS && GetPresentSignals(tile) & SignalAlongTrackdir(trackdir); + } ++/** @copydoc HasSignalOnTrackdir(TileIndexT<Tgeneric>::T,Trackdir) */ ++static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) { return HasSignalOnTrackdir<false>(tile, trackdir); } ++/** @copydoc HasSignalOnTrackdir(TileIndexT<Tgeneric>::T,Trackdir) */ ++static inline bool HasSignalOnTrackdir(GenericTileIndex tile, Trackdir trackdir) { return HasSignalOnTrackdir<true>(tile, trackdir); } + + /** + * Gets the state of the signal along the given trackdir. +@@ -480,7 +590,12 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) + } + + +-RailType GetTileRailType(TileIndex tile); ++template <bool Tgeneric> ++RailType GetTileRailType(typename TileIndexT<Tgeneric>::T tile); ++/** @copydoc GetTileRailType(TileIndexT<Tgeneric>::T) */ ++static inline RailType GetTileRailType(TileIndex tile) { return GetTileRailType<false>(tile); } ++/** @copydoc GetTileRailType(TileIndexT<Tgeneric>::T) */ ++static inline RailType GetTileRailType(GenericTileIndex tile) { return GetTileRailType<true>(tile); } + + /** The ground 'under' the rail */ + enum RailGroundType { +@@ -503,12 +618,12 @@ enum RailGroundType { + + static inline void SetRailGroundType(TileIndex t, RailGroundType rgt) + { +- SB(_m[t].m4, 0, 4, rgt); ++ SB(GetTile(t)->m4, 0, 4, rgt); + } + + static inline RailGroundType GetRailGroundType(TileIndex t) + { +- return (RailGroundType)GB(_m[t].m4, 0, 4); ++ return (RailGroundType)GB(GetTile(t)->m4, 0, 4); + } + + static inline bool IsSnowRailGround(TileIndex t) +@@ -517,29 +632,38 @@ static inline bool IsSnowRailGround(TileIndex t) + } + + +-static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) ++template <bool Tgeneric> ++static inline void MakeRailNormal(typename TileIndexT<Tgeneric>::T t, Owner o, TrackBits b, RailType r) + { + SetTileType(t, MP_RAILWAY); + SetTileOwner(t, o); +- _m[t].m2 = 0; +- _m[t].m3 = r; +- _m[t].m4 = 0; +- _m[t].m5 = RAIL_TILE_NORMAL << 6 | b; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = r; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = RAIL_TILE_NORMAL << 6 | b; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeRailNormal(TileIndexT<Tgeneric>::T,Owner,TrackBits,RailType) */ ++static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) { MakeRailNormal<false>(t, o, b, r); } ++/** @copydoc MakeRailNormal(TileIndexT<Tgeneric>::T,Owner,TrackBits,RailType) */ ++static inline void MakeRailNormal(GenericTileIndex t, Owner o, TrackBits b, RailType r) { MakeRailNormal<true>(t, o, b, r); } + +- +-static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) ++template <bool Tgeneric> ++inline void MakeRailDepot(typename TileIndexT<Tgeneric>::T t, Owner o, DepotID did, DiagDirection d, RailType r) + { + SetTileType(t, MP_RAILWAY); + SetTileOwner(t, o); +- _m[t].m2 = did; +- _m[t].m3 = r; +- _m[t].m4 = 0; +- _m[t].m5 = RAIL_TILE_DEPOT << 6 | d; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; +-} ++ GetTile(t)->m2 = did; ++ GetTile(t)->m3 = r; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = RAIL_TILE_DEPOT << 6 | d; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; ++} ++/** @copydoc MakeRailDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DiagDirection,RailType) */ ++static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) { MakeRailDepot<false>(t, o, did, d, r); } ++/** @copydoc MakeRailDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DiagDirection,RailType) */ ++static inline void MakeRailDepot(GenericTileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) { MakeRailDepot<true>(t, o, did, d, r); } + + #endif /* RAIL_MAP_H */ +diff --git a/src/rev.cpp.in b/src/rev.cpp.in +index 899a565..1d9f572 100644 +--- a/src/rev.cpp.in ++++ b/src/rev.cpp.in +@@ -39,7 +39,7 @@ bool IsReleasedVersion() + * norev000 is for non-releases that are made on systems without + * subversion or sources that are not a checkout of subversion. + */ +-const char _openttd_revision[] = "!!VERSION!!"; ++const char _openttd_revision[] = "!!VERSION!!-CLIPBOARD"; + + /** + * The text version of OpenTTD's build date. +diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp +index aa445eb..4923338 100644 +--- a/src/road_cmd.cpp ++++ b/src/road_cmd.cpp +@@ -11,6 +11,7 @@ + + #include "stdafx.h" + #include "cmd_helper.h" ++#include "copypaste_cmd.h" + #include "road_internal.h" + #include "viewport_func.h" + #include "command_func.h" +@@ -35,6 +36,7 @@ + #include "date_func.h" + #include "genworld.h" + #include "company_gui.h" ++#include "clipboard_gui.h" + + #include "table/strings.h" + +@@ -1328,11 +1330,14 @@ static void DrawTile_Road(TileInfo *ti) + switch (GetRoadTileType(ti->tile)) { + case ROAD_TILE_NORMAL: + DrawRoadBits(ti); ++ DrawOverlay(ti, MP_ROAD); + break; + + case ROAD_TILE_CROSSING: { + if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); + ++ DrawOverlay(ti, MP_ROAD); ++ + PaletteID pal = PAL_NONE; + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); + +@@ -1408,6 +1413,7 @@ static void DrawTile_Road(TileInfo *ti) + } + + DrawGroundSprite(dts->ground.sprite, PAL_NONE); ++ DrawOverlay(ti, MP_ROAD); + DrawOrigTileSeq(ti, dts, TO_BUILDINGS, palette); + break; + } +@@ -1853,6 +1859,176 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + ++static void CopyPastePlaceRoad(GenericTileIndex tile, RoadBits roadbits_road, DisallowedRoadDirections drd, RoadBits roadbits_tram) ++{ ++ if (IsMainMapTile(tile)) { ++ /* road */ ++ if (roadbits_road != ROAD_NONE) { ++ if (IsNormalRoadTile(tile) && HasTileRoadType(tile, ROADTYPE_ROAD) && ++ IsRoadOwner(tile, ROADTYPE_ROAD, _current_company) && ++ (GetRoadBits(tile, ROADTYPE_ROAD) == roadbits_road)) { ++ drd &= ~GetDisallowedRoadDirections(tile); ++ if (drd == DRD_NONE) return; ++ } ++ _current_pasting->DoCommand(AsMainMapTile(tile), roadbits_road | (ROADTYPE_ROAD << 4) | (drd << 6), 0, CMD_BUILD_ROAD | CMD_MSG(STR_ERROR_CAN_T_BUILD_ROAD_HERE)); ++ } ++ /* tram */ ++ if (roadbits_tram != ROAD_NONE) { ++ _current_pasting->DoCommand(AsMainMapTile(tile), roadbits_tram | (ROADTYPE_TRAM << 4), 0, CMD_BUILD_ROAD | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE)); ++ } ++ } else { ++ RoadTypes rt = ROADTYPES_NONE; ++ if (roadbits_road != ROAD_NONE) rt |= ROADTYPES_ROAD; ++ if (roadbits_tram != ROAD_NONE) rt |= ROADTYPES_TRAM; ++ assert(rt != ROADTYPES_NONE); ++ ++ MakeRoadNormal(tile, ROAD_NONE, rt, 0, OWNER_NONE, OWNER_NONE); ++ SetRoadBits(tile, roadbits_road, ROADTYPE_ROAD); ++ SetDisallowedRoadDirections(tile, drd); ++ SetRoadBits(tile, roadbits_tram, ROADTYPE_TRAM); ++ } ++} ++ ++static void CopyPastePlaceRoadCrossing(GenericTileIndex tile, RoadTypes road_types, Axis road_axis, RailType railtype) ++{ ++ if (IsMainMapTile(tile)) { ++ CopyPastePlaceRoad(tile, HasBit(road_types, ROADTYPE_ROAD) ? AxisToRoadBits(road_axis) : ROAD_NONE, ++ DRD_NONE, HasBit(road_types, ROADTYPE_TRAM) ? AxisToRoadBits(road_axis) : ROAD_NONE); ++ CopyPastePlaceTracks(tile, railtype, AxisToTrackBits(OtherAxis(road_axis))); ++ } else { ++ MakeRoadCrossing(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, road_axis, railtype, road_types, 0); ++ } ++} ++ ++static void CopyPastePlaceRoadDepot(GenericTileIndex tile, RoadType rt, DiagDirection dir) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ if (IsRoadDepotTile(t) && IsTileOwner(t, _current_company) && ++ GetRoadDepotDirection(t) == dir && GetRoadTypes(t) == RoadTypeToRoadTypes(rt)) { ++ _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_ROAD_DEPOT); ++ } else { ++ _current_pasting->DoCommand(t, dir | (rt << 2), 0, CMD_BUILD_ROAD_DEPOT | CMD_MSG(rt + STR_ERROR_CAN_T_BUILD_ROAD_DEPOT)); ++ } ++ } else { ++ MakeRoadDepot(tile, OWNER_NONE, 0, dir, rt); ++ } ++} ++ ++static bool IsRoadCopyable(GenericTileIndex tile, RoadType rt, CompanyID company = _current_company) ++{ ++ return HasTileRoadType(tile, rt) && (!IsMainMapTile(tile) || IsRoadOwner(tile, rt, company)); ++} ++ ++/** ++ * Test a given road tile if there is any contented to be copied from it. ++ * @param tile the tile to test ++ * @param mode copy-paste mode ++ * @param company the #Company to check ownership against to ++ * @param preview (out, may be NULL) information on how to higlight preview of the tile ++ * @return whether this tile needs to be copy-pasted ++ */ ++bool TestRoadTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) ++{ ++ if (preview != NULL) MemSetT(preview, 0); ++ ++ if (!(mode & (CPM_WITH_ROAD_TRANSPORT | CPM_WITH_RAIL_TRANSPORT))) return false; ++ ++ switch (GetRoadTileType(tile)) { ++ case ROAD_TILE_NORMAL: ++ if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; ++ if (!IsRoadCopyable(tile, ROADTYPE_ROAD, company) && !IsRoadCopyable(tile, ROADTYPE_TRAM, company)) return false; ++ if (preview != NULL) preview->highlight_tile_rect = true; ++ break; ++ ++ case ROAD_TILE_CROSSING: { ++ bool road_ok = (mode & CPM_WITH_ROAD_TRANSPORT) && (IsRoadCopyable(tile, ROADTYPE_ROAD, company) || IsRoadCopyable(tile, ROADTYPE_TRAM, company)); ++ bool rail_ok = (mode & CPM_WITH_RAIL_TRANSPORT) && (!IsMainMapTile(tile) || IsTileOwner(tile, company)); ++ if (!road_ok && !rail_ok) return false; ++ if (preview != NULL) { ++ if (road_ok) preview->highlight_tile_rect = true; ++ if (rail_ok) preview->highlight_track_bits = GetCrossingRailBits(tile); ++ } ++ break; ++ } ++ ++ case ROAD_TILE_DEPOT: ++ if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; ++ if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; ++ if (preview != NULL) preview->highlight_tile_rect = true; ++ break; ++ } ++ ++ return true; ++} ++ ++void CopyPasteTile_Road(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) ++{ ++ if (!TestRoadTileCopyability(src_tile, copy_paste.mode)) return; ++ ++ /* Terraform tile if needed */ ++ if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { ++ CopyPasteHeights(GenericTileArea(src_tile, 1, 1), dst_tile, copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ } ++ ++ switch (GetRoadTileType(src_tile)) { ++ case ROAD_TILE_NORMAL: { ++ RoadBits roadbits_road = IsRoadCopyable(src_tile, ROADTYPE_ROAD) ? ++ TransformRoadBits(GetRoadBits(src_tile, ROADTYPE_ROAD), copy_paste.transformation) : ROAD_NONE; ++ RoadBits roadbits_tram = IsRoadCopyable(src_tile, ROADTYPE_TRAM) ? ++ TransformRoadBits(GetRoadBits(src_tile, ROADTYPE_TRAM), copy_paste.transformation) : ROAD_NONE; ++ ++ /* transform DisallowedRoadDirections */ ++ DisallowedRoadDirections drd = GetDisallowedRoadDirections(src_tile); ++ if (copy_paste.transformation != DTR_IDENTITY && (drd == DRD_SOUTHBOUND || drd == DRD_NORTHBOUND)) { ++ /* investigate current direction */ ++ DiagDirection dir = (GetRoadBits(src_tile, ROADTYPE_ROAD) & ROAD_X) ? DIAGDIR_SW : DIAGDIR_SE; ++ if (drd == DRD_NORTHBOUND) dir = ReverseDiagDir(dir); ++ /* transform */ ++ dir = TransformDiagDir(dir, copy_paste.transformation); ++ /* convert result to DisallowedRoadDirections */ ++ drd = (dir == DIAGDIR_SW || dir == DIAGDIR_SE) ? DRD_SOUTHBOUND : DRD_NORTHBOUND; ++ } ++ ++ /* paste roads */ ++ CopyPastePlaceRoad(dst_tile, roadbits_road, drd, roadbits_tram); ++ break; ++ } ++ ++ case ROAD_TILE_CROSSING: { ++ Axis road_axis = TransformAxis(GetCrossingRoadAxis(src_tile), copy_paste.transformation); ++ RoadTypes road_types = ROADTYPES_NONE; ++ if (copy_paste.mode & CPM_WITH_ROAD_TRANSPORT) { ++ if (IsRoadCopyable(src_tile, ROADTYPE_ROAD)) SetBit(road_types, ROADTYPE_ROAD); ++ if (IsRoadCopyable(src_tile, ROADTYPE_TRAM)) SetBit(road_types, ROADTYPE_TRAM); ++ } ++ ++ if ((copy_paste.mode & CPM_WITH_RAIL_TRANSPORT) && (!IsMainMapTile(src_tile) || IsTileOwner(src_tile, _current_company))) { ++ RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); ++ if (road_types != ROADTYPES_NONE) { ++ CopyPastePlaceRoadCrossing(dst_tile, road_types, road_axis, railtype); ++ } else { ++ CopyPastePlaceTracks(dst_tile, railtype, AxisToTrackBits(OtherAxis(road_axis))); ++ } ++ } else if (road_types != ROADTYPES_NONE) { ++ CopyPastePlaceRoad(dst_tile, HasBit(road_types, ROADTYPE_ROAD) ? AxisToRoadBits(road_axis) : ROAD_NONE, ++ DRD_NONE, HasBit(road_types, ROADTYPE_TRAM) ? AxisToRoadBits(road_axis) : ROAD_NONE); ++ } ++ break; ++ } ++ ++ case ROAD_TILE_DEPOT: ++ /* paste depot */ ++ CopyPastePlaceRoadDepot(dst_tile, (RoadType)FIND_FIRST_BIT(GetRoadTypes(src_tile)), ++ TransformDiagDir(GetRoadDepotDirection(src_tile), copy_paste.transformation)); ++ break; ++ ++ default: ++ NOT_REACHED(); // corrupted tile data? ++ } ++} ++ + /** Tile callback functions for road tiles */ + extern const TileTypeProcs _tile_type_road_procs = { + DrawTile_Road, // draw_tile_proc +@@ -1869,4 +2045,5 @@ extern const TileTypeProcs _tile_type_road_procs = { + VehicleEnter_Road, // vehicle_enter_tile_proc + GetFoundation_Road, // get_foundation_proc + TerraformTile_Road, // terraform_tile_proc ++ CopyPasteTile_Road, // copypaste_tile_proc + }; +diff --git a/src/road_func.h b/src/road_func.h +index c4af229..c6ea68c 100644 +--- a/src/road_func.h ++++ b/src/road_func.h +@@ -14,6 +14,7 @@ + + #include "core/bitmath_func.hpp" + #include "road_type.h" ++#include "direction_func.h" + #include "economy_func.h" + + /** +@@ -114,10 +115,37 @@ static inline RoadBits MirrorRoadBits(RoadBits r) + static inline RoadBits RotateRoadBits(RoadBits r, DiagDirDiff rot) + { + assert(IsValidRoadBits(r)); +- for (; rot > (DiagDirDiff)0; rot--) { +- r = (RoadBits)(GB(r, 0, 1) << 3 | GB(r, 1, 3)); ++ rot = (DiagDirDiff)((uint)rot % DIAGDIR_END); ++ return (RoadBits)((r | (r << DIAGDIR_END)) >> rot) & ROAD_ALL; ++} ++ ++/** ++ * Transform RoadBits by given transformation. ++ * @param road_bits The RoadBits to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed RoadBits. ++ */ ++static inline RoadBits TransformRoadBits(RoadBits road_bits, DirTransformation transformation) ++{ ++ /* reflect agains X axis before rotating */ ++ if (transformation & DTR_REFLECTION_BIT) { ++ /* firstly reflect against W-E axis by swapping odd and even bits (the numbers are bit positions) ++ * ++ * [ROAD_NW] [ROAD_NE] 0 3 1 2 /N\ ++ * ------------------- ----- --reflect-against-W-E--> ----- W-+-E ++ * [ROAD_SW] [ROAD_SE] 1 2 0 3 \S/ ++ * ++ * bit 0 (ROAD_NW) swaps with bit 1 (ROAD_SW) ++ * bit 2 (ROAD_SE) swaps with bit 3 (ROAD_NE) */ ++ road_bits = SwapOddEvenBits(road_bits); ++ /* Now we have reflection agains W-E axis. To get reflection agains X axis we must rotate the ++ * result left by 90 degree. To do that we can simply add 3 to the number of 90-degree ++ * right rotations that we will be doing in the next step. We can safely overflow. */ ++ transformation = (DirTransformation)(transformation + 3); + } +- return r; ++ ++ /* rotate */ ++ return RotateRoadBits(road_bits, (DiagDirDiff)transformation); + } + + /** +diff --git a/src/road_gui.cpp b/src/road_gui.cpp +index 92c660e..cb52651 100644 +--- a/src/road_gui.cpp ++++ b/src/road_gui.cpp +@@ -633,11 +633,11 @@ struct BuildRoadToolbarWindow : Window { + break; + + case DDSP_BUILD_BUSSTOP: +- PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); ++ PlaceRoadStop(start_tile, end_tile, ((_settings_game.station.adjacent_stations && _ctrl_pressed) << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); + break; + + case DDSP_BUILD_TRUCKSTOP: +- PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); ++ PlaceRoadStop(start_tile, end_tile, ((_settings_game.station.adjacent_stations && _ctrl_pressed) << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); + break; + + case DDSP_REMOVE_BUSSTOP: { +diff --git a/src/road_map.h b/src/road_map.h +index 6937302..d1bfaec 100644 +--- a/src/road_map.h ++++ b/src/road_map.h +@@ -32,11 +32,16 @@ enum RoadTileType { + * @pre IsTileType(t, MP_ROAD) + * @return The road tile type. + */ +-static inline RoadTileType GetRoadTileType(TileIndex t) ++template <bool Tgeneric> ++static inline RoadTileType GetRoadTileType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_ROAD)); +- return (RoadTileType)GB(_m[t].m5, 6, 2); ++ return (RoadTileType)GB(GetTile(t)->m5, 6, 2); + } ++/** @copydoc GetRoadTileType(TileIndexT<Tgeneric>::T) */ ++static inline RoadTileType GetRoadTileType(TileIndex t) { return GetRoadTileType<false>(t); } ++/** @copydoc GetRoadTileType(TileIndexT<Tgeneric>::T) */ ++static inline RoadTileType GetRoadTileType(GenericTileIndex t) { return GetRoadTileType<true>(t); } + + /** + * Return whether a tile is a normal road. +@@ -44,20 +49,30 @@ static inline RoadTileType GetRoadTileType(TileIndex t) + * @pre IsTileType(t, MP_ROAD) + * @return True if normal road. + */ +-static inline bool IsNormalRoad(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsNormalRoad(typename TileIndexT<Tgeneric>::T t) + { + return GetRoadTileType(t) == ROAD_TILE_NORMAL; + } ++/** @copydoc IsNormalRoad(TileIndexT<Tgeneric>::T) */ ++static inline bool IsNormalRoad(TileIndex t) { return IsNormalRoad<false>(t); } ++/** @copydoc IsNormalRoad(TileIndexT<Tgeneric>::T) */ ++static inline bool IsNormalRoad(GenericTileIndex t) { return IsNormalRoad<true>(t); } + + /** + * Return whether a tile is a normal road tile. + * @param t Tile to query. + * @return True if normal road tile. + */ +-static inline bool IsNormalRoadTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsNormalRoadTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_ROAD) && IsNormalRoad(t); + } ++/** @copydoc IsNormalRoadTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsNormalRoadTile(TileIndex t) { return IsNormalRoadTile<false>(t); } ++/** @copydoc IsNormalRoadTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsNormalRoadTile(GenericTileIndex t) { return IsNormalRoadTile<true>(t); } + + /** + * Return whether a tile is a level crossing. +@@ -65,20 +80,30 @@ static inline bool IsNormalRoadTile(TileIndex t) + * @pre IsTileType(t, MP_ROAD) + * @return True if level crossing. + */ +-static inline bool IsLevelCrossing(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsLevelCrossing(typename TileIndexT<Tgeneric>::T t) + { + return GetRoadTileType(t) == ROAD_TILE_CROSSING; + } ++/** @copydoc IsLevelCrossing(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLevelCrossing(TileIndex t) { return IsLevelCrossing<false>(t); } ++/** @copydoc IsLevelCrossing(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLevelCrossing(GenericTileIndex t) { return IsLevelCrossing<true>(t); } + + /** + * Return whether a tile is a level crossing tile. + * @param t Tile to query. + * @return True if level crossing tile. + */ +-static inline bool IsLevelCrossingTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsLevelCrossingTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_ROAD) && IsLevelCrossing(t); + } ++/** @copydoc IsLevelCrossingTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLevelCrossingTile(TileIndex t) { return IsLevelCrossingTile<false>(t); } ++/** @copydoc IsLevelCrossingTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLevelCrossingTile(GenericTileIndex t) { return IsLevelCrossingTile<true>(t); } + + /** + * Return whether a tile is a road depot. +@@ -86,10 +111,15 @@ static inline bool IsLevelCrossingTile(TileIndex t) + * @pre IsTileType(t, MP_ROAD) + * @return True if road depot. + */ +-static inline bool IsRoadDepot(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRoadDepot(typename TileIndexT<Tgeneric>::T t) + { + return GetRoadTileType(t) == ROAD_TILE_DEPOT; + } ++/** @copydoc IsRoadDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadDepot(TileIndex t) { return IsRoadDepot<false>(t); } ++/** @copydoc IsRoadDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadDepot(GenericTileIndex t) { return IsRoadDepot<true>(t); } + + /** + * Return whether a tile is a road depot tile. +@@ -108,15 +138,20 @@ static inline bool IsRoadDepotTile(TileIndex t) + * @pre IsNormalRoad(t) + * @return The present road bits for the road type. + */ +-static inline RoadBits GetRoadBits(TileIndex t, RoadType rt) ++template <bool Tgeneric> ++static inline RoadBits GetRoadBits(typename TileIndexT<Tgeneric>::T t, RoadType rt) + { + assert(IsNormalRoad(t)); + switch (rt) { + default: NOT_REACHED(); +- case ROADTYPE_ROAD: return (RoadBits)GB(_m[t].m5, 0, 4); +- case ROADTYPE_TRAM: return (RoadBits)GB(_m[t].m3, 0, 4); ++ case ROADTYPE_ROAD: return (RoadBits)GB(GetTile(t)->m5, 0, 4); ++ case ROADTYPE_TRAM: return (RoadBits)GB(GetTile(t)->m3, 0, 4); + } + } ++/** @copydoc GetRoadBits(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline RoadBits GetRoadBits(TileIndex t, RoadType rt) { return GetRoadBits<false>(t, rt); } ++/** @copydoc GetRoadBits(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline RoadBits GetRoadBits(GenericTileIndex t, RoadType rt) { return GetRoadBits<true>(t, rt); } + + /** + * Get all RoadBits set on a tile except from the given RoadType +@@ -148,36 +183,51 @@ static inline RoadBits GetAllRoadBits(TileIndex tile) + * @param rt Road type. + * @pre IsNormalRoad(t) + */ +-static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt) ++template <bool Tgeneric> ++static inline void SetRoadBits(typename TileIndexT<Tgeneric>::T t, RoadBits r, RoadType rt) + { + assert(IsNormalRoad(t)); // XXX incomplete + switch (rt) { + default: NOT_REACHED(); +- case ROADTYPE_ROAD: SB(_m[t].m5, 0, 4, r); break; +- case ROADTYPE_TRAM: SB(_m[t].m3, 0, 4, r); break; ++ case ROADTYPE_ROAD: SB(GetTile(t)->m5, 0, 4, r); break; ++ case ROADTYPE_TRAM: SB(GetTile(t)->m3, 0, 4, r); break; + } + } ++/** @copydoc SetRoadBits(TileIndexT<Tgeneric>::T,RoadBits,RoadType) */ ++static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt) { SetRoadBits<false>(t, r, rt); } ++/** @copydoc SetRoadBits(TileIndexT<Tgeneric>::T,RoadBits,RoadType) */ ++static inline void SetRoadBits(GenericTileIndex t, RoadBits r, RoadType rt) { SetRoadBits<true>(t, r, rt); } + + /** + * Get the present road types of a tile. + * @param t The tile to query. + * @return Present road types. + */ +-static inline RoadTypes GetRoadTypes(TileIndex t) ++template <bool Tgeneric> ++static inline RoadTypes GetRoadTypes(typename TileIndexT<Tgeneric>::T t) + { +- return (RoadTypes)GB(_me[t].m7, 6, 2); ++ return (RoadTypes)GB(GetTileEx(t)->m7, 6, 2); + } ++/** @copydoc GetRoadTypes(TileIndexT<Tgeneric>::T) */ ++static inline RoadTypes GetRoadTypes(TileIndex t) { return GetRoadTypes<false>(t); } ++/** @copydoc GetRoadTypes(TileIndexT<Tgeneric>::T) */ ++static inline RoadTypes GetRoadTypes(GenericTileIndex t) { return GetRoadTypes<true>(t); } + + /** + * Set the present road types of a tile. + * @param t The tile to change. + * @param rt The new road types. + */ +-static inline void SetRoadTypes(TileIndex t, RoadTypes rt) ++template <bool Tgeneric> ++static inline void SetRoadTypes(typename TileIndexT<Tgeneric>::T t, RoadTypes rt) + { + assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); +- SB(_me[t].m7, 6, 2, rt); ++ SB(GetTileEx(t)->m7, 6, 2, rt); + } ++/** @copydoc SetRoadTypes(TileIndexT<Tgeneric>::T,RoadTypes) */ ++static inline void SetRoadTypes(TileIndex t, RoadTypes rt) { SetRoadTypes<false>(t, rt); } ++/** @copydoc SetRoadTypes(TileIndexT<Tgeneric>::T,RoadTypes) */ ++static inline void SetRoadTypes(GenericTileIndex t, RoadTypes rt) { SetRoadTypes<true>(t, rt); } + + /** + * Check if a tile has a specific road type. +@@ -185,10 +235,15 @@ static inline void SetRoadTypes(TileIndex t, RoadTypes rt) + * @param rt Road type to check. + * @return True if the tile has the specified road type. + */ +-static inline bool HasTileRoadType(TileIndex t, RoadType rt) ++template <bool Tgeneric> ++static inline bool HasTileRoadType(typename TileIndexT<Tgeneric>::T t, RoadType rt) + { + return HasBit(GetRoadTypes(t), rt); + } ++/** @copydoc HasTileRoadType(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline bool HasTileRoadType(TileIndex t, RoadType rt) { return HasTileRoadType<false>(t, rt); } ++/** @copydoc HasTileRoadType(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline bool HasTileRoadType(GenericTileIndex t, RoadType rt) { return HasTileRoadType<true>(t, rt); } + + /** + * Get the owner of a specific road type. +@@ -196,20 +251,25 @@ static inline bool HasTileRoadType(TileIndex t, RoadType rt) + * @param rt The road type to get the owner of. + * @return Owner of the given road type. + */ +-static inline Owner GetRoadOwner(TileIndex t, RoadType rt) ++template <bool Tgeneric> ++static inline Owner GetRoadOwner(typename TileIndexT<Tgeneric>::T t, RoadType rt) + { + assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); + switch (rt) { + default: NOT_REACHED(); +- case ROADTYPE_ROAD: return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5); ++ case ROADTYPE_ROAD: return (Owner)GB(IsNormalRoadTile(t) ? GetTile(t)->m1 : GetTileEx(t)->m7, 0, 5); + case ROADTYPE_TRAM: { + /* Trams don't need OWNER_TOWN, and remapping OWNER_NONE + * to OWNER_TOWN makes it use one bit less */ +- Owner o = (Owner)GB(_m[t].m3, 4, 4); ++ Owner o = (Owner)GB(GetTile(t)->m3, 4, 4); + return o == OWNER_TOWN ? OWNER_NONE : o; + } + } + } ++/** @copydoc GetRoadOwner(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline Owner GetRoadOwner(TileIndex t, RoadType rt) { return GetRoadOwner<false>(t, rt); } ++/** @copydoc GetRoadOwner(TileIndexT<Tgeneric>::T,RoadType) */ ++static inline Owner GetRoadOwner(GenericTileIndex t, RoadType rt) { return GetRoadOwner<true>(t, rt); } + + /** + * Set the owner of a specific road type. +@@ -217,14 +277,19 @@ static inline Owner GetRoadOwner(TileIndex t, RoadType rt) + * @param rt The road type to change the owner of. + * @param o New owner of the given road type. + */ +-static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) ++template <bool Tgeneric> ++static inline void SetRoadOwner(typename TileIndexT<Tgeneric>::T t, RoadType rt, Owner o) + { + switch (rt) { + default: NOT_REACHED(); +- case ROADTYPE_ROAD: SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o); break; +- case ROADTYPE_TRAM: SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); break; ++ case ROADTYPE_ROAD: SB(IsNormalRoadTile(t) ? GetTile(t)->m1 : GetTileEx(t)->m7, 0, 5, o); break; ++ case ROADTYPE_TRAM: SB(GetTile(t)->m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); break; + } + } ++/** @copydoc SetRoadOwner(TileIndexT<Tgeneric>::T,RoadType,Owner) */ ++static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) { SetRoadOwner<false>(t, rt, o); } ++/** @copydoc SetRoadOwner(TileIndexT<Tgeneric>::T,RoadType,Owner) */ ++static inline void SetRoadOwner(GenericTileIndex t, RoadType rt, Owner o) { SetRoadOwner<true>(t, rt, o); } + + /** + * Check if a specific road type is owned by an owner. +@@ -234,11 +299,16 @@ static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) + * @pre HasTileRoadType(t, rt) + * @return True if the road type is owned by the given owner. + */ +-static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o) ++template <bool Tgeneric> ++static inline bool IsRoadOwner(typename TileIndexT<Tgeneric>::T t, RoadType rt, Owner o) + { + assert(HasTileRoadType(t, rt)); + return (GetRoadOwner(t, rt) == o); + } ++/** @copydoc IsRoadOwner(TileIndexT<Tgeneric>::T,RoadType,Owner) */ ++static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o) { return IsRoadOwner<false>(t, rt, o); } ++/** @copydoc IsRoadOwner(TileIndexT<Tgeneric>::T,RoadType,Owner) */ ++static inline bool IsRoadOwner(GenericTileIndex t, RoadType rt, Owner o) { return IsRoadOwner<true>(t, rt, o); } + + /** + * Checks if given tile has town owned road +@@ -268,23 +338,33 @@ template <> struct EnumPropsT<DisallowedRoadDirections> : MakeEnumPropsT<Disallo + * @param t the tile to get the directions from + * @return the disallowed directions + */ +-static inline DisallowedRoadDirections GetDisallowedRoadDirections(TileIndex t) ++template <bool Tgeneric> ++static inline DisallowedRoadDirections GetDisallowedRoadDirections(typename TileIndexT<Tgeneric>::T t) + { + assert(IsNormalRoad(t)); +- return (DisallowedRoadDirections)GB(_m[t].m5, 4, 2); ++ return (DisallowedRoadDirections)GB(GetTile(t)->m5, 4, 2); + } ++/** @copydoc GetDisallowedRoadDirections(TileIndexT<Tgeneric>::T) */ ++static inline DisallowedRoadDirections GetDisallowedRoadDirections(TileIndex t) { return GetDisallowedRoadDirections<false>(t); } ++/** @copydoc GetDisallowedRoadDirections(TileIndexT<Tgeneric>::T) */ ++static inline DisallowedRoadDirections GetDisallowedRoadDirections(GenericTileIndex t) { return GetDisallowedRoadDirections<true>(t); } + + /** + * Sets the disallowed directions + * @param t the tile to set the directions for + * @param drd the disallowed directions + */ +-static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd) ++template <bool Tgeneric> ++static inline void SetDisallowedRoadDirections(typename TileIndexT<Tgeneric>::T t, DisallowedRoadDirections drd) + { + assert(IsNormalRoad(t)); + assert(drd < DRD_END); +- SB(_m[t].m5, 4, 2, drd); ++ SB(GetTile(t)->m5, 4, 2, drd); + } ++/** @copydoc SetDisallowedRoadDirections(TileIndexT<Tgeneric>::T,DisallowedRoadDirections) */ ++static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd) { SetDisallowedRoadDirections<false>(t, drd); } ++/** @copydoc SetDisallowedRoadDirections(TileIndexT<Tgeneric>::T,DisallowedRoadDirections) */ ++static inline void SetDisallowedRoadDirections(GenericTileIndex t, DisallowedRoadDirections drd) { SetDisallowedRoadDirections<true>(t, drd); } + + /** + * Get the road axis of a level crossing. +@@ -292,11 +372,16 @@ static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirect + * @pre IsLevelCrossing(t) + * @return The axis of the road. + */ +-static inline Axis GetCrossingRoadAxis(TileIndex t) ++template <bool Tgeneric> ++static inline Axis GetCrossingRoadAxis(typename TileIndexT<Tgeneric>::T t) + { + assert(IsLevelCrossing(t)); +- return (Axis)GB(_m[t].m5, 0, 1); ++ return (Axis)GB(GetTile(t)->m5, 0, 1); + } ++/** @copydoc GetCrossingRoadAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetCrossingRoadAxis(TileIndex t) { return GetCrossingRoadAxis<false>(t); } ++/** @copydoc GetCrossingRoadAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetCrossingRoadAxis(GenericTileIndex t) { return GetCrossingRoadAxis<true>(t); } + + /** + * Get the rail axis of a level crossing. +@@ -304,21 +389,31 @@ static inline Axis GetCrossingRoadAxis(TileIndex t) + * @pre IsLevelCrossing(t) + * @return The axis of the rail. + */ +-static inline Axis GetCrossingRailAxis(TileIndex t) ++template <bool Tgeneric> ++static inline Axis GetCrossingRailAxis(typename TileIndexT<Tgeneric>::T t) + { + assert(IsLevelCrossing(t)); + return OtherAxis((Axis)GetCrossingRoadAxis(t)); + } ++/** @copydoc GetCrossingRailAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetCrossingRailAxis(TileIndex t) { return GetCrossingRailAxis<false>(t); } ++/** @copydoc GetCrossingRailAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetCrossingRailAxis(GenericTileIndex t) { return GetCrossingRailAxis<true>(t); } + + /** + * Get the road bits of a level crossing. + * @param tile The tile to query. + * @return The present road bits. + */ +-static inline RoadBits GetCrossingRoadBits(TileIndex tile) ++template <bool Tgeneric> ++static inline RoadBits GetCrossingRoadBits(typename TileIndexT<Tgeneric>::T tile) + { + return GetCrossingRoadAxis(tile) == AXIS_X ? ROAD_X : ROAD_Y; + } ++/** @copydoc GetCrossingRoadBits(TileIndexT<Tgeneric>::T) */ ++static inline RoadBits GetCrossingRoadBits(TileIndex tile) { return GetCrossingRoadBits<false>(tile); } ++/** @copydoc GetCrossingRoadBits(TileIndexT<Tgeneric>::T) */ ++static inline RoadBits GetCrossingRoadBits(GenericTileIndex tile) { return GetCrossingRoadBits<true>(tile); } + + /** + * Get the rail track of a level crossing. +@@ -335,10 +430,15 @@ static inline Track GetCrossingRailTrack(TileIndex tile) + * @param tile The tile to query. + * @return The rail track bits. + */ +-static inline TrackBits GetCrossingRailBits(TileIndex tile) ++template <bool Tgeneric> ++static inline TrackBits GetCrossingRailBits(typename TileIndexT<Tgeneric>::T tile) + { + return AxisToTrackBits(GetCrossingRailAxis(tile)); + } ++/** @copydoc GetCrossingRailBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetCrossingRailBits(TileIndex tile) { return GetCrossingRailBits<false>(tile); } ++/** @copydoc GetCrossingRailBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetCrossingRailBits(GenericTileIndex tile) { return GetCrossingRailBits<true>(tile); } + + + /** +@@ -350,7 +450,7 @@ static inline TrackBits GetCrossingRailBits(TileIndex tile) + static inline bool HasCrossingReservation(TileIndex t) + { + assert(IsLevelCrossingTile(t)); +- return HasBit(_m[t].m5, 4); ++ return HasBit(GetTile(t)->m5, 4); + } + + /** +@@ -363,7 +463,7 @@ static inline bool HasCrossingReservation(TileIndex t) + static inline void SetCrossingReservation(TileIndex t, bool b) + { + assert(IsLevelCrossingTile(t)); +- SB(_m[t].m5, 4, 1, b ? 1 : 0); ++ SB(GetTile(t)->m5, 4, 1, b ? 1 : 0); + } + + /** +@@ -386,7 +486,7 @@ static inline TrackBits GetCrossingReservationTrackBits(TileIndex t) + static inline bool IsCrossingBarred(TileIndex t) + { + assert(IsLevelCrossing(t)); +- return HasBit(_m[t].m5, 5); ++ return HasBit(GetTile(t)->m5, 5); + } + + /** +@@ -398,7 +498,7 @@ static inline bool IsCrossingBarred(TileIndex t) + static inline void SetCrossingBarred(TileIndex t, bool barred) + { + assert(IsLevelCrossing(t)); +- SB(_m[t].m5, 5, 1, barred ? 1 : 0); ++ SB(GetTile(t)->m5, 5, 1, barred ? 1 : 0); + } + + /** +@@ -428,7 +528,7 @@ static inline void BarCrossing(TileIndex t) + */ + static inline bool IsOnSnow(TileIndex t) + { +- return HasBit(_me[t].m7, 5); ++ return HasBit(GetTileEx(t)->m7, 5); + } + + /** Toggle the snow/desert state of a road tile. */ +@@ -439,7 +539,7 @@ static inline bool IsOnSnow(TileIndex t) + */ + static inline void ToggleSnow(TileIndex t) + { +- ToggleBit(_me[t].m7, 5); ++ ToggleBit(GetTileEx(t)->m7, 5); + } + + +@@ -461,7 +561,7 @@ enum Roadside { + */ + static inline Roadside GetRoadside(TileIndex tile) + { +- return (Roadside)GB(_me[tile].m6, 3, 3); ++ return (Roadside)GB(GetTileEx(tile)->m6, 3, 3); + } + + /** +@@ -471,7 +571,7 @@ static inline Roadside GetRoadside(TileIndex tile) + */ + static inline void SetRoadside(TileIndex tile, Roadside s) + { +- SB(_me[tile].m6, 3, 3, s); ++ SB(GetTileEx(tile)->m6, 3, 3, s); + } + + /** +@@ -491,9 +591,9 @@ static inline bool HasRoadWorks(TileIndex t) + */ + static inline bool IncreaseRoadWorksCounter(TileIndex t) + { +- AB(_me[t].m7, 0, 4, 1); ++ AB(GetTileEx(t)->m7, 0, 4, 1); + +- return GB(_me[t].m7, 0, 4) == 15; ++ return GB(GetTileEx(t)->m7, 0, 4) == 15; + } + + /** +@@ -522,7 +622,7 @@ static inline void TerminateRoadWorks(TileIndex t) + assert(HasRoadWorks(t)); + SetRoadside(t, (Roadside)(GetRoadside(t) - ROADSIDE_GRASS_ROAD_WORKS + ROADSIDE_GRASS)); + /* Stop the counter */ +- SB(_me[t].m7, 0, 4, 0); ++ SB(GetTileEx(t)->m7, 0, 4, 0); + } + + +@@ -531,11 +631,16 @@ static inline void TerminateRoadWorks(TileIndex t) + * @param t The tile to query. + * @return Diagonal direction of the depot exit. + */ +-static inline DiagDirection GetRoadDepotDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetRoadDepotDirection(typename TileIndexT<Tgeneric>::T t) + { + assert(IsRoadDepot(t)); +- return (DiagDirection)GB(_m[t].m5, 0, 2); ++ return (DiagDirection)GB(GetTile(t)->m5, 0, 2); + } ++/** @copydoc GetRoadDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRoadDepotDirection(TileIndex t) { return GetRoadDepotDirection<false>(t); } ++/** @copydoc GetRoadDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRoadDepotDirection(GenericTileIndex t) { return GetRoadDepotDirection<true>(t); } + + + RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance = false); +@@ -550,18 +655,23 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge + * @param road New owner of road. + * @param tram New owner of tram tracks. + */ +-static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) ++template <bool Tgeneric> ++static inline void MakeRoadNormal(typename TileIndexT<Tgeneric>::T t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) + { + SetTileType(t, MP_ROAD); + SetTileOwner(t, road); +- _m[t].m2 = town; +- _m[t].m3 = (HasBit(rot, ROADTYPE_TRAM) ? bits : 0); +- _m[t].m4 = 0; +- _m[t].m5 = (HasBit(rot, ROADTYPE_ROAD) ? bits : 0) | ROAD_TILE_NORMAL << 6; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = rot << 6; ++ GetTile(t)->m2 = town; ++ GetTile(t)->m3 = (HasBit(rot, ROADTYPE_TRAM) ? bits : 0); ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = (HasBit(rot, ROADTYPE_ROAD) ? bits : 0) | ROAD_TILE_NORMAL << 6; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = rot << 6; + SetRoadOwner(t, ROADTYPE_TRAM, tram); + } ++/** @copydoc MakeRoadNormal(TileIndexT<Tgeneric>::T,RoadBits,RoadTypes,TownID,Owner,Owner) */ ++static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) { MakeRoadNormal<false>(t, bits, rot, town, road, tram); } ++/** @copydoc MakeRoadNormal(TileIndexT<Tgeneric>::T,RoadBits,RoadTypes,TownID,Owner,Owner) */ ++static inline void MakeRoadNormal(GenericTileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) { MakeRoadNormal<true>(t, bits, rot, town, road, tram); } + + /** + * Make a level crossing. +@@ -574,18 +684,23 @@ static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, Tow + * @param rot New present road types. + * @param town Town ID if the road is a town-owned road. + */ +-static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) ++template <bool Tgeneric> ++static inline void MakeRoadCrossing(typename TileIndexT<Tgeneric>::T t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) + { + SetTileType(t, MP_ROAD); + SetTileOwner(t, rail); +- _m[t].m2 = town; +- _m[t].m3 = rat; +- _m[t].m4 = 0; +- _m[t].m5 = ROAD_TILE_CROSSING << 6 | roaddir; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = rot << 6 | road; ++ GetTile(t)->m2 = town; ++ GetTile(t)->m3 = rat; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = ROAD_TILE_CROSSING << 6 | roaddir; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = rot << 6 | road; + SetRoadOwner(t, ROADTYPE_TRAM, tram); + } ++/** @copydoc MakeRoadCrossing(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,Axis,RailType,RoadTypes,uint) */ ++static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) { MakeRoadCrossing<false>(t, road, tram, rail, roaddir, rat, rot, town); } ++/** @copydoc MakeRoadCrossing(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,Axis,RailType,RoadTypes,uint) */ ++static inline void MakeRoadCrossing(GenericTileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) { MakeRoadCrossing<true>(t, road, tram, rail, roaddir, rat, rot, town); } + + /** + * Make a road depot. +@@ -595,17 +710,22 @@ static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner r + * @param dir Direction of the depot exit. + * @param rt Road type of the depot. + */ +-static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) ++template <bool Tgeneric> ++static inline void MakeRoadDepot(typename TileIndexT<Tgeneric>::T t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) + { + SetTileType(t, MP_ROAD); + SetTileOwner(t, owner); +- _m[t].m2 = did; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = ROAD_TILE_DEPOT << 6 | dir; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = RoadTypeToRoadTypes(rt) << 6 | owner; ++ GetTile(t)->m2 = did; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = ROAD_TILE_DEPOT << 6 | dir; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = RoadTypeToRoadTypes(rt) << 6 | owner; + SetRoadOwner(t, ROADTYPE_TRAM, owner); + } ++/** @copydoc MakeRoadDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DiagDirection,RoadType) */ ++static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { MakeRoadDepot<false>(t, owner, did, dir, rt); } ++/** @copydoc MakeRoadDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DiagDirection,RoadType) */ ++static inline void MakeRoadDepot(GenericTileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { MakeRoadDepot<true>(t, owner, did, dir, rt); } + + #endif /* ROAD_MAP_H */ +diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp +index 35c671d..d246a3b 100644 +--- a/src/roadveh_cmd.cpp ++++ b/src/roadveh_cmd.cpp +@@ -1160,7 +1160,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) + v->x_pos = gp.x; + v->y_pos = gp.y; + v->UpdatePosition(); +- if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); ++ if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); + return true; + } + +diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp +index 9690481..58009bc 100644 +--- a/src/saveload/afterload.cpp ++++ b/src/saveload/afterload.cpp +@@ -125,7 +125,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate + + case MP_TREES: + /* trees on shore */ +- has_water |= (GB(_m[neighbour].m2, 4, 2) == TREE_GROUND_SHORE); ++ has_water |= (GB(_main_map.m[neighbour].m2, 4, 2) == TREE_GROUND_SHORE); + break; + + default: break; +@@ -151,13 +151,13 @@ static void ConvertTownOwner() + for (TileIndex tile = 0; tile != MapSize(); tile++) { + switch (GetTileType(tile)) { + case MP_ROAD: +- if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { +- _m[tile].m3 = OWNER_TOWN; ++ if (GB(_main_map.m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_main_map.m[tile].m3, 7)) { ++ _main_map.m[tile].m3 = OWNER_TOWN; + } + /* FALL THROUGH */ + + case MP_TUNNELBRIDGE: +- if (_m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN); ++ if (_main_map.m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN); + break; + + default: break; +@@ -572,8 +572,8 @@ bool AfterLoadGame() + } + for (TileIndex t = 0; t < map_size; t++) { + if (!IsTileType(t, MP_STATION)) continue; +- if (_m[t].m5 > 7) continue; // is it a rail station tile? +- st = Station::Get(_m[t].m2); ++ if (_main_map.m[t].m5 > 7) continue; // is it a rail station tile? ++ st = Station::Get(_main_map.m[t].m2); + assert(st->train_station.tile != 0); + int dx = TileX(t) - TileX(st->train_station.tile); + int dy = TileY(t) - TileY(st->train_station.tile); +@@ -588,14 +588,14 @@ bool AfterLoadGame() + + /* In old savegame versions, the heightlevel was coded in bits 0..3 of the type field */ + for (TileIndex t = 0; t < map_size; t++) { +- _m[t].height = GB(_m[t].type, 0, 4); +- SB(_m[t].type, 0, 2, GB(_me[t].m6, 0, 2)); +- SB(_me[t].m6, 0, 2, 0); ++ GetTile(t)->height = GB(GetTile(t)->type, 0, 4); ++ SB(GetTile(t)->type, 0, 2, GB(GetTileEx(t)->m6, 0, 2)); ++ SB(GetTileEx(t)->m6, 0, 2, 0); + if (MayHaveBridgeAbove(t)) { +- SB(_m[t].type, 2, 2, GB(_me[t].m6, 6, 2)); +- SB(_me[t].m6, 6, 2, 0); ++ SB(GetTile(t)->type, 2, 2, GB(GetTileEx(t)->m6, 6, 2)); ++ SB(GetTileEx(t)->m6, 6, 2, 0); + } else { +- SB(_m[t].type, 2, 2, 0); ++ SB(GetTile(t)->type, 2, 2, 0); + } + } + } +@@ -825,7 +825,7 @@ bool AfterLoadGame() + break; + + case MP_STATION: { +- if (HasBit(_me[t].m6, 3)) SetBit(_me[t].m6, 2); ++ if (HasBit(_main_map.me[t].m6, 3)) SetBit(_main_map.me[t].m6, 2); + StationGfx gfx = GetStationGfx(t); + StationType st; + if ( IsInsideMM(gfx, 0, 8)) { // Rail station +@@ -863,7 +863,7 @@ bool AfterLoadGame() + ResetSignalHandlers(); + return false; + } +- SB(_me[t].m6, 3, 3, st); ++ SB(_main_map.me[t].m6, 3, 3, st); + break; + } + } +@@ -882,6 +882,9 @@ bool AfterLoadGame() + if (!Station::IsExpected(bst)) break; + Station *st = Station::From(bst); + ++ /* Set up station catchment */ ++ st->catchment.BeforeAddTile(t, st->GetCatchmentRadius()); ++ + switch (GetStationType(t)) { + case STATION_TRUCK: + case STATION_BUS: +@@ -944,13 +947,13 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_HOUSE: +- _m[t].m4 = _m[t].m2; ++ _main_map.m[t].m4 = _main_map.m[t].m2; + SetTownIndex(t, CalcClosestTownFromTile(t)->index); + break; + + case MP_ROAD: +- _m[t].m4 |= (_m[t].m2 << 4); +- if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { ++ _main_map.m[t].m4 |= (_main_map.m[t].m2 << 4); ++ if ((GB(_main_map.m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_main_map.m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { + SetTownIndex(t, CalcClosestTownFromTile(t)->index); + } else { + SetTownIndex(t, 0); +@@ -1004,20 +1007,20 @@ bool AfterLoadGame() + if (IsPlainRail(t)) { + /* Swap ground type and signal type for plain rail tiles, so the + * ground type uses the same bits as for depots and waypoints. */ +- uint tmp = GB(_m[t].m4, 0, 4); +- SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); +- SB(_m[t].m2, 0, 4, tmp); +- } else if (HasBit(_m[t].m5, 2)) { ++ uint tmp = GB(_main_map.m[t].m4, 0, 4); ++ SB(_main_map.m[t].m4, 0, 4, GB(_main_map.m[t].m2, 0, 4)); ++ SB(_main_map.m[t].m2, 0, 4, tmp); ++ } else if (HasBit(_main_map.m[t].m5, 2)) { + /* Split waypoint and depot rail type and remove the subtype. */ +- ClrBit(_m[t].m5, 2); +- ClrBit(_m[t].m5, 6); ++ ClrBit(_main_map.m[t].m5, 2); ++ ClrBit(_main_map.m[t].m5, 6); + } + break; + + case MP_ROAD: + /* Swap m3 and m4, so the track type for rail crossings is the + * same as for normal rail. */ +- Swap(_m[t].m3, _m[t].m4); ++ Swap(_main_map.m[t].m3, _main_map.m[t].m4); + break; + + default: break; +@@ -1031,16 +1034,16 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_ROAD: +- SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); ++ SB(_main_map.m[t].m5, 6, 2, GB(_main_map.m[t].m5, 4, 2)); + switch (GetRoadTileType(t)) { + default: SlErrorCorrupt("Invalid road tile type"); + case ROAD_TILE_NORMAL: +- SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4)); +- SB(_m[t].m4, 4, 4, 0); +- SB(_me[t].m6, 2, 4, 0); ++ SB(_main_map.m[t].m4, 0, 4, GB(_main_map.m[t].m5, 0, 4)); ++ SB(_main_map.m[t].m4, 4, 4, 0); ++ SB(_main_map.me[t].m6, 2, 4, 0); + break; + case ROAD_TILE_CROSSING: +- SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2)); ++ SB(_main_map.m[t].m4, 5, 2, GB(_main_map.m[t].m5, 2, 2)); + break; + case ROAD_TILE_DEPOT: break; + } +@@ -1053,8 +1056,8 @@ bool AfterLoadGame() + + case MP_TUNNELBRIDGE: + /* Middle part of "old" bridges */ +- if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; +- if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { ++ if (old_bridge && IsBridge(t) && HasBit(_main_map.m[t].m5, 6)) break; ++ if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_main_map.m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + SetRoadTypes(t, ROADTYPES_ROAD); + } + break; +@@ -1071,24 +1074,24 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + switch (GetTileType(t)) { + case MP_ROAD: +- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_me[t].m7, 5, 3)); +- SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert ++ if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.me[t].m7, 5, 3)); ++ SB(_main_map.me[t].m7, 5, 1, GB(_main_map.m[t].m3, 7, 1)); // snow/desert + switch (GetRoadTileType(t)) { + default: SlErrorCorrupt("Invalid road tile type"); + case ROAD_TILE_NORMAL: +- SB(_me[t].m7, 0, 4, GB(_m[t].m3, 0, 4)); // road works +- SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground +- SB(_m[t].m3, 0, 4, GB(_m[t].m4, 4, 4)); // tram bits +- SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner +- SB(_m[t].m5, 0, 4, GB(_m[t].m4, 0, 4)); // road bits ++ SB(_main_map.me[t].m7, 0, 4, GB(_main_map.m[t].m3, 0, 4)); // road works ++ SB(_main_map.me[t].m6, 3, 3, GB(_main_map.m[t].m3, 4, 3)); // ground ++ SB(_main_map.m[t].m3, 0, 4, GB(_main_map.m[t].m4, 4, 4)); // tram bits ++ SB(_main_map.m[t].m3, 4, 4, GB(_main_map.m[t].m5, 0, 4)); // tram owner ++ SB(_main_map.m[t].m5, 0, 4, GB(_main_map.m[t].m4, 0, 4)); // road bits + break; + + case ROAD_TILE_CROSSING: +- SB(_me[t].m7, 0, 5, GB(_m[t].m4, 0, 5)); // road owner +- SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground +- SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner +- SB(_m[t].m5, 0, 1, GB(_m[t].m4, 6, 1)); // road axis +- SB(_m[t].m5, 5, 1, GB(_m[t].m4, 5, 1)); // crossing state ++ SB(_main_map.me[t].m7, 0, 5, GB(_main_map.m[t].m4, 0, 5)); // road owner ++ SB(_main_map.me[t].m6, 3, 3, GB(_main_map.m[t].m3, 4, 3)); // ground ++ SB(_main_map.m[t].m3, 4, 4, GB(_main_map.m[t].m5, 0, 4)); // tram owner ++ SB(_main_map.m[t].m5, 0, 1, GB(_main_map.m[t].m4, 6, 1)); // road axis ++ SB(_main_map.m[t].m5, 5, 1, GB(_main_map.m[t].m4, 5, 1)); // crossing state + break; + + case ROAD_TILE_DEPOT: +@@ -1098,32 +1101,32 @@ bool AfterLoadGame() + const Town *town = CalcClosestTownFromTile(t); + if (town != NULL) SetTownIndex(t, town->index); + } +- _m[t].m4 = 0; ++ _main_map.m[t].m4 = 0; + break; + + case MP_STATION: + if (!IsRoadStop(t)) break; + +- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3)); +- SB(_me[t].m7, 0, 5, HasBit(_me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t)); +- SB(_m[t].m3, 4, 4, _m[t].m1); +- _m[t].m4 = 0; ++ if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.m[t].m3, 0, 3)); ++ SB(_main_map.me[t].m7, 0, 5, HasBit(_main_map.me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t)); ++ SB(_main_map.m[t].m3, 4, 4, _main_map.m[t].m1); ++ _main_map.m[t].m4 = 0; + break; + + case MP_TUNNELBRIDGE: +- if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; +- if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { +- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3)); ++ if (old_bridge && IsBridge(t) && HasBit(_main_map.m[t].m5, 6)) break; ++ if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_main_map.m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { ++ if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.m[t].m3, 0, 3)); + + Owner o = GetTileOwner(t); +- SB(_me[t].m7, 0, 5, o); // road owner +- SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner ++ SB(_main_map.me[t].m7, 0, 5, o); // road owner ++ SB(_main_map.m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner + } +- SB(_me[t].m6, 2, 4, GB(_m[t].m2, 4, 4)); // bridge type +- SB(_me[t].m7, 5, 1, GB(_m[t].m4, 7, 1)); // snow/desert ++ SB(_main_map.me[t].m6, 2, 4, GB(_main_map.m[t].m2, 4, 4)); // bridge type ++ SB(_main_map.me[t].m7, 5, 1, GB(_main_map.m[t].m4, 7, 1)); // snow/desert + +- _m[t].m2 = 0; +- _m[t].m4 = 0; ++ _main_map.m[t].m2 = 0; ++ _main_map.m[t].m4 = 0; + break; + + default: break; +@@ -1137,11 +1140,11 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); + if (IsBridgeTile(t)) { +- if (HasBit(_m[t].m5, 6)) { // middle part +- Axis axis = (Axis)GB(_m[t].m5, 0, 1); ++ if (HasBit(_main_map.m[t].m5, 6)) { // middle part ++ Axis axis = (Axis)GB(_main_map.m[t].m5, 0, 1); + +- if (HasBit(_m[t].m5, 5)) { // transport route under bridge? +- if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { ++ if (HasBit(_main_map.m[t].m5, 5)) { // transport route under bridge? ++ if (GB(_main_map.m[t].m5, 3, 2) == TRANSPORT_RAIL) { + MakeRailNormal( + t, + GetTileOwner(t), +@@ -1160,7 +1163,7 @@ bool AfterLoadGame() + ); + } + } else { +- if (GB(_m[t].m5, 3, 2) == 0) { ++ if (GB(_main_map.m[t].m5, 3, 2) == 0) { + MakeClear(t, CLEAR_GRASS, 3); + } else { + if (!IsTileFlat(t)) { +@@ -1176,12 +1179,12 @@ bool AfterLoadGame() + } + SetBridgeMiddle(t, axis); + } else { // ramp +- Axis axis = (Axis)GB(_m[t].m5, 0, 1); +- uint north_south = GB(_m[t].m5, 5, 1); ++ Axis axis = (Axis)GB(_main_map.m[t].m5, 0, 1); ++ uint north_south = GB(_main_map.m[t].m5, 5, 1); + DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); +- TransportType type = (TransportType)GB(_m[t].m5, 1, 2); ++ TransportType type = (TransportType)GB(_main_map.m[t].m5, 1, 2); + +- _m[t].m5 = 1 << 7 | type << 2 | dir; ++ _main_map.m[t].m5 = 1 << 7 | type << 2 | dir; + } + } + } +@@ -1287,23 +1290,23 @@ bool AfterLoadGame() + * (see the code somewhere above) so don't use m4, use m2 instead. */ + + /* convert PBS signals to combo-signals */ +- if (HasBit(_m[t].m2, 2)) SB(_m[t].m2, 0, 2, SIGTYPE_COMBO); ++ if (HasBit(_main_map.m[t].m2, 2)) SB(_main_map.m[t].m2, 0, 2, SIGTYPE_COMBO); + + /* move the signal variant back */ +- SB(_m[t].m2, 2, 1, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); +- ClrBit(_m[t].m2, 3); ++ SB(_main_map.m[t].m2, 2, 1, HasBit(_main_map.m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); ++ ClrBit(_main_map.m[t].m2, 3); + } + + /* Clear PBS reservation on track */ + if (!IsRailDepotTile(t)) { +- SB(_m[t].m4, 4, 4, 0); ++ SB(_main_map.m[t].m4, 4, 4, 0); + } else { +- ClrBit(_m[t].m3, 6); ++ ClrBit(_main_map.m[t].m3, 6); + } + break; + + case MP_STATION: // Clear PBS reservation on station +- ClrBit(_m[t].m3, 6); ++ ClrBit(_main_map.m[t].m3, 6); + break; + + default: break; +@@ -1412,31 +1415,31 @@ bool AfterLoadGame() + if (IsSavegameVersionBefore(53)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_HOUSE)) { +- if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { ++ if (GB(_main_map.m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + /* Move the construction stage from m3[7..6] to m5[5..4]. + * The construction counter does not have to move. */ +- SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); +- SB(_m[t].m3, 6, 2, 0); ++ SB(_main_map.m[t].m5, 3, 2, GB(_main_map.m[t].m3, 6, 2)); ++ SB(_main_map.m[t].m3, 6, 2, 0); + + /* The "house is completed" bit is now in m6[2]. */ + SetHouseCompleted(t, false); + } else { + /* The "lift has destination" bit has been moved from + * m5[7] to m7[0]. */ +- SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7)); +- ClrBit(_m[t].m5, 7); ++ SB(_main_map.me[t].m7, 0, 1, HasBit(_main_map.m[t].m5, 7)); ++ ClrBit(_main_map.m[t].m5, 7); + + /* The "lift is moving" bit has been removed, as it does + * the same job as the "lift has destination" bit. */ +- ClrBit(_m[t].m1, 7); ++ ClrBit(_main_map.m[t].m1, 7); + + /* The position of the lift goes from m1[7..0] to m6[7..2], + * making m1 totally free, now. The lift position does not + * have to be a full byte since the maximum value is 36. */ +- SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); ++ SetLiftPosition(t, GB(_main_map.m[t].m1, 0, 6 )); + +- _m[t].m1 = 0; +- _m[t].m3 = 0; ++ _main_map.m[t].m1 = 0; ++ _main_map.m[t].m3 = 0; + SetHouseCompleted(t, true); + } + } +@@ -1451,19 +1454,19 @@ bool AfterLoadGame() + if (IsTileType(t, MP_INDUSTRY)) { + switch (GetIndustryGfx(t)) { + case GFX_POWERPLANT_SPARKS: +- _m[t].m3 = GB(_m[t].m1, 2, 5); ++ _main_map.m[t].m3 = GB(_main_map.m[t].m1, 2, 5); + break; + + case GFX_OILWELL_ANIMATED_1: + case GFX_OILWELL_ANIMATED_2: + case GFX_OILWELL_ANIMATED_3: +- _m[t].m3 = GB(_m[t].m1, 0, 2); ++ _main_map.m[t].m3 = GB(_main_map.m[t].m1, 0, 2); + break; + + case GFX_COAL_MINE_TOWER_ANIMATED: + case GFX_COPPER_MINE_TOWER_ANIMATED: + case GFX_GOLD_MINE_TOWER_ANIMATED: +- _m[t].m3 = _m[t].m1; ++ _main_map.m[t].m3 = _main_map.m[t].m1; + break; + + default: // No animation states to change +@@ -1511,8 +1514,8 @@ bool AfterLoadGame() + + if (IsSavegameVersionBefore(52)) { + for (TileIndex t = 0; t < map_size; t++) { +- if (IsTileType(t, MP_OBJECT) && _m[t].m5 == OBJECT_STATUE) { +- _m[t].m2 = CalcClosestTownFromTile(t)->index; ++ if (IsTileType(t, MP_OBJECT) && _main_map.m[t].m5 == OBJECT_STATUE) { ++ _main_map.m[t].m2 = CalcClosestTownFromTile(t)->index; + } + } + } +@@ -1575,10 +1578,10 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { + /* move signal states */ +- SetSignalStates(t, GB(_m[t].m2, 4, 4)); +- SB(_m[t].m2, 4, 4, 0); ++ SetSignalStates(t, GB(_main_map.m[t].m2, 4, 4)); ++ SB(_main_map.m[t].m2, 4, 4, 0); + /* clone signal type and variant */ +- SB(_m[t].m2, 4, 3, GB(_m[t].m2, 0, 3)); ++ SB(_main_map.m[t].m2, 4, 3, GB(_main_map.m[t].m2, 0, 3)); + } + } + } +@@ -1621,7 +1624,7 @@ bool AfterLoadGame() + if (IsSavegameVersionBefore(83)) { + for (TileIndex t = 0; t < map_size; t++) { + if (IsShipDepotTile(t)) { +- _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; ++ _main_map.m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; + } + } + } +@@ -1657,8 +1660,8 @@ bool AfterLoadGame() + if (IsSavegameVersionBefore(81)) { + for (TileIndex t = 0; t < map_size; t++) { + if (GetTileType(t) == MP_TREES) { +- TreeGround groundType = (TreeGround)GB(_m[t].m2, 4, 2); +- if (groundType != TREE_GROUND_SNOW_DESERT) SB(_m[t].m2, 6, 2, 3); ++ TreeGround groundType = (TreeGround)GB(_main_map.m[t].m2, 4, 2); ++ if (groundType != TREE_GROUND_SNOW_DESERT) SB(_main_map.m[t].m2, 6, 2, 3); + } + } + } +@@ -1725,8 +1728,8 @@ bool AfterLoadGame() + case STATION_OILRIG: + case STATION_DOCK: + case STATION_BUOY: +- SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); +- SB(_m[t].m3, 0, 2, 0); ++ SetWaterClass(t, (WaterClass)GB(_main_map.m[t].m3, 0, 2)); ++ SB(_main_map.m[t].m3, 0, 2, 0); + break; + + default: +@@ -1736,8 +1739,8 @@ bool AfterLoadGame() + break; + + case MP_WATER: +- SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); +- SB(_m[t].m3, 0, 2, 0); ++ SetWaterClass(t, (WaterClass)GB(_main_map.m[t].m3, 0, 2)); ++ SB(_main_map.m[t].m3, 0, 2, 0); + break; + + case MP_OBJECT: +@@ -1764,7 +1767,7 @@ bool AfterLoadGame() + MakeCanal(t, o, Random()); + } + } else if (IsShipDepot(t)) { +- Owner o = (Owner)_m[t].m4; // Original water owner ++ Owner o = (Owner)_main_map.m[t].m4; // Original water owner + SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL); + } + } +@@ -1853,8 +1856,8 @@ bool AfterLoadGame() + /* Increase HouseAnimationFrame from 5 to 7 bits */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { +- SB(_me[t].m6, 2, 6, GB(_me[t].m6, 3, 5)); +- SB(_m[t].m3, 5, 1, 0); ++ SB(_main_map.me[t].m6, 2, 6, GB(_main_map.me[t].m6, 3, 5)); ++ SB(_main_map.m[t].m3, 5, 1, 0); + } + } + } +@@ -1887,7 +1890,7 @@ bool AfterLoadGame() + + /* Replace "house construction year" with "house age" */ + if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) { +- _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); ++ _main_map.m[t].m5 = Clamp(_cur_year - (_main_map.m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); + } + } + } +@@ -1901,10 +1904,10 @@ bool AfterLoadGame() + case MP_RAILWAY: + if (HasSignals(t)) { + /* move the signal variant */ +- SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); +- SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); +- ClrBit(_m[t].m2, 2); +- ClrBit(_m[t].m2, 6); ++ SetSignalVariant(t, TRACK_UPPER, HasBit(_main_map.m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); ++ SetSignalVariant(t, TRACK_LOWER, HasBit(_main_map.m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); ++ ClrBit(_main_map.m[t].m2, 2); ++ ClrBit(_main_map.m[t].m2, 6); + } + + /* Clear PBS reservation on track */ +@@ -1996,11 +1999,11 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + /* Check for HQ bit being set, instead of using map accessor, + * since we've already changed it code-wise */ +- if (IsTileType(t, MP_OBJECT) && HasBit(_m[t].m5, 7)) { ++ if (IsTileType(t, MP_OBJECT) && HasBit(_main_map.m[t].m5, 7)) { + /* Move size and part identification of HQ out of the m5 attribute, + * on new locations */ +- _m[t].m3 = GB(_m[t].m5, 0, 5); +- _m[t].m5 = OBJECT_HQ; ++ _main_map.m[t].m3 = GB(_main_map.m[t].m5, 0, 5); ++ _main_map.m[t].m5 = OBJECT_HQ; + } + } + } +@@ -2009,13 +2012,13 @@ bool AfterLoadGame() + if (!IsTileType(t, MP_OBJECT)) continue; + + /* Reordering/generalisation of the object bits. */ +- ObjectType type = _m[t].m5; +- SB(_me[t].m6, 2, 4, type == OBJECT_HQ ? GB(_m[t].m3, 2, 3) : 0); +- _m[t].m3 = type == OBJECT_HQ ? GB(_m[t].m3, 1, 1) | GB(_m[t].m3, 0, 1) << 4 : 0; ++ ObjectType type = _main_map.m[t].m5; ++ SB(_main_map.me[t].m6, 2, 4, type == OBJECT_HQ ? GB(_main_map.m[t].m3, 2, 3) : 0); ++ _main_map.m[t].m3 = type == OBJECT_HQ ? GB(_main_map.m[t].m3, 1, 1) | GB(_main_map.m[t].m3, 0, 1) << 4 : 0; + + /* Make sure those bits are clear as well! */ +- _m[t].m4 = 0; +- _me[t].m7 = 0; ++ _main_map.m[t].m4 = 0; ++ _main_map.me[t].m7 = 0; + } + } + +@@ -2028,15 +2031,15 @@ bool AfterLoadGame() + /* No towns, so remove all objects! */ + DoClearSquare(t); + } else { +- uint offset = _m[t].m3; ++ uint offset = _main_map.m[t].m3; + + /* Also move the animation state. */ +- _m[t].m3 = GB(_me[t].m6, 2, 4); +- SB(_me[t].m6, 2, 4, 0); ++ _main_map.m[t].m3 = GB(_main_map.me[t].m6, 2, 4); ++ SB(_main_map.me[t].m6, 2, 4, 0); + + if (offset == 0) { + /* No offset, so make the object. */ +- ObjectType type = _m[t].m5; ++ ObjectType type = _main_map.m[t].m5; + int size = type == OBJECT_HQ ? 2 : 1; + + if (!Object::CanAllocateItem()) { +@@ -2050,14 +2053,14 @@ bool AfterLoadGame() + o->location.w = size; + o->location.h = size; + o->build_date = _date; +- o->town = type == OBJECT_STATUE ? Town::Get(_m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX); +- _m[t].m2 = o->index; ++ o->town = type == OBJECT_STATUE ? Town::Get(_main_map.m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX); ++ _main_map.m[t].m2 = o->index; + Object::IncTypeCount(type); + } else { + /* We're at an offset, so get the ID from our "root". */ + TileIndex northern_tile = t - TileXY(GB(offset, 0, 4), GB(offset, 4, 4)); + assert(IsTileType(northern_tile, MP_OBJECT)); +- _m[t].m2 = _m[northern_tile].m2; ++ _main_map.m[t].m2 = _main_map.m[northern_tile].m2; + } + } + } +@@ -2272,8 +2275,8 @@ bool AfterLoadGame() + if (IsSavegameVersionBefore(128)) { + const Depot *d; + FOR_ALL_DEPOTS(d) { +- _m[d->xy].m2 = d->index; +- if (IsTileType(d->xy, MP_WATER)) _m[GetOtherShipDepotTile(d->xy)].m2 = d->index; ++ _main_map.m[d->xy].m2 = d->index; ++ if (IsTileType(d->xy, MP_WATER)) _main_map.m[GetOtherShipDepotTile(d->xy)].m2 = d->index; + } + } + +@@ -2295,16 +2298,16 @@ bool AfterLoadGame() + if (IsTileType(t, MP_CLEAR)) { + if (GetRawClearGround(t) == CLEAR_SNOW) { + SetClearGroundDensity(t, CLEAR_GRASS, GetClearDensity(t)); +- SetBit(_m[t].m3, 4); ++ SetBit(_main_map.m[t].m3, 4); + } else { +- ClrBit(_m[t].m3, 4); ++ ClrBit(_main_map.m[t].m3, 4); + } + } + if (IsTileType(t, MP_TREES)) { +- uint density = GB(_m[t].m2, 6, 2); +- uint ground = GB(_m[t].m2, 4, 2); +- uint counter = GB(_m[t].m2, 0, 4); +- _m[t].m2 = ground << 6 | density << 4 | counter; ++ uint density = GB(_main_map.m[t].m2, 6, 2); ++ uint ground = GB(_main_map.m[t].m2, 4, 2); ++ uint counter = GB(_main_map.m[t].m2, 0, 4); ++ _main_map.m[t].m2 = ground << 6 | density << 4 | counter; + } + } + } +@@ -2418,23 +2421,23 @@ bool AfterLoadGame() + switch (GetTileType(t)) { + case MP_HOUSE: + if (GetHouseType(t) >= NEW_HOUSE_OFFSET) { +- uint per_proc = _me[t].m7; +- _me[t].m7 = GB(_me[t].m6, 2, 6) | (GB(_m[t].m3, 5, 1) << 6); +- SB(_m[t].m3, 5, 1, 0); +- SB(_me[t].m6, 2, 6, min(per_proc, 63)); ++ uint per_proc = _main_map.me[t].m7; ++ _main_map.me[t].m7 = GB(_main_map.me[t].m6, 2, 6) | (GB(_main_map.m[t].m3, 5, 1) << 6); ++ SB(_main_map.m[t].m3, 5, 1, 0); ++ SB(_main_map.me[t].m6, 2, 6, min(per_proc, 63)); + } + break; + + case MP_INDUSTRY: { +- uint rand = _me[t].m7; +- _me[t].m7 = _m[t].m3; +- _m[t].m3 = rand; ++ uint rand = _main_map.me[t].m7; ++ _main_map.me[t].m7 = _main_map.m[t].m3; ++ _main_map.m[t].m3 = rand; + break; + } + + case MP_OBJECT: +- _me[t].m7 = _m[t].m3; +- _m[t].m3 = 0; ++ _main_map.me[t].m7 = _main_map.m[t].m3; ++ _main_map.m[t].m3 = 0; + break; + + default: +@@ -2759,16 +2762,16 @@ bool AfterLoadGame() + for (TileIndex t = 0; t < map_size; t++) { + if (!IsTileType(t, MP_CLEAR) && !IsTileType(t, MP_TREES)) continue; + if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) continue; +- uint fence = GB(_m[t].m4, 5, 3); ++ uint fence = GB(_main_map.m[t].m4, 5, 3); + if (fence != 0 && IsTileType(TILE_ADDXY(t, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 1, 0), CLEAR_FIELDS)) { + SetFence(TILE_ADDXY(t, 1, 0), DIAGDIR_NE, fence); + } +- fence = GB(_m[t].m4, 2, 3); ++ fence = GB(_main_map.m[t].m4, 2, 3); + if (fence != 0 && IsTileType(TILE_ADDXY(t, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 0, 1), CLEAR_FIELDS)) { + SetFence(TILE_ADDXY(t, 0, 1), DIAGDIR_NW, fence); + } +- SB(_m[t].m4, 2, 3, 0); +- SB(_m[t].m4, 5, 3, 0); ++ SB(_main_map.m[t].m4, 2, 3, 0); ++ SB(_main_map.m[t].m4, 5, 3, 0); + } + } + +@@ -2885,9 +2888,9 @@ bool AfterLoadGame() + /* Move ObjectType from map to pool */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_OBJECT)) { +- Object *o = Object::Get(_m[t].m2); +- o->type = _m[t].m5; +- _m[t].m5 = 0; // zero upper bits of (now bigger) ObjectID ++ Object *o = Object::Get(_main_map.m[t].m2); ++ o->type = _main_map.m[t].m5; ++ _main_map.m[t].m5 = 0; // zero upper bits of (now bigger) ObjectID + } + } + } +diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp +index 86a185c..3a37173 100644 +--- a/src/saveload/map_sl.cpp ++++ b/src/saveload/map_sl.cpp +@@ -56,7 +56,7 @@ static void Load_MAPT() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].type = buf[j]; + } + } + +@@ -67,7 +67,7 @@ static void Save_MAPT() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].type; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -79,7 +79,7 @@ static void Load_MAPH() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) GetTile(i++)->height = buf[j]; + } + } + +@@ -90,7 +90,7 @@ static void Save_MAPH() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].height; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = GetTile(i++)->height; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -102,7 +102,7 @@ static void Load_MAP1() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m1 = buf[j]; + } + } + +@@ -113,7 +113,7 @@ static void Save_MAP1() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m1; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -128,7 +128,7 @@ static void Load_MAP2() + /* In those versions the m2 was 8 bits */ + IsSavegameVersionBefore(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 + ); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m2 = buf[j]; + } + } + +@@ -139,7 +139,7 @@ static void Save_MAP2() + + SlSetLength(size * sizeof(uint16)); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m2; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT16); + } + } +@@ -151,7 +151,7 @@ static void Load_MAP3() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m3 = buf[j]; + } + } + +@@ -162,7 +162,7 @@ static void Save_MAP3() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m3; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -174,7 +174,7 @@ static void Load_MAP4() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m4 = buf[j]; + } + } + +@@ -185,7 +185,7 @@ static void Save_MAP4() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m4; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -197,7 +197,7 @@ static void Load_MAP5() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m5 = buf[j]; + } + } + +@@ -208,7 +208,7 @@ static void Save_MAP5() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m5; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -223,16 +223,16 @@ static void Load_MAP6() + /* 1024, otherwise we overflow on 64x64 maps! */ + SlArray(buf, 1024, SLE_UINT8); + for (uint j = 0; j != 1024; j++) { +- _me[i++].m6 = GB(buf[j], 0, 2); +- _me[i++].m6 = GB(buf[j], 2, 2); +- _me[i++].m6 = GB(buf[j], 4, 2); +- _me[i++].m6 = GB(buf[j], 6, 2); ++ _main_map.me[i++].m6 = GB(buf[j], 0, 2); ++ _main_map.me[i++].m6 = GB(buf[j], 2, 2); ++ _main_map.me[i++].m6 = GB(buf[j], 4, 2); ++ _main_map.me[i++].m6 = GB(buf[j], 6, 2); + } + } + } else { + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.me[i++].m6 = buf[j]; + } + } + } +@@ -244,7 +244,7 @@ static void Save_MAP6() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m6; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.me[i++].m6; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +@@ -256,7 +256,7 @@ static void Load_MAP7() + + for (TileIndex i = 0; i != size;) { + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.me[i++].m7 = buf[j]; + } + } + +@@ -267,7 +267,7 @@ static void Save_MAP7() + + SlSetLength(size); + for (TileIndex i = 0; i != size;) { +- for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; ++ for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.me[i++].m7; + SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); + } + } +diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp +index 4afbe60..dff9bc7 100644 +--- a/src/saveload/oldloader_sl.cpp ++++ b/src/saveload/oldloader_sl.cpp +@@ -47,55 +47,55 @@ void FixOldMapArray() + { + /* TTO/TTD/TTDP savegames could have buoys at tile 0 + * (without assigned station struct) */ +- MemSetT(&_m[0], 0); ++ MemSetT(&_main_map.m[0], 0); + SetTileType(0, MP_WATER); + SetTileOwner(0, OWNER_WATER); + } + + static void FixTTDMapArray() + { +- /* _old_map3 is moved to _m::m3 and _m::m4 */ ++ /* _old_map3 is moved to m3 and m4 */ + for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { +- _m[t].m3 = _old_map3[t * 2]; +- _m[t].m4 = _old_map3[t * 2 + 1]; ++ _main_map.m[t].m3 = _old_map3[t * 2]; ++ _main_map.m[t].m4 = _old_map3[t * 2 + 1]; + } + + for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { + switch (GetTileType(t)) { + case MP_STATION: +- _m[t].m4 = 0; // We do not understand this TTDP station mapping (yet) +- switch (_m[t].m5) { ++ _main_map.m[t].m4 = 0; // We do not understand this TTDP station mapping (yet) ++ switch (_main_map.m[t].m5) { + /* We have drive through stops at a totally different place */ +- case 0x53: case 0x54: _m[t].m5 += 170 - 0x53; break; // Bus drive through +- case 0x57: case 0x58: _m[t].m5 += 168 - 0x57; break; // Truck drive through +- case 0x55: case 0x56: _m[t].m5 += 170 - 0x55; break; // Bus tram stop +- case 0x59: case 0x5A: _m[t].m5 += 168 - 0x59; break; // Truck tram stop ++ case 0x53: case 0x54: _main_map.m[t].m5 += 170 - 0x53; break; // Bus drive through ++ case 0x57: case 0x58: _main_map.m[t].m5 += 168 - 0x57; break; // Truck drive through ++ case 0x55: case 0x56: _main_map.m[t].m5 += 170 - 0x55; break; // Bus tram stop ++ case 0x59: case 0x5A: _main_map.m[t].m5 += 168 - 0x59; break; // Truck tram stop + default: break; + } + break; + + case MP_RAILWAY: + /* We save presignals different from TTDPatch, convert them */ +- if (GB(_m[t].m5, 6, 2) == 1) { // RAIL_TILE_SIGNALS ++ if (GB(_main_map.m[t].m5, 6, 2) == 1) { // RAIL_TILE_SIGNALS + /* This byte is always zero in TTD for this type of tile */ +- if (_m[t].m4) { // Convert the presignals to our own format +- _m[t].m4 = (_m[t].m4 >> 1) & 7; ++ if (_main_map.m[t].m4) { // Convert the presignals to our own format ++ _main_map.m[t].m4 = (_main_map.m[t].m4 >> 1) & 7; + } + } + /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just + * clear it for ourselves and let OTTD's rebuild PBS itself */ +- _m[t].m4 &= 0xF; // Only keep the lower four bits; upper four is PBS ++ _main_map.m[t].m4 &= 0xF; // Only keep the lower four bits; upper four is PBS + break; + + case MP_WATER: + /* if water class == 3, make river there */ +- if (GB(_m[t].m3, 0, 2) == 3) { ++ if (GB(_main_map.m[t].m3, 0, 2) == 3) { + SetTileType(t, MP_WATER); + SetTileOwner(t, OWNER_WATER); +- _m[t].m2 = 0; +- _m[t].m3 = 2; // WATER_CLASS_RIVER +- _m[t].m4 = Random(); +- _m[t].m5 = 0; ++ _main_map.m[t].m2 = 0; ++ _main_map.m[t].m3 = 2; // WATER_CLASS_RIVER ++ _main_map.m[t].m4 = Random(); ++ _main_map.m[t].m5 = 0; + } + break; + +@@ -199,7 +199,7 @@ void FixOldVehicles() + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->state != RVSB_IN_DEPOT && rv->state != RVSB_WORMHOLE) { + ClrBit(rv->state, 2); +- if (IsTileType(rv->tile, MP_STATION) && _m[rv->tile].m5 >= 168) { ++ if (IsTileType(rv->tile, MP_STATION) && _main_map.m[rv->tile].m5 >= 168) { + /* Update the vehicle's road state to show we're in a drive through road stop. */ + SetBit(rv->state, RVS_IN_DT_ROAD_STOP); + } +@@ -229,9 +229,9 @@ static bool FixTTOMapArray() + if (tt == 11) { + /* TTO has a different way of storing monorail. + * Instead of using bits in m3 it uses a different tile type. */ +- _m[t].m3 = 1; // rail type = monorail (in TTD) ++ _main_map.m[t].m3 = 1; // rail type = monorail (in TTD) + SetTileType(t, MP_RAILWAY); +- _m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS ++ _main_map.m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS + tt = MP_RAILWAY; + } + +@@ -240,18 +240,18 @@ static bool FixTTOMapArray() + break; + + case MP_RAILWAY: +- switch (GB(_m[t].m5, 6, 2)) { ++ switch (GB(_main_map.m[t].m5, 6, 2)) { + case 0: // RAIL_TILE_NORMAL + break; + case 1: // RAIL_TILE_SIGNALS +- _m[t].m4 = (~_m[t].m5 & 1) << 2; // signal variant (present only in OTTD) +- SB(_m[t].m2, 6, 2, GB(_m[t].m5, 3, 2)); // signal status +- _m[t].m3 |= 0xC0; // both signals are present +- _m[t].m5 = HasBit(_m[t].m5, 5) ? 2 : 1; // track direction (only X or Y) +- _m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS ++ _main_map.m[t].m4 = (~_main_map.m[t].m5 & 1) << 2; // signal variant (present only in OTTD) ++ SB(_main_map.m[t].m2, 6, 2, GB(_main_map.m[t].m5, 3, 2)); // signal status ++ _main_map.m[t].m3 |= 0xC0; // both signals are present ++ _main_map.m[t].m5 = HasBit(_main_map.m[t].m5, 5) ? 2 : 1; // track direction (only X or Y) ++ _main_map.m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS + break; + case 3: // RAIL_TILE_DEPOT +- _m[t].m2 = 0; ++ _main_map.m[t].m2 = 0; + break; + default: + return false; +@@ -259,12 +259,12 @@ static bool FixTTOMapArray() + break; + + case MP_ROAD: // road (depot) or level crossing +- switch (GB(_m[t].m5, 4, 4)) { ++ switch (GB(_main_map.m[t].m5, 4, 4)) { + case 0: // ROAD_TILE_NORMAL +- if (_m[t].m2 == 4) _m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES ++ if (_main_map.m[t].m2 == 4) _main_map.m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES + break; + case 1: // ROAD_TILE_CROSSING (there aren't monorail crossings in TTO) +- _m[t].m3 = _m[t].m1; // set owner of road = owner of rail ++ _main_map.m[t].m3 = _main_map.m[t].m1; // set owner of road = owner of rail + break; + case 2: // ROAD_TILE_DEPOT + break; +@@ -274,69 +274,69 @@ static bool FixTTOMapArray() + break; + + case MP_HOUSE: +- _m[t].m3 = _m[t].m2 & 0xC0; // construction stage +- _m[t].m2 &= 0x3F; // building type +- if (_m[t].m2 >= 5) _m[t].m2++; // skip "large office block on snow" ++ _main_map.m[t].m3 = _main_map.m[t].m2 & 0xC0; // construction stage ++ _main_map.m[t].m2 &= 0x3F; // building type ++ if (_main_map.m[t].m2 >= 5) _main_map.m[t].m2++; // skip "large office block on snow" + break; + + case MP_TREES: +- _m[t].m3 = GB(_m[t].m5, 3, 3); // type of trees +- _m[t].m5 &= 0xC7; // number of trees and growth status ++ _main_map.m[t].m3 = GB(_main_map.m[t].m5, 3, 3); // type of trees ++ _main_map.m[t].m5 &= 0xC7; // number of trees and growth status + break; + + case MP_STATION: +- _m[t].m3 = (_m[t].m5 >= 0x08 && _m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) +- if (_m[t].m5 >= 8) _m[t].m5 -= 8; // shift for monorail +- if (_m[t].m5 >= 0x42) _m[t].m5++; // skip heliport ++ _main_map.m[t].m3 = (_main_map.m[t].m5 >= 0x08 && _main_map.m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) ++ if (_main_map.m[t].m5 >= 8) _main_map.m[t].m5 -= 8; // shift for monorail ++ if (_main_map.m[t].m5 >= 0x42) _main_map.m[t].m5++; // skip heliport + break; + + case MP_WATER: +- _m[t].m3 = _m[t].m2 = 0; ++ _main_map.m[t].m3 = _main_map.m[t].m2 = 0; + break; + + case MP_VOID: +- _m[t].m2 = _m[t].m3 = _m[t].m5 = 0; ++ _main_map.m[t].m2 = _main_map.m[t].m3 = _main_map.m[t].m5 = 0; + break; + + case MP_INDUSTRY: +- _m[t].m3 = 0; +- switch (_m[t].m5) { ++ _main_map.m[t].m3 = 0; ++ switch (_main_map.m[t].m5) { + case 0x24: // farm silo +- _m[t].m5 = 0x25; ++ _main_map.m[t].m5 = 0x25; + break; + case 0x25: case 0x27: // farm + case 0x28: case 0x29: case 0x2A: case 0x2B: // factory +- _m[t].m5--; ++ _main_map.m[t].m5--; + break; + default: +- if (_m[t].m5 >= 0x2C) _m[t].m5 += 3; // iron ore mine, steel mill or bank ++ if (_main_map.m[t].m5 >= 0x2C) _main_map.m[t].m5 += 3; // iron ore mine, steel mill or bank + break; + } + break; + + case MP_TUNNELBRIDGE: +- if (HasBit(_m[t].m5, 7)) { // bridge +- byte m5 = _m[t].m5; +- _m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1 +- if (GB(m5, 1, 2) == 1) _m[t].m5 |= 0x02; // road bridge +- if (GB(m5, 1, 2) == 3) _m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge ++ if (HasBit(_main_map.m[t].m5, 7)) { // bridge ++ byte m5 = _main_map.m[t].m5; ++ _main_map.m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1 ++ if (GB(m5, 1, 2) == 1) _main_map.m[t].m5 |= 0x02; // road bridge ++ if (GB(m5, 1, 2) == 3) _main_map.m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge + if (!HasBit(m5, 6)) { // bridge head +- _m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) ++ _main_map.m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) + } else { // middle bridge part +- _m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge +- if (GB(m5, 3, 2) == 3) _m[t].m3 |= 1; // track subtype under bridge +- if (GB(m5, 3, 2) == 1) _m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear) ++ _main_map.m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge ++ if (GB(m5, 3, 2) == 3) _main_map.m[t].m3 |= 1; // track subtype under bridge ++ if (GB(m5, 3, 2) == 1) _main_map.m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear) + } + } else { // tunnel entrance/exit +- _m[t].m2 = 0; +- _m[t].m3 = HasBit(_m[t].m5, 3); // monorail +- _m[t].m5 &= HasBit(_m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) ++ _main_map.m[t].m2 = 0; ++ _main_map.m[t].m3 = HasBit(_main_map.m[t].m5, 3); // monorail ++ _main_map.m[t].m5 &= HasBit(_main_map.m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) + } + break; + + case MP_OBJECT: +- _m[t].m2 = 0; +- _m[t].m3 = 0; ++ _main_map.m[t].m2 = 0; ++ _main_map.m[t].m3 = 0; + break; + + default: +@@ -1477,15 +1477,15 @@ static bool LoadOldGameDifficulty(LoadgameState *ls, int num) + static bool LoadOldMapPart1(LoadgameState *ls, int num) + { + if (_savegame_type == SGT_TTO) { +- MemSetT(_m, 0, OLD_MAP_SIZE); +- MemSetT(_me, 0, OLD_MAP_SIZE); ++ MemSetT(_main_map.m, 0, OLD_MAP_SIZE); ++ MemSetT(_main_map.me, 0, OLD_MAP_SIZE); + } + + for (uint i = 0; i < OLD_MAP_SIZE; i++) { +- _m[i].m1 = ReadByte(ls); ++ _main_map.m[i].m1 = ReadByte(ls); + } + for (uint i = 0; i < OLD_MAP_SIZE; i++) { +- _m[i].m2 = ReadByte(ls); ++ _main_map.m[i].m2 = ReadByte(ls); + } + + if (_savegame_type != SGT_TTO) { +@@ -1495,10 +1495,10 @@ static bool LoadOldMapPart1(LoadgameState *ls, int num) + } + for (uint i = 0; i < OLD_MAP_SIZE / 4; i++) { + byte b = ReadByte(ls); +- _me[i * 4 + 0].m6 = GB(b, 0, 2); +- _me[i * 4 + 1].m6 = GB(b, 2, 2); +- _me[i * 4 + 2].m6 = GB(b, 4, 2); +- _me[i * 4 + 3].m6 = GB(b, 6, 2); ++ _main_map.me[i * 4 + 0].m6 = GB(b, 0, 2); ++ _main_map.me[i * 4 + 1].m6 = GB(b, 2, 2); ++ _main_map.me[i * 4 + 2].m6 = GB(b, 4, 2); ++ _main_map.me[i * 4 + 3].m6 = GB(b, 6, 2); + } + } + +@@ -1510,10 +1510,10 @@ static bool LoadOldMapPart2(LoadgameState *ls, int num) + uint i; + + for (i = 0; i < OLD_MAP_SIZE; i++) { +- _m[i].type = ReadByte(ls); ++ _main_map.m[i].type = ReadByte(ls); + } + for (i = 0; i < OLD_MAP_SIZE; i++) { +- _m[i].m5 = ReadByte(ls); ++ _main_map.m[i].m5 = ReadByte(ls); + } + + return true; +diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp +index 3db5e1f..813d4a1 100644 +--- a/src/saveload/station_sl.cpp ++++ b/src/saveload/station_sl.cpp +@@ -95,7 +95,7 @@ void MoveBuoysToWaypoints() + TILE_AREA_LOOP(t, train_st) { + if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; + +- SB(_me[t].m6, 3, 3, STATION_WAYPOINT); ++ SB(_main_map.me[t].m6, 3, 3, STATION_WAYPOINT); + wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); + } + +diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp +index 0f93969..0abd04c 100644 +--- a/src/saveload/waypoint_sl.cpp ++++ b/src/saveload/waypoint_sl.cpp +@@ -75,10 +75,10 @@ void MoveWaypointsToBaseStations() + if (wp->delete_ctr != 0) continue; // The waypoint was deleted + + /* Waypoint indices were not added to the map prior to this. */ +- _m[wp->xy].m2 = (StationID)wp->index; ++ _main_map.m[wp->xy].m2 = (StationID)wp->index; + +- if (HasBit(_m[wp->xy].m3, 4)) { +- wp->spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_m[wp->xy].m4 + 1); ++ if (HasBit(_main_map.m[wp->xy].m3, 4)) { ++ wp->spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_main_map.m[wp->xy].m4 + 1); + } + } + } else { +@@ -111,12 +111,12 @@ void MoveWaypointsToBaseStations() + new_wp->string_id = STR_SV_STNAME_WAYPOINT; + + TileIndex t = wp->xy; +- if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _m[t].m2 == wp->index) { ++ if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _main_map.m[t].m2 == wp->index) { + /* The tile might've been reserved! */ +- bool reserved = !IsSavegameVersionBefore(100) && HasBit(_m[t].m5, 4); ++ bool reserved = !IsSavegameVersionBefore(100) && HasBit(_main_map.m[t].m5, 4); + + /* The tile really has our waypoint, so reassign the map array */ +- MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_m[t].m5, 0, 1), 0, GetRailType(t)); ++ MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_main_map.m[t].m5, 0, 1), 0, GetRailType(t)); + new_wp->facilities |= FACIL_TRAIN; + new_wp->owner = GetTileOwner(t); + +diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq +index 23627ca..6986b48 100644 +--- a/src/script/api/game/game_window.hpp.sq ++++ b/src/script/api/game/game_window.hpp.sq +@@ -252,6 +252,28 @@ void SQGSWindow_Register(Squirrel *engine) + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BV_BUILD_SEL, "WID_BV_BUILD_SEL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BV_RENAME, "WID_BV_RENAME"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_PANEL, "WID_C_PANEL"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_COPY, "WID_CT_COPY"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE, "WID_CT_PASTE"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE_FLAG_BUTTON_BEGIN, "WID_CT_PASTE_FLAG_BUTTON_BEGIN"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_RAIL, "WID_CT_WITH_RAIL"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_ROAD, "WID_CT_WITH_ROAD"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_WATER, "WID_CT_WITH_WATER"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_AIR, "WID_CT_WITH_AIR"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_MIRROR_SIGNALS, "WID_CT_MIRROR_SIGNALS"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_UPGRADE_BRIDGES, "WID_CT_UPGRADE_BRIDGES"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_STATIONS, "WID_CT_WITH_STATIONS"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE_FLAG_BUTTON_END, "WID_CT_PASTE_FLAG_BUTTON_END"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_CONVERT_RAILTYPE, "WID_CT_CONVERT_RAILTYPE"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_TERRAFORM, "WID_CT_TERRAFORM"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_TRANSFORMATION, "WID_CT_TRANSFORMATION"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_ROTATE_LEFT, "WID_CT_ROTATE_LEFT"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_ROTATE_RIGHT, "WID_CT_ROTATE_RIGHT"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_REFLECT_NE_SW, "WID_CT_REFLECT_NE_SW"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_REFLECT_NW_SE, "WID_CT_REFLECT_NW_SE"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_GLYPH, "WID_CT_HEIGHT_DIFF_GLYPH"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF, "WID_CT_HEIGHT_DIFF"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_INCREASE, "WID_CT_HEIGHT_DIFF_INCREASE"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_DECREASE, "WID_CT_HEIGHT_DIFF_DECREASE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_CAPTION, "WID_C_CAPTION"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_FACE, "WID_C_FACE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_FACE_TITLE, "WID_C_FACE_TITLE"); +@@ -1123,6 +1145,7 @@ void SQGSWindow_Register(Squirrel *engine) + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_LOWER_LAND, "WID_TT_LOWER_LAND"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_RAISE_LAND, "WID_TT_RAISE_LAND"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_LEVEL_LAND, "WID_TT_LEVEL_LAND"); ++ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_CLIPBOARD, "WID_TT_CLIPBOARD"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_DEMOLISH, "WID_TT_DEMOLISH"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_BUY_LAND, "WID_TT_BUY_LAND"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_PLANT_TREES, "WID_TT_PLANT_TREES"); +diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp +index 58e1147..a55ab5c 100644 +--- a/src/script/api/script_window.hpp ++++ b/src/script/api/script_window.hpp +@@ -23,6 +23,7 @@ + #include "../../widgets/bridge_widget.h" + #include "../../widgets/build_vehicle_widget.h" + #include "../../widgets/cheat_widget.h" ++#include "../../widgets/clipboard_widget.h" + #include "../../widgets/company_widget.h" + #include "../../widgets/console_widget.h" + #include "../../widgets/date_widget.h" +@@ -995,6 +996,43 @@ public: + WID_C_PANEL = ::WID_C_PANEL, ///< Panel where all cheats are shown in. + }; + ++ /* automatically generated from ../../widgets/clipboard_widget.h */ ++ /** Widgets of the #ClipboardToolbarWindow class. */ ++ enum ClipboardToolbarWidgets { ++ WID_CT_CLIPBOARD_1 = ::WID_CT_CLIPBOARD_1, ///< Button to switch to clipboard #1 ++ WID_CT_CLIPBOARD_2 = ::WID_CT_CLIPBOARD_2, ///< Button to switch to clipboard #2 ++ WID_CT_CLIPBOARD_3 = ::WID_CT_CLIPBOARD_3, ///< Button to switch to clipboard #3 ++ WID_CT_CLIPBOARD_4 = ::WID_CT_CLIPBOARD_4, ///< Button to switch to clipboard #4 ++ ++ WID_CT_COPY = ::WID_CT_COPY, ///< Copy button (single player) ++ WID_CT_PASTE = ::WID_CT_PASTE, ///< Paste button (single player) ++ ++ WID_CT_PASTE_FLAG_BUTTON_BEGIN = ::WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< First button to toggle copy-paste flag ++ WID_CT_WITH_RAIL = ::WID_CT_WITH_RAIL, ///< Toggle rails button ++ WID_CT_WITH_ROAD = ::WID_CT_WITH_ROAD, ///< Toggle roads button ++ WID_CT_WITH_WATER = ::WID_CT_WITH_WATER, ///< Toggle water button ++ WID_CT_WITH_AIR = ::WID_CT_WITH_AIR, ///< Toggle air button ++ WID_CT_MIRROR_SIGNALS = ::WID_CT_MIRROR_SIGNALS, ///< Toggle signal mirrorig button ++ WID_CT_UPGRADE_BRIDGES = ::WID_CT_UPGRADE_BRIDGES, ///< Toggle bridge upgrading button ++ WID_CT_WITH_STATIONS = ::WID_CT_WITH_STATIONS, ///< Toggle stations button ++ WID_CT_PASTE_FLAG_BUTTON_END = ::WID_CT_PASTE_FLAG_BUTTON_END, ///< Past-the-last button to toggle copy-paste flag ++ ++ WID_CT_CONVERT_RAILTYPE = ::WID_CT_CONVERT_RAILTYPE, ///< Button to select railtype to convert to ++ ++ WID_CT_TERRAFORM = ::WID_CT_TERRAFORM, ///< Button to select terraforming mode ++ ++ WID_CT_TRANSFORMATION = ::WID_CT_TRANSFORMATION, ///< Button to show/reset clipboard transformation ++ WID_CT_ROTATE_LEFT = ::WID_CT_ROTATE_LEFT, ///< Rotate left button ++ WID_CT_ROTATE_RIGHT = ::WID_CT_ROTATE_RIGHT, ///< Rotate right button ++ WID_CT_REFLECT_NE_SW = ::WID_CT_REFLECT_NE_SW, ///< Reflect against NE-SW axis button ++ WID_CT_REFLECT_NW_SE = ::WID_CT_REFLECT_NW_SE, ///< Reflect against NW-SE axis button ++ ++ WID_CT_HEIGHT_DIFF_GLYPH = ::WID_CT_HEIGHT_DIFF_GLYPH, ///< Image in front of buttons to increase/decrease height level ++ WID_CT_HEIGHT_DIFF = ::WID_CT_HEIGHT_DIFF, ///< Panel with buttons to increase/decrease height level ++ WID_CT_HEIGHT_DIFF_INCREASE = ::WID_CT_HEIGHT_DIFF_INCREASE, ///< Button to increase height level ++ WID_CT_HEIGHT_DIFF_DECREASE = ::WID_CT_HEIGHT_DIFF_DECREASE, ///< Button to decrease height level ++ }; ++ + /* automatically generated from ../../widgets/company_widget.h */ + /** Widgets of the #CompanyWindow class. */ + enum CompanyWidgets { +@@ -2310,6 +2348,7 @@ public: + WID_TT_LOWER_LAND = ::WID_TT_LOWER_LAND, ///< Lower land button. + WID_TT_RAISE_LAND = ::WID_TT_RAISE_LAND, ///< Raise land button. + WID_TT_LEVEL_LAND = ::WID_TT_LEVEL_LAND, ///< Level land button. ++ WID_TT_CLIPBOARD = ::WID_TT_CLIPBOARD, ///< Button to open the clipboard toolbar + WID_TT_DEMOLISH = ::WID_TT_DEMOLISH, ///< Demolish aka dynamite button. + WID_TT_BUY_LAND = ::WID_TT_BUY_LAND, ///< Buy land button. + WID_TT_PLANT_TREES = ::WID_TT_PLANT_TREES, ///< Plant trees button (note: opens separate window, no place-push-button). +diff --git a/src/script/api/template/template_window.hpp.sq b/src/script/api/template/template_window.hpp.sq +index a21a75a..14cac3d 100644 +--- a/src/script/api/template/template_window.hpp.sq ++++ b/src/script/api/template/template_window.hpp.sq +@@ -47,6 +47,8 @@ namespace SQConvert { + template <> inline int Return<ScriptWindow::BuildVehicleWidgets>(HSQUIRRELVM vm, ScriptWindow::BuildVehicleWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::CheatWidgets GetParam(ForceType<ScriptWindow::CheatWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CheatWidgets)tmp; } + template <> inline int Return<ScriptWindow::CheatWidgets>(HSQUIRRELVM vm, ScriptWindow::CheatWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } ++ template <> inline ScriptWindow::ClipboardToolbarWidgets GetParam(ForceType<ScriptWindow::ClipboardToolbarWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::ClipboardToolbarWidgets)tmp; } ++ template <> inline int Return<ScriptWindow::ClipboardToolbarWidgets>(HSQUIRRELVM vm, ScriptWindow::ClipboardToolbarWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::CompanyWidgets GetParam(ForceType<ScriptWindow::CompanyWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CompanyWidgets)tmp; } + template <> inline int Return<ScriptWindow::CompanyWidgets>(HSQUIRRELVM vm, ScriptWindow::CompanyWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::CompanyFinancesWidgets GetParam(ForceType<ScriptWindow::CompanyFinancesWidgets>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CompanyFinancesWidgets)tmp; } +diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp +index 0652d1b..1716d2f 100644 +--- a/src/settings_gui.cpp ++++ b/src/settings_gui.cpp +@@ -1478,6 +1478,7 @@ static SettingsContainer &GetSettingsTree() + graphics->Add(new SettingEntry("gui.zoom_max")); + graphics->Add(new SettingEntry("gui.smallmap_land_colour")); + graphics->Add(new SettingEntry("gui.graph_line_thickness")); ++ graphics->Add(new SettingEntry("gui.forecast_display")); + } + + SettingsPage *sound = main->Add(new SettingsPage(STR_CONFIG_SETTING_SOUND)); +@@ -1534,6 +1535,7 @@ static SettingsContainer &GetSettingsTree() + construction->Add(new SettingEntry("gui.quick_goto")); + construction->Add(new SettingEntry("gui.default_rail_type")); + construction->Add(new SettingEntry("gui.disable_unsuitable_building")); ++ construction->Add(new SettingEntry("construction.clipboard_capacity")); + } + + interface->Add(new SettingEntry("gui.autosave")); +@@ -1561,6 +1563,7 @@ static SettingsContainer &GetSettingsTree() + advisors->Add(new SettingEntry("gui.vehicle_income_warn")); + advisors->Add(new SettingEntry("gui.lost_vehicle_warn")); + advisors->Add(new SettingEntry("gui.show_finances")); ++ advisors->Add(new SettingEntry("gui.townrating_indicator")); + advisors->Add(new SettingEntry("news_display.economy")); + advisors->Add(new SettingEntry("news_display.subsidies")); + advisors->Add(new SettingEntry("news_display.open")); +diff --git a/src/settings_type.h b/src/settings_type.h +index 41366a7..bfc5b79 100644 +--- a/src/settings_type.h ++++ b/src/settings_type.h +@@ -108,6 +108,7 @@ struct GUISettings { + uint8 date_format_in_default_names; ///< should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) or ISO dates (2008-12-31) + byte max_num_autosaves; ///< controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1) + bool population_in_label; ///< show the population of a town in his label? ++ bool forecast_display; ///< show the supply and demand forecasting on station building + uint8 right_mouse_btn_emulation; ///< should we emulate right mouse clicking? + uint8 scrollwheel_scrolling; ///< scrolling using the scroll wheel? + uint8 scrollwheel_multiplier; ///< how much 'wheel' per incoming event from the OS? +@@ -119,6 +120,7 @@ struct GUISettings { + bool timetable_in_ticks; ///< whether to show the timetable in ticks rather than days + bool quick_goto; ///< Allow quick access to 'goto button' in vehicle orders window + bool auto_euro; ///< automatically switch to euro in 2002 ++ byte simulated_wormhole_signals; ///< simulate signals in tunnel + byte drag_signals_density; ///< many signals density + bool drag_signals_fixed_distance; ///< keep fixed distance between signals when dragging + Year semaphore_build_before; ///< build semaphore signals automatically before this year +@@ -156,6 +158,7 @@ struct GUISettings { + bool scenario_developer; ///< activate scenario developer: allow modifying NewGRFs in an existing game + uint8 settings_restriction_mode; ///< selected restriction mode in adv. settings GUI. @see RestrictionMode + bool newgrf_show_old_versions; ///< whether to show old versions in the NewGRF list ++ bool townrating_indicator; ///< Whether to show the town rating indicators. + uint8 newgrf_default_palette; ///< default palette to use for NewGRFs without action 14 palette information + + /** +@@ -311,6 +314,7 @@ struct ConstructionSettings { + bool freeform_edges; ///< allow terraforming the tiles at the map edges + uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game + uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused ++ uint8 clipboard_capacity; ///< maximum copy/paste area size (width/height) + + uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? + uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? +diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp +index a24fb35..f3127d6 100644 +--- a/src/ship_cmd.cpp ++++ b/src/ship_cmd.cpp +@@ -628,7 +628,7 @@ static void ShipController(Ship *v) + v->x_pos = gp.x; + v->y_pos = gp.y; + v->UpdatePosition(); +- if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); ++ if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); + return; + } + } +diff --git a/src/signal.cpp b/src/signal.cpp +index 8e870b5..059a3fb 100644 +--- a/src/signal.cpp ++++ b/src/signal.cpp +@@ -197,6 +197,14 @@ static Vehicle *TrainOnTileEnum(Vehicle *v, void *) + return v; + } + ++/** Check whether there is a train only on ramp. */ ++static Vehicle *TrainInWormholeTileEnum(Vehicle *v, void *data) ++{ ++ /* Only look for front engine or last wagon. */ ++ if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; ++ if (*(TileIndex *)data != TileVirtXY(v->x_pos, v->y_pos)) return NULL; ++ return v; ++} + + /** + * Perform some operations before adding data into Todo set +@@ -376,17 +384,39 @@ static SigFlags ExploreSegment(Owner owner) + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; + DiagDirection dir = GetTunnelBridgeDirection(tile); + +- if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole +- if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; +- enterdir = dir; +- exitdir = ReverseDiagDir(dir); +- tile += TileOffsByDiagDir(exitdir); // just skip to next tile +- } else { // NOT incoming from the wormhole! +- if (ReverseDiagDir(enterdir) != dir) continue; +- if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; +- tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile +- enterdir = INVALID_DIAGDIR; +- exitdir = INVALID_DIAGDIR; ++ if (HasWormholeSignals(tile)) { ++ if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole ++ if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { // tunnel entrence is ignored ++ if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; ++ if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; ++ } ++ enterdir = dir; ++ exitdir = ReverseDiagDir(dir); ++ tile += TileOffsByDiagDir(exitdir); // just skip to next tile ++ } else { // NOT incoming from the wormhole! ++ if (ReverseDiagDir(enterdir) != dir) continue; ++ if (!(flags & SF_TRAIN)) { ++ if (HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; ++ if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { ++ if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; ++ } ++ } ++ continue; ++ } ++ } else { ++ if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole ++ if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; ++ enterdir = dir; ++ exitdir = ReverseDiagDir(dir); ++ tile += TileOffsByDiagDir(exitdir); // just skip to next tile ++ } else { // NOT incoming from the wormhole! ++ if (ReverseDiagDir(enterdir) != dir) continue; ++ if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; ++ tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile ++ enterdir = INVALID_DIAGDIR; ++ exitdir = INVALID_DIAGDIR; ++ } ++ + } + } + break; +@@ -494,7 +524,9 @@ static SigSegState UpdateSignalsInBuffer(Owner owner) + assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL); + assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile))); + _tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre +- _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); ++ if (!HasWormholeSignals(tile)) { // Don't worry with other side of tunnel. ++ _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); ++ } + break; + + case MP_RAILWAY: +diff --git a/src/signs.cpp b/src/signs.cpp +index 2a23a43..83179ee 100644 +--- a/src/signs.cpp ++++ b/src/signs.cpp +@@ -49,7 +49,7 @@ void Sign::UpdateVirtCoord() + { + Point pt = RemapCoords(this->x, this->y, this->z); + SetDParam(0, this->index); +- this->sign.UpdatePosition(pt.x, pt.y - 6 * ZOOM_LVL_BASE, STR_WHITE_SIGN); ++ this->sign.UpdatePosition(pt.x, pt.y - 6 * ZOOM_LVL_BASE, STR_WHITE_SIGN, STR_WHITE_SIGN); + } + + /** Update the coordinates of all signs */ +diff --git a/src/slope_func.h b/src/slope_func.h +index 4aff6b9..40431b3 100644 +--- a/src/slope_func.h ++++ b/src/slope_func.h +@@ -189,6 +189,48 @@ static inline Corner OppositeCorner(Corner corner) + } + + /** ++ * Transform a Corner. ++ * @param c The Corner to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Corner. ++ */ ++static inline Corner TransformCorner(Corner c, DirTransformation transformation) ++{ ++ if (transformation & DTR_REFLECTION_BIT) c = (Corner)(1 - c); // reflect against X-axis (let overflow) ++ return (Corner)((uint)(c + transformation) % CORNER_END); // rotate and cut off overflowing bits ++} ++ ++/** ++ * Transform a Slope. ++ * @param s The Slope to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Slope. ++ */ ++static inline Slope TransformSlope(Slope s, DirTransformation transformation) ++{ ++ assert((s & ~(SLOPE_ELEVATED | SLOPE_STEEP)) == 0); ++ ++ Slope steep_bit = s & SLOPE_STEEP; // store the "steep" bit ++ s &= SLOPE_ELEVATED; // only "corner" bits need to be transformed ++ ++ /* reflect agains X axis before rotating */ ++ if (transformation & DTR_REFLECTION_BIT) { ++ /* reflect by swapping odd and even bits (the numbers are bit positions): ++ * [N] 3/ 2/ ++ * [W] [E] 0/2 --reflect-against-x-axis--> 1/3 ++ * [S] /1 /0 ++ * SLOPE_W (bit 0) needs to be swapped with SLOPE_S (bit 1) ++ * SLOPE_E (bit 2) needs to be swapped with SLOPE_N (bit 3) */ ++ s = SwapOddEvenBits(s); ++ } ++ ++ /* rotate */ ++ s = (Slope)((s | (s << 4)) >> (transformation & DTR_ROTATION_MASK)) & SLOPE_ELEVATED; ++ ++ return s | steep_bit; ++} ++ ++/** + * Tests if a specific slope has exactly three corners raised. + * + * @param s The #Slope +diff --git a/src/station.cpp b/src/station.cpp +index 456262d..593096c 100644 +--- a/src/station.cpp ++++ b/src/station.cpp +@@ -24,6 +24,7 @@ + #include "roadstop_base.h" + #include "industry.h" + #include "core/random_func.hpp" ++#include "overlay_cmd.h" + #include "linkgraph/linkgraph.h" + #include "linkgraph/linkgraphschedule.h" + +@@ -132,6 +133,10 @@ Station::~Station() + InvalidateWindowData(WC_STATION_LIST, this->owner, 0); + } + ++ if (Overlays::Instance()->HasStation(Station::Get(this->index))) { ++ Overlays::Instance()->ToggleStation(Station::Get(this->index)); ++ }; ++ + DeleteWindowById(WC_STATION_VIEW, index); + + /* Now delete all orders that go to the station */ +@@ -147,6 +152,25 @@ Station::~Station() + CargoPacket::InvalidateAllFrom(this->index); + } + ++/** ++ * Evaluate if a tile is in station catchment area. ++ * @param ti the tile info ++ * @param type the catchment type of the station ++ * @return true/false if the tile is in catchment ++ */ ++bool Station::IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) const ++{ ++ switch (type) { ++ case ACCEPTANCE: ++ return this->rect.PtInExtendedRect(TileX(ti->tile),TileY(ti->tile),this->GetCatchmentRadius()); ++ case PRODUCTION: ++ return this->catchment.IsTileInCatchment(ti->tile); ++ case INDUSTRY: ++ return false; ++ default: ++ NOT_REACHED(); ++ } ++} + + /** + * Invalidating of the JoinStation window has to be done +@@ -227,6 +251,23 @@ void Station::MarkTilesDirty(bool cargo_change) const + } + } + ++/** ++ * Marks the acceptance tiles of the station as dirty. ++ * ++ * @ingroup dirty ++ */ ++void Station::MarkAcceptanceTilesDirty() const ++{ ++ Rect rec = this->GetCatchmentRect(); ++ TileIndex top_left = TileXY(rec.left, rec.top); ++ int width = rec.right - rec.left + 1; ++ int height = rec.bottom - rec.top + 1; ++ ++ TILE_AREA_LOOP(tile, TileArea(top_left, width, height) ) { ++ MarkTileDirtyByTile(tile); ++ } ++} ++ + /* virtual */ uint Station::GetPlatformLength(TileIndex tile) const + { + assert(this->TileBelongsToRailStation(tile)); +@@ -568,3 +609,88 @@ Money AirportMaintenanceCost(Owner owner) + /* 3 bits fraction for the maintenance cost factor. */ + return total_cost >> 3; + } ++ ++/************************************************************************/ ++/* StationCatchment implementation */ ++/************************************************************************/ ++ ++StationCatchment::StationCatchment() ++{ ++} ++ ++/** ++ * Determines whether a given point (x, y) is within the station catchment area ++ * @param tile TileIndex to test ++ * @return true if the point is within the station catchment area ++ */ ++bool StationCatchment::IsTileInCatchment(TileIndex tile) const ++{ ++ return this->catchmentTiles.find(tile) != this->catchmentTiles.end(); ++} ++ ++bool StationCatchment::IsEmpty() const ++{ ++ return this->catchmentTiles.empty(); ++} ++ ++void StationCatchment::BeforeAddTile(TileIndex tile, uint catchmentRadius) ++{ ++ int x = TileX(tile); ++ int y = TileY(tile); ++ TileIndex top_left = TileXY(max<int>(x - catchmentRadius,0),max<int>(y - catchmentRadius, 0)); ++ int w = min<int>(x + catchmentRadius, MapMaxX()) - TileX(top_left) + 1; ++ int h = min<int>(y + catchmentRadius, MapMaxY()) - TileY(top_left) + 1; ++ if (IsEmpty()) { ++ /* we are adding the first station tile */ ++ TILE_AREA_LOOP(t, TileArea(top_left, w, h) ) { ++ std::set<TileIndex> fromSet; ++ fromSet.insert(tile); ++ this->catchmentTiles[t] = fromSet; ++ } ++ } else { ++ TILE_AREA_LOOP(t, TileArea(top_left, w, h) ) { ++ std::map<TileIndex, std::set<TileIndex> >::iterator found = this->catchmentTiles.find(t); ++ if ( found == this->catchmentTiles.end()) { ++ std::set<TileIndex> fromSet; ++ fromSet.insert(tile); ++ this->catchmentTiles[t] = fromSet; ++ } else if ((*found).second.find(tile) == (*found).second.end()) { ++ (*found).second.insert(tile); ++ } ++ } ++ } ++} ++ ++void StationCatchment::BeforeAddRect(TileIndex tile, int w, int h, uint catchmentRadius) ++{ ++ TILE_AREA_LOOP(t, TileArea(tile, w, h) ) { ++ this->BeforeAddTile(t, catchmentRadius); ++ } ++} ++ ++void StationCatchment::AfterRemoveTile(TileIndex tile, uint catchmentRadius) ++{ ++ int x = TileX(tile); ++ int y = TileY(tile); ++ TileIndex top_left = TileXY(max<int>(x - catchmentRadius,0),max<int>(y - catchmentRadius, 0)); ++ int w = min<int>(x + catchmentRadius, MapMaxX()) - TileX(top_left) + 1; ++ int h = min<int>(y + catchmentRadius, MapMaxY()) - TileY(top_left) + 1; ++ TILE_AREA_LOOP(t, TileArea(top_left, w, h)) { ++ std::map<TileIndex, std::set<TileIndex> >::iterator found = this->catchmentTiles.find(t); ++ assert(found != this->catchmentTiles.end()); ++ std::set<TileIndex>::iterator stTileIter = (*found).second.find(tile); ++ assert(stTileIter != (*found).second.end()); ++ (*found).second.erase(stTileIter); ++ if ((*found).second.empty()) { ++ // tile t is no longer in StationCatchment ++ this->catchmentTiles.erase(found); ++ } ++ } ++} ++ ++void StationCatchment::AfterRemoveRect(TileIndex tile, int w, int h, uint catchmentRadius) ++{ ++ TILE_AREA_LOOP(t, TileArea(tile, w, h)) { ++ this->AfterRemoveTile(t, catchmentRadius); ++ } ++} +\ No newline at end of file +diff --git a/src/station_base.h b/src/station_base.h +index af4d206..f017f27 100644 +--- a/src/station_base.h ++++ b/src/station_base.h +@@ -19,7 +19,9 @@ + #include "industry_type.h" + #include "linkgraph/linkgraph_type.h" + #include "newgrf_storage.h" ++#include "core/smallvec_type.hpp" + #include <map> ++#include <set> + + typedef Pool<BaseStation, StationID, 32, 64000> StationPool; + extern StationPool _station_pool; +@@ -301,6 +303,25 @@ struct GoodsEntry { + } + }; + ++enum CatchmentType { ++ ACCEPTANCE = 0, ++ PRODUCTION = 1, ++ INDUSTRY = 2 ++}; ++ ++struct StationCatchment { ++ std::map<TileIndex, std::set<TileIndex> > catchmentTiles; ++public: ++ StationCatchment(); ++ void MakeEmpty(); ++ bool IsTileInCatchment(TileIndex tile) const; ++ bool IsEmpty() const; ++ void BeforeAddTile(TileIndex tile, uint catchmentRadius); ++ void BeforeAddRect(TileIndex tile, int w, int h, uint catchmentRadius); ++ void AfterRemoveTile(TileIndex tile, uint catchmentRadius); ++ void AfterRemoveRect(TileIndex tile, int w, int h, uint catchmentRadius); ++}; ++ + /** All airport-related information. Only valid if tile != INVALID_TILE. */ + struct Airport : public TileArea { + Airport() : TileArea(INVALID_TILE, 0, 0) {} +@@ -474,6 +495,8 @@ public: + + IndustryVector industries_near; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry() + ++ StationCatchment catchment; ++ + Station(TileIndex tile = INVALID_TILE); + ~Station(); + +@@ -489,6 +512,11 @@ public: + static void RecomputeIndustriesNearForAll(); + + uint GetCatchmentRadius() const; ++ ++ bool IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) const; ++ ++ void MarkAcceptanceTilesDirty() const; ++ + Rect GetCatchmentRect() const; + + /* virtual */ inline bool TileBelongsToRailStation(TileIndex tile) const +diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp +index eb90c29..2d62246 100644 +--- a/src/station_cmd.cpp ++++ b/src/station_cmd.cpp +@@ -13,6 +13,9 @@ + #include "aircraft.h" + #include "bridge_map.h" + #include "cmd_helper.h" ++#include "copypaste_cmd.h" ++#include "clipboard_func.h" ++#include "clipboard_gui.h" + #include "viewport_func.h" + #include "command_func.h" + #include "town.h" +@@ -42,6 +45,7 @@ + #include "waypoint_base.h" + #include "waypoint_func.h" + #include "pbs.h" ++#include "overlay_cmd.h" + #include "debug.h" + #include "core/random_func.hpp" + #include "company_base.h" +@@ -53,11 +57,17 @@ + #include "linkgraph/linkgraph_base.h" + #include "linkgraph/refresh.h" + #include "widgets/station_widget.h" ++#include "tilearea_func.h" + + #include "table/strings.h" + + #include "safeguards.h" + ++#include <deque> ++ ++int _station_cmd_gfx_to_paste = -1; ///< station graphics (#StationGfx) to be set on currently being pasted rail station tile, -1 to use default graphics ++int _station_cmd_specindex_to_paste = -1; ///< custom spec index to be used by currently being pasted rail station tile or waypoint part, -1 to generate new index ++ + /** + * Static instance of FlowStat::SharesMap. + * Note: This instance is created on task start. +@@ -93,19 +103,25 @@ bool IsHangar(TileIndex t) + * @param ta the area to search over + * @param closest_station the closest station found so far + * @param st to 'return' the found station ++ * @param station_mask if not INVALID_STATION, search for this exact station only and ignore other stations + * @return Succeeded command (if zero or one station found) or failed command (for two or more stations found). + */ + template <class T> +-CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st) ++CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st, byte radius = 1, StationID station_mask = INVALID_STATION) + { +- ta.tile -= TileDiffXY(1, 1); +- ta.w += 2; +- ta.h += 2; ++ int x = Clamp<int>(TileX(ta.tile) - radius, 0, MapMaxX()); ++ int y = Clamp<int>(TileY(ta.tile) - radius, 0, MapMaxY()); ++ ta.w = Clamp<int>(TileX(ta.tile) + ta.w + radius, x, MapMaxX()) - x; ++ ta.h = Clamp<int>(TileY(ta.tile) + ta.h + radius, y, MapMaxY()) - y; ++ ta.tile = TileXY(x, y); ++ ++ if (station_mask != INVALID_STATION && closest_station != station_mask) closest_station = INVALID_STATION; + + /* check around to see if there's any stations there */ + TILE_AREA_LOOP(tile_cur, ta) { + if (IsTileType(tile_cur, MP_STATION)) { + StationID t = GetStationIndex(tile_cur); ++ if (station_mask != INVALID_STATION && t != station_mask) continue; + if (!T::IsValidID(t)) continue; + + if (closest_station == INVALID_STATION) { +@@ -420,7 +436,7 @@ void Station::UpdateVirtCoord() + + SetDParam(0, this->index); + SetDParam(1, this->facilities); +- this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION); ++ this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION, STR_VIEWPORT_STATION); + + SetWindowDirty(WC_STATION_VIEW, this->index); + } +@@ -555,6 +571,126 @@ CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint3 + } + + /** ++ * Get the rate of cargo being produced around the tile (in a rectangle). ++ * @param tile Northtile of area ++ * @param w X extent of the area ++ * @param h Y extent of the area ++ * @param rad Search radius in addition to the given area ++ */ ++CargoArray GetProductionRateAroundTiles(TileIndex tile, int w, int h, int rad) ++{ ++ CargoArray production_rate; ++ ++ int x = TileX(tile); ++ int y = TileY(tile); ++ ++ /* expand the region by rad tiles on each side ++ * while making sure that we remain inside the board. */ ++ int x2 = min(x + w + rad, MapSizeX()); ++ int x1 = max(x - rad, 0); ++ ++ int y2 = min(y + h + rad, MapSizeY()); ++ int y1 = max(y - rad, 0); ++ ++ assert(x1 < x2); ++ assert(y1 < y2); ++ assert(w > 0); ++ assert(h > 0); ++ ++ TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1)); ++ ++ /* Loop over all tiles to get the produced cargo of ++ * everything except industries */ ++ TILE_AREA_LOOP(tile, ta) { ++ if (GetTileType(tile) == MP_HOUSE) { ++ if (!IsHouseCompleted(tile)) continue; ++ ++ const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile)); ++ ++ /* Use expected values to calculate supply forecasting since there is a random factor ++ * in the equation. ++ * E[x] = x1p1 + x2p2 + ... + xkpk ++ * random number ranges from 0 to 255. However, all the ones above population are dropped. ++ * All probabilities p1...pk are the same ( = 1 / 256 ) ++ * Thus, E[x] = (1 + 2 + ... + pop - 1) / 256 ++ */ ++ uint sum = 0; ++ for (uint i = 1; i < hs->population; i++) { ++ sum += i; ++ } ++ /* Bitshift to the right by 8 is from the above equation and 3 is ++ * to divide by 8. For details, look at TileLoop_Town() in town_cmd.cpp */ ++ uint amt = (sum >> 11) + 1; ++ if (EconomyIsInRecession()) amt = (amt + 1) >> 1; ++ production_rate[CT_PASSENGERS] += amt; ++ ++ sum = 0; ++ for (uint i = 1; i < hs->mail_generation; i++) { ++ sum += i; ++ } ++ amt = (sum >> 11) + 1; ++ if (EconomyIsInRecession()) amt = (amt + 1) >> 1; ++ production_rate[CT_MAIL] += amt; ++ } ++ } ++ ++ /* Loop over the industries. They produce cargo for ++ * anything that is within 'rad' from their bounding ++ * box. As such if you have e.g. a oil well the tile ++ * area loop might not hit an industry tile while ++ * the industry would produce cargo for the station. ++ */ ++ const Industry *i; ++ FOR_ALL_INDUSTRIES(i) { ++ if (!ta.Intersects(i->location)) continue; ++ ++ for (uint j = 0; j < lengthof(i->produced_cargo); j++) { ++ CargoID cargo = i->produced_cargo[j]; ++ ++ if (cargo != CT_INVALID) production_rate[cargo] += i->last_month_production[j]; ++ } ++ } ++ ++ return production_rate; ++} ++ ++/** ++ * Get the acceptance rate of cargoes around the tile. ++ * @param tile Center of the search area ++ * @param w X extent of area ++ * @param h Y extent of area ++ * @param rad Search radius in addition to given area ++ * @param always_accepted bitmask of cargo accepted by houses and headquarters; can be NULL ++ */ ++CargoArray GetAcceptanceRateAroundTiles(TileIndex tile, int w, int h, int rad) ++{ ++ CargoArray acceptance_rate; ++ ++ int x = TileX(tile); ++ int y = TileY(tile); ++ ++ /* expand the region by rad tiles on each side ++ * while making sure that we remain inside the board. */ ++ int x2 = min(x + w + rad, MapSizeX()); ++ int y2 = min(y + h + rad, MapSizeY()); ++ int x1 = max(x - rad, 0); ++ int y1 = max(y - rad, 0); ++ ++ assert(x1 < x2); ++ assert(y1 < y2); ++ assert(w > 0); ++ assert(h > 0); ++ ++ for (int yc = y1; yc != y2; yc++) { ++ for (int xc = x1; xc != x2; xc++) { ++ TileIndex tile = TileXY(xc, yc); ++ AddAcceptedCargo(tile, acceptance_rate, NULL); ++ } ++ } ++ ++ return acceptance_rate; ++} ++/** + * Update the acceptance for a station. + * @param st Station to update + * @param show_msg controls whether to display a message that acceptance was changed. +@@ -639,6 +775,7 @@ void UpdateStationAcceptance(Station *st, bool show_msg) + + /* redraw the station view since acceptance changed */ + SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_ACCEPT_RATING_LIST); ++ if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); + } + + static void UpdateStationSignCoord(BaseStation *st) +@@ -708,11 +845,16 @@ static CommandCost BuildStationPart(Station **st, DoCommandFlag flags, bool reus + static void DeleteStationIfEmpty(BaseStation *st) + { + if (!st->IsInUse()) { ++ if (Station::IsExpected(st)) Overlays::Instance()->RemoveStation((Station *)st); + st->delete_ctr = 0; + InvalidateWindowData(WC_STATION_LIST, st->owner, 0); + } + /* station remains but it probably lost some parts - station sign should stay in the station boundaries */ + UpdateStationSignCoord(st); ++ ++ if (Station::IsExpected(st)) { ++ MarkWholeScreenDirty(); ++ } + } + + CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags); +@@ -1081,24 +1223,24 @@ template <class T, StringID error_message> + CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st) + { + assert(*st == NULL); +- bool check_surrounding = true; +- +- if (_settings_game.station.adjacent_stations) { +- if (existing_station != INVALID_STATION) { +- if (adjacent && existing_station != station_to_join) { +- /* You can't build an adjacent station over the top of one that +- * already exists. */ +- return_cmd_error(error_message); +- } else { +- /* Extend the current station, and don't check whether it will +- * be near any other stations. */ +- *st = T::GetIfValid(existing_station); +- check_surrounding = (*st == NULL); +- } ++ bool check_surrounding = !adjacent || !_settings_game.station.adjacent_stations; ++ ++ if (existing_station != INVALID_STATION) { ++ if (station_to_join == INVALID_STATION) { ++ /* You can't build an adjacent station over the top of one that ++ * already exists. */ ++ if (adjacent) return_cmd_error(error_message); + } else { +- /* There's no station here. Don't check the tiles surrounding this +- * one if the company wanted to build an adjacent station. */ +- if (adjacent) check_surrounding = false; ++ /* You can't join to a station while building over the top of ++ * some other station. */ ++ if (existing_station != station_to_join) return_cmd_error(error_message); ++ } ++ ++ if (!adjacent && _settings_game.station.adjacent_stations) { ++ /* Extend the current station, and don't check whether it will ++ * be near any other stations. */ ++ *st = T::GetIfValid(existing_station); ++ check_surrounding = (*st == NULL); + } + } + +@@ -1108,8 +1250,23 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station + if (ret.Failed()) return ret; + } + ++ /* Fail if we would have to join to other station then we want. */ ++ if (*st != NULL && (station_to_join == INVALID_STATION ? adjacent : (*st)->index != station_to_join)) { ++ *st = NULL; ++ return_cmd_error(STR_ERROR_ADJOINS_OTHER_STATION); ++ } ++ + /* Distant join */ +- if (*st == NULL && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join); ++ if (*st == NULL && station_to_join != INVALID_STATION) { ++ /* Test if we are not breaking the distant-join rule. */ ++ if (!_settings_game.station.distant_join_stations && ( ++ check_surrounding || // surrounding already cheked? ++ GetStationAround(ta, INVALID_STATION, st, station_to_join).Failed() || // search surrounding tiles ++ *st == NULL)) { // station not found? ++ return_cmd_error(STR_ERROR_CAN_T_DISTANT_JOIN); ++ } ++ *st = T::GetIfValid(station_to_join); ++ } + + return CommandCost(); + } +@@ -1194,10 +1351,8 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 + + bool reuse = (station_to_join != NEW_STATION); + if (!reuse) station_to_join = INVALID_STATION; +- bool distant_join = (station_to_join != INVALID_STATION); +- +- if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; + ++ if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; + if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR; + + /* these values are those that will be stored in train_tile and station_platforms */ +@@ -1227,14 +1382,22 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 + + /* Check if we can allocate a custom stationspec to this station */ + const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); +- int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0); +- if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); ++ int specindex; ++ if ((flags & DC_PASTE) && (flags & DC_EXEC) && (_station_cmd_specindex_to_paste != -1)) { ++ assert(_station_cmd_specindex_to_paste == 0 || (_station_cmd_specindex_to_paste < st->num_specs && st->speclist[_station_cmd_specindex_to_paste].spec == statspec)); ++ specindex = _station_cmd_specindex_to_paste; ++ } else { ++ specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0); ++ if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); ++ } + + if (statspec != NULL) { + /* Perform NewStation checks */ + + /* Check if the station size is permitted */ +- if (HasBit(statspec->disallowed_platforms, min(numtracks - 1, 7)) || HasBit(statspec->disallowed_lengths, min(plat_len - 1, 7))) { ++ if (!(flags & DC_PASTE) && ( ++ HasBit(statspec->disallowed_platforms, min(numtracks - 1, 7)) || ++ HasBit(statspec->disallowed_lengths, min(plat_len - 1, 7)))) { + return CMD_ERROR; + } + +@@ -1255,6 +1418,7 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 + st->AddFacility(FACIL_TRAIN, new_location.tile); + + st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY); ++ st->catchment.BeforeAddRect(tile_org, w_org, h_org, CA_TRAIN); + + if (statspec != NULL) { + /* Include this station spec's animation trigger bitmask +@@ -1309,7 +1473,9 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 + if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++; + c->infrastructure.station++; + +- if (statspec != NULL) { ++ if ((flags & DC_PASTE) && (_station_cmd_gfx_to_paste != -1)) { ++ SetStationGfx(tile, _station_cmd_gfx_to_paste); ++ } else if (statspec != NULL) { + /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */ + uint32 platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false); + +@@ -1322,7 +1488,9 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 + ErrorUnknownCallbackResult(statspec->grf_prop.grffile->grfid, CBID_STATION_TILE_LAYOUT, callback); + } + } ++ } + ++ if (statspec != NULL) { + /* Trigger station animation -- after building? */ + TriggerStationAnimation(st, tile, SAT_BUILT); + } +@@ -1500,6 +1668,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector<T *, 4> &affected + Track track = GetRailStationTrack(tile); + Owner owner = GetTileOwner(tile); + RailType rt = GetRailType(tile); ++ if (Station::IsExpected(st)) ((Station *)st)->catchment.AfterRemoveTile(tile, CA_TRAIN); + Train *v = NULL; + + if (HasStationReservation(tile)) { +@@ -1520,6 +1689,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector<T *, 4> &affected + DoClearSquare(tile); + DeleteNewGRFInspectWindow(GSF_STATIONS, tile); + if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt); ++ if (Station::IsExpected(st) && Overlays::Instance()->HasStation((Station *)st)) ((Station *)st)->MarkAcceptanceTilesDirty(); + Company::Get(owner)->infrastructure.station--; + DirtyCompanyInfrastructureWindows(owner); + +@@ -1592,6 +1762,7 @@ CommandCost CmdRemoveFromRailStation(TileIndex start, DoCommandFlag flags, uint3 + Station *st = *stp; + + if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_TRAINS); ++ if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); + st->MarkTilesDirty(false); + st->RecomputeIndustriesNear(); + } +@@ -1758,7 +1929,6 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin + StationID station_to_join = GB(p2, 16, 16); + bool reuse = (station_to_join != NEW_STATION); + if (!reuse) station_to_join = INVALID_STATION; +- bool distant_join = (station_to_join != INVALID_STATION); + + uint8 width = (uint8)GB(p1, 0, 8); + uint8 lenght = (uint8)GB(p1, 8, 8); +@@ -1772,7 +1942,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin + + TileArea roadstop_area(tile, width, lenght); + +- if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; ++ if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; + + if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR; + +@@ -1837,6 +2007,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin + st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, cur_tile); + + st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY); ++ st->catchment.BeforeAddTile(cur_tile, type ? CA_TRUCK : CA_BUS); + + RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS; + if (is_drive_through) { +@@ -1966,6 +2137,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) + DoClearSquare(tile); + } + ++ if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); + SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_ROADVEHS); + delete cur_stop; + +@@ -1979,6 +2151,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) + } + + st->rect.AfterRemoveTile(st, tile); ++ st->catchment.AfterRemoveTile(tile, is_truck ? CA_TRUCK : CA_BUS); + + st->UpdateVirtCoord(); + st->RecomputeIndustriesNear(); +@@ -2187,11 +2360,10 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint + StationID station_to_join = GB(p2, 16, 16); + bool reuse = (station_to_join != NEW_STATION); + if (!reuse) station_to_join = INVALID_STATION; +- bool distant_join = (station_to_join != INVALID_STATION); + byte airport_type = GB(p1, 0, 8); + byte layout = GB(p1, 8, 8); + +- if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; ++ if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; + + if (airport_type >= NUM_AIRPORTS) return CMD_ERROR; + +@@ -2252,9 +2424,6 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint + ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), airport_area, &st); + if (ret.Failed()) return ret; + +- /* Distant join */ +- if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); +- + ret = BuildStationPart(&st, flags, reuse, airport_area, (GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT); + if (ret.Failed()) return ret; + +@@ -2282,6 +2451,7 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint + MakeAirport(iter, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID); + SetStationTileRandomBits(iter, GB(Random(), 0, 4)); + st->airport.Add(iter); ++ st->catchment.BeforeAddTile(iter, as->catchment); + + if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(iter); + } +@@ -2355,8 +2525,10 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) + cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); + + if (flags & DC_EXEC) { ++ const AirportSpec *as = st->airport.GetSpec(); + if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); + DeleteAnimatedTile(tile_cur); ++ st->catchment.AfterRemoveTile(tile_cur, as->catchment); + DoClearSquare(tile_cur); + DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); + } +@@ -2466,9 +2638,8 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + StationID station_to_join = GB(p2, 16, 16); + bool reuse = (station_to_join != NEW_STATION); + if (!reuse) station_to_join = INVALID_STATION; +- bool distant_join = (station_to_join != INVALID_STATION); + +- if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; ++ if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; + + DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile)); + if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); +@@ -2487,21 +2658,30 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + + TileIndex tile_cur = tile + TileOffsByDiagDir(direction); + +- if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { +- return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ /* Get the water class of the water tile before it is cleared. */ ++ WaterClass wc; ++ /* When pasting a dock, there may be no water yet (a canal will be placed when DC_EXE'ing). ++ * Ignore that there is no water so we can calculate the cost more precisely. */ ++ if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { ++ wc = WATER_CLASS_INVALID; ++ } else { ++ if (!IsTileType(tile_cur, MP_WATER)) { ++ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ } ++ wc = GetWaterClass(tile_cur); + } + ++ if (!IsTileFlat(tile_cur)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + if (IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + +- /* Get the water class of the water tile before it is cleared.*/ +- WaterClass wc = GetWaterClass(tile_cur); +- + ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret.Failed()) return ret; + +- tile_cur += TileOffsByDiagDir(direction); +- if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { +- return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ if (!(flags & DC_PASTE)) { ++ tile_cur += TileOffsByDiagDir(direction); ++ if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { ++ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ } + } + + TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]), +@@ -2512,9 +2692,6 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0), dock_area, &st); + if (ret.Failed()) return ret; + +- /* Distant join */ +- if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); +- + ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK); + if (ret.Failed()) return ret; + +@@ -2525,6 +2702,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + st->AddFacility(FACIL_DOCK, tile); + + st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY); ++ st->catchment.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, CA_DOCK); + + /* If the water part of the dock is on a canal, update infrastructure counts. + * This is needed as we've unconditionally cleared that tile before. */ +@@ -2534,6 +2712,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + Company::Get(st->owner)->infrastructure.station += 2; + DirtyCompanyInfrastructureWindows(st->owner); + ++ assert(wc != WATER_CLASS_INVALID); + MakeDock(tile, st->owner, st->index, direction, wc); + + st->UpdateVirtCoord(); +@@ -2569,10 +2748,13 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags) + if (ret.Failed()) return ret; + + if (flags & DC_EXEC) { ++ st->catchment.AfterRemoveTile(tile1, CA_DOCK); ++ st->catchment.AfterRemoveTile(tile2, CA_DOCK); + DoClearSquare(tile1); + MarkTileDirtyByTile(tile1); + MakeWaterKeepingClass(tile2, st->owner); + ++ if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); + st->rect.AfterRemoveTile(st, tile1); + st->rect.AfterRemoveTile(st, tile2); + +@@ -2897,6 +3079,8 @@ draw_default_foundation: + } + } + ++ DrawOverlay(ti, MP_STATION); ++ + if (HasStationRail(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti); + + if (HasBit(roadtypes, ROADTYPE_TRAM)) { +@@ -3637,6 +3821,615 @@ void StationMonthlyLoop() + } + + ++/** Maps station IDs and platform layouts (custom spec indices) ++ * from sources to their destinations while copy-pasting stations. */ ++struct StationIDPasteMap { ++private: ++ typedef SmallVector<int16, 4> SpecIndexMap; ++ ++ struct StationPasteID { ++ StationID dest_sid; ///< ID of the destination station ++ bool overbuilding; ///< whether the destination is being overbuilt ++ SpecIndexMap specindices; ///< maps custom spec indices (source -> destination) ++ }; ++ ++ typedef std::map<StationID, StationPasteID> PasteMap; ++ ++ PasteMap map; ++ ++public: ++ /** ++ * Get the destination station ID assigned to a given source station ID. ++ * @param src_sid the source ID ++ * @param overbuilding (out) whether the destination station has been overbuilt (see #SetOverbuilding) ++ * @return the destination ID or #NEW_STATION if the destination hasn't been chosen yet. ++ * @see ConfirmIDForStation ++ */ ++ StationID QueryIDForStation(StationID src_sid, bool *overbuilding) const ++ { ++ assert(src_sid != INVALID_STATION); ++ ++ PasteMap::const_iterator it = this->map.find(src_sid); ++ if (it != this->map.end()) { ++ *overbuilding = it->second.overbuilding; ++ return it->second.dest_sid; ++ } else { ++ *overbuilding = false; ++ return NEW_STATION; ++ } ++ } ++ ++ /** ++ * Assign destination station ID to a source station ID. ++ * @param src_sid the source ID ++ * @param src_sid the destination ID (after buildng) ++ * @pre the choice cannot be changed, only the same args can be passed more then once ++ * @see QueryIDForStation ++ */ ++ void ConfirmIDForStation(StationID src_sid, StationID dst_sid) ++ { ++ assert(src_sid != INVALID_STATION && dst_sid != INVALID_STATION && dst_sid != NEW_STATION); ++ ++ PasteMap::iterator it = this->map.find(src_sid); ++ if (it != this->map.end()) { ++ assert(it->second.dest_sid == dst_sid); ++ } else { ++ StationPasteID &item = this->map[src_sid]; ++ item.dest_sid = src_sid; ++ item.dest_sid = dst_sid; ++ item.overbuilding = false; ++ } ++ } ++ ++ /** ++ * Get the destination spec index assigned to a given source spec index. ++ * @param src_sid ID of the source station ++ * @param src_specindex spec index of the source station part ++ * @return the destination spec index or -1 if the destination hasn't been chosen yet. ++ * @see ConfirmSpecIndexForStationPart ++ */ ++ int QuerySpecIndexForStationPart(StationID src_sid, byte src_specindex) const ++ { ++ assert(src_sid != INVALID_STATION); ++ if (src_specindex == 0) return 0; // reserved for the default station class ++ ++ PasteMap::const_iterator it = this->map.find(src_sid); ++ if (it != this->map.end() && src_specindex < it->second.specindices.Length()) { ++ return it->second.specindices[src_specindex]; ++ } ++ return -1; // allocate new specindex ++ } ++ ++ /** ++ * Assign destination spec index to a source spec index. ++ * @param src_sid ID of the source station ++ * @param src_specindex spec index of the source station part ++ * @param dst_specindex spec index of the destination station part (after buildng) ++ * @pre the choice cannot be changed, only the same args can be passed more then once ++ * @pre the destination ID of this station must be already set (see #ConfirmIDForStation) ++ * @see QuerySpecIndexForStationPart ++ */ ++ void ConfirmSpecIndexForStationPart(StationID src_sid, byte src_specindex, byte dst_specindex) ++ { ++ if (src_specindex == 0) return; ++ PasteMap::iterator it = this->map.find(src_sid); ++ assert(it != this->map.end()); ++ ++ SpecIndexMap &indices = it->second.specindices; ++ if (indices.Length() == 0) *indices.Append() = 0; // default station ++ while (src_specindex >= indices.Length()) *indices.Append() = -1; // placeholders ++ ++ assert(indices[src_specindex] == -1 || indices[src_specindex] == dst_specindex); ++ indices[src_specindex] = dst_specindex; ++ } ++ ++ /** ++ * Mark that a certain station overbuilt it's destination station. ++ * @param src_sid the source ID of the overbuilding station ++ * @pre the destination ID of this station must be already set (see #ConfirmIDForStation) ++ */ ++ void SetOverbuilding(StationID src_sid) ++ { ++ PasteMap::iterator it = this->map.find(src_sid); ++ assert(it != this->map.end()); ++ it->second.overbuilding = true; ++ } ++}; ++ ++struct StationPartPasteInfo { ++ GenericTileIndex src_tile; ///< source tile ++ TileIndex dst_tile; ///< destination tile ++ StationID adjoining_station; ///< the station that is being overbuilt (#MULTIPLE_STATIONS if many), the adjacent station if not overbuilding (#INVALID_STATION if none, #MULTIPLE_STATIONS if many) ++ bool overbuilding; ///< whether there is a station on the destination tile already ++}; ++typedef std::deque<StationPartPasteInfo> StationPartPasteQueue; ++ ++static const StationID MULTIPLE_STATIONS = NEW_STATION; ++ ++static StationIDPasteMap _copy_paste_station_id_paste_map; ///< map of station IDs and specindices currently being pasted onto the main map ++static StationPartPasteQueue _copy_paste_station_part_paste_queue; ///< station parts queued to be pasted onto the main map ++ClipboardStationsBuilder _clipboard_stations_builder; ///< for collecting metadata about stations (sizes, facilities, ...) while copying them to the clipboard ++ ++static void GetSpecFromGenericStation(GenericTileIndex tile, StationClassID *stat_class, byte *stat_type) ++{ ++ assert(HasStationTileRail(tile)); ++ ++ byte specindex = GetCustomStationSpecIndex(tile); ++ if (specindex == 0) { ++ *stat_class = IsRailWaypointTile(tile) ? STAT_CLASS_WAYP : STAT_CLASS_DFLT; ++ *stat_type = 0; ++ } else if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ const StationSpecList &statspec = BaseStation::GetByTile(t)->speclist[specindex]; ++ *stat_class = statspec.spec->cls_id; ++ int stat_type_int; ++ StationClass::GetByGrf(statspec.grfid, statspec.localidx, &stat_type_int); ++ *stat_type = (byte)stat_type_int; ++ } else { ++ const ClipboardStation::Spec &statspec = ClipboardStation::GetByTile(tile)->speclist[specindex]; ++ *stat_class = statspec.stat_class; ++ *stat_type = statspec.stat_type; ++ } ++} ++ ++static void GetTypeLayoutFromGenericAirport(GenericTileIndex tile, AirportTypes *type, byte *layout) ++{ ++ if (IsMainMapTile(tile)) { ++ Station *st = Station::GetByTile(AsMainMapTile(tile)); ++ *type = (AirportTypes)st->airport.type; ++ *layout = st->airport.layout; ++ } else { ++ ClipboardStation *st = ClipboardStation::GetByTile(tile); ++ *type = st->airport.type; ++ *layout = st->airport.layout; ++ } ++} ++ ++/** ++ * Test a given station tile if there is any contented to be copied from it. ++ * ++ * Stations are copy/pasted part by part, where a part is a minimal station piece that we can move ++ * e.g. a single rail station tile or a whole airport. The function writes bounds of that piece to ++ * location pointed by \c station_part_area but only once per a piece - when a cartin tile is being ++ * tested: ++ * - in case of docks, it's the tile with land section ++ * - in other cases, it's the most norhern tile ++ * For the rest of tiles the function still returns \c true but writes "invalid" area. ++ * ++ * If the funtion returns \c false, \c object_rect remains unchanged. ++ * ++ * @param tile the tile to test ++ * @param src_area the area we are copying ++ * @param mode copy-paste mode ++ * @param station_part_area (out, may be NULL) bounds of the station part or "invalid" area, depending on which tile was given ++ * @param company the #Company to check ownership against to ++ * @param preview (out, may be NULL) information on how to higlight preview of the tile ++ * @return whether this tile needs to be copy-pasted ++ */ ++bool TestStationTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *station_part_area, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) ++{ ++ if (preview != NULL) MemSetT(preview, 0); ++ if (!(mode & CPM_WITH_STATIONS)) return false; ++ ++ StationType type = GetStationType(tile); ++ if (type != STATION_BUOY && IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; ++ ++ switch (type) { ++ case STATION_WAYPOINT: ++ case STATION_RAIL: ++ if (!(mode & CPM_WITH_RAIL_TRANSPORT)) return false; ++ if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); ++ if (preview != NULL) preview->highlight_track_bits = GetRailStationTrackBits(tile); ++ break; ++ ++ case STATION_AIRPORT: ++ if (!(mode & CPM_WITH_AIR_TRANSPORT)) return false; ++ if (IsMainMapTile(tile) || station_part_area != NULL) { ++ GenericTileArea area; ++ if (IsMainMapTile(tile)) { ++ area = Station::GetByTile(AsMainMapTile(tile))->airport; ++ if (!src_area.Contains(area)) return false; ++ } else { ++ area = GenericTileArea(ClipboardStation::GetByTile(tile)->airport, MapOf(tile)); ++ } ++ ++ if (station_part_area != NULL) { ++ if (tile != area.tile) { ++ *station_part_area = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); ++ } else { ++ *station_part_area = area; ++ } ++ } ++ } ++ break; ++ ++ case STATION_TRUCK: ++ case STATION_BUS: ++ if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; ++ if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); ++ break; ++ ++ case STATION_OILRIG: ++ return false; ++ ++ case STATION_DOCK: { ++ if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; ++ if (IsMainMapTile(tile) || station_part_area != NULL) { ++ GenericTileIndex other_tile = GetOtherDockTile(tile); ++ if (IsMainMapTile(tile) && !src_area.Contains(other_tile)) return false; ++ if (station_part_area != NULL) *station_part_area = IsLandDockSection(tile) ? GenericTileArea(tile, other_tile) : GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); ++ } ++ break; ++ } ++ ++ case STATION_BUOY: ++ if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; ++ if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); ++ break; ++ ++ default: ++ return false; ++ } ++ ++ if (preview != NULL) preview->highlight_tile_rect = true; ++ return true; ++} ++ ++static void TransformRailStation(StationGfx *gfx, DirTransformation transformation, StationClassID *stat_class, byte *stat_type) ++{ ++ if (transformation == DTR_IDENTITY) return; ++ ++ if (*stat_class != STAT_CLASS_DFLT && *stat_class != STAT_CLASS_WAYP && ++ IsCustomLayoutStation(StationClass::Get(*stat_class)->GetSpec(*stat_type))) { ++ /* convert to a default station if we dont know how to transform station graphics */ ++ *stat_class = STAT_CLASS_DFLT; ++ *stat_type = 0; ++ *gfx = TransformAxis((Axis)(*gfx & 1), transformation); ++ return; ++ } ++ ++ if (*stat_class == STAT_CLASS_WAYP || *gfx < 4) { ++ /* change axis */ ++ *gfx ^= TransformAxis(AXIS_X, transformation); ++ } else { ++ /* change direction */ ++ DiagDirection dir = (DiagDirection)((*gfx - 1) & 0x3); // down the roof ++ dir = TransformDiagDir(dir, transformation); ++ *gfx = ((dir + 1) & 0x3) + 4; ++ } ++} ++ ++static bool IsAirportTransformable(AirportTypes type, DirTransformation dtr) ++{ ++ if (type >= NEW_AIRPORT_OFFSET) return dtr == DTR_IDENTITY; ++ if (TransformAxis(AXIS_X, dtr) == AXIS_X) return true; ++ const AirportSpec *as = AirportSpec::Get(type); ++ return as->size_x == as->size_y; ++} ++ ++static const StringID _paste_station_errors[] = { ++ STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, // STATION_RAIL ++ STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, // STATION_AIRPORT ++ INVALID_STRING_ID, // STATION_TRUCK ++ INVALID_STRING_ID, // STATION_BUS ++ INVALID_STRING_ID, // STATION_OILRIG ++ STR_ERROR_CAN_T_BUILD_DOCK_HERE, // STATION_DOCK ++ STR_ERROR_CAN_T_POSITION_BUOY_HERE, // STATION_BUOY ++ STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, // STATION_WAYPOINT ++}; ++ ++static const StringID _paste_truck_bus_station_errors[][ROADTYPE_END] = { ++ /* ROADTYPE_ROAD ROADTYPE_TRAM */ ++ { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION}, // ROADSTOP_BUS ++ { STR_ERROR_CAN_T_BUILD_TRUCK_STATION, STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION }, // ROADSTOP_TRUCK ++}; ++ ++static StringID GetPasteStationErrorMsg(GenericTileIndex src_tile) ++{ ++ StationType type = GetStationType(src_tile); ++ if (type != STATION_TRUCK && type != STATION_BUS) { ++ assert((size_t)type < lengthof(_paste_station_errors)); ++ return _paste_station_errors[type]; ++ } else { ++ RoadStopType rst = (RoadStopType)(STATION_BUS - type); ++ RoadType rt = (RoadType)FindLastBit(GetRoadTypes(src_tile)); ++ assert((size_t)rst < lengthof(_paste_truck_bus_station_errors)); ++ assert((size_t)rt < lengthof(_paste_truck_bus_station_errors[0])); ++ return _paste_truck_bus_station_errors[rst][rt]; ++ } ++} ++ ++static void CopyPastePlaceRailStation(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent) ++{ ++ if (IsMainMapTile(tile)) { ++ _station_cmd_gfx_to_paste = gfx; ++ _station_cmd_specindex_to_paste = specindex; ++ uint32 p1 = 0; ++ SB(p1, 0, 4, rt); ++ SB(p1, 4, 1, axis); ++ SB(p1, 8, 8, 1); // number of tracks ++ SB(p1, 16, 8, 1); // platform length ++ SB(p1, 24, 1, adjacent); ++ uint32 p2 = 0; ++ SB(p2, 0, 8, stat_class); ++ SB(p2, 8, 8, stat_type); ++ SB(p2, 16, 16, sid); ++ _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION)); ++ } else { ++ MakeRailStation(tile, OWNER_NONE, sid, axis, gfx & ~1, rt); ++ assert(IsInsideMM(specindex, 0, MAX_UVALUE(byte) + 1)); ++ SetCustomStationSpecIndex(tile, (byte)specindex); ++ _clipboard_stations_builder.AddRailPart(sid, stat_class, stat_type, (byte)specindex); ++ } ++} ++ ++static void CopyPastePlaceAirport(GenericTileIndex tile, StationID sid, AirportTypes type, byte layout, bool adjacent) ++{ ++ if (IsMainMapTile(tile)) { ++ uint32 p1 = 0; ++ SB(p1, 0, 8, type); ++ SB(p1, 8, 8, layout); ++ uint32 p2 = 0; ++ SB(p2, 0, 1, adjacent); ++ SB(p2, 16, 16, sid); ++ _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE)); ++ } else { ++ for (AirportTileTableIteratorT<true> iter(AirportSpec::Get(type)->table[layout], tile); IsValidTileIndex(iter); ++iter) { ++ MakeAirport(iter, OWNER_NONE, sid, 0, WATER_CLASS_INVALID); ++ } ++ _clipboard_stations_builder.AddAirportPart(sid, IndexOf(tile), type, layout); ++ } ++} ++ ++static void CopyPastePlaceRoadStop(GenericTileIndex tile, StationID sid, bool drive_through, RoadStopType rst, RoadTypes rt, DiagDirection dir, bool adjacent) ++{ ++ if (drive_through) dir = (DiagDirection)DiagDirToAxis(dir); ++ ++ if (IsMainMapTile(tile)) { ++ uint32 p1 = 0; ++ SB(p1, 0 , 8, 1); // width ++ SB(p1, 8 , 8, 1); // height ++ uint32 p2 = 0; ++ SB(p2, 0 , 1, rst); ++ SB(p2, 1 , 1, drive_through); ++ SB(p2, 2 , 2, rt); ++ SB(p2, 5 , 1, adjacent); // ++ SB(p2, 6 , 2, dir); ++ SB(p2, 16 , 16, sid); ++ _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_ROAD_STOP | CMD_MSG(STR_ERROR_CAN_T_BUILD_BUS_STATION + rst)); ++ } else { ++ if (drive_through) { ++ MakeDriveThroughRoadStop(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, sid, rst, rt, DiagDirToAxis(dir)); ++ } else { ++ MakeRoadStop(tile, OWNER_NONE, sid, rst, rt, dir); ++ } ++ _clipboard_stations_builder.AddPart(sid); ++ } ++} ++ ++static void CopyPastePlaceDock(GenericTileIndex tile, StationID sid, DiagDirection dir, WaterClass wc, bool adjacent) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ TileIndex t_lower = TileAddByDiagDir(t, dir); ++ if (!HasTileWaterGround(t_lower)) { ++ CopyPastePlaceCannal(GenericTileIndex(t_lower)); ++ if (_current_pasting->last_result.Failed()) return; ++ } ++ ++ uint32 p1 = 0; ++ SB(p1, 0, 1, adjacent); ++ uint32 p2 = 0; ++ SB(p2, 16 , 16, sid); ++ _current_pasting->DoCommand(t, p1, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE)); ++ } else { ++ MakeDock(tile, OWNER_NONE, sid, dir, wc); ++ _clipboard_stations_builder.AddPart(sid); ++ } ++} ++ ++static void CopyPasteStation(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste, StationID dst_sid, int dst_specindex, bool adjacent = false) ++{ ++ StationType station_type = GetStationType(src_tile); ++ switch (station_type) { ++ case STATION_RAIL: ++ case STATION_WAYPOINT: { ++ StationGfx gfx = GetStationGfx(src_tile); ++ Axis axis = TransformAxis(GetRailStationAxis(src_tile), copy_paste.transformation); ++ RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); ++ StationClassID stat_class; ++ byte stat_type; ++ GetSpecFromGenericStation(src_tile, &stat_class, &stat_type); ++ ++ TransformRailStation(&gfx, copy_paste.transformation, &stat_class, &stat_type); ++ ++ switch (station_type) { ++ case STATION_RAIL: CopyPastePlaceRailStation(dst_tile, dst_sid, axis, railtype, gfx, stat_class, stat_type, dst_specindex, adjacent); break; ++ case STATION_WAYPOINT: CopyPastePlaceRailWaypoint(dst_tile, dst_sid, axis, railtype, gfx, stat_class, stat_type, dst_specindex, adjacent); break; ++ default: NOT_REACHED(); ++ } ++ ++ break; ++ } ++ ++ case STATION_AIRPORT: { ++ AirportTypes type; ++ byte layout; ++ GetTypeLayoutFromGenericAirport(src_tile, &type, &layout); ++ if (!IsAirportTransformable(type, copy_paste.transformation)) { ++ assert(IsMainMapTile(dst_tile)); // copying should be always successful ++ _current_pasting->CollectError(AsMainMapTile(dst_tile), STR_ERROR_INAPPLICABLE_TRANSFORMATION, STR_ERROR_CAN_T_BUILD_AIRPORT_HERE); ++ return; ++ } ++ CopyPastePlaceAirport(dst_tile, dst_sid, type, layout, adjacent); ++ break; ++ } ++ ++ case STATION_TRUCK: ++ case STATION_BUS: ++ CopyPastePlaceRoadStop(dst_tile, dst_sid, IsDriveThroughStopTile(src_tile), GetRoadStopType(src_tile), ++ GetRoadTypes(src_tile), TransformDiagDir(GetRoadStopDir(src_tile), copy_paste.transformation), adjacent); ++ break; ++ ++ case STATION_DOCK: CopyPastePlaceDock(dst_tile, dst_sid, TransformDiagDir(GetDockDirection(src_tile), copy_paste.transformation), GetWaterClass(src_tile), adjacent); break; ++ case STATION_BUOY: CopyPastePlaceBuoy(dst_tile, dst_sid, GetWaterClass(src_tile)); break; ++ ++ default: ++ NOT_REACHED(); ++ } ++} ++ ++void CopyPasteTile_Station(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) ++{ ++ GenericTileArea part_src_rect; ++ if (!TestStationTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &part_src_rect)) return; ++ if (part_src_rect.tile.index == INVALID_TILE_INDEX) return; // copy this part only once ++ ++ if (!IsMainMapTile(dst_tile)) { ++ /* When copying to the clipboard, keep original station ID's and specindices. */ ++ int specindex = HasStationTileRail(src_tile) ? GetCustomStationSpecIndex(src_tile) : -1; ++ CopyPasteStation(src_tile, dst_tile, copy_paste, GetStationIndex(src_tile), specindex); ++ } else { ++ /* Calculate the destination area for current station part. */ ++ TileIndex t = copy_paste.src_area.ReverseTransformTile(src_tile, AsMainMapTile(dst_tile), copy_paste.transformation); // transformed northern tile of the copy_paste.src_area ++ t = copy_paste.src_area.TransformTile(part_src_rect.tile, t, copy_paste.transformation); // transformed northern tile of the part_src_rect ++ t = part_src_rect.ReverseTransformedNorth(t, copy_paste.transformation); // northern tile of the transformed part_src_rect ++ TileArea part_dst_rect = TransformTileArea(part_src_rect, t, copy_paste.transformation); // transformed part_src_rect ++ ++ /* Terraform tiles */ ++ if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { ++ CopyPasteHeights(part_src_rect, GenericTileIndex(part_dst_rect.tile), copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ } ++ ++ StationType station_type = GetStationType(src_tile); ++ if ((station_type == STATION_BUOY) || !(_current_pasting->dc_flags & DC_EXEC)) { ++ /* Paste the part */ ++ CopyPasteStation(src_tile, dst_tile, copy_paste, NEW_STATION, -1, false); ++ } else { ++ /* Queue for later pasting. We must find station candidates to to be joined to before ++ * we try to build any station parts to avoid joining pasted stations together. */ ++ StationPartPasteInfo info = { ++ src_tile, ++ AsMainMapTile(dst_tile), ++ INVALID_STATION, ++ false ++ }; ++ /* Station parts that overbuild other stations go to the front of the queue, ++ * they will be tried firstly. */ ++ BaseStation *st = NULL; ++ CommandCost ret = (station_type != STATION_WAYPOINT) ? ++ GetStationAround(part_dst_rect, INVALID_STATION, (Station**)&st, 0) : ++ GetStationAround(part_dst_rect, INVALID_STATION, (Waypoint**)&st, 0); ++ if (ret.Failed() || st != NULL) { ++ info.adjoining_station = ret.Failed() ? MULTIPLE_STATIONS : st->index; ++ info.overbuilding = true; ++ _copy_paste_station_part_paste_queue.push_front(info); ++ } else { ++ /* Joining parts go behind overbuilding parts. They are next to try. */ ++ ret = (station_type != STATION_WAYPOINT) ? ++ GetStationAround(part_dst_rect, INVALID_STATION, (Station**)&st, 1) : ++ GetStationAround(part_dst_rect, INVALID_STATION, (Waypoint**)&st, 1); ++ if (ret.Failed() || st != NULL) { ++ info.adjoining_station = ret.Failed() ? MULTIPLE_STATIONS : st->index; ++ StationPartPasteQueue::iterator pos = _copy_paste_station_part_paste_queue.begin(); ++ while (pos < _copy_paste_station_part_paste_queue.end() && pos->overbuilding) pos++; ++ _copy_paste_station_part_paste_queue.insert(pos, info); ++ } else { ++ /* Non-joining parts go to the back. */ ++ _copy_paste_station_part_paste_queue.push_back(info); ++ } ++ } ++ } ++ } ++} ++ ++static void ProcessStationPartPasteQueue(const CopyPasteParams ©_paste) ++{ ++ if (_copy_paste_station_part_paste_queue.empty()) return; ++ ++ for (;;) { ++ uint orig_queue_size = _copy_paste_station_part_paste_queue.size(); ++ for (uint i = 0; i < orig_queue_size; i++) { ++ if (IsPastingInterrupted()) break; ++ StationPartPasteInfo part = _copy_paste_station_part_paste_queue.front(); ++ _copy_paste_station_part_paste_queue.pop_front(); ++ ++ bool overbuilding = false; // did current station overbuild some other station already? ++ StationID src_sid = GetStationIndex(part.src_tile); ++ StationID dst_sid = _copy_paste_station_id_paste_map.QueryIDForStation(src_sid, &overbuilding); ++ ++ int src_specindex = -1; ++ int dst_specindex = -1; ++ if (HasStationTileRail(part.src_tile)) { ++ src_specindex = GetCustomStationSpecIndex(part.src_tile); ++ dst_specindex = _copy_paste_station_id_paste_map.QuerySpecIndexForStationPart(src_sid, src_specindex); ++ } ++ ++ if (part.overbuilding) { // would this stations part overbuild other stations? ++ if ((part.adjoining_station == MULTIPLE_STATIONS) || (overbuilding && dst_sid != part.adjoining_station)) { ++ /* Can't paste this part because current station would overbuild multiple different stations. */ ++ _current_pasting->CollectError(part.dst_tile, STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING, GetPasteStationErrorMsg(part.src_tile)); ++ } else { ++ /* Try to overbuild a station. */ ++ CopyPasteStation(part.src_tile, GenericTileIndex(part.dst_tile), copy_paste, dst_sid, dst_specindex, false); ++ } ++ } else { ++ if (!overbuilding && (part.adjoining_station == MULTIPLE_STATIONS || ( ++ dst_sid != NEW_STATION && part.adjoining_station != INVALID_STATION && dst_sid != part.adjoining_station))) { ++ /* can't paste this part because current station would have to join multiple different stations */ ++ _current_pasting->CollectError(part.dst_tile, STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING, GetPasteStationErrorMsg(part.src_tile)); ++ } else { ++ if (dst_sid == NEW_STATION && part.adjoining_station != INVALID_STATION) { // is the destination station not yet determined? ++ dst_sid = part.adjoining_station; // try to join a new station ++ } ++ CopyPasteStation(part.src_tile, GenericTileIndex(part.dst_tile), copy_paste, dst_sid, dst_specindex, true); ++ } ++ } ++ ++ if (_current_pasting->dc_flags & DC_EXEC) { ++ if (_current_pasting->last_result.Succeeded()) { ++ /* Confirm that station will be using certain ID and specindex. */ ++ _copy_paste_station_id_paste_map.ConfirmIDForStation(src_sid, GetStationIndex(part.dst_tile)); ++ if (src_specindex != -1) _copy_paste_station_id_paste_map.ConfirmSpecIndexForStationPart(src_sid, src_specindex, GetCustomStationSpecIndex(part.dst_tile)); ++ if (part.overbuilding) _copy_paste_station_id_paste_map.SetOverbuilding(src_sid); ++ } else if (_current_pasting->last_result.GetErrorMessage() == STR_ERROR_CAN_T_DISTANT_JOIN) { ++ /* If we can't distant-join now then perhaps we will be able to do it later, after other parts. */ ++ if (_current_pasting->err_message == STR_ERROR_CAN_T_DISTANT_JOIN) { ++ /* discard the "can't distatnt-join" error */ ++ _current_pasting->err_tile = INVALID_TILE; ++ _current_pasting->err_message = STR_ERROR_NOTHING_TO_DO; ++ } ++ StationPartPasteInfo info = { part.src_tile, part.dst_tile, INVALID_STATION, false }; ++ _copy_paste_station_part_paste_queue.push_back(info); ++ } ++ } ++ } ++ if (IsPastingInterrupted()) break; ++ if (orig_queue_size == _copy_paste_station_part_paste_queue.size()) break; // don't retry if the queue didn't shrink ++ } ++ ++ /* set the "can't distatnt-join" error if not all retries were successfull */ ++ if (!IsPastingInterrupted() && !_copy_paste_station_part_paste_queue.empty()) { ++ _current_pasting->CollectError(_copy_paste_station_part_paste_queue.back().dst_tile, STR_ERROR_CAN_T_DISTANT_JOIN, GetPasteStationErrorMsg(_copy_paste_station_part_paste_queue.back().src_tile)); ++ } ++ ++ _copy_paste_station_part_paste_queue.clear(); ++} ++ ++void AfterPastingStations(const CopyPasteParams ©_paste) ++{ ++ ProcessStationPartPasteQueue(copy_paste); ++ ++ _copy_paste_station_id_paste_map = StationIDPasteMap(); // clear ++} ++ ++void AfterCopyingStations(const CopyPasteParams ©_paste) ++{ ++ _clipboard_stations_builder.BuildDone(MapOf(copy_paste.dst_area.tile)); ++} ++ ++ + void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius) + { + Station *st; +@@ -3899,6 +4692,7 @@ void BuildOilRig(TileIndex tile) + st->build_date = _date; + + st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE); ++ st->catchment.BeforeAddTile(tile, st->GetCatchmentRadius()); + + st->UpdateVirtCoord(); + UpdateStationAcceptance(st, false); +@@ -3909,6 +4703,7 @@ void DeleteOilRig(TileIndex tile) + { + Station *st = Station::GetByTile(tile); + ++ st->catchment.AfterRemoveTile(tile, st->GetCatchmentRadius()); + MakeWaterKeepingClass(tile, OWNER_NONE); + + st->dock_tile = INVALID_TILE; +@@ -3916,6 +4711,7 @@ void DeleteOilRig(TileIndex tile) + st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK); + st->airport.flags = 0; + ++ if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); + st->rect.AfterRemoveTile(st, tile); + + st->UpdateVirtCoord(); +@@ -4514,4 +5310,5 @@ extern const TileTypeProcs _tile_type_station_procs = { + VehicleEnter_Station, // vehicle_enter_tile_proc + GetFoundation_Station, // get_foundation_proc + TerraformTile_Station, // terraform_tile_proc ++ CopyPasteTile_Station, // copypaste_tile_proc + }; +diff --git a/src/station_func.h b/src/station_func.h +index f33dbd2..fedd91f 100644 +--- a/src/station_func.h ++++ b/src/station_func.h +@@ -30,6 +30,9 @@ void UpdateAllStationVirtCoords(); + CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad); + CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint32 *always_accepted = NULL); + ++CargoArray GetProductionRateAroundTiles(TileIndex tile, int w, int h, int rad); ++CargoArray GetAcceptanceRateAroundTiles(TileIndex tile, int w, int h, int rad); ++ + void UpdateStationAcceptance(Station *st, bool show_msg); + + const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx); +diff --git a/src/station_gui.cpp b/src/station_gui.cpp +index 7399fe0..5ca6773 100644 +--- a/src/station_gui.cpp ++++ b/src/station_gui.cpp +@@ -30,6 +30,8 @@ + #include "sortlist_type.h" + #include "core/geometry_func.hpp" + #include "vehiclelist.h" ++#include "core/math_func.hpp" ++#include "overlay_cmd.h" + #include "town.h" + #include "linkgraph/linkgraph.h" + #include "zoom_func.h" +@@ -37,6 +39,7 @@ + #include "widgets/station_widget.h" + + #include "table/strings.h" ++#include "table/control_codes.h" + + #include <set> + #include <vector> +@@ -59,10 +62,13 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp + uint32 cargo_mask = 0; + if (_thd.drawstyle == HT_RECT && tile < MapSize()) { + CargoArray cargoes; ++ CargoArray rates; + if (supplies) { + cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); ++ rates = GetProductionRateAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); + } else { + cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); ++ rates = GetAcceptanceRateAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); + } + + /* Convert cargo counts to a set of cargo bits, and draw the result. */ +@@ -74,9 +80,18 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp + default: NOT_REACHED(); + } + if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i); ++ ++ if (i == CT_PASSENGERS) { ++ SetDParam(2, rates[i]); ++ } else if (i == CT_MAIL) { ++ SetDParam(3, rates[i]); ++ } + } + } + SetDParam(0, cargo_mask); ++ ++ /* SCC_CARGO_LIST works as a magic number to let FormatString() know it's being called from here. */ ++ SetDParam(1, SCC_CARGO_LIST); + return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO); + } + +@@ -777,6 +792,8 @@ static const NWidgetPart _nested_station_view_widgets[] = { + NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_ACCEPT_RATING_LIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), ++ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_COVERAGE), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1), ++ SetDataTip(STR_BUTTON_COVERAGE, STR_STATION_VIEW_COVERAGE_TIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_LOCATION), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1), + SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1), +@@ -1311,6 +1328,9 @@ struct StationViewWindow : public Window { + + ~StationViewWindow() + { ++ Overlays::Instance()->RemoveStation(Station::Get(this->window_number)); ++ MarkWholeScreenDirty(); ++ Owner owner = Station::Get(this->window_number)->owner; + DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, this->owner, this->window_number).Pack(), false); + DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, this->owner, this->window_number).Pack(), false); + DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, this->owner, this->window_number).Pack(), false); +@@ -1404,6 +1424,9 @@ struct StationViewWindow : public Window { + this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company || st->owner == OWNER_NONE); // Also consider SE, where _local_company == OWNER_NONE + this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, (st->facilities & FACIL_AIRPORT) && (st->airport.flags & AIRPORT_CLOSED_block) != 0); + ++ /* check lowered stated for some buttons */ ++ this->SetWidgetLoweredState(WID_SV_COVERAGE, Overlays::Instance()->HasStation(st)); ++ + this->DrawWidgets(); + + if (!this->IsShaded()) { +@@ -1893,6 +1916,11 @@ struct StationViewWindow : public Window { + } + break; + ++ case WID_SV_COVERAGE: ++ Overlays::Instance()->ToggleStation(Station::Get(this->window_number)); ++ MarkWholeScreenDirty(); ++ break; ++ + case WID_SV_ACCEPTS_RATINGS: { + /* Swap between 'accepts' and 'ratings' view. */ + int height_change; +@@ -2075,6 +2103,8 @@ struct StationViewWindow : public Window { + } + } + } ++protected: ++ void Get(WindowNumber window_number); + }; + + const StringID StationViewWindow::_sort_names[] = { +@@ -2109,7 +2139,12 @@ static WindowDesc _station_view_desc( + */ + void ShowStationViewWindow(StationID station) + { +- AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station); ++ if (_ctrl_pressed) { ++ Overlays::Instance()->ToggleStation(Station::Get(station)); ++ MarkWholeScreenDirty(); ++ } else { ++ AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station); ++ } + } + + /** Struct containing TileIndex and StationID */ +diff --git a/src/station_map.h b/src/station_map.h +index 7ca9bd7..9dbc7bf 100644 +--- a/src/station_map.h ++++ b/src/station_map.h +@@ -26,11 +26,16 @@ typedef byte StationGfx; ///< Index of station graphics. @see _station_display_d + * @pre IsTileType(t, MP_STATION) + * @return Station ID of the station at \a t + */ +-static inline StationID GetStationIndex(TileIndex t) ++template <bool Tgeneric> ++static inline StationID GetStationIndex(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_STATION)); +- return (StationID)_m[t].m2; ++ return (StationID)GetTile(t)->m2; + } ++/** @copydoc GetStationIndex(TileIndexT<Tgeneric>::T) */ ++static inline StationID GetStationIndex(TileIndex t) { return GetStationIndex<false>(t); } ++/** @copydoc GetStationIndex(TileIndexT<Tgeneric>::T) */ ++static inline StationID GetStationIndex(GenericTileIndex t) { return GetStationIndex<true>(t); } + + + static const int GFX_DOCK_BASE_WATER_PART = 4; ///< The offset for the water parts. +@@ -42,11 +47,16 @@ static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET = 4; ///< The offset for the + * @pre IsTileType(t, MP_STATION) + * @return the station type + */ +-static inline StationType GetStationType(TileIndex t) ++template <bool Tgeneric> ++static inline StationType GetStationType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_STATION)); +- return (StationType)GB(_me[t].m6, 3, 3); ++ return (StationType)GB(GetTileEx(t)->m6, 3, 3); + } ++/** @copydoc GetStationType(TileIndexT<Tgeneric>::T) */ ++static inline StationType GetStationType(TileIndex t) { return GetStationType<false>(t); } ++/** @copydoc GetStationType(TileIndexT<Tgeneric>::T) */ ++static inline StationType GetStationType(GenericTileIndex t) { return GetStationType<true>(t); } + + /** + * Get the road stop type of this tile +@@ -54,11 +64,16 @@ static inline StationType GetStationType(TileIndex t) + * @pre GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS + * @return the road stop type + */ +-static inline RoadStopType GetRoadStopType(TileIndex t) ++template <bool Tgeneric> ++static inline RoadStopType GetRoadStopType(typename TileIndexT<Tgeneric>::T t) + { + assert(GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS); + return GetStationType(t) == STATION_TRUCK ? ROADSTOP_TRUCK : ROADSTOP_BUS; + } ++/** @copydoc GetRoadStopType(TileIndexT<Tgeneric>::T) */ ++static inline RoadStopType GetRoadStopType(TileIndex t) { return GetRoadStopType<false>(t); } ++/** @copydoc GetRoadStopType(TileIndexT<Tgeneric>::T) */ ++static inline RoadStopType GetRoadStopType(GenericTileIndex t) { return GetRoadStopType<true>(t); } + + /** + * Get the station graphics of this tile +@@ -66,11 +81,16 @@ static inline RoadStopType GetRoadStopType(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return the station graphics + */ +-static inline StationGfx GetStationGfx(TileIndex t) ++template <bool Tgeneric> ++static inline StationGfx GetStationGfx(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_STATION)); +- return _m[t].m5; ++ return GetTile(t)->m5; + } ++/** @copydoc GetStationGfx(TileIndexT<Tgeneric>::T) */ ++static inline StationGfx GetStationGfx(TileIndex t) { return GetStationGfx<false>(t); } ++/** @copydoc GetStationGfx(TileIndexT<Tgeneric>::T) */ ++static inline StationGfx GetStationGfx(GenericTileIndex t) { return GetStationGfx<true>(t); } + + /** + * Set the station graphics of this tile +@@ -78,11 +98,16 @@ static inline StationGfx GetStationGfx(TileIndex t) + * @param gfx the new graphics + * @pre IsTileType(t, MP_STATION) + */ +-static inline void SetStationGfx(TileIndex t, StationGfx gfx) ++template <bool Tgeneric> ++static inline void SetStationGfx(typename TileIndexT<Tgeneric>::T t, StationGfx gfx) + { + assert(IsTileType(t, MP_STATION)); +- _m[t].m5 = gfx; ++ GetTile(t)->m5 = gfx; + } ++/** @copydoc SetStationGfx(TileIndexT<Tgeneric>::T,StationGfx) */ ++static inline void SetStationGfx(TileIndex t, StationGfx gfx) { SetStationGfx<false>(t, gfx); } ++/** @copydoc SetStationGfx(TileIndexT<Tgeneric>::T,StationGfx) */ ++static inline void SetStationGfx(GenericTileIndex t, StationGfx gfx) { SetStationGfx<true>(t, gfx); } + + /** + * Is this station tile a rail station? +@@ -90,20 +115,30 @@ static inline void SetStationGfx(TileIndex t, StationGfx gfx) + * @pre IsTileType(t, MP_STATION) + * @return true if and only if the tile is a rail station + */ +-static inline bool IsRailStation(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailStation(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_RAIL; + } ++/** @copydoc IsRailStation(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailStation(TileIndex t) { return IsRailStation<false>(t); } ++/** @copydoc IsRailStation(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailStation(GenericTileIndex t) { return IsRailStation<true>(t); } + + /** + * Is this tile a station tile and a rail station? + * @param t the tile to get the information from + * @return true if and only if the tile is a rail station + */ +-static inline bool IsRailStationTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailStationTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && IsRailStation(t); + } ++/** @copydoc IsRailStationTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailStationTile(TileIndex t) { return IsRailStationTile<false>(t); } ++/** @copydoc IsRailStationTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailStationTile(GenericTileIndex t) { return IsRailStationTile<true>(t); } + + /** + * Is this station tile a rail waypoint? +@@ -111,20 +146,30 @@ static inline bool IsRailStationTile(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return true if and only if the tile is a rail waypoint + */ +-static inline bool IsRailWaypoint(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailWaypoint(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_WAYPOINT; + } ++/** @copydoc IsRailWaypoint(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailWaypoint(TileIndex t) { return IsRailWaypoint<false>(t); } ++/** @copydoc IsRailWaypoint(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailWaypoint(GenericTileIndex t) { return IsRailWaypoint<true>(t); } + + /** + * Is this tile a station tile and a rail waypoint? + * @param t the tile to get the information from + * @return true if and only if the tile is a rail waypoint + */ +-static inline bool IsRailWaypointTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRailWaypointTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && IsRailWaypoint(t); + } ++/** @copydoc IsRailWaypointTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailWaypointTile(TileIndex t) { return IsRailWaypointTile<false>(t); } ++/** @copydoc IsRailWaypointTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRailWaypointTile(GenericTileIndex t) { return IsRailWaypointTile<true>(t); } + + /** + * Has this station tile a rail? In other words, is this station +@@ -133,10 +178,15 @@ static inline bool IsRailWaypointTile(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return true if and only if the tile has rail + */ +-static inline bool HasStationRail(TileIndex t) ++template <bool Tgeneric> ++static inline bool HasStationRail(typename TileIndexT<Tgeneric>::T t) + { + return IsRailStation(t) || IsRailWaypoint(t); + } ++/** @copydoc HasStationRail(TileIndexT<Tgeneric>::T) */ ++static inline bool HasStationRail(TileIndex t) { return HasStationRail<false>(t); } ++/** @copydoc HasStationRail(TileIndexT<Tgeneric>::T) */ ++static inline bool HasStationRail(GenericTileIndex t) { return HasStationRail<true>(t); } + + /** + * Has this station tile a rail? In other words, is this station +@@ -144,10 +194,15 @@ static inline bool HasStationRail(TileIndex t) + * @param t the tile to check + * @return true if and only if the tile is a station tile and has rail + */ +-static inline bool HasStationTileRail(TileIndex t) ++template <bool Tgeneric> ++static inline bool HasStationTileRail(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && HasStationRail(t); + } ++/** @copydoc HasStationTileRail(TileIndexT<Tgeneric>::T) */ ++static inline bool HasStationTileRail(TileIndex t) { return HasStationTileRail<false>(t); } ++/** @copydoc HasStationTileRail(TileIndexT<Tgeneric>::T) */ ++static inline bool HasStationTileRail(GenericTileIndex t) { return HasStationTileRail<true>(t); } + + /** + * Is this station tile an airport? +@@ -155,20 +210,30 @@ static inline bool HasStationTileRail(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return true if and only if the tile is an airport + */ +-static inline bool IsAirport(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsAirport(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_AIRPORT; + } ++/** @copydoc IsAirport(TileIndexT<Tgeneric>::T) */ ++static inline bool IsAirport(TileIndex t) { return IsAirport<false>(t); } ++/** @copydoc IsAirport(TileIndexT<Tgeneric>::T) */ ++static inline bool IsAirport(GenericTileIndex t) { return IsAirport<true>(t); } + + /** + * Is this tile a station tile and an airport tile? + * @param t the tile to get the information from + * @return true if and only if the tile is an airport + */ +-static inline bool IsAirportTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsAirportTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && IsAirport(t); + } ++/** @copydoc IsAirportTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsAirportTile(TileIndex t) { return IsAirportTile<false>(t); } ++/** @copydoc IsAirportTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsAirportTile(GenericTileIndex t) { return IsAirportTile<true>(t); } + + bool IsHangar(TileIndex t); + +@@ -178,10 +243,15 @@ bool IsHangar(TileIndex t); + * @pre IsTileType(t, MP_STATION) + * @return \c true if station is a truck stop, \c false otherwise + */ +-static inline bool IsTruckStop(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsTruckStop(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_TRUCK; + } ++/** @copydoc IsTruckStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTruckStop(TileIndex t) { return IsTruckStop<false>(t); } ++/** @copydoc IsTruckStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTruckStop(GenericTileIndex t) { return IsTruckStop<true>(t); } + + /** + * Is the station at \a t a bus stop? +@@ -189,10 +259,15 @@ static inline bool IsTruckStop(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return \c true if station is a bus stop, \c false otherwise + */ +-static inline bool IsBusStop(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBusStop(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_BUS; + } ++/** @copydoc IsBusStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBusStop(TileIndex t) { return IsBusStop<false>(t); } ++/** @copydoc IsBusStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBusStop(GenericTileIndex t) { return IsBusStop<true>(t); } + + /** + * Is the station at \a t a road station? +@@ -200,41 +275,61 @@ static inline bool IsBusStop(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return \c true if station at the tile is a bus top or a truck stop, \c false otherwise + */ +-static inline bool IsRoadStop(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRoadStop(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_STATION)); + return IsTruckStop(t) || IsBusStop(t); + } ++/** @copydoc IsRoadStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadStop(TileIndex t) { return IsRoadStop<false>(t); } ++/** @copydoc IsRoadStop(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadStop(GenericTileIndex t) { return IsRoadStop<true>(t); } + + /** + * Is tile \a t a road stop station? + * @param t Tile to check + * @return \c true if the tile is a station tile and a road stop + */ +-static inline bool IsRoadStopTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsRoadStopTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && IsRoadStop(t); + } ++/** @copydoc IsRoadStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadStopTile(TileIndex t) { return IsRoadStopTile<false>(t); } ++/** @copydoc IsRoadStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsRoadStopTile(GenericTileIndex t) { return IsRoadStopTile<true>(t); } + + /** + * Is tile \a t a standard (non-drive through) road stop station? + * @param t Tile to check + * @return \c true if the tile is a station tile and a standard road stop + */ +-static inline bool IsStandardRoadStopTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsStandardRoadStopTile(typename TileIndexT<Tgeneric>::T t) + { + return IsRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; + } ++/** @copydoc IsStandardRoadStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsStandardRoadStopTile(TileIndex t) { return IsStandardRoadStopTile<false>(t); } ++/** @copydoc IsStandardRoadStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsStandardRoadStopTile(GenericTileIndex t) { return IsStandardRoadStopTile<true>(t); } + + /** + * Is tile \a t a drive through road stop station? + * @param t Tile to check + * @return \c true if the tile is a station tile and a drive through road stop + */ +-static inline bool IsDriveThroughStopTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsDriveThroughStopTile(typename TileIndexT<Tgeneric>::T t) + { + return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; + } ++/** @copydoc IsDriveThroughStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDriveThroughStopTile(TileIndex t) { return IsDriveThroughStopTile<false>(t); } ++/** @copydoc IsDriveThroughStopTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDriveThroughStopTile(GenericTileIndex t) { return IsDriveThroughStopTile<true>(t); } + + /** + * Get the station graphics of this airport tile +@@ -255,7 +350,8 @@ static inline StationGfx GetAirportGfx(TileIndex t) + * @pre IsRoadStopTile(t) + * @return the direction of the entrance + */ +-static inline DiagDirection GetRoadStopDir(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetRoadStopDir(typename TileIndexT<Tgeneric>::T t) + { + StationGfx gfx = GetStationGfx(t); + assert(IsRoadStopTile(t)); +@@ -265,6 +361,10 @@ static inline DiagDirection GetRoadStopDir(TileIndex t) + return (DiagDirection)(gfx - GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); + } + } ++/** @copydoc GetRoadStopDir(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRoadStopDir(TileIndex t) { return GetRoadStopDir<false>(t); } ++/** @copydoc GetRoadStopDir(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetRoadStopDir(GenericTileIndex t) { return GetRoadStopDir<true>(t); } + + /** + * Is tile \a t part of an oilrig? +@@ -283,20 +383,30 @@ static inline bool IsOilRig(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return \c true if the tile is a dock + */ +-static inline bool IsDock(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsDock(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_DOCK; + } ++/** @copydoc IsDock(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDock(TileIndex t) { return IsDock<false>(t); } ++/** @copydoc IsDock(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDock(GenericTileIndex t) { return IsDock<true>(t); } + + /** + * Is tile \a t a dock tile? + * @param t Tile to check + * @return \c true if the tile is a dock + */ +-static inline bool IsDockTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsDockTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && GetStationType(t) == STATION_DOCK; + } ++/** @copydoc IsDockTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDockTile(TileIndex t) { return IsDockTile<false>(t); } ++/** @copydoc IsDockTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsDockTile(GenericTileIndex t) { return IsDockTile<true>(t); } + + /** + * Is tile \a t a buoy tile? +@@ -304,20 +414,30 @@ static inline bool IsDockTile(TileIndex t) + * @pre IsTileType(t, MP_STATION) + * @return \c true if the tile is a buoy + */ +-static inline bool IsBuoy(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBuoy(typename TileIndexT<Tgeneric>::T t) + { + return GetStationType(t) == STATION_BUOY; + } ++/** @copydoc IsBuoy(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBuoy(TileIndex t) { return IsBuoy<false>(t); } ++/** @copydoc IsBuoy(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBuoy(GenericTileIndex t) { return IsBuoy<true>(t); } + + /** + * Is tile \a t a buoy tile? + * @param t Tile to check + * @return \c true if the tile is a buoy + */ +-static inline bool IsBuoyTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsBuoyTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_STATION) && IsBuoy(t); + } ++/** @copydoc IsBuoyTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBuoyTile(TileIndex t) { return IsBuoyTile<false>(t); } ++/** @copydoc IsBuoyTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsBuoyTile(GenericTileIndex t) { return IsBuoyTile<true>(t); } + + /** + * Is tile \a t an hangar tile? +@@ -335,11 +455,16 @@ static inline bool IsHangarTile(TileIndex t) + * @pre HasStationRail(t) + * @return The direction of the rails on tile \a t. + */ +-static inline Axis GetRailStationAxis(TileIndex t) ++template <bool Tgeneric> ++static inline Axis GetRailStationAxis(typename TileIndexT<Tgeneric>::T t) + { + assert(HasStationRail(t)); + return HasBit(GetStationGfx(t), 0) ? AXIS_Y : AXIS_X; + } ++/** @copydoc GetRailStationAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetRailStationAxis(TileIndex t) { return GetRailStationAxis<false>(t); } ++/** @copydoc GetRailStationAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetRailStationAxis(GenericTileIndex t) { return GetRailStationAxis<true>(t); } + + /** + * Get the rail track of a rail station tile. +@@ -347,10 +472,15 @@ static inline Axis GetRailStationAxis(TileIndex t) + * @pre HasStationRail(t) + * @return The rail track of the rails on tile \a t. + */ +-static inline Track GetRailStationTrack(TileIndex t) ++template <bool Tgeneric> ++static inline Track GetRailStationTrack(typename TileIndexT<Tgeneric>::T t) + { + return AxisToTrack(GetRailStationAxis(t)); + } ++/** @copydoc GetRailStationTrack(TileIndexT<Tgeneric>::T) */ ++static inline Track GetRailStationTrack(TileIndex t) { return GetRailStationTrack<false>(t); } ++/** @copydoc GetRailStationTrack(TileIndexT<Tgeneric>::T) */ ++static inline Track GetRailStationTrack(GenericTileIndex t) { return GetRailStationTrack<true>(t); } + + /** + * Get the trackbits of a rail station tile. +@@ -358,10 +488,15 @@ static inline Track GetRailStationTrack(TileIndex t) + * @pre HasStationRail(t) + * @return The trackbits of the rails on tile \a t. + */ +-static inline TrackBits GetRailStationTrackBits(TileIndex t) ++template <bool Tgeneric> ++static inline TrackBits GetRailStationTrackBits(typename TileIndexT<Tgeneric>::T t) + { + return AxisToTrackBits(GetRailStationAxis(t)); + } ++/** @copydoc GetRailStationTrackBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetRailStationTrackBits(TileIndex t) { return GetRailStationTrackBits<false>(t); } ++/** @copydoc GetRailStationTrackBits(TileIndexT<Tgeneric>::T) */ ++static inline TrackBits GetRailStationTrackBits(GenericTileIndex t) { return GetRailStationTrackBits<true>(t); } + + /** + * Check if a tile is a valid continuation to a railstation tile. +@@ -394,7 +529,7 @@ static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex s + static inline bool HasStationReservation(TileIndex t) + { + assert(HasStationRail(t)); +- return HasBit(_me[t].m6, 2); ++ return HasBit(GetTileEx(t)->m6, 2); + } + + /** +@@ -403,11 +538,16 @@ static inline bool HasStationReservation(TileIndex t) + * @param t the station tile + * @param b the reservation state + */ +-static inline void SetRailStationReservation(TileIndex t, bool b) ++template <bool Tgeneric> ++static inline void SetRailStationReservation(typename TileIndexT<Tgeneric>::T t, bool b) + { + assert(HasStationRail(t)); +- SB(_me[t].m6, 2, 1, b ? 1 : 0); ++ SB(GetTileEx(t)->m6, 2, 1, b ? 1 : 0); + } ++/** @copydoc SetRailStationReservation(TileIndexT<Tgeneric>::T) */ ++static inline void SetRailStationReservation(TileIndex t, bool b) { SetRailStationReservation<false>(t, b); } ++/** @copydoc SetRailStationReservation(TileIndexT<Tgeneric>::T) */ ++static inline void SetRailStationReservation(GenericTileIndex t, bool b) { SetRailStationReservation<true>(t, b); } + + /** + * Get the reserved track bits for a waypoint +@@ -421,18 +561,56 @@ static inline TrackBits GetStationReservationTrackBits(TileIndex t) + } + + /** ++ * Test whether a given water dock tile is the land part of the dock ++ * @param t the water dock tile ++ * @return if the given tile is the land part of a dock ++ * @pre IsDockTile(t) ++ */ ++template <bool Tgeneric> ++static inline bool IsLandDockSection(typename TileIndexT<Tgeneric>::T t) ++{ ++ assert(IsDockTile(t)); ++ return GetStationGfx(t) < GFX_DOCK_BASE_WATER_PART; ++} ++/** @copydoc IsLandDockSection<bool> */ ++static inline bool IsLandDockSection(TileIndex t) { return IsLandDockSection<false>(t); } ++/** @copydoc IsLandDockSection<bool> */ ++static inline bool IsLandDockSection(GenericTileIndex t) { return IsLandDockSection<true>(t); } ++ ++/** + * Get the direction of a dock. + * @param t Tile to query +- * @pre IsDock(t) ++ * @pre IsLandDockSection(t) + * @pre \a t is the land part of the dock + * @return The direction of the dock on tile \a t. + */ +-static inline DiagDirection GetDockDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetDockDirection(typename TileIndexT<Tgeneric>::T t) + { +- StationGfx gfx = GetStationGfx(t); +- assert(IsDock(t) && gfx < GFX_DOCK_BASE_WATER_PART); +- return (DiagDirection)(gfx); ++ assert(IsLandDockSection(t)); ++ return (DiagDirection)GetStationGfx(t); ++} ++/** @copydoc GetDockDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetDockDirection(TileIndex t) { return GetDockDirection<false>(t); } ++/** @copydoc GetDockDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetDockDirection(GenericTileIndex t) { return GetDockDirection<true>(t); } ++ ++/** ++ * Get the other tile of a dock. ++ * @param t Tile to query ++ * @pre IsDockTile(t) ++ * @return The other tile of the dock. ++ */ ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T GetOtherDockTile(typename TileIndexT<Tgeneric>::T t) ++{ ++ TileIndexDiff delta = ToTileIndexDiff(TileIndexDiffCByDiagDir(AxisToDiagDir((Axis)(GetStationGfx(t) & 0x1))), MapOf(t)); ++ return IsDockTile(t + delta) ? t + delta : t - delta; + } ++/** @copydoc GetOtherDockTile<bool> */ ++static inline TileIndex GetOtherDockTile(TileIndex t) { return GetOtherDockTile<false>(t); } ++/** @copydoc GetOtherDockTile<bool> */ ++static inline GenericTileIndex GetOtherDockTile(GenericTileIndex t) { return GetOtherDockTile<true>(t); } + + /** + * Get the tileoffset from this tile a ship should target to get to this dock. +@@ -467,11 +645,16 @@ static inline TileIndexDiffC GetDockOffset(TileIndex t) + * @pre HasStationTileRail(t) + * @return True if this station is part of a newgrf station. + */ +-static inline bool IsCustomStationSpecIndex(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsCustomStationSpecIndex(typename TileIndexT<Tgeneric>::T t) + { + assert(HasStationTileRail(t)); +- return _m[t].m4 != 0; ++ return GetTile(t)->m4 != 0; + } ++/** @copydoc IsCustomStationSpecIndex(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCustomStationSpecIndex(TileIndex t) { return IsCustomStationSpecIndex<false>(t); } ++/** @copydoc IsCustomStationSpecIndex(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCustomStationSpecIndex(GenericTileIndex t) { return IsCustomStationSpecIndex<true>(t); } + + /** + * Set the custom station spec for this tile. +@@ -479,11 +662,16 @@ static inline bool IsCustomStationSpecIndex(TileIndex t) + * @param specindex The new spec. + * @pre HasStationTileRail(t) + */ +-static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) ++template <bool Tgeneric> ++static inline void SetCustomStationSpecIndex(typename TileIndexT<Tgeneric>::T t, byte specindex) + { + assert(HasStationTileRail(t)); +- _m[t].m4 = specindex; ++ GetTile(t)->m4 = specindex; + } ++/** @copydoc SetCustomStationSpecIndex(TileIndexT<Tgeneric>::T,byte) */ ++static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) { return SetCustomStationSpecIndex<false>(t, specindex); } ++/** @copydoc SetCustomStationSpecIndex(TileIndexT<Tgeneric>::T,byte) */ ++static inline void SetCustomStationSpecIndex(GenericTileIndex t, byte specindex) { return SetCustomStationSpecIndex<true>(t, specindex); } + + /** + * Get the custom station spec for this tile. +@@ -491,11 +679,16 @@ static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) + * @pre HasStationTileRail(t) + * @return The custom station spec of this tile. + */ +-static inline uint GetCustomStationSpecIndex(TileIndex t) ++template <bool Tgeneric> ++static inline uint GetCustomStationSpecIndex(typename TileIndexT<Tgeneric>::T t) + { + assert(HasStationTileRail(t)); +- return _m[t].m4; ++ return GetTile(t)->m4; + } ++/** @copydoc GetCustomStationSpecIndex(TileIndexT<Tgeneric>::T) */ ++static inline uint GetCustomStationSpecIndex(TileIndex t) { return GetCustomStationSpecIndex<false>(t); } ++/** @copydoc GetCustomStationSpecIndex(TileIndexT<Tgeneric>::T) */ ++static inline uint GetCustomStationSpecIndex(GenericTileIndex t) { return GetCustomStationSpecIndex<true>(t); } + + /** + * Set the random bits for a station tile. +@@ -506,7 +699,7 @@ static inline uint GetCustomStationSpecIndex(TileIndex t) + static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) + { + assert(IsTileType(t, MP_STATION)); +- SB(_m[t].m3, 4, 4, random_bits); ++ SB(GetTile(t)->m3, 4, 4, random_bits); + } + + /** +@@ -518,7 +711,7 @@ static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) + static inline byte GetStationTileRandomBits(TileIndex t) + { + assert(IsTileType(t, MP_STATION)); +- return GB(_m[t].m3, 4, 4); ++ return GB(GetTile(t)->m3, 4, 4); + } + + /** +@@ -530,19 +723,24 @@ static inline byte GetStationTileRandomBits(TileIndex t) + * @param section the StationGfx to be used for this tile + * @param wc The water class of the station + */ +-static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) ++template <bool Tgeneric> ++static inline void MakeStation(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) + { + SetTileType(t, MP_STATION); + SetTileOwner(t, o); + SetWaterClass(t, wc); +- _m[t].m2 = sid; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = section; +- SB(_me[t].m6, 2, 1, 0); +- SB(_me[t].m6, 3, 3, st); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = sid; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = section; ++ SB(GetTileEx(t)->m6, 2, 1, 0); ++ SB(GetTileEx(t)->m6, 3, 3, st); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeStation(TileIndexT<Tgeneric>::T,Owner,StationID,StationType,byte,WaterClass) */ ++static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { MakeStation<false>(t, o, sid, st, section, wc); } ++/** @copydoc MakeStation(TileIndexT<Tgeneric>::T,Owner,StationID,StationType,byte,WaterClass) */ ++static inline void MakeStation(GenericTileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { MakeStation<true>(t, o, sid, st, section, wc); } + + /** + * Make the given tile a rail station tile. +@@ -553,12 +751,17 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType + * @param section the StationGfx to be used for this tile + * @param rt the railtype of this tile + */ +-static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) ++template <bool Tgeneric> ++static inline void MakeRailStation(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, Axis a, byte section, RailType rt) + { + MakeStation(t, o, sid, STATION_RAIL, section + a); + SetRailType(t, rt); + SetRailStationReservation(t, false); + } ++/** @copydoc MakeRailStation(TileIndexT<Tgeneric>::T,Owner,StationID,Axis,byte,RailType) */ ++static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailStation<false>(t, o, sid, a, section, rt); } ++/** @copydoc MakeRailStation(TileIndexT<Tgeneric>::T,Owner,StationID,Axis,byte,RailType) */ ++static inline void MakeRailStation(GenericTileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailStation<true>(t, o, sid, a, section, rt); } + + /** + * Make the given tile a rail waypoint tile. +@@ -569,12 +772,17 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, + * @param section the StationGfx to be used for this tile + * @param rt the railtype of this tile + */ +-static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) ++template <bool Tgeneric> ++static inline void MakeRailWaypoint(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, Axis a, byte section, RailType rt) + { + MakeStation(t, o, sid, STATION_WAYPOINT, section + a); + SetRailType(t, rt); + SetRailStationReservation(t, false); + } ++/** @copydoc MakeRailWaypoint(TileIndexT<Tgeneric>::T,Owner,StationID,Axis,byte,RailType) */ ++static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailWaypoint<false>(t, o, sid, a, section, rt); } ++/** @copydoc MakeRailWaypoint(TileIndexT<Tgeneric>::T,Owner,StationID,Axis,byte,RailType) */ ++static inline void MakeRailWaypoint(GenericTileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailWaypoint<true>(t, o, sid, a, section, rt); } + + /** + * Make the given tile a roadstop tile. +@@ -585,13 +793,18 @@ static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, + * @param rt the roadtypes on this tile + * @param d the direction of the roadstop + */ +-static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) ++template <bool Tgeneric> ++static inline void MakeRoadStop(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) + { + MakeStation(t, o, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), d); + SetRoadTypes(t, rt); + SetRoadOwner(t, ROADTYPE_ROAD, o); + SetRoadOwner(t, ROADTYPE_TRAM, o); + } ++/** @copydoc MakeRoadStop(TileIndexT<Tgeneric>::T,Owner,StationID,RoadStopType,RoadTypes,DiagDirection) */ ++static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) { MakeRoadStop<false>(t, o, sid, rst, rt, d); } ++/** @copydoc MakeRoadStop(TileIndexT<Tgeneric>::T,Owner,StationID,RoadStopType,RoadTypes,DiagDirection) */ ++static inline void MakeRoadStop(GenericTileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) { MakeRoadStop<true>(t, o, sid, rst, rt, d); } + + /** + * Make the given tile a drivethrough roadstop tile. +@@ -604,13 +817,18 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp + * @param rt the roadtypes on this tile + * @param a the direction of the roadstop + */ +-static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) ++template <bool Tgeneric> ++static inline void MakeDriveThroughRoadStop(typename TileIndexT<Tgeneric>::T t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) + { + MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a); + SetRoadTypes(t, rt); + SetRoadOwner(t, ROADTYPE_ROAD, road); + SetRoadOwner(t, ROADTYPE_TRAM, tram); + } ++/** @copydoc MakeDriveThroughRoadStop(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,StationID,RoadStopType,RoadTypes,Axis) */ ++static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) { MakeDriveThroughRoadStop<false>(t, station, road, tram, sid, rst, rt, a); } ++/** @copydoc MakeDriveThroughRoadStop(TileIndexT<Tgeneric>::T,Owner,Owner,Owner,StationID,RoadStopType,RoadTypes,Axis) */ ++static inline void MakeDriveThroughRoadStop(GenericTileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) { MakeDriveThroughRoadStop<true>(t, station, road, tram, sid, rst, rt, a); } + + /** + * Make the given tile an airport tile. +@@ -620,10 +838,15 @@ static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner ro + * @param section the StationGfx to be used for this tile + * @param wc the type of water on this tile + */ +-static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section, WaterClass wc) ++template <bool Tgeneric> ++static inline void MakeAirport(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, byte section, WaterClass wc) + { + MakeStation(t, o, sid, STATION_AIRPORT, section, wc); + } ++/** @copydoc MakeAirport(TileIndexT<Tgeneric>::T,Owner,StationID,byte,WaterClass) */ ++static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section, WaterClass wc) { MakeAirport<false>(t, o, sid, section, wc); } ++/** @copydoc MakeAirport(TileIndexT<Tgeneric>::T,Owner,StationID,byte,WaterClass) */ ++static inline void MakeAirport(GenericTileIndex t, Owner o, StationID sid, byte section, WaterClass wc) { MakeAirport<true>(t, o, sid, section, wc); } + + /** + * Make the given tile a buoy tile. +@@ -631,13 +854,18 @@ static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section + * @param sid the station to which this tile belongs + * @param wc the type of water on this tile + */ +-static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) ++template <bool Tgeneric> ++static inline void MakeBuoy(typename TileIndexT<Tgeneric>::T t, StationID sid, WaterClass wc) + { + /* Make the owner of the buoy tile the same as the current owner of the + * water tile. In this way, we can reset the owner of the water to its + * original state when the buoy gets removed. */ + MakeStation(t, GetTileOwner(t), sid, STATION_BUOY, 0, wc); + } ++/** @copydoc MakeBuoy(TileIndexT<Tgeneric>::T,StationID,WaterClass) */ ++static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) { MakeBuoy<false>(t, sid, wc); } ++/** @copydoc MakeBuoy(TileIndexT<Tgeneric>::T,StationID,WaterClass) */ ++static inline void MakeBuoy(GenericTileIndex t, StationID sid, WaterClass wc) { MakeBuoy<true>(t, sid, wc); } + + /** + * Make the given tile a dock tile. +@@ -647,11 +875,16 @@ static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) + * @param d the direction of the dock + * @param wc the type of water on this tile + */ +-static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) ++template <bool Tgeneric> ++static inline void MakeDock(typename TileIndexT<Tgeneric>::T t, Owner o, StationID sid, DiagDirection d, WaterClass wc) + { + MakeStation(t, o, sid, STATION_DOCK, d); +- MakeStation(t + TileOffsByDiagDir(d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); ++ MakeStation(TileAddByDiagDir(t, d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); + } ++/** @copydoc MakeDock(TileIndexT<Tgeneric>::T,Owner,StationID,DiagDirection,WaterClass) */ ++static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeDock<false>(t, o, sid, d, wc); } ++/** @copydoc MakeDock(TileIndexT<Tgeneric>::T,Owner,StationID,DiagDirection,WaterClass) */ ++static inline void MakeDock(GenericTileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeDock<true>(t, o, sid, d, wc); } + + /** + * Make the given tile an oilrig tile. +diff --git a/src/strings.cpp b/src/strings.cpp +index d2ce762..1e3b9b4 100644 +--- a/src/strings.cpp ++++ b/src/strings.cpp +@@ -1154,6 +1154,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg + + const CargoSpec *cs; + FOR_ALL_SORTED_CARGOSPECS(cs) { ++ int n; ++ + if (!HasBit(cmask, cs->Index())) continue; + + if (buff >= last - 2) break; // ',' and ' ' +@@ -1167,6 +1169,20 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg + } + + buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script); ++ ++ if (args->GetParam(1) != SCC_CARGO_LIST || !_settings_client.gui.forecast_display) { ++ continue; ++ } ++ ++ /* Shows only passengers and mails since other cargoes provide no useful value. (all 1) */ ++ if (cs->Index() == CT_PASSENGERS || cs->Index() == CT_MAIL) { ++ if (cs->Index() == CT_PASSENGERS) { ++ n = seprintf(buff, last, "(%d)", (int) args->GetParam(2)); ++ } else { ++ n = seprintf(buff, last, "(%d)", (int) args->GetParam(3)); ++ } ++ buff += n; ++ } + } + + /* If first is still true then no cargo is accepted */ +diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini +index 52ca2d1..fda2f29 100644 +--- a/src/table/misc_settings.ini ++++ b/src/table/misc_settings.ini +@@ -255,7 +255,7 @@ type = SLE_UINT + var = _transparency_opt + def = 0 + min = 0 +-max = 0x1FF ++max = 0x3FF + cat = SC_BASIC + + [SDTG_VAR] +@@ -264,7 +264,7 @@ type = SLE_UINT + var = _transparency_lock + def = 0 + min = 0 +-max = 0x1FF ++max = 0x3FF + cat = SC_BASIC + + [SDTG_VAR] +diff --git a/src/table/settings.ini b/src/table/settings.ini +index f314f21..9d36c04 100644 +--- a/src/table/settings.ini ++++ b/src/table/settings.ini +@@ -418,6 +418,20 @@ strval = STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_NO_ACTIONS + + [SDT_VAR] + base = GameSettings ++var = construction.clipboard_capacity ++type = SLE_UINT8 ++flags = SLF_NOT_IN_SAVE ++guiflags = SGF_NEWGAME_ONLY ++def = 63 ++min = 1 ++max = 63 ++interval = 1 ++str = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY ++strhelp = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT ++strval = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE ++ ++[SDT_VAR] ++base = GameSettings + var = construction.terraform_per_64k_frames + type = SLE_UINT32 + from = 156 +@@ -2719,6 +2733,13 @@ strhelp = STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT + proc = PopulationInLabelActive + + [SDTC_BOOL] ++var = gui.forecast_display ++flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC ++def = true ++str = STR_CONFIG_SETTING_FORECAST_DISPLAY ++strhelp = STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT ++ ++[SDTC_BOOL] + var = gui.link_terraform_toolbar + flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC + def = false +@@ -2858,6 +2879,12 @@ strval = STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST + cat = SC_BASIC + + [SDTC_BOOL] ++var = gui.townrating_indicator ++flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC ++def = true ++str = STR_CONFIG_SETTING_TOWNRATING_INDICATOR ++ ++[SDTC_BOOL] + var = gui.enable_signal_gui + flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC + def = true +@@ -2880,6 +2907,16 @@ strval = STR_JUST_INT + cat = SC_EXPERT + + [SDTC_VAR] ++var = gui.simulated_wormhole_signals ++type = SLE_UINT8 ++flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC ++def = 4 ++min = 1 ++max = 16 ++str = STR_CONFIG_SETTING_SIMULATE_SIGNALS ++proc = RedrawScreen ++ ++[SDTC_VAR] + var = gui.drag_signals_density + type = SLE_UINT8 + flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +diff --git a/src/table/sprites.h b/src/table/sprites.h +index 81d5388..221eb4c 100644 +--- a/src/table/sprites.h ++++ b/src/table/sprites.h +@@ -163,7 +163,10 @@ static const SpriteID SPR_WINDOW_DEFSIZE = SPR_OPENTTD_BASE + 168; + + static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; + +-static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; ++static const SpriteID SPR_CLIPBOARD_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; ++static const SpriteID CLIPBOARD_SPRITE_COUNT = 25; ++ ++static const SpriteID SPR_SIGNALS_BASE = SPR_CLIPBOARD_BASE + CLIPBOARD_SPRITE_COUNT; + static const uint16 PRESIGNAL_SPRITE_COUNT = 48; + static const uint16 PRESIGNAL_AND_SEMAPHORE_SPRITE_COUNT = 112; + static const uint16 PRESIGNAL_SEMAPHORE_AND_PBS_SPRITE_COUNT = 240; +@@ -1332,6 +1335,30 @@ static const SpriteID SPR_IMG_GOAL = SPR_OPENTTD_BASE + 171; + static const SpriteID SPR_IMG_GOAL_COMPLETED = SPR_OPENTTD_BASE + 172; + static const SpriteID SPR_IMG_GOAL_BROKEN_REF= SPR_OPENTTD_BASE + 173; + ++/* clipboard_gui.cpp */ ++static const SpriteID SPR_IMG_CLIPBOARD = SPR_CLIPBOARD_BASE + 0; ++static const SpriteID SPR_IMG_CLIPBOARD_COPY = SPR_CLIPBOARD_BASE + 1; ++static const SpriteID SPR_IMG_CLIPBOARD_PASTE = SPR_CLIPBOARD_BASE + 2; ++static const SpriteID SPR_IMG_CLIPBOARD_SELECT_COPY_AREA = SPR_CLIPBOARD_BASE + 3; ++static const SpriteID SPR_IMG_CLIPBOARD_INSTANT_COPY_PASTE = SPR_CLIPBOARD_BASE + 4; ++static const SpriteID SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION = SPR_CLIPBOARD_BASE + 5; ++static const SpriteID SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_OFF = SPR_CLIPBOARD_BASE + 6; ++static const SpriteID SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_ON = SPR_CLIPBOARD_BASE + 7; ++static const SpriteID SPR_IMG_CLIPBOARD_UPGRADE_BRIDGES = SPR_CLIPBOARD_BASE + 8; ++static const SpriteID SPR_IMG_CLIPBOARD_ROTATE_LEFT = SPR_CLIPBOARD_BASE + 9; ++static const SpriteID SPR_IMG_CLIPBOARD_ROTATE_RIGHT = SPR_CLIPBOARD_BASE + 10; ++static const SpriteID SPR_IMG_CLIPBOARD_REFLECT_NE_SW = SPR_CLIPBOARD_BASE + 11; ++static const SpriteID SPR_IMG_CLIPBOARD_REFLECT_NW_SE = SPR_CLIPBOARD_BASE + 12; ++static const SpriteID SPR_IMG_TRANFORMATION_IDENTITY = SPR_CLIPBOARD_BASE + 13; ++static const SpriteID SPR_IMG_TRANFORMATION_ROT_90_R = SPR_CLIPBOARD_BASE + 14; ++static const SpriteID SPR_IMG_TRANFORMATION_ROT_180 = SPR_CLIPBOARD_BASE + 15; ++static const SpriteID SPR_IMG_TRANFORMATION_ROT_90_L = SPR_CLIPBOARD_BASE + 16; ++static const SpriteID SPR_IMG_TRANFORMATION_REF_NE_SW = SPR_CLIPBOARD_BASE + 17; ++static const SpriteID SPR_IMG_TRANFORMATION_REF_W_E = SPR_CLIPBOARD_BASE + 18; ++static const SpriteID SPR_IMG_TRANFORMATION_REF_NW_SE = SPR_CLIPBOARD_BASE + 19; ++static const SpriteID SPR_IMG_TRANFORMATION_REF_N_S = SPR_CLIPBOARD_BASE + 20; ++static const SpriteID SPR_IMG_CLIPBOARD_HEIGHT_PANEL = SPR_CLIPBOARD_BASE + 21; ++ + /* intro_gui.cpp, genworld_gui.cpp */ + static const SpriteID SPR_SELECT_TEMPERATE = 4882; + static const SpriteID SPR_SELECT_TEMPERATE_PUSHED = 4883; +@@ -1440,6 +1467,11 @@ static const CursorID SPR_CURSOR_CLONE_ROADVEH = SPR_OPENTTD_BASE + 111; + static const CursorID SPR_CURSOR_CLONE_SHIP = SPR_OPENTTD_BASE + 112; + static const CursorID SPR_CURSOR_CLONE_AIRPLANE = SPR_OPENTTD_BASE + 113; + ++/* Clipboard cursors */ ++static const CursorID SPR_CURSOR_COPY = SPR_CLIPBOARD_BASE + 22; ++static const CursorID SPR_CURSOR_PASTE = SPR_CLIPBOARD_BASE + 23; ++static const CursorID SPR_CURSOR_ADJUST_HEIGHT = SPR_CLIPBOARD_BASE + 24; ++ + /** Animation macro in table/animcursors.h (_animcursors[]) */ + + static const CursorID SPR_CURSOR_DEMOLISH_FIRST = 704; +diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp +index aad9822..5c090bf 100644 +--- a/src/terraform_cmd.cpp ++++ b/src/terraform_cmd.cpp +@@ -10,7 +10,9 @@ + /** @file terraform_cmd.cpp Commands related to terraforming. */ + + #include "stdafx.h" ++#include "core/geometry_func.hpp" + #include "command_func.h" ++#include "copypaste_cmd.h" + #include "tunnel_map.h" + #include "bridge_map.h" + #include "viewport_func.h" +@@ -18,6 +20,7 @@ + #include "object_base.h" + #include "company_base.h" + #include "company_func.h" ++#include "strings_func.h" + + #include "table/strings.h" + +@@ -408,6 +411,121 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin + return total_cost; + } + ++/** Tile iterator for terraforming purposes. */ ++class TerraformingIterator : public TileIterator { ++public: ++ TerraformingIterator(TileIndex tile) : TileIterator(tile) ++ { } ++ ++ /* ++ * Get the target height of currently iterated tile. ++ * @return target height for the tile ++ */ ++ virtual int GetTileTargetHeight() const = 0; ++ ++ virtual TerraformingIterator *Clone() const { NOT_REACHED(); /* not implemented */ }; ++}; ++ ++/** Terraforming iterator for leveling an area. */ ++class LandLevelingIterator : public TerraformingIterator { ++public: ++ int target_height; ++ ++ LandLevelingIterator(TileIndex tile, int target_height) ++ : TerraformingIterator(tile), target_height(target_height) ++ { } ++ ++ virtual int GetTileTargetHeight() const ++ { ++ return this->target_height; ++ } ++}; ++ ++/** Orthogonal variant of a #LandLevelingIterator. */ ++class OrthogonalLandLevelingIterator : public LandLevelingIterator, protected OrthogonalTileIteratorController { ++public: ++ OrthogonalLandLevelingIterator(const OrthogonalTileArea &ta, int target_height) : LandLevelingIterator(ta.tile, target_height) ++ { ++ this->Init(this->MyIndex(), ta.w, ta.h); ++ } ++ ++ TileIterator &operator ++ () ++ { ++ this->Advance(this->MyIndex(), this->MyMap()); ++ return *this; ++ } ++}; ++ ++/** Diagonal variant of a #LandLevelingIterator. */ ++class DiagonalLandLevelingIterator : public LandLevelingIterator, protected DiagonalTileIteratorController { ++public: ++ DiagonalLandLevelingIterator(const DiagonalTileArea &ta, int target_height) : LandLevelingIterator(ta.tile, target_height) ++ { ++ this->Init(this->MyIndex(), ta.a, ta.b, this->MyMap()); ++ } ++ ++ TileIterator &operator ++ () ++ { ++ this->Advance(this->MyIndex(), this->MyMap()); ++ return *this; ++ } ++}; ++ ++/** Terraforming iterator for leveling an area for pasting purposes. */ ++class PasteLandLevelingIterator : public OrthogonalLandLevelingIterator { ++protected: ++ CopyPasteLevelVariant variant; ++ ++public: ++ PasteLandLevelingIterator(const TileArea &ta, int target_height, CopyPasteLevelVariant variant) ++ : OrthogonalLandLevelingIterator(ta, target_height), variant(variant) ++ { ++ } ++ ++ virtual int GetTileTargetHeight() const ++ { ++ uint ret = this->target_height; ++ switch (this->variant) { ++ case CPLV_LEVEL_ABOVE: ret = min(ret, TileHeight(*this)); break; ++ case CPLV_LEVEL_BELOW: ret = max(ret, TileHeight(*this)); break; ++ } ++ return ret; ++ } ++}; ++ ++/** Terraforming iterator for copy-pasting tile heights. */ ++class HeightsCopyPastingIterator : public TerraformingIterator, protected TransformationTileIteratorController { ++protected: ++ GenericTileIndex src_tile; ///< Current tile of the source area. ++ int height_delta; ///< Amount of units to add to each height ++ ++public: ++ HeightsCopyPastingIterator(const GenericTileArea &src_area, TileIndex transformed_north, DirTransformation transformation, int height_delta) ++ : TerraformingIterator(transformed_north), src_tile(src_area.tile), height_delta(height_delta) ++ { ++ this->Init(&IndexOf(this->src_tile), this->MyIndex(), src_area.w, src_area.h, transformation); ++ } ++ ++ virtual TileIterator &operator ++() ++ { ++ this->Advance(&IndexOf(this->src_tile), MapOf(this->src_tile), this->MyIndex(), this->MyMap()); ++ return *this; ++ } ++ ++ virtual int GetTileTargetHeight() const ++ { ++ return TileHeight(this->src_tile) + this->height_delta; ++ } ++}; ++ ++/** Compound result of a terraform process. */ ++struct TerraformTilesResult { ++ Money cost; ///< Overal cost. ++ bool had_success; ///< Whether any success occured. ++ StringID last_error; ///< Last error, STR_NULL if there were no errors. ++}; ++ ++static TerraformTilesResult TerraformTiles(TerraformingIterator *iter, DoCommandFlag flags, Money available_money = GetAvailableMoneyForCommand()); + + /** + * Levels a selected (rectangle) area of land +@@ -424,41 +542,146 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + { + if (p1 >= MapSize()) return CMD_ERROR; + +- _terraform_err_tile = INVALID_TILE; +- +- /* remember level height */ +- uint oldh = TileHeight(p1); +- + /* compute new height */ +- uint h = oldh; +- LevelMode lm = (LevelMode)GB(p2, 1, 2); +- switch (lm) { ++ int h = TileHeight(p1); ++ switch ((LevelMode)GB(p2, 1, 2)) { + case LM_LEVEL: break; + case LM_RAISE: h++; break; + case LM_LOWER: h--; break; + default: return CMD_ERROR; + } + +- /* Check range of destination height */ +- if (h > _settings_game.construction.max_heightlevel) return_cmd_error((oldh == 0) ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH); ++ TerraformTilesResult ret; ++ if (HasBit(p2, 0)) { ++ DiagonalLandLevelingIterator iter(DiagonalTileArea(tile, p1), h); ++ ret = TerraformTiles(&iter, flags); ++ } else { ++ OrthogonalLandLevelingIterator iter(OrthogonalTileArea(tile, p1), h); ++ ret = TerraformTiles(&iter, flags); ++ } ++ ++ /* If there were only errors then fail with the last one. */ ++ if (!ret.had_success && ret.last_error != STR_NULL) return_cmd_error(ret.last_error); ++ /* Return overal cost. */ ++ return CommandCost(EXPENSES_CONSTRUCTION, ret.cost); ++} ++ ++/** ++ * Terraform tiles as a part of a pasting process. ++ * @param iter iterator to use when terraforming ++ */ ++static void TerraformPasteTiles(TerraformingIterator *iter) ++{ ++ TileIndex start_tile = *iter; ++ ++ /* Do actual terraforming. */ ++ TerraformTilesResult ret = TerraformTiles(iter, _current_pasting->dc_flags | DC_ALL_TILES, _current_pasting->GetAvailableMoney()); ++ ++ /* When copy-pasting, we want to higlight error tiles more frequently. TerraformTiles ++ * doesn't always set the _terraform_err_tile (on some errors it's just INVALID_TILE). ++ * We will assume the start tile in these cases. This will give a better overview on ++ * what area failed to paste. */ ++ if (_terraform_err_tile == INVALID_TILE) _terraform_err_tile = start_tile; ++ ++ /* Collect overal cost of the operation. */ ++ if (ret.had_success) { ++ _current_pasting->CollectCost(CommandCost(EXPENSES_CONSTRUCTION, ret.cost), _terraform_err_tile, STR_ERROR_CAN_T_LEVEL_LAND_HERE); ++ } ++ ++ /* Handle _additional_cash_required */ ++ if ((_current_pasting->dc_flags & DC_EXEC) && _additional_cash_required > 0) { ++ SetDParam(0, _additional_cash_required); ++ _current_pasting->CollectError(_terraform_err_tile, STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, STR_ERROR_CAN_T_LEVEL_LAND_HERE); ++ } ++ ++ /* Collect last error, if any. */ ++ if (ret.last_error != STR_NULL) { ++ _current_pasting->CollectError(_terraform_err_tile, ret.last_error, STR_ERROR_CAN_T_LEVEL_LAND_HERE); ++ } ++} ++ ++/** ++ * Level land (as a part of a pasting process). ++ * ++ * @param ta Area of tiles corners to level. ++ * @param height Desired height. ++ * @param variant Leveling variant. ++ */ ++void LevelPasteLand(const TileArea &ta, uint height, CopyPasteLevelVariant variant) ++{ ++ PasteLandLevelingIterator iter(ta, height, variant); ++ TerraformPasteTiles(&iter); ++} + +- Money money = GetAvailableMoneyForCommand(); +- CommandCost cost(EXPENSES_CONSTRUCTION); +- CommandCost last_error(lm == LM_LEVEL ? STR_ERROR_ALREADY_LEVELLED : INVALID_STRING_ID); +- bool had_success = false; ++/** ++ * Copy and paste heights from one map to another. ++ * ++ * @param src_area Area to read heights from. It consists of tiles, not of tile corners ++ * e.g. if you pass a single tile area then 4 corners will be terraformed. ++ * @param dst_area_north Norhern tile of the area to write heigths at. ++ * @param transformation Transformation to perform on tile indices. ++ * @param height_delta Offset, number of units to add to each height. ++ */ ++void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta) ++{ ++ /* include also corners at SW and SE edges */ ++ GenericTileArea src_corners(src_area.tile, src_area.w + 1, src_area.h + 1); ++ /* transform the most northern corner */ ++ GenericTileIndex transformed_north_corner = src_corners.TransformedNorth(dst_area_north, transformation); ++ ++#ifdef WITH_ASSERT ++ { ++ assert(IsValidTileIndex(dst_area_north)); ++ uint x = TileX(dst_area_north); ++ uint y = TileY(dst_area_north); ++ assert(!IsMainMapTile(dst_area_north) || !_settings_game.construction.freeform_edges || (x > 0 && y > 0)); ++ Dimension dst_dim = { src_corners.w, src_corners.h }; ++ dst_dim = TransformDimension(dst_dim, transformation); ++ assert(x + dst_dim.width <= MapSizeX(MapOf(dst_area_north)) && y + dst_dim.height <= MapSizeY(MapOf(dst_area_north))); ++ } ++#endif /* WITH_ASSERT */ ++ ++ if (IsMainMapTile(dst_area_north)) { ++ HeightsCopyPastingIterator iter(src_corners, AsMainMapTile(transformed_north_corner), transformation, height_delta); ++ TerraformPasteTiles(&iter); ++ } else { ++ for (TransformationTileIteratorT<true, true> iter(src_corners, transformed_north_corner, transformation); IsValidTileIndex(iter); ++iter) { ++ SetTileHeight(iter.DstTile(), TileHeight(iter.SrcTile())); ++ } ++ } ++} ++ ++/** ++ * Terraform multiple tiles. ++ * ++ * @param iter Iterator pointing tiles to terraform and their target heights. ++ * @return The cost of all successfull operations and the last error. ++ * ++ * @note _terraform_err_tile will be set to the tile where the last error occured ++ * ++ * @warning Note non-standard return behaviour - booth the cost \b and the error combined. ++ */ ++static TerraformTilesResult TerraformTiles(TerraformingIterator *iter, DoCommandFlag flags, Money available_money) ++{ ++ TerraformTilesResult result = { ++ 0, // cost ++ false, // had_success ++ STR_NULL // last_error ++ }; ++ TileIndex last_err_tile = INVALID_TILE; + + const Company *c = Company::GetIfValid(_current_company); + int limit = (c == NULL ? INT32_MAX : GB(c->terraform_limit, 16, 16)); +- if (limit == 0) return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED); ++ if (limit == 0) result.last_error = STR_ERROR_TERRAFORM_LIMIT_REACHED; + +- TileIterator *iter = HasBit(p2, 0) ? (TileIterator *)new DiagonalTileIterator(tile, p1) : new OrthogonalTileIterator(tile, p1); +- for (; *iter != INVALID_TILE; ++(*iter)) { ++ for (; *iter != INVALID_TILE && limit > 0; ++(*iter)) { ++ int h = iter->GetTileTargetHeight(); + TileIndex t = *iter; +- uint curh = TileHeight(t); +- while (curh != h) { ++ for (int curh = TileHeight(t); curh != h; curh += (curh > h) ? -1 : 1) { + CommandCost ret = DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); + if (ret.Failed()) { +- last_error = ret; ++ result.last_error = ret.GetErrorMessage(); ++ last_err_tile = _terraform_err_tile; + + /* Did we reach the limit? */ + if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0; +@@ -466,11 +689,11 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + } + + if (flags & DC_EXEC) { +- money -= ret.GetCost(); +- if (money < 0) { ++ available_money -= ret.GetCost(); ++ if (available_money < 0) { + _additional_cash_required = ret.GetCost(); +- delete iter; +- return cost; ++ _terraform_err_tile = t; ++ return result; + } + DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); + } else { +@@ -479,20 +702,22 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + * when it's near the terraforming limit. Even then, the estimation is + * completely off due to it basically counting terraforming double, so it being + * cut off earlier might even give a better estimate in some cases. */ +- if (--limit <= 0) { +- had_success = true; ++ if (--limit <= 0) { ++ result.had_success = true; + break; + } + } + +- cost.AddCost(ret); +- curh += (curh > h) ? -1 : 1; +- had_success = true; ++ result.cost += ret.GetCost(); ++ result.had_success = true; + } ++ } + +- if (limit <= 0) break; ++ if (!result.had_success && result.last_error == STR_NULL) { ++ result.last_error = STR_ERROR_ALREADY_LEVELLED; ++ last_err_tile = INVALID_TILE; + } + +- delete iter; +- return had_success ? cost : last_error; ++ _terraform_err_tile = last_err_tile; ++ return result; + } +diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp +index 7a319d6..397dbc4 100644 +--- a/src/terraform_gui.cpp ++++ b/src/terraform_gui.cpp +@@ -190,6 +190,12 @@ struct TerraformToolbarWindow : Window { + this->last_user_action = widget; + break; + ++ case WID_TT_CLIPBOARD: // Show the clipboard toolbar ++ /* This button is NOT a place-push-button, so don't treat it as such */ ++ this->HandleButtonClick(WID_TT_CLIPBOARD); ++ ShowClipboardToolbar(); ++ break; ++ + case WID_TT_DEMOLISH: // Demolish aka dynamite button + HandlePlacePushButton(this, WID_TT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL); + this->last_user_action = widget; +@@ -325,6 +331,8 @@ static const NWidgetPart _nested_terraform_widgets[] = { + + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), EndContainer(), + ++ NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_TT_CLIPBOARD), SetMinimalSize(22, 22), ++ SetFill(0, 1), SetDataTip(SPR_IMG_CLIPBOARD, STR_LANDSCAPING_TOOLTIP_SHOW_CLIPBOARD_TOOLBAR), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_DEMOLISH), SetMinimalSize(22, 22), + SetFill(0, 1), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_BUY_LAND), SetMinimalSize(22, 22), +diff --git a/src/tile_cmd.h b/src/tile_cmd.h +index 966694b..a710feb 100644 +--- a/src/tile_cmd.h ++++ b/src/tile_cmd.h +@@ -18,6 +18,8 @@ + #include "track_type.h" + #include "tile_map.h" + ++struct CopyPasteParams; ++ + /** The returned bits of VehicleEnterTile. */ + enum VehicleEnterTileStatus { + VETS_ENTERED_STATION = 1, ///< The vehicle entered a station +@@ -137,6 +139,17 @@ typedef Foundation GetFoundationProc(TileIndex tile, Slope tileh); + typedef CommandCost TerraformTileProc(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new); + + /** ++ * Tile callback function signature for copy-pasting tile content. ++ * ++ * @param src_tile Tile to copy content from. ++ * @param dst_tile Tile where to paste the content. ++ * @param copy_paste What, where and how we are copying. ++ * @param flags Command flags passed to the copy-paste command. ++ */ ++typedef void CopyPasteTileProc(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste); ++ ++ ++/** + * Set of callback functions for performing tile operations of a given tile type. + * @see TileType + */ +@@ -155,6 +168,7 @@ struct TileTypeProcs { + VehicleEnterTileProc *vehicle_enter_tile_proc; ///< Called when a vehicle enters a tile + GetFoundationProc *get_foundation_proc; + TerraformTileProc *terraform_tile_proc; ///< Called when a terraforming operation is about to take place ++ CopyPasteTileProc *copy_paste_tile_proc; ///< Called to copy-paste content of a tile + }; + + extern const TileTypeProcs * const _tile_type_procs[16]; +diff --git a/src/tile_map.cpp b/src/tile_map.cpp +index c566ad0..911f8bc 100644 +--- a/src/tile_map.cpp ++++ b/src/tile_map.cpp +@@ -112,9 +112,10 @@ static Slope GetTileSlopeGivenHeight(int hnorth, int hwest, int heast, int hsout + * @param h If not \c NULL, pointer to storage of z height + * @return Slope of the tile, except for the HALFTILE part + */ +-Slope GetTileSlope(TileIndex tile, int *h) ++template <bool Tgeneric> ++Slope GetTileSlope(typename TileIndexT<Tgeneric>::T tile, int *h) + { +- assert(tile < MapSize()); ++ assert(IsValidTileIndex(tile)); + + uint x = TileX(tile); + uint y = TileY(tile); +@@ -123,10 +124,10 @@ Slope GetTileSlope(TileIndex tile, int *h) + return SLOPE_FLAT; + } + +- int hnorth = TileHeight(tile); // Height of the North corner. +- int hwest = TileHeight(tile + TileDiffXY(1, 0)); // Height of the West corner. +- int heast = TileHeight(tile + TileDiffXY(0, 1)); // Height of the East corner. +- int hsouth = TileHeight(tile + TileDiffXY(1, 1)); // Height of the South corner. ++ int hnorth = TileHeight(tile); // Height of the North corner. ++ int hwest = TileHeight(tile + TileDiffXY(1, 0, MapOf(tile))); // Height of the West corner. ++ int heast = TileHeight(tile + TileDiffXY(0, 1, MapOf(tile))); // Height of the East corner. ++ int hsouth = TileHeight(tile + TileDiffXY(1, 1, MapOf(tile))); // Height of the South corner. + + return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h); + } +@@ -149,6 +150,9 @@ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h) + if (h != NULL) *h *= TILE_HEIGHT; + return s; + } ++/* instantiate */ ++template Slope GetTileSlope<false>(TileIndex tile, int *h); ++template Slope GetTileSlope<true>(GenericTileIndex tile, int *h); + + /** + * Check if a given tile is flat +@@ -179,17 +183,21 @@ bool IsTileFlat(TileIndex tile, int *h) + * @param tile Tile to compute height of + * @return Minimum height of the tile + */ +-int GetTileZ(TileIndex tile) ++template <bool Tgeneric> ++int GetTileZ(typename TileIndexT<Tgeneric>::T tile) + { +- if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return 0; ++ if (TileX(tile) == MapMaxX(MapOf(tile)) || TileY(tile) == MapMaxY(MapOf(tile))) return 0; + + int h = TileHeight(tile); // N corner +- h = min(h, TileHeight(tile + TileDiffXY(1, 0))); // W corner +- h = min(h, TileHeight(tile + TileDiffXY(0, 1))); // E corner +- h = min(h, TileHeight(tile + TileDiffXY(1, 1))); // S corner ++ h = min(h, TileHeight(tile + TileDiffXY(1, 0, MapOf(tile)))); // W corner ++ h = min(h, TileHeight(tile + TileDiffXY(0, 1, MapOf(tile)))); // E corner ++ h = min(h, TileHeight(tile + TileDiffXY(1, 1, MapOf(tile)))); // S corner + + return h; + } ++/* instantiate */ ++template int GetTileZ<false>(TileIndex tile); ++template int GetTileZ<true>(GenericTileIndex tile); + + /** + * Get bottom height of the tile outside map. +@@ -212,14 +220,15 @@ int GetTilePixelZOutsideMap(int x, int y) + * @param t Tile to compute height of + * @return Maximum height of the tile + */ +-int GetTileMaxZ(TileIndex t) ++template <bool Tgeneric> ++int GetTileMaxZ(typename TileIndexT<Tgeneric>::T t) + { +- if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return TileHeightOutsideMap(TileX(t), TileY(t)); ++ if (TileX(t) == MapMaxX(MapOf(t)) || TileY(t) == MapMaxY(MapOf(t))) return TileHeightOutsideMap(TileX(t), TileY(t)); + + int h = TileHeight(t); // N corner +- h = max<int>(h, TileHeight(t + TileDiffXY(1, 0))); // W corner +- h = max<int>(h, TileHeight(t + TileDiffXY(0, 1))); // E corner +- h = max<int>(h, TileHeight(t + TileDiffXY(1, 1))); // S corner ++ h = max<int>(h, TileHeight(t + TileDiffXY(1, 0, MapOf(t)))); // W corner ++ h = max<int>(h, TileHeight(t + TileDiffXY(0, 1, MapOf(t)))); // E corner ++ h = max<int>(h, TileHeight(t + TileDiffXY(1, 1, MapOf(t)))); // S corner + + return h; + } +@@ -241,3 +250,7 @@ int GetTileMaxPixelZOutsideMap(int x, int y) + + return h * TILE_HEIGHT; + } ++ ++/* instantiate */ ++template int GetTileMaxZ<false>(TileIndex t); ++template int GetTileMaxZ<true>(GenericTileIndex t); +diff --git a/src/tile_map.h b/src/tile_map.h +index 4d5891d..c0a34b5 100644 +--- a/src/tile_map.h ++++ b/src/tile_map.h +@@ -26,13 +26,18 @@ + * + * @param tile The tile to get the height from + * @return the height of the tile +- * @pre tile < MapSize() ++ * @pre IsValidTileIndex(tile) + */ +-static inline uint TileHeight(TileIndex tile) ++template <bool Tgeneric> ++static inline uint TileHeight(typename TileIndexT<Tgeneric>::T tile) + { +- assert(tile < MapSize()); +- return _m[tile].height; ++ assert(IsValidTileIndex(tile)); ++ return GetTile(tile)->height; + } ++/** @copydoc TileHeight(TileIndexT<Tgeneric>::T) */ ++static inline uint TileHeight(TileIndex tile) { return TileHeight<false>(tile); } ++/** @copydoc TileHeight(TileIndexT<Tgeneric>::T) */ ++static inline uint TileHeight(GenericTileIndex tile) { return TileHeight<true>(tile); } + + uint TileHeightOutsideMap(int x, int y); + +@@ -43,15 +48,20 @@ uint TileHeightOutsideMap(int x, int y); + * + * @param tile The tile to change the height + * @param height The new height value of the tile +- * @pre tile < MapSize() ++ * @pre IsValidTileIndex(tile) + * @pre heigth <= MAX_TILE_HEIGHT + */ +-static inline void SetTileHeight(TileIndex tile, uint height) ++template <bool Tgeneric> ++static inline void SetTileHeight(typename TileIndexT<Tgeneric>::T tile, uint height) + { +- assert(tile < MapSize()); ++ assert(IsValidTileIndex(tile)); + assert(height <= MAX_TILE_HEIGHT); +- _m[tile].height = height; ++ GetTile(tile)->height = height; + } ++/** @copydoc SetTileHeight(TileIndexT<Tgeneric>::T,uint) */ ++static inline void SetTileHeight(TileIndex tile, uint height) { SetTileHeight<false>(tile, height); } ++/** @copydoc SetTileHeight(TileIndexT<Tgeneric>::T,uint) */ ++static inline void SetTileHeight(GenericTileIndex tile, uint height) { SetTileHeight<true>(tile, height); } + + /** + * Returns the height of a tile in pixels. +@@ -61,10 +71,15 @@ static inline void SetTileHeight(TileIndex tile, uint height) + * @param tile The tile to get the height + * @return The height of the tile in pixel + */ +-static inline uint TilePixelHeight(TileIndex tile) ++template <bool Tgeneric> ++static inline uint TilePixelHeight(typename TileIndexT<Tgeneric>::T tile) + { + return TileHeight(tile) * TILE_HEIGHT; + } ++/** @copydoc TilePixelHeight(TileIndexT<Tgeneric>::T) */ ++static inline uint TilePixelHeight(TileIndex tile) { return TilePixelHeight<false>(tile); } ++/** @copydoc TilePixelHeight(TileIndexT<Tgeneric>::T) */ ++static inline uint TilePixelHeight(GenericTileIndex tile) { return TilePixelHeight<true>(tile); } + + /** + * Returns the tile height for a coordinate outside map. Such a height is +@@ -84,30 +99,41 @@ static inline uint TilePixelHeightOutsideMap(int x, int y) + * + * @param tile The tile to get the TileType + * @return The tiletype of the tile +- * @pre tile < MapSize() ++ * @pre IsValidTileIndex(tile) + */ +-static inline TileType GetTileType(TileIndex tile) ++template <bool Tgeneric> ++static inline TileType GetTileType(typename TileIndexT<Tgeneric>::T tile) + { +- assert(tile < MapSize()); +- return (TileType)GB(_m[tile].type, 4, 4); ++ assert(IsValidTileIndex(tile)); ++ return (TileType)GB(GetTile(tile)->type, 4, 4); + } ++/** @copydoc GetTileType(TileIndexT<Tgeneric>::T) */ ++static inline TileType GetTileType(TileIndex tile) { return GetTileType<false>(tile); } ++/** @copydoc GetTileType(TileIndexT<Tgeneric>::T) */ ++static inline TileType GetTileType(GenericTileIndex tile) { return GetTileType<true>(tile); } + + /** + * Check if a tile is within the map (not a border) + * + * @param tile The tile to check + * @return Whether the tile is in the interior of the map +- * @pre tile < MapSize() ++ * @pre IsValidTileIndex(tile) + */ +-static inline bool IsInnerTile(TileIndex tile) ++template <bool Tgeneric> ++static inline bool IsInnerTile(typename TileIndexT<Tgeneric>::T tile) + { +- assert(tile < MapSize()); ++ assert(IsValidTileIndex(tile)); + + uint x = TileX(tile); + uint y = TileY(tile); + +- return x < MapMaxX() && y < MapMaxY() && ((x > 0 && y > 0) || !_settings_game.construction.freeform_edges); ++ return x < MapMaxX(MapOf(tile)) && y < MapMaxY(MapOf(tile)) && ++ ((x > 0 && y > 0) || !IsMainMapTile(tile) || !_settings_game.construction.freeform_edges); + } ++/** @copydoc IsInnerTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsInnerTile(TileIndex tile) { return IsInnerTile<false>(tile); } ++/** @copydoc IsInnerTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsInnerTile(GenericTileIndex tile) { return IsInnerTile<true>(tile); } + + /** + * Set the type of a tile +@@ -118,18 +144,23 @@ static inline bool IsInnerTile(TileIndex tile) + * + * @param tile The tile to save the new type + * @param type The type to save +- * @pre tile < MapSize() ++ * @pre IsValidTileIndex(tile) + * @pre type MP_VOID <=> tile is on the south-east or south-west edge. + */ +-static inline void SetTileType(TileIndex tile, TileType type) ++template <bool Tgeneric> ++static inline void SetTileType(typename TileIndexT<Tgeneric>::T tile, TileType type) + { +- assert(tile < MapSize()); ++ assert(IsValidTileIndex(tile)); + /* VOID tiles (and no others) are exactly allowed at the lower left and right + * edges of the map. If _settings_game.construction.freeform_edges is true, + * the upper edges of the map are also VOID tiles. */ + assert(IsInnerTile(tile) == (type != MP_VOID)); +- SB(_m[tile].type, 4, 4, type); ++ SB(GetTile(tile)->type, 4, 4, type); + } ++/** @copydoc SetTileType(TileIndexT<Tgeneric>::T,TileType) */ ++static inline void SetTileType(TileIndex tile, TileType type) { return SetTileType<false>(tile, type); } ++/** @copydoc SetTileType(TileIndexT<Tgeneric>::T) */ ++static inline void SetTileType(GenericTileIndex tile, TileType type) { return SetTileType<true>(tile, type); } + + /** + * Checks if a tile is a give tiletype. +@@ -140,10 +171,15 @@ static inline void SetTileType(TileIndex tile, TileType type) + * @param type The type to check against + * @return true If the type matches against the type of the tile + */ +-static inline bool IsTileType(TileIndex tile, TileType type) ++template <bool Tgeneric> ++static inline bool IsTileType(typename TileIndexT<Tgeneric>::T tile, TileType type) + { + return GetTileType(tile) == type; + } ++/** @copydoc IsTileType(TileIndexT<Tgeneric>::T,TileType) */ ++static inline bool IsTileType(TileIndex tile, TileType type) { return IsTileType<false>(tile, type); } ++/** @copydoc IsTileType(TileIndexT<Tgeneric>::T,TileType) */ ++static inline bool IsTileType(GenericTileIndex tile, TileType type) { return IsTileType<true>(tile, type); } + + /** + * Checks if a tile is valid +@@ -151,10 +187,15 @@ static inline bool IsTileType(TileIndex tile, TileType type) + * @param tile The tile to check + * @return True if the tile is on the map and not one of MP_VOID. + */ +-static inline bool IsValidTile(TileIndex tile) ++template <bool Tgeneric> ++static inline bool IsValidTile(typename TileIndexT<Tgeneric>::T tile) + { +- return tile < MapSize() && !IsTileType(tile, MP_VOID); ++ return IsValidTileIndex(tile) && !IsTileType(tile, MP_VOID); + } ++/** @copydoc IsValidTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsValidTile(TileIndex tile) { return IsValidTile<false>(tile); } ++/** @copydoc IsValidTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsValidTile(GenericTileIndex tile) { return IsValidTile<true>(tile); } + + /** + * Returns the owner of a tile +@@ -168,14 +209,19 @@ static inline bool IsValidTile(TileIndex tile) + * @pre IsValidTile(tile) + * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY + */ +-static inline Owner GetTileOwner(TileIndex tile) ++template <bool Tgeneric> ++static inline Owner GetTileOwner(typename TileIndexT<Tgeneric>::T tile) + { + assert(IsValidTile(tile)); + assert(!IsTileType(tile, MP_HOUSE)); + assert(!IsTileType(tile, MP_INDUSTRY)); + +- return (Owner)GB(_m[tile].m1, 0, 5); ++ return (Owner)GB(GetTile(tile)->m1, 0, 5); + } ++/** @copydoc GetTileOwner(TileIndexT<Tgeneric>::T) */ ++static inline Owner GetTileOwner(TileIndex tile) { return GetTileOwner<false>(tile); } ++/** @copydoc GetTileOwner(TileIndexT<Tgeneric>::T) */ ++static inline Owner GetTileOwner(GenericTileIndex tile) { return GetTileOwner<true>(tile); } + + /** + * Sets the owner of a tile +@@ -188,14 +234,19 @@ static inline Owner GetTileOwner(TileIndex tile) + * @pre IsValidTile(tile) + * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY + */ +-static inline void SetTileOwner(TileIndex tile, Owner owner) ++template <bool Tgeneric> ++static inline void SetTileOwner(typename TileIndexT<Tgeneric>::T tile, Owner owner) + { + assert(IsValidTile(tile)); + assert(!IsTileType(tile, MP_HOUSE)); + assert(!IsTileType(tile, MP_INDUSTRY)); + +- SB(_m[tile].m1, 0, 5, owner); ++ SB(GetTile(tile)->m1, 0, 5, owner); + } ++/** @copydoc SetTileOwner(TileIndexT<Tgeneric>::T,Owner) */ ++static inline void SetTileOwner(TileIndex tile, Owner owner) { SetTileOwner<false>(tile, owner); } ++/** @copydoc SetTileOwner(TileIndexT<Tgeneric>::T,Owner) */ ++static inline void SetTileOwner(GenericTileIndex tile, Owner owner) { SetTileOwner<true>(tile, owner); } + + /** + * Checks if a tile belongs to the given owner +@@ -204,10 +255,15 @@ static inline void SetTileOwner(TileIndex tile, Owner owner) + * @param owner The owner to check against + * @return True if a tile belongs the the given owner + */ +-static inline bool IsTileOwner(TileIndex tile, Owner owner) ++template <bool Tgeneric> ++static inline bool IsTileOwner(typename TileIndexT<Tgeneric>::T tile, Owner owner) + { + return GetTileOwner(tile) == owner; + } ++/** @copydoc IsTileOwner(TileIndexT<Tgeneric>::T,Owner) */ ++static inline bool IsTileOwner(TileIndex tile, Owner owner) { return IsTileOwner<false>(tile, owner); } ++/** @copydoc IsTileOwner(TileIndexT<Tgeneric>::T,Owner) */ ++static inline bool IsTileOwner(GenericTileIndex tile, Owner owner) { return IsTileOwner<true>(tile, owner); } + + /** + * Set the tropic zone +@@ -219,7 +275,7 @@ static inline void SetTropicZone(TileIndex tile, TropicZone type) + { + assert(tile < MapSize()); + assert(!IsTileType(tile, MP_VOID) || type == TROPICZONE_NORMAL); +- SB(_m[tile].type, 0, 2, type); ++ SB(GetTile(tile)->type, 0, 2, type); + } + + /** +@@ -231,7 +287,7 @@ static inline void SetTropicZone(TileIndex tile, TropicZone type) + static inline TropicZone GetTropicZone(TileIndex tile) + { + assert(tile < MapSize()); +- return (TropicZone)GB(_m[tile].type, 0, 2); ++ return (TropicZone)GB(GetTile(tile)->type, 0, 2); + } + + /** +@@ -243,7 +299,7 @@ static inline TropicZone GetTropicZone(TileIndex tile) + static inline byte GetAnimationFrame(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); +- return _me[t].m7; ++ return GetTileEx(t)->m7; + } + + /** +@@ -255,12 +311,29 @@ static inline byte GetAnimationFrame(TileIndex t) + static inline void SetAnimationFrame(TileIndex t, byte frame) + { + assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); +- _me[t].m7 = frame; ++ GetTileEx(t)->m7 = frame; + } + +-Slope GetTileSlope(TileIndex tile, int *h = NULL); +-int GetTileZ(TileIndex tile); +-int GetTileMaxZ(TileIndex tile); ++template <bool Tgeneric> ++Slope GetTileSlope(typename TileIndexT<Tgeneric>::T tile, int *h = NULL); ++/** @copydoc GetTileSlope(TileIndexT<Tgeneric>::T,int*) */ ++static inline Slope GetTileSlope(TileIndex tile, int *h = NULL) { return GetTileSlope<false>(tile, h); } ++/** @copydoc GetTileSlope(TileIndexT<Tgeneric>::T,int*) */ ++static inline Slope GetTileSlope(GenericTileIndex tile, int *h = NULL) { return GetTileSlope<true>(tile, h); } ++ ++template <bool Tgeneric> ++int GetTileZ(typename TileIndexT<Tgeneric>::T tile); ++/** @copydoc GetTileZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileZ(TileIndex tile) { return GetTileZ<false>(tile); } ++/** @copydoc GetTileZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileZ(GenericTileIndex tile) { return GetTileZ<true>(tile); } ++ ++template <bool Tgeneric> ++int GetTileMaxZ(typename TileIndexT<Tgeneric>::T tile); ++/** @copydoc GetTileMaxZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileMaxZ(TileIndex tile) { return GetTileMaxZ<false>(tile); } ++/** @copydoc GetTileMaxZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileMaxZ(GenericTileIndex tile) { return GetTileMaxZ<true>(tile); } + + bool IsTileFlat(TileIndex tile, int *h = NULL); + +@@ -270,12 +343,17 @@ bool IsTileFlat(TileIndex tile, int *h = NULL); + * @param h If not \c NULL, pointer to storage of z height + * @return Slope of the tile, except for the HALFTILE part + */ +-static inline Slope GetTilePixelSlope(TileIndex tile, int *h) ++template <bool Tgeneric> ++static inline Slope GetTilePixelSlope(typename TileIndexT<Tgeneric>::T tile, int *h) + { + Slope s = GetTileSlope(tile, h); + if (h != NULL) *h *= TILE_HEIGHT; + return s; + } ++/** @copydoc GetTilePixelSlope(TileIndexT<Tgeneric>::T,int*) */ ++static inline Slope GetTilePixelSlope(TileIndex tile, int *h) { return GetTilePixelSlope<false>(tile, h); } ++/** @copydoc GetTilePixelSlope(TileIndexT<Tgeneric>::T,int*) */ ++static inline Slope GetTilePixelSlope(GenericTileIndex tile, int *h) { return GetTilePixelSlope<true>(tile, h); } + + Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h); + +@@ -284,10 +362,15 @@ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h); + * @param tile Tile to compute height of + * @return Minimum height of the tile + */ +-static inline int GetTilePixelZ(TileIndex tile) ++template <bool Tgeneric> ++static inline int GetTilePixelZ(typename TileIndexT<Tgeneric>::T tile) + { + return GetTileZ(tile) * TILE_HEIGHT; + } ++/** @copydoc GetTilePixelZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTilePixelZ(TileIndex tile) { return GetTilePixelZ<false>(tile); } ++/** @copydoc GetTilePixelZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTilePixelZ(GenericTileIndex tile) { return GetTilePixelZ<true>(tile); } + + int GetTilePixelZOutsideMap(int x, int y); + +@@ -296,10 +379,15 @@ int GetTilePixelZOutsideMap(int x, int y); + * @param t Tile to compute height of + * @return Maximum height of the tile + */ +-static inline int GetTileMaxPixelZ(TileIndex tile) ++template <bool Tgeneric> ++static inline int GetTileMaxPixelZ(typename TileIndexT<Tgeneric>::T tile) + { + return GetTileMaxZ(tile) * TILE_HEIGHT; + } ++/** @copydoc GetTileMaxPixelZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileMaxPixelZ(TileIndex tile) { return GetTileMaxPixelZ<false>(tile); } ++/** @copydoc GetTileMaxPixelZ(TileIndexT<Tgeneric>::T) */ ++static inline int GetTileMaxPixelZ(GenericTileIndex tile) { return GetTileMaxPixelZ<true>(tile); } + + int GetTileMaxPixelZOutsideMap(int x, int y); + +diff --git a/src/tile_type.h b/src/tile_type.h +index 0d72092..b258fce 100644 +--- a/src/tile_type.h ++++ b/src/tile_type.h +@@ -12,6 +12,8 @@ + #ifndef TILE_TYPE_H + #define TILE_TYPE_H + ++#include "map_type.h" ++ + static const uint TILE_SIZE = 16; ///< Tile size in world coordinates. + static const uint TILE_UNIT_MASK = TILE_SIZE - 1; ///< For masking in/out the inner-tile world coordinate units. + static const uint TILE_PIXELS = 32; ///< Pixel distance between tile columns/rows in #ZOOM_LVL_BASE. +@@ -72,14 +74,91 @@ enum TropicZone { + TROPICZONE_RAINFOREST = 2, ///< Rainforest tile + }; + ++typedef uint32 RawTileIndex; ///< general purpose tile index, not bounded to any map ++static const RawTileIndex INVALID_TILE_INDEX = (RawTileIndex)-1; ++ + /** +- * The index/ID of a Tile. ++ * The index/ID of a Tile on the main map. ++ * ++ * While this is just another name for RawTileIndex type, it should be used ++ * in context of tiles of the main tile array. + */ +-typedef uint32 TileIndex; ++typedef RawTileIndex TileIndex; + + /** + * The very nice invalid tile marker + */ + static const TileIndex INVALID_TILE = (TileIndex)-1; + ++/** The index/ID of a tile bounded to a given map. */ ++struct GenericTileIndex { ++ RawTileIndex index; ///< position of the tile in array ++ Map *map; ///< the map that this index is bounded to ++ ++ inline GenericTileIndex() : map(NULL) { } ++ inline GenericTileIndex(const GenericTileIndex &tile) : index(tile.index), map(tile.map) { } ++ inline GenericTileIndex(RawTileIndex index, Map *map) : index(index), map(map) { } ++ ++ inline explicit GenericTileIndex(const TileIndex &tile) : index(tile) ++ { ++ extern MainMap _main_map; ++ this->map = &_main_map; ++ } ++ ++ inline GenericTileIndex &operator += (TileIndexDiff diff) { return this->index += diff, *this; } ++ inline GenericTileIndex &operator -= (TileIndexDiff diff) { return this->index -= diff, *this; } ++ inline GenericTileIndex operator + (TileIndexDiff diff) const { return GenericTileIndex(this->index + diff, this->map); } ++ inline GenericTileIndex operator - (TileIndexDiff diff) const { return GenericTileIndex(this->index - diff, this->map); } ++ ++ inline GenericTileIndex &operator ++ () { return ++this->index, *this; } ++ inline GenericTileIndex &operator -- () { return --this->index, *this; } ++ inline GenericTileIndex operator ++ (int) { return GenericTileIndex(this->index++, this->map); } ++ inline GenericTileIndex operator -- (int) { return GenericTileIndex(this->index--, this->map); } ++ ++ inline bool operator == (const GenericTileIndex &tile) const { return this->index == tile.index && this->map == tile.map; } ++ inline bool operator != (const GenericTileIndex &tile) const { return this->index != tile.index || this->map != tile.map; } ++ ++ inline bool operator <= (const GenericTileIndex &tile) const ++ { ++ assert(this->map == tile.map); ++ return this->index <= tile.index; ++ } ++ ++ inline bool operator >= (const GenericTileIndex &tile) const ++ { ++ assert(this->map == tile.map); ++ return this->index >= tile.index; ++ } ++ ++ inline bool operator < (const GenericTileIndex &tile) const ++ { ++ assert(this->map == tile.map); ++ return this->index < tile.index; ++ } ++ ++ inline bool operator > (const GenericTileIndex &tile) const ++ { ++ assert(this->map == tile.map); ++ return this->index > tile.index; ++ } ++ ++}; ++ ++/** ++ * Helper class to construct templatized functions operating on different ++ * types of tile indices. ++ */ ++template <bool Tgeneric> ++struct TileIndexT; ++ ++template <> ++struct TileIndexT<false> { ++ typedef TileIndex T; ++}; ++ ++template <> ++struct TileIndexT<true> { ++ typedef GenericTileIndex T; ++}; ++ + #endif /* TILE_TYPE_H */ +diff --git a/src/tilearea.cpp b/src/tilearea.cpp +index ec3b9aa..f46759d 100644 +--- a/src/tilearea.cpp ++++ b/src/tilearea.cpp +@@ -11,6 +11,7 @@ + + #include "stdafx.h" + ++#include "core/geometry_func.hpp" + #include "tilearea_type.h" + + #include "safeguards.h" +@@ -20,10 +21,12 @@ + * @param start the start of the area + * @param end the end of the area + */ +-OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) ++template <bool Tgeneric> ++OrthogonalTileAreaT<Tgeneric>::OrthogonalTileAreaT(TileIndexType start, TileIndexType end) + { +- assert(start < MapSize()); +- assert(end < MapSize()); ++ assert(IsSameMap(start, end)); ++ assert(IsValidTileIndex(start)); ++ assert(IsValidTileIndex(end)); + + uint sx = TileX(start); + uint sy = TileY(start); +@@ -33,7 +36,7 @@ OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) + if (sx > ex) Swap(sx, ex); + if (sy > ey) Swap(sy, ey); + +- this->tile = TileXY(sx, sy); ++ this->tile = TileXY<Tgeneric>(sx, sy, MapOf(start)); + this->w = ex - sx + 1; + this->h = ey - sy + 1; + } +@@ -42,9 +45,10 @@ OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) + * Add a single tile to a tile area; enlarge if needed. + * @param to_add The tile to add + */ +-void OrthogonalTileArea::Add(TileIndex to_add) ++template <bool Tgeneric> ++void OrthogonalTileAreaT<Tgeneric>::Add(TileIndexType to_add) + { +- if (this->tile == INVALID_TILE) { ++ if (!IsValidTileIndex(this->tile)) { + this->tile = to_add; + this->w = 1; + this->h = 1; +@@ -64,7 +68,7 @@ void OrthogonalTileArea::Add(TileIndex to_add) + ex = max(ax, ex); + ey = max(ay, ey); + +- this->tile = TileXY(sx, sy); ++ this->tile = TileXY<Tgeneric>(sx, sy, MapOf(to_add)); + this->w = ex - sx + 1; + this->h = ey - sy + 1; + } +@@ -74,11 +78,13 @@ void OrthogonalTileArea::Add(TileIndex to_add) + * @param ta the other tile area to check against. + * @return true if they intersect. + */ +-bool OrthogonalTileArea::Intersects(const OrthogonalTileArea &ta) const ++template <bool Tgeneric> ++bool OrthogonalTileAreaT<Tgeneric>::Intersects(const OrthogonalTileAreaT<Tgeneric> &ta) const + { + if (ta.w == 0 || this->w == 0) return false; + + assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0); ++ assert(IsSameMap(this->tile, ta.tile)); + + uint left1 = TileX(this->tile); + uint top1 = TileY(this->tile); +@@ -99,15 +105,47 @@ bool OrthogonalTileArea::Intersects(const OrthogonalTileArea &ta) const + } + + /** ++ * Does this tile area contains another? ++ * @param ta the other tile area to check against. ++ * @return true if the other area is fully contained. ++ */ ++template <bool Tgeneric> ++bool OrthogonalTileAreaT<Tgeneric>::Contains(const OrthogonalTileAreaT<Tgeneric> &ta) const ++{ ++ if (ta.w == 0 || this->w == 0) return false; ++ ++ assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0); ++ assert(IsSameMap(this->tile, ta.tile)); ++ ++ uint left1 = TileX(this->tile); ++ uint top1 = TileY(this->tile); ++ uint right1 = left1 + this->w - 1; ++ uint bottom1 = top1 + this->h - 1; ++ ++ uint left2 = TileX(ta.tile); ++ uint top2 = TileY(ta.tile); ++ uint right2 = left2 + ta.w - 1; ++ uint bottom2 = top2 + ta.h - 1; ++ ++ return ++ left2 >= left1 && ++ right2 <= right1 && ++ top2 >= top1 && ++ bottom2 <= bottom1; ++} ++ ++/** + * Does this tile area contain a tile? + * @param tile Tile to test for. + * @return True if the tile is inside the area. + */ +-bool OrthogonalTileArea::Contains(TileIndex tile) const ++template <bool Tgeneric> ++bool OrthogonalTileAreaT<Tgeneric>::Contains(TileIndexType tile) const + { + if (this->w == 0) return false; + + assert(this->w != 0 && this->h != 0); ++ assert(IsSameMap(this->tile, tile)); + + uint left = TileX(this->tile); + uint top = TileY(this->tile); +@@ -120,11 +158,62 @@ bool OrthogonalTileArea::Contains(TileIndex tile) const + /** + * Clamp the tile area to map borders. + */ +-void OrthogonalTileArea::ClampToMap() ++template <bool Tgeneric> ++void OrthogonalTileAreaT<Tgeneric>::ClampToMap() + { +- assert(this->tile < MapSize()); +- this->w = min(this->w, MapSizeX() - TileX(this->tile)); +- this->h = min(this->h, MapSizeY() - TileY(this->tile)); ++ assert(IsValidTileIndex(this->tile)); ++ this->w = min(this->w, MapSizeX(MapOf(this->tile)) - TileX(this->tile)); ++ this->h = min(this->h, MapSizeY(MapOf(this->tile)) - TileY(this->tile)); ++} ++ ++/** ++ * Get coordinates of transformed nothern tile of this area relative to the ++ * northern tile of transformed area. ++ * ++ * When transforming this area into another, the northern tile becomes some other ++ * tile in the transformed area. The function returns coordinates of this other tile ++ * relative to the transformed area. ++ * ++ * Note that calculation are independent from desired position of the transformed area. ++ * ++ * @param transformation transformation to perform ++ * @return the distance ++ */ ++template <bool Tgeneric> ++TileIndexDiffC OrthogonalTileAreaT<Tgeneric>::TransformedNorthOffset(DirTransformation transformation) const ++{ ++ Dimension distance = { this->w - 1, this->h - 1 }; ++ distance = TransformDimension(distance, transformation); ++ TileIndexDiffC ret = TransformedNorthCornerDiffC(transformation); ++ ret.x *= distance.width; ++ ret.y *= distance.height; ++ return ret; ++} ++ ++/** ++ * Get coordinates of a transformed tile of this area relative to the transformed ++ * northern tile of this area. ++ * ++ * The function takes x/y coordinates of a tile relative to this area and performs ++ * a transformation on them. ++ * ++ * Note that calculation are independent from desired position of the transformed area. ++ * ++ * @param tile the tile to transform ++ * @param transformation transformation to perform ++ * @return the distance ++ */ ++template <bool Tgeneric> ++TileIndexDiffC OrthogonalTileAreaT<Tgeneric>::TransformedTileOffset(TileIndexType tile, DirTransformation transformation) const ++{ ++ assert(IsSameMap(this->tile, tile)); ++ ++ /* calculate coordinates of the tile relative to the northern tile of the area */ ++ Point coords = { TileX(tile) - TileX(this->tile), TileY(tile) - TileY(this->tile) }; ++ /* transform coordinates, now they will be relative to the transformed northern tile of the area */ ++ coords = TransformPoint(coords, transformation); ++ TileIndexDiffC ret = { (uint16)coords.x, (uint16)coords.y }; ++ return ret; + } + + /** +@@ -132,10 +221,12 @@ void OrthogonalTileArea::ClampToMap() + * @param start First corner of the area. + * @param end Second corner of the area. + */ +-DiagonalTileArea::DiagonalTileArea(TileIndex start, TileIndex end) : tile(start) ++template <bool Tgeneric> ++DiagonalTileAreaT<Tgeneric>::DiagonalTileAreaT(TileIndexType start, TileIndexType end) : tile(start) + { +- assert(start < MapSize()); +- assert(end < MapSize()); ++ assert(IsSameMap(start, end)); ++ assert(IsValidTileIndex(start)); ++ assert(IsValidTileIndex(end)); + + /* Unfortunately we can't find a new base and make all a and b positive because + * the new base might be a "flattened" corner where there actually is no single +@@ -165,8 +256,11 @@ DiagonalTileArea::DiagonalTileArea(TileIndex start, TileIndex end) : tile(start) + * @param tile Tile to test for. + * @return True if the tile is inside the area. + */ +-bool DiagonalTileArea::Contains(TileIndex tile) const ++template <bool Tgeneric> ++bool DiagonalTileAreaT<Tgeneric>::Contains(TileIndexType tile) const + { ++ assert(IsSameMap(this->tile, tile)); ++ + int a = TileY(tile) + TileX(tile); + int b = TileY(tile) - TileX(tile); + +@@ -192,11 +286,13 @@ bool DiagonalTileArea::Contains(TileIndex tile) const + } + + /** +- * Move ourselves to the next tile in the rectangle on the map. ++ * Perform single iteration step. ++ * @param my_index Pointer to the tile index of the iterator. ++ * @param my_map The map that iterator iterates through. + */ +-TileIterator &DiagonalTileIterator::operator++() ++void DiagonalTileIteratorController::Advance(RawTileIndex *my_index, Map *my_map) + { +- assert(this->tile != INVALID_TILE); ++ assert(*my_index != INVALID_TILE_INDEX); + + /* Determine the next tile, while clipping at map borders */ + bool new_line = false; +@@ -237,9 +333,55 @@ TileIterator &DiagonalTileIterator::operator++() + uint x = this->base_x + (this->a_cur - this->b_cur) / 2; + uint y = this->base_y + (this->b_cur + this->a_cur) / 2; + /* Prevent wrapping around the map's borders. */ +- this->tile = x >= MapSizeX() || y >= MapSizeY() ? INVALID_TILE : TileXY(x, y); +- } while (this->tile > MapSize() && this->b_max != this->b_cur); ++ *my_index = x >= MapSizeX(my_map) || y >= MapSizeY(my_map) ? INVALID_TILE_INDEX : TileXY<true>(x, y, my_map).index; ++ } while (!IsValidTileIndex(GenericTileIndex(*my_index, my_map)) && this->b_max != this->b_cur); ++ ++ if (this->b_max == this->b_cur) *my_index = INVALID_TILE_INDEX; ++} ++ ++/** ++ * Initialize iteration. ++ * @param src_index Pointer to the source tile index of the iterator. The index must be set to the northern tile of the source area before you call Init. ++ * @param dst_index Pointer to the destination tile index of the iterator. The index must be set to the transformed northern tile of the source area before you call Init. ++ * @param src_w The width of the source area. ++ * @param src_h The height of the source area. ++ * @param transformation Transformation to perform. ++ */ ++void TransformationTileIteratorController::Init(RawTileIndex *src_index, RawTileIndex *dst_index, uint16 src_w, uint16 src_h, DirTransformation transformation) ++{ ++ assert((*src_index != INVALID_TILE_INDEX) == (*dst_index != INVALID_TILE_INDEX)); + +- if (this->b_max == this->b_cur) this->tile = INVALID_TILE; +- return *this; ++ this->OrthogonalTileIteratorController::Init(src_index, src_w, src_h); ++ this->transformation = transformation; + } ++ ++/** ++ * Perform single iteration step. ++ * @param src_index Pointer to the source tile index of the iterator. ++ * @param src_map The source map that iterator iterates through. ++ * @param dst_index Pointer to the destination tile index of the iterator. ++ * @param dst_map The destination map that iterator iterates through. ++ */ ++void TransformationTileIteratorController::Advance(RawTileIndex *src_index, Map *src_map, RawTileIndex *dst_index, Map *dst_map) ++{ ++ assert(*src_index != INVALID_TILE_INDEX); ++ ++ if (--this->x > 0) { ++ ++*src_index; ++ *dst_index += ToTileIndexDiff<true>(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, this->transformation)), dst_map); ++ } else if (--this->y > 0) { ++ this->x = this->w; ++ *src_index += TileDiffXY(1, 1, src_map) - this->w; ++ *dst_index -= ToTileIndexDiff<true>(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, this->transformation)), dst_map) * (this->w - 1); ++ *dst_index += ToTileIndexDiff<true>(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SE, this->transformation)), dst_map); ++ } else { ++ *src_index = INVALID_TILE_INDEX; ++ *dst_index = INVALID_TILE_INDEX; ++ } ++} ++ ++/* instantiate */ ++template struct OrthogonalTileAreaT<false>; ++template struct OrthogonalTileAreaT<true>; ++template struct DiagonalTileAreaT<false>; ++template struct DiagonalTileAreaT<true>; +diff --git a/src/tilearea_func.h b/src/tilearea_func.h +new file mode 100644 +index 0000000..84cfd77 +--- /dev/null ++++ b/src/tilearea_func.h +@@ -0,0 +1,42 @@ ++/* $Id$ */ ++ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file tilearea_type.h Functions related to tile areas. */ ++ ++#ifndef TILEAREA_FUNC_H ++#define TILEAREA_FUNC_H ++ ++#include "core/math_func.hpp" ++#include "direction_func.h" ++#include "tilearea_type.h" ++ ++/** ++ * Transform a tile area. ++ * @param trackdir The area to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed area. ++ */ ++template <bool TgenericSrc, bool TgenericDst> ++static OrthogonalTileAreaT<TgenericDst> TransformTileArea(const OrthogonalTileAreaT<TgenericSrc> &ta, typename TileIndexT<TgenericDst>::T dst_area_north, DirTransformation transformation) ++{ ++ OrthogonalTileAreaT<TgenericDst> ret(dst_area_north, ta.w, ta.h); ++ if (TransformAxis(AXIS_X, transformation) != AXIS_X) Swap(ret.w, ret.h); ++ return ret; ++} ++ ++/** @copydoc TransformTileArea<bool,bool> */ ++static inline TileArea TransformTileArea(const TileArea &ta, TileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea<false, false>(ta, dst_area_north, transformation); } ++/** @copydoc TransformTileArea<bool,bool> */ ++static inline GenericTileArea TransformTileArea(const TileArea &ta, GenericTileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea<false, true>(ta, dst_area_north, transformation); } ++/** @copydoc TransformTileArea<bool,bool> */ ++static inline TileArea TransformTileArea(const GenericTileArea &ta, TileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea<true, false>(ta, dst_area_north, transformation); } ++/** @copydoc TransformTileArea<bool,bool> */ ++static inline GenericTileArea TransformTileArea(const GenericTileArea &ta, GenericTileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea<true, true>(ta, dst_area_north, transformation); } ++ ++#endif /* TILEAREA_FUNC_H */ +diff --git a/src/tilearea_type.h b/src/tilearea_type.h +index 45bfb3d..114d10a 100644 +--- a/src/tilearea_type.h ++++ b/src/tilearea_type.h +@@ -9,16 +9,55 @@ + + /** @file tilearea_type.h Type for storing the 'area' of something uses on the map. */ + ++/** ++ * @defgroup TileIndexTransformations Tile index transformations ++ * ++ * @image html tile_index_transformations.svg ++ */ ++ + #ifndef TILEAREA_TYPE_H + #define TILEAREA_TYPE_H + + #include "map_func.h" ++#include "direction_type.h" + +-/** Represents the covered area of e.g. a rail station */ +-struct OrthogonalTileArea { +- TileIndex tile; ///< The base tile of the area +- uint16 w; ///< The width of the area +- uint16 h; ///< The height of the area ++/** ++ * Set of coordinates representing rectangular piece of a tile map. ++ * ++ * This "raw" area does not point to any map. These are pure coordinates. Unless ++ * we bound them to a cartain map we cannot make most of calculations. ++ * ++ * @see RawTileIndex ++ * @see OrthogonalTileAreaT ++ */ ++struct RawTileArea { ++ RawTileIndex tile; ///< The base (northern) tile of the area ++ uint16 w; ///< The width of the area ++ uint16 h; ///< The height of the area ++}; ++ ++/** ++ * Set of coordinates representing rectangular piece of a tile map e.g. a rail station. ++ * ++ * This tile area, based on template args, can represent a part of either the main map ++ * or any chosen map. ++ * ++ * @tparam Tgeneric If \c false, this area will represent tiles on the main map. ++ * If \c true, this area will be able to represent tiles on any map chosen at runtime. ++ * ++ * @note To use a specific overload there are #TileArea and #GenericTileArea to your ++ * disposition. Use OrthogonalTileAreaT type directly only when working with templates. ++ * ++ * @see RawOrthogonalTileArea ++ * @see TileIndexT ++ */ ++template <bool Tgeneric> ++struct OrthogonalTileAreaT { ++ typedef typename TileIndexT<Tgeneric>::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. ++ ++ TileIndexType tile; ///< The base tile of the area ++ uint16 w; ///< The width of the area ++ uint16 h; ///< The height of the area + + /** + * Construct this tile area with some set values +@@ -26,27 +65,48 @@ struct OrthogonalTileArea { + * @param w the width + * @param h the height + */ +- OrthogonalTileArea(TileIndex tile = INVALID_TILE, uint8 w = 0, uint8 h = 0) : tile(tile), w(w), h(h) ++ OrthogonalTileAreaT(TileIndexType tile = TileIndexType(INVALID_TILE), uint8 w = 0, uint8 h = 0) : tile(tile), w(w), h(h) ++ { ++ } ++ ++ /** ++ * Make a copy of a given tile area ++ * @param ta the area to copy ++ */ ++ template <bool TotherGeneric> ++ OrthogonalTileAreaT(const OrthogonalTileAreaT<TotherGeneric> &ta) ++ : tile(MakeTileIndex<Tgeneric>(IndexOf(ta.tile), MapOf(ta.tile))), w(ta.w), h(ta.h) ++ { ++ } ++ ++ /** ++ * Construct this tile area from a "raw" tile area and a given tile map ++ * @param ta the "raw" tile area ++ * @param map the map ++ */ ++ inline OrthogonalTileAreaT(const RawTileArea &ta, Map *map) ++ : tile(MakeTileIndex<Tgeneric>(ta.tile, map)), w(ta.w), h(ta.h) + { + } + +- OrthogonalTileArea(TileIndex start, TileIndex end); ++ OrthogonalTileAreaT(TileIndexType start, TileIndexType end); + +- void Add(TileIndex to_add); ++ void Add(TileIndexType to_add); + + /** + * Clears the 'tile area', i.e. make the tile invalid. + */ + void Clear() + { +- this->tile = INVALID_TILE; +- this->w = 0; +- this->h = 0; ++ IndexOf(this->tile) = INVALID_TILE_INDEX; ++ this->w = 0; ++ this->h = 0; + } + +- bool Intersects(const OrthogonalTileArea &ta) const; ++ bool Intersects(const OrthogonalTileAreaT<Tgeneric> &ta) const; + +- bool Contains(TileIndex tile) const; ++ bool Contains(const OrthogonalTileAreaT<Tgeneric> &ta) const; ++ bool Contains(TileIndexType tile) const; + + void ClampToMap(); + +@@ -54,16 +114,111 @@ struct OrthogonalTileArea { + * Get the center tile. + * @return The tile at the center, or just north of it. + */ +- TileIndex GetCenterTile() const ++ TileIndexType GetCenterTile() const + { + return TILE_ADDXY(this->tile, this->w / 2, this->h / 2); + } ++ ++ TileIndexDiffC TransformedNorthOffset(DirTransformation transformation) const; ++ TileIndexDiffC TransformedTileOffset(TileIndexType tile, DirTransformation transformation) const; ++ ++ /** ++ * Transform northern tile of this area based on a given northern tile of transformed area. ++ * ++ * @param dst_area_north Point of reference, the northern tile of the transformed area. ++ * @param transformation Transformation to perform. ++ * @return Northern tile of this area after transformation. ++ * ++ * @see TileIndexTransformations ++ * ++ * @ingroup TileIndexTransformations ++ */ ++ template <bool TgenericDst> ++ typename TileIndexT<TgenericDst>::T TransformedNorth(typename TileIndexT<TgenericDst>::T dst_area_north, DirTransformation transformation) const ++ { ++ TileIndexDiffC offs = this->TransformedNorthOffset(transformation); ++ return TILE_ADDXY(dst_area_north, offs.x, offs.y); ++ } ++ /** @copydoc OrthogonalTileAreaT::TransformedNorth(TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline TileIndex TransformedNorth(TileIndex dst_area_north, DirTransformation transformation) const { return this->TransformedNorth<false>(dst_area_north, transformation); } ++ /** @copydoc OrthogonalTileAreaT::TransformedNorth(TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline GenericTileIndex TransformedNorth(GenericTileIndex dst_area_north, DirTransformation transformation) const { return this->TransformedNorth<true>(dst_area_north, transformation); } ++ ++ /** ++ * Calculate northern tile of transformed area based on transformed northern tile of this area. ++ * ++ * @param transformed_north Point of reference, northern tile of this area after transformation. ++ * @param transformation The transformation. ++ * @return Northern tile of the transformed area. ++ * ++ * @see TileIndexTransformations ++ * ++ * @ingroup TileIndexTransformations ++ */ ++ template <bool TgenericDst> ++ typename TileIndexT<TgenericDst>::T ReverseTransformedNorth(typename TileIndexT<TgenericDst>::T transformed_north, DirTransformation transformation) const ++ { ++ TileIndexDiffC offs = this->TransformedNorthOffset(transformation); ++ return TILE_ADDXY(transformed_north, -offs.x, -offs.y); ++ } ++ /** @copydoc OrthogonalTileAreaT::ReverseTransformedNorth(TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline TileIndex ReverseTransformedNorth(TileIndex transformed_north, DirTransformation transformation) const { return this->ReverseTransformedNorth<false>(transformed_north, transformation); } ++ /** @copydoc OrthogonalTileAreaT::ReverseTransformedNorth(TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline GenericTileIndex ReverseTransformedNorth(GenericTileIndex transformed_north, DirTransformation transformation) const { return this->ReverseTransformedNorth<true>(transformed_north, transformation); } ++ ++ /** ++ * Transform a given tile within this area. ++ * ++ * @param tile The tile to transform. ++ * @param transformed_north Point of reference, northern tile of this area after transformation. ++ * @param transformation Transformation to perform. ++ * @return Transformed tile. ++ * ++ * @see TileIndexTransformations ++ * ++ * @ingroup TileIndexTransformations ++ */ ++ template <bool TgenericDst> ++ typename TileIndexT<TgenericDst>::T TransformTile(TileIndexType tile, typename TileIndexT<TgenericDst>::T transformed_north, DirTransformation transformation) const ++ { ++ TileIndexDiffC offs = this->TransformedTileOffset(tile, transformation); ++ return TILE_ADDXY(transformed_north, offs.x, offs.y); ++ } ++ /** @copydoc OrthogonalTileAreaT::TransformTile(TileIndexType,TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline TileIndex TransformTile(TileIndexType tile, TileIndex transformed_north, DirTransformation transformation) const { return this->TransformTile<false>(tile, transformed_north, transformation); } ++ /** @copydoc OrthogonalTileAreaT::TransformTile(TileIndexType,TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline GenericTileIndex TransformTile(TileIndexType tile, GenericTileIndex transformed_north, DirTransformation transformation) const { return this->TransformTile<true>(tile, transformed_north, transformation); } ++ ++ /** ++ * Get the point of reference of a transfomation based on a given tile before and after transformation. ++ * ++ * @param tile The tile before transformation. ++ * @param transformed_tile The tile after transformation. ++ * @param transformation The transformation. ++ * @return The point of reference (northern tile of this area after transformation). ++ * ++ * @see TileIndexTransformations ++ * ++ * @ingroup TileIndexTransformations ++ */ ++ template <bool TgenericDst> ++ typename TileIndexT<TgenericDst>::T ReverseTransformTile(TileIndexType source_tile, typename TileIndexT<TgenericDst>::T transformed_tile, DirTransformation transformation) const ++ { ++ TileIndexDiffC offs = this->TransformedTileOffset(source_tile, transformation); ++ return TILE_ADDXY(transformed_tile, -offs.x, -offs.y); ++ } ++ /** @copydoc OrthogonalTileAreaT::ReverseTransformTile(TileIndexType,TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline TileIndex ReverseTransformTile(TileIndexType source_tile, TileIndex transformed_tile, DirTransformation transformation) const { return this->ReverseTransformTile<false>(source_tile, transformed_tile, transformation); } ++ /** @copydoc OrthogonalTileAreaT::ReverseTransformTile(TileIndexType,TileIndexT<TgenericDst>::T,DirTransformation) */ ++ inline GenericTileIndex ReverseTransformTile(TileIndexType source_tile, GenericTileIndex transformed_tile, DirTransformation transformation) const { return this->ReverseTransformTile<true>(source_tile, transformed_tile, transformation); } + }; + + /** Represents a diagonal tile area. */ +-struct DiagonalTileArea { ++template <bool Tgeneric> ++struct DiagonalTileAreaT { ++ typedef typename TileIndexT<Tgeneric>::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. + +- TileIndex tile; ///< Base tile of the area ++ TileIndexType tile; ///< Base tile of the area + int16 a; ///< Extent in diagonal "x" direction (may be negative to signify the area stretches to the left) + int16 b; ///< Extent in diagonal "y" direction (may be negative to signify the area stretches upwards) + +@@ -73,52 +228,96 @@ struct DiagonalTileArea { + * @param a The "x" extent. + * @param b The "y" estent. + */ +- DiagonalTileArea(TileIndex tile = INVALID_TILE, int8 a = 0, int8 b = 0) : tile(tile), a(a), b(b) ++ DiagonalTileAreaT(TileIndexType tile = TileIndexType(INVALID_TILE), int8 a = 0, int8 b = 0) : tile(tile), a(a), b(b) + { + } + +- DiagonalTileArea(TileIndex start, TileIndex end); ++ DiagonalTileAreaT(TileIndexType start, TileIndexType end); + + /** + * Clears the TileArea by making the tile invalid and setting a and b to 0. + */ + void Clear() + { +- this->tile = INVALID_TILE; +- this->a = 0; +- this->b = 0; ++ IndexOf(this->tile) = INVALID_TILE_INDEX; ++ this->a = 0; ++ this->b = 0; + } + +- bool Contains(TileIndex tile) const; ++ bool Contains(TileIndexType tile) const; + }; + ++/** ++ * Set of coordinates representing rectangular piece of the main tile map. ++ * ++ * This is the most common type of tile area. It represents tiles on the main tile array. ++ * ++ * @see TileIndex ++ * @see GenericTileArea ++ */ ++typedef OrthogonalTileAreaT<false> OrthogonalTileArea; ++ + /** Shorthand for the much more common orthogonal tile area. */ + typedef OrthogonalTileArea TileArea; + ++/** ++ * Set of coordinates representing rectangular piece of a tile map. ++ * ++ * This "generic" tile area is able to represent part of any map chosen at runtime. ++ * ++ * @see GenericTileIndex ++ * @see TileArea ++ */ ++typedef OrthogonalTileAreaT<true> GenericTileArea; ++ ++typedef DiagonalTileAreaT<false> DiagonalTileArea; ++ + /** Base class for tile iterators. */ +-class TileIterator { ++template <bool Tgeneric> ++class TileIteratorT { ++public: ++ typedef typename TileIndexT<Tgeneric>::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. ++ + protected: +- TileIndex tile; ///< The current tile we are at. ++ TileIndexType tile; ///< The current tile we are at. + + /** + * Initialise the iterator starting at this tile. + * @param tile The tile we start iterating from. + */ +- TileIterator(TileIndex tile = INVALID_TILE) : tile(tile) ++ TileIteratorT(TileIndexType tile = TileIndexType(INVALID_TILE)) : tile(tile) + { + } + ++ /** ++ * Get the raw tile index of this iterator. ++ * @return Pointer to the index. ++ */ ++ inline RawTileIndex *MyIndex() ++ { ++ return &IndexOf(this->tile); ++ } ++ ++ /** ++ * Get the map of this iterator. ++ * @return The map that this iterator iterates through. ++ */ ++ inline Map *MyMap() const ++ { ++ return MapOf(this->tile); ++ } ++ + public: + /** Some compilers really like this. */ +- virtual ~TileIterator() ++ virtual ~TileIteratorT() + { + } + + /** + * Get the tile we are currently at. +- * @return The tile we are at, or INVALID_TILE when we're done. ++ * @return The tile we are at, or "invalid" when we're done. + */ +- inline operator TileIndex () const ++ inline operator TileIndexType () const + { + return this->tile; + } +@@ -126,28 +325,71 @@ public: + /** + * Move ourselves to the next tile in the rectangle on the map. + */ +- virtual TileIterator& operator ++() = 0; ++ virtual TileIteratorT<Tgeneric>& operator ++() = 0; + + /** + * Allocate a new iterator that is a copy of this one. + */ +- virtual TileIterator *Clone() const = 0; ++ virtual TileIteratorT<Tgeneric> *Clone() const = 0; + }; + +-/** Iterator to iterate over a tile area (rectangle) of the map. */ +-class OrthogonalTileIterator : public TileIterator { +-private: +- int w; ///< The width of the iterated area. +- int x; ///< The current 'x' position in the rectangle. +- int y; ///< The current 'y' position in the rectangle. ++/** Base class for tile iterators of the main map. */ ++typedef TileIteratorT<false> TileIterator; ++ ++/** Helper class to build diagonal tile iterators. */ ++class OrthogonalTileIteratorController { ++public: ++ int w; ///< The width of the iterated area. ++ int x; ///< The current 'x' position in the rectangle. ++ int y; ///< The current 'y' position in the rectangle. ++ ++ /** ++ * Initialize iteration. ++ * @param my_index Pointer to the tile index of the iterator. The index must be set to the first tile of the iteration before you call Init. ++ * @param w The width of the iterated area. ++ * @param h The height of the iterated area. ++ */ ++ void Init(RawTileIndex *my_index, uint w, uint h) ++ { ++ this->w = w; ++ this->x = w; ++ this->y = h; ++ if (w == 0 || h == 0) *my_index = INVALID_TILE_INDEX; ++ } ++ ++ /** ++ * Perform single iteration step. ++ * @param my_index Pointer to the tile index of the iterator. ++ * @param my_map The map that iterator iterates through. ++ */ ++ inline void Advance(RawTileIndex *my_index, Map *my_map) ++ { ++ assert(*my_index != INVALID_TILE_INDEX); + ++ if (--this->x > 0) { ++ ++*my_index; ++ } else if (--this->y > 0) { ++ this->x = this->w; ++ *my_index += TileDiffXY(1, 1, my_map) - this->w; ++ } else { ++ *my_index = INVALID_TILE_INDEX; ++ } ++ } ++}; ++ ++/** Iterator to iterate over a tile area (rectangle) of a map. */ ++template <bool Tgeneric> ++class OrthogonalTileIteratorT : public TileIteratorT<Tgeneric>, protected OrthogonalTileIteratorController { + public: ++ typedef typename TileIteratorT<Tgeneric>::TileIndexType TileIndexType; ++ + /** + * Construct the iterator. + * @param ta Area, i.e. begin point and width/height of to-be-iterated area. + */ +- OrthogonalTileIterator(const OrthogonalTileArea &ta) : TileIterator(ta.w == 0 || ta.h == 0 ? INVALID_TILE : ta.tile), w(ta.w), x(ta.w), y(ta.h) ++ OrthogonalTileIteratorT(const OrthogonalTileAreaT<Tgeneric> &ta) : TileIteratorT<Tgeneric>(ta.tile) + { ++ this->Init(this->MyIndex(), ta.w, ta.h); + } + + /** +@@ -155,38 +397,32 @@ public: + * @param corner1 Tile from where to begin iterating. + * @param corner2 Tile where to end the iterating. + */ +- OrthogonalTileIterator(TileIndex corner1, TileIndex corner2) ++ OrthogonalTileIteratorT(TileIndexType corner1, TileIndexType corner2) + { +- *this = OrthogonalTileIterator(OrthogonalTileArea(corner1, corner2)); ++ *this = OrthogonalTileIteratorT<Tgeneric>(OrthogonalTileAreaT<Tgeneric>(corner1, corner2)); + } + + /** + * Move ourselves to the next tile in the rectangle on the map. + */ +- inline TileIterator& operator ++() ++ inline TileIteratorT<Tgeneric>& operator ++() + { +- assert(this->tile != INVALID_TILE); +- +- if (--this->x > 0) { +- this->tile++; +- } else if (--this->y > 0) { +- this->x = this->w; +- this->tile += TileDiffXY(1, 1) - this->w; +- } else { +- this->tile = INVALID_TILE; +- } ++ this->Advance(this->MyIndex(), this->MyMap()); + return *this; + } + +- virtual TileIterator *Clone() const ++ virtual TileIteratorT<Tgeneric> *Clone() const + { +- return new OrthogonalTileIterator(*this); ++ return new OrthogonalTileIteratorT<Tgeneric>(*this); + } + }; + +-/** Iterator to iterate over a diagonal area of the map. */ +-class DiagonalTileIterator : public TileIterator { +-private: ++/** Iterator to iterate over a tile area (rectangle) of the main map. */ ++typedef OrthogonalTileIteratorT<false> OrthogonalTileIterator; ++ ++/** Helper class to build diagonal tile iterators. */ ++class DiagonalTileIteratorController { ++public: + uint base_x; ///< The base tile x coordinate from where the iterating happens. + uint base_y; ///< The base tile y coordinate from where the iterating happens. + int a_cur; ///< The current (rotated) x coordinate of the iteration. +@@ -194,15 +430,32 @@ private: + int a_max; ///< The (rotated) x coordinate of the end of the iteration. + int b_max; ///< The (rotated) y coordinate of the end of the iteration. + ++ void Init(RawTileIndex *my_index, int a_max, int b_max, Map *my_map) ++ { ++ this->base_x = TileX(GenericTileIndex(*my_index, my_map)); ++ this->base_y = TileY(GenericTileIndex(*my_index, my_map)); ++ this->a_cur = 0; ++ this->b_cur = 0; ++ this->a_max = a_max; ++ this->b_max = b_max; ++ } ++ ++ void Advance(RawTileIndex *my_index, Map *my_map); ++}; ++ ++/** Iterator to iterate over a diagonal area of a map. */ ++template <bool Tgeneric> ++class DiagonalTileIteratorT : public TileIteratorT<Tgeneric>, protected DiagonalTileIteratorController { + public: ++ typedef typename TileIteratorT<Tgeneric>::TileIndexType TileIndexType; + + /** + * Construct the iterator. + * @param ta Area, i.e. begin point and (diagonal) width/height of to-be-iterated area. + */ +- DiagonalTileIterator(const DiagonalTileArea &ta) : +- TileIterator(ta.tile), base_x(TileX(ta.tile)), base_y(TileY(ta.tile)), a_cur(0), b_cur(0), a_max(ta.a), b_max(ta.b) ++ DiagonalTileIteratorT(const DiagonalTileAreaT<Tgeneric> &ta) : TileIteratorT<Tgeneric>(ta.tile) + { ++ this->Init(this->MyIndex(), ta.a, ta.b, this->MyMap()); + } + + /** +@@ -210,19 +463,94 @@ public: + * @param corner1 Tile from where to begin iterating. + * @param corner2 Tile where to end the iterating. + */ +- DiagonalTileIterator(TileIndex corner1, TileIndex corner2) ++ DiagonalTileIteratorT(TileIndexType corner1, TileIndexType corner2) + { +- *this = DiagonalTileIterator(DiagonalTileArea(corner1, corner2)); ++ *this = DiagonalTileIteratorT<Tgeneric>(DiagonalTileAreaT<Tgeneric>(corner1, corner2)); + } + +- TileIterator& operator ++(); ++ inline TileIteratorT<Tgeneric>& operator ++() ++ { ++ this->Advance(this->MyIndex(), this->MyMap()); ++ return *this; ++ } + +- virtual TileIterator *Clone() const ++ virtual TileIteratorT<Tgeneric> *Clone() const + { +- return new DiagonalTileIterator(*this); ++ return new DiagonalTileIteratorT<Tgeneric>(*this); + } + }; + ++/** Iterator to iterate over a diagonal area of the main map. */ ++typedef DiagonalTileIteratorT<false> DiagonalTileIterator; ++ ++/** Helper class to build transformative tile iterators. */ ++class TransformationTileIteratorController : public OrthogonalTileIteratorController { ++public: ++ DirTransformation transformation; ///< Transformation to perform. ++ ++ void Init(RawTileIndex *src_index, RawTileIndex *dst_index, uint16 src_w, uint16 src_h, DirTransformation transformation); ++ void Advance(RawTileIndex *src_index, Map *src_map, RawTileIndex *dst_index, Map *dst_map); ++}; ++ ++/** ++ * Iterator to iterate over a diagonal area of a map performing transformation on tile indices. ++ * ++ * Iterator will iterate over source area in the same way OrthogonalTileIteratorT do, additionally ++ * performing transformation on tile indices. You can call SrcTile or DstTile to get the tile before ++ * and after transformation. ++ * ++ * The tile of this iterator (it's base) is the transformed one. ++ */ ++template <bool TgenericSrc, bool TgenericDst> ++class TransformationTileIteratorT : public TileIteratorT<TgenericDst>, protected TransformationTileIteratorController { ++public: ++ typedef typename TileIndexT<TgenericDst>::T DstTileIndexType; ++ typedef typename TileIndexT<TgenericSrc>::T SrcTileIndexType; ++ ++protected: ++ SrcTileIndexType src_tile; ///< Current tile of the source area. ++ ++public: ++ /** ++ * Create a TransformationTileIteratorT. ++ * ++ * @param src_area Source area to be transformed and iterated over. ++ * @param transformed_north Transformed northern tile of the source area. ++ * @param transformation Transformation to perform. ++ */ ++ TransformationTileIteratorT(const OrthogonalTileAreaT<TgenericSrc> &src_area, DstTileIndexType transformed_north, DirTransformation transformation) ++ : TileIteratorT<TgenericDst>(transformed_north), src_tile(src_area.tile) ++ { ++ this->Init(&IndexOf(this->src_tile), this->MyIndex(), src_area.w, src_area.h, transformation); ++ } ++ ++ /** ++ * The source tile of the transformation. ++ * @return Tile before transformation. ++ */ ++ inline const SrcTileIndexType &SrcTile() const { return this->src_tile; } ++ ++ /** ++ * The destination tile of the transformation (the tile of this iterator). ++ * @return Tile after transformation. ++ */ ++ inline const DstTileIndexType &DstTile() const { return this->tile; } ++ ++ virtual TileIteratorT<TgenericDst> &operator ++ () ++ { ++ this->Advance(&IndexOf(this->src_tile), MapOf(this->src_tile), this->MyIndex(), this->MyMap()); ++ return *this; ++ } ++ ++ virtual TileIteratorT<TgenericDst> *Clone() const ++ { ++ return new TransformationTileIteratorT<TgenericSrc, TgenericDst>(*this); ++ } ++}; ++ ++/** Iterator to iterate over a diagonal area of the main map performing transformation on tile indices. */ ++typedef TransformationTileIteratorT<false, false> TransformationTileIterator; ++ + /** + * A loop which iterates over the tiles of a TileArea. + * @param var The name of the variable which contains the current tile. +@@ -231,4 +559,12 @@ public: + */ + #define TILE_AREA_LOOP(var, ta) for (OrthogonalTileIterator var(ta); var != INVALID_TILE; ++var) + ++/** ++ * A loop which iterates over the tiles of a GenericTileArea. ++ * @param var The name of the variable which contains the current tile. ++ * This variable will be allocated in this \c for of this loop. ++ * @param ta The tile area to search over. ++ */ ++#define GENERIC_TILE_AREA_LOOP(var, ta) for (OrthogonalTileIteratorT<true> var(ta); IsValidTileIndex<true>(var); ++var) ++ + #endif /* TILEAREA_TYPE_H */ +diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h +index 3d64248..1157697 100644 +--- a/src/tilehighlight_type.h ++++ b/src/tilehighlight_type.h +@@ -19,16 +19,17 @@ + + /** Highlighting draw styles */ + enum HighLightStyle { +- HT_NONE = 0x000, ///< default +- HT_RECT = 0x010, ///< rectangle (stations, depots, ...) +- HT_POINT = 0x020, ///< point (lower land, raise land, level land, ...) +- HT_SPECIAL = 0x030, ///< special mode used for highlighting while dragging (and for tunnels/docks) +- HT_DRAG = 0x040, ///< dragging items in the depot windows +- HT_LINE = 0x008, ///< used for autorail highlighting (longer stretches), lower bits: direction +- HT_RAIL = 0x080, ///< autorail (one piece), lower bits: direction +- HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) +- HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT. +- HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes. ++ HT_NONE = 0x000, ///< default ++ HT_RECT = 0x010, ///< rectangle (stations, depots, ...) ++ HT_POINT = 0x020, ///< point (lower land, raise land, level land, ...) ++ HT_SPECIAL = 0x030, ///< special mode used for highlighting while dragging (and for tunnels/docks) ++ HT_DRAG = 0x040, ///< dragging items in the depot windows ++ HT_LINE = 0x008, ///< used for autorail highlighting (longer stretches), lower bits: direction ++ HT_RAIL = 0x080, ///< autorail (one piece), lower bits: direction ++ HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) ++ HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT. ++ HT_PASTE_PREVIEW = 0x400, ///< Preview of a paste result. Only usable in combination with #HT_POINT. ++ HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes. + + /* lower bits (used with HT_LINE and HT_RAIL): + * (see ASCII art in table/autorail.h for a visual interpretation) */ +diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp +index 45d751d..91553ae 100644 +--- a/src/toolbar_gui.cpp ++++ b/src/toolbar_gui.cpp +@@ -964,7 +964,7 @@ static CallBackFunction MenuClickBuildAir(int index) + + static CallBackFunction ToolbarForestClick(Window *w) + { +- PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 3); ++ PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 4); + return CBF_NONE; + } + +@@ -978,8 +978,9 @@ static CallBackFunction MenuClickForest(int index) + { + switch (index) { + case 0: ShowTerraformToolbar(); break; +- case 1: ShowBuildTreesToolbar(); break; +- case 2: return SelectSignTool(); ++ case 1: ShowClipboardToolbar(); break; ++ case 2: ShowBuildTreesToolbar(); break; ++ case 3: return SelectSignTool(); + } + return CBF_NONE; + } +diff --git a/src/town.h b/src/town.h +index 010c7c2..8165110 100644 +--- a/src/town.h ++++ b/src/town.h +@@ -15,6 +15,9 @@ + #include "viewport_type.h" + #include "town_map.h" + #include "subsidy_type.h" ++#include "openttd.h" ++#include "table/strings.h" ++#include "company_func.h" + #include "newgrf_storage.h" + #include "cargotype.h" + #include "tilematrix_type.hpp" +@@ -75,6 +78,7 @@ struct Town : TownPool::PoolItem<&_town_pool> { + CompanyByte exclusivity; ///< which company has exclusivity + uint8 exclusive_counter; ///< months till the exclusivity expires + int16 ratings[MAX_COMPANIES]; ///< ratings of each company for this town ++ StringID town_label; ///< Label dependent on _local_company rating. + + TransportedCargoStat<uint32> supplied[NUM_CARGO]; ///< Cargo statistics about supplied cargo. + TransportedCargoStat<uint16> received[NUM_TE]; ///< Cargo statistics about received cargotypes. +@@ -113,6 +117,30 @@ struct Town : TownPool::PoolItem<&_town_pool> { + + void InitializeLayout(TownLayout layout); + ++ void UpdateLabel(); ++ ++ /** ++ * Returns the correct town label, based on rating. ++ */ ++ StringID Label() const{ ++ if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { ++ return STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING + this->town_label; ++ } else { ++ return _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN; ++ } ++ } ++ ++ /** ++ * Returns the correct town small label, based on rating. ++ */ ++ StringID SmallLabel() const{ ++ if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { ++ return STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING + this->town_label; ++ } else { ++ return STR_VIEWPORT_TOWN_TINY_WHITE; ++ } ++ } ++ + /** + * Calculate the max town noise. + * The value is counted using the population divided by the content of the +@@ -145,6 +173,40 @@ void ShowTownViewWindow(TownID town); + void ExpandTown(Town *t); + + /** ++ * Return the offset for a given town rating. ++ * ++ * This function returns an offset which can be ++ * added to the STR_CARGO_RATING_APPALLING value to get the ++ * corresponding StringID for the given town rating. ++ * ++ * @param rating The town rating ++ * @return The offset for the given rating ++ */ ++static inline StringID OffsetByTownRating(int16 rating) ++{ ++ int16 ratings[8] = { ++ RATING_APPALLING, ++ RATING_VERYPOOR, ++ RATING_POOR, ++ RATING_MEDIOCRE, ++ RATING_GOOD, ++ RATING_VERYGOOD, ++ RATING_EXCELLENT, ++ RATING_OUTSTANDING ++ }; ++ int offset = 0; ++ ++ for (;offset < 7; offset++) { ++ ++ if (rating <= ratings[offset]) { ++ break; ++ } ++ } ++ // if offset is 7 its Outstanding as nothing is above it ++ return offset; ++} ++ ++/** + * Action types that a company must ask permission for to a town authority. + * @see CheckforTownRating + */ +diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp +index 7479892..bbe6e13 100644 +--- a/src/town_cmd.cpp ++++ b/src/town_cmd.cpp +@@ -26,6 +26,7 @@ + #include "newgrf_debug.h" + #include "newgrf_house.h" + #include "newgrf_text.h" ++#include "texteff.hpp" + #include "autoslope.h" + #include "tunnelbridge_map.h" + #include "strings_func.h" +@@ -163,6 +164,26 @@ void Town::InitializeLayout(TownLayout layout) + } + + /** ++ * Updates the town label of the town after changes in rating. The colour scheme is: ++ * Red: Appalling and Very poor ratings. ++ * Orange: Poor and mediocre ratings. ++ * Yellow: Good rating. ++ * White: Very good rating (standard). ++ * Green: Excellent and outstanding ratings. ++ */ ++void Town::UpdateLabel() ++{ ++ if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { ++ int r = this->ratings[_local_company]; ++ (this->town_label = 0, r <= RATING_VERYPOOR) || // Appalling and Very Poor ++ (this->town_label++, r <= RATING_MEDIOCRE) || // Poor and Mediocre ++ (this->town_label++, r <= RATING_GOOD) || // Good ++ (this->town_label++, r <= RATING_VERYGOOD) || // Very Good ++ (this->town_label++, true); // Excellent and Outstanding ++ } ++} ++ ++/** + * Get the cost for removing this house + * @return the cost (inflation corrected etc) + */ +@@ -232,6 +253,8 @@ static void DrawTile_Town(TileInfo *ti) + + DrawGroundSprite(dcts->ground.sprite, dcts->ground.pal); + ++ DrawOverlay(ti, MP_HOUSE); ++ + /* If houses are invisible, do not draw the upper part */ + if (IsInvisibilitySet(TO_HOUSES)) return; + +@@ -373,11 +396,11 @@ static bool IsCloseToTown(TileIndex tile, uint dist) + */ + void Town::UpdateVirtCoord() + { ++ this->UpdateLabel(); + Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); + SetDParam(0, this->index); + SetDParam(1, this->cache.population); +- this->cache.sign.UpdatePosition(pt.x, pt.y - 24 * ZOOM_LVL_BASE, +- _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, ++ this->cache.sign.UpdatePosition(pt.x, pt.y - 24 * ZOOM_LVL_BASE, this->Label(), + STR_VIEWPORT_TOWN); + + SetWindowDirty(WC_TOWN_VIEW, this->index); +@@ -2964,6 +2987,7 @@ static CommandCost TownActionBribe(Town *t, DoCommandFlag flags) + */ + if (t->ratings[_current_company] > RATING_BRIBE_DOWN_TO) { + t->ratings[_current_company] = RATING_BRIBE_DOWN_TO; ++ t->UpdateVirtCoord(); + SetWindowDirty(WC_TOWN_AUTHORITY, t->index); + } + } else { +@@ -3096,6 +3120,7 @@ static void UpdateTownRating(Town *t) + t->ratings[i] = Clamp(t->ratings[i], RATING_MINIMUM, RATING_MAXIMUM); + } + ++ t->UpdateVirtCoord(); + SetWindowDirty(WC_TOWN_AUTHORITY, t->index); + } + +@@ -3331,6 +3356,8 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags) + } + + int rating = GetRating(t); ++ int old_rating = rating; ++ + if (add < 0) { + if (rating > max) { + rating += add; +@@ -3347,7 +3374,28 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags) + } else { + SetBit(t->have_ratings, _current_company); + t->ratings[_current_company] = rating; ++ t->UpdateVirtCoord(); + SetWindowDirty(WC_TOWN_AUTHORITY, t->index); ++ ++ if (_settings_client.gui.townrating_indicator && _current_company == _local_company) { ++ if (rating != old_rating) { // got a change ++ StringID msg = (rating > old_rating) ? STR_TOWN_RATING_INCREASED : STR_TOWN_RATING_DECREASED; ++ StringID display; ++ StringID old_display; ++ // rating -> string_rating ++ display = STR_CARGO_RATING_APPALLING + OffsetByTownRating(rating); ++ old_display = STR_CARGO_RATING_APPALLING + OffsetByTownRating(old_rating); ++ ++ if (display != old_display) { // change in rating, notice it (abundant check?) ++ // place the indicator 1 tile above the town tile results in ++ // showing the indicator above the town name ++ Point pt = RemapCoords2((TileX(t->xy) - 1) * TILE_SIZE - (display - STR_CARGO_RATING_APPALLING) * 7, ++ (TileY(t->xy) - 1) * TILE_SIZE - (display - STR_CARGO_RATING_APPALLING) * 7); ++ SetDParam(0, display); ++ AddTextEffect(msg, pt.x, pt.y, DAY_TICKS, TE_RISING); ++ } ++} ++ } + } + } + +@@ -3462,6 +3510,7 @@ extern const TileTypeProcs _tile_type_town_procs = { + NULL, // vehicle_enter_tile_proc + GetFoundation_Town, // get_foundation_proc + TerraformTile_Town, // terraform_tile_proc ++ NULL, // copypaste_tile_proc + }; + + +diff --git a/src/town_gui.cpp b/src/town_gui.cpp +index 222549f..febae9c 100644 +--- a/src/town_gui.cpp ++++ b/src/town_gui.cpp +@@ -154,15 +154,7 @@ public: + SetDParam(1, c->index); + + int r = this->town->ratings[c->index]; +- StringID str; +- (str = STR_CARGO_RATING_APPALLING, r <= RATING_APPALLING) || // Apalling +- (str++, r <= RATING_VERYPOOR) || // Very Poor +- (str++, r <= RATING_POOR) || // Poor +- (str++, r <= RATING_MEDIOCRE) || // Mediocore +- (str++, r <= RATING_GOOD) || // Good +- (str++, r <= RATING_VERYGOOD) || // Very Good +- (str++, r <= RATING_EXCELLENT) || // Excellent +- (str++, true); // Outstanding ++ StringID str = STR_CARGO_RATING_APPALLING + OffsetByTownRating(r); + + SetDParam(2, str); + if (this->town->exclusivity == c->index) { +diff --git a/src/town_map.h b/src/town_map.h +index 016ff9a..4c648ad 100644 +--- a/src/town_map.h ++++ b/src/town_map.h +@@ -24,7 +24,7 @@ + static inline TownID GetTownIndex(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); +- return _m[t].m2; ++ return GetTile(t)->m2; + } + + /** +@@ -36,7 +36,7 @@ static inline TownID GetTownIndex(TileIndex t) + static inline void SetTownIndex(TileIndex t, TownID index) + { + assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); +- _m[t].m2 = index; ++ GetTile(t)->m2 = index; + } + + /** +@@ -49,7 +49,7 @@ static inline void SetTownIndex(TileIndex t, TownID index) + static inline HouseID GetCleanHouseType(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); ++ return GetTile(t)->m4 | (GB(GetTile(t)->m3, 6, 1) << 8); + } + + /** +@@ -72,8 +72,8 @@ static inline HouseID GetHouseType(TileIndex t) + static inline void SetHouseType(TileIndex t, HouseID house_id) + { + assert(IsTileType(t, MP_HOUSE)); +- _m[t].m4 = GB(house_id, 0, 8); +- SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); ++ GetTile(t)->m4 = GB(house_id, 0, 8); ++ SB(GetTile(t)->m3, 6, 1, GB(house_id, 8, 1)); + } + + /** +@@ -83,7 +83,7 @@ static inline void SetHouseType(TileIndex t, HouseID house_id) + */ + static inline bool LiftHasDestination(TileIndex t) + { +- return HasBit(_me[t].m7, 0); ++ return HasBit(GetTileEx(t)->m7, 0); + } + + /** +@@ -94,8 +94,8 @@ static inline bool LiftHasDestination(TileIndex t) + */ + static inline void SetLiftDestination(TileIndex t, byte dest) + { +- SetBit(_me[t].m7, 0); +- SB(_me[t].m7, 1, 3, dest); ++ SetBit(GetTileEx(t)->m7, 0); ++ SB(GetTileEx(t)->m7, 1, 3, dest); + } + + /** +@@ -105,7 +105,7 @@ static inline void SetLiftDestination(TileIndex t, byte dest) + */ + static inline byte GetLiftDestination(TileIndex t) + { +- return GB(_me[t].m7, 1, 3); ++ return GB(GetTileEx(t)->m7, 1, 3); + } + + /** +@@ -116,7 +116,7 @@ static inline byte GetLiftDestination(TileIndex t) + */ + static inline void HaltLift(TileIndex t) + { +- SB(_me[t].m7, 0, 4, 0); ++ SB(GetTileEx(t)->m7, 0, 4, 0); + } + + /** +@@ -126,7 +126,7 @@ static inline void HaltLift(TileIndex t) + */ + static inline byte GetLiftPosition(TileIndex t) + { +- return GB(_me[t].m6, 2, 6); ++ return GB(GetTileEx(t)->m6, 2, 6); + } + + /** +@@ -136,7 +136,7 @@ static inline byte GetLiftPosition(TileIndex t) + */ + static inline void SetLiftPosition(TileIndex t, byte pos) + { +- SB(_me[t].m6, 2, 6, pos); ++ SB(GetTileEx(t)->m6, 2, 6, pos); + } + + /** +@@ -147,7 +147,7 @@ static inline void SetLiftPosition(TileIndex t, byte pos) + static inline bool IsHouseCompleted(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return HasBit(_m[t].m3, 7); ++ return HasBit(GetTile(t)->m3, 7); + } + + /** +@@ -158,7 +158,7 @@ static inline bool IsHouseCompleted(TileIndex t) + static inline void SetHouseCompleted(TileIndex t, bool status) + { + assert(IsTileType(t, MP_HOUSE)); +- SB(_m[t].m3, 7, 1, !!status); ++ SB(GetTile(t)->m3, 7, 1, !!status); + } + + /** +@@ -185,7 +185,7 @@ static inline void SetHouseCompleted(TileIndex t, bool status) + static inline byte GetHouseBuildingStage(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(_m[t].m5, 3, 2); ++ return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(GetTile(t)->m5, 3, 2); + } + + /** +@@ -197,7 +197,7 @@ static inline byte GetHouseBuildingStage(TileIndex t) + static inline byte GetHouseConstructionTick(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return IsHouseCompleted(t) ? 0 : GB(_m[t].m5, 0, 3); ++ return IsHouseCompleted(t) ? 0 : GB(GetTile(t)->m5, 0, 3); + } + + /** +@@ -210,9 +210,9 @@ static inline byte GetHouseConstructionTick(TileIndex t) + static inline void IncHouseConstructionTick(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- AB(_m[t].m5, 0, 5, 1); ++ AB(GetTile(t)->m5, 0, 5, 1); + +- if (GB(_m[t].m5, 3, 2) == TOWN_HOUSE_COMPLETED) { ++ if (GB(GetTile(t)->m5, 3, 2) == TOWN_HOUSE_COMPLETED) { + /* House is now completed. + * Store the year of construction as well, for newgrf house purpose */ + SetHouseCompleted(t, true); +@@ -228,7 +228,7 @@ static inline void IncHouseConstructionTick(TileIndex t) + static inline void ResetHouseAge(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)); +- _m[t].m5 = 0; ++ GetTile(t)->m5 = 0; + } + + /** +@@ -239,7 +239,7 @@ static inline void ResetHouseAge(TileIndex t) + static inline void IncrementHouseAge(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- if (IsHouseCompleted(t) && _m[t].m5 < 0xFF) _m[t].m5++; ++ if (IsHouseCompleted(t) && GetTile(t)->m5 < 0xFF) GetTile(t)->m5++; + } + + /** +@@ -251,7 +251,7 @@ static inline void IncrementHouseAge(TileIndex t) + static inline Year GetHouseAge(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return IsHouseCompleted(t) ? _m[t].m5 : 0; ++ return IsHouseCompleted(t) ? GetTile(t)->m5 : 0; + } + + /** +@@ -264,7 +264,7 @@ static inline Year GetHouseAge(TileIndex t) + static inline void SetHouseRandomBits(TileIndex t, byte random) + { + assert(IsTileType(t, MP_HOUSE)); +- _m[t].m1 = random; ++ GetTile(t)->m1 = random; + } + + /** +@@ -277,7 +277,7 @@ static inline void SetHouseRandomBits(TileIndex t, byte random) + static inline byte GetHouseRandomBits(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return _m[t].m1; ++ return GetTile(t)->m1; + } + + /** +@@ -290,7 +290,7 @@ static inline byte GetHouseRandomBits(TileIndex t) + static inline void SetHouseTriggers(TileIndex t, byte triggers) + { + assert(IsTileType(t, MP_HOUSE)); +- SB(_m[t].m3, 0, 5, triggers); ++ SB(GetTile(t)->m3, 0, 5, triggers); + } + + /** +@@ -303,7 +303,7 @@ static inline void SetHouseTriggers(TileIndex t, byte triggers) + static inline byte GetHouseTriggers(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return GB(_m[t].m3, 0, 5); ++ return GB(GetTile(t)->m3, 0, 5); + } + + /** +@@ -315,7 +315,7 @@ static inline byte GetHouseTriggers(TileIndex t) + static inline byte GetHouseProcessingTime(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- return GB(_me[t].m6, 2, 6); ++ return GB(GetTileEx(t)->m6, 2, 6); + } + + /** +@@ -327,7 +327,7 @@ static inline byte GetHouseProcessingTime(TileIndex t) + static inline void SetHouseProcessingTime(TileIndex t, byte time) + { + assert(IsTileType(t, MP_HOUSE)); +- SB(_me[t].m6, 2, 6, time); ++ SB(GetTileEx(t)->m6, 2, 6, time); + } + + /** +@@ -338,7 +338,7 @@ static inline void SetHouseProcessingTime(TileIndex t, byte time) + static inline void DecHouseProcessingTime(TileIndex t) + { + assert(IsTileType(t, MP_HOUSE)); +- _me[t].m6 -= 1 << 2; ++ GetTileEx(t)->m6 -= 1 << 2; + } + + /** +@@ -356,12 +356,12 @@ static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte sta + assert(IsTileType(t, MP_CLEAR)); + + SetTileType(t, MP_HOUSE); +- _m[t].m1 = random_bits; +- _m[t].m2 = tid; +- _m[t].m3 = 0; ++ GetTile(t)->m1 = random_bits; ++ GetTile(t)->m2 = tid; ++ GetTile(t)->m3 = 0; + SetHouseType(t, type); + SetHouseCompleted(t, stage == TOWN_HOUSE_COMPLETED); +- _m[t].m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); ++ GetTile(t)->m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); + SetAnimationFrame(t, 0); + SetHouseProcessingTime(t, HouseSpec::Get(type)->processing_time); + } +diff --git a/src/track_func.h b/src/track_func.h +index 6896792..b7f2d21 100644 +--- a/src/track_func.h ++++ b/src/track_func.h +@@ -16,6 +16,7 @@ + #include "track_type.h" + #include "direction_func.h" + #include "slope_func.h" ++#include "direction_func.h" + + /** + * Iterate through each set Track in a TrackBits value. +@@ -692,4 +693,43 @@ static inline bool IsUphillTrackdir(Slope slope, Trackdir dir) + return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir); + } + ++/** ++ * Transform a Track. ++ * @param track The Track to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Track. ++ */ ++static inline Track TransformTrack(Track track, DirTransformation transformation) ++{ ++ extern const byte _track_transformation_map[DTR_END][TRACK_END]; ++ return (Track)_track_transformation_map[transformation][track]; ++} ++ ++/** ++ * Transform a TrackBits. ++ * @param track_bits The TrackBits to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed TrackBits. ++ */ ++static inline TrackBits TransformTrackBits(TrackBits track_bits, DirTransformation transformation) ++{ ++ TrackBits ret = track_bits & ~TRACK_BIT_ALL; ++ ++ Track t; ++ FOR_EACH_SET_TRACK(t, track_bits & TRACK_BIT_ALL) SetBit(ret, TransformTrack(t, transformation)); ++ ++ return ret; ++} ++ ++/** ++ * Transform a Trackdir. ++ * @param trackdir The Trackdir to transform. ++ * @param transformation Transformation to perform. ++ * @return The transformed Trackdir. ++ */ ++static inline Trackdir TransformTrackdir(Trackdir trackdir, DirTransformation transformation) ++{ ++ return TrackExitdirToTrackdir(TransformTrack(TrackdirToTrack(trackdir), transformation), TransformDiagDir(TrackdirToExitdir(trackdir), transformation)); ++} ++ + #endif /* TRACK_FUNC_H */ +diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp +index 6f2feea..451623d 100644 +--- a/src/train_cmd.cpp ++++ b/src/train_cmd.cpp +@@ -1861,6 +1861,17 @@ void ReverseTrainDirection(Train *v) + return; + } + ++ /* We are inside tunnel/bidge with signals, reversing will close the entrance. */ ++ if (HasWormholeSignals(v->tile)) { ++ /* Flip signal on tunnel entrance tile red. */ ++ SetBitTunnelBridgeExit(v->tile); ++ MarkTileDirtyByTile(v->tile); ++ /* Clear counters. */ ++ v->wait_counter = 0; ++ v->load_unload_ticks = 0; ++ return; ++ } ++ + /* TrainExitDir does not always produce the desired dir for depots and + * tunnels/bridges that is needed for UpdateSignalsOnSegment. */ + DiagDirection dir = TrainExitDir(v->direction, v->track); +@@ -2197,6 +2208,42 @@ static bool CheckTrainStayInDepot(Train *v) + return false; + } + ++static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDirection dir, bool free) ++{ ++ if (IsBridge(end) && GetTile(end)->m2 > 0){ ++ /* Clearing last bridge signal. */ ++ uint16 m = GetTile(end)->m2; ++ byte i = 15; ++ while((m & 0x8000) == 0 && --i > 0) m <<= 1; ++ ClrBit(GetTile(end)->m2, i); ++ ++ uint x = TileX(end)* TILE_SIZE; ++ uint y = TileY(end)* TILE_SIZE; ++ uint distance = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) * ++i; ++ switch (dir) { ++ default: NOT_REACHED(); ++ case DIAGDIR_NE: MarkTileDirtyByTile(TileVirtXY(x - distance, y)); break; ++ case DIAGDIR_SE: MarkTileDirtyByTile(TileVirtXY(x, y + distance)); break; ++ case DIAGDIR_SW: MarkTileDirtyByTile(TileVirtXY(x + distance, y)); break; ++ case DIAGDIR_NW: MarkTileDirtyByTile(TileVirtXY(x, y - distance)); break; ++ } ++ MarkTileDirtyByTile(tile); ++ } ++ if (free) { ++ /* Open up the wormhole and clear m2. */ ++ GetTile(tile)->m2 = 0; ++ GetTile(end)->m2 = 0; ++ ++ if (IsTunnelBridgeWithSignRed(end)) { ++ ClrBitTunnelBridgeExit(end); ++ if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(end); ++ } else if (IsTunnelBridgeWithSignRed(tile)) { ++ ClrBitTunnelBridgeExit(tile); ++ if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(tile); ++ } ++ } ++} ++ + /** + * Clear the reservation of \a tile that was just left by a wagon on \a track_dir. + * @param v %Train owning the reservation. +@@ -2212,7 +2259,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ + if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { + TileIndex end = GetOtherTunnelBridgeEnd(tile); + +- if (TunnelBridgeIsFree(tile, end, v).Succeeded()) { ++ bool free = TunnelBridgeIsFree(tile, end, v).Succeeded(); ++ if (free) { + /* Free the reservation only if no other train is on the tiles. */ + SetTunnelBridgeReservation(tile, false); + SetTunnelBridgeReservation(end, false); +@@ -2226,6 +2274,7 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ + } + } + } ++ if (HasWormholeSignals(tile)) HandleLastTunnelBridgeSignals(tile, end, dir, free); + } + } else if (IsRailStationTile(tile)) { + TileIndex new_tile = TileAddByDiagDir(tile, dir); +@@ -3093,6 +3142,99 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) + return t; + } + ++/** Find train in front and keep distance between trains in tunnel/bridge. */ ++static Vehicle *FindSpaceBetweenTrainsEnum(Vehicle *v, void *data) ++{ ++ /* Don't look at wagons between front and back of train. */ ++ if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; ++ ++ const Vehicle *u = (Vehicle*)data; ++ int32 a, b = 0; ++ ++ switch (u->direction) { ++ default: NOT_REACHED(); ++ case DIR_NE: a = u->x_pos; b = v->x_pos; break; ++ case DIR_SE: a = v->y_pos; b = u->y_pos; break; ++ case DIR_SW: a = v->x_pos; b = u->x_pos; break; ++ case DIR_NW: a = u->y_pos; b = v->y_pos; break; ++ } ++ ++ if (a > b && a <= (b + (int)(Train::From(u)->wait_counter)) + (int)(TILE_SIZE)) return v; ++ return NULL; ++} ++ ++static bool IsToCloseBehindTrain(Vehicle *v, TileIndex tile, bool check_endtile) ++{ ++ Train *t = (Train *)v; ++ ++ if (t->force_proceed != 0) return false; ++ ++ if (HasVehicleOnPos(t->tile, v, &FindSpaceBetweenTrainsEnum)) { ++ /* Revert train if not going with tunnel direction. */ ++ if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { ++ v->cur_speed = 0; ++ ToggleBit(t->flags, VRF_REVERSING); ++ } ++ return true; ++ } ++ /* Cover blind spot at end of tunnel bridge. */ ++ if (check_endtile){ ++ if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), v, &FindSpaceBetweenTrainsEnum)) { ++ /* Revert train if not going with tunnel direction. */ ++ if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { ++ v->cur_speed = 0; ++ ToggleBit(t->flags, VRF_REVERSING); ++ } ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/** Simulate signals in tunnel - bridge. */ ++static bool CheckTrainStayInWormHole(Train *t, TileIndex tile) ++{ ++ if (t->force_proceed != 0) return false; ++ ++ /* When not exit reverse train. */ ++ if (!IsTunnelBridgeExit(tile)) { ++ t->cur_speed = 0; ++ ToggleBit(t->flags, VRF_REVERSING); ++ return true; ++ } ++ SigSegState seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner); ++ if (seg_state == SIGSEG_FULL || (seg_state == SIGSEG_PBS && !TryPathReserve(t))) { ++ t->cur_speed = 0; ++ return true; ++ } ++ ++ return false; ++} ++ ++static void HandleSignalBehindTrain(Train *v, uint signal_number) ++{ ++ TileIndex tile; ++ switch (v->direction) { ++ default: NOT_REACHED(); ++ case DIR_NE: tile = TileVirtXY(v->x_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break; ++ case DIR_SE: tile = TileVirtXY(v->x_pos, v->y_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) ); break; ++ case DIR_SW: tile = TileVirtXY(v->x_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break; ++ case DIR_NW: tile = TileVirtXY(v->x_pos, v->y_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals)); break; ++ } ++ ++ if(tile == v->tile) { ++ /* Flip signal on ramp. */ ++ if (IsTunnelBridgeWithSignRed(tile)) { ++ ClrBitTunnelBridgeExit(tile); ++ MarkTileDirtyByTile(tile); ++ } ++ } else if (IsBridge(v->tile) && signal_number <= 16) { ++ ClrBit(GetTile(v->tile)->m2, signal_number); ++ MarkTileDirtyByTile(tile); ++ } ++} ++ + /** + * Move a vehicle chain one movement stop forwards. + * @param v First vehicle to move. +@@ -3278,6 +3420,23 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) + goto invalid_rail; + } + ++ if (HasWormholeSignals(gp.new_tile)) { ++ /* If red signal stop. */ ++ if (v->IsFrontEngine() && v->force_proceed == 0) { ++ if (IsTunnelBridgeWithSignRed(gp.new_tile)) { ++ v->cur_speed = 0; ++ return false; ++ } ++ if (IsTunnelBridgeExit(gp.new_tile)) { ++ v->cur_speed = 0; ++ goto invalid_rail; ++ } ++ /* Flip signal on tunnel entrance tile red. */ ++ SetBitTunnelBridgeExit(gp.new_tile); ++ MarkTileDirtyByTile(gp.new_tile); ++ } ++ } ++ + if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { + Track track = FindFirstTrack(chosen_track); + Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir); +@@ -3330,6 +3489,64 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) + } + } + } else { ++ /* Handle signal simulation on tunnel/bridge. */ ++ TileIndex old_tile = TileVirtXY(v->x_pos, v->y_pos); ++ if (old_tile != gp.new_tile && HasWormholeSignals(v->tile) && (v->IsFrontEngine() || v->Next() == NULL)){ ++ if (old_tile == v->tile) { ++ if (v->IsFrontEngine() && v->force_proceed == 0 && IsTunnelBridgeExit(v->tile)) goto invalid_rail; ++ /* Entered wormhole set counters. */ ++ v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE; ++ v->load_unload_ticks = 0; ++ } ++ ++ uint distance = v->wait_counter; ++ bool leaving = false; ++ if (distance == 0) v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals); ++ ++ if (v->IsFrontEngine()) { ++ /* Check if track in front is free and see if we can leave wormhole. */ ++ int z = GetSlopePixelZ(gp.x, gp.y) - v->z_pos; ++ if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && !(abs(z) > 2)) { ++ if (CheckTrainStayInWormHole(v, gp.new_tile)) return false; ++ leaving = true; ++ } else { ++ if (IsToCloseBehindTrain(v, gp.new_tile, distance == 0)) { ++ if (distance == 0) v->wait_counter = 0; ++ v->cur_speed = 0; ++ return false; ++ } ++ /* flip signal in front to red on bridges*/ ++ if (distance == 0 && v->load_unload_ticks <= 15 && IsBridge(v->tile)){ ++ SetBit(GetTile(v->tile)->m2, v->load_unload_ticks); ++ MarkTileDirtyByTile(gp.new_tile); ++ } ++ } ++ } ++ if (v->Next() == NULL) { ++ if (v->load_unload_ticks > 0 && v->load_unload_ticks <= 16 && distance == (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE) HandleSignalBehindTrain(v, v->load_unload_ticks - 2); ++ if (old_tile == v->tile) { ++ /* We left ramp into wormhole. */ ++ v->x_pos = gp.x; ++ v->y_pos = gp.y; ++ UpdateSignalsOnSegment(old_tile, INVALID_DIAGDIR, v->owner); ++ } ++ } ++ if (distance == 0) v->load_unload_ticks++; ++ v->wait_counter -= TILE_SIZE; ++ ++ if (leaving) { // Reset counters. ++ v->force_proceed = 0; ++ v->wait_counter = 0; ++ v->load_unload_ticks = 0; ++ v->x_pos = gp.x; ++ v->y_pos = gp.y; ++ v->UpdatePosition(); ++ v->UpdateViewport(false,false); ++ UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner); ++ continue; ++ } ++ } ++ + if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { + /* Perform look-ahead on tunnel exit. */ + if (v->IsFrontEngine()) { +@@ -3345,7 +3562,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) + v->x_pos = gp.x; + v->y_pos = gp.y; + v->UpdatePosition(); +- if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); ++ if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); + continue; + } + } +@@ -3517,7 +3734,7 @@ static void ChangeTrainDirRandomly(Train *v) + + do { + /* We don't need to twist around vehicles if they're not visible */ +- if (!(v->vehstatus & VS_HIDDEN)) { ++ if (v->IsDrawn()) { + v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]); + v->UpdateDeltaXY(v->direction); + v->cur_image = v->GetImage(v->direction, EIT_ON_MAP); +@@ -3541,7 +3758,7 @@ static bool HandleCrashedTrain(Train *v) + { + int state = ++v->crash_anim_pos; + +- if (state == 4 && !(v->vehstatus & VS_HIDDEN)) { ++ if (state == 4 && v->IsDrawn()) { + CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); + } + +@@ -3872,7 +4089,7 @@ static bool TrainLocoHandler(Train *v, bool mode) + } + + for (Train *u = v; u != NULL; u = u->Next()) { +- if ((u->vehstatus & VS_HIDDEN) != 0) continue; ++ if (!u->IsDrawn()) continue; + + u->UpdateViewport(false, false); + } +diff --git a/src/transparency.h b/src/transparency.h +index ab6f9a6..773a633 100644 +--- a/src/transparency.h ++++ b/src/transparency.h +@@ -31,6 +31,7 @@ enum TransparencyOption { + TO_STRUCTURES, ///< other objects such as transmitters and lighthouses + TO_CATENARY, ///< catenary + TO_LOADING, ///< loading indicators ++ TO_TUNNELS, ///< vehicles in tunnels + TO_END, + TO_INVALID, ///< Invalid transparency option + }; +diff --git a/src/transparency_gui.cpp b/src/transparency_gui.cpp +index 4bad2b0..8156229 100644 +--- a/src/transparency_gui.cpp ++++ b/src/transparency_gui.cpp +@@ -52,14 +52,15 @@ public: + case WID_TT_BRIDGES: + case WID_TT_STRUCTURES: + case WID_TT_CATENARY: +- case WID_TT_LOADING: { ++ case WID_TT_LOADING: ++ case WID_TT_TUNNELS: { + uint i = widget - WID_TT_BEGIN; + if (HasBit(_transparency_lock, i)) DrawSprite(SPR_LOCK, PAL_NONE, r.left + 1, r.top + 1); + break; + } + case WID_TT_BUTTONS: + for (uint i = WID_TT_BEGIN; i < WID_TT_END; i++) { +- if (i == WID_TT_LOADING) continue; // Do not draw button for invisible loading indicators. ++ if (i >= WID_TT_LOADING) continue; // Do not draw button for invisible loading indicators. + + const NWidgetBase *wi = this->GetWidget<NWidgetBase>(i); + DrawFrameRect(wi->pos_x + 1, r.top + 2, wi->pos_x + wi->current_x - 2, r.bottom - 2, COLOUR_PALE_GREEN, +@@ -141,6 +142,7 @@ static const NWidgetPart _nested_transparency_widgets[] = { + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_STRUCTURES), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRANSMITTER, STR_TRANSPARENT_STRUCTURES_TOOLTIP), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_CATENARY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_BUILD_X_ELRAIL, STR_TRANSPARENT_CATENARY_TOOLTIP), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_LOADING), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRAINLIST, STR_TRANSPARENT_LOADING_TOOLTIP), ++ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_TUNNELS), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_TRANSPARENT_TUNNELS_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 1), EndContainer(), + EndContainer(), + /* Panel with 'invisibility' buttons. */ +diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp +index c862f0d..759a001 100644 +--- a/src/tree_cmd.cpp ++++ b/src/tree_cmd.cpp +@@ -470,6 +470,8 @@ static void DrawTile_Trees(TileInfo *ti) + default: DrawGroundSprite(_clear_land_sprites_snow_desert[GetTreeDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break; + } + ++ DrawOverlay(ti, MP_TREES); ++ + /* Do not draw trees when the invisible trees setting is set */ + if (IsInvisibilitySet(TO_TREES)) return; + +@@ -813,4 +815,5 @@ extern const TileTypeProcs _tile_type_trees_procs = { + NULL, // vehicle_enter_tile_proc + GetFoundation_Trees, // get_foundation_proc + TerraformTile_Trees, // terraform_tile_proc ++ NULL // copypaste_tile_proc + }; +diff --git a/src/tree_map.h b/src/tree_map.h +index e614099..8eaa1a7 100644 +--- a/src/tree_map.h ++++ b/src/tree_map.h +@@ -74,7 +74,7 @@ enum TreeGround { + static inline TreeType GetTreeType(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return (TreeType)_m[t].m3; ++ return (TreeType)GetTile(t)->m3; + } + + /** +@@ -89,7 +89,7 @@ static inline TreeType GetTreeType(TileIndex t) + static inline TreeGround GetTreeGround(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return (TreeGround)GB(_m[t].m2, 6, 3); ++ return (TreeGround)GB(GetTile(t)->m2, 6, 3); + } + + /** +@@ -114,7 +114,7 @@ static inline TreeGround GetTreeGround(TileIndex t) + static inline uint GetTreeDensity(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return GB(_m[t].m2, 4, 2); ++ return GB(GetTile(t)->m2, 4, 2); + } + + /** +@@ -131,8 +131,8 @@ static inline uint GetTreeDensity(TileIndex t) + static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- SB(_m[t].m2, 4, 2, d); +- SB(_m[t].m2, 6, 3, g); ++ SB(GetTile(t)->m2, 4, 2, d); ++ SB(GetTile(t)->m2, 6, 3, g); + } + + /** +@@ -149,7 +149,7 @@ static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) + static inline uint GetTreeCount(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return GB(_m[t].m5, 6, 2) + 1; ++ return GB(GetTile(t)->m5, 6, 2) + 1; + } + + /** +@@ -166,7 +166,7 @@ static inline uint GetTreeCount(TileIndex t) + static inline void AddTreeCount(TileIndex t, int c) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- _m[t].m5 += c << 6; ++ GetTile(t)->m5 += c << 6; + } + + /** +@@ -181,7 +181,7 @@ static inline void AddTreeCount(TileIndex t, int c) + static inline uint GetTreeGrowth(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return GB(_m[t].m5, 0, 3); ++ return GB(GetTile(t)->m5, 0, 3); + } + + /** +@@ -196,7 +196,7 @@ static inline uint GetTreeGrowth(TileIndex t) + static inline void AddTreeGrowth(TileIndex t, int a) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- _m[t].m5 += a; ++ GetTile(t)->m5 += a; + } + + /** +@@ -212,7 +212,7 @@ static inline void AddTreeGrowth(TileIndex t, int a) + static inline void SetTreeGrowth(TileIndex t, uint g) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- SB(_m[t].m5, 0, 3, g); ++ SB(GetTile(t)->m5, 0, 3, g); + } + + /** +@@ -226,7 +226,7 @@ static inline void SetTreeGrowth(TileIndex t, uint g) + static inline uint GetTreeCounter(TileIndex t) + { + assert(IsTileType(t, MP_TREES)); +- return GB(_m[t].m2, 0, 4); ++ return GB(GetTile(t)->m2, 0, 4); + } + + /** +@@ -241,7 +241,7 @@ static inline uint GetTreeCounter(TileIndex t) + static inline void AddTreeCounter(TileIndex t, int a) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- _m[t].m2 += a; ++ GetTile(t)->m2 += a; + } + + /** +@@ -256,7 +256,7 @@ static inline void AddTreeCounter(TileIndex t, int a) + static inline void SetTreeCounter(TileIndex t, uint c) + { + assert(IsTileType(t, MP_TREES)); // XXX incomplete +- SB(_m[t].m2, 0, 4, c); ++ SB(GetTile(t)->m2, 0, 4, c); + } + + /** +@@ -275,12 +275,12 @@ static inline void MakeTree(TileIndex t, TreeType type, uint count, uint growth, + { + SetTileType(t, MP_TREES); + SetTileOwner(t, OWNER_NONE); +- _m[t].m2 = ground << 6 | density << 4 | 0; +- _m[t].m3 = type; +- _m[t].m4 = 0 << 5 | 0 << 2; +- _m[t].m5 = count << 6 | growth; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = ground << 6 | density << 4 | 0; ++ GetTile(t)->m3 = type; ++ GetTile(t)->m4 = 0 << 5 | 0 << 2; ++ GetTile(t)->m5 = count << 6 | growth; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } + + #endif /* TREE_MAP_H */ +diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp +index 4e6d5a7..c992b09 100644 +--- a/src/tunnel_map.cpp ++++ b/src/tunnel_map.cpp +@@ -21,27 +21,90 @@ + * @param tile the tile to search from. + * @return the tile of the other end of the tunnel. + */ +-TileIndex GetOtherTunnelEnd(TileIndex tile) ++template <bool Tgeneric> ++typename TileIndexT<Tgeneric>::T GetOtherTunnelEnd(typename TileIndexT<Tgeneric>::T tile) + { ++ assert(IsTunnelTile(tile)); ++ + DiagDirection dir = GetTunnelBridgeDirection(tile); +- TileIndexDiff delta = TileOffsByDiagDir(dir); +- int z = GetTileZ(tile); ++ TileIndexDiff delta = TileOffsByDiagDir<Tgeneric>(dir, MapOf(tile)); ++ uint h = TileHeight(tile); + +- dir = ReverseDiagDir(dir); ++ if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { ++ h--; ++continue_ne_nw: + do { + tile += delta; +- } while ( +- !IsTunnelTile(tile) || +- GetTunnelBridgeDirection(tile) != dir || +- GetTileZ(tile) != z +- ); ++ } while (TileHeight(tile) != h); ++ } else { ++continue_se_sw: ++ tile += delta; ++ do { ++ tile += delta; ++ } while (TileHeight(tile) != h); ++ tile -= delta; ++ } ++ ++ if (IsTunnelTile(tile) && GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { ++ } else { ++ /* Handle Chunnels. ++ * Only look for tunnel when hight level changes. ++ * And only at sea level. ++ */ ++ assert(h <= 1); ++ (h == 0) ? h = 1 : h = 0; ++ if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { ++ goto continue_ne_nw; ++ } else { ++ goto continue_se_sw; ++ } ++ } + + return tile; + } ++/* instantiate */ ++template TileIndex GetOtherTunnelEnd<false>(TileIndex tile); ++template GenericTileIndex GetOtherTunnelEnd<true>(GenericTileIndex tile); + ++/** ++ * Is there a Chunnel in the way in the given direction? ++ * Only between height level 0 and 1. ++ * Used to avoid building bridge or tunnel between existing chunnel. ++ * @param tile the tile to search from. ++ * @param dir the direction to start searching to. ++ * @return true if and only if there is a chunnel. ++ */ ++bool IsBetweenChunnelPortals(TileIndex tile, DiagDirection dir) ++{ ++ uint h = 0; ++ TileIndexDiff delta = TileOffsByDiagDir(dir); ++ if (GetTileZ(tile) > 0) return false; ++ ++ do { ++ if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { ++ do { ++ tile += delta; ++ if (!IsValidTile(tile)) return false; ++ } while (TileHeight(tile) != h); ++ } else { ++ tile += delta; ++ do { ++ tile += delta; ++ if (!IsValidTile(tile)) return false; ++ } while (TileHeight(tile) != h); ++ tile -= delta; ++ } ++ (h == 0) ? h = 1 : h = 0; ++ } while (!IsTunnelTile(tile)); ++ ++ if (GetTunnelBridgeDirection(tile) != ReverseDiagDir(dir)) return false; ++ ++ return true; ++} + + /** + * Is there a tunnel in the way in the given direction? ++ * Between level 0 and 1 terraforming is allowed. (No search) + * @param tile the tile to search from. + * @param z the 'z' to search on. + * @param dir the direction to start searching to. +@@ -49,6 +112,9 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) + */ + bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir) + { ++ /* Between level 0 and 1 terraforming is allowed. */ ++ if (GetTileZ(tile) <= 1) return false; ++ + TileIndexDiff delta = TileOffsByDiagDir(dir); + int height; + +diff --git a/src/tunnel_map.h b/src/tunnel_map.h +index e200a12..73a55e2 100644 +--- a/src/tunnel_map.h ++++ b/src/tunnel_map.h +@@ -21,25 +21,42 @@ + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return true if and only if this tile is a tunnel (entrance) + */ +-static inline bool IsTunnel(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsTunnel(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- return !HasBit(_m[t].m5, 7); ++ return !HasBit(GetTile(t)->m5, 7); + } ++/** @copydoc IsTunnel(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTunnel(TileIndex t) { return IsTunnel<false>(t); } ++/** @copydoc IsTunnel(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTunnel(GenericTileIndex t) { return IsTunnel<true>(t); } + + /** + * Is this a tunnel (entrance)? + * @param t the tile that might be a tunnel + * @return true if and only if this tile is a tunnel (entrance) + */ +-static inline bool IsTunnelTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsTunnelTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_TUNNELBRIDGE) && IsTunnel(t); + } ++/** @copydoc IsTunnelTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTunnelTile(TileIndex t) { return IsTunnelTile<false>(t); } ++/** @copydoc IsTunnelTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsTunnelTile(GenericTileIndex t) { return IsTunnelTile<true>(t); } ++ ++template <bool Tgeneric> ++typename TileIndexT<Tgeneric>::T GetOtherTunnelEnd(typename TileIndexT<Tgeneric>::T t); ++/** @copydoc GetOtherTunnelEnd(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex GetOtherTunnelEnd(TileIndex t) { return GetOtherTunnelEnd<false>(t); } ++/** @copydoc GetOtherTunnelEnd(TileIndexT<Tgeneric>::T) */ ++static inline GenericTileIndex GetOtherTunnelEnd(GenericTileIndex t) { return GetOtherTunnelEnd<true>(t); } + +-TileIndex GetOtherTunnelEnd(TileIndex); + bool IsTunnelInWay(TileIndex, int z); + bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); ++bool IsBetweenChunnelPortals(TileIndex tile, DiagDirection dir); + + /** + * Makes a road tunnel entrance +@@ -48,20 +65,25 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); + * @param d the direction facing out of the tunnel + * @param r the road type used in the tunnel + */ +-static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTypes r) ++template <bool Tgeneric> ++static inline void MakeRoadTunnel(typename TileIndexT<Tgeneric>::T t, Owner o, DiagDirection d, RoadTypes r) + { + SetTileType(t, MP_TUNNELBRIDGE); + SetTileOwner(t, o); +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = TRANSPORT_ROAD << 2 | d; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = TRANSPORT_ROAD << 2 | d; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + SetRoadOwner(t, ROADTYPE_ROAD, o); + if (o != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, o); + SetRoadTypes(t, r); + } ++/** @copydoc MakeRoadTunnel(TileIndexT<Tgeneric>::T,Owner,DiagDirection,RoadTypes) */ ++static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTypes r) { MakeRoadTunnel<false>(t, o, d, r); } ++/** @copydoc MakeRoadTunnel(TileIndexT<Tgeneric>::T,Owner,DiagDirection,RoadTypes) */ ++static inline void MakeRoadTunnel(GenericTileIndex t, Owner o, DiagDirection d, RoadTypes r) { MakeRoadTunnel<true>(t, o, d, r); } + + /** + * Makes a rail tunnel entrance +@@ -70,16 +92,21 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp + * @param d the direction facing out of the tunnel + * @param r the rail type used in the tunnel + */ +-static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r) ++template <bool Tgeneric> ++static inline void MakeRailTunnel(typename TileIndexT<Tgeneric>::T t, Owner o, DiagDirection d, RailType r) + { + SetTileType(t, MP_TUNNELBRIDGE); + SetTileOwner(t, o); +- _m[t].m2 = 0; +- _m[t].m3 = r; +- _m[t].m4 = 0; +- _m[t].m5 = TRANSPORT_RAIL << 2 | d; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = r; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = TRANSPORT_RAIL << 2 | d; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeRailTunnel(TileIndexT<Tgeneric>::T,Owner,DiagDirection,RailType) */ ++static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r) { MakeRailTunnel<false>(t, o, d, r); } ++/** @copydoc MakeRailTunnel(TileIndexT<Tgeneric>::T,Owner,DiagDirection,RailType) */ ++static inline void MakeRailTunnel(GenericTileIndex t, Owner o, DiagDirection d, RailType r) { MakeRailTunnel<true>(t, o, d, r); } + + #endif /* TUNNEL_MAP_H */ +diff --git a/src/tunnelbridge.h b/src/tunnelbridge.h +index 0a2c229..9e46c3e 100644 +--- a/src/tunnelbridge.h ++++ b/src/tunnelbridge.h +@@ -23,7 +23,8 @@ void MarkBridgeDirty(TileIndex tile); + * @param end The end of the tunnel or bridge. + * @return length of bridge/tunnel middle + */ +-static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) ++template <bool Tgeneric> ++static inline uint GetTunnelBridgeLength(typename TileIndexT<Tgeneric>::T begin, typename TileIndexT<Tgeneric>::T end) + { + int x1 = TileX(begin); + int y1 = TileY(begin); +@@ -32,6 +33,10 @@ static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) + + return abs(x2 + y2 - x1 - y1) - 1; + } ++/** @copydoc GetTunnelBridgeLength(TileIndexT<Tgeneric>::T,TileIndexT<Tgeneric>::T) */ ++static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) { return GetTunnelBridgeLength<false>(begin, end); } ++/** @copydoc GetTunnelBridgeLength(TileIndexT<Tgeneric>::T,TileIndexT<Tgeneric>::T) */ ++static inline uint GetTunnelBridgeLength(GenericTileIndex begin, GenericTileIndex end) { return GetTunnelBridgeLength<true>(begin, end); } + + extern TileIndex _build_tunnel_endtile; + +diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp +index 5f2534b..5e5d506 100644 +--- a/src/tunnelbridge_cmd.cpp ++++ b/src/tunnelbridge_cmd.cpp +@@ -17,6 +17,7 @@ + #include "newgrf_object.h" + #include "viewport_func.h" + #include "cmd_helper.h" ++#include "copypaste_cmd.h" + #include "command_func.h" + #include "town.h" + #include "train.h" +@@ -30,6 +31,7 @@ + #include "date_func.h" + #include "clear_func.h" + #include "vehicle_func.h" ++#include "vehicle_gui.h" + #include "sound_func.h" + #include "tunnelbridge.h" + #include "cheat_type.h" +@@ -40,6 +42,7 @@ + #include "object_base.h" + #include "water.h" + #include "company_gui.h" ++#include "clipboard_gui.h" + + #include "table/strings.h" + #include "table/bridge_land.h" +@@ -225,6 +228,24 @@ CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoC + return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG); + } + ++BridgeType FastestAvailableBridgeType(uint bridge_len) ++{ ++ BridgeType ret = MAX_BRIDGES; ++ uint max_speed = 0; ++ ++ /* loop for all bridgetypes */ ++ for (BridgeType brd_type = 0; brd_type != MAX_BRIDGES; brd_type++) { ++ if (CheckBridgeAvailability(brd_type, bridge_len).Failed()) continue; ++ uint speed = GetBridgeSpec(brd_type)->speed; ++ if (max_speed < speed) { ++ max_speed = speed; ++ ret = brd_type; ++ } ++ } ++ ++ return ret; ++} ++ + /** + * Build a Bridge + * @param end_tile end tile +@@ -255,7 +276,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u + switch (transport_type) { + case TRANSPORT_ROAD: + roadtypes = Extract<RoadTypes, 8, 2>(p2); +- if (!HasExactlyOneBit(roadtypes) || !HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR; ++ if (!HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR; + break; + + case TRANSPORT_RAIL: +@@ -322,6 +343,12 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u + if (transport_type == TRANSPORT_WATER && (tileh_start == SLOPE_FLAT || tileh_end == SLOPE_FLAT)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + if (z_start != z_end) return_cmd_error(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT); + ++ /* Don't allow building bridge ramps between chunnel portals. */ ++ DiagDirection dir = DiagdirBetweenTiles(tile_start, tile_end); ++ if (IsBetweenChunnelPortals(tile_start, dir)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ if (IsBetweenChunnelPortals(tile_start, ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ if (IsBetweenChunnelPortals(tile_end, ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ + CommandCost cost(EXPENSES_CONSTRUCTION); + Owner owner; + bool is_new_owner; +@@ -576,7 +603,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u + * @param flags type of operation + * @param p1 bit 0-3 railtype or roadtypes + * bit 8-9 transport type +- * @param p2 unused ++ * @param p2 the end tile (only if DC_PASTE flags is set) + * @param text unused + * @return the cost of this operation or an error + */ +@@ -589,6 +616,9 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + RailType railtype = INVALID_RAILTYPE; + RoadTypes rts = ROADTYPES_NONE; + _build_tunnel_endtile = 0; ++ ++ TileIndex expected_end_tile = (flags & DC_PASTE) ? p2 : (uint32)0; ++ + switch (transport_type) { + case TRANSPORT_RAIL: + railtype = Extract<RailType, 0, 4>(p1); +@@ -597,7 +627,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + + case TRANSPORT_ROAD: + rts = Extract<RoadTypes, 0, 2>(p1); +- if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(company, rts)) return CMD_ERROR; ++ if (!HasRoadTypesAvail(company, rts)) return CMD_ERROR; + break; + + default: return CMD_ERROR; +@@ -618,18 +648,43 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + int start_z; + int end_z; + Slope start_tileh = GetTileSlope(start_tile, &start_z); +- DiagDirection direction = GetInclinedSlopeDirection(start_tileh); +- if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); ++ ++ DiagDirection direction; ++ if (expected_end_tile == 0) { // the end tile is given implicitly by the terrain ++ direction = GetInclinedSlopeDirection(start_tileh); ++ if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); ++ } else { // the end tile is given explicitly ++ direction = DiagdirBetweenTiles(start_tile, expected_end_tile); ++ if (direction == INVALID_DIAGDIR) return CMD_ERROR; ++ if (InclinedSlope(direction) != RemoveHalftileSlope(start_tileh)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); ++ } + + if (HasTileWaterGround(start_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); + +- CommandCost ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); +- if (ret.Failed()) return ret; ++ /* Don't allow building tunnel tiles between chunnel portals. */ ++ if (IsBetweenChunnelPortals(start_tile, direction)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ if (IsBetweenChunnelPortals(start_tile, ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ ++ /* Check if there is already the entrance we are willing to build */ ++ bool may_be_already_built = ++ (flags & DC_PASTE) && ++ IsTileOwner(start_tile, _current_company) && ++ IsTunnelTile(start_tile) && ++ GetTunnelBridgeTransportType(start_tile) == transport_type && ++ GetTunnelBridgeDirection(start_tile) == direction && ++ (transport_type == TRANSPORT_RAIL ? GetTileRailType(start_tile) == railtype : GetRoadTypes(start_tile) == rts); + +- /* XXX - do NOT change 'ret' in the loop, as it is used as the price +- * for the clearing of the entrance of the tunnel. Assigning it to +- * cost before the loop will yield different costs depending on start- +- * position, because of increased-cost-by-length: 'cost += cost >> 3' */ ++ /* If the end tile is not given then we have a match already */ ++ if (may_be_already_built && expected_end_tile == 0) return_cmd_error(STR_ERROR_ALREADY_BUILT); ++ ++ CommandCost cost(EXPENSES_CONSTRUCTION); ++ ++ /* Clear the entrance */ ++ if (!may_be_already_built) { ++ CommandCost ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); ++ if (ret.Failed()) return ret; ++ cost.AddCost(ret); ++ } + + TileIndexDiff delta = TileOffsByDiagDir(direction); + DiagDirection tunnel_in_way_dir; +@@ -647,33 +702,79 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + int tiles = 0; + /* Number of tiles at which the cost increase coefficient per tile is halved */ + int tiles_bump = 25; ++ end_tile += delta; + +- CommandCost cost(EXPENSES_CONSTRUCTION); ++ CommandCost ret(EXPENSES_CONSTRUCTION); ++ /* XXX: The 'ret' is used recursively inside the loop to calculate the cost-by-length: ++ * 'ret += ret >> 3' */ + Slope end_tileh; + for (;;) { +- end_tile += delta; + if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER); + end_tileh = GetTileSlope(end_tile, &end_z); + +- if (start_z == end_z) break; ++ if (start_z == end_z) { ++ /* Handle chunnels only on sea level */ ++ if (end_z != 0) break; ++ ++ /* Test for minimal one water tile. */ ++ if (!IsValidTile(end_tile + delta) || !((IsWaterTile(end_tile + delta)) || (IsCoastTile(end_tile + delta)))) break; ++ ++ /* Continue until water is passed and suitable endsstop is found. */ ++ for (;;) { ++ if (!IsValidTile(end_tile)) break; ++ ++ end_tileh = GetTileSlope(end_tile); ++ if(direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; ++ if(direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; ++ if(direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break; ++ if(direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break; ++ ++ end_tile += delta; ++ tiles++; ++ if (tiles == tiles_bump) { ++ tiles_coef++; ++ tiles_bump *= 2; ++ } ++ ret.AddCost(_price[PR_BUILD_TUNNEL]); ++ ret.AddCost(ret.GetCost() >> tiles_coef); // add a multiplier for longer tunnels ++ } ++ } ++ ++ if (end_tile == expected_end_tile) { // are we getting too far? ++ _build_tunnel_endtile = end_tile; ++ return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); ++ } + + if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) { + return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + } + ++ end_tile += delta; + tiles++; + if (tiles == tiles_bump) { + tiles_coef++; + tiles_bump *= 2; + } + +- cost.AddCost(_price[PR_BUILD_TUNNEL]); +- cost.AddCost(cost.GetCost() >> tiles_coef); // add a multiplier for longer tunnels ++ ret.AddCost(_price[PR_BUILD_TUNNEL]); ++ ret.AddCost(ret.GetCost() >> tiles_coef); // add a multiplier for longer tunnels + } ++ cost.AddCost(ret.GetCost()); ++ ++ /* is the end tile reached? */ ++ if (expected_end_tile != 0 && expected_end_tile != end_tile) { ++ if (may_be_already_built) { ++ return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST); ++ } else { ++ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ } ++ } ++ ++ /* booth ends match? */ ++ if (may_be_already_built) return_cmd_error(STR_ERROR_ALREADY_BUILT); + + /* Add the cost of the entrance */ + cost.AddCost(_price[PR_BUILD_TUNNEL]); +- cost.AddCost(ret); + + /* if the command fails from here on we want the end tile to be highlighted */ + _build_tunnel_endtile = end_tile; +@@ -682,7 +783,10 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + + if (HasTileWaterGround(end_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); + +- /* Clear the tile in any case */ ++ /* Don't allow building tunnels between chunnel portals looking sideways. */ ++ if (IsBetweenChunnelPortals(end_tile, ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); ++ ++ /* Clear the end tile in any case */ + ret = DoCommand(end_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND); + cost.AddCost(ret); +@@ -710,7 +814,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, + + /* Pay for the rail/road in the tunnel including entrances */ + switch (transport_type) { +- case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * _price[PR_BUILD_ROAD] * 2); break; ++ case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * _price[PR_BUILD_ROAD] * 2 * CountBits(rts)); break; + case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break; + default: NOT_REACHED(); + } +@@ -1125,6 +1229,103 @@ static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bo + } + } + ++/* Draws a signal on tunnel / bridge entrance tile. */ ++static void DrawTunnelBridgeRampSignal(const TileInfo *ti) ++{ ++ bool side = (_settings_game.vehicle.road_side != 0) &&_settings_game.construction.train_signal_side; ++ ++ static const Point SignalPositions[2][4] = { ++ { /* X X Y Y Signals on the left side */ ++ {13, 3}, { 2, 13}, { 3, 4}, {13, 14} ++ }, {/* X X Y Y Signals on the right side */ ++ {14, 13}, { 3, 3}, {13, 2}, { 3, 13} ++ } ++ }; ++ ++ uint position; ++ DiagDirection dir = GetTunnelBridgeDirection(ti->tile); ++ ++ switch (dir) { ++ default: NOT_REACHED(); ++ case DIAGDIR_NE: position = 0; break; ++ case DIAGDIR_SE: position = 2; break; ++ case DIAGDIR_SW: position = 1; break; ++ case DIAGDIR_NW: position = 3; break; ++ } ++ ++ uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x; ++ uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y; ++ uint z = ti->z; ++ ++ if (ti->tileh != SLOPE_FLAT && IsBridge(ti->tile)) z += 8; // sloped bridge head ++ SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC); ++ ++ SpriteID sprite; ++ if (variant == SIG_ELECTRIC) { ++ /* Normal electric signals are picked from original sprites. */ ++ sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + IsTunnelBridgeWithSignGreen(ti->tile)); ++ } else { ++ /* All other signals are picked from add on sprites. */ ++ sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + IsTunnelBridgeWithSignGreen(ti->tile)); ++ } ++ ++ AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR); ++} ++ ++/* Draws a signal on tunnel / bridge entrance tile. */ ++static void DrawBrigeSignalOnMiddelPart(const TileInfo *ti, TileIndex bridge_start_tile, uint z) ++{ ++ ++ uint bridge_signal_position = 0; ++ int m2_position = 0; ++ ++ uint bridge_section = GetTunnelBridgeLength(ti->tile, bridge_start_tile) + 1; ++ ++ while (bridge_signal_position <= bridge_section) { ++ bridge_signal_position += _settings_client.gui.simulated_wormhole_signals; ++ if (bridge_signal_position == bridge_section) { ++ bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side; ++ ++ static const Point SignalPositions[2][4] = { ++ { /* X X Y Y Signals on the left side */ ++ {11, 3}, { 4, 13}, { 3, 4}, {11, 13} ++ }, {/* X X Y Y Signals on the right side */ ++ {11, 13}, { 4, 3}, {13, 4}, { 3, 11} ++ } ++ }; ++ ++ uint position; ++ ++ switch (GetTunnelBridgeDirection(bridge_start_tile)) { ++ default: NOT_REACHED(); ++ case DIAGDIR_NE: position = 0; break; ++ case DIAGDIR_SE: position = 2; break; ++ case DIAGDIR_SW: position = 1; break; ++ case DIAGDIR_NW: position = 3; break; ++ } ++ ++ uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x; ++ uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y; ++ z += 5; ++ ++ SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC); ++ ++ SpriteID sprite; ++ ++ if (variant == SIG_ELECTRIC) { ++ /* Normal electric signals are picked from original sprites. */ ++ sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + !HasBit(GetTile(bridge_start_tile)->m2, m2_position)); ++ } else { ++ /* All other signals are picked from add on sprites. */ ++ sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + !HasBit(GetTile(bridge_start_tile)->m2, m2_position)); ++ } ++ ++ AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR); ++ } ++ m2_position++; ++ } ++} ++ + /** + * Draws a tunnel of bridge tile. + * For tunnels, this is rather simple, as you only need to draw the entrance. +@@ -1206,6 +1407,8 @@ static void DrawTile_TunnelBridge(TileInfo *ti) + if (surface != 0) DrawGroundSprite(surface + tunnelbridge_direction, PAL_NONE); + } + ++ DrawOverlay(ti, MP_TUNNELBRIDGE); ++ + /* PBS debugging, draw reserved tracks darker */ + if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) { + if (rti->UsesOverlay()) { +@@ -1239,6 +1442,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti) + AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x, ti->y, BB_data[6], BB_data[7], TILE_HEIGHT, ti->z); + AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x + BB_data[4], ti->y + BB_data[5], BB_data[6], BB_data[7], TILE_HEIGHT, ti->z); + ++ /* Draw signals for tunnel. */ ++ if (IsTunnelBridgeEntrance(ti->tile)) DrawTunnelBridgeRampSignal(ti); ++ + DrawBridgeMiddle(ti); + } else { // IsBridge(ti->tile) + const PalSpriteID *psid; +@@ -1346,6 +1552,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti) + } + } + ++ /* Draw signals for bridge. */ ++ if (HasWormholeSignals(ti->tile)) DrawTunnelBridgeRampSignal(ti); ++ + DrawBridgeMiddle(ti); + } + } +@@ -1494,6 +1703,9 @@ void DrawBridgeMiddle(const TileInfo *ti) + if (HasCatenaryDrawn(GetRailType(rampsouth))) { + DrawCatenaryOnBridge(ti); + } ++ if (HasWormholeSignals(rampsouth)) { ++ IsTunnelBridgeExit(rampsouth) ? DrawBrigeSignalOnMiddelPart(ti, rampnorth, z): DrawBrigeSignalOnMiddelPart(ti, rampsouth, z); ++ } + } + + /* draw roof, the component of the bridge which is logically between the vehicle and the camera */ +@@ -1582,9 +1794,9 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td) + TransportType tt = GetTunnelBridgeTransportType(tile); + + if (IsTunnel(tile)) { +- td->str = (tt == TRANSPORT_RAIL) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD; +- } else { // IsBridge(tile) +- td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt]; ++ td->str = (tt == TRANSPORT_RAIL) ? HasWormholeSignals(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD; ++ } else { // IsBridge(tile) ++ td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : HasWormholeSignals(tile) ? STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt]; + } + td->owner[0] = GetTileOwner(tile); + +@@ -1653,6 +1865,26 @@ static void TileLoop_TunnelBridge(TileIndex tile) + } + } + ++static bool ClickTile_TunnelBridge(TileIndex tile) ++{ ++ /* Show vehicles found in tunnel. */ ++ if (IsTunnelTile(tile)) { ++ int count = 0; ++ const Train *t; ++ TileIndex tile_end = GetOtherTunnelBridgeEnd(tile); ++ FOR_ALL_TRAINS(t) { ++ if (!t->IsFrontEngine()) continue; ++ if (tile == t->tile || tile_end == t->tile) { ++ ShowVehicleViewWindow(t); ++ count++; ++ } ++ if (count > 19) break; // no more than 20 windows open ++ } ++ if (count > 0) return true; ++ } ++ return false; ++} ++ + static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) + { + TransportType transport_type = GetTunnelBridgeTransportType(tile); +@@ -1898,6 +2130,178 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + ++static void CopyPastePlaceTunnel(GenericTileIndex tile, DiagDirection dir, uint mid_len, TransportType transport_type, uint rail_road_types) ++{ ++ GenericTileIndex end_tile = TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x * (mid_len + 1), TileIndexDiffCByDiagDir(dir).y * (mid_len + 1)); ++ if (IsMainMapTile(tile)) { ++ _current_pasting->DoCommand(AsMainMapTile(tile), rail_road_types | (transport_type << 8), AsMainMapTile(end_tile), CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE)); ++ if (_current_pasting->last_result.Failed() && _current_pasting->last_result.GetErrorMessage() == _current_pasting->err_message && _build_tunnel_endtile != 0) { ++ _current_pasting->err_tile = _build_tunnel_endtile; ++ } ++ } else { ++ if (transport_type == TRANSPORT_RAIL) { ++ MakeRailTunnel(tile, OWNER_NONE, dir, (RailType)rail_road_types); ++ MakeRailTunnel(end_tile, OWNER_NONE, ReverseDiagDir(dir), (RailType)rail_road_types); ++ } else { ++ MakeRoadTunnel(tile, OWNER_NONE, dir, (RoadTypes)rail_road_types); ++ MakeRoadTunnel(end_tile, OWNER_NONE, ReverseDiagDir(dir), (RoadTypes)rail_road_types); ++ } ++ } ++} ++ ++static void CopyPastePlaceBridge(GenericTileIndex tile, DiagDirection dir, uint mid_len, BridgeType bridgetype, TransportType transport_type, uint rail_road_types) ++{ ++ GenericTileIndex end_tile = TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x * (mid_len + 1), TileIndexDiffCByDiagDir(dir).y * (mid_len + 1)); ++ if (IsMainMapTile(tile)) { ++ _current_pasting->DoCommand(AsMainMapTile(end_tile), AsMainMapTile(tile), bridgetype | (rail_road_types << 8) | (transport_type << 15), CMD_BUILD_BRIDGE | CMD_MSG(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE)); ++ } else { ++ switch (transport_type) { ++ case TRANSPORT_RAIL: ++ MakeRailBridgeRamp(tile, OWNER_NONE, bridgetype, dir, (RailType)rail_road_types); ++ MakeRailBridgeRamp(end_tile, OWNER_NONE, bridgetype, ReverseDiagDir(dir), (RailType)rail_road_types); ++ break; ++ ++ case TRANSPORT_ROAD: ++ MakeRoadBridgeRamp(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, bridgetype, dir, (RoadTypes)rail_road_types); ++ MakeRoadBridgeRamp(end_tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, bridgetype, ReverseDiagDir(dir), (RoadTypes)rail_road_types); ++ break; ++ ++ case TRANSPORT_WATER: ++ MakeAqueductBridgeRamp(tile, OWNER_NONE, dir); ++ MakeAqueductBridgeRamp(end_tile, OWNER_NONE, ReverseDiagDir(dir)); ++ break; ++ ++ default: ++ NOT_REACHED(); ++ } ++ ++ Axis axis = DiagDirToAxis(dir); ++ while (mid_len-- > 0) { ++ tile = TileAddByDiagDir(tile, dir); ++ SetBridgeMiddle(tile, axis); ++ } ++ } ++} ++ ++/** ++ * Test a given tunnel/bridge tile if there is any contented to be copied from it. ++ * ++ * Tunnels and bridges can't be copy/pasted tile by tile, we have to do it in one step for all ++ * tiles (booth ends). So the function writes the other end to location pointed by \c other_end ++ * but only once per a tunnel/bridge - when it's northern tile is tested. For the second tile ++ * (second end) it still returns \c true but writes "invalid" tile. ++ * ++ * If the funtion returns \c false, \c object_rect remains unchanged. ++ * ++ * @param tile the tile to test ++ * @param src_area the area we are copying ++ * @param mode copy-paste mode ++ * @param other_end (out, may be NULL) other end or "invalid" tile, depending on which tile of the bridge was given ++ * @param company the #Company to check ownership against to ++ * @param preview (out, may be NULL) information on how to higlight preview of the tile ++ * @return whether this tile needs to be copy-pasted ++ */ ++bool TestTunnelBridgeTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileIndex *other_end, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) ++{ ++ if (preview != NULL) MemSetT(preview, 0); ++ ++ /* test ownership */ ++ if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; ++ /* test if tunnel/bridge transport type is enabled in the current copy/paste mode */ ++ TransportType tt = GetTunnelBridgeTransportType(tile); ++ assert_compile(CPM_WITH_RAIL_TRANSPORT == 1 << TRANSPORT_RAIL); ++ if (!HasBit(mode, tt)) return false; ++ ++ if (IsMainMapTile(tile) || other_end != NULL) { ++ GenericTileIndex end_tile = GetOtherTunnelBridgeEnd(tile); ++ /* test if tunnel/bridge is within copy area */ ++ if (IsMainMapTile(tile) && !src_area.Contains(end_tile)) return false; ++ ++ if (other_end != NULL) { ++ *other_end = end_tile; ++ if (tile > end_tile) *other_end = GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)); // copy this tunnel/bridge only once ++ } ++ } ++ ++ if (preview != NULL) { ++ preview->highlight_tile_rect = true; ++ if (tt == TRANSPORT_RAIL) preview->highlight_track_bits = AxisToTrackBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))); ++ } ++ ++ return true; ++} ++ ++void CopyPasteTile_TunnelBridge(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) ++{ ++ GenericTileIndex src_other_end; ++ if (!TestTunnelBridgeTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &src_other_end)) return; // src_other_end will be acquired ++ if (!IsValidTileIndex(src_other_end)) return; // copy this tunnel/bridge only once ++ ++ DiagDirection src_dir = GetTunnelBridgeDirection(src_tile); ++ DiagDirection dst_dir = TransformDiagDir(src_dir, copy_paste.transformation); ++ uint mid_len = GetTunnelBridgeLength(src_tile, src_other_end); ++ ++ /* Terrafrom tiles if needed */ ++ if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { ++ TileIndex first_end = AsMainMapTile(dst_tile); ++ TileIndex second_end = first_end + (mid_len + 1) * ToTileIndexDiff(TileIndexDiffCByDiagDir(dst_dir)); ++ ++ /* copy-paste heights around entrances/heads */ ++ CopyPasteHeights(GenericTileArea(src_tile, 1, 1), GenericTileIndex(first_end), copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ CopyPasteHeights(GenericTileArea(src_other_end, 1, 1), GenericTileIndex(second_end), copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ ++ /* now level land in the middle */ ++ if (mid_len > 1) { ++ /* calculate height and leveling variant */ ++ CopyPasteLevelVariant variant; ++ uint height; ++ if (IsTunnel(src_tile)) { ++ variant = CPLV_LEVEL_BELOW; ++ height = GetTileMaxZ(src_tile); ++ } else { ++ variant = CPLV_LEVEL_ABOVE; ++ height = GetBridgeHeight(src_tile) - 1; ++ } ++ height += copy_paste.height_delta; ++ ++ /* strat from the northern corner of the second (counting from north) middle tile ++ * and finish at the southern corner of the last but one middle tile */ ++ TileArea leveling_area(first_end, second_end); ++ if (DiagDirToAxis(dst_dir) == AXIS_X) { ++ leveling_area.tile += TileDiffXY(2, 0); ++ leveling_area.w -= 3; // length ++ leveling_area.h += 1; ++ } else { ++ leveling_area.tile += TileDiffXY(0, 2); ++ leveling_area.w += 1; ++ leveling_area.h -= 3; // length ++ } ++ ++ /* level land */ ++ LevelPasteLand(leveling_area, height, variant); ++ if (IsPastingInterrupted()) return; ++ } ++ } ++ ++ TransportType transport_type = GetTunnelBridgeTransportType(src_tile); ++ uint rail_road_types = 0; ++ switch (transport_type) { ++ case TRANSPORT_RAIL: rail_road_types = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); break; ++ case TRANSPORT_ROAD: rail_road_types = GetRoadTypes(src_tile); break; ++ default: break; ++ } ++ ++ if (IsTunnel(src_tile)) { ++ CopyPastePlaceTunnel(dst_tile, dst_dir, mid_len, transport_type, rail_road_types); ++ } else { ++ BridgeType bridge_type = (copy_paste.mode & CPM_UPGRADE_BRIDGES) ? FastestAvailableBridgeType(mid_len) : GetBridgeType(src_tile); ++ CopyPastePlaceBridge(dst_tile, dst_dir, mid_len, bridge_type, transport_type, rail_road_types); ++ } ++} ++ ++ + extern const TileTypeProcs _tile_type_tunnelbridge_procs = { + DrawTile_TunnelBridge, // draw_tile_proc + GetSlopePixelZ_TunnelBridge, // get_slope_z_proc +@@ -1905,7 +2309,7 @@ extern const TileTypeProcs _tile_type_tunnelbridge_procs = { + NULL, // add_accepted_cargo_proc + GetTileDesc_TunnelBridge, // get_tile_desc_proc + GetTileTrackStatus_TunnelBridge, // get_tile_track_status_proc +- NULL, // click_tile_proc ++ ClickTile_TunnelBridge, // click_tile_proc + NULL, // animate_tile_proc + TileLoop_TunnelBridge, // tile_loop_proc + ChangeTileOwner_TunnelBridge, // change_tile_owner_proc +@@ -1913,4 +2317,5 @@ extern const TileTypeProcs _tile_type_tunnelbridge_procs = { + VehicleEnter_TunnelBridge, // vehicle_enter_tile_proc + GetFoundation_TunnelBridge, // get_foundation_proc + TerraformTile_TunnelBridge, // terraform_tile_proc ++ CopyPasteTile_TunnelBridge, // copypaste_tile_proc + }; +diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h +index 0f7f17b..a077f11 100644 +--- a/src/tunnelbridge_map.h ++++ b/src/tunnelbridge_map.h +@@ -25,11 +25,16 @@ + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return the above mentioned direction + */ +-static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetTunnelBridgeDirection(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- return (DiagDirection)GB(_m[t].m5, 0, 2); ++ return (DiagDirection)GB(GetTile(t)->m5, 0, 2); + } ++/** @copydoc GetTunnelBridgeDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) { return GetTunnelBridgeDirection<false>(t); } ++/** @copydoc GetTunnelBridgeDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetTunnelBridgeDirection(GenericTileIndex t) { return GetTunnelBridgeDirection<true>(t); } + + /** + * Tunnel: Get the transport type of the tunnel (road or rail) +@@ -38,11 +43,16 @@ static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return the transport type in the tunnel/bridge + */ +-static inline TransportType GetTunnelBridgeTransportType(TileIndex t) ++template <bool Tgeneric> ++static inline TransportType GetTunnelBridgeTransportType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- return (TransportType)GB(_m[t].m5, 2, 2); ++ return (TransportType)GB(GetTile(t)->m5, 2, 2); + } ++/** @copydoc GetTunnelBridgeTransportType(TileIndexT<Tgeneric>::T) */ ++static inline TransportType GetTunnelBridgeTransportType(TileIndex t) { return GetTunnelBridgeTransportType<false>(t); } ++/** @copydoc GetTunnelBridgeTransportType(TileIndexT<Tgeneric>::T) */ ++static inline TransportType GetTunnelBridgeTransportType(GenericTileIndex t) { return GetTunnelBridgeTransportType<true>(t); } + + /** + * Tunnel: Is this tunnel entrance in a snowy or desert area? +@@ -54,7 +64,7 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t) + static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- return HasBit(_me[t].m7, 5); ++ return HasBit(GetTileEx(t)->m7, 5); + } + + /** +@@ -68,7 +78,7 @@ static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) + static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); +- SB(_me[t].m7, 5, 1, snow_or_desert); ++ SB(GetTileEx(t)->m7, 5, 1, snow_or_desert); + } + + /** +@@ -77,11 +87,16 @@ static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return other end + */ +-static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T GetOtherTunnelBridgeEnd(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); + return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t); + } ++/** @copydoc GetOtherTunnelBridgeEnd(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) { return GetOtherTunnelBridgeEnd<false>(t); } ++/** @copydoc GetOtherTunnelBridgeEnd(TileIndexT<Tgeneric>::T) */ ++static inline GenericTileIndex GetOtherTunnelBridgeEnd(GenericTileIndex t) { return GetOtherTunnelBridgeEnd<true>(t); } + + + /** +@@ -94,7 +109,7 @@ static inline bool HasTunnelBridgeReservation(TileIndex t) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); + assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); +- return HasBit(_m[t].m5, 4); ++ return HasBit(GetTile(t)->m5, 4); + } + + /** +@@ -107,7 +122,7 @@ static inline void SetTunnelBridgeReservation(TileIndex t, bool b) + { + assert(IsTileType(t, MP_TUNNELBRIDGE)); + assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); +- SB(_m[t].m5, 4, 1, b ? 1 : 0); ++ SB(GetTile(t)->m5, 4, 1, b ? 1 : 0); + } + + /** +@@ -121,4 +136,98 @@ static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t) + return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; + } + ++/** ++ * Declare tunnel/bridge with signal simulation. ++ * @param t the tunnel/bridge tile. ++ */ ++static inline void SetBitTunnelBridgeSignal(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ SetBit(GetTile(t)->m5, 5); ++} ++ ++/** ++ * Remove tunnel/bridge with signal simulation. ++ * @param t the tunnel/bridge tile. ++ */ ++static inline void ClrBitTunnelBridgeSignal(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ ClrBit(GetTile(t)->m5, 5); ++} ++ ++/** ++ * Declare tunnel/bridge exit. ++ * @param t the tunnel/bridge tile. ++ */ ++static inline void SetBitTunnelBridgeExit(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ SetBit(GetTile(t)->m5, 6); ++} ++ ++/** ++ * Remove tunnel/bridge exit declaration. ++ * @param t the tunnel/bridge tile. ++ */ ++static inline void ClrBitTunnelBridgeExit(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ ClrBit(GetTile(t)->m5, 6); ++} ++ ++/** ++ * Is this a tunnel/bridge pair with signal simulation? ++ * On tunnel/bridge pair minimal one of the two bits is set. ++ * @param t the tile that might be a tunnel/bridge. ++ * @return true if and only if this tile is a tunnel/bridge with signal simulation. ++ */ ++static inline bool HasWormholeSignals(TileIndex t) ++{ ++ return IsTileType(t, MP_TUNNELBRIDGE) && (HasBit(GetTile(t)->m5, 5) || HasBit(GetTile(t)->m5, 6)) ; ++} ++ ++/** ++ * Is this a tunnel/bridge with sign on green? ++ * @param t the tile that might be a tunnel/bridge with sign set green. ++ * @pre IsTileType(t, MP_TUNNELBRIDGE) ++ * @return true if and only if this tile is a tunnel/bridge entrance. ++ */ ++static inline bool IsTunnelBridgeWithSignGreen(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ return HasBit(GetTile(t)->m5, 5) && !HasBit(GetTile(t)->m5, 6); ++} ++ ++static inline bool IsTunnelBridgeWithSignRed(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ return HasBit(GetTile(t)->m5, 5) && HasBit(GetTile(t)->m5, 6); ++} ++ ++/** ++ * Is this a tunnel/bridge entrance tile with signal? ++ * Tunnel bridge signal simulation has allways bit 5 on at entrance. ++ * @param t the tile that might be a tunnel/bridge. ++ * @return true if and only if this tile is a tunnel/bridge entrance. ++ */ ++static inline bool IsTunnelBridgeEntrance(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ return HasBit(GetTile(t)->m5, 5) ; ++} ++ ++/** ++ * Is this a tunnel/bridge exit? ++ * @param t the tile that might be a tunnel/bridge. ++ * @return true if and only if this tile is a tunnel/bridge exit. ++ */ ++static inline bool IsTunnelBridgeExit(TileIndex t) ++{ ++ assert(IsTileType(t, MP_TUNNELBRIDGE)); ++ return !HasBit(GetTile(t)->m5, 5) && HasBit(GetTile(t)->m5, 6); ++} ++ ++ ++ + #endif /* TUNNELBRIDGE_MAP_H */ +diff --git a/src/vehicle.cpp b/src/vehicle.cpp +index a482520..730a647 100644 +--- a/src/vehicle.cpp ++++ b/src/vehicle.cpp +@@ -221,6 +221,13 @@ uint Vehicle::Crash(bool flooded) + return RandomRange(pass + 1); // Randomise deceased passengers. + } + ++bool Vehicle::IsDrawn() const ++{ ++ return !(this->vehstatus & VS_HIDDEN) || ++ (!IsTransparencySet(TO_TUNNELS) && ++ ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) || ++ (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE))); ++} + + /** + * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking. +@@ -809,7 +816,7 @@ Vehicle::~Vehicle() + + /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles, + * it may happen that vehicle chain is deleted when visible */ +- if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty(); ++ if (this->IsDrawn()) this->MarkAllViewportsDirty(); + + Vehicle *v = this->Next(); + this->SetNext(NULL); +@@ -914,7 +921,7 @@ void CallVehicleTicks() + if (front->vehstatus & VS_CRASHED) continue; + + /* Do not play any sound when in depot or tunnel */ +- if (v->vehstatus & VS_HIDDEN) continue; ++ if (!v->IsDrawn()) continue; + + /* Do not play any sound when stopped */ + if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue; +@@ -1013,7 +1020,7 @@ static void DoDrawVehicle(const Vehicle *v) + if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); + + /* Check whether the vehicle shall be transparent due to the game state */ +- bool shadowed = (v->vehstatus & VS_SHADOW) != 0; ++ bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN)) != 0; + + if (v->type == VEH_EFFECT) { + /* Check whether the vehicle shall be transparent/invisible due to GUI settings. +@@ -1064,7 +1071,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi) + const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF + + while (v != NULL) { +- if (!(v->vehstatus & VS_HIDDEN) && ++ if (v->IsDrawn() && + l <= v->coord.right && + t <= v->coord.bottom && + r >= v->coord.left && +@@ -1099,7 +1106,7 @@ Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y) + y = ScaleByZoom(y, vp->zoom) + vp->virtual_top; + + FOR_ALL_VEHICLES(v) { +- if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 && ++ if (v->IsDrawn() && !(v->vehstatus & VS_UNCLICKABLE) && + x >= v->coord.left && x <= v->coord.right && + y >= v->coord.top && y <= v->coord.bottom) { + +diff --git a/src/vehicle_base.h b/src/vehicle_base.h +index 59584da..672218f 100644 +--- a/src/vehicle_base.h ++++ b/src/vehicle_base.h +@@ -295,6 +295,12 @@ public: + uint GetConsistTotalCapacity() const; + + /** ++ * Is this vehicle drawn? ++ * @return true if it is drawn ++ */ ++ bool IsDrawn() const; ++ ++ /** + * Marks the vehicles to be redrawn and updates cached variables + * + * This method marks the area of the vehicle on the screen as dirty. +diff --git a/src/viewport.cpp b/src/viewport.cpp +index a1bb2c8..560fc3b 100644 +--- a/src/viewport.cpp ++++ b/src/viewport.cpp +@@ -41,19 +41,19 @@ + * looking only at the X-coordinate. + * + * \verbatim +- * ╳ * +- * ╱ ╲ * +- * ╳ 0 ╳ * +- * ╱ ╲ ╱ ╲ * +- * ╳-1 ╳ 1 ╳ * +- * ╱ ╲ ╱ ╲ ╱ ╲ * +- * ╳-2 ╳ 0 ╳ 2 ╳ * +- * ╲ ╱ ╲ ╱ ╲ ╱ * +- * ╳-1 ╳ 1 ╳ * +- * ╲ ╱ ╲ ╱ * +- * ╳ 0 ╳ * +- * ╲ ╱ * +- * ╳ * ++ * ? * ++ * ? ? * ++ * ? 0 ? * ++ * ? ? ? ? * ++ * ?-1 ? 1 ? * ++ * ? ? ? ? ? ? * ++ * ?-2 ? 0 ? 2 ? * ++ * ? ? ? ? ? ? * ++ * ?-1 ? 1 ? * ++ * ? ? ? ? * ++ * ? 0 ? * ++ * ? ? * ++ * ? * + * \endverbatim + * + * +@@ -75,11 +75,14 @@ + #include "blitter/factory.hpp" + #include "strings_func.h" + #include "zoom_func.h" ++#include "overlay.h" ++#include "overlay_cmd.h" + #include "vehicle_func.h" + #include "company_func.h" + #include "waypoint_func.h" + #include "window_func.h" + #include "tilehighlight_func.h" ++#include "clipboard_gui.h" + #include "window_gui.h" + #include "linkgraph/linkgraph_gui.h" + #include "viewport_sprite_sorter.h" +@@ -937,6 +940,32 @@ static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal) + DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL); + } + ++/** ++ * Draws a selection point on a tile. ++ * ++ * @param ti TileInfo Tile that is being drawn ++ * @param pal Palette to apply. ++ */ ++static void DrawPointSelection(const TileInfo *ti, PaletteID pal) ++{ ++ /* Figure out the Z coordinate for the single dot. */ ++ int z = 0; ++ FoundationPart foundation_part = FOUNDATION_PART_NORMAL; ++ if (ti->tileh & SLOPE_N) { ++ z += TILE_HEIGHT; ++ if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; ++ } ++ if (IsHalftileSlope(ti->tileh)) { ++ Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); ++ if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; ++ if (halftile_corner != CORNER_S) { ++ foundation_part = FOUNDATION_PART_HALFTILE; ++ if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; ++ } ++ } ++ DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, pal, ti, z, foundation_part); ++} ++ + static bool IsPartOfAutoLine(int px, int py) + { + px -= _thd.selstart.x; +@@ -1004,6 +1033,31 @@ static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type) + DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part); + } + ++static void DrawPastePreviewSelection(const TileInfo *ti, bool is_redsq) ++{ ++ TilePastePreview tile_preview; ++ GetTilePastePreview(ti->tile, &tile_preview); ++ ++ /* draw tile rectangle */ ++ if (!is_redsq && tile_preview.highlight_tile_rect) DrawTileSelectionRect(ti, PAL_NONE); ++ ++ /* draw tracks */ ++ Track t; ++ FOR_EACH_SET_TRACK(t, tile_preview.highlight_track_bits) DrawAutorailSelection(ti, t); ++ ++ /* draw height point */ ++ PaletteID pal; ++ int height_diff = tile_preview.tile_height - TileHeight(ti->tile); ++ if (height_diff > 0) { ++ pal = PALETTE_SEL_TILE_RED; // target height is grater then current ++ } else if (height_diff < 0) { ++ pal = PALETTE_SEL_TILE_BLUE; // target height is lower then current ++ } else { ++ pal = PAL_NONE; // target and current height is the same ++ } ++ DrawPointSelection(ti, pal); ++} ++ + /** + * Checks if the specified tile is selected and if so draws selection using correct selectionstyle. + * @param *ti TileInfo Tile that is being drawn +@@ -1029,22 +1083,11 @@ draw_inner: + if (_thd.drawstyle & HT_RECT) { + if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE); + } else if (_thd.drawstyle & HT_POINT) { +- /* Figure out the Z coordinate for the single dot. */ +- int z = 0; +- FoundationPart foundation_part = FOUNDATION_PART_NORMAL; +- if (ti->tileh & SLOPE_N) { +- z += TILE_HEIGHT; +- if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; +- } +- if (IsHalftileSlope(ti->tileh)) { +- Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); +- if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; +- if (halftile_corner != CORNER_S) { +- foundation_part = FOUNDATION_PART_HALFTILE; +- if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; +- } ++ if (_thd.place_mode & HT_PASTE_PREVIEW) { ++ DrawPastePreviewSelection(ti, is_redsq); ++ } else { ++ DrawPointSelection(ti, PAL_NONE); + } +- DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part); + } else if (_thd.drawstyle & HT_RAIL) { + /* autorail highlight piece under cursor */ + HighLightStyle type = _thd.drawstyle & HT_DIR_MASK; +@@ -1255,8 +1298,7 @@ static void ViewportAddTownNames(DrawPixelInfo *dpi) + const Town *t; + FOR_ALL_TOWNS(t) { + ViewportAddString(dpi, ZOOM_LVL_OUT_16X, &t->cache.sign, +- _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, +- STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK, ++ t->Label(), t->SmallLabel(), STR_VIEWPORT_TOWN_TINY_BLACK, + t->index, t->cache.population); + } + } +@@ -2153,7 +2195,7 @@ static void PlaceObject() + pt = GetTileBelowCursor(); + if (pt.x == -1) return; + +- if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT) { ++ if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT && !(_thd.place_mode & HT_PASTE_PREVIEW)) { + pt.x += TILE_SIZE / 2; + pt.y += TILE_SIZE / 2; + } +@@ -2396,8 +2438,10 @@ void UpdateTileSelection() + break; + case HT_POINT: + new_drawstyle = HT_POINT; +- x1 += TILE_SIZE / 2; +- y1 += TILE_SIZE / 2; ++ if (!(_thd.place_mode & HT_PASTE_PREVIEW)) { ++ x1 += TILE_SIZE / 2; ++ y1 += TILE_SIZE / 2; ++ } + break; + case HT_RAIL: + /* Draw one highlighted tile in any direction */ +@@ -3141,6 +3185,19 @@ EventState VpHandlePlaceSizingDrag() + return ES_HANDLED; + } + ++extern EventState VpHandleMouseWheel(int mousewheel) ++{ ++ EventState ret = ES_NOT_HANDLED; ++ ++ Window *w = _thd.GetCallbackWnd(); ++ if (w != NULL) { ++ ret = w->OnPlaceMouseWheel(GetTileBelowCursor(), mousewheel); ++ if (ret == ES_HANDLED) SetSelectionTilesDirty(); ++ } ++ ++ return ret; ++} ++ + /** + * Change the cursor and mouse click/drag handling to a mode for performing special operations like tile area selection, object placement, etc. + * @param icon New shape of the mouse cursor. +@@ -3225,6 +3282,14 @@ Point GetViewportStationMiddle(const ViewPort *vp, const Station *st) + return p; + } + ++void DrawOverlay(const TileInfo *ti, TileType tt) ++{ ++ if (Overlays::Instance()->IsTileInCatchmentArea(ti, PRODUCTION)) { ++ DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); ++ } else if (Overlays::Instance()->IsTileInCatchmentArea(ti, ACCEPTANCE)) { ++ DrawTileSelectionRect(ti, PAL_NONE); ++ } ++} + /** Helper class for getting the best sprite sorter. */ + struct ViewportSSCSS { + VpSorterChecker fct_checker; ///< The check function. +diff --git a/src/viewport_func.h b/src/viewport_func.h +index cbdcc50..fb6008d 100644 +--- a/src/viewport_func.h ++++ b/src/viewport_func.h +@@ -17,6 +17,7 @@ + #include "window_type.h" + #include "tile_type.h" + #include "station_type.h" ++#include "tile_cmd.h" + + static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference is displayed as 50m. + +@@ -84,4 +85,6 @@ void MarkTileDirtyByTileOutsideMap(int x, int y); + + Point GetViewportStationMiddle(const ViewPort *vp, const Station *st); + +-#endif /* VIEWPORT_FUNC_H */ ++void DrawOverlay(const TileInfo *ti, TileType tt); ++ ++#endif /* VIEWPORT_FUNC_H */ +\ No newline at end of file +diff --git a/src/viewport_gui.cpp b/src/viewport_gui.cpp +index 90b0e14..d91a614 100644 +--- a/src/viewport_gui.cpp ++++ b/src/viewport_gui.cpp +@@ -16,6 +16,7 @@ + #include "strings_func.h" + #include "zoom_func.h" + #include "window_func.h" ++#include "gfx_func.h" + + #include "widgets/viewport_widget.h" + +@@ -47,15 +48,21 @@ static const NWidgetPart _nested_extra_view_port_widgets[] = { + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), ++ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_EV_FOLLOW_CURSOR), SetMinimalSize(22, 22), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_EXTRA_VIEW_FOLLOW_CURSOR, STR_EXTRA_VIEW_FOLLOW_CURSOR_TT), ++ EndContainer(), ++ NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + }; + + class ExtraViewportWindow : public Window { ++ bool follow_cursor; + public: + ExtraViewportWindow(WindowDesc *desc, int window_number, TileIndex tile) : Window(desc) + { ++ this->follow_cursor = false; ++ + this->InitNested(window_number); + + NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_EV_VIEWPORT); +@@ -117,6 +124,12 @@ public: + this->viewport->dest_scrollpos_y = y + (w->viewport->virtual_height - this->viewport->virtual_height) / 2; + break; + } ++ ++ case WID_EV_FOLLOW_CURSOR: ++ this->follow_cursor ^= true; ++ this->SetWidgetsDisabledState(this->follow_cursor, WID_EV_MAIN_TO_VIEW, WID_EV_VIEW_TO_MAIN, WIDGET_LIST_END); ++ this->SetDirty(); ++ break; + } + } + +@@ -130,6 +143,9 @@ public: + + virtual void OnScroll(Point delta) + { ++ /* do not scroll if following cursor */ ++ if (this->follow_cursor) return; ++ + this->viewport->scrollpos_x += ScaleByZoom(delta.x, this->viewport->zoom); + this->viewport->scrollpos_y += ScaleByZoom(delta.y, this->viewport->zoom); + this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; +@@ -154,6 +170,25 @@ public: + /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */ + HandleZoomMessage(this, this->viewport, WID_EV_ZOOM_IN, WID_EV_ZOOM_OUT); + } ++ ++ virtual void OnPaint() ++ { ++ this->SetWidgetLoweredState(WID_EV_FOLLOW_CURSOR, this->follow_cursor); ++ this->DrawWidgets(); ++ } ++ ++ virtual void OnMouseLoop() ++ { ++ if (!this->follow_cursor) return; ++ if (FindWindowFromPt(_cursor.pos.x, _cursor.pos.y) != this) ++ { ++ Point pt = GetTileBelowCursor(); ++ if (pt.x > -1 && pt.y > -1) ++ { ++ ScrollWindowTo(pt.x, pt.y, -1, this, true); ++ } ++ } ++ } + }; + + static WindowDesc _extra_view_port_desc( +diff --git a/src/viewport_type.h b/src/viewport_type.h +index 07485c3..bb171f7 100644 +--- a/src/viewport_type.h ++++ b/src/viewport_type.h +@@ -121,6 +121,9 @@ enum ViewportDragDropSelectionProcess { + DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks) + DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses) + DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks) ++ ++ /* Clipboard */ ++ DDSP_COPY_TO_CLIPBOARD, ///< Copy area to clipboard + }; + + #endif /* VIEWPORT_TYPE_H */ +diff --git a/src/void_cmd.cpp b/src/void_cmd.cpp +index ffe54df..83a6760 100644 +--- a/src/void_cmd.cpp ++++ b/src/void_cmd.cpp +@@ -83,4 +83,5 @@ extern const TileTypeProcs _tile_type_void_procs = { + NULL, // vehicle_enter_tile_proc + GetFoundation_Void, // get_foundation_proc + TerraformTile_Void, // terraform_tile_proc ++ NULL // copypaste_tile_proc + }; +diff --git a/src/void_map.h b/src/void_map.h +index 5ccc4e9..97ce33f 100644 +--- a/src/void_map.h ++++ b/src/void_map.h +@@ -18,17 +18,22 @@ + * Make a nice void tile ;) + * @param t the tile to make void + */ +-static inline void MakeVoid(TileIndex t) ++template <bool Tgeneric> ++static inline void MakeVoid(typename TileIndexT<Tgeneric>::T t) + { + SetTileType(t, MP_VOID); + SetTileHeight(t, 0); +- _m[t].m1 = 0; +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = 0; +- _me[t].m6 = 0; +- _me[t].m7 = 0; ++ GetTile(t)->m1 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = 0; ++ GetTileEx(t)->m6 = 0; ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeVoid(TileIndexT<Tgeneric>::T) */ ++static inline void MakeVoid(TileIndex t) { MakeVoid<false>(t); } ++/** @copydoc MakeVoid(TileIndexT<Tgeneric>::T) */ ++static inline void MakeVoid(GenericTileIndex t) { MakeVoid<true>(t); } + + #endif /* VOID_MAP_H */ +diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp +index 4392eb2..c05a129 100644 +--- a/src/water_cmd.cpp ++++ b/src/water_cmd.cpp +@@ -11,6 +11,7 @@ + + #include "stdafx.h" + #include "cmd_helper.h" ++#include "copypaste_cmd.h" + #include "landscape.h" + #include "viewport_func.h" + #include "command_func.h" +@@ -38,6 +39,7 @@ + #include "date_func.h" + #include "company_base.h" + #include "company_gui.h" ++#include "clipboard_gui.h" + #include "newgrf_generic.h" + + #include "table/strings.h" +@@ -105,8 +107,15 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui + + TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + +- if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { +- return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); ++ WaterClass wc1, wc2; ++ if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { ++ /* When pasting a ship depot, there may be no water yet (a canal will be placed when DC_EXE'ing). ++ * Ignore that there is no water so we can calculate the cost more precisely. */ ++ wc1 = wc2 = WATER_CLASS_INVALID; ++ } else { ++ if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); ++ wc1 = GetWaterClass(tile); ++ wc2 = GetWaterClass(tile2); + } + + if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); +@@ -118,8 +127,6 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui + + if (!Depot::CanAllocateItem()) return CMD_ERROR; + +- WaterClass wc1 = GetWaterClass(tile); +- WaterClass wc2 = GetWaterClass(tile2); + CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); + + bool add_cost = !IsWaterTile(tile); +@@ -146,6 +153,7 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui + Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR; + DirtyCompanyInfrastructureWindows(_current_company); + ++ assert(wc1 != WATER_CLASS_INVALID && wc2 != WATER_CLASS_INVALID); + MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); + MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); + MarkTileDirtyByTile(tile); +@@ -863,6 +871,7 @@ static void DrawTile_Water(TileInfo *ti) + DrawWaterDepot(ti); + break; + } ++ DrawOverlay(ti, MP_WATER); + } + + void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part) +@@ -1315,6 +1324,175 @@ static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + ++void CopyPastePlaceCannal(GenericTileIndex tile) ++{ ++ if (IsMainMapTile(tile)) { ++ _current_pasting->DoCommand(AsMainMapTile(tile), AsMainMapTile(tile), WATER_CLASS_CANAL, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_BUILD_CANALS)); ++ } else { ++ MakeCanal(tile, OWNER_NONE, 0); ++ } ++} ++ ++static void CopyPastePlaceLock(GenericTileIndex tile, DiagDirection dir) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ if (dir != GetInclinedSlopeDirection(GetTilePixelSlope(tile, NULL))) { ++ _current_pasting->CollectError(t, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION, STR_ERROR_CAN_T_BUILD_LOCKS); ++ } else if (IsTileType(t, MP_WATER) && IsTileOwner(t, _current_company) && IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) { ++ _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_LOCKS); ++ } else { ++ _current_pasting->DoCommand(t, 0, 0, CMD_BUILD_LOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_LOCKS)); ++ } ++ } else { ++ MakeLock(tile, OWNER_NONE, dir, WATER_CLASS_INVALID, WATER_CLASS_INVALID, WATER_CLASS_INVALID); ++ } ++} ++ ++static void CopyPastePlaceShipDepot(GenericTileIndex tile, DiagDirection dir) ++{ ++ GenericTileIndex other_tile = TileAddByDiagDir(tile, ReverseDiagDir(dir)); ++ ++ if (IsMainMapTile(tile)) { ++ TileIndex t1 = AsMainMapTile(tile); ++ TileIndex t2 = AsMainMapTile(other_tile); ++ /* build a canal if not on water */ ++ if (!HasTileWaterGround(t1)) { ++ CopyPastePlaceCannal(GenericTileIndex(t1)); ++ if (_current_pasting->last_result.Failed()) return; ++ } ++ if (!HasTileWaterGround(t2)) { ++ CopyPastePlaceCannal(GenericTileIndex(t2)); ++ if (_current_pasting->last_result.Failed()) return; ++ } ++ /* build the depot */ ++ if (IsShipDepotTile(t1) && IsTileOwner(t1, _current_company) && GetOtherShipDepotTile(t1) == t2) { ++ _current_pasting->CollectError(t1, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_SHIP_DEPOT); ++ } else { ++ _current_pasting->DoCommand(min(t1, t2), DiagDirToAxis(dir), 0, CMD_BUILD_SHIP_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT)); ++ } ++ } else { ++ MakeShipDepot(min(tile, other_tile), OWNER_NONE, 0, DEPOT_PART_NORTH, DiagDirToAxis(dir), WATER_CLASS_INVALID); ++ MakeShipDepot(max(tile, other_tile), OWNER_NONE, 0, DEPOT_PART_SOUTH, DiagDirToAxis(dir), WATER_CLASS_INVALID); ++ } ++} ++ ++/** ++ * Test a given water tile if there is any contented to be copied from it. ++ * ++ * Some water objects (e.g. water locks) can't be copy/pasted tile by tile, we have to do it with ++ * bigger rectangular pieces. The function writes this area to location pointed by \c object_rect ++ * but only once per a piece - when a certain tile is tested: ++ * - in case of water locks, valid area is written when the center tile of a lock is tested ++ * - in case of ship depots, valid area is written when the northern tile of a depot is tested ++ * For the rest of tiles the function still returns \c true but writes "invalid" area. ++ * ++ * If the funtion returns \c false, \c object_rect remains unchanged. ++ * ++ * @param tile the tile to test ++ * @param src_area the tile area we are copying ++ * @param mode copy-paste mode ++ * @param object_rect (out, may be NULL) area to be copy pasted in this step or "invalid" area, depending on which tile was given ++ * @param company the #Company to check ownership against to ++ * @param preview (out, may be NULL) information on how to higlight preview of the tile ++ * @return whether this tile needs to be copy-pasted ++ */ ++bool TestWaterTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *object_rect, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) ++{ ++ if (preview != NULL) MemSetT(preview, 0); ++ ++ if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; ++ if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; ++ ++ switch (GetWaterTileType(tile)) { ++ case WATER_TILE_CLEAR: ++ if (GetWaterClass(tile) != WATER_CLASS_CANAL) return false; // copy only cannals ++ if (object_rect != NULL) *object_rect = GenericTileArea(tile, 1, 1); ++ break; ++ ++ case WATER_TILE_LOCK: ++ if (IsMainMapTile(tile) || object_rect != NULL) { ++ DiagDirection dir = GetLockDirection(tile); ++ switch (GetLockPart(tile)) { ++ case LOCK_PART_MIDDLE: { ++ GenericTileArea ta(tile, 1, 1); ++ ta.Add(TileAddByDiagDir(tile, dir)); ++ ta.Add(TileAddByDiagDir(tile, ReverseDiagDir(dir))); ++ if (IsMainMapTile(tile) && !src_area.Contains(ta)) return false; ++ if (object_rect != NULL) *object_rect = ta; ++ break; ++ } ++ ++ case LOCK_PART_UPPER: ++ dir = ReverseDiagDir(dir); ++ /* FALLTHROUGH */ ++ case LOCK_PART_LOWER: ++ if (IsMainMapTile(tile) && !src_area.Contains(TileAddByDiagDir(TileAddByDiagDir(tile, dir), dir))) return false; ++ if (object_rect != NULL) *object_rect = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); ++ break; ++ } ++ } ++ break; ++ ++ case WATER_TILE_DEPOT: { ++ /* test if the depot is within copy area */ ++ GenericTileIndex other_tile = GetOtherShipDepotTile(tile); ++ if (IsMainMapTile(tile) && !src_area.Contains(other_tile)) return false; ++ ++ if (object_rect != NULL) { ++ if (tile < other_tile) { // copy this depot only once ++ *object_rect = GenericTileArea(tile, other_tile); ++ } else { ++ *object_rect = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); ++ } ++ } ++ break; ++ } ++ ++ default: ++ return false; ++ } ++ ++ if (preview != NULL) preview->highlight_tile_rect = true; ++ return true; ++} ++ ++void CopyPasteTile_Water(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) ++{ ++ GenericTileArea src_object_rect; ++ if (!TestWaterTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &src_object_rect)) return; ++ if (!IsValidTileIndex(src_object_rect.tile)) return; // copy water object (e.g. water lock or ship depot) only once ++ ++ /* Terraform tiles if needed */ ++ if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { ++ GenericTileIndex t = copy_paste.src_area.ReverseTransformTile(src_tile, dst_tile, copy_paste.transformation); // transformed northern tile of the area.src ++ t = copy_paste.src_area.TransformTile(src_object_rect.tile, t, copy_paste.transformation); // transformed northern tile of the src_object_rect ++ t = src_object_rect.ReverseTransformedNorth(t, copy_paste.transformation); // northern tile of the transformed src_object_rect ++ CopyPasteHeights(src_object_rect, t, copy_paste.transformation, copy_paste.height_delta); ++ if (IsPastingInterrupted()) return; ++ } ++ ++ switch (GetWaterTileType(src_tile)) { ++ case WATER_TILE_CLEAR: ++ CopyPastePlaceCannal(dst_tile); ++ break; ++ ++ case WATER_TILE_LOCK: { ++ DiagDirection dir = TransformDiagDir(GetLockDirection(src_tile), copy_paste.transformation); ++ CopyPastePlaceLock(dst_tile, dir); ++ break; ++ } ++ ++ case WATER_TILE_DEPOT: ++ CopyPastePlaceShipDepot(dst_tile, TransformDiagDir(GetShipDepotDirection(src_tile), copy_paste.transformation)); ++ break; ++ ++ default: ++ NOT_REACHED(); ++ break; ++ } ++} ++ + + extern const TileTypeProcs _tile_type_water_procs = { + DrawTile_Water, // draw_tile_proc +@@ -1331,4 +1509,5 @@ extern const TileTypeProcs _tile_type_water_procs = { + VehicleEnter_Water, // vehicle_enter_tile_proc + GetFoundation_Water, // get_foundation_proc + TerraformTile_Water, // terraform_tile_proc ++ CopyPasteTile_Water, // copypaste_tile_proc + }; +diff --git a/src/water_map.h b/src/water_map.h +index ab249a8..7e25182 100644 +--- a/src/water_map.h ++++ b/src/water_map.h +@@ -74,17 +74,22 @@ enum LockPart { + * @param t Water tile to query. + * @return Water tile type at the tile. + */ +-static inline WaterTileType GetWaterTileType(TileIndex t) ++template <bool Tgeneric> ++static inline WaterTileType GetWaterTileType(typename TileIndexT<Tgeneric>::T t) + { + assert(IsTileType(t, MP_WATER)); + +- switch (GB(_m[t].m5, WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { +- case WBL_TYPE_NORMAL: return HasBit(_m[t].m5, WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; ++ switch (GB(GetTile(t)->m5, WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { ++ case WBL_TYPE_NORMAL: return HasBit(GetTile(t)->m5, WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; + case WBL_TYPE_LOCK: return WATER_TILE_LOCK; + case WBL_TYPE_DEPOT: return WATER_TILE_DEPOT; + default: NOT_REACHED(); + } + } ++/** @copydoc GetWaterTileType(TileIndexT<Tgeneric>::T) */ ++static inline WaterTileType GetWaterTileType(TileIndex t) { return GetWaterTileType<false>(t); } ++/** @copydoc GetWaterTileType(TileIndexT<Tgeneric>::T) */ ++static inline WaterTileType GetWaterTileType(GenericTileIndex t) { return GetWaterTileType<true>(t); } + + /** + * Checks whether the tile has an waterclass associated. +@@ -92,10 +97,15 @@ static inline WaterTileType GetWaterTileType(TileIndex t) + * @param t Tile to query. + * @return True if the tiletype has a waterclass. + */ +-static inline bool HasTileWaterClass(TileIndex t) ++template <bool Tgeneric> ++static inline bool HasTileWaterClass(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT); + } ++/** @copydoc HasTileWaterClass(TileIndexT<Tgeneric>::T) */ ++static inline bool HasTileWaterClass(TileIndex t) { return HasTileWaterClass<false>(t); } ++/** @copydoc HasTileWaterClass(TileIndexT<Tgeneric>::T) */ ++static inline bool HasTileWaterClass(GenericTileIndex t) { return HasTileWaterClass<true>(t); } + + /** + * Get the water class at a tile. +@@ -103,11 +113,16 @@ static inline bool HasTileWaterClass(TileIndex t) + * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) + * @return Water class at the tile. + */ +-static inline WaterClass GetWaterClass(TileIndex t) ++template <bool Tgeneric> ++static inline WaterClass GetWaterClass(typename TileIndexT<Tgeneric>::T t) + { + assert(HasTileWaterClass(t)); +- return (WaterClass)GB(_m[t].m1, 5, 2); ++ return (WaterClass)GB(GetTile(t)->m1, 5, 2); + } ++/** @copydoc GetWaterClass(TileIndexT<Tgeneric>::T) */ ++static inline WaterClass GetWaterClass(TileIndex t) { return GetWaterClass<false>(t); } ++/** @copydoc GetWaterClass(TileIndexT<Tgeneric>::T) */ ++static inline WaterClass GetWaterClass(GenericTileIndex t) { return GetWaterClass<true>(t); } + + /** + * Set the water class at a tile. +@@ -115,11 +130,16 @@ static inline WaterClass GetWaterClass(TileIndex t) + * @param wc New water class. + * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) + */ +-static inline void SetWaterClass(TileIndex t, WaterClass wc) ++template <bool Tgeneric> ++static inline void SetWaterClass(typename TileIndexT<Tgeneric>::T t, WaterClass wc) + { + assert(HasTileWaterClass(t)); +- SB(_m[t].m1, 5, 2, wc); ++ SB(GetTile(t)->m1, 5, 2, wc); + } ++/** @copydoc SetWaterClass(TileIndexT<Tgeneric>::T,WaterClass) */ ++static inline void SetWaterClass(TileIndex t, WaterClass wc) { return SetWaterClass<false>(t, wc); } ++/** @copydoc SetWaterClass(TileIndexT<Tgeneric>::T,WaterClass) */ ++static inline void SetWaterClass(GenericTileIndex t, WaterClass wc) { return SetWaterClass<true>(t, wc); } + + /** + * Tests if the tile was built on water. +@@ -138,10 +158,15 @@ static inline bool IsTileOnWater(TileIndex t) + * @return \c true if any type of clear water like ocean, river, or canal. + * @pre IsTileType(t, MP_WATER) + */ +-static inline bool IsWater(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsWater(typename TileIndexT<Tgeneric>::T t) + { + return GetWaterTileType(t) == WATER_TILE_CLEAR; + } ++/** @copydoc IsWater(TileIndexT<Tgeneric>::T) */ ++static inline bool IsWater(TileIndex t) { return IsWater<false>(t); } ++/** @copydoc IsWater(TileIndexT<Tgeneric>::T) */ ++static inline bool IsWater(GenericTileIndex t) { return IsWater<true>(t); } + + /** + * Is it a sea water tile? +@@ -160,10 +185,15 @@ static inline bool IsSea(TileIndex t) + * @return \c true if it is a canal tile. + * @pre IsTileType(t, MP_WATER) + */ +-static inline bool IsCanal(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsCanal(typename TileIndexT<Tgeneric>::T t) + { + return IsWater(t) && GetWaterClass(t) == WATER_CLASS_CANAL; + } ++/** @copydoc IsCanal(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCanal(TileIndex t) { return IsCanal<false>(t); } ++/** @copydoc IsCanal(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCanal(GenericTileIndex t) { return IsCanal<true>(t); } + + /** + * Is it a river water tile? +@@ -181,10 +211,15 @@ static inline bool IsRiver(TileIndex t) + * @param t Tile to query. + * @return \c true if it is a plain water tile. + */ +-static inline bool IsWaterTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsWaterTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_WATER) && IsWater(t); + } ++/** @copydoc IsWaterTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsWaterTile(TileIndex t) { return IsWaterTile<false>(t); } ++/** @copydoc IsWaterTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsWaterTile(GenericTileIndex t) { return IsWaterTile<true>(t); } + + /** + * Is it a coast tile? +@@ -192,10 +227,15 @@ static inline bool IsWaterTile(TileIndex t) + * @return \c true if it is a sea water tile. + * @pre IsTileType(t, MP_WATER) + */ +-static inline bool IsCoast(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsCoast(typename TileIndexT<Tgeneric>::T t) + { + return GetWaterTileType(t) == WATER_TILE_COAST; + } ++/** @copydoc IsCoast(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCoast(TileIndex t) { return IsCoast<false>(t); } ++/** @copydoc IsCoast(TileIndexT<Tgeneric>::T) */ ++static inline bool IsCoast(GenericTileIndex t) { return IsCoast<true>(t); } + + /** + * Is it a coast tile +@@ -213,20 +253,30 @@ static inline bool IsCoastTile(TileIndex t) + * @return \c true if it is a ship depot tile. + * @pre IsTileType(t, MP_WATER) + */ +-static inline bool IsShipDepot(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsShipDepot(typename TileIndexT<Tgeneric>::T t) + { + return GetWaterTileType(t) == WATER_TILE_DEPOT; + } ++/** @copydoc IsShipDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsShipDepot(TileIndex t) { return IsShipDepot<false>(t); } ++/** @copydoc IsShipDepot(TileIndexT<Tgeneric>::T) */ ++static inline bool IsShipDepot(GenericTileIndex t) { return IsShipDepot<true>(t); } + + /** + * Is it a ship depot tile? + * @param t Tile to query. + * @return \c true if it is a ship depot tile. + */ +-static inline bool IsShipDepotTile(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsShipDepotTile(typename TileIndexT<Tgeneric>::T t) + { + return IsTileType(t, MP_WATER) && IsShipDepot(t); + } ++/** @copydoc IsShipDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsShipDepotTile(TileIndex t) { return IsShipDepotTile<false>(t); } ++/** @copydoc IsShipDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline bool IsShipDepotTile(GenericTileIndex t) { return IsShipDepotTile<true>(t); } + + /** + * Get the axis of the ship depot. +@@ -234,11 +284,16 @@ static inline bool IsShipDepotTile(TileIndex t) + * @return Axis of the depot. + * @pre IsShipDepotTile(t) + */ +-static inline Axis GetShipDepotAxis(TileIndex t) ++template <bool Tgeneric> ++static inline Axis GetShipDepotAxis(typename TileIndexT<Tgeneric>::T t) + { + assert(IsShipDepotTile(t)); +- return (Axis)GB(_m[t].m5, WBL_DEPOT_AXIS, 1); ++ return (Axis)GB(GetTile(t)->m5, WBL_DEPOT_AXIS, 1); + } ++/** @copydoc GetShipDepotAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetShipDepotAxis(TileIndex t) { return GetShipDepotAxis<false>(t); } ++/** @copydoc GetShipDepotAxis(TileIndexT<Tgeneric>::T) */ ++static inline Axis GetShipDepotAxis(GenericTileIndex t) { return GetShipDepotAxis<true>(t); } + + /** + * Get the part of a ship depot. +@@ -246,11 +301,16 @@ static inline Axis GetShipDepotAxis(TileIndex t) + * @return Part of the depot. + * @pre IsShipDepotTile(t) + */ +-static inline DepotPart GetShipDepotPart(TileIndex t) ++template <bool Tgeneric> ++static inline DepotPart GetShipDepotPart(typename TileIndexT<Tgeneric>::T t) + { + assert(IsShipDepotTile(t)); +- return (DepotPart)GB(_m[t].m5, WBL_DEPOT_PART, 1); ++ return (DepotPart)GB(GetTile(t)->m5, WBL_DEPOT_PART, 1); + } ++/** @copydoc GetShipDepotPart(TileIndexT<Tgeneric>::T) */ ++static inline DepotPart GetShipDepotPart(TileIndex t) { return GetShipDepotPart<false>(t); } ++/** @copydoc GetShipDepotPart(TileIndexT<Tgeneric>::T) */ ++static inline DepotPart GetShipDepotPart(GenericTileIndex t) { return GetShipDepotPart<true>(t); } + + /** + * Get the direction of the ship depot. +@@ -258,10 +318,15 @@ static inline DepotPart GetShipDepotPart(TileIndex t) + * @return Direction of the depot. + * @pre IsShipDepotTile(t) + */ +-static inline DiagDirection GetShipDepotDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetShipDepotDirection(typename TileIndexT<Tgeneric>::T t) + { + return XYNSToDiagDir(GetShipDepotAxis(t), GetShipDepotPart(t)); + } ++/** @copydoc GetShipDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetShipDepotDirection(TileIndex t) { return GetShipDepotDirection<false>(t); } ++/** @copydoc GetShipDepotDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetShipDepotDirection(GenericTileIndex t) { return GetShipDepotDirection<true>(t); } + + /** + * Get the other tile of the ship depot. +@@ -269,10 +334,15 @@ static inline DiagDirection GetShipDepotDirection(TileIndex t) + * @return Tile containing the other section of the depot. + * @pre IsShipDepotTile(t) + */ +-static inline TileIndex GetOtherShipDepotTile(TileIndex t) ++template <bool Tgeneric> ++static inline typename TileIndexT<Tgeneric>::T GetOtherShipDepotTile(typename TileIndexT<Tgeneric>::T t) + { +- return t + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1) : TileDiffXY(1, 0)); ++ return t + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1, MapOf(t)) : TileDiffXY(1, 0, MapOf(t))); + } ++/** @copydoc GetOtherShipDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline TileIndex GetOtherShipDepotTile(TileIndex t) { return GetOtherShipDepotTile<false>(t); } ++/** @copydoc GetOtherShipDepotTile(TileIndexT<Tgeneric>::T) */ ++static inline GenericTileIndex GetOtherShipDepotTile(GenericTileIndex t) { return GetOtherShipDepotTile<true>(t); } + + /** + * Get the most northern tile of a ship depot. +@@ -294,10 +364,15 @@ static inline TileIndex GetShipDepotNorthTile(TileIndex t) + * @return \c true if it is a water lock tile. + * @pre IsTileType(t, MP_WATER) + */ +-static inline bool IsLock(TileIndex t) ++template <bool Tgeneric> ++static inline bool IsLock(typename TileIndexT<Tgeneric>::T t) + { + return GetWaterTileType(t) == WATER_TILE_LOCK; + } ++/** @copydoc IsLock(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLock(TileIndex t) { return IsLock<false>(t); } ++/** @copydoc IsLock(TileIndexT<Tgeneric>::T) */ ++static inline bool IsLock(GenericTileIndex t) { return IsLock<true>(t); } + + /** + * Get the direction of the water lock. +@@ -305,11 +380,16 @@ static inline bool IsLock(TileIndex t) + * @return Direction of the lock. + * @pre IsTileType(t, MP_WATER) && IsLock(t) + */ +-static inline DiagDirection GetLockDirection(TileIndex t) ++template <bool Tgeneric> ++static inline DiagDirection GetLockDirection(typename TileIndexT<Tgeneric>::T t) + { + assert(IsLock(t)); +- return (DiagDirection)GB(_m[t].m5, WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); ++ return (DiagDirection)GB(GetTile(t)->m5, WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); + } ++/** @copydoc GetLockDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetLockDirection(TileIndex t) { return GetLockDirection<false>(t); } ++/** @copydoc GetLockDirection(TileIndexT<Tgeneric>::T) */ ++static inline DiagDirection GetLockDirection(GenericTileIndex t) { return GetLockDirection<true>(t); } + + /** + * Get the part of a lock. +@@ -317,11 +397,16 @@ static inline DiagDirection GetLockDirection(TileIndex t) + * @return The part. + * @pre IsTileType(t, MP_WATER) && IsLock(t) + */ +-static inline byte GetLockPart(TileIndex t) ++template <bool Tgeneric> ++static inline byte GetLockPart(typename TileIndexT<Tgeneric>::T t) + { + assert(IsLock(t)); +- return GB(_m[t].m5, WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); ++ return GB(GetTile(t)->m5, WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); + } ++/** @copydoc GetLockPart(TileIndexT<Tgeneric>::T) */ ++static inline byte GetLockPart(TileIndex t) { return GetLockPart<false>(t); } ++/** @copydoc GetLockPart(TileIndexT<Tgeneric>::T) */ ++static inline byte GetLockPart(GenericTileIndex t) { return GetLockPart<true>(t); } + + /** + * Get the random bits of the water tile. +@@ -332,7 +417,7 @@ static inline byte GetLockPart(TileIndex t) + static inline byte GetWaterTileRandomBits(TileIndex t) + { + assert(IsTileType(t, MP_WATER)); +- return _m[t].m4; ++ return GetTile(t)->m4; + } + + /** +@@ -356,12 +441,12 @@ static inline void MakeShore(TileIndex t) + SetTileType(t, MP_WATER); + SetTileOwner(t, OWNER_WATER); + SetWaterClass(t, WATER_CLASS_SEA); +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } + + /** +@@ -371,18 +456,23 @@ static inline void MakeShore(TileIndex t) + * @param wc The class of water the tile has to be + * @param random_bits Eventual random bits to be set for this tile + */ +-static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_bits) ++template <bool Tgeneric> ++static inline void MakeWater(typename TileIndexT<Tgeneric>::T t, Owner o, WaterClass wc, uint8 random_bits) + { + SetTileType(t, MP_WATER); + SetTileOwner(t, o); + SetWaterClass(t, wc); +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = random_bits; +- _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = random_bits; ++ GetTile(t)->m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeWater(TileIndexT<Tgeneric>::T,Owner,WaterClass,uint8) */ ++static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_bits) { MakeWater<false>(t, o, wc, random_bits); } ++/** @copydoc MakeWater(TileIndexT<Tgeneric>::T,Owner,WaterClass,uint8) */ ++static inline void MakeWater(GenericTileIndex t, Owner o, WaterClass wc, uint8 random_bits) { MakeWater<true>(t, o, wc, random_bits); } + + /** + * Make a sea tile. +@@ -409,11 +499,16 @@ static inline void MakeRiver(TileIndex t, uint8 random_bits) + * @param o The owner of the canal + * @param random_bits Random bits to be set for this tile + */ +-static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) ++template <bool Tgeneric> ++static inline void MakeCanal(typename TileIndexT<Tgeneric>::T t, Owner o, uint8 random_bits) + { + assert(o != OWNER_WATER); + MakeWater(t, o, WATER_CLASS_CANAL, random_bits); + } ++/** @copydoc MakeCanal(TileIndexT<Tgeneric>::T,Owner,uint) */ ++static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) { MakeCanal<false>(t, o, random_bits); } ++/** @copydoc MakeCanal(TileIndexT<Tgeneric>::T,Owner,uint) */ ++static inline void MakeCanal(GenericTileIndex t, Owner o, uint8 random_bits) { MakeCanal<true>(t, o, random_bits); } + + /** + * Make a ship depot section. +@@ -424,18 +519,23 @@ static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) + * @param a Axis of the depot. + * @param original_water_class Original water class. + */ +-static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) ++template <bool Tgeneric> ++static inline void MakeShipDepot(typename TileIndexT<Tgeneric>::T t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) + { + SetTileType(t, MP_WATER); + SetTileOwner(t, o); + SetWaterClass(t, original_water_class); +- _m[t].m2 = did; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = did; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeShipDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DepotPart,Axis,WaterClass) */ ++static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { MakeShipDepot<false>(t, o, did, part, a, original_water_class); } ++/** @copydoc MakeShipDepot(TileIndexT<Tgeneric>::T,Owner,DepotID,DepotPart,Axis,WaterClass) */ ++static inline void MakeShipDepot(GenericTileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { MakeShipDepot<true>(t, o, did, part, a, original_water_class); } + + /** + * Make a lock section. +@@ -446,18 +546,23 @@ static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart pa + * @param original_water_class Original water class. + * @see MakeLock + */ +-static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) ++template <bool Tgeneric> ++static inline void MakeLockTile(typename TileIndexT<Tgeneric>::T t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) + { + SetTileType(t, MP_WATER); + SetTileOwner(t, o); + SetWaterClass(t, original_water_class); +- _m[t].m2 = 0; +- _m[t].m3 = 0; +- _m[t].m4 = 0; +- _m[t].m5 = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; +- SB(_me[t].m6, 2, 4, 0); +- _me[t].m7 = 0; ++ GetTile(t)->m2 = 0; ++ GetTile(t)->m3 = 0; ++ GetTile(t)->m4 = 0; ++ GetTile(t)->m5 = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; ++ SB(GetTileEx(t)->m6, 2, 4, 0); ++ GetTileEx(t)->m7 = 0; + } ++/** @copydoc MakeLockTile(TileIndexT<Tgeneric>::T,Owner,LockPart,DiagDirection,WaterClass) */ ++static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { MakeLockTile<false>(t, o, part, dir, original_water_class); } ++/** @copydoc MakeLockTile(TileIndexT<Tgeneric>::T,Owner,LockPart,DiagDirection,WaterClass) */ ++static inline void MakeLockTile(GenericTileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { MakeLockTile<true>(t, o, part, dir, original_water_class); } + + /** + * Make a water lock. +@@ -468,9 +573,10 @@ static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirecti + * @param wc_upper Original water class of the upper part. + * @param wc_middle Original water class of the middle part. + */ +-static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) ++template <bool Tgeneric> ++static inline void MakeLock(typename TileIndexT<Tgeneric>::T t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) + { +- TileIndexDiff delta = TileOffsByDiagDir(d); ++ TileIndexDiff delta = TileOffsByDiagDir<Tgeneric>(d, MapOf(t)); + + /* Keep the current waterclass and owner for the tiles. + * It allows to restore them after the lock is deleted */ +@@ -478,5 +584,9 @@ static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc + MakeLockTile(t - delta, IsWaterTile(t - delta) ? GetTileOwner(t - delta) : o, LOCK_PART_LOWER, d, wc_lower); + MakeLockTile(t + delta, IsWaterTile(t + delta) ? GetTileOwner(t + delta) : o, LOCK_PART_UPPER, d, wc_upper); + } ++/** @copydoc MakeLock(TileIndexT<Tgeneric>::T,Owner,DiagDirection,WaterClass,WaterClass,WaterClass) */ ++static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { MakeLock<false>(t, o, d, wc_lower, wc_upper, wc_middle); } ++/** @copydoc MakeLock(TileIndexT<Tgeneric>::T,Owner,DiagDirection,WaterClass,WaterClass,WaterClass) */ ++static inline void MakeLock(GenericTileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { MakeLock<true>(t, o, d, wc_lower, wc_upper, wc_middle); } + + #endif /* WATER_MAP_H */ +diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp +index efa4800..bb16af7 100644 +--- a/src/waypoint_cmd.cpp ++++ b/src/waypoint_cmd.cpp +@@ -13,6 +13,8 @@ + + #include "cmd_helper.h" + #include "command_func.h" ++#include "copypaste_cmd.h" ++#include "clipboard_func.h" + #include "landscape.h" + #include "bridge_map.h" + #include "town.h" +@@ -34,6 +36,9 @@ + + #include "safeguards.h" + ++extern ClipboardStationsBuilder _clipboard_stations_builder; ++extern int _station_cmd_specindex_to_paste; ++ + /** + * Update the virtual coords needed to draw the waypoint sign. + */ +@@ -41,7 +46,7 @@ void Waypoint::UpdateVirtCoord() + { + Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); + SetDParam(0, this->index); +- this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT); ++ this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT, STR_VIEWPORT_WAYPOINT); + /* Recenter viewport */ + InvalidateWindowData(WC_WAYPOINT_VIEW, this->index); + } +@@ -101,8 +106,9 @@ extern CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags); + * @param tile the tile to check for suitability + * @param axis the axis of the waypoint + * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. ++ * @param flags flags for the command + */ +-static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint) ++static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags) + { + /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. + * so waypoint points to INVALID_STATION if we can build on any waypoint. +@@ -120,11 +126,21 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * + } + } + +- if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); ++ /* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing). ++ * Ignore that so we can calculate the cost more precisely. */ ++ bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC); ++ ++ if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) { ++ if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); ++ } + + Owner owner = GetTileOwner(tile); +- CommandCost ret = CheckOwnership(owner); +- if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile); ++ if (!ignore_lack_of_tracks || owner != OWNER_NONE) { ++ CommandCost ret = CheckOwnership(owner); ++ if (ret.Failed()) return ret; ++ } ++ ++ CommandCost ret = EnsureNoVehicleOnGround(tile); + if (ret.Failed()) return ret; + + Slope tileh = GetTileSlope(tile); +@@ -182,9 +198,8 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint + + bool reuse = (station_to_join != NEW_STATION); + if (!reuse) station_to_join = INVALID_STATION; +- bool distant_join = (station_to_join != INVALID_STATION); + +- if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR; ++ if (station_to_join != INVALID_STATION && !Waypoint::IsValidID(station_to_join)) return CMD_ERROR; + + /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ + StationID est = INVALID_STATION; +@@ -193,7 +208,7 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint + TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis))); + for (int i = 0; i < count; i++) { + TileIndex tile = start_tile + i * offset; +- CommandCost ret = IsValidTileForWaypoint(tile, axis, &est); ++ CommandCost ret = IsValidTileForWaypoint(tile, axis, &est, flags); + if (ret.Failed()) return ret; + } + +@@ -253,7 +268,14 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint + /* But for NewGRF waypoints we like to have their style. */ + GetStationLayout(layout_ptr, count, 1, spec); + } +- byte map_spec_index = AllocateSpecToStation(spec, wp, true); ++ ++ byte map_spec_index; ++ if ((flags & DC_PASTE) && (_station_cmd_specindex_to_paste != -1)) { ++ assert(_station_cmd_specindex_to_paste == 0 || (_station_cmd_specindex_to_paste < wp->num_specs && wp->speclist[_station_cmd_specindex_to_paste].spec == spec)); ++ map_spec_index = _station_cmd_specindex_to_paste; ++ } else { ++ map_spec_index = AllocateSpecToStation(spec, wp, true); ++ } + + Company *c = Company::Get(wp->owner); + for (int i = 0; i < count; i++) { +@@ -288,7 +310,18 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint + */ + CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) + { +- if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ if (tile == 0) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ ++ WaterClass wc; ++ if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { ++ /* When pasting a buoy, there may be no water yet (a canal will be placed when DC_EXE'ing). ++ * Ignore that there is no water so we can calculate the cost more precisely. */ ++ wc = WATER_CLASS_INVALID; ++ } else { ++ if (!HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); ++ wc = GetWaterClass(tile); ++ } ++ + if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + + if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); +@@ -323,7 +356,8 @@ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 + + if (wp->town == NULL) MakeDefaultName(wp); + +- MakeBuoy(tile, wp->index, GetWaterClass(tile)); ++ assert(wc != WATER_CLASS_INVALID); ++ MakeBuoy(tile, wp->index, wc); + + wp->UpdateVirtCoord(); + InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); +@@ -422,3 +456,54 @@ CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, ui + } + return CommandCost(); + } ++ ++void CopyPastePlaceRailWaypoint(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ /* check if required track is already there, try to build one if not */ ++ if (!IsTileOwner(t, _current_company) || ++ (!IsRailWaypointTile(tile) && !IsPlainRailTile(tile)) || ++ GetRailType(t) != rt || ++ (IsTileType(t, MP_STATION) ? GetRailStationAxis(tile) != axis : !HasBit(GetTrackBits(t), AxisToTrack(axis)))) { ++ CopyPastePlaceTracks(tile, rt, AxisToTrackBits(axis)); ++ if (_current_pasting->last_result.Failed()) return; ++ } ++ /* build the waypoint */ ++ _station_cmd_specindex_to_paste = specindex; ++ uint32 p1 = 0; ++ SB(p1, 0, 4, rt); ++ SB(p1, 4, 1, axis); ++ SB(p1, 8, 8, 1); // width ++ SB(p1, 16, 8, 1); // height ++ SB(p1, 24, 1, adjacent); ++ uint32 p2 = 0; ++ SB(p2, 0, 8, stat_class); ++ SB(p2, 8, 8, stat_type); ++ SB(p2, 16, 16, sid); ++ _current_pasting->DoCommand(t, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT)); ++ } else { ++ MakeRailWaypoint(tile, OWNER_NONE, sid, axis, gfx & ~1, rt); ++ assert(IsInsideMM(specindex, 0, MAX_UVALUE(byte) + 1)); ++ SetCustomStationSpecIndex(tile, (byte)specindex); ++ _clipboard_stations_builder.AddRailPart(sid, stat_class, stat_type, (byte)specindex); ++ } ++} ++ ++void CopyPastePlaceBuoy(GenericTileIndex tile, StationID sid, WaterClass wc) ++{ ++ if (IsMainMapTile(tile)) { ++ TileIndex t = AsMainMapTile(tile); ++ /* build a piece of canal if not on water */ ++ if (!HasTileWaterGround(t)) { ++ CopyPastePlaceCannal(tile); ++ if (_current_pasting->last_result.Failed()) return; ++ } ++ /* build the buoy */ ++ _current_pasting->DoCommand(t, 0, 0, CMD_BUILD_BUOY | CMD_MSG(STR_ERROR_CAN_T_POSITION_BUOY_HERE)); ++ } else { ++ SetTileOwner(tile, OWNER_NONE); ++ MakeBuoy(tile, sid, wc); ++ _clipboard_stations_builder.AddPart(sid); ++ } ++} +diff --git a/src/widgets/clipboard_widget.h b/src/widgets/clipboard_widget.h +new file mode 100644 +index 0000000..a4c59d1 +--- /dev/null ++++ b/src/widgets/clipboard_widget.h +@@ -0,0 +1,49 @@ ++/* ++ * This file is part of OpenTTD. ++ * OpenTTD 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, version 2. ++ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. ++ */ ++ ++/** @file clipboard_widget.h Types related to the clipboard widgets. */ ++ ++#ifndef WIDGETS_CLIPBOARD_WIDGET_H ++#define WIDGETS_CLIPBOARD_WIDGET_H ++ ++/** Widgets of the #ClipboardToolbarWindow class. */ ++enum ClipboardToolbarWidgets { ++ WID_CT_CLIPBOARD_1, ///< Button to switch to clipboard #1 ++ WID_CT_CLIPBOARD_2, ///< Button to switch to clipboard #2 ++ WID_CT_CLIPBOARD_3, ///< Button to switch to clipboard #3 ++ WID_CT_CLIPBOARD_4, ///< Button to switch to clipboard #4 ++ ++ WID_CT_COPY, ///< Copy button (single player) ++ WID_CT_PASTE, ///< Paste button (single player) ++ ++ WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< First button to toggle copy-paste flag ++ WID_CT_WITH_RAIL = WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< Toggle rails button ++ WID_CT_WITH_ROAD, ///< Toggle roads button ++ WID_CT_WITH_WATER, ///< Toggle water button ++ WID_CT_WITH_AIR, ///< Toggle air button ++ WID_CT_MIRROR_SIGNALS, ///< Toggle signal mirrorig button ++ WID_CT_UPGRADE_BRIDGES, ///< Toggle bridge upgrading button ++ WID_CT_WITH_STATIONS, ///< Toggle stations button ++ WID_CT_PASTE_FLAG_BUTTON_END, ///< Past-the-last button to toggle copy-paste flag ++ ++ WID_CT_CONVERT_RAILTYPE = WID_CT_PASTE_FLAG_BUTTON_END, ///< Button to select railtype to convert to ++ ++ WID_CT_TERRAFORM, ///< Button to select terraforming mode ++ ++ WID_CT_TRANSFORMATION, ///< Button to show/reset clipboard transformation ++ WID_CT_ROTATE_LEFT, ///< Rotate left button ++ WID_CT_ROTATE_RIGHT, ///< Rotate right button ++ WID_CT_REFLECT_NE_SW, ///< Reflect against NE-SW axis button ++ WID_CT_REFLECT_NW_SE, ///< Reflect against NW-SE axis button ++ ++ WID_CT_HEIGHT_DIFF_GLYPH, ///< Image in front of buttons to increase/decrease height level ++ WID_CT_HEIGHT_DIFF, ///< Panel with buttons to increase/decrease height level ++ WID_CT_HEIGHT_DIFF_INCREASE, ///< Button to increase height level ++ WID_CT_HEIGHT_DIFF_DECREASE, ///< Button to decrease height level ++}; ++ ++#endif /* WIDGETS_CLIPBOARD_WIDGET_H */ +diff --git a/src/widgets/group_widget.h b/src/widgets/group_widget.h +index bd0d904..d0c2aed 100644 +--- a/src/widgets/group_widget.h ++++ b/src/widgets/group_widget.h +@@ -32,6 +32,7 @@ enum GroupListWidgets { + WID_GL_DELETE_GROUP, ///< Delete group button. + WID_GL_RENAME_GROUP, ///< Rename group button. + WID_GL_REPLACE_PROTECTION, ///< Replace protection button. ++ WID_GL_GROUP_INFO, ///< Label for info about group income. + }; + + #endif /* WIDGETS_GROUP_WIDGET_H */ +diff --git a/src/widgets/station_widget.h b/src/widgets/station_widget.h +index 82fe392..2fd2fd4 100644 +--- a/src/widgets/station_widget.h ++++ b/src/widgets/station_widget.h +@@ -23,6 +23,7 @@ enum StationViewWidgets { + WID_SV_SCROLLBAR, ///< Scrollbar. + WID_SV_ACCEPT_RATING_LIST, ///< List of accepted cargoes / rating of cargoes. + WID_SV_LOCATION, ///< 'Location' button. ++ WID_SV_COVERAGE, ///< Show area coverage button + WID_SV_ACCEPTS_RATINGS, ///< 'Accepts' / 'Ratings' button. + WID_SV_RENAME, ///< 'Rename' button. + WID_SV_CLOSE_AIRPORT, ///< 'Close airport' button. +diff --git a/src/widgets/terraform_widget.h b/src/widgets/terraform_widget.h +index 7f8a4c4..4327d1f 100644 +--- a/src/widgets/terraform_widget.h ++++ b/src/widgets/terraform_widget.h +@@ -19,6 +19,7 @@ enum TerraformToolbarWidgets { + WID_TT_LOWER_LAND = WID_TT_BUTTONS_START, ///< Lower land button. + WID_TT_RAISE_LAND, ///< Raise land button. + WID_TT_LEVEL_LAND, ///< Level land button. ++ WID_TT_CLIPBOARD, ///< Button to open the clipboard toolbar + WID_TT_DEMOLISH, ///< Demolish aka dynamite button. + WID_TT_BUY_LAND, ///< Buy land button. + WID_TT_PLANT_TREES, ///< Plant trees button (note: opens separate window, no place-push-button). +diff --git a/src/widgets/transparency_widget.h b/src/widgets/transparency_widget.h +index 87618fc..dd3880a 100644 +--- a/src/widgets/transparency_widget.h ++++ b/src/widgets/transparency_widget.h +@@ -25,6 +25,7 @@ enum TransparencyToolbarWidgets { + WID_TT_STRUCTURES, ///< Object structure transparency toggle button. + WID_TT_CATENARY, ///< Catenary transparency toggle button. + WID_TT_LOADING, ///< Loading indicators transparency toggle button. ++ WID_TT_TUNNELS, ///< Vehicles in tunnels toggle button. + WID_TT_END, ///< End of toggle buttons. + + /* Panel with buttons for invisibility */ +diff --git a/src/widgets/viewport_widget.h b/src/widgets/viewport_widget.h +index 187659f..57c964e 100644 +--- a/src/widgets/viewport_widget.h ++++ b/src/widgets/viewport_widget.h +@@ -14,12 +14,13 @@ + + /** Widgets of the #ExtraViewportWindow class. */ + enum ExtraViewportWidgets { +- WID_EV_CAPTION, ///< Caption of window. +- WID_EV_VIEWPORT, ///< The viewport. +- WID_EV_ZOOM_IN, ///< Zoom in. +- WID_EV_ZOOM_OUT, ///< Zoom out. +- WID_EV_MAIN_TO_VIEW, ///< Center the view of this viewport on the main view. +- WID_EV_VIEW_TO_MAIN, ///< Center the main view on the view of this viewport. ++ WID_EV_CAPTION, ///< Caption of window. ++ WID_EV_VIEWPORT, ///< The viewport. ++ WID_EV_ZOOM_IN, ///< Zoom in. ++ WID_EV_ZOOM_OUT, ///< Zoom out. ++ WID_EV_MAIN_TO_VIEW, ///< Center the view of this viewport on the main view. ++ WID_EV_VIEW_TO_MAIN, ///< Center the main view on the view of this viewport. ++ WID_EV_FOLLOW_CURSOR, ///< Follow the cursor with this viewport. + }; + + #endif /* WIDGETS_VIEWPORT_WIDGET_H */ +diff --git a/src/window.cpp b/src/window.cpp +index 1fce1f3..8136702 100644 +--- a/src/window.cpp ++++ b/src/window.cpp +@@ -2729,6 +2729,7 @@ enum MouseClick { + MAX_OFFSET_HOVER = 5, ///< Maximum mouse movement before stopping a hover event. + }; + extern EventState VpHandlePlaceSizingDrag(); ++extern EventState VpHandleMouseWheel(int mousewheel); + + static void ScrollMainViewport(int x, int y) + { +@@ -2790,11 +2791,12 @@ static void MouseLoop(MouseClick click, int mousewheel) + HandlePlacePresize(); + UpdateTileSelection(); + +- if (VpHandlePlaceSizingDrag() == ES_HANDLED) return; +- if (HandleMouseDragDrop() == ES_HANDLED) return; +- if (HandleWindowDragging() == ES_HANDLED) return; +- if (HandleScrollbarScrolling() == ES_HANDLED) return; +- if (HandleViewportScroll() == ES_HANDLED) return; ++ if (VpHandlePlaceSizingDrag() == ES_HANDLED) return; ++ if (VpHandleMouseWheel(mousewheel) == ES_HANDLED) return; ++ if (HandleMouseDragDrop() == ES_HANDLED) return; ++ if (HandleWindowDragging() == ES_HANDLED) return; ++ if (HandleScrollbarScrolling() == ES_HANDLED) return; ++ if (HandleViewportScroll() == ES_HANDLED) return; + + HandleMouseOver(); + +diff --git a/src/window_gui.h b/src/window_gui.h +index b81b06e..a023002 100644 +--- a/src/window_gui.h ++++ b/src/window_gui.h +@@ -769,6 +769,15 @@ public: + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) {} + + /** ++ * The user scrolling the mouse wheel while the tile highlight mode ++ * has been set. ++ * @param pt the exact point on the map where the mouse is. ++ * @param mousewheel the amount of scrolls. ++ * @return #ES_HANDLED to prevent viewport from zooming. ++ */ ++ virtual EventState OnPlaceMouseWheel(Point pt, int mousewheel) { return ES_NOT_HANDLED; } ++ ++ /** + * The user moves over the map when a tile highlight mode has been set + * when the special mouse mode has been set to 'PRESIZE' mode. An + * example of this is the tile highlight for dock building. |