summaryrefslogtreecommitdiff
path: root/openttd-svn
diff options
context:
space:
mode:
Diffstat (limited to 'openttd-svn')
-rwxr-xr-xopenttd-svn/PKGBUILD90
-rw-r--r--openttd-svn/clipboard.grfbin0 -> 5405 bytes
-rw-r--r--openttd-svn/everything.patch19875
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
new file mode 100644
index 00000000..60ebb06a
--- /dev/null
+++ b/openttd-svn/clipboard.grf
Binary files differ
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 &copy_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 &copy_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 &params)
++{
++ 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 &copy_paste);
++void AfterPastingStations(const CopyPasteParams &copy_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 &copy_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 &copy_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 &copy_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 &copy_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 &copy_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 &copy_paste)
++{
++ ProcessStationPartPasteQueue(copy_paste);
++
++ _copy_paste_station_id_paste_map = StationIDPasteMap(); // clear
++}
++
++void AfterCopyingStations(const CopyPasteParams &copy_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 &copy_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 &copy_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 &copy_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.