From c02ef3e4564b7b54d49f0827d2d7625cbc38f335 Mon Sep 17 00:00:00 2001 From: peter1138 Date: Sat, 6 Apr 2019 07:46:15 +0100 Subject: [PATCH] Feature: Add NotRoadTypes (NRT) --- bin/baseset/openttd.grf | Bin 502521 -> 510237 bytes docs/landscape.html | 51 +- docs/landscape_grid.html | 30 +- media/extra_grf/openttdgui.nfo | 7 +- media/extra_grf/openttdgui_build_tram.png | Bin 0 -> 1017 bytes media/extra_grf/openttdgui_convert_road.png | Bin 0 -> 1274 bytes media/extra_grf/openttdgui_convert_tram.png | Bin 0 -> 1289 bytes media/extra_grf/tramtracks.nfo | 8 +- media/extra_grf/tramtracks_bare_depot.png | Bin 0 -> 3661 bytes projects/openttd_vs140.vcxproj | 4 + projects/openttd_vs140.vcxproj.filters | 12 + projects/openttd_vs141.vcxproj | 4 + projects/openttd_vs141.vcxproj.filters | 12 + projects/openttd_vs142.vcxproj | 4 + projects/openttd_vs142.vcxproj.filters | 12 + source.list | 4 + src/autoreplace_cmd.cpp | 4 + src/autoreplace_gui.cpp | 171 ++- src/bridge_gui.cpp | 21 +- src/bridge_map.h | 15 +- src/build_vehicle_gui.cpp | 65 +- src/command.cpp | 3 + src/command_type.h | 1 + src/company_base.h | 3 + src/company_cmd.cpp | 28 +- src/company_gui.cpp | 86 +- src/economy.cpp | 4 +- src/engine.cpp | 14 +- src/engine_gui.cpp | 4 +- src/engine_type.h | 2 + src/ground_vehicle.cpp | 2 +- src/ground_vehicle.hpp | 2 +- src/lang/english.txt | 43 +- src/misc_gui.cpp | 24 + src/newgrf.cpp | 372 +++++- src/newgrf.h | 16 + src/newgrf_engine.cpp | 21 +- src/newgrf_roadtype.cpp | 143 +++ src/newgrf_roadtype.h | 51 + src/openttd.cpp | 2 + src/pathfinder/follow_track.hpp | 26 +- src/pathfinder/npf/npf.cpp | 50 +- src/pathfinder/yapf/yapf_costrail.hpp | 2 +- src/pathfinder/yapf/yapf_road.cpp | 8 +- src/rail_cmd.cpp | 44 +- src/rail_gui.cpp | 6 + src/road.cpp | 226 +++- src/road.h | 316 +++++ src/road_cmd.cpp | 1143 ++++++++++++++----- src/road_func.h | 88 +- src/road_gui.cpp | 486 +++++--- src/road_gui.h | 6 +- src/road_internal.h | 4 +- src/road_map.cpp | 6 +- src/road_map.h | 257 +++-- src/road_type.h | 29 +- src/roadstop.cpp | 2 +- src/roadveh.h | 13 +- src/roadveh_cmd.cpp | 75 +- src/saveload/afterload.cpp | 91 +- src/saveload/company_sl.cpp | 23 +- src/saveload/saveload.h | 1 + src/saveload/vehicle_sl.cpp | 16 +- src/script/api/ai/ai_rail.hpp.sq | 2 +- src/script/api/ai/ai_road.hpp.sq | 13 + src/script/api/ai_changelog.hpp | 8 + src/script/api/game/game_rail.hpp.sq | 2 +- src/script/api/game/game_road.hpp.sq | 13 + src/script/api/game/game_window.hpp.sq | 8 +- src/script/api/game_changelog.hpp | 8 + src/script/api/script_bridge.cpp | 6 +- src/script/api/script_engine.cpp | 2 +- src/script/api/script_infrastructure.cpp | 6 +- src/script/api/script_rail.hpp | 2 +- src/script/api/script_road.cpp | 84 +- src/script/api/script_road.hpp | 72 +- src/script/api/script_station.cpp | 6 +- src/script/api/script_tile.cpp | 15 +- src/script/api/script_tunnel.cpp | 2 +- src/script/api/script_vehicle.cpp | 2 +- src/script/api/script_window.hpp | 11 +- src/smallmap_gui.cpp | 61 +- src/station.cpp | 2 +- src/station_cmd.cpp | 276 +++-- src/station_func.h | 1 + src/station_map.h | 23 +- src/table/engines.h | 2 +- src/table/newgrf_debug_data.h | 47 +- src/table/railtypes.h | 2 +- src/table/road_land.h | 29 - src/table/roadtypes.h | 183 +++ src/table/sprites.h | 13 +- src/tile_cmd.h | 5 +- src/toolbar_gui.cpp | 124 +- src/town.h | 2 + src/town_cmd.cpp | 92 +- src/tunnel_map.h | 8 +- src/tunnelbridge.h | 13 + src/tunnelbridge_cmd.cpp | 374 +++--- src/vehicle.cpp | 9 +- src/vehicle_func.h | 2 +- src/viewport_type.h | 1 + src/widgets/autoreplace_widget.h | 4 +- src/widgets/company_widget.h | 2 + src/widgets/road_widget.h | 2 + src/widgets/toolbar_widget.h | 2 + 106 files changed, 4462 insertions(+), 1242 deletions(-) create mode 100644 media/extra_grf/openttdgui_build_tram.png create mode 100644 media/extra_grf/openttdgui_convert_road.png create mode 100644 media/extra_grf/openttdgui_convert_tram.png create mode 100644 media/extra_grf/tramtracks_bare_depot.png create mode 100644 src/newgrf_roadtype.cpp create mode 100644 src/newgrf_roadtype.h create mode 100644 src/road.h create mode 100644 src/table/roadtypes.h diff --git a/bin/baseset/openttd.grf b/bin/baseset/openttd.grf index ea5f55ce1dcb859d86f74620b4869a07158a1f9a..111004c175ba03f894e0b8f3a2bf67b29df4d524 100644 GIT binary patch delta 1442 zcmaKqZHQD=7{||ZUT5Yy>&woVX1Oz_v6vtQQRdlocFCA#mfWH;u$NU=qk>E!+?a4@ zv1Mo5B1>W>>E%+CTOy6N+bByk_N6g{PDF6+gPcjoXA45YFMYCeXLE^&4jk^`IrlmD z|GU5Q=Z`Djd831Kay1y?qZ~##9p!AA^Xpf`{#wUs-u(f5Q*Qg6FI&8YJWQS-Pm}%R zCGr}nkoU?XKD}|EPKu;xc@R+tyQtQq8&U8=VQd2~kXq;R<9A>(lGWL4u5aS@z{3Sp z=WU#2vjtquvxR&gu7Pl=v-v)=0i$>?u7Er~M6(5dx4+LEsBL_fzb@ch7-H#gp7fKg zWPm(FhR7&6MBXAk`H&nXACqI`IQf*EAfJ(GQX{9y4Ech5NzTlr!$@L;(1S#4I!X$K ztfpfmpUc^(94CFbd@e|c3DTF(V?0)^O7#fCnpp`JG1BJ2@3BW;Cj*wh7d@qOO72PCLY6 zOw@;hL`gHoqGkw{nYKPA>=Z=vr+k1QZKrU3wNcg8Ay8(@9Wm&(FEhnNrPE#pHbMLOs@4Qch2c zO8MQA*gh=UE;I>?O6nFpt diff --git a/docs/landscape.html b/docs/landscape.html index d4d8f7efeb..1a6cd83868 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -98,6 +98,32 @@ +
  • m4:
    + + Road roadtype. Used for all tiles with road (road, station, tunnelbridge). +
      +
    • + Bits 5..0: Road roadtype, 0x3F for no road. +
    • +
    +
  • +
  • m8:
    + + Tram roadtype. Used for all tiles with road (road, station, tunnelbridge). +
      +
    • + Bits 11..6: Tram roadtype, 0x3F for no tram. +
    • +
    +
  • +
  • m8:
    +
      +
    • + + Bits 5..0: Railtype. Used for all tiles with rail (road, rail, station, tunnelbridge). +
    • +
    +
  • m7:
    Animation frame/state. Used for houses, industries, objects and stations.
  • @@ -108,7 +134,7 @@ - + @@ -535,21 +561,10 @@
    ClassMeaning & details of encodingMeaning & details of encoding
    0
    • m2: Index into the array of towns (owning town for town roads; closest town otherwise, INVALID_TOWN if there is no town or we are creating a town)
    • -
    • m7 bit 5 set = on snow or desert
    • -
    • m7 bits 7..6: present road types - - - - - - - - - - -
      bit 0  normal road
      bit 1  tram
      -
    • m3 bits 7..4: owner of road type 1 (tram); OWNER_NONE (10) is stored as OWNER_TOWN (0F) +
    • m4 bits 5..0: Roadtype
    • +
    • m7 bit 5 set = on snow or desert
    • +
    • m8 bits 11..6: Tramtype
    • m5 bits 7 clear: road or level-crossing
      • m6 bits 5..3: @@ -862,6 +877,7 @@
      • m3 bits 7..4: persistent random data for railway stations/waypoints and airports)
      • m3 bits 7..4: owner of tram tracks (road stop)
      • m4: custom station id; 0 means standard graphics
      • +
      • m4: Roadtype for road stops
      • m5: graphics index (range from 0..255 for each station type): @@ -977,8 +993,8 @@
      • m6 bit 2: pbs reservation state for railway stations/waypoints
      • m7 bits 4..0: owner of road (road stops)
      • -
      • m7 bits 7..6: present road types (road stops)
      • m7: animation frame (railway stations/waypoints, airports)
      • +
      • m8 bits 11..6: Tramtype
      • m8 bits 5..0: track type for railway stations/waypoints
      • @@ -1445,6 +1461,7 @@
        • m1 bits 4..0: owner
        • m3 bits 7..4: owner of tram
        • +
        • m4: Roadtype
        • m5 bit 4: pbs reservation state for railway
        • m5 bits 7 clear: tunnel entrance/exit
        • m5 bit 7 set: bridge ramp @@ -1581,7 +1598,7 @@
        • m7 bits 4..0: owner of road
        • m7 bit 5 set = on snow or desert
        • -
        • m7 bits 7..6: present road types for road
        • +
        • m8 bits 11..6: Tramtype
        • m8 bits 5..0: track type for railway
        diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 4948366e65..5d65214cba 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -143,11 +143,11 @@ the array so you can quickly see what is used and what is not. - + - - + + @@ -159,8 +159,8 @@ the array so you can quickly see what is used and what is not. - - + + @@ -169,11 +169,11 @@ the array so you can quickly see what is used and what is not. - + - - + + @@ -237,11 +237,11 @@ the array so you can quickly see what is used and what is not. - + - - + + @@ -357,11 +357,11 @@ the array so you can quickly see what is used and what is not. - + - - + + @@ -370,7 +370,7 @@ the array so you can quickly see what is used and what is not. - + diff --git a/media/extra_grf/openttdgui.nfo b/media/extra_grf/openttdgui.nfo index d0fbba0e70..b01a70f795 100644 --- a/media/extra_grf/openttdgui.nfo +++ b/media/extra_grf/openttdgui.nfo @@ -7,7 +7,7 @@ // 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 . // -1 * 0 0C "OpenTTD GUI graphics" - -1 * 3 05 15 \b 179 // OPENTTD_SPRITE_COUNT + -1 * 3 05 15 \b 184 // OPENTTD_SPRITE_COUNT -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal @@ -187,3 +187,8 @@ -1 sprites/openttdgui_group_livery.png 8bpp 21 0 20 20 0 0 normal -1 sprites/openttdgui_group_livery.png 8bpp 42 0 20 20 0 0 normal -1 sprites/openttdgui_group_livery.png 8bpp 63 0 20 20 0 0 normal + -1 sprites/openttdgui_build_tram.png 8bpp 0 0 20 20 0 0 normal + -1 sprites/openttdgui_convert_road.png 8bpp 0 0 20 20 0 0 normal + -1 sprites/openttdgui_convert_road.png 8bpp 24 0 32 32 0 0 normal + -1 sprites/openttdgui_convert_tram.png 8bpp 0 0 20 20 0 0 normal + -1 sprites/openttdgui_convert_tram.png 8bpp 24 0 32 32 0 0 normal diff --git a/media/extra_grf/openttdgui_build_tram.png b/media/extra_grf/openttdgui_build_tram.png new file mode 100644 index 0000000000000000000000000000000000000000..d9af8effe2d2ccb820df1d8a24251e13aae68f9e GIT binary patch literal 1017 zcmWksQHa|F82*ooTz9*}pnV9gO3?Ez5Alc+r4ME1G{4MOIe<#w1D7G|h3GAP9=07>1F{#Ef@^F6P)TdrWefjfYM45BMD zpl1>}E#>FZWnZb4t>(5bQlPVhD<;Z189P*`Ob=obJAfk>u|X3cBok_iwAhrJW&$Z4 zW#!n?ny%Fe+yoD#0JK7IIw9C|s+Tl_lpC=ro?=Bhqj8FvG4rw;WJ@(`yPYR_ik8^4 zF3CAf_bsR5ml{#rYta;>Qy?*f#?P4ol~>t7fcL& ztValx1q&YVAxJUk9m?sYivum%bDLQ2BPr;VVlleI+n!PgbJa$<-D~e*mFGJ=j}sUg z7V{W~*lpo(S4!GhwqT1&pIfyq|VPF*T>jcPwz=B=8LZ^^}ewm-zy?r6KW z>(#qjsXBz=ZMc00?(D<<2u63|?mf77AMPXI19)%%2V)q^RKjAZqQuuM_1Z`8O(voW zXaeQ|1HdX^6NJe(18^J&0w@ZM$3Tnrf|O%qhgI^N>Pkk@@S^Szb+;nwh4FZN<@EZe z05ElS{g?>;BTq^bbI6une+S_3;}1CnAD5{~IFnmDu{!g|)Kf73;tQATgGqE@Ou+veo&AAJy!z$UpB6XObGMGnoVu|*+}>*ba(&~Cw@R0vd-wOxe|zlPQ^v(3 y+e?q0`^sEA`qzrHc=XCjY3Y1&d4KcLzoY4`!-V=QdFG4B6j)o`5Wjv!fB%0^Gq*Yb literal 0 HcmV?d00001 diff --git a/media/extra_grf/openttdgui_convert_road.png b/media/extra_grf/openttdgui_convert_road.png new file mode 100644 index 0000000000000000000000000000000000000000..9218e770544b2533ff736e0c10336307b91a0dbb GIT binary patch literal 1274 zcmWks0c;ax6n;P_Wo&q41y9nAQ>PX!(!i`aNLbfhs;4rpYMPHLz)MiUDZEDoU=(-0ZZZU3k>`M&q^y}U2^lJ`Dk zo0rvAB0+9ulhdZpVdB#cuk)q8pQMSv;5GOncnWemdzg0R_a6h*NtD+q$m=aXew zRaH&XbX`xU)45!(SS%uhNC)j?+#K%}D=WcpBpOfXsZ4*~W;IZb8kRBfZnNJ{sj84j z$eD~rTMeAUdeomysw4RX1qRN5c`-sE$f%u3y2Okpke5Tlkr+z$qJBU{ z2SO5K0p4Nsac0G5k0@*+BBpv2Cl0*TAUTZ@9@G7JHbNA23}pe8B!qei15hTT%Z!Ou zvt+lZv|V#Ey2zy@F{?^O5-?yn(yV3@w>k+2XJb7MPk@nBcQ_&T zWC9q86SURN(~OVf711A4!o8ZFPZK!U%|KfW99iR`@PNx26sf4hF&2y`ai7zoxa={W z(!Fd(;mXt!t0>UUBXGII|C%rNojQZle zkxV{w3&lw?OOmJpfkUAH(xWJsMkB+t$?LWTy-qDCq%@_FPL%rfaPH|#JQSAkBJPJok5E`X7?xQa88I9 z+!0btP=PcPD!2{jE%!M0yF^=_u+mS z$_#E4t$2tg6QXONV@o9>asl!H3II|8#Q~*&s(dqm%?1<&EDPl_5KeC+?Xxg`D;u!6 zBw7dwvX&b`ycBct!E(8L=FR0@05ECw@?}m?s_N;AnC7KdZUC5G_dh4W-Uwb9PW3gt zx^(K`qz0HlwsqDWs(7R(=VF)rOlP*GV>^LWvGYDujrt>5?aPS;^Ujl3XSJTn&+Y!~ zo5a<Yn}=4~73flW83RQC`Mo|!yyUihou9E-Q^?vKWSLRP$^@TwXzjfrr=(!)-Hd5VdFAqtHL-*S5 zWHhF`r7W#EGR7Zl*}v$@vB$@w>zaplt=&#OeTt=SeE5^-tBh# zd_F}{!r`#0s>x(Bo6Qyq1%wc3VQ8Dv!HGg?#orwaMPh0qmCl)r8p=|^+H{;#@9|KI z!pCC1R4U9EH4cl;#TvaXrrYOKL!L}r8PCNi&^R=hh!HYDhRn9OT}ZjSIbUET*n{GI zC=IA!K}fP0fU{`b4!!I)2W2)E6cQI@8VAm(k!Wp@!&DER2@(YrLm5Dm5<-211}L4@ zuEzwUUNRdL#vFFqRKbyugp49-Nx&!|SdEF(P-2DMr*kQKDQwh}xRE9-4ioFLxV$z= zadyXqizzQg;sj$fbBxXH;AFwmBX{?O)m)Om!K??ypmC5DE(-VBjeda&Ne-I<<4D|1 z8)Un=hoe-HO-Wov5%XbbB(AV}4R6qS%m%-W3h|sO%e4mDMA|8eXIVGT%RXOEB+{43 zDBftfy^#K_IMg=Z|dvfML!4V#l5-6TW7{%eNVBkEY z=wtmMccd?v%B99ogd{U0iAvxdDBwkE2xXIKe1y@7PNQF>!+t&ymh;KjWLhm2(#Qhf z1dsqi011FR04jk6Bmj~D^#JMvlmmnyC2Q{mG$;rtW;}7@vTNr||R{Je!2cDVTZ=&!=G; z0h)oCS(q(C(S~aUBOYMLm|*|o+x?}8$PUN_$O}jT6aka~s`SkOCKFH;uq+gdK+s|> z<2KklM%HVxOAH_2ec|jl;-ns$^B0T7!OhL50ibMK^F|uvc`q>~u|QOjo<#xc#2J753wyyX6Ysl=F?8n?6I%JRWX^nCLiwzBC&&DLd>?T!1Y?%as} z+q%QL`|Y~Lhx+d?y?&yram%CD&W<29we-^5y6``pO_wX}@pFa;ZKv+GiVZ_shJRaG zp0C<;y*^dh6l_?xq}sl(Zmo-6ClaxrI^VE-u=nzzSC#s!Ynlr;@86EXmyK3&Z*. // -1 * 0 0C "Tram track graphics by PikkaBird" - -1 * 3 05 0B 71 + -1 * 3 05 0B 77 -1 sprites/tramtracks.png 8bpp 18 8 20 13 0 4 normal -1 sprites/tramtracks.png 8bpp 50 8 20 13 0 4 normal -1 sprites/tramtracks.png 8bpp 82 8 64 36 -18 -8 normal @@ -121,3 +121,9 @@ -1 sprites/tramtracks.png 8bpp 722 696 64 23 -31 0 normal -1 sprites/tramtracks.png 8bpp 2 776 64 23 -31 0 normal -1 sprites/tramtracks.png 8bpp 82 776 64 39 -31 -8 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 0 0 64 31 -31 0 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 80 0 62 64 2 -49 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 158 0 64 31 -31 0 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 238 0 62 64 -62 -49 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 318 0 62 64 -62 -49 normal + -1 sprites/tramtracks_bare_depot.png 8bpp 398 0 62 64 2 -49 normal diff --git a/media/extra_grf/tramtracks_bare_depot.png b/media/extra_grf/tramtracks_bare_depot.png new file mode 100644 index 0000000000000000000000000000000000000000..d45bcff41e0b222dcadaaec032620405cff2ea21 GIT binary patch literal 3661 zcmXAndpOe#8^?b|h2#`jPbzxEVzjg}^0G8JG@6ViVrtE3mSc@3!_V5C)d)?Rl$M^bN_xHZ;SgeEf4!OVN007uwYlA)q0GmV` zGfGBkqw@g%a{!P6uo#>b0IVx1DQRkI!r^cf3WddDi9}*>a4?g}WV6}X+1UbtprN6m zySsaQd>jNpgqaN*IJ?3_J#^L_k1-(NGo+nL$7a{IMbip@Z!|kjklRpp4F8~XRz~V39*AlR_3@onzE33dN2!LzA z+Hc_ZIcF9%%Lf;=bG-8A z%*Uxh_6tqH+}yRXIh&7NEi)g7ws^wd5pGq1iPiBP)=@bf>Y}RP0|zgwg{yMpik&^- z1rumJl|bF$*v_7WUDWMPk9rgaKry^Vd$+yr&;yyx|0-@?_ETx0mT1fJqjQ2wRf=B^ zgCh8*D~|D*f8%OTrd(9@T1{aT?u{ z9n9^%85;VykNN2gOmtkY*twKqp}*nL@k!ZRuOC#nWhm@=V|PSoJ*<_Qli3grTl z5C^e3vij8gL*K2fA~OrikW9DA;43F=LC2t=zcGhX$0B#eZMkJaIDO#W)LM&{as!SamSz#7X*{vNGAstY*o7fHH!dK^)apr5=mOYHqPM8AhB)DR81^m6dJEdAv3On;VI46PMS<1P9zmLfm%U3t@xFQ^EZ2iyOGwPWPSQxp)?%Iz4~vr z@6-voGNt;JN+pVzuMgQr9cs)g_v>#D{D_U4q(jbGE~gJ2R`~R&;H{Ct=vJ&z)o#9q zyH2I*LTCT+Yd&R{RZ2NGy}Z7>dXWZyJF2q_b&9MxJAT~F+U(<-FTJA%uqU_xp#_%= z_3*6FSK9Xb%>G@OC){*JX=Slnst1-Bp}OhiXNA`a{tuRI(>2xLh40Nk1ysBf(j!7~ zoEeJlQT@-IFY0u~= zceB!m-6d4?vSdg~Vm;-1EtOy7&V^Syya{&c1URORVE z6@k#kzSd`FPweL;dH(e$i<+L3TweuQ{-=i02emu=R% zG*2^YhL`>HjY#EZyK|3L%6wh$_&TV%(j~e0Ee&ofZo2dHYH1iL(0p%ZpBNSX^b9++ zDzi?i@3&zb`~3OUUg4IJ>Q#qKqutXX$}`;){&ry%cS{l)AHxT8K6#jH=sRJb8nj@R z-X?t_C?xO6J{${`!TK$-vZC{Zu-15`v#@|s$01lp}MX{wc0nrmTQ@E z@ahR&v%wstAC#go?}u%>;3q@#4>Z^>NFrrpuG=t@y##|K$8V!=5UejY+@TZwZ(STP;N6KHz=GF;(Yqtk^Uy@tV6R` zYObn9uFNKSK?FlSIlF&w_7u{hCnvhU>_=SYce=ToBipUorhRTi_@o$&5$WpkIk~1< z&pN$QdPuoR0cQH8;XG1L!2!M5>rkziG|SfBmt}LY#+myTjk}IHr04Vx{d{4YjJ**t zxae!5JN%XqoBzc#DSyYqhBtAu$;BS#c&TzdqjudK{j?lR!n`k4IJ6_rn^bmhuSdep zdMD?C{vr6>nAmUi?q&XfaDr=u`T$itl^zk(Rad;Ew6yoJqQcphS1#t7JA%>FymamF z?`-9U4o|_xNlC5~)9Us(76wRV9(TBZt-!+(=9wIOtNWMwN|471UG_aW_TaNw z_2s=4eo~Sh{=cKLI*^fL$M$nM(at#Hb5rFLK1`>Q5w0N6}xln;)K-lEahms_jQgy z{Fm-NN0DPpYn*ZJzG+3$Jv^iUr&w|nTC7H})UiD`uo`zdcPH2Bk;sDN{lH>ybr)QU zmSX7e{aX?FbgoQTks9f>SjPbYJvv<0aZ2*s-akeqZLvU(zZFA|z(*cRaMq>2#wPvp z*MHXYK6WiG!9}@Z=Z?Fd<%%y*6CMuVZ8$n?(&=QsV8?bA9CIjMg2MBHXDM6r3LGy* zAN-NpziIOP7+&ql=*5`XZD)j|fqObljMEas4mMsD93x!%eCMcD?mD9A-87UR)l+YB z&MI4t-ZcHF$Ag*>L={dr+kfhiAs6mrKKufH# zRryr@G}G<9_v9gk@&hpjF5}-j%~F%yq-$>nUghlNKGw@!ELU!s+3+7z_O1=eJKukx zD8}u%k$s+xiX>E<v&m|ABo2_Hui1%)?TWn@Pg_>g*lw(YC-NL-91~LawWi zEZ;8mz3%6)cV9UT)Vp$uU3;``B+@rMIR@Op*KzZol`S;?&bzgvq$hIZ68ZgfK~7yZ3>!LeKI zJl`Frg}$s*N8sz7yq~w=`rH*xplE)qlQ=p>R;^i`m9p@Gzj0ru88h9+ycwc@Oh4*U z)&fqx@fklmuUCidSIn%E?fuQ^G#&YT0Meo754+(Y>>2Eebe$m+h~a}n=nr)-7F?2=zseEM)e%KvEf5*hv$%qPQONd%2k-(G9K zk;C9dB<*31pVX)6!Xc?j(YD(U)J1iHPhyRah+W|NV;#%5=i8!_jc4Nfq>DXf&duq> z^eSWqI9RPTz&}-s3q1f*nRsN#UWR`VV?#E3^tMJ`&ht;KC-GzQ>frvz0r`^9J;xEi OM%h|9pkG=NqW%lmF%m`q literal 0 HcmV?d00001 diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 8bcb36764a..07bf2e6d60 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -584,6 +584,7 @@ + @@ -614,6 +615,7 @@ + @@ -951,6 +953,7 @@ + @@ -1228,6 +1231,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index 140e632e3e..669c533d27 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -840,6 +840,9 @@ Header Files + + Header Files + Header Files @@ -930,6 +933,9 @@ Header Files + + Header Files + Header Files @@ -1941,6 +1947,9 @@ Tables + + Tables + Tables @@ -2772,6 +2781,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index 3f0eff4730..a8dc7c8696 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -584,6 +584,7 @@ + @@ -614,6 +615,7 @@ + @@ -951,6 +953,7 @@ + @@ -1228,6 +1231,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index 140e632e3e..669c533d27 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -840,6 +840,9 @@ Header Files + + Header Files + Header Files @@ -930,6 +933,9 @@ Header Files + + Header Files + Header Files @@ -1941,6 +1947,9 @@ Tables + + Tables + Tables @@ -2772,6 +2781,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj index de1838a23c..6687c3899e 100644 --- a/projects/openttd_vs142.vcxproj +++ b/projects/openttd_vs142.vcxproj @@ -584,6 +584,7 @@ + @@ -614,6 +615,7 @@ + @@ -951,6 +953,7 @@ + @@ -1228,6 +1231,7 @@ + diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters index 140e632e3e..669c533d27 100644 --- a/projects/openttd_vs142.vcxproj.filters +++ b/projects/openttd_vs142.vcxproj.filters @@ -840,6 +840,9 @@ Header Files + + Header Files + Header Files @@ -930,6 +933,9 @@ Header Files + + Header Files + Header Files @@ -1941,6 +1947,9 @@ Tables + + Tables + Tables @@ -2772,6 +2781,9 @@ NewGRF + + NewGRF + NewGRF diff --git a/source.list b/source.list index f5089d03de..bc1832f220 100644 --- a/source.list +++ b/source.list @@ -271,6 +271,7 @@ newgrf_industrytiles.h newgrf_object.h newgrf_properties.h newgrf_railtype.h +newgrf_roadtype.h newgrf_sound.h newgrf_spritegroup.h newgrf_station.h @@ -301,6 +302,7 @@ rail.h rail_gui.h rail_type.h rev.h +road.h road_cmd.h road_func.h road_gui.h @@ -666,6 +668,7 @@ table/pricebase.h table/railtypes.h table/road_land.h table/roadveh_movement.h +table/roadtypes.h ../objs/settings/table/settings.h table/sprites.h table/station_land.h @@ -978,6 +981,7 @@ newgrf_industries.cpp newgrf_industrytiles.cpp newgrf_object.cpp newgrf_railtype.cpp +newgrf_roadtype.cpp newgrf_sound.cpp newgrf_spritegroup.cpp newgrf_station.cpp diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 45cf83f30c..99f14f218b 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -20,6 +20,7 @@ #include "articulated_vehicles.h" #include "core/random_func.hpp" #include "vehiclelist.h" +#include "road.h" #include "table/strings.h" @@ -74,6 +75,9 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) } case VEH_ROAD: + /* make sure the roadtypes are compatible */ + if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false; + /* make sure that we do not replace a tram with a normal road vehicles or vice versa */ if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false; break; diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index c00d97b91e..a1a152cf69 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -14,6 +14,7 @@ #include "vehicle_gui.h" #include "newgrf_engine.h" #include "rail.h" +#include "road.h" #include "strings_func.h" #include "window_func.h" #include "autoreplace_func.h" @@ -24,6 +25,7 @@ #include "settings_func.h" #include "core/geometry_func.hpp" #include "rail_gui.h" +#include "road_gui.h" #include "widgets/dropdown_func.h" #include "widgets/autoreplace_widget.h" @@ -86,6 +88,7 @@ class ReplaceVehicleWindow : public Window { bool descending_sort_order; ///< Order of sorting vehicles. bool show_hidden_engines; ///< Whether to show the hidden engines. RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all. + RoadType sel_roadtype; ///< Type of road selected. #INVALID_ROADTYPE to show all. Scrollbar *vscroll[2]; /** @@ -127,7 +130,21 @@ class ReplaceVehicleWindow : public Window { FOR_ALL_ENGINES_OF_TYPE(e, type) { if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; - if (type == VEH_TRAIN && !this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains + switch (type) { + case VEH_TRAIN: + if (!this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains + break; + + case VEH_ROAD: + if (draw_left && this->sel_roadtype != INVALID_ROADTYPE) { + /* Ensure that the roadtype is specific to the selected one */ + if (e->u.road.roadtype != this->sel_roadtype) continue; + } + break; + + default: + break; + } if (draw_left) { const uint num_engines = GetGroupNumEngines(_local_company, this->sel_group, eid); @@ -210,6 +227,7 @@ public: ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc) { this->sel_railtype = INVALID_RAILTYPE; + this->sel_roadtype = INVALID_ROADTYPE; this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool) this->engines[0].ForceRebuild(); this->engines[1].ForceRebuild(); @@ -229,6 +247,11 @@ public: widget->SetLowered(this->show_hidden_engines); this->FinishInitNested(vehicletype); + if (vehicletype == VEH_TRAIN || vehicletype == VEH_ROAD) { + widget = this->GetWidget(WID_RV_RAIL_ROAD_TYPE_DROPDOWN); + widget->tool_tip = STR_REPLACE_HELP_RAILTYPE + vehicletype; + } + this->sort_criteria = _engine_sort_last_criteria[vehicletype]; this->descending_sort_order = _engine_sort_last_order[vehicletype]; this->owner = _local_company; @@ -287,13 +310,28 @@ public: break; } - case WID_RV_TRAIN_RAILTYPE_DROPDOWN: { + case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: { Dimension d = {0, 0}; - for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) { - const RailtypeInfo *rti = GetRailTypeInfo(rt); - /* Skip rail type if it has no label */ - if (rti->label == 0) continue; - d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text)); + switch (this->window_number) { + case VEH_TRAIN: + for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) { + const RailtypeInfo *rti = GetRailTypeInfo(rt); + /* Skip rail type if it has no label */ + if (rti->label == 0) continue; + d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text)); + } + break; + + case VEH_ROAD: + for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) { + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + /* Skip road type if it has no label */ + if (rti->label == 0) continue; + d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text)); + } + break; + + default: NOT_REACHED(); } d.width += padding.width; d.height += padding.height; @@ -409,9 +447,18 @@ public: * or The selected vehicle has no replacement set up */ this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)); - if (this->window_number == VEH_TRAIN) { - /* Show the selected railtype in the pulldown menu */ - this->GetWidget(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text; + switch (this->window_number) { + case VEH_TRAIN: + /* Show the selected railtype in the pulldown menu */ + this->GetWidget(WID_RV_RAIL_ROAD_TYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text; + break; + + case VEH_ROAD: + /* Show the selected roadtype in the pulldown menu */ + this->GetWidget(WID_RV_RAIL_ROAD_TYPE_DROPDOWN)->widget_data = sel_roadtype == INVALID_ROADTYPE ? STR_REPLACE_ALL_ROADTYPE : GetRoadTypeInfo(sel_roadtype)->strings.replace_text; + break; + + default: break; } this->DrawWidgets(); @@ -472,8 +519,16 @@ public: break; } - case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu - ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_TRAIN_RAILTYPE_DROPDOWN); + case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: // Rail/roadtype selection dropdown menu + switch (this->window_number) { + case VEH_TRAIN: + ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN); + break; + + case VEH_ROAD: + ShowDropDownList(this, GetRoadTypeDropDownList(RTTB_ROAD | RTTB_TRAM, true, true), sel_roadtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN); + break; + } break; case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length @@ -533,10 +588,25 @@ public: } break; - case WID_RV_TRAIN_RAILTYPE_DROPDOWN: { - RailType temp = (RailType)index; - if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything - sel_railtype = temp; + case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: + switch (this->window_number) { + case VEH_TRAIN: { + RailType temp = (RailType)index; + if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything + sel_railtype = temp; + break; + } + + case VEH_ROAD: { + RoadType temp = (RoadType)index; + if (temp == sel_roadtype) return; // we didn't select a new one. No need to change anything + sel_roadtype = temp; + break; + } + + default: NOT_REACHED(); + } + /* Reset scrollbar positions */ this->vscroll[0]->SetPosition(0); this->vscroll[1]->SetPosition(0); @@ -546,7 +616,6 @@ public: this->reset_sel_engine = true; this->SetDirty(); break; - } case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: { this->replace_engines = index != 0; @@ -603,7 +672,7 @@ static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(NWID_VERTICAL), NWidget(NWID_HORIZONTAL), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_REPLACE_ENGINE_WAGON_SELECT_HELP), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(), @@ -648,6 +717,64 @@ static WindowDesc _replace_rail_vehicle_desc( _nested_replace_rail_vehicle_widgets, lengthof(_nested_replace_rail_vehicle_widgets) ); +static const NWidgetPart _nested_replace_road_vehicle_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(NWID_VERTICAL), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(), + EndContainer(), + NWidget(NWID_VERTICAL), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP), + NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON), + NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), + EndContainer(), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), +}; + +static WindowDesc _replace_road_vehicle_desc( + WDP_AUTO, "replace_vehicle_road", 500, 140, + WC_REPLACE_VEHICLE, WC_NONE, + WDF_CONSTRUCTION, + _nested_replace_road_vehicle_widgets, lengthof(_nested_replace_road_vehicle_widgets) +); + static const NWidgetPart _nested_replace_vehicle_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -710,5 +837,11 @@ static WindowDesc _replace_vehicle_desc( void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype) { DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype); - new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g); + WindowDesc *desc; + switch (vehicletype) { + case VEH_TRAIN: desc = &_replace_rail_vehicle_desc; break; + case VEH_ROAD: desc = &_replace_road_vehicle_desc; break; + default: desc = &_replace_vehicle_desc; break; + } + new ReplaceVehicleWindow(desc, vehicletype, id_g); } diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index b11290ae43..710c591caa 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -13,6 +13,7 @@ #include "error.h" #include "command_func.h" #include "rail.h" +#include "road.h" #include "strings_func.h" #include "window_func.h" #include "sound_func.h" @@ -403,11 +404,25 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo Money infra_cost = 0; switch (transport_type) { - case TRANSPORT_ROAD: - infra_cost = (bridge_len + 2) * _price[PR_BUILD_ROAD] * 2; + case TRANSPORT_ROAD: { /* In case we add a new road type as well, we must be aware of those costs. */ - if (IsBridgeTile(start)) infra_cost *= CountBits(GetRoadTypes(start) | (RoadTypes)road_rail_type); + RoadType road_rt = INVALID_ROADTYPE; + RoadType tram_rt = INVALID_ROADTYPE; + if (IsBridgeTile(start)) { + road_rt = GetRoadTypeRoad(start); + tram_rt = GetRoadTypeTram(start); + } + if (RoadTypeIsRoad((RoadType)road_rail_type)) { + road_rt = (RoadType)road_rail_type; + } else { + tram_rt = (RoadType)road_rail_type; + } + + if (road_rt != INVALID_ROADTYPE) infra_cost += (bridge_len + 2) * 2 * RoadBuildCost(road_rt); + if (tram_rt != INVALID_ROADTYPE) infra_cost += (bridge_len + 2) * 2 * RoadBuildCost(tram_rt); + break; + } case TRANSPORT_RAIL: infra_cost = (bridge_len + 2) * RailBuildCost((RailType)road_rail_type); break; default: break; } diff --git a/src/bridge_map.h b/src/bridge_map.h index 4e70238c27..1461df13a8 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -132,11 +132,11 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D SetTileOwner(t, o); _m[t].m2 = 0; _m[t].m3 = 0; - _m[t].m4 = 0; + _m[t].m4 = INVALID_ROADTYPE; _m[t].m5 = 1 << 7 | tt << 2 | d; SB(_me[t].m6, 2, 4, bridgetype); _me[t].m7 = 0; - _me[t].m8 = 0; + _me[t].m8 = INVALID_ROADTYPE << 6; } /** @@ -147,14 +147,15 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D * @param owner_tram the new owner of the tram on the bridge * @param bridgetype the type of bridge this bridge ramp belongs to * @param d the direction this ramp must be facing - * @param rts the road types of the bridge + * @param road_rt the road type of the bridge + * @param tram_rt the tram type of the bridge */ -static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes rts) +static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadType road_rt, RoadType tram_rt) { MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD); - SetRoadOwner(t, ROADTYPE_ROAD, owner_road); - if (owner_tram != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, owner_tram); - SetRoadTypes(t, rts); + SetRoadOwner(t, RTT_ROAD, owner_road); + if (owner_tram != OWNER_TOWN) SetRoadOwner(t, RTT_TRAM, owner_tram); + SetRoadTypes(t, road_rt, tram_rt); } /** diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index 32310b9c35..ba3eaf4d08 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -1014,8 +1014,8 @@ void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selecte struct BuildVehicleWindow : Window { VehicleType vehicle_type; ///< Type of vehicles shown in the window. union { - RailType railtype; ///< Rail type to show, or #RAILTYPE_END. - RoadTypes roadtypes; ///< Road type to show, or #ROADTYPES_ALL. + RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE. + RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE. } filter; ///< Filter to apply. bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction byte sort_criteria; ///< Current sort criterium. @@ -1050,7 +1050,8 @@ struct BuildVehicleWindow : Window { BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc) { this->vehicle_type = type; - this->window_number = tile == INVALID_TILE ? (int)type : tile; + this->listview_mode = tile == INVALID_TILE; + this->window_number = this->listview_mode ? (int)type : tile; this->sel_engine = INVALID_ENGINE; @@ -1058,19 +1059,7 @@ struct BuildVehicleWindow : Window { this->descending_sort_order = _engine_sort_last_order[type]; this->show_hidden_engines = _engine_sort_show_hidden_engines[type]; - switch (type) { - default: NOT_REACHED(); - case VEH_TRAIN: - this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile); - break; - case VEH_ROAD: - this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile); - case VEH_SHIP: - case VEH_AIRCRAFT: - break; - } - - this->listview_mode = (this->window_number <= VEH_END); + this->UpdateFilterByTile(); this->CreateNestedTree(); @@ -1114,6 +1103,36 @@ struct BuildVehicleWindow : Window { } } + /** Set the filter type according to the depot type */ + void UpdateFilterByTile() + { + switch (this->vehicle_type) { + default: NOT_REACHED(); + case VEH_TRAIN: + if (this->listview_mode) { + this->filter.railtype = INVALID_RAILTYPE; + } else { + this->filter.railtype = GetRailType(this->window_number); + } + break; + + case VEH_ROAD: + if (this->listview_mode) { + this->filter.roadtype = INVALID_ROADTYPE; + } else { + this->filter.roadtype = GetRoadTypeRoad(this->window_number); + if (this->filter.roadtype == INVALID_ROADTYPE) { + this->filter.roadtype = GetRoadTypeTram(this->window_number); + } + } + break; + + case VEH_SHIP: + case VEH_AIRCRAFT: + break; + } + } + /** Populate the filter list and set the cargo filter criteria. */ void SetCargoFilterArray() { @@ -1223,8 +1242,6 @@ struct BuildVehicleWindow : Window { int num_engines = 0; int num_wagons = 0; - this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number); - this->eng_list.clear(); /* Make list of all available train engines and wagons. @@ -1237,7 +1254,7 @@ struct BuildVehicleWindow : Window { EngineID eid = e->index; const RailVehicleInfo *rvi = &e->u.rail; - if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; + if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; /* Filter now! So num_engines and num_wagons is valid */ @@ -1280,7 +1297,8 @@ struct BuildVehicleWindow : Window { if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue; EngineID eid = e->index; if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; - if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue; + if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue; + this->eng_list.push_back(eid); if (eid == this->sel_engine) sel_id = eid; @@ -1338,6 +1356,10 @@ struct BuildVehicleWindow : Window { void GenerateBuildList() { if (!this->eng_list.NeedRebuild()) return; + + /* Update filter type in case the road/railtype of the depot got converted */ + this->UpdateFilterByTile(); + switch (this->vehicle_type) { default: NOT_REACHED(); case VEH_TRAIN: @@ -1460,6 +1482,9 @@ struct BuildVehicleWindow : Window { if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) { const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype); SetDParam(0, rti->strings.build_caption); + } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) { + const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype); + SetDParam(0, rti->strings.build_caption); } else { SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type); } diff --git a/src/command.cpp b/src/command.cpp index 8a92c3478c..d81e639b08 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -68,6 +68,8 @@ CommandProc CmdBuildRoad; CommandProc CmdBuildRoadDepot; +CommandProc CmdConvertRoad; + CommandProc CmdBuildAirport; CommandProc CmdBuildDock; @@ -235,6 +237,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed. DEF_CMD(CmdBuildRoad, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD DEF_CMD(CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_DEPOT + DEF_CMD(CmdConvertRoad, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_ROAD DEF_CMD(CmdBuildAirport, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_AIRPORT DEF_CMD(CmdBuildDock, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_DOCK diff --git a/src/command_type.h b/src/command_type.h index 95b6c0d623..e24467b0a3 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -202,6 +202,7 @@ enum Commands { CMD_REMOVE_LONG_ROAD, ///< remove a complete road (not a "half" one) CMD_BUILD_ROAD, ///< build a "half" road CMD_BUILD_ROAD_DEPOT, ///< build a road depot + CMD_CONVERT_ROAD, ///< convert a road type CMD_BUILD_AIRPORT, ///< build an airport diff --git a/src/company_base.h b/src/company_base.h index 27ae96fce2..728d4ffeca 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -43,6 +43,9 @@ struct CompanyInfrastructure { for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) total += this->rail[rt]; return total; } + + uint32 GetRoadTotal() const; + uint32 GetTramTotal() const; }; typedef Pool CompanyPool; diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index fe4163acd8..af7913e2cc 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -562,7 +562,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY) c->share_owners[0] = c->share_owners[1] = c->share_owners[2] = c->share_owners[3] = INVALID_OWNER; c->avail_railtypes = GetCompanyRailtypes(c->index); - c->avail_roadtypes = GetCompanyRoadtypes(c->index); + c->avail_roadtypes = GetCompanyRoadTypes(c->index); c->inaugurated_year = _cur_year; RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false); // create a random company manager face @@ -1165,3 +1165,29 @@ int CompanyServiceInterval(const Company *c, VehicleType type) case VEH_SHIP: return vds->servint_ships; } } + +/** + * Get total sum of all owned road bits. + * @return Combined total road road bits. + */ +uint32 CompanyInfrastructure::GetRoadTotal() const +{ + uint32 total = 0; + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + if (RoadTypeIsRoad(rt)) total += this->road[rt]; + } + return total; +} + +/** + * Get total sum of all owned tram bits. + * @return Combined total of tram road bits. + */ +uint32 CompanyInfrastructure::GetTramTotal() const +{ + uint32 total = 0; + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + if (RoadTypeIsTram(rt)) total += this->road[rt]; + } + return total; +} diff --git a/src/company_gui.cpp b/src/company_gui.cpp index b36ab959e3..eaa7ad55a3 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -30,6 +30,7 @@ #include "core/geometry_func.hpp" #include "object_type.h" #include "rail.h" +#include "road.h" #include "engine_base.h" #include "window_func.h" #include "road_func.h" @@ -1782,6 +1783,10 @@ static const NWidgetPart _nested_company_infrastructure_widgets[] = { NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1), EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0), + NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1), + EndContainer(), NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0), NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1), @@ -1819,7 +1824,7 @@ struct CompanyInfrastructureWindow : Window void UpdateRailRoadTypes() { this->railtypes = RAILTYPES_NONE; - this->roadtypes = ROADTYPES_ROAD; // Road is always available. + this->roadtypes = ROADTYPES_NONE; /* Find the used railtypes. */ Engine *e; @@ -1832,14 +1837,16 @@ struct CompanyInfrastructureWindow : Window /* Get the date introduced railtypes as well. */ this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DAY); - /* Tram is only visible when there will be a tram. */ + /* Find the used roadtypes. */ FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue; - if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue; - this->roadtypes |= ROADTYPES_TRAM; - break; + this->roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes; } + + /* Get the date introduced roadtypes as well. */ + this->roadtypes = AddDateIntroducedRoadTypes(this->roadtypes, MAX_DAY); + this->roadtypes &= ~_roadtypes_hidden_mask; } /** Get total infrastructure maintenance cost. */ @@ -1854,8 +1861,11 @@ struct CompanyInfrastructureWindow : Window } total += SignalMaintenanceCost(c->infrastructure.signal); - if (HasBit(this->roadtypes, ROADTYPE_ROAD)) total += RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]); - if (HasBit(this->roadtypes, ROADTYPE_TRAM)) total += RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]); + uint32 road_total = c->infrastructure.GetRoadTotal(); + uint32 tram_total = c->infrastructure.GetTramTotal(); + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + if (HasBit(this->roadtypes, rt)) total += RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total); + } total += CanalMaintenanceCost(c->infrastructure.water); total += StationMaintenanceCost(c->infrastructure.station); @@ -1883,7 +1893,8 @@ struct CompanyInfrastructureWindow : Window size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width); - for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) { + RailType rt; + FOR_ALL_SORTED_RAILTYPES(rt) { if (HasBit(this->railtypes, rt)) { lines++; SetDParam(0, GetRailTypeInfo(rt)->strings.name); @@ -1899,18 +1910,19 @@ struct CompanyInfrastructureWindow : Window break; } - case WID_CI_ROAD_DESC: { - uint lines = 1; + case WID_CI_ROAD_DESC: + case WID_CI_TRAM_DESC: { + uint lines = 0; - size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT).width); + size->width = max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width); - if (HasBit(this->roadtypes, ROADTYPE_ROAD)) { - lines++; - size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD).width + WD_FRAMERECT_LEFT); - } - if (HasBit(this->roadtypes, ROADTYPE_TRAM)) { - lines++; - size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY).width + WD_FRAMERECT_LEFT); + RoadType rt; + FOR_ALL_SORTED_ROADTYPES(rt) { + if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { + lines++; + SetDParam(0, GetRoadTypeInfo(rt)->strings.name); + size->width = max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT); + } } size->height = max(size->height, lines * FONT_HEIGHT_NORMAL); @@ -1930,6 +1942,7 @@ struct CompanyInfrastructureWindow : Window case WID_CI_RAIL_COUNT: case WID_CI_ROAD_COUNT: + case WID_CI_TRAM_COUNT: case WID_CI_WATER_COUNT: case WID_CI_STATION_COUNT: case WID_CI_TOTAL: { @@ -1943,9 +1956,12 @@ struct CompanyInfrastructureWindow : Window } max_val = max(max_val, c->infrastructure.signal); max_cost = max(max_cost, SignalMaintenanceCost(c->infrastructure.signal)); + uint32 road_total = c->infrastructure.GetRoadTotal(); + uint32 tram_total = c->infrastructure.GetTramTotal(); for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) { max_val = max(max_val, c->infrastructure.road[rt]); - max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt])); + max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total)); + } max_val = max(max_val, c->infrastructure.water); max_cost = max(max_cost, CanalMaintenanceCost(c->infrastructure.water)); @@ -2041,26 +2057,32 @@ struct CompanyInfrastructureWindow : Window } case WID_CI_ROAD_DESC: - DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT); - - if (this->roadtypes != ROADTYPES_NONE) { - if (HasBit(this->roadtypes, ROADTYPE_ROAD)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD); - if (HasBit(this->roadtypes, ROADTYPE_TRAM)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY); - } else { - /* No valid roadtypes. */ - DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE); + case WID_CI_TRAM_DESC: { + DrawString(r.left, r.right, y, widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT); + + /* Draw name of each valid roadtype. */ + RoadType rt; + FOR_ALL_SORTED_ROADTYPES(rt) { + if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) { + SetDParam(0, GetRoadTypeInfo(rt)->strings.name); + DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING); + } } break; + } case WID_CI_ROAD_COUNT: - if (HasBit(this->roadtypes, ROADTYPE_ROAD)) { - this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_ROAD], RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD])); - } - if (HasBit(this->roadtypes, ROADTYPE_TRAM)) { - this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_TRAM], RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM])); + case WID_CI_TRAM_COUNT: { + uint32 road_tram_total = widget == WID_CI_ROAD_COUNT ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal(); + RoadType rt; + FOR_ALL_SORTED_ROADTYPES(rt) { + if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_COUNT)) { + this->DrawCountLine(r, y, c->infrastructure.road[rt], RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_tram_total)); + } } break; + } case WID_CI_WATER_DESC: DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT); diff --git a/src/economy.cpp b/src/economy.cpp index ad48ce25ef..b6778f9e5d 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -685,8 +685,10 @@ static void CompaniesGenStatistics() if (c->infrastructure.rail[rt] != 0) cost.AddCost(RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total)); } cost.AddCost(SignalMaintenanceCost(c->infrastructure.signal)); + uint32 road_total = c->infrastructure.GetRoadTotal(); + uint32 tram_total = c->infrastructure.GetTramTotal(); for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) { - if (c->infrastructure.road[rt] != 0) cost.AddCost(RoadMaintenanceCost(rt, c->infrastructure.road[rt])); + if (c->infrastructure.road[rt] != 0) cost.AddCost(RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total)); } cost.AddCost(CanalMaintenanceCost(c->infrastructure.water)); cost.AddCost(StationMaintenanceCost(c->infrastructure.station)); diff --git a/src/engine.cpp b/src/engine.cpp index 3e2a7d2d7c..f9363b6014 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -708,7 +708,7 @@ void StartupEngines() Company *c; FOR_ALL_COMPANIES(c) { c->avail_railtypes = GetCompanyRailtypes(c->index); - c->avail_roadtypes = GetCompanyRoadtypes(c->index); + c->avail_roadtypes = GetCompanyRoadTypes(c->index); } /* Invalidate any open purchase lists */ @@ -730,7 +730,8 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company) assert(e->u.rail.railtype < RAILTYPE_END); c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date); } else if (e->type == VEH_ROAD) { - SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); + assert(e->u.road.roadtype < ROADTYPE_END); + c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date); } e->preview_company = INVALID_COMPANY; @@ -810,6 +811,7 @@ void EnginesDailyLoop() Company *c; FOR_ALL_COMPANIES(c) { c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date); + c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, _date); } if (_cur_year >= _year_engine_aging_stops) return; @@ -951,7 +953,8 @@ static void NewVehicleAvailable(Engine *e) FOR_ALL_COMPANIES(c) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date); } else if (e->type == VEH_ROAD) { /* maybe make another road type available */ - FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); + assert(e->u.road.roadtype < ROADTYPE_END); + FOR_ALL_COMPANIES(c) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date); } /* Only broadcast event if AIs are able to build this vehicle type. */ @@ -1098,6 +1101,11 @@ bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company) const Company *c = Company::Get(company); if (((GetRailTypeInfo(e->u.rail.railtype))->compatible_railtypes & c->avail_railtypes) == 0) return false; } + if (type == VEH_ROAD && company != OWNER_DEITY) { + /* Check if the road type is available to this company */ + const Company *c = Company::Get(company); + if ((GetRoadTypeInfo(e->u.road.roadtype)->powered_roadtypes & c->avail_roadtypes) == ROADTYPES_NONE) return false; + } return true; } diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index eb14b5d8a2..90cba6ed18 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -19,6 +19,7 @@ #include "vehicle_func.h" #include "company_func.h" #include "rail.h" +#include "road.h" #include "settings_type.h" #include "train.h" #include "roadveh.h" @@ -41,7 +42,8 @@ StringID GetEngineCategoryName(EngineID engine) const Engine *e = Engine::Get(engine); switch (e->type) { default: NOT_REACHED(); - case VEH_ROAD: return STR_ENGINE_PREVIEW_ROAD_VEHICLE; + case VEH_ROAD: + return GetRoadTypeInfo(e->u.road.roadtype)->strings.new_engine; case VEH_AIRCRAFT: return STR_ENGINE_PREVIEW_AIRCRAFT; case VEH_SHIP: return STR_ENGINE_PREVIEW_SHIP; case VEH_TRAIN: diff --git a/src/engine_type.h b/src/engine_type.h index 1cc0452a5f..6cc8dc68eb 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -14,6 +14,7 @@ #include "economy_type.h" #include "rail_type.h" +#include "road_type.h" #include "cargo_type.h" #include "date_type.h" #include "sound_type.h" @@ -123,6 +124,7 @@ struct RoadVehicleInfo { uint8 air_drag; ///< Coefficient of air drag byte visual_effect; ///< Bitstuffed NewGRF visual effect data byte shorten_factor; ///< length on main map for this type is 8 - shorten_factor + RoadType roadtype; ///< Road type }; /** diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp index b94fb0e7bf..c269aeb797 100644 --- a/src/ground_vehicle.cpp +++ b/src/ground_vehicle.cpp @@ -28,7 +28,7 @@ void GroundVehicle::PowerChanged() uint32 total_power = 0; uint32 max_te = 0; uint32 number_of_parts = 0; - uint16 max_track_speed = v->GetDisplayMaxSpeed(); + uint16 max_track_speed = this->vcache.cached_max_speed; // Max track speed in internal units. for (const T *u = v; u != nullptr; u = u->Next()) { uint32 current_power = u->GetPower() + u->GetPoweredPartPower(u); diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp index 109257fe20..5268651348 100644 --- a/src/ground_vehicle.hpp +++ b/src/ground_vehicle.hpp @@ -36,7 +36,7 @@ struct GroundVehicleCache { uint16 cached_axle_resistance; ///< Resistance caused by the axles of the vehicle (valid only for the first engine). /* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */ - uint16 cached_max_track_speed; ///< Maximum consist speed limited by track type (valid only for the first engine). + uint16 cached_max_track_speed; ///< Maximum consist speed (in internal units) limited by track type (valid only for the first engine). uint32 cached_power; ///< Total power of the consist (valid only for the first engine). uint32 cached_air_drag; ///< Air drag coefficient of the vehicle (valid only for the first engine). diff --git a/src/lang/english.txt b/src/lang/english.txt index 83a28d8cdc..9641ecc041 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -341,6 +341,7 @@ STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN :{BLACK}Zoom the STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT :{BLACK}Zoom the view out STR_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Build railway track STR_TOOLBAR_TOOLTIP_BUILD_ROADS :{BLACK}Build roads +STR_TOOLBAR_TOOLTIP_BUILD_TRAMWAYS :{BLACK}Build tramways STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS :{BLACK}Build ship docks STR_TOOLBAR_TOOLTIP_BUILD_AIRPORTS :{BLACK}Build airports STR_TOOLBAR_TOOLTIP_LANDSCAPING :{BLACK}Open the landscaping toolbar to raise/lower land, plant trees, etc. @@ -361,6 +362,7 @@ STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Landscap STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Town generation STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Industry generation STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Road construction +STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Tramway construction STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plant trees. Shift toggles building/showing cost estimate STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Place sign STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Place object. Shift toggles building/showing cost estimate @@ -2434,6 +2436,11 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}Build ro STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}Build tramway tunnel. Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}Toggle build/remove for road construction STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}Toggle build/remove for tramway construction +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}Convert/Upgrade the type of road. Shift toggles building/showing cost estimate +STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM :{BLACK}Convert/Upgrade the type of tram. Shift toggles building/showing cost estimate + +STR_ROAD_NAME_ROAD :Road +STR_ROAD_NAME_TRAM :Tramway # Road depot construction window STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION :{WHITE}Road Depot Orientation @@ -2618,8 +2625,11 @@ STR_LAND_AREA_INFORMATION_NEWGRF_NAME :{BLACK}NewGRF: STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED :{BLACK}Cargo accepted: {LTBLUE} STR_LAND_AREA_INFORMATION_CARGO_EIGHTS :({COMMA}/8 {STRING}) STR_LANG_AREA_INFORMATION_RAIL_TYPE :{BLACK}Rail type: {LTBLUE}{STRING} +STR_LANG_AREA_INFORMATION_ROAD_TYPE :{BLACK}Road type: {LTBLUE}{STRING} +STR_LANG_AREA_INFORMATION_TRAM_TYPE :{BLACK}Tram type: {LTBLUE}{STRING} STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Rail speed limit: {LTBLUE}{VELOCITY} STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Road speed limit: {LTBLUE}{VELOCITY} +STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Tram speed limit: {LTBLUE}{VELOCITY} # Description of land area of different tiles STR_LAI_CLEAR_DESCRIPTION_ROCKS :Rocks @@ -3359,8 +3369,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION :{WHITE}Infrastr STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT :{GOLD}Rail pieces: STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS :{WHITE}Signals STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT :{GOLD}Road pieces: -STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD :{WHITE}Road -STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY :{WHITE}Tramway +STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT :{GOLD}Tram pieces: STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT :{GOLD}Water tiles: STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS :{WHITE}Canals STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}Stations: @@ -3468,10 +3477,15 @@ STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION :New Electric Ra STR_BUY_VEHICLE_TRAIN_MONORAIL_CAPTION :New Monorail Vehicles STR_BUY_VEHICLE_TRAIN_MAGLEV_CAPTION :New Maglev Vehicles -STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :New Rail Vehicles STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :New Road Vehicles +STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :New Tram Vehicles + +############ range for vehicle availability starts +STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :New Rail Vehicles +STR_BUY_VEHICLE_ROAD_VEHICLE_ALL_CAPTION :New Road Vehicles STR_BUY_VEHICLE_SHIP_CAPTION :New Ships STR_BUY_VEHICLE_AIRCRAFT_CAPTION :New Aircraft +############ range for vehicle availability ends STR_PURCHASE_INFO_COST_WEIGHT :{BLACK}Cost: {GOLD}{CURRENCY_LONG}{BLACK} Weight: {GOLD}{WEIGHT_SHORT} STR_PURCHASE_INFO_COST_REFIT_WEIGHT :{BLACK}Cost: {GOLD}{CURRENCY_LONG}{BLACK} (Refit Cost: {GOLD}{CURRENCY_LONG}{BLACK}) Weight: {GOLD}{WEIGHT_SHORT} @@ -3632,12 +3646,17 @@ STR_DEPOT_SELL_CONFIRMATION_TEXT :{YELLOW}You are # Engine preview window STR_ENGINE_PREVIEW_CAPTION :{WHITE}Message from vehicle manufacturer STR_ENGINE_PREVIEW_MESSAGE :{GOLD}We have just designed a new {STRING} - would you be interested in a year's exclusive use of this vehicle, so we can see how it performs before making it universally available? + STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE :railway locomotive +STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE :electrified railway locomotive +STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :monorail locomotive +STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :maglev locomotive + STR_ENGINE_PREVIEW_ROAD_VEHICLE :road vehicle +STR_ENGINE_PREVIEW_TRAM_VEHICLE :tramway vehicle + STR_ENGINE_PREVIEW_AIRCRAFT :aircraft STR_ENGINE_PREVIEW_SHIP :ship -STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :monorail locomotive -STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :maglev locomotive STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER}{}Running Cost: {CURRENCY_LONG}/yr{}Capacity: {CARGO_LONG} STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER} Max. T.E.: {6:FORCE}{}Running Cost: {4:CURRENCY_LONG}/yr{}Capacity: {5:CARGO_LONG} @@ -3676,14 +3695,19 @@ STR_REPLACE_ENGINE_WAGON_SELECT_HELP :{BLACK}Switch b STR_REPLACE_ENGINES :Engines STR_REPLACE_WAGONS :Wagons STR_REPLACE_ALL_RAILTYPE :All rail vehicles +STR_REPLACE_ALL_ROADTYPE :All road vehicles STR_REPLACE_HELP_RAILTYPE :{BLACK}Choose the rail type you want to replace engines for +STR_REPLACE_HELP_ROADTYPE :{BLACK}Choose the road type you want to replace engines for STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Displays which engine the left selected engine is being replaced with, if any STR_REPLACE_RAIL_VEHICLES :Rail Vehicles STR_REPLACE_ELRAIL_VEHICLES :Electrified Rail Vehicles STR_REPLACE_MONORAIL_VEHICLES :Monorail Vehicles STR_REPLACE_MAGLEV_VEHICLES :Maglev Vehicles +STR_REPLACE_ROAD_VEHICLES :Road Vehicles +STR_REPLACE_TRAM_VEHICLES :Tramway Vehicles + STR_REPLACE_REMOVE_WAGON :{BLACK}Wagon removal: {ORANGE}{STRING} STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Make autoreplace keep the length of a train the same by removing wagons (starting at the front), if replacing the engine would make the train longer @@ -4406,7 +4430,8 @@ STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Must rem STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}No suitable railway track STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Must remove railway track first STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Road is one way or blocked -STR_ERROR_CROSSING_DISALLOWED :{WHITE}Level crossings not allowed for this rail type +STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Level crossings not allowed for this rail type +STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}Level crossings not allowed for this road type STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Can't build signals here... STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Can't build railway track here... STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Can't remove railway track from here... @@ -4426,6 +4451,12 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Can't re STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Can't remove tramway from here... STR_ERROR_THERE_IS_NO_ROAD :{WHITE}... there is no road STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}... there is no tramway +STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}Can't convert road type here... +STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}Can't convert tram type here... +STR_ERROR_NO_SUITABLE_ROAD :{WHITE}No suitable road +STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}No suitable tramway +STR_ERROR_INCOMPATIBLE_ROAD :{WHITE}... incompatible road +STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... incompatible tramway # Waterway construction errors STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Can't build canals here... diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 86c17cdccd..353972d489 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -166,7 +166,10 @@ public: td.airport_tile_name = STR_NULL; td.railtype = STR_NULL; td.rail_speed = 0; + td.roadtype = STR_NULL; td.road_speed = 0; + td.tramtype = STR_NULL; + td.tram_speed = 0; td.grf = nullptr; @@ -286,6 +289,13 @@ public: line_nr++; } + /* Road type name */ + if (td.roadtype != STR_NULL) { + SetDParam(0, td.roadtype); + GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_ROAD_TYPE, lastof(this->landinfo_data[line_nr])); + line_nr++; + } + /* Road speed limit */ if (td.road_speed != 0) { SetDParam(0, td.road_speed); @@ -293,6 +303,20 @@ public: line_nr++; } + /* Tram type name */ + if (td.tramtype != STR_NULL) { + SetDParam(0, td.tramtype); + GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_TRAM_TYPE, lastof(this->landinfo_data[line_nr])); + line_nr++; + } + + /* Tram speed limit */ + if (td.tram_speed != 0) { + SetDParam(0, td.tram_speed); + GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT, lastof(this->landinfo_data[line_nr])); + line_nr++; + } + /* NewGRF name */ if (td.grf != nullptr) { SetDParamStr(0, td.grf); diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 759c556a79..82279ab996 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -49,6 +49,7 @@ #include "vehicle_func.h" #include "language.h" #include "vehicle_base.h" +#include "road.h" #include "table/strings.h" #include "table/build_industry.h" @@ -309,6 +310,7 @@ struct GRFTempEngineData { uint16 cargo_allowed; uint16 cargo_disallowed; RailTypeLabel railtypelabel; + uint8 roadtramtype; const GRFFile *defaultcargo_grf; ///< GRF defining the cargo translation table to use if the default cargo is the 'first refittable'. Refittability refittability; ///< Did the newgrf set any refittability property? If not, default refittability will be applied. bool prop27_set; ///< Did the NewGRF set property 27 (misc flags)? @@ -1345,6 +1347,12 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop RoadVehicleInfo *rvi = &e->u.road; switch (prop) { + case 0x05: // Road/tram type + /* RoadTypeLabel is looked up later after the engine's road/tram + * flag is set, however 0 means the value has not been set. */ + _gted[e->index].roadtramtype = buf->ReadByte() + 1; + break; + case 0x08: // Speed (1 unit is 0.5 kmh) rvi->max_speed = buf->ReadByte(); break; @@ -2615,6 +2623,12 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, By case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes) return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->railtype_list, "Rail type"); + case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes) + return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->roadtype_list, "Road type"); + + case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes) + return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->tramtype_list, "Tram type"); + default: break; } @@ -2830,6 +2844,12 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, B case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes) return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->railtype_list, "Rail type"); + case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined roadtypes) + return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->roadtype_list, "Road type"); + + case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes) + return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->tramtype_list, "Tram type"); + default: break; } @@ -4368,6 +4388,228 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, Byte return ret; } +/** + * Define properties for roadtypes + * @param id ID of the roadtype. + * @param numinfo Number of subsequent IDs to change the property for. + * @param prop The property to change. + * @param buf The property value. + * @return ChangeInfoResult. + */ +static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf, RoadTramType rtt) +{ + ChangeInfoResult ret = CIR_SUCCESS; + + extern RoadTypeInfo _roadtypes[ROADTYPE_END]; + RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map; + + if (id + numinfo > ROADTYPE_END) { + grfmsg(1, "RoadTypeChangeInfo: Road type %u is invalid, max %u, ignoring", id + numinfo, ROADTYPE_END); + return CIR_INVALID_ID; + } + + for (int i = 0; i < numinfo; i++) { + RoadType rt = type_map[id + i]; + if (rt == INVALID_ROADTYPE) return CIR_INVALID_ID; + + RoadTypeInfo *rti = &_roadtypes[rt]; + + switch (prop) { + case 0x08: // Label of road type + /* Skipped here as this is loaded during reservation stage. */ + buf->ReadDWord(); + break; + + case 0x09: { // Toolbar caption of roadtype (sets name as well for backwards compatibility for grf ver < 8) + uint16 str = buf->ReadWord(); + AddStringForMapping(str, &rti->strings.toolbar_caption); + break; + } + + case 0x0A: // Menu text of roadtype + AddStringForMapping(buf->ReadWord(), &rti->strings.menu_text); + break; + + case 0x0B: // Build window caption + AddStringForMapping(buf->ReadWord(), &rti->strings.build_caption); + break; + + case 0x0C: // Autoreplace text + AddStringForMapping(buf->ReadWord(), &rti->strings.replace_text); + break; + + case 0x0D: // New engine text + AddStringForMapping(buf->ReadWord(), &rti->strings.new_engine); + break; + + case 0x0F: // Powered roadtype list + case 0x18: // Roadtype list required for date introduction + case 0x19: { // Introduced roadtype list + /* Road type compatibility bits are added to the existing bits + * to allow multiple GRFs to modify compatibility with the + * default road types. */ + int n = buf->ReadByte(); + for (int j = 0; j != n; j++) { + RoadTypeLabel label = buf->ReadDWord(); + RoadType rt = GetRoadTypeByLabel(BSWAP32(label), false); + if (rt != INVALID_ROADTYPE) { + switch (prop) { + case 0x0F: SetBit(rti->powered_roadtypes, rt); break; + case 0x18: SetBit(rti->introduction_required_roadtypes, rt); break; + case 0x19: SetBit(rti->introduces_roadtypes, rt); break; + } + } + } + break; + } + + case 0x10: // Road Type flags + rti->flags = (RoadTypeFlags)buf->ReadByte(); + break; + + case 0x13: // Construction cost factor + rti->cost_multiplier = buf->ReadWord(); + break; + + case 0x14: // Speed limit + rti->max_speed = buf->ReadWord(); + break; + + case 0x16: // Map colour + rti->map_colour = buf->ReadByte(); + break; + + case 0x17: // Introduction date + rti->introduction_date = buf->ReadDWord(); + break; + + case 0x1A: // Sort order + rti->sorting_order = buf->ReadByte(); + break; + + case 0x1B: // Name of roadtype + AddStringForMapping(buf->ReadWord(), &rti->strings.name); + break; + + case 0x1C: // Maintenance cost factor + rti->maintenance_multiplier = buf->ReadWord(); + break; + + case 0x1D: // Alternate road type label list + /* Skipped here as this is loaded during reservation stage. */ + for (int j = buf->ReadByte(); j != 0; j--) buf->ReadDWord(); + break; + + default: + ret = CIR_UNKNOWN; + break; + } + } + + return ret; +} + +static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf) +{ + return RoadTypeChangeInfo(id, numinfo, prop, buf, RTT_ROAD); +} + +static ChangeInfoResult TramTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf) +{ + return RoadTypeChangeInfo(id, numinfo, prop, buf, RTT_TRAM); +} + + +static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf, RoadTramType rtt) +{ + ChangeInfoResult ret = CIR_SUCCESS; + + extern RoadTypeInfo _roadtypes[ROADTYPE_END]; + RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map; + + if (id + numinfo > ROADTYPE_END) { + grfmsg(1, "RoadTypeReserveInfo: Road type %u is invalid, max %u, ignoring", id + numinfo, ROADTYPE_END); + return CIR_INVALID_ID; + } + + for (int i = 0; i < numinfo; i++) { + switch (prop) { + case 0x08: { // Label of road type + RoadTypeLabel rtl = buf->ReadDWord(); + rtl = BSWAP32(rtl); + + RoadType rt = GetRoadTypeByLabel(rtl, false); + if (rt == INVALID_ROADTYPE) { + /* Set up new road type */ + rt = AllocateRoadType(rtl, rtt); + } else if (GetRoadTramType(rt) != rtt) { + grfmsg(1, "RoadTypeReserveInfo: Road type %u is invalid type (road/tram), ignoring", id + numinfo); + return CIR_INVALID_ID; + } + + type_map[id + i] = rt; + break; + } + case 0x09: // Toolbar caption of roadtype + case 0x0A: // Menu text + case 0x0B: // Build window caption + case 0x0C: // Autoreplace text + case 0x0D: // New loco + case 0x13: // Construction cost + case 0x14: // Speed limit + case 0x1B: // Name of roadtype + case 0x1C: // Maintenance cost factor + buf->ReadWord(); + break; + + case 0x1D: // Alternate road type label list + if (type_map[id + i] != INVALID_ROADTYPE) { + int n = buf->ReadByte(); + for (int j = 0; j != n; j++) { + _roadtypes[type_map[id + i]].alternate_labels.push_back(BSWAP32(buf->ReadDWord())); + } + break; + } + grfmsg(1, "RoadTypeReserveInfo: Ignoring property 1D for road type %u because no label was set", id + i); + /* FALL THROUGH */ + + case 0x0F: // Powered roadtype list + case 0x18: // Roadtype list required for date introduction + case 0x19: // Introduced roadtype list + for (int j = buf->ReadByte(); j != 0; j--) buf->ReadDWord(); + break; + + case 0x10: // Road Type flags + case 0x12: // Station graphic + case 0x15: // Acceleration model + case 0x16: // Map colour + case 0x1A: // Sort order + buf->ReadByte(); + break; + + case 0x17: // Introduction date + buf->ReadDWord(); + break; + + default: + ret = CIR_UNKNOWN; + break; + } + } + + return ret; +} + +static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf) +{ + return RoadTypeReserveInfo(id, numinfo, prop, buf, RTT_ROAD); +} + +static ChangeInfoResult TramTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf) +{ + return RoadTypeReserveInfo(id, numinfo, prop, buf, RTT_TRAM); +} + static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int prop, ByteReader *buf) { ChangeInfoResult ret = CIR_SUCCESS; @@ -4520,6 +4762,8 @@ static void FeatureChangeInfo(ByteReader *buf) /* GSF_OBJECTS */ ObjectChangeInfo, /* GSF_RAILTYPES */ RailTypeChangeInfo, /* GSF_AIRPORTTILES */ AirportTilesChangeInfo, + /* GSF_ROADTYPES */ RoadTypeChangeInfo, + /* GSF_TRAMTYPES */ TramTypeChangeInfo, }; uint8 feature = buf->ReadByte(); @@ -4593,7 +4837,7 @@ static void ReserveChangeInfo(ByteReader *buf) { uint8 feature = buf->ReadByte(); - if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES) return; + if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES && feature != GSF_ROADTYPES && feature != GSF_TRAMTYPES) return; uint8 numprops = buf->ReadByte(); uint8 numinfo = buf->ReadByte(); @@ -4616,6 +4860,14 @@ static void ReserveChangeInfo(ByteReader *buf) case GSF_RAILTYPES: cir = RailTypeReserveInfo(index, numinfo, prop, buf); break; + + case GSF_ROADTYPES: + cir = RoadTypeReserveInfo(index, numinfo, prop, buf); + break; + + case GSF_TRAMTYPES: + cir = TramTypeReserveInfo(index, numinfo, prop, buf); + break; } if (HandleChangeInfoResult("ReserveChangeInfo", cir, feature, prop)) return; @@ -4928,6 +5180,8 @@ static void NewSpriteGroup(ByteReader *buf) case GSF_CARGOES: case GSF_AIRPORTS: case GSF_RAILTYPES: + case GSF_ROADTYPES: + case GSF_TRAMTYPES: { byte num_loaded = type; byte num_loading = buf->ReadByte(); @@ -5505,6 +5759,39 @@ static void RailTypeMapSpriteGroup(ByteReader *buf, uint8 idcount) buf->ReadWord(); } +static void RoadTypeMapSpriteGroup(ByteReader *buf, uint8 idcount, RoadTramType rtt) +{ + RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map; + + uint8 *roadtypes = AllocaM(uint8, idcount); + for (uint i = 0; i < idcount; i++) { + uint8 id = buf->ReadByte(); + roadtypes[i] = id < ROADTYPE_END ? type_map[id] : INVALID_ROADTYPE; + } + + uint8 cidcount = buf->ReadByte(); + for (uint c = 0; c < cidcount; c++) { + uint8 ctype = buf->ReadByte(); + uint16 groupid = buf->ReadWord(); + if (!IsValidGroupID(groupid, "RoadTypeMapSpriteGroup")) continue; + + if (ctype >= ROTSG_END) continue; + + extern RoadTypeInfo _roadtypes[ROADTYPE_END]; + for (uint i = 0; i < idcount; i++) { + if (roadtypes[i] != INVALID_ROADTYPE) { + RoadTypeInfo *rti = &_roadtypes[roadtypes[i]]; + + rti->grffile[ctype] = _cur.grffile; + rti->group[ctype] = _cur.spritegroups[groupid]; + } + } + } + + /* Roadtypes do not use the default group. */ + buf->ReadWord(); +} + static void AirportMapSpriteGroup(ByteReader *buf, uint8 idcount) { uint8 *airports = AllocaM(uint8, idcount); @@ -5655,6 +5942,14 @@ static void FeatureMapSpriteGroup(ByteReader *buf) RailTypeMapSpriteGroup(buf, idcount); break; + case GSF_ROADTYPES: + RoadTypeMapSpriteGroup(buf, idcount, RTT_ROAD); + break; + + case GSF_TRAMTYPES: + RoadTypeMapSpriteGroup(buf, idcount, RTT_TRAM); + break; + case GSF_AIRPORTTILES: AirportTileMapSpriteGroup(buf, idcount); return; @@ -5917,13 +6212,20 @@ static void GraphicsNew(ByteReader *buf) /* Load sprites starting from , then skip sprites. */ grfmsg(2, "GraphicsNew: Replacing sprites %d to %d of %s (type 0x%02X) at SpriteID 0x%04X", offset, offset + num - 1, action5_type->name, type, replace); + if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5; + + if (type == 0x0B) { + static const SpriteID depot_with_track_offset = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_TRAMWAY_BASE; + static const SpriteID depot_no_track_offset = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_TRAMWAY_BASE; + if (offset <= depot_with_track_offset && offset + num > depot_with_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_WITH_TRACK; + if (offset <= depot_no_track_offset && offset + num > depot_no_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NO_TRACK; + } + for (; num > 0; num--) { _cur.nfo_line++; LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); } - if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5; - _cur.skip_sprites = skip_num; } @@ -6349,6 +6651,14 @@ static void SkipIf(ByteReader *buf) break; case 0x0E: result = GetRailTypeByLabel(BSWAP32(cond_val)) != INVALID_RAILTYPE; break; + case 0x0F: result = GetRoadTypeByLabel(BSWAP32(cond_val)) == INVALID_ROADTYPE; + break; + case 0x10: result = GetRoadTypeByLabel(BSWAP32(cond_val)) != INVALID_ROADTYPE; + break; + case 0x11: result = GetRoadTypeByLabel(BSWAP32(cond_val)) == INVALID_ROADTYPE; + break; + case 0x12: result = GetRoadTypeByLabel(BSWAP32(cond_val)) != INVALID_ROADTYPE; + break; default: grfmsg(1, "SkipIf: Unsupported condition type %02X. Ignoring", condtype); return; } @@ -8269,6 +8579,9 @@ void ResetNewGRFData() /* Reset rail type information */ ResetRailTypes(); + /* Copy/reset original road type info data */ + ResetRoadTypes(); + /* Allocate temporary refit/cargo class data */ _gted = CallocT(Engine::GetPoolSize()); @@ -8337,6 +8650,7 @@ void ResetNewGRFData() _loaded_newgrf_features.has_newhouses = false; _loaded_newgrf_features.has_newindustries = false; _loaded_newgrf_features.shore = SHORE_REPLACE_NONE; + _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NONE; /* Clear all GRF overrides */ _grf_id_overrides.clear(); @@ -8424,6 +8738,14 @@ GRFFile::GRFFile(const GRFConfig *config) this->railtype_map[2] = RAILTYPE_MONO; this->railtype_map[3] = RAILTYPE_MAGLEV; + /* Initialise road type map with default road types */ + memset(this->roadtype_map, INVALID_ROADTYPE, sizeof(this->roadtype_map)); + this->roadtype_map[0] = ROADTYPE_ROAD; + + /* Initialise tram type map with default tram types */ + memset(this->tramtype_map, INVALID_ROADTYPE, sizeof(this->tramtype_map)); + this->tramtype_map[0] = ROADTYPE_TRAM; + /* Copy the initial parameter list * 'Uninitialised' parameters are zeroed as that is their default value when dynamically creating them. */ assert_compile(lengthof(this->param) == lengthof(config->param) && lengthof(this->param) == 0x80); @@ -9193,6 +9515,21 @@ static void ActivateOldShore() } } +/** + * Replocate the old tram depot sprites to the new position, if no new ones were loaded. + */ +static void ActivateOldTramDepot() +{ + if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK) { + DupSprite(SPR_ROAD_DEPOT + 0, SPR_TRAMWAY_DEPOT_NO_TRACK + 0); // use road depot graphics for "no tracks" + DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 1, SPR_TRAMWAY_DEPOT_NO_TRACK + 1); + DupSprite(SPR_ROAD_DEPOT + 2, SPR_TRAMWAY_DEPOT_NO_TRACK + 2); // use road depot graphics for "no tracks" + DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 3, SPR_TRAMWAY_DEPOT_NO_TRACK + 3); + DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 4, SPR_TRAMWAY_DEPOT_NO_TRACK + 4); + DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 5, SPR_TRAMWAY_DEPOT_NO_TRACK + 5); + } +} + /** * Decide whether price base multipliers of grfs shall apply globally or only to the grf specifying them */ @@ -9371,8 +9708,12 @@ static void AfterLoadGRFs() /* Load old shore sprites in new position, if they were replaced by ActionA */ ActivateOldShore(); + /* Load old tram depot sprites in new position, if no new ones are present */ + ActivateOldTramDepot(); + /* Set up custom rail types */ InitRailTypes(); + InitRoadTypes(); Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { @@ -9380,6 +9721,31 @@ static void AfterLoadGRFs() /* Set RV maximum speed from the mph/0.8 unit value */ e->u.road.max_speed = _gted[e->index].rv_max_speed * 4; } + + RoadTramType rtt = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? RTT_TRAM : RTT_ROAD; + + const GRFFile *file = e->GetGRF(); + if (file == nullptr || _gted[e->index].roadtramtype == 0) { + e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; + continue; + } + + /* Remove +1 offset. */ + _gted[e->index].roadtramtype--; + + const std::vector *list = (rtt == RTT_TRAM) ? &file->tramtype_list : &file->roadtype_list; + if (_gted[e->index].roadtramtype < list->size()) + { + RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype]; + RoadType rt = GetRoadTypeByLabel(rtl); + if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) { + e->u.road.roadtype = rt; + continue; + } + } + + /* Road type is not available, so disable this engine */ + e->info.climates = 0; } FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) { diff --git a/src/newgrf.h b/src/newgrf.h index c36005b0f2..5d76a33642 100644 --- a/src/newgrf.h +++ b/src/newgrf.h @@ -14,6 +14,7 @@ #include "cargotype.h" #include "rail_type.h" +#include "road_type.h" #include "fileio_type.h" #include "core/bitmath_func.hpp" #include "core/alloc_type.hpp" @@ -83,6 +84,8 @@ enum GrfSpecFeature { GSF_OBJECTS, GSF_RAILTYPES, GSF_AIRPORTTILES, + GSF_ROADTYPES, + GSF_TRAMTYPES, GSF_END, GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope) @@ -128,6 +131,12 @@ struct GRFFile : ZeroedMemoryAllocator { std::vector railtype_list; ///< Railtype translation table RailType railtype_map[RAILTYPE_END]; + std::vector roadtype_list; ///< Roadtype translation table (road) + RoadType roadtype_map[ROADTYPE_END]; + + std::vector tramtype_list; ///, Roadtype translation table (tram) + RoadType tramtype_map[ROADTYPE_END]; + CanalProperties canal_local_properties[CF_END]; ///< Canal properties as set by this NewGRF struct LanguageMap *language_map; ///< Mappings related to the languages. @@ -158,12 +167,19 @@ enum ShoreReplacement { SHORE_REPLACE_ONLY_NEW, ///< Only corner-shores were loaded by Action5 (openttd(w/d).grf only). }; +enum TramReplacement { + TRAMWAY_REPLACE_DEPOT_NONE, ///< No tram depot graphics were loaded. + TRAMWAY_REPLACE_DEPOT_WITH_TRACK, ///< Electrified depot graphics with tram track were loaded. + TRAMWAY_REPLACE_DEPOT_NO_TRACK, ///< Electrified depot graphics without tram track were loaded. +}; + struct GRFLoadedFeatures { bool has_2CC; ///< Set if any vehicle is loaded which uses 2cc (two company colours). uint64 used_liveries; ///< Bitmask of #LiveryScheme used by the defined engines. bool has_newhouses; ///< Set if there are any newhouses loaded. bool has_newindustries; ///< Set if there are any newindustries loaded. ShoreReplacement shore; ///< In which way shore sprites were replaced. + TramReplacement tram; ///< In which way tram depots were replaced. }; /** diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 3a43188731..955e4ac31b 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -23,6 +23,7 @@ #include "station_base.h" #include "company_base.h" #include "newgrf_railtype.h" +#include "newgrf_roadtype.h" #include "ship.h" #include "safeguards.h" @@ -606,11 +607,21 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object, case 0x48: return v->GetEngine()->flags; // Vehicle Type Info case 0x49: return v->build_year; - case 0x4A: { - if (v->type != VEH_TRAIN) return 0; - RailType rt = GetTileRailType(v->tile); - return (HasPowerOnRail(Train::From(v)->railtype, rt) ? 0x100 : 0) | GetReverseRailTypeTranslation(rt, object->ro.grffile); - } + case 0x4A: + switch (v->type) { + case VEH_TRAIN: { + RailType rt = GetTileRailType(v->tile); + return (HasPowerOnRail(Train::From(v)->railtype, rt) ? 0x100 : 0) | GetReverseRailTypeTranslation(rt, object->ro.grffile); + } + + case VEH_ROAD: { + RoadType rt = GetRoadType(v->tile, GetRoadTramType(RoadVehicle::From(v)->roadtype)); + return 0x100 | GetReverseRoadTypeTranslation(rt, object->ro.grffile); + } + + default: + return 0; + } case 0x4B: // Long date of last service return v->date_of_last_service; diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp new file mode 100644 index 0000000000..1fb8e5ec1a --- /dev/null +++ b/src/newgrf_roadtype.cpp @@ -0,0 +1,143 @@ +/* $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 . + */ + +/** @file newgrf_roadtype.cpp NewGRF handling of road types. */ + +#include "stdafx.h" +#include "debug.h" +#include "newgrf_roadtype.h" +#include "date_func.h" +#include "depot_base.h" +#include "town.h" + +#include "safeguards.h" + +/* virtual */ uint32 RoadTypeScopeResolver::GetRandomBits() const +{ + uint tmp = CountBits(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE); + return GB(tmp, 0, 2); +} + +/* virtual */ uint32 RoadTypeScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const +{ + if (this->tile == INVALID_TILE) { + switch (variable) { + case 0x40: return 0; + case 0x41: return 0; + case 0x42: return 0; + case 0x43: return _date; + case 0x44: return HZB_TOWN_EDGE; + } + } + + switch (variable) { + case 0x40: return GetTerrainType(this->tile, this->context); + case 0x41: return 0; + case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile); + case 0x43: + if (IsRoadDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date; + return _date; + case 0x44: { + const Town *t = nullptr; + if (IsRoadDepotTile(this->tile)) { + t = Depot::GetByTile(this->tile)->town; + } else if (IsTileType(this->tile, MP_ROAD)) { + t = ClosestTownFromTile(this->tile, UINT_MAX); + } + return t != nullptr ? GetTownRadiusGroup(t, this->tile) : HZB_TOWN_EDGE; + } + } + + DEBUG(grf, 1, "Unhandled road type tile variable 0x%X", variable); + + *available = false; + return UINT_MAX; +} + +/* virtual */ const SpriteGroup *RoadTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const +{ + if (group->num_loading > 0) return group->loading[0]; + if (group->num_loaded > 0) return group->loaded[0]; + return nullptr; +} + +/** + * Constructor of the roadtype scope resolvers. + * @param ro Surrounding resolver. + * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead. + * @param context Are we resolving sprites for the upper halftile, or on a bridge? + */ +RoadTypeScopeResolver::RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context) : ScopeResolver(ro) +{ + this->tile = tile; + this->context = context; +} + +/** + * Resolver object for road types. + * @param rti Roadtype. nullptr in NewGRF Inspect window. + * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead. + * @param context Are we resolving sprites for the upper halftile, or on a bridge? + * @param rtsg Roadpart of interest + * @param param1 Extra parameter (first parameter of the callback, except roadtypes do not have callbacks). + * @param param2 Extra parameter (second parameter of the callback, except roadtypes do not have callbacks). + */ +RoadTypeResolverObject::RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1, uint32 param2) + : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), roadtype_scope(*this, tile, context) +{ + this->root_spritegroup = rti != nullptr ? rti->group[rtsg] : nullptr; +} + +/** + * Get the sprite to draw for the given tile. + * @param rti The road type data (spec). + * @param tile The tile to get the sprite for. + * @param rtsg The type of sprite to draw. + * @param content Where are we drawing the tile? + * @param [out] num_results If not nullptr, return the number of sprites in the spriteset. + * @return The sprite to draw. + */ +SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results) +{ + assert(rtsg < ROTSG_END); + + if (rti->group[rtsg] == nullptr) return 0; + + RoadTypeResolverObject object(rti, tile, context, rtsg); + const SpriteGroup *group = object.Resolve(); + if (group == nullptr || group->GetNumResults() == 0) return 0; + + if (num_results) *num_results = group->GetNumResults(); + + return group->GetResult(); +} + +/** + * Perform a reverse roadtype lookup to get the GRF internal ID. + * @param roadtype The global (OpenTTD) roadtype. + * @param grffile The GRF to do the lookup for. + * @return the GRF internal ID. + */ +uint8 GetReverseRoadTypeTranslation(RoadType roadtype, const GRFFile *grffile) +{ + /* No road type table present, return road type as-is */ + if (grffile == nullptr) return roadtype; + + const std::vector *list = RoadTypeIsRoad(roadtype) ? &grffile->roadtype_list : &grffile->tramtype_list; + if (list->size() == 0) return roadtype; + + /* Look for a matching road type label in the table */ + RoadTypeLabel label = GetRoadTypeInfo(roadtype)->label; + + int index = find_index(*list, label); + if (index >= 0) return index; + + /* If not found, return as invalid */ + return 0xFF; +} diff --git a/src/newgrf_roadtype.h b/src/newgrf_roadtype.h new file mode 100644 index 0000000000..2da7a82c1f --- /dev/null +++ b/src/newgrf_roadtype.h @@ -0,0 +1,51 @@ +/* $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 . + */ + +/** @file newgrf_roadtype.h NewGRF handling of road types. */ + +#ifndef NEWGRF_ROADTYPE_H +#define NEWGRF_ROADTYPE_H + +#include "road.h" +#include "newgrf_commons.h" +#include "newgrf_spritegroup.h" + +/** Resolver for the railtype scope. */ +struct RoadTypeScopeResolver : public ScopeResolver { + TileIndex tile; ///< Tracktile. For track on a bridge this is the southern bridgehead. + TileContext context; ///< Are we resolving sprites for the upper halftile, or on a bridge? + + RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context); + + /* virtual */ uint32 GetRandomBits() const; + /* virtual */ uint32 GetVariable(byte variable, uint32 parameter, bool *available) const; +}; + +/** Resolver object for road types. */ +struct RoadTypeResolverObject : public ResolverObject { + RoadTypeScopeResolver roadtype_scope; ///< Resolver for the roadtype scope. + + RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1 = 0, uint32 param2 = 0); + + /* virtual */ ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) + { + switch (scope) { + case VSG_SCOPE_SELF: return &this->roadtype_scope; + default: return ResolverObject::GetScope(scope, relative); + } + } + + /* virtual */ const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const; +}; + +SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr); + +uint8 GetReverseRoadTypeTranslation(RoadType roadtype, const GRFFile *grffile); + +#endif /* NEWGRF_ROADTYPE_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 243302d619..8ca080989f 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -53,6 +53,7 @@ #include "engine_func.h" #include "core/random_func.hpp" #include "rail_gui.h" +#include "road_gui.h" #include "core/backup_type.hpp" #include "hotkeys.h" #include "newgrf.h" @@ -949,6 +950,7 @@ static void MakeNewGameDone() SetLocalCompany(COMPANY_FIRST); InitializeRailGUI(); + InitializeRoadGUI(); /* We are the server, we start a new company (not dedicated), * so set the default password *if* needed. */ diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 999f7f94de..31a7816509 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -32,7 +32,7 @@ struct CFollowTrackT enum ErrorCode { EC_NONE, EC_OWNER, - EC_RAIL_TYPE, + EC_RAIL_ROAD_TYPE, EC_90DEG, EC_NO_WAY, EC_RESERVED, @@ -60,6 +60,7 @@ struct CFollowTrackT inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr) { + assert(IsRailTT()); m_veh = nullptr; Init(o, railtype_override, pPerf); } @@ -92,7 +93,7 @@ struct CFollowTrackT inline static TransportType TT() { return Ttr_type_; } inline static bool IsWaterTT() { return TT() == TRANSPORT_WATER; } inline static bool IsRailTT() { return TT() == TRANSPORT_RAIL; } - inline bool IsTram() { return IsRoadTT() && HasBit(RoadVehicle::From(m_veh)->compatible_roadtypes, ROADTYPE_TRAM); } + inline bool IsTram() { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(m_veh)->roadtype); } inline static bool IsRoadTT() { return TT() == TRANSPORT_ROAD; } inline static bool Allow90degTurns() { return T90deg_turns_allowed_; } inline static bool DoTrackMasking() { return Tmask_reserved_tracks; } @@ -103,7 +104,7 @@ struct CFollowTrackT assert(IsTram()); // this function shouldn't be called in other cases if (IsNormalRoadTile(tile)) { - RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM); + RoadBits rb = GetRoadBits(tile, RTT_TRAM); switch (rb) { case ROAD_NW: return DIAGDIR_NW; case ROAD_SW: return DIAGDIR_SW; @@ -126,7 +127,7 @@ struct CFollowTrackT m_err = EC_NONE; assert( ((TrackStatusToTrackdirBits( - GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != nullptr) ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0) + GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != nullptr) ? (this->IsTram() ? RTT_TRAM : RTT_ROAD) : 0) ) & TrackdirToTrackdirBits(m_old_td)) != 0) || (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR) // Disable the assertion for single tram bits ); @@ -151,7 +152,7 @@ struct CFollowTrackT if (IsRoadTT() && !IsTram() && TryReverse()) return true; /* CanEnterNewTile already set a reason. - * Do NOT overwrite it (important for example for EC_RAIL_TYPE). + * Do NOT overwrite it (important for example for EC_RAIL_ROAD_TYPE). * Only set a reason if CanEnterNewTile was not called */ if (m_new_td_bits == TRACKDIR_BIT_NONE) m_err = EC_NO_WAY; @@ -241,7 +242,7 @@ protected: if (IsRailTT() && IsPlainRailTile(m_new_tile)) { m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101); } else { - m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), IsRoadTT() ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)); + m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), IsRoadTT() ? (this->IsTram() ? RTT_TRAM : RTT_ROAD) : 0)); if (IsTram() && m_new_td_bits == TRACKDIR_BIT_NONE) { /* GetTileTrackStatus() returns 0 for single tram bits. @@ -350,7 +351,18 @@ protected: RailType rail_type = GetTileRailType(m_new_tile); if (!HasBit(m_railtypes, rail_type)) { /* incompatible rail type */ - m_err = EC_RAIL_TYPE; + m_err = EC_RAIL_ROAD_TYPE; + return false; + } + } + + /* road transport is possible only on compatible road types */ + if (IsRoadTT()) { + const RoadVehicle *v = RoadVehicle::From(m_veh); + RoadType roadtype = GetRoadType(m_new_tile, GetRoadTramType(v->roadtype)); + if (!HasBit(v->compatible_roadtypes, roadtype)) { + /* incompatible road type */ + m_err = EC_RAIL_ROAD_TYPE; return false; } } diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 36f66b5a93..2cf3338902 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -43,6 +43,7 @@ struct AyStarUserData { TransportType type; RailTypes railtypes; RoadTypes roadtypes; + uint subtype; }; /** Indices into AyStarNode.userdata[] */ @@ -719,7 +720,7 @@ static DiagDirection GetDepotDirection(TileIndex tile, TransportType type) static DiagDirection GetSingleTramBit(TileIndex tile) { if (IsNormalRoadTile(tile)) { - RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM); + RoadBits rb = GetRoadBits(tile, RTT_TRAM); switch (rb) { case ROAD_NW: return DIAGDIR_NW; case ROAD_SW: return DIAGDIR_SW; @@ -747,7 +748,7 @@ static DiagDirection GetTileSingleEntry(TileIndex tile, TransportType type, uint if (type == TRANSPORT_ROAD) { if (IsStandardRoadStopTile(tile)) return GetRoadStopDir(tile); - if (HasBit(subtype, ROADTYPE_TRAM)) return GetSingleTramBit(tile); + if ((RoadTramType)subtype == RTT_TRAM) return GetSingleTramBit(tile); } return INVALID_DIAGDIR; @@ -785,13 +786,24 @@ static bool CanEnterTile(TileIndex tile, DiagDirection dir, AyStarUserData *user if (!CanEnterTileOwnerCheck(user->owner, tile, dir)) return false; /* check correct rail type (mono, maglev, etc) */ - if (user->type == TRANSPORT_RAIL) { - RailType rail_type = GetTileRailType(tile); - if (!HasBit(user->railtypes, rail_type)) return false; + switch (user->type) { + case TRANSPORT_RAIL: { + RailType rail_type = GetTileRailType(tile); + if (!HasBit(user->railtypes, rail_type)) return false; + break; + } + + case TRANSPORT_ROAD: { + RoadType road_type = GetRoadType(tile, (RoadTramType)user->subtype); + if (!HasBit(user->roadtypes, road_type)) return false; + break; + } + + default: break; } /* Depots, standard roadstops and single tram bits can only be entered from one direction */ - DiagDirection single_entry = GetTileSingleEntry(tile, user->type, user->roadtypes); + DiagDirection single_entry = GetTileSingleEntry(tile, user->type, user->subtype); if (single_entry != INVALID_DIAGDIR && single_entry != ReverseDiagDir(dir)) return false; return true; @@ -813,7 +825,7 @@ static TrackdirBits GetDriveableTrackdirBits(TileIndex dst_tile, TileIndex src_t { TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(dst_tile, type, subtype)); - if (trackdirbits == TRACKDIR_BIT_NONE && type == TRANSPORT_ROAD && HasBit(subtype, ROADTYPE_TRAM)) { + if (trackdirbits == TRACKDIR_BIT_NONE && type == TRANSPORT_ROAD && (RoadTramType)subtype == RTT_TRAM) { /* GetTileTrackStatus() returns 0 for single tram bits. * As we cannot change it there (easily) without breaking something, change it here */ switch (GetSingleTramBit(dst_tile)) { @@ -863,7 +875,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) /* Information about the vehicle: TransportType (road/rail/water) and SubType (compatible rail/road types) */ TransportType type = user->type; - uint subtype = user->roadtypes; + uint subtype = user->subtype; /* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */ aystar->num_neighbours = 0; @@ -894,11 +906,11 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) /* We leave src_tile in src_exitdir and reach dst_tile */ dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir)); - if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, user)) dst_tile = INVALID_TILE; + if (dst_tile != INVALID_TILE && IsNormalRoadTile(dst_tile) && !CanEnterTile(dst_tile, src_exitdir, user)) dst_tile = INVALID_TILE; if (dst_tile == INVALID_TILE) { /* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */ - if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return; + if (type != TRANSPORT_ROAD || (RoadTramType)subtype == RTT_TRAM) return; dst_tile = src_tile; src_trackdir = ReverseTrackdir(src_trackdir); @@ -908,7 +920,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) if (trackdirbits == TRACKDIR_BIT_NONE) { /* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */ - if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return; + if (type != TRANSPORT_ROAD || (RoadTramType)subtype == RTT_TRAM) return; dst_tile = src_tile; src_trackdir = ReverseTrackdir(src_trackdir); @@ -1120,7 +1132,7 @@ FindDepotData NPFRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_penal { Trackdir trackdir = v->GetVehicleTrackdir(); - AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes }; + AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes, GetRoadTramType(v->roadtype) }; NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, INVALID_TILE, INVALID_TRACKDIR, false, nullptr, &user, 0, max_penalty); if (ftd.best_bird_dist != 0) return FindDepotData(); @@ -1140,7 +1152,7 @@ Trackdir NPFRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDir NPFFillWithOrderData(&fstd, v); Trackdir trackdir = DiagDirToDiagTrackdir(enterdir); - AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes }; + AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes, GetRoadTramType(v->roadtype) }; NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, &user); assert(ftd.best_trackdir != INVALID_TRACKDIR); @@ -1163,7 +1175,7 @@ Track NPFShipChooseTrack(const Ship *v, bool &path_found) NPFFillWithOrderData(&fstd, v); - AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE, 0 }; NPFFoundTargetData ftd = NPFRouteToStationOrTile(v->tile, trackdir, true, &fstd, &user); assert(ftd.best_trackdir != INVALID_TRACKDIR); @@ -1188,7 +1200,7 @@ bool NPFShipCheckReverse(const Ship *v) assert(trackdir != INVALID_TRACKDIR); assert(trackdir_rev != INVALID_TRACKDIR); - AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE, 0 }; ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, v->tile, trackdir_rev, false, &fstd, &user); /* If we didn't find anything, just keep on going straight ahead, otherwise take the reverse flag */ return ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE); @@ -1206,7 +1218,7 @@ FindDepotData NPFTrainFindNearestDepot(const Train *v, int max_penalty) fstd.reserve_path = false; assert(trackdir != INVALID_TRACKDIR); - AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 }; NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, &fstd, &user, NPF_INFINITE_PENALTY, max_penalty); if (ftd.best_bird_dist != 0) return FindDepotData(); @@ -1235,7 +1247,7 @@ bool NPFTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir trackd /* perform a breadth first search. Target is nullptr, * since we are just looking for any safe tile...*/ - AyStarUserData user = { v->owner, TRANSPORT_RAIL, railtypes, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_RAIL, railtypes, ROADTYPES_NONE, 0 }; return NPFRouteInternal(&start1, true, nullptr, false, &fstd, NPFFindSafeTile, NPFCalcZero, &user, 0, true).res_okay; } @@ -1252,7 +1264,7 @@ bool NPFTrainCheckReverse(const Train *v) assert(trackdir != INVALID_TRACKDIR); assert(trackdir_rev != INVALID_TRACKDIR); - AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 }; ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, &fstd, &user); /* If we didn't find anything, just keep on going straight ahead, otherwise take the reverse flag */ return ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE); @@ -1266,7 +1278,7 @@ Track NPFTrainChooseTrack(const Train *v, bool &path_found, bool reserve_track, PBSTileInfo origin = FollowTrainReservation(v); assert(IsValidTrackdir(origin.trackdir)); - AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE }; + AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 }; NPFFoundTargetData ftd = NPFRouteToStationOrTile(origin.tile, origin.trackdir, true, &fstd, &user); if (target != nullptr) { diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 6c1c3ca7a0..9613fd0797 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -496,7 +496,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th if (!tf_local.Follow(cur.tile, cur.td)) { assert(tf_local.m_err != TrackFollower::EC_NONE); /* Can't move to the next tile (EOL?). */ - if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) { + if (tf_local.m_err == TrackFollower::EC_RAIL_ROAD_TYPE) { end_segment_reason |= ESRB_RAIL_TYPE; } else { end_segment_reason |= ESRB_DEAD_END; diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 9e206115f9..3935ba18d3 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -249,7 +249,7 @@ public: } else { m_dest_station = INVALID_STATION; m_destTile = v->dest_tile; - m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, v->compatible_roadtypes)); + m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))); } } @@ -367,7 +367,7 @@ public: /* our source tile will be the next vehicle tile (should be the given one) */ TileIndex src_tile = tile; /* get available trackdirs on the start tile */ - TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)); + TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))); /* select reachable trackdirs only */ src_trackdirs &= DiagdirReachesTrackdirs(enterdir); @@ -449,7 +449,7 @@ public: /* set origin (tile, trackdir) */ TileIndex src_tile = v->tile; Trackdir src_td = v->GetVehicleTrackdir(); - if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)), src_td)) { + if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, this->IsTram() ? RTT_TRAM : RTT_ROAD)), src_td)) { /* sometimes the roadveh is not on the road (it resides on non-existing track) * how should we handle that situation? */ return false; @@ -529,7 +529,7 @@ FindDepotData YapfRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_dist { TileIndex tile = v->tile; Trackdir trackdir = v->GetVehicleTrackdir(); - if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)), trackdir)) { + if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))), trackdir)) { return FindDepotData(); } diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 0804a4d14a..ab87fc40fd 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -512,41 +512,48 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (GetDisallowedRoadDirections(tile) != DRD_NONE) return_cmd_error(STR_ERROR_CROSSING_ON_ONEWAY_ROAD); - if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED); + if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_RAIL); - RoadTypes roadtypes = GetRoadTypes(tile); - RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); - RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); + RoadType roadtype_road = GetRoadTypeRoad(tile); + RoadType roadtype_tram = GetRoadTypeTram(tile); + + if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD); + if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD); + + RoadBits road = GetRoadBits(tile, RTT_ROAD); + RoadBits tram = GetRoadBits(tile, RTT_TRAM); if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) || (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) { - Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + Owner road_owner = GetRoadOwner(tile, RTT_ROAD); + Owner tram_owner = GetRoadOwner(tile, RTT_TRAM); /* Disallow breaking end-of-line of someone else * so trams can still reverse on this tile. */ if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) { CommandCost ret = CheckOwnership(tram_owner); if (ret.Failed()) return ret; } - /* Crossings must always have a road... */ - uint num_new_road_pieces = 2 - CountBits(road); - if (road == ROAD_NONE) road_owner = _current_company; - roadtypes |= ROADTYPES_ROAD; - /* ...but tram is not required. */ - uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0; - cost.AddCost((num_new_road_pieces + num_new_tram_pieces) * _price[PR_BUILD_ROAD]); + uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 0; + if (num_new_road_pieces > 0) { + cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road)); + } + + uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0; + if (num_new_tram_pieces > 0) { + cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram)); + } 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, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile)); UpdateLevelCrossing(tile, false); Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) { - Company::Get(road_owner)->infrastructure.road[ROADTYPE_ROAD] += num_new_road_pieces; + Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces; DirtyCompanyInfrastructureWindows(road_owner); } if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) { - Company::Get(tram_owner)->infrastructure.road[ROADTYPE_TRAM] += num_new_tram_pieces; + Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces; DirtyCompanyInfrastructureWindows(tram_owner); } } @@ -644,10 +651,11 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, v = GetTrainForReservation(tile, track); if (v != nullptr) FreeTrainTrackReservation(v); } + owner = GetTileOwner(tile); Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(owner); - MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypes(tile), GetTownIndex(tile), GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM)); + MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM)); DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile); } break; @@ -1579,7 +1587,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 case MP_ROAD: if (!IsLevelCrossing(tile)) continue; if (RailNoLevelCrossings(totype)) { - error.MakeError(STR_ERROR_CROSSING_DISALLOWED); + error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL); continue; } break; diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 6d70686fde..b024625fa1 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -2042,5 +2042,11 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option) item->SetParam(1, rti->max_speed); list.emplace_back(item); } + + if (list.size() == 0) { + /* Empty dropdowns are not allowed */ + list.emplace_back(new DropDownListStringItem(STR_NONE, INVALID_RAILTYPE, true)); + } + return list; } diff --git a/src/road.cpp b/src/road.cpp index 3700629ef6..8c9591ff19 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -19,6 +19,9 @@ #include "engine_base.h" #include "date_func.h" #include "landscape.h" +#include "road.h" +#include "road_func.h" +#include "roadveh.h" #include "safeguards.h" @@ -72,7 +75,7 @@ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb) /* Always connective */ connective = true; } else { - const RoadBits neighbor_rb = GetAnyRoadBits(neighbor_tile, ROADTYPE_ROAD) | GetAnyRoadBits(neighbor_tile, ROADTYPE_TRAM); + const RoadBits neighbor_rb = GetAnyRoadBits(neighbor_tile, RTT_ROAD) | GetAnyRoadBits(neighbor_tile, RTT_TRAM); /* Accept only connective tiles */ connective = (neighbor_rb & mirrored_rb) != ROAD_NONE; @@ -102,53 +105,236 @@ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb) } /** - * Finds out, whether given company has all given RoadTypes available + * Finds out, whether given company has a given RoadType available for construction. * @param company ID of company - * @param rts RoadTypes to test - * @return true if company has all requested RoadTypes available + * @param roadtypet RoadType to test + * @return true if company has the requested RoadType available */ -bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts) +bool HasRoadTypeAvail(const CompanyID company, RoadType roadtype) { - RoadTypes avail_roadtypes; - if (company == OWNER_DEITY || company == OWNER_TOWN || _game_mode == GM_EDITOR || _generating_world) { - avail_roadtypes = ROADTYPES_ROAD; + return true; // TODO: should there be a proper check? } else { - Company *c = Company::GetIfValid(company); + const Company *c = Company::GetIfValid(company); if (c == nullptr) return false; - avail_roadtypes = (RoadTypes)c->avail_roadtypes | ROADTYPES_ROAD; // road is available for always for everybody + return HasBit(c->avail_roadtypes & ~_roadtypes_hidden_mask, roadtype); } - return (rts & ~avail_roadtypes) == 0; +} + +static RoadTypes GetMaskForRoadTramType(RoadTramType rtt) +{ + return rtt == RTT_TRAM ? _roadtypes_type : ~_roadtypes_type; +} + +/** + * Test if any buildable RoadType is available for a company. + * @param company the company in question + * @return true if company has any RoadTypes available + */ +bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt) +{ + return (Company::Get(company)->avail_roadtypes & ~_roadtypes_hidden_mask & GetMaskForRoadTramType(rtt)) != ROADTYPES_NONE; } /** * Validate functions for rail building. - * @param rt road type to check. + * @param roadtype road type to check. * @return true if the current company may build the road. */ -bool ValParamRoadType(const RoadType rt) +bool ValParamRoadType(RoadType roadtype) { - return HasRoadTypesAvail(_current_company, RoadTypeToRoadTypes(rt)); + return roadtype != INVALID_ROADTYPE && HasRoadTypeAvail(_current_company, roadtype); +} + +/** + * Add the road types that are to be introduced at the given date. + * @param rt Roadtype + * @param current The currently available roadtypes. + * @param date The date for the introduction comparisons. + * @return The road types that should be available when date + * introduced road types are taken into account as well. + */ +RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date) +{ + RoadTypes rts = current; + + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + /* Unused road type. */ + if (rti->label == 0) continue; + + /* Not date introduced. */ + if (!IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue; + + /* Not yet introduced at this date. */ + if (rti->introduction_date > date) continue; + + /* Have we introduced all required roadtypes? */ + RoadTypes required = rti->introduction_required_roadtypes; + if ((rts & required) != required) continue; + + rts |= rti->introduces_roadtypes; + } + + /* When we added roadtypes we need to run this method again; the added + * roadtypes might enable more rail types to become introduced. */ + return rts == current ? rts : AddDateIntroducedRoadTypes(rts, date); } /** * Get the road types the given company can build. - * @param company the company to get the roadtypes for. + * @param company the company to get the road types for. + * @param introduces If true, include road types introduced by other road types * @return the road types. */ -RoadTypes GetCompanyRoadtypes(CompanyID company) +RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces) { - RoadTypes rt = ROADTYPES_NONE; + RoadTypes rts = ROADTYPES_NONE; - Engine *e; + const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { const EngineInfo *ei = &e->info; if (HasBit(ei->climates, _settings_game.game_creation.landscape) && (HasBit(e->company_avail, company) || _date >= e->intro_date + DAYS_IN_YEAR)) { - SetBit(rt, HasBit(ei->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD); + const RoadVehicleInfo *rvi = &e->u.road; + assert(rvi->roadtype < ROADTYPE_END); + if (introduces) { + rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes; + } else { + SetBit(rts, rvi->roadtype); + } + } + } + + if (introduces) return AddDateIntroducedRoadTypes(rts, _date); + return rts; +} + +/** + * Get list of road types, regardless of company availability. + * @param introduces If true, include road types introduced by other road types + * @return the road types. + */ +RoadTypes GetRoadTypes(bool introduces) +{ + RoadTypes rts = ROADTYPES_NONE; + + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + const EngineInfo *ei = &e->info; + if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue; + + const RoadVehicleInfo *rvi = &e->u.road; + assert(rvi->roadtype < ROADTYPE_END); + if (introduces) { + rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes; + } else { + SetBit(rts, rvi->roadtype); } } - return rt; + if (introduces) return AddDateIntroducedRoadTypes(rts, MAX_DAY); + return rts; +} + +/** + * Get the road type for a given label. + * @param label the roadtype label. + * @param allow_alternate_labels Search in the alternate label lists as well. + * @return the roadtype. + */ +RoadType GetRoadTypeByLabel(RoadTypeLabel label, bool allow_alternate_labels) +{ + /* Loop through each road type until the label is found */ + for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) { + const RoadTypeInfo *rti = GetRoadTypeInfo(r); + if (rti->label == label) return r; + } + + if (allow_alternate_labels) { + /* Test if any road type defines the label as an alternate. */ + for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) { + const RoadTypeInfo *rti = GetRoadTypeInfo(r); + if (std::find(rti->alternate_labels.begin(), rti->alternate_labels.end(), label) != rti->alternate_labels.end()) return r; + } + } + + /* No matching label was found, so it is invalid */ + return INVALID_ROADTYPE; +} + +/** + * Returns the available RoadSubTypes for the provided RoadType + * If the given company is valid then will be returned a list of the available sub types at the current date, while passing + * a deity company will make all the sub types available + * @param rt the RoadType to filter + * @param c the company ID to check the roadtype against + * @param any_date whether to return only currently introduced roadtypes or also future ones + * @returns the existing RoadSubTypes + */ +RoadTypes ExistingRoadTypes(CompanyID c) +{ + /* Check only players which can actually own vehicles, editor and gamescripts are considered deities */ + if (c < OWNER_END) { + const Company *company = Company::GetIfValid(c); + if (company != nullptr) return company->avail_roadtypes; + } + + RoadTypes known_roadtypes = ROADTYPES_NONE; + + /* Find used roadtypes */ + Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + /* Check if the roadtype can be used in the current climate */ + if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue; + + /* Check whether available for all potential companies */ + if (e->company_avail != (CompanyMask)-1) continue; + + known_roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes; + } + + /* Get the date introduced roadtypes as well. */ + known_roadtypes = AddDateIntroducedRoadTypes(known_roadtypes, MAX_DAY); + + return known_roadtypes; +} + +/** + * Check whether we can build infrastructure for the given RoadType. This to disable building stations etc. when + * you are not allowed/able to have the RoadType yet. + * @param roadtype the roadtype to check this for + * @param company the company id to check this for + * @param any_date to check only existing vehicles or if it is possible to build them in the future + * @return true if there is any reason why you may build the infrastructure for the given roadtype + */ +bool CanBuildRoadTypeInfrastructure(RoadType roadtype, CompanyID company) +{ + if (_game_mode != GM_EDITOR && !Company::IsValidID(company)) return false; + if (!_settings_client.gui.disable_unsuitable_building) return true; + if (!HasAnyRoadTypesAvail(company, GetRoadTramType(roadtype))) return false; + + RoadTypes roadtypes = ExistingRoadTypes(company); + + /* Check if the filtered roadtypes does have the roadtype we are checking for + * and if we can build new ones */ + if (_settings_game.vehicle.max_roadveh > 0 && HasBit(roadtypes, roadtype)) { + /* Can we actually build the vehicle type? */ + const Engine *e; + FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { + if (!HasBit(e->company_avail, company)) continue; + if (HasPowerOnRoad(e->u.road.roadtype, roadtype) || HasPowerOnRoad(roadtype, e->u.road.roadtype)) return true; + } + return false; + } + + /* We should be able to build infrastructure when we have the actual vehicle type */ + const Vehicle *v; + FOR_ALL_VEHICLES(v) { + if (v->type == VEH_ROAD && (company == OWNER_DEITY || v->owner == company) && + HasBit(roadtypes, RoadVehicle::From(v)->roadtype) && HasPowerOnRoad(RoadVehicle::From(v)->roadtype, roadtype)) return true; + } + + return false; } diff --git a/src/road.h b/src/road.h new file mode 100644 index 0000000000..c359fc779c --- /dev/null +++ b/src/road.h @@ -0,0 +1,316 @@ +/* $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 . + */ + +/** @file road.h Road specific functions. */ + +#ifndef ROAD_H +#define ROAD_H + +#include "road_type.h" +#include "gfx_type.h" +#include "core/bitmath_func.hpp" +#include "strings_type.h" +#include "date_type.h" +#include "core/enum_type.hpp" +#include "newgrf.h" +#include "economy_func.h" + +#include + +enum RoadTramType : bool { + RTT_ROAD, + RTT_TRAM, +}; + +enum RoadTramTypes : uint8 { + RTTB_ROAD = 1 << RTT_ROAD, + RTTB_TRAM = 1 << RTT_TRAM, +}; +DECLARE_ENUM_AS_BIT_SET(RoadTramTypes) + +#define FOR_ALL_ROADTRAMTYPES(x) for (RoadTramType x : { RTT_ROAD, RTT_TRAM }) + +/** Roadtype flags. Starts with RO instead of R because R is used for rails */ +enum RoadTypeFlags { + ROTF_CATENARY = 0, ///< Bit number for adding catenary + ROTF_NO_LEVEL_CROSSING, ///< Bit number for disabling level crossing + ROTF_NO_HOUSES, ///< Bit number for setting this roadtype as not house friendly + ROTF_HIDDEN, ///< Bit number for hidden from construction. + ROTF_TOWN_BUILD, ///< Bit number for allowing towns to build this roadtype. + + ROTFB_NONE = 0, ///< All flags cleared. + ROTFB_CATENARY = 1 << ROTF_CATENARY, ///< Value for drawing a catenary. + ROTFB_NO_LEVEL_CROSSING = 1 << ROTF_NO_LEVEL_CROSSING, ///< Value for disabling a level crossing. + ROTFB_NO_HOUSES = 1 << ROTF_NO_HOUSES, ///< Value for for setting this roadtype as not house friendly. + ROTFB_HIDDEN = 1 << ROTF_HIDDEN, ///< Value for hidden from construction. + ROTFB_TOWN_BUILD = 1 << ROTF_TOWN_BUILD, ///< Value for allowing towns to build this roadtype. +}; +DECLARE_ENUM_AS_BIT_SET(RoadTypeFlags) + +struct SpriteGroup; + +/** Sprite groups for a roadtype. */ +enum RoadTypeSpriteGroup { + ROTSG_CURSORS, ///< Optional: Cursor and toolbar icon images + ROTSG_OVERLAY, ///< Optional: Images for overlaying track + ROTSG_GROUND, ///< Required: Main group of ground images + ROTSG_reserved1, ///< Placeholder, if we need specific tunnel sprites. + ROTSG_CATENARY_FRONT, ///< Optional: Catenary front + ROTSG_CATENARY_BACK, ///< Optional: Catenary back + ROTSG_BRIDGE, ///< Required: Bridge surface images + ROTSG_reserved2, ///< Placeholder, if we need specific level crossing sprites. + ROTSG_DEPOT, ///< Optional: Depot images + ROTSG_reserved3, ///< Placeholder, if we add road fences (for highways). + ROTSG_ROADSTOP, ///< Required: Drive-in stop surface + ROTSG_END, +}; + +/** List of road type labels. */ +typedef std::vector RoadTypeLabelList; + +class RoadTypeInfo { +public: + /** + * struct containing the sprites for the road GUI. @note only sprites referred to + * directly in the code are listed + */ + struct { + SpriteID build_x_road; ///< button for building single rail in X direction + SpriteID build_y_road; ///< button for building single rail in Y direction + SpriteID auto_road; ///< button for the autoroad construction + SpriteID build_depot; ///< button for building depots + SpriteID build_tunnel; ///< button for building a tunnel + SpriteID convert_road; ///< button for converting road types + } gui_sprites; + + struct { + CursorID road_swne; ///< Cursor for building rail in X direction + CursorID road_nwse; ///< Cursor for building rail in Y direction + CursorID autoroad; ///< Cursor for autorail tool + CursorID depot; ///< Cursor for building a depot + CursorID tunnel; ///< Cursor for building a tunnel + SpriteID convert_road; ///< Cursor for converting road types + } cursor; ///< Cursors associated with the road type. + + struct { + StringID name; ///< Name of this rail type. + StringID toolbar_caption; ///< Caption in the construction toolbar GUI for this rail type. + StringID menu_text; ///< Name of this rail type in the main toolbar dropdown. + StringID build_caption; ///< Caption of the build vehicle GUI for this rail type. + StringID replace_text; ///< Text used in the autoreplace GUI. + StringID new_engine; ///< Name of an engine for this type of road in the engine preview GUI. + + StringID err_build_road; ///< Building a normal piece of road + StringID err_remove_road; ///< Removing a normal piece of road + StringID err_depot; ///< Building a depot + StringID err_build_station[2]; ///< Building a bus or truck station + StringID err_remove_station[2]; ///< Removing of a bus or truck station + StringID err_convert_road; ///< Converting a road type + + StringID picker_title[2]; ///< Title for the station picker for bus or truck stations + StringID picker_tooltip[2]; ///< Tooltip for the station picker for bus or truck stations + } strings; ///< Strings associated with the rail type. + + /** bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power */ + RoadTypes powered_roadtypes; + + /** + * Bit mask of road type flags + */ + RoadTypeFlags flags; + + /** + * Cost multiplier for building this road type + */ + uint16 cost_multiplier; + + /** + * Cost multiplier for maintenance of this road type + */ + uint16 maintenance_multiplier; + + /** + * Maximum speed for vehicles travelling on this road type + */ + uint16 max_speed; + + /** + * Unique 32 bit road type identifier + */ + RoadTypeLabel label; + + /** + * Road type labels this type provides in addition to the main label. + */ + RoadTypeLabelList alternate_labels; + + /** + * Colour on mini-map + */ + byte map_colour; + + /** + * Introduction date. + * When #INVALID_DATE or a vehicle using this roadtype gets introduced earlier, + * the vehicle's introduction date will be used instead for this roadtype. + * The introduction at this date is furthermore limited by the + * #introduction_required_types. + */ + Date introduction_date; + + /** + * Bitmask of roadtypes that are required for this roadtype to be introduced + * at a given #introduction_date. + */ + RoadTypes introduction_required_roadtypes; + + /** + * Bitmask of which other roadtypes are introduced when this roadtype is introduced. + */ + RoadTypes introduces_roadtypes; + + /** + * The sorting order of this roadtype for the toolbar dropdown. + */ + byte sorting_order; + + /** + * NewGRF providing the Action3 for the roadtype. nullptr if not available. + */ + const GRFFile *grffile[ROTSG_END]; + + /** + * Sprite groups for resolving sprites + */ + const SpriteGroup *group[ROTSG_END]; + + inline bool UsesOverlay() const + { + return this->group[ROTSG_GROUND] != nullptr; + } +}; + +extern RoadTypes _roadtypes_type; + +static inline bool RoadTypeIsRoad(RoadType roadtype) +{ + return !HasBit(_roadtypes_type, roadtype); +} + +static inline bool RoadTypeIsTram(RoadType roadtype) +{ + return HasBit(_roadtypes_type, roadtype); +} + +static inline RoadTramType GetRoadTramType(RoadType roadtype) +{ + return RoadTypeIsTram(roadtype) ? RTT_TRAM : RTT_ROAD; +} + +static inline RoadTramType OtherRoadTramType(RoadTramType rtt) +{ + return rtt == RTT_ROAD ? RTT_TRAM : RTT_ROAD; +} + +/** + * Returns a pointer to the Roadtype information for a given roadtype + * @param roadtype the road type which the information is requested for + * @return The pointer to the RoadTypeInfo + */ +static inline const RoadTypeInfo *GetRoadTypeInfo(RoadType roadtype) +{ + extern RoadTypeInfo _roadtypes[ROADTYPE_END]; + assert(roadtype < ROADTYPE_END); + return &_roadtypes[roadtype]; +} + +/** + * Checks if an engine of the given RoadType got power on a tile with a given + * RoadType. This would normally just be an equality check, but for electrified + * roads (which also support non-electric vehicles). + * @return Whether the engine got power on this tile. + * @param enginetype The RoadType of the engine we are considering. + * @param tiletype The RoadType of the tile we are considering. + */ +static inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype) +{ + return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, tiletype); +} + +/** + * Returns the cost of building the specified roadtype. + * @param roadtype The roadtype being built. + * @return The cost multiplier. + */ +static inline Money RoadBuildCost(RoadType roadtype) +{ + assert(roadtype < ROADTYPE_END); + return (_price[PR_BUILD_ROAD] * GetRoadTypeInfo(roadtype)->cost_multiplier) >> 3; +} + +/** + * Returns the cost of clearing the specified roadtype. + * @param roadtype The roadtype being removed. + * @return The cost. + */ +static inline Money RoadClearCost(RoadType roadtype) +{ + assert(roadtype < ROADTYPE_END); + + /* Flat fee for removing road. */ + if (RoadTypeIsRoad(roadtype)) return _price[PR_CLEAR_ROAD]; + + /* Clearing tram earns a little money, but also incurs the standard clear road cost, + * so no profit can be made. */ + return _price[PR_CLEAR_ROAD] - RoadBuildCost(roadtype) * 3 / 4; +} + +/** + * Calculates the cost of road conversion + * @param from The roadtype we are converting from + * @param to The roadtype we are converting to + * @return Cost per RoadBit + */ +static inline Money RoadConvertCost(RoadType from, RoadType to) +{ + /* Don't apply convert costs when converting to the same roadtype (ex. building a roadstop over existing road) */ + if (from == to) return (Money)0; + + /* Same cost as removing and then building. */ + return RoadBuildCost(to) + RoadClearCost(from); +} + +/** + * Test if road disallows level crossings + * @param roadtype The roadtype we are testing + * @return True iff the roadtype disallows level crossings + */ +static inline bool RoadNoLevelCrossing(RoadType roadtype) +{ + assert(roadtype < ROADTYPE_END); + return HasBit(GetRoadTypeInfo(roadtype)->flags, ROTF_NO_LEVEL_CROSSING); +} + +RoadType GetRoadTypeByLabel(RoadTypeLabel label, bool allow_alternate_labels = true); + +void ResetRoadTypes(); +void InitRoadTypes(); +RoadType AllocateRoadType(RoadTypeLabel label, RoadTramType rtt); +bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt); + +extern std::vector _sorted_roadtypes; +extern RoadTypes _roadtypes_hidden_mask; + +/** + * Loop header for iterating over roadtypes, sorted by sortorder. + * @param var Roadtype. + */ +#define FOR_ALL_SORTED_ROADTYPES(var) for (uint8 index = 0; index < _sorted_roadtypes.size() && (var = _sorted_roadtypes[index], true) ; index++) + +#endif /* ROAD_H */ diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index e9edbf2549..325dd0f6ea 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "cmd_helper.h" +#include "road.h" #include "road_internal.h" #include "viewport_func.h" #include "command_func.h" @@ -31,15 +32,152 @@ #include "town.h" #include "company_base.h" #include "core/random_func.hpp" +#include "newgrf_debug.h" #include "newgrf_railtype.h" +#include "newgrf_roadtype.h" #include "date_func.h" #include "genworld.h" #include "company_gui.h" +#include "road_func.h" #include "table/strings.h" +#include "table/roadtypes.h" #include "safeguards.h" +/** Helper type for lists/vectors of road vehicles */ +typedef std::vector RoadVehicleList; + +RoadTypeInfo _roadtypes[ROADTYPE_END]; +std::vector _sorted_roadtypes; +RoadTypes _roadtypes_hidden_mask; + +/** + * Bitmap of road/tram types. + * Bit if set if a roadtype is tram. + */ +RoadTypes _roadtypes_type; + +/** + * Reset all road type information to its default values. + */ +void ResetRoadTypes() +{ + assert_compile(lengthof(_original_roadtypes) <= lengthof(_roadtypes)); + + uint i = 0; + for (; i < lengthof(_original_roadtypes); i++) _roadtypes[i] = _original_roadtypes[i]; + + static const RoadTypeInfo empty_roadtype = { + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, 0, {}, {} }, + ROADTYPES_NONE, ROTFB_NONE, 0, 0, 0, 0, + RoadTypeLabelList(), 0, 0, ROADTYPES_NONE, ROADTYPES_NONE, 0, + {}, {} }; + for (; i < lengthof(_roadtypes); i++) _roadtypes[i] = empty_roadtype; + + _roadtypes_hidden_mask = ROADTYPES_NONE; + _roadtypes_type = ROADTYPES_TRAM; +} + +void ResolveRoadTypeGUISprites(RoadTypeInfo *rti) +{ + SpriteID cursors_base = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_CURSORS); + if (cursors_base != 0) { + rti->gui_sprites.build_x_road = cursors_base + 0; + rti->gui_sprites.build_y_road = cursors_base + 1; + rti->gui_sprites.auto_road = cursors_base + 2; + rti->gui_sprites.build_depot = cursors_base + 3; + rti->gui_sprites.build_tunnel = cursors_base + 4; + rti->gui_sprites.convert_road = cursors_base + 5; + rti->cursor.road_swne = cursors_base + 6; + rti->cursor.road_nwse = cursors_base + 7; + rti->cursor.autoroad = cursors_base + 8; + rti->cursor.depot = cursors_base + 9; + rti->cursor.tunnel = cursors_base + 10; + rti->cursor.convert_road = cursors_base + 11; + } +} + +/** + * Compare roadtypes based on their sorting order. + * @param first The roadtype to compare to. + * @param second The roadtype to compare. + * @return True iff the first should be sorted before the second. + */ +static bool CompareRoadTypes(const RoadType &first, const RoadType &second) +{ + if (RoadTypeIsRoad(first) == RoadTypeIsRoad(second)) { + return GetRoadTypeInfo(first)->sorting_order < GetRoadTypeInfo(second)->sorting_order; + } + return RoadTypeIsTram(first) < RoadTypeIsTram(second); +} + +/** + * Resolve sprites of custom road types + */ +void InitRoadTypes() +{ + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + RoadTypeInfo *rti = &_roadtypes[rt]; + ResolveRoadTypeGUISprites(rti); + if (HasBit(rti->flags, ROTF_HIDDEN)) SetBit(_roadtypes_hidden_mask, rt); + } + + _sorted_roadtypes.clear(); + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + if (_roadtypes[rt].label != 0 && !HasBit(_roadtypes_hidden_mask, rt)) { + _sorted_roadtypes.push_back(rt); + } + } + std::sort(_sorted_roadtypes.begin(), _sorted_roadtypes.end(), CompareRoadTypes); +} + +/** + * Allocate a new road type label + */ +RoadType AllocateRoadType(RoadTypeLabel label, RoadTramType rtt) +{ + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + RoadTypeInfo *rti = &_roadtypes[rt]; + + if (rti->label == 0) { + /* Set up new road type */ + *rti = _original_roadtypes[(rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD]; + rti->label = label; + rti->alternate_labels.clear(); + rti->flags = ROTFB_NONE; + rti->introduction_date = INVALID_DATE; + + /* Make us compatible with ourself. */ + rti->powered_roadtypes = (RoadTypes)(1ULL << rt); + + /* We also introduce ourself. */ + rti->introduces_roadtypes = (RoadTypes)(1ULL << rt); + + /* Default sort order; order of allocation, but with some + * offsets so it's easier for NewGRF to pick a spot without + * changing the order of other (original) road types. + * The << is so you can place other roadtypes in between the + * other roadtypes, the 7 is to be able to place something + * before the first (default) road type. */ + rti->sorting_order = rt << 2 | 7; + + /* Set bitmap of road/tram types */ + if (rtt == RTT_TRAM) { + SetBit(_roadtypes_type, rt); + } else { + ClrBit(_roadtypes_type, rt); + } + + return rt; + } + } + + return INVALID_ROADTYPE; +} + /** * Verify whether a road vehicle is available. * @return \c true if at least one road vehicle is available, \c false if not @@ -52,6 +190,23 @@ bool RoadVehiclesAreBuilt() return false; } +/** + * Update road infrastructure counts for a company. + * @param rt Road type to update count of. + * @param o Owner of road piece. + * @param count Number of road pieces to adjust. + */ +void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count) +{ + if (rt == INVALID_ROADTYPE) return; + + Company *c = Company::GetIfValid(o); + if (c == nullptr) return; + + c->infrastructure.road[rt] += count; + DirtyCompanyInfrastructureWindows(c->index); +} + /** Invalid RoadBits on slopes. */ static const RoadBits _invalid_tileh_slopes_road[2][15] = { /* The inverse of the mixable RoadBits on a leveled slope */ @@ -113,7 +268,7 @@ static Foundation GetRoadFoundation(Slope tileh, RoadBits bits); * @param town_check Shall the town rating checked/affected * @return A succeeded command when it is allowed to remove the road bits, a failed command otherwise. */ -CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadType rt, DoCommandFlag flags, bool town_check) +CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlag flags, bool town_check) { if (_game_mode == GM_EDITOR || remove == ROAD_NONE) return CommandCost(); @@ -121,7 +276,7 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R * Towns are not be allowed to remove non "normal" road pieces, like tram * tracks as that would result in trams that cannot turn. */ if (_current_company == OWNER_WATER || - (rt == ROADTYPE_ROAD && !Company::IsValidID(_current_company))) return CommandCost(); + (rtt == RTT_ROAD && !Company::IsValidID(_current_company))) return CommandCost(); /* Only do the special processing if the road is owned * by a town */ @@ -145,11 +300,11 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R /* Get a bitmask of which neighbouring roads has a tile */ RoadBits n = ROAD_NONE; - RoadBits present = GetAnyRoadBits(tile, rt); - if ((present & ROAD_NE) && (GetAnyRoadBits(TILE_ADDXY(tile, -1, 0), rt) & ROAD_SW)) n |= ROAD_NE; - if ((present & ROAD_SE) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, 1), rt) & ROAD_NW)) n |= ROAD_SE; - if ((present & ROAD_SW) && (GetAnyRoadBits(TILE_ADDXY(tile, 1, 0), rt) & ROAD_NE)) n |= ROAD_SW; - if ((present & ROAD_NW) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, -1), rt) & ROAD_SE)) n |= ROAD_NW; + RoadBits present = GetAnyRoadBits(tile, rtt); + if ((present & ROAD_NE) && (GetAnyRoadBits(TILE_ADDXY(tile, -1, 0), rtt) & ROAD_SW)) n |= ROAD_NE; + if ((present & ROAD_SE) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, 1), rtt) & ROAD_NW)) n |= ROAD_SE; + if ((present & ROAD_SW) && (GetAnyRoadBits(TILE_ADDXY(tile, 1, 0), rtt) & ROAD_NE)) n |= ROAD_SW; + if ((present & ROAD_NW) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, -1), rtt) & ROAD_SE)) n |= ROAD_NW; int rating_decrease = RATING_ROAD_DOWN_STEP_EDGE; /* If 0 or 1 bits are set in n, or if no bits that match the bits to remove, @@ -177,11 +332,13 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R * @param crossing_check should we check if there is a tram track when we are removing road from crossing? * @param town_check should we check if the town allows removal? */ -static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits pieces, RoadType rt, bool crossing_check, bool town_check = true) +static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits pieces, RoadTramType rtt, bool crossing_check, bool town_check = true) { - RoadTypes rts = GetRoadTypes(tile); + assert(pieces != ROAD_NONE); + + RoadType existing_rt = MayHaveRoad(tile) ? GetRoadType(tile, rtt) : INVALID_ROADTYPE; /* The tile doesn't have the given road type */ - if (!HasBit(rts, rt)) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); + if (existing_rt == INVALID_ROADTYPE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); switch (GetTileType(tile)) { case MP_ROAD: { @@ -209,37 +366,32 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec return CMD_ERROR; } - CommandCost ret = CheckAllowRemoveRoad(tile, pieces, GetRoadOwner(tile, rt), rt, flags, town_check); + CommandCost ret = CheckAllowRemoveRoad(tile, pieces, GetRoadOwner(tile, rtt), rtt, flags, town_check); if (ret.Failed()) return ret; if (!IsTileType(tile, MP_ROAD)) { /* If it's the last roadtype, just clear the whole tile */ - if (rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); CommandCost cost(EXPENSES_CONSTRUCTION); if (IsTileType(tile, MP_TUNNELBRIDGE)) { /* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */ - if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); + if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); TileIndex other_end = GetOtherTunnelBridgeEnd(tile); /* Pay for *every* tile of the bridge or tunnel */ uint len = GetTunnelBridgeLength(other_end, tile) + 2; - cost.AddCost(len * 2 * _price[PR_CLEAR_ROAD]); + cost.AddCost(len * 2 * RoadClearCost(existing_rt)); if (flags & DC_EXEC) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; - DirtyCompanyInfrastructureWindows(c->index); - } + /* A full diagonal road tile has two road bits. */ + UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); - SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt)); - SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt)); + SetRoadType(other_end, rtt, INVALID_ROADTYPE); + SetRoadType(tile, rtt, INVALID_ROADTYPE); /* If the owner of the bridge sells all its road, also move the ownership * to the owner of the other roadtype, unless the bridge owner is a town. */ - RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - Owner other_owner = GetRoadOwner(tile, other_rt); + Owner other_owner = GetRoadOwner(tile, OtherRoadTramType(rtt)); if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) { SetTileOwner(tile, other_owner); SetTileOwner(other_end, other_owner); @@ -255,15 +407,11 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } } else { assert(IsDriveThroughStopTile(tile)); - cost.AddCost(_price[PR_CLEAR_ROAD] * 2); + cost.AddCost(RoadClearCost(existing_rt) * 2); if (flags & DC_EXEC) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= 2; - DirtyCompanyInfrastructureWindows(c->index); - } - SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt)); + /* A full diagonal road tile has two road bits. */ + UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -2); + SetRoadType(tile, rtt, INVALID_ROADTYPE); MarkTileDirtyByTile(tile); } } @@ -279,8 +427,8 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec tileh = SlopeWithOneCornerRaised(GetHighestSlopeCorner(tileh)); } - RoadBits present = GetRoadBits(tile, rt); - const RoadBits other = GetOtherRoadBits(tile, rt); + RoadBits present = GetRoadBits(tile, rtt); + const RoadBits other = GetRoadBits(tile, OtherRoadTramType(rtt)); const Foundation f = GetRoadFoundation(tileh, present); if (HasRoadWorks(tile) && _current_company != OWNER_WATER) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS); @@ -295,7 +443,7 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec /* limit the bits to delete to the existing bits. */ pieces &= present; - if (pieces == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); + if (pieces == ROAD_NONE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); /* Now set present what it will be after the remove */ present ^= pieces; @@ -318,38 +466,34 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } } - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - c->infrastructure.road[rt] -= CountBits(pieces); - DirtyCompanyInfrastructureWindows(c->index); - } + UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -(int)CountBits(pieces)); if (present == ROAD_NONE) { - RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt)); - if (rts == ROADTYPES_NONE) { + /* No other road type, just clear tile. */ + if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) { /* Includes MarkTileDirtyByTile() */ DoClearSquare(tile); } else { - if (rt == ROADTYPE_ROAD && IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) { + if (rtt == RTT_ROAD && IsRoadOwner(tile, rtt, OWNER_TOWN)) { /* Update nearest-town index */ const Town *town = CalcClosestTownFromTile(tile); SetTownIndex(tile, town == nullptr ? INVALID_TOWN : town->index); } - SetRoadBits(tile, ROAD_NONE, rt); - SetRoadTypes(tile, rts); + SetRoadBits(tile, ROAD_NONE, rtt); + SetRoadType(tile, rtt, INVALID_ROADTYPE); MarkTileDirtyByTile(tile); } } else { /* When bits are removed, you *always* end up with something that * is not a complete straight road tile. However, trams do not have * onewayness, so they cannot remove it either. */ - if (rt != ROADTYPE_TRAM) SetDisallowedRoadDirections(tile, DRD_NONE); - SetRoadBits(tile, present, rt); + if (rtt == RTT_ROAD) SetDisallowedRoadDirections(tile, DRD_NONE); + SetRoadBits(tile, present, rtt); MarkTileDirtyByTile(tile); } } - CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * _price[PR_CLEAR_ROAD]); + CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * RoadClearCost(existing_rt)); /* If we build a foundation we have to pay for it. */ if (f == FOUNDATION_NONE && GetRoadFoundation(tileh, present) != FOUNDATION_NONE) cost.AddCost(_price[PR_BUILD_FOUNDATION]); @@ -361,21 +505,12 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec return CMD_ERROR; } - /* Don't allow road to be removed from the crossing when there is tram; - * we can't draw the crossing without roadbits ;) */ - if (rt == ROADTYPE_ROAD && HasTileRoadType(tile, ROADTYPE_TRAM) && (flags & DC_EXEC || crossing_check)) return CMD_ERROR; - if (flags & DC_EXEC) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= 2; - DirtyCompanyInfrastructureWindows(c->index); - } + /* A full diagonal road tile has two road bits. */ + UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -2); Track railtrack = GetCrossingRailTrack(tile); - RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt)); - if (rts == ROADTYPES_NONE) { + if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) { TrackBits tracks = GetCrossingRailBits(tile); bool reserved = HasCrossingReservation(tile); MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile)); @@ -383,18 +518,18 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec /* Update rail count for level crossings. The plain track should still be accounted * for, so only subtract the difference to the level crossing cost. */ - c = Company::GetIfValid(GetTileOwner(tile)); + Company *c = Company::GetIfValid(GetTileOwner(tile)); if (c != nullptr) { c->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR - 1; DirtyCompanyInfrastructureWindows(c->index); } } else { - SetRoadTypes(tile, rts); + SetRoadType(tile, rtt, INVALID_ROADTYPE); } MarkTileDirtyByTile(tile); YapfNotifyTrackLayoutChange(tile, railtrack); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROAD] * 2); + return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2); } default: @@ -475,8 +610,8 @@ static CommandCost CheckRoadSlope(Slope tileh, RoadBits *pieces, RoadBits existi * @param tile tile where to build road * @param flags operation to perform * @param p1 bit 0..3 road pieces to build (RoadBits) - * bit 4..5 road type - * bit 6..7 disallowed directions to toggle + * bit 4..9 road type + * bit 11..12 disallowed directions to toggle * @param p2 the town that is building the road (0 if not applicable) * @param text unused * @return the cost of this operation or an error @@ -511,12 +646,13 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* do not allow building 'zero' road bits, code wouldn't handle it */ if (pieces == ROAD_NONE) return CMD_ERROR; - RoadType rt = Extract(p1); - if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR; + RoadType rt = Extract(p1); + if (!ValParamRoadType(rt)) return CMD_ERROR; - DisallowedRoadDirections toggle_drd = Extract(p1); + DisallowedRoadDirections toggle_drd = Extract(p1); Slope tileh = GetTileSlope(tile); + RoadTramType rtt = GetRoadTramType(rt); bool need_to_clear = false; switch (GetTileType(tile)) { @@ -525,21 +661,21 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 case ROAD_TILE_NORMAL: { if (HasRoadWorks(tile)) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS); - other_bits = GetOtherRoadBits(tile, rt); - if (!HasTileRoadType(tile, rt)) break; + other_bits = GetRoadBits(tile, OtherRoadTramType(rtt)); + if (!HasTileRoadType(tile, rtt)) break; - existing = GetRoadBits(tile, rt); + existing = GetRoadBits(tile, rtt); bool crossing = !IsStraightRoad(existing | pieces); - if (rt != ROADTYPE_TRAM && (GetDisallowedRoadDirections(tile) != DRD_NONE || toggle_drd != DRD_NONE) && crossing) { + if (rtt == RTT_ROAD && (GetDisallowedRoadDirections(tile) != DRD_NONE || toggle_drd != DRD_NONE) && crossing) { /* Junctions cannot be one-way */ return_cmd_error(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION); } if ((existing & pieces) == pieces) { /* We only want to set the (dis)allowed road directions */ - if (toggle_drd != DRD_NONE && rt != ROADTYPE_TRAM) { + if (toggle_drd != DRD_NONE && rtt == RTT_ROAD) { if (crossing) return_cmd_error(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION); - Owner owner = GetRoadOwner(tile, ROADTYPE_ROAD); + Owner owner = GetRoadOwner(tile, rtt); if (owner != OWNER_NONE) { CommandCost ret = CheckOwnership(owner, tile); if (ret.Failed()) return ret; @@ -558,7 +694,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } /* Ignore half built tiles */ - if ((flags & DC_EXEC) && rt != ROADTYPE_TRAM && IsStraightRoad(existing)) { + if ((flags & DC_EXEC) && IsStraightRoad(existing)) { SetDisallowedRoadDirections(tile, dis_new); MarkTileDirtyByTile(tile); } @@ -568,8 +704,8 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } /* Disallow breaking end-of-line of someone else * so trams can still reverse on this tile. */ - if (rt == ROADTYPE_TRAM && HasExactlyOneBit(existing)) { - Owner owner = GetRoadOwner(tile, rt); + if (rtt == RTT_TRAM && HasExactlyOneBit(existing)) { + Owner owner = GetRoadOwner(tile, rtt); if (Company::IsValidID(owner)) { CommandCost ret = CheckOwnership(owner); if (ret.Failed()) return ret; @@ -579,15 +715,19 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } case ROAD_TILE_CROSSING: + if (RoadNoLevelCrossing(rt)) { + return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD); + } + other_bits = GetCrossingRoadBits(tile); if (pieces & ComplementRoadBits(other_bits)) goto do_clear; pieces = other_bits; // we need to pay for both roadbits - if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); break; case ROAD_TILE_DEPOT: - if ((GetAnyRoadBits(tile, rt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); goto do_clear; default: NOT_REACHED(); @@ -606,8 +746,12 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (GetRailTileType(tile) != RAIL_TILE_NORMAL) goto do_clear; + if (RoadNoLevelCrossing(rt)) { + return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD); + } + if (RailNoLevelCrossings(GetRailType(tile))) { - return_cmd_error(STR_ERROR_CROSSING_DISALLOWED); + return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_RAIL); } Axis roaddir; @@ -632,15 +776,11 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 Track railtrack = AxisToTrack(OtherAxis(roaddir)); YapfNotifyTrackLayoutChange(tile, railtrack); /* Update company infrastructure counts. A level crossing has two road bits. */ - Company *c = Company::GetIfValid(company); - if (c != nullptr) { - c->infrastructure.road[rt] += 2; - if (rt != ROADTYPE_ROAD) c->infrastructure.road[ROADTYPE_ROAD] += 2; - DirtyCompanyInfrastructureWindows(company); - } + UpdateCompanyRoadInfrastructure(rt, company, 2); + /* Update rail count for level crossings. The plain track is already * counted, so only add the difference to the level crossing cost. */ - c = Company::GetIfValid(GetTileOwner(tile)); + Company *c = Company::GetIfValid(GetTileOwner(tile)); if (c != nullptr) { c->infrastructure.rail[GetRailType(tile)] += LEVELCROSSING_TRACKBIT_FACTOR - 1; DirtyCompanyInfrastructureWindows(c->index); @@ -648,23 +788,23 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Always add road to the roadtypes (can't draw without it) */ bool reserved = HasBit(GetRailReservationTrackBits(tile), railtrack); - MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2); + MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), rtt == RTT_ROAD ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, p2); SetCrossingReservation(tile, reserved); UpdateLevelCrossing(tile, false); MarkTileDirtyByTile(tile); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_ROAD] * (rt == ROADTYPE_ROAD ? 2 : 4)); + return CommandCost(EXPENSES_CONSTRUCTION, 2 * RoadBuildCost(rt)); } case MP_STATION: { - if ((GetAnyRoadBits(tile, rt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); if (!IsDriveThroughStopTile(tile)) goto do_clear; RoadBits curbits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(tile))); if (pieces & ~curbits) goto do_clear; pieces = curbits; // we need to pay for both roadbits - if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); break; } @@ -672,7 +812,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) goto do_clear; /* Only allow building the outern roadbit, so building long roads stops at existing bridges */ if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear; - if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */ CommandCost ret = TunnelBridgeIsFree(tile, GetOtherTunnelBridgeEnd(tile)); if (ret.Failed()) return ret; @@ -713,15 +853,10 @@ do_clear:; Slope slope = GetTileSlope(tile); Foundation found_new = GetRoadFoundation(slope, pieces | existing); - /* Test if all other roadtypes can be built at that foundation */ - for (RoadType rtest = ROADTYPE_ROAD; rtest < ROADTYPE_END; rtest++) { - if (rtest != rt) { // check only other road types - RoadBits bits = GetRoadBits(tile, rtest); - /* do not check if there are not road bits of given type */ - if (bits != ROAD_NONE && GetRoadFoundation(slope, bits) != found_new) { - return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); - } - } + RoadBits bits = GetRoadBits(tile, OtherRoadTramType(rtt)); + /* do not check if there are not road bits of given type */ + if (bits != ROAD_NONE && GetRoadFoundation(slope, bits) != found_new) { + return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } } } @@ -729,6 +864,23 @@ do_clear:; CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; + if (IsNormalRoadTile(tile)) { + /* If the road types don't match, try to convert only if vehicles of + * the new road type are not powered on the present road type and vehicles of + * the present road type are powered on the new road type. */ + RoadType existing_rt = GetRoadType(tile, rtt); + if (existing_rt != INVALID_ROADTYPE && existing_rt != rt) { + if (HasPowerOnRoad(rt, existing_rt)) { + rt = existing_rt; + } else if (HasPowerOnRoad(existing_rt, rt)) { + CommandCost ret = DoCommand(tile, tile, rt, flags, CMD_CONVERT_ROAD); + if (ret.Failed()) return ret; + cost.AddCost(ret); + } else { + return CMD_ERROR; + } + } + } } uint num_pieces = (!need_to_clear && IsTileType(tile, MP_TUNNELBRIDGE)) ? @@ -737,28 +889,28 @@ do_clear:; /* Count pieces */ CountBits(pieces); - cost.AddCost(num_pieces * _price[PR_BUILD_ROAD]); + cost.AddCost(num_pieces * RoadBuildCost(rt)); if (flags & DC_EXEC) { switch (GetTileType(tile)) { case MP_ROAD: { - RoadTileType rtt = GetRoadTileType(tile); - if (existing == ROAD_NONE || rtt == ROAD_TILE_CROSSING) { - SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); - SetRoadOwner(tile, rt, company); - if (rt == ROADTYPE_ROAD) SetTownIndex(tile, p2); + RoadTileType rttype = GetRoadTileType(tile); + if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) { + SetRoadType(tile, rtt, rt); + SetRoadOwner(tile, rtt, company); + if (rtt == RTT_ROAD) SetTownIndex(tile, p2); } - if (rtt != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rt); + if (rttype != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rtt); break; } case MP_TUNNELBRIDGE: { TileIndex other_end = GetOtherTunnelBridgeEnd(tile); - SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt)); - SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); - SetRoadOwner(other_end, rt, company); - SetRoadOwner(tile, rt, company); + SetRoadType(other_end, rtt, rt); + SetRoadType(tile, rtt, rt); + SetRoadOwner(other_end, rtt, company); + SetRoadOwner(tile, rtt, company); /* Mark tiles dirty that have been repaved */ if (IsBridge(tile)) { @@ -770,26 +922,23 @@ do_clear:; break; } - case MP_STATION: + case MP_STATION: { assert(IsDriveThroughStopTile(tile)); - SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); - SetRoadOwner(tile, rt, company); + SetRoadType(tile, rtt, rt); + SetRoadOwner(tile, rtt, company); break; + } default: - MakeRoadNormal(tile, pieces, RoadTypeToRoadTypes(rt), p2, company, company); + MakeRoadNormal(tile, pieces, (rtt == RTT_ROAD) ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, p2, company, company); break; } /* Update company infrastructure count. */ - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - if (IsTileType(tile, MP_TUNNELBRIDGE)) num_pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR; - c->infrastructure.road[rt] += num_pieces; - DirtyCompanyInfrastructureWindows(c->index); - } + if (IsTileType(tile, MP_TUNNELBRIDGE)) num_pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR; + UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), num_pieces); - if (rt != ROADTYPE_TRAM && IsNormalRoadTile(tile)) { + if (rtt == RTT_ROAD && IsNormalRoadTile(tile)) { existing |= pieces; SetDisallowedRoadDirections(tile, IsStraightRoad(existing) ? GetDisallowedRoadDirections(tile) ^ toggle_drd : DRD_NONE); @@ -810,8 +959,14 @@ do_clear:; static bool CanConnectToRoad(TileIndex tile, RoadType rt, DiagDirection dir) { tile += TileOffsByDiagDir(dir); - if (!IsValidTile(tile)) return false; - RoadBits bits = GetAnyRoadBits(tile, rt, false); + if (!IsValidTile(tile) || !MayHaveRoad(tile)) return false; + + RoadTramType rtt = GetRoadTramType(rt); + RoadType existing = GetRoadType(tile, rtt); + if (existing == INVALID_ROADTYPE) return false; + if (!HasPowerOnRoad(existing, rt) && !HasPowerOnRoad(rt, existing)) return false; + + RoadBits bits = GetAnyRoadBits(tile, rtt, false); return (bits & DiagDirToRoadBits(ReverseDiagDir(dir))) != 0; } @@ -824,9 +979,9 @@ static bool CanConnectToRoad(TileIndex tile, RoadType rt, DiagDirection dir) * - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1). Only used if bit 6 is set or if we are building a single tile * - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2). Only used if bit 6 is set or if we are building a single tile * - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4) - * - p2 = (bit 3 + 4) - road type - * - p2 = (bit 5) - set road direction - * - p2 = (bit 6) - defines two different behaviors for this command: + * - p2 = (bit 3..8) - road type + * - p2 = (bit 10) - set road direction + * - p2 = (bit 11) - defines two different behaviors for this command: * - 0 = Build up to an obstacle. Do not build the first and last roadbits unless they can be connected to something, or if we are building a single tile * - 1 = Fail if an obstacle is found. Always take into account bit 0 and 1. This behavior is used for scripts * @param text unused @@ -837,10 +992,10 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p DisallowedRoadDirections drd = DRD_NORTHBOUND; if (p1 >= MapSize()) return CMD_ERROR; - TileIndex end_tile = p1; - RoadType rt = Extract(p2); - if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR; + + RoadType rt = Extract(p2); + if (!ValParamRoadType(rt)) return CMD_ERROR; Axis axis = Extract(p2); /* Only drag in X or Y direction dictated by the direction variable */ @@ -861,7 +1016,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p * when you just 'click' on one tile to build them. */ if ((axis == AXIS_Y) == (start_tile == end_tile && HasBit(p2, 0) == HasBit(p2, 1))) drd ^= DRD_BOTH; /* No disallowed direction bits have to be toggled */ - if (!HasBit(p2, 5)) drd = DRD_NONE; + if (!HasBit(p2, 10)) drd = DRD_NONE; CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost last_error = CMD_ERROR; @@ -869,7 +1024,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p bool had_bridge = false; bool had_tunnel = false; bool had_success = false; - bool is_ai = HasBit(p2, 6); + bool is_ai = HasBit(p2, 11); /* Start tile is the first tile clicked by the user. */ for (;;) { @@ -889,7 +1044,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p if (tile == start_tile && HasBit(p2, 0)) bits &= DiagDirToRoadBits(dir); } - CommandCost ret = DoCommand(tile, drd << 6 | rt << 4 | bits, 0, flags, CMD_BUILD_ROAD); + CommandCost ret = DoCommand(tile, drd << 11 | rt << 4 | bits, 0, flags, CMD_BUILD_ROAD); if (ret.Failed()) { last_error = ret; if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT) { @@ -933,7 +1088,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p * - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1) * - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2) * - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4) - * - p2 = (bit 3 + 4) - road type + * - p2 = (bit 3 - 8) - road type * @param text unused * @return the cost of this operation or an error */ @@ -944,8 +1099,8 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 if (p1 >= MapSize()) return CMD_ERROR; TileIndex end_tile = p1; - RoadType rt = Extract(p2); - if (!IsValidRoadType(rt)) return CMD_ERROR; + RoadType rt = Extract(p2); + if (!ValParamRoadType(rt)) return CMD_ERROR; Axis axis = Extract(p2); /* Only drag in X or Y direction dictated by the direction variable */ @@ -973,7 +1128,8 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 /* try to remove the halves. */ if (bits != 0) { - CommandCost ret = RemoveRoad(tile, flags & ~DC_EXEC, bits, rt, true); + RoadTramType rtt = GetRoadTramType(rt); + CommandCost ret = RemoveRoad(tile, flags & ~DC_EXEC, bits, rtt, true); if (ret.Succeeded()) { if (flags & DC_EXEC) { money -= ret.GetCost(); @@ -981,7 +1137,7 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 _additional_cash_required = DoCommand(start_tile, end_tile, p2, flags & ~DC_EXEC, CMD_REMOVE_LONG_ROAD).GetCost(); return cost; } - RemoveRoad(tile, flags, bits, rt, true, false); + RemoveRoad(tile, flags, bits, rtt, true, false); } cost.AddCost(ret); had_success = true; @@ -1004,7 +1160,7 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 * @param tile tile where to build the depot * @param flags operation to perform * @param p1 bit 0..1 entrance direction (DiagDirection) - * bit 2..3 road type + * bit 2..7 road type * @param p2 unused * @param text unused * @return the cost of this operation or an error @@ -1015,9 +1171,9 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { DiagDirection dir = Extract(p1); - RoadType rt = Extract(p1); - if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR; + RoadType rt = Extract(p1); + if (!ValParamRoadType(rt)) return CMD_ERROR; CommandCost cost(EXPENSES_CONSTRUCTION); @@ -1041,8 +1197,7 @@ CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui dep->build_date = _date; /* A road depot has two road bits. */ - Company::Get(_current_company)->infrastructure.road[rt] += 2; - DirtyCompanyInfrastructureWindows(_current_company); + UpdateCompanyRoadInfrastructure(rt, _current_company, 2); MakeRoadDepot(tile, _current_company, dep->index, dir, rt); MarkTileDirtyByTile(tile); @@ -1066,7 +1221,9 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) Company *c = Company::GetIfValid(GetTileOwner(tile)); if (c != nullptr) { /* A road depot has two road bits. */ - c->infrastructure.road[FIND_FIRST_BIT(GetRoadTypes(tile))] -= 2; + RoadType rt = GetRoadTypeRoad(tile); + if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile); + c->infrastructure.road[rt] -= 2; DirtyCompanyInfrastructureWindows(c->index); } @@ -1084,11 +1241,12 @@ static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags) RoadBits b = GetAllRoadBits(tile); /* Clear the road if only one piece is on the tile OR we are not using the DC_AUTO flag */ - if ((HasExactlyOneBit(b) && GetRoadBits(tile, ROADTYPE_TRAM) == ROAD_NONE) || !(flags & DC_AUTO)) { + if ((HasExactlyOneBit(b) && GetRoadBits(tile, RTT_TRAM) == ROAD_NONE) || !(flags & DC_AUTO)) { CommandCost ret(EXPENSES_CONSTRUCTION); - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - CommandCost tmp_ret = RemoveRoad(tile, flags, GetRoadBits(tile, rt), rt, true); + FOR_ALL_ROADTRAMTYPES(rtt) { + if (!MayHaveRoad(tile) || GetRoadType(tile, rtt) == INVALID_ROADTYPE) continue; + + CommandCost tmp_ret = RemoveRoad(tile, flags, GetRoadBits(tile, rtt), rtt, true); if (tmp_ret.Failed()) return tmp_ret; ret.AddCost(tmp_ret); } @@ -1098,21 +1256,19 @@ static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags) } case ROAD_TILE_CROSSING: { - RoadTypes rts = GetRoadTypes(tile); CommandCost ret(EXPENSES_CONSTRUCTION); if (flags & DC_AUTO) return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST); /* Must iterate over the roadtypes in a reverse manner because * tram tracks must be removed before the road bits. */ - RoadType rt = ROADTYPE_TRAM; - do { - if (HasBit(rts, rt)) { - CommandCost tmp_ret = RemoveRoad(tile, flags, GetCrossingRoadBits(tile), rt, false); - if (tmp_ret.Failed()) return tmp_ret; - ret.AddCost(tmp_ret); - } - } while (rt-- != ROADTYPE_ROAD); + for (RoadTramType rtt : { RTT_TRAM, RTT_ROAD }) { + if (GetRoadType(tile, rtt) == INVALID_ROADTYPE) continue; + + CommandCost tmp_ret = RemoveRoad(tile, flags, GetCrossingRoadBits(tile), rtt, false); + if (tmp_ret.Failed()) return tmp_ret; + ret.AddCost(tmp_ret); + } if (flags & DC_EXEC) { DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); @@ -1174,6 +1330,33 @@ const byte _road_sloped_sprites[14] = { 0, 0 }; +/** + * Get the sprite offset within a spritegroup. + * @param slope Slope + * @param bits Roadbits + * @return Offset for the sprite within the spritegroup. + */ +static uint GetRoadSpriteOffset(Slope slope, RoadBits bits) +{ + if (slope != SLOPE_FLAT) { + switch (slope) { + case SLOPE_NE: return 11; + case SLOPE_SE: return 12; + case SLOPE_SW: return 13; + case SLOPE_NW: return 14; + default: NOT_REACHED(); + } + } else { + static const uint offsets[] = { + 0, 18, 17, 7, + 16, 0, 10, 5, + 15, 8, 1, 4, + 9, 3, 6, 2 + }; + return offsets[bits]; + } +} + /** * Should the road be drawn as a unpaved snow/desert road? * By default, roads are always drawn as unpaved if they are on desert or @@ -1191,15 +1374,13 @@ static bool DrawRoadAsSnowDesert(TileIndex tile, Roadside roadside) } /** - * Draws the catenary for the given tile - * @param ti information about the tile (slopes, height etc) - * @param tram the roadbits for the tram + * Draws the catenary for the RoadType of the given tile + * @param ti information about the tile (slopes, height etc) + * @param rt road type to draw catenary for + * @param rb the roadbits for the tram */ -void DrawRoadCatenary(const TileInfo *ti, RoadBits tram) +void DrawRoadTypeCatenary(const TileInfo *ti, RoadType rt, RoadBits rb) { - /* Do not draw catenary if it is invisible */ - if (IsInvisibilitySet(TO_CATENARY)) return; - /* Don't draw the catenary under a low bridge */ if (IsBridgeAbove(ti->tile) && !IsTransparencySet(TO_CATENARY)) { int height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile)); @@ -1207,19 +1388,89 @@ void DrawRoadCatenary(const TileInfo *ti, RoadBits tram) if (height <= GetTileMaxZ(ti->tile) + 1) return; } - SpriteID front; - SpriteID back; + if (CountBits(rb) > 2) { + /* On junctions we check whether neighbouring tiles also have catenary, and possibly + * do not draw catenary towards those neighbours, which do not have catenary. */ + RoadBits rb_new = ROAD_NONE; + for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { + if (rb & DiagDirToRoadBits(dir)) { + TileIndex neighbour = TileAddByDiagDir(ti->tile, dir); + if (MayHaveRoad(neighbour)) { + RoadType rt_road = GetRoadTypeRoad(neighbour); + RoadType rt_tram = GetRoadTypeTram(neighbour); + + if ((rt_road != INVALID_ROADTYPE && HasRoadCatenary(rt_road)) || + (rt_tram != INVALID_ROADTYPE && HasRoadCatenary(rt_tram))) { + rb_new |= DiagDirToRoadBits(dir); + } + } + } + } + if (CountBits(rb_new) >= 2) rb = rb_new; + } - if (ti->tileh != SLOPE_FLAT) { + const RoadTypeInfo* rti = GetRoadTypeInfo(rt); + SpriteID front = GetCustomRoadSprite(rti, ti->tile, ROTSG_CATENARY_FRONT); + SpriteID back = GetCustomRoadSprite(rti, ti->tile, ROTSG_CATENARY_BACK); + + if (front != 0 || back != 0) { + if (front != 0) front += GetRoadSpriteOffset(ti->tileh, rb); + if (back != 0) back += GetRoadSpriteOffset(ti->tileh, rb); + } else if (ti->tileh != SLOPE_FLAT) { back = SPR_TRAMWAY_BACK_WIRES_SLOPED + _road_sloped_sprites[ti->tileh - 1]; front = SPR_TRAMWAY_FRONT_WIRES_SLOPED + _road_sloped_sprites[ti->tileh - 1]; } else { - back = SPR_TRAMWAY_BASE + _road_backpole_sprites_1[tram]; - front = SPR_TRAMWAY_BASE + _road_frontwire_sprites_1[tram]; + back = SPR_TRAMWAY_BASE + _road_backpole_sprites_1[rb]; + front = SPR_TRAMWAY_BASE + _road_frontwire_sprites_1[rb]; + } + + /* Catenary uses 1st company colour to help identify owner. + * For tiles with OWNER_TOWN or OWNER_NONE, recolour CC to grey as a neutral colour. */ + Owner owner = GetRoadOwner(ti->tile, GetRoadTramType(rt)); + PaletteID pal = (owner == OWNER_NONE || owner == OWNER_TOWN ? GENERAL_SPRITE_COLOUR(COLOUR_GREY) : COMPANY_SPRITE_COLOUR(owner)); + if (back != 0) AddSortableSpriteToDraw(back, pal, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY)); + if (front != 0) AddSortableSpriteToDraw(front, pal, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY)); +} + +/** + * Draws the catenary for the given tile + * @param ti information about the tile (slopes, height etc) + */ +void DrawRoadCatenary(const TileInfo *ti) +{ + RoadBits road = ROAD_NONE; + RoadBits tram = ROAD_NONE; + + if (IsTileType(ti->tile, MP_ROAD)) { + if (IsNormalRoad(ti->tile)) { + road = GetRoadBits(ti->tile, RTT_ROAD); + tram = GetRoadBits(ti->tile, RTT_TRAM); + } else if (IsLevelCrossing(ti->tile)) { + tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y); + } + } else if (IsTileType(ti->tile, MP_STATION)) { + if (IsRoadStop(ti->tile)) { + if (IsDriveThroughStopTile(ti->tile)) { + Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y; + tram = road = (axis == AXIS_X ? ROAD_X : ROAD_Y); + } else { + tram = road = DiagDirToRoadBits(GetRoadStopDir(ti->tile)); + } + } + } else { + // No road here, no catenary to draw + return; } - AddSortableSpriteToDraw(back, PAL_NONE, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY)); - AddSortableSpriteToDraw(front, PAL_NONE, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY)); + RoadType rt = GetRoadTypeRoad(ti->tile); + if (rt != INVALID_ROADTYPE && HasRoadCatenaryDrawn(rt)) { + DrawRoadTypeCatenary(ti, rt, road); + } + + rt = GetRoadTypeTram(ti->tile); + if (rt != INVALID_ROADTYPE && HasRoadCatenaryDrawn(rt)) { + DrawRoadTypeCatenary(ti, rt, tram); + } } /** @@ -1240,56 +1491,124 @@ static void DrawRoadDetail(SpriteID img, const TileInfo *ti, int dx, int dy, int } /** - * Draw ground sprite and road pieces + * Draw road underlay and overlay sprites. * @param ti TileInfo + * @param road_rti Road road type information + * @param tram_rti Tram road type information + * @param road_offset Road sprite offset (based on road bits) + * @param tram_offset Tram sprite offset (based on road bits) */ -static void DrawRoadBits(TileInfo *ti) +void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset) { - RoadBits road = GetRoadBits(ti->tile, ROADTYPE_ROAD); - RoadBits tram = GetRoadBits(ti->tile, ROADTYPE_TRAM); - - SpriteID image = 0; - PaletteID pal = PAL_NONE; + /* Road underlay takes precedence over tram */ + if (road_rti != nullptr) { + if (road_rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_GROUND); + DrawGroundSprite(ground + road_offset, pal); + } + } else { + if (tram_rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_GROUND); + DrawGroundSprite(ground + tram_offset, pal); + } else { + DrawGroundSprite(SPR_TRAMWAY_TRAM + tram_offset, pal); + } + } - if (ti->tileh != SLOPE_FLAT) { - DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram)); + /* Draw road overlay */ + if (road_rti != nullptr) { + if (road_rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_OVERLAY); + if (ground != 0) DrawGroundSprite(ground + road_offset, pal); + } + } - /* DrawFoundation() modifies ti. - * Default sloped sprites.. */ - if (ti->tileh != SLOPE_FLAT) image = _road_sloped_sprites[ti->tileh - 1] + SPR_ROAD_SLOPE_START; + /* Draw tram overlay */ + if (tram_rti != nullptr) { + if (tram_rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_OVERLAY); + if (ground != 0) DrawGroundSprite(ground + tram_offset, pal); + } else if (road_rti != nullptr) { + DrawGroundSprite(SPR_TRAMWAY_OVERLAY + tram_offset, pal); + } } +} - if (image == 0) image = _road_tile_sprites_1[road != ROAD_NONE ? road : tram]; +/** + * Get ground sprite to draw for a road tile. + * @param ti TileInof + * @param roadside Road side type + * @param rti Road type info + * @param offset Road sprite offset + * @param[out] pal Palette to draw. + */ +static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const RoadTypeInfo *rti, uint offset, PaletteID *pal) +{ + /* Draw bare ground sprite if no road or road uses overlay system. */ + if (rti == nullptr || rti->UsesOverlay()) { + if (DrawRoadAsSnowDesert(ti->tile, roadside)) { + return SPR_FLAT_SNOW_DESERT_TILE + SlopeToSpriteOffset(ti->tileh); + } - Roadside roadside = GetRoadside(ti->tile); + switch (roadside) { + case ROADSIDE_BARREN: *pal = PALETTE_TO_BARE_LAND; + return SPR_FLAT_GRASS_TILE + SlopeToSpriteOffset(ti->tileh); + case ROADSIDE_GRASS: + case ROADSIDE_GRASS_ROAD_WORKS: return SPR_FLAT_GRASS_TILE + SlopeToSpriteOffset(ti->tileh); + default: break; // Paved + } + } + /* Draw original road base sprite */ + SpriteID image = SPR_ROAD_Y + offset; if (DrawRoadAsSnowDesert(ti->tile, roadside)) { image += 19; } else { switch (roadside) { - case ROADSIDE_BARREN: pal = PALETTE_TO_BARE_LAND; break; + case ROADSIDE_BARREN: *pal = PALETTE_TO_BARE_LAND; break; case ROADSIDE_GRASS: break; case ROADSIDE_GRASS_ROAD_WORKS: break; default: image -= 19; break; // Paved } } - DrawGroundSprite(image, pal); + return image; +} - /* For tram we overlay the road graphics with either tram tracks only - * (when there is actual road beneath the trams) or with tram tracks - * and some dirts which hides the road graphics */ - if (tram != ROAD_NONE) { - if (ti->tileh != SLOPE_FLAT) { - image = _road_sloped_sprites[ti->tileh - 1] + SPR_TRAMWAY_SLOPED_OFFSET; - } else { - image = _road_tile_sprites_1[tram] - SPR_ROAD_Y; - } - image += (road == ROAD_NONE) ? SPR_TRAMWAY_TRAM : SPR_TRAMWAY_OVERLAY; - DrawGroundSprite(image, pal); +/** + * Draw ground sprite and road pieces + * @param ti TileInfo + */ +static void DrawRoadBits(TileInfo *ti) +{ + RoadBits road = GetRoadBits(ti->tile, RTT_ROAD); + RoadBits tram = GetRoadBits(ti->tile, RTT_TRAM); + + RoadType road_rt = GetRoadTypeRoad(ti->tile); + RoadType tram_rt = GetRoadTypeTram(ti->tile); + const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); + const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + + if (ti->tileh != SLOPE_FLAT) { + DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram)); + /* DrawFoundation() modifies ti. */ } - if (road != ROAD_NONE) { + /* Determine sprite offsets */ + uint road_offset = GetRoadSpriteOffset(ti->tileh, road); + uint tram_offset = GetRoadSpriteOffset(ti->tileh, tram); + + /* Draw baseset underlay */ + Roadside roadside = GetRoadside(ti->tile); + + PaletteID pal = PAL_NONE; + SpriteID image = GetRoadGroundSprite(ti, roadside, road_rti, road == ROAD_NONE ? tram_offset : road_offset, &pal); + DrawGroundSprite(image, pal); + + DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset); + + /* Draw one way */ + if (road_rti != nullptr) { DisallowedRoadDirections drd = GetDisallowedRoadDirections(ti->tile); if (drd != DRD_NONE) { DrawGroundSpriteAt(SPR_ONEWAY_BASE + drd - 1 + ((road == ROAD_X) ? 0 : 3), PAL_NONE, 8, 8, GetPartialPixelZ(8, 8, ti->tileh)); @@ -1302,7 +1621,8 @@ static void DrawRoadBits(TileInfo *ti) return; } - if (tram != ROAD_NONE) DrawRoadCatenary(ti, tram); + /* Draw road, tram catenary */ + DrawRoadCatenary(ti); /* Return if full detail is disabled, or we are zoomed fully out. */ if (!HasBit(_display_opt, DO_FULL_DETAIL) || _cur_dpi->zoom > ZOOM_LVL_DETAIL) return; @@ -1337,41 +1657,38 @@ static void DrawTile_Road(TileInfo *ti) case ROAD_TILE_CROSSING: { if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); - PaletteID pal = PAL_NONE; + Axis axis = GetCrossingRailAxis(ti->tile); + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); + RoadType road_rt = GetRoadTypeRoad(ti->tile); + RoadType tram_rt = GetRoadTypeTram(ti->tile); + const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); + const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + + PaletteID pal = PAL_NONE; + + /* Draw base ground */ if (rti->UsesOverlay()) { - Axis axis = GetCrossingRailAxis(ti->tile); - SpriteID road = SPR_ROAD_Y + axis; + SpriteID image = SPR_ROAD_Y + axis; Roadside roadside = GetRoadside(ti->tile); - if (DrawRoadAsSnowDesert(ti->tile, roadside)) { - road += 19; + image += 19; } else { switch (roadside) { case ROADSIDE_BARREN: pal = PALETTE_TO_BARE_LAND; break; case ROADSIDE_GRASS: break; - default: road -= 19; break; // Paved + default: image -= 19; break; // Paved } } - DrawGroundSprite(road, pal); - - SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis; - /* Draw tracks, but draw PBS reserved tracks darker. */ - pal = (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile)) ? PALETTE_CRASH : PAL_NONE; - DrawGroundSprite(rail, pal); - - DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE); + DrawGroundSprite(image, pal); } else { - SpriteID image = rti->base_sprites.crossing; - - if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++; + SpriteID image = rti->base_sprites.crossing + axis; if (IsCrossingBarred(ti->tile)) image += 2; Roadside roadside = GetRoadside(ti->tile); - if (DrawRoadAsSnowDesert(ti->tile, roadside)) { image += 8; } else { @@ -1383,18 +1700,31 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(image, pal); - - /* PBS debugging, draw reserved tracks darker */ - if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile)) { - DrawGroundSprite(GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y, PALETTE_CRASH); - } } - if (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) { - DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal); - DrawRoadCatenary(ti, GetCrossingRoadBits(ti->tile)); + DrawRoadOverlays(ti, pal, road_rti, tram_rti, axis, axis); + + /* Draw rail/PBS overlay */ + bool draw_pbs = _game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile); + if (rti->UsesOverlay()) { + PaletteID pal = draw_pbs ? PALETTE_CRASH : PAL_NONE; + SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis; + DrawGroundSprite(rail, pal); + + DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE); + } else if (draw_pbs || tram_rti != nullptr || road_rti->UsesOverlay()) { + /* Add another rail overlay, unless there is only the base road sprite. */ + PaletteID pal = draw_pbs ? PALETTE_CRASH : PAL_NONE; + SpriteID rail = GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y; + DrawGroundSprite(rail, pal); } + + /* Draw road, tram catenary */ + DrawRoadCatenary(ti); + + /* Draw rail catenary */ if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); + break; } @@ -1404,15 +1734,42 @@ static void DrawTile_Road(TileInfo *ti) PaletteID palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)); - const DrawTileSprites *dts; - if (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) { - dts = &_tram_depot[GetRoadDepotDirection(ti->tile)]; + RoadType road_rt = GetRoadTypeRoad(ti->tile); + RoadType tram_rt = GetRoadTypeTram(ti->tile); + const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt == INVALID_ROADTYPE ? tram_rt : road_rt); + + int relocation = GetCustomRoadSprite(rti, ti->tile, ROTSG_DEPOT); + bool default_gfx = relocation == 0; + if (default_gfx) { + if (HasBit(rti->flags, ROTF_CATENARY)) { + if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !rti->UsesOverlay()) { + /* Sprites with track only work for default tram */ + relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT; + default_gfx = false; + } else { + /* Sprites without track are always better, if provided */ + relocation = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_ROAD_DEPOT; + } + } } else { - dts = &_road_depot[GetRoadDepotDirection(ti->tile)]; + relocation -= SPR_ROAD_DEPOT; } + DiagDirection dir = GetRoadDepotDirection(ti->tile); + const DrawTileSprites *dts = &_road_depot[dir]; DrawGroundSprite(dts->ground.sprite, PAL_NONE); - DrawOrigTileSeq(ti, dts, TO_BUILDINGS, palette); + + if (default_gfx) { + uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); + if (rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, ti->tile, ROTSG_OVERLAY); + if (ground != 0) DrawGroundSprite(ground + offset, PAL_NONE); + } else if (road_rt == INVALID_ROADTYPE) { + DrawGroundSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE); + } + } + + DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, palette); break; } } @@ -1429,10 +1786,39 @@ static void DrawTile_Road(TileInfo *ti) void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt) { PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company); - const DrawTileSprites *dts = (rt == ROADTYPE_TRAM) ? &_tram_depot[dir] : &_road_depot[dir]; + const RoadTypeInfo* rti = GetRoadTypeInfo(rt); + int relocation = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_DEPOT); + bool default_gfx = relocation == 0; + if (default_gfx) { + if (HasBit(rti->flags, ROTF_CATENARY)) { + if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && RoadTypeIsTram(rt) && !rti->UsesOverlay()) { + /* Sprites with track only work for default tram */ + relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT; + default_gfx = false; + } else { + /* Sprites without track are always better, if provided */ + relocation = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_ROAD_DEPOT; + } + } + } else { + relocation -= SPR_ROAD_DEPOT; + } + + const DrawTileSprites *dts = &_road_depot[dir]; DrawSprite(dts->ground.sprite, PAL_NONE, x, y); - DrawOrigTileSeqInGUI(x, y, dts, palette); + + if (default_gfx) { + uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); + if (rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY); + if (ground != 0) DrawSprite(ground + offset, PAL_NONE, x, y); + } else if (RoadTypeIsTram(rt)) { + DrawSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE, x, y); + } + } + + DrawRailTileSeqInGUI(x, y, dts, relocation, 0, palette); } /** @@ -1570,11 +1956,19 @@ static void TileLoop_Road(TileIndex tile) if (_settings_game.economy.mod_road_rebuild) { /* Generate a nicer town surface */ - const RoadBits old_rb = GetAnyRoadBits(tile, ROADTYPE_ROAD); + const RoadBits old_rb = GetAnyRoadBits(tile, RTT_ROAD); const RoadBits new_rb = CleanUpRoadBits(tile, old_rb); if (old_rb != new_rb) { - RemoveRoad(tile, DC_EXEC | DC_AUTO | DC_NO_WATER, (old_rb ^ new_rb), ROADTYPE_ROAD, true); + RemoveRoad(tile, DC_EXEC | DC_AUTO | DC_NO_WATER, (old_rb ^ new_rb), RTT_ROAD, true); + } + } + + /* Possibly change road type */ + if (GetRoadOwner(tile, RTT_ROAD) == OWNER_TOWN) { + RoadType rt = GetTownRoadType(t); + if (rt != GetRoadTypeRoad(tile)) { + SetRoadType(tile, RTT_ROAD, rt); } } @@ -1619,18 +2013,18 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u if (IsLevelCrossing(tile)) trackdirbits = TrackBitsToTrackdirBits(GetCrossingRailBits(tile)); break; - case TRANSPORT_ROAD: - if ((GetRoadTypes(tile) & sub_mode) == 0) break; + case TRANSPORT_ROAD: { + RoadTramType rtt = (RoadTramType)sub_mode; + if (!HasTileRoadType(tile, rtt)) break; switch (GetRoadTileType(tile)) { case ROAD_TILE_NORMAL: { const uint drd_to_multiplier[DRD_END] = { 0x101, 0x100, 0x1, 0x0 }; - RoadType rt = (RoadType)FindFirstBit(sub_mode); - RoadBits bits = GetRoadBits(tile, rt); + RoadBits bits = GetRoadBits(tile, rtt); /* no roadbit at this side of tile, return 0 */ if (side != INVALID_DIAGDIR && (DiagDirToRoadBits(side) & bits) == 0) break; - uint multiplier = drd_to_multiplier[rt == ROADTYPE_TRAM ? DRD_NONE : GetDisallowedRoadDirections(tile)]; + uint multiplier = drd_to_multiplier[(rtt == RTT_TRAM) ? DRD_NONE : GetDisallowedRoadDirections(tile)]; if (!HasRoadWorks(tile)) trackdirbits = (TrackdirBits)(_road_trackbits[bits] * multiplier); break; } @@ -1656,6 +2050,7 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u } } break; + } default: break; } @@ -1679,13 +2074,25 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td) Owner road_owner = INVALID_OWNER; Owner tram_owner = INVALID_OWNER; + RoadType road_rt = GetRoadTypeRoad(tile); + RoadType tram_rt = GetRoadTypeTram(tile); + if (road_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt); + td->roadtype = rti->strings.name; + td->road_speed = rti->max_speed / 2; + road_owner = GetRoadOwner(tile, RTT_ROAD); + } + if (tram_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt); + td->tramtype = rti->strings.name; + td->tram_speed = rti->max_speed / 2; + tram_owner = GetRoadOwner(tile, RTT_TRAM); + } + switch (GetRoadTileType(tile)) { case ROAD_TILE_CROSSING: { td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_RAIL_LEVEL_CROSSING; - RoadTypes rts = GetRoadTypes(tile); rail_owner = GetTileOwner(tile); - if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile)); td->railtype = rti->strings.name; @@ -1696,15 +2103,11 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td) case ROAD_TILE_DEPOT: td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; - road_owner = GetTileOwner(tile); // Tile has only one owner, roadtype does not matter td->build_date = Depot::GetByTile(tile)->build_date; break; default: { - RoadTypes rts = GetRoadTypes(tile); - td->str = (HasBit(rts, ROADTYPE_ROAD) ? _road_tile_strings[GetRoadside(tile)] : STR_LAI_ROAD_DESCRIPTION_TRAMWAY); - if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + td->str = (road_rt != INVALID_ROADTYPE ? _road_tile_strings[GetRoadside(tile)] : STR_LAI_ROAD_DESCRIPTION_TRAMWAY); break; } } @@ -1774,14 +2177,15 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR); } else { /* A road depot has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */ - RoadType rt = (RoadType)FIND_FIRST_BIT(GetRoadTypes(tile)); + RoadType rt = GetRoadTypeRoad(tile); + if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile); Company::Get(old_owner)->infrastructure.road[rt] -= 2; Company::Get(new_owner)->infrastructure.road[rt] += 2; SetTileOwner(tile, new_owner); - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { - if (GetRoadOwner(tile, rt) == old_owner) { - SetRoadOwner(tile, rt, new_owner); + FOR_ALL_ROADTRAMTYPES(rtt) { + if (GetRoadOwner(tile, rtt) == old_owner) { + SetRoadOwner(tile, rtt, new_owner); } } } @@ -1789,17 +2193,18 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne return; } - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { + FOR_ALL_ROADTRAMTYPES(rtt) { /* Update all roadtypes, no matter if they are present */ - if (GetRoadOwner(tile, rt) == old_owner) { - if (HasTileRoadType(tile, rt)) { + if (GetRoadOwner(tile, rtt) == old_owner) { + RoadType rt = GetRoadType(tile, rtt); + if (rt != INVALID_ROADTYPE) { /* A level crossing has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */ - uint num_bits = IsLevelCrossing(tile) ? 2 : CountBits(GetRoadBits(tile, rt)); + uint num_bits = IsLevelCrossing(tile) ? 2 : CountBits(GetRoadBits(tile, rtt)); Company::Get(old_owner)->infrastructure.road[rt] -= num_bits; if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_bits; } - SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); + SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); } } @@ -1858,6 +2263,228 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); } +/** Update power of road vehicle under which is the roadtype being converted */ +static Vehicle *UpdateRoadVehPowerProc(Vehicle *v, void *data) +{ + if (v->type != VEH_ROAD) return nullptr; + + RoadVehicleList *affected_rvs = static_cast(data); + include(*affected_rvs, RoadVehicle::From(v)->First()); + + return nullptr; +} + +/** + * Checks the tile and returns whether the current player is allowed to convert the roadtype to another roadtype + * @param owner the tile owner. + * @param rtt Road/tram type. + * @return whether the road is convertible + */ +static bool CanConvertRoadType(Owner owner, RoadTramType rtt) +{ + return (owner == OWNER_NONE || (owner == OWNER_TOWN && rtt == RTT_ROAD)); +} + +/** + * Convert the ownership of the RoadType of the tile if applyable + * @param tile the tile of which convert ownership + * @param num_pieces the count of the roadbits to assign to the new owner + * @param owner the current owner of the RoadType + * @param from_type the old road type + * @param to_type the new road type + */ +static void ConvertRoadTypeOwner(TileIndex tile, uint num_pieces, Owner owner, RoadType from_type, RoadType to_type) +{ + // Scenario editor, maybe? Don't touch the owners when converting roadtypes... + if (_current_company >= MAX_COMPANIES) return; + + // We can't get a company from invalid owners but we can get ownership of roads without an owner + if (owner >= MAX_COMPANIES && owner != OWNER_NONE) return; + + Company *c; + + switch (owner) { + case OWNER_NONE: + SetRoadOwner(tile, GetRoadTramType(to_type), (Owner)_current_company); + UpdateCompanyRoadInfrastructure(to_type, _current_company, num_pieces); + break; + + default: + c = Company::Get(owner); + c->infrastructure.road[from_type] -= num_pieces; + c->infrastructure.road[to_type] += num_pieces; + DirtyCompanyInfrastructureWindows(c->index); + break; + } +} + +/** + * Convert one road subtype to another. + * Not meant to convert from road to tram. + * + * @param tile end tile of road conversion drag + * @param flags operation to perform + * @param p1 start tile of drag + * @param p2 various bitstuffed elements: + * - p2 = (bit 0..5) new roadtype to convert to. + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + RoadType to_type = Extract(p2); + + TileIndex area_start = p1; + TileIndex area_end = tile; + + if (!ValParamRoadType(to_type)) return CMD_ERROR; + if (area_start >= MapSize()) return CMD_ERROR; + + RoadVehicleList affected_rvs; + RoadTramType rtt = GetRoadTramType(to_type); + + CommandCost cost(EXPENSES_CONSTRUCTION); + CommandCost error = CommandCost((rtt == RTT_TRAM) ? STR_ERROR_NO_SUITABLE_TRAMWAY : STR_ERROR_NO_SUITABLE_ROAD); // by default, there is no road to convert. + + TileIterator *iter = new OrthogonalTileIterator(area_start, area_end); + for (; (tile = *iter) != INVALID_TILE; ++(*iter)) { + /* Is road present on tile? */ + if (!MayHaveRoad(tile)) continue; + + /* Converting to the same subtype? */ + RoadType from_type = GetRoadType(tile, rtt); + if (from_type == INVALID_ROADTYPE || from_type == to_type) continue; + + /* Check if there is any infrastructure on tile */ + TileType tt = GetTileType(tile); + switch (tt) { + case MP_STATION: + if (!IsRoadStop(tile)) continue; + break; + case MP_ROAD: + if (IsLevelCrossing(tile) && RoadNoLevelCrossing(to_type)) { + error.MakeError(STR_ERROR_CROSSING_DISALLOWED_ROAD); + continue; + } + break; + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) continue; + break; + default: continue; + } + + /* Trying to convert other's road */ + Owner owner = GetRoadOwner(tile, rtt); + if (!CanConvertRoadType(owner, rtt)) { + CommandCost ret = CheckOwnership(owner, tile); + if (ret.Failed()) { + error = ret; + continue; + } + } + + /* Vehicle on the tile when not converting normal <-> powered + * Tunnels and bridges have special check later */ + if (tt != MP_TUNNELBRIDGE) { + if (!HasPowerOnRoad(from_type, to_type)) { + CommandCost ret = EnsureNoVehicleOnGround(tile); + if (ret.Failed()) { + error = ret; + continue; + } + + if (rtt == RTT_ROAD && owner == OWNER_TOWN) { + error.MakeError(STR_ERROR_INCOMPATIBLE_ROAD); + continue; + } + } + + uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt));; + cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type)); + + if (flags & DC_EXEC) { // we can safely convert, too + /* Update the company infrastructure counters. */ + if (!IsRoadStopTile(tile) && CanConvertRoadType(owner, rtt) && owner != OWNER_TOWN) { + ConvertRoadTypeOwner(tile, num_pieces, owner, from_type, to_type); + } + + /* Perform the conversion */ + SetRoadType(tile, rtt, to_type); + MarkTileDirtyByTile(tile); + + /* update power of train on this tile */ + FindVehicleOnPos(tile, &affected_rvs, &UpdateRoadVehPowerProc); + + if (IsRoadDepotTile(tile)) { + /* Update build vehicle window related to this depot */ + InvalidateWindowData(WC_VEHICLE_DEPOT, tile); + InvalidateWindowData(WC_BUILD_VEHICLE, tile); + } + } + } else { + TileIndex endtile = GetOtherTunnelBridgeEnd(tile); + + /* If both ends of tunnel/bridge are in the range, do not try to convert twice - + * it would cause assert because of different test and exec runs */ + if (endtile < tile) { + if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue; + } + + /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ + if (!HasPowerOnRoad(from_type, to_type)) { + CommandCost ret = TunnelBridgeIsFree(tile, endtile); + if (ret.Failed()) { + error = ret; + continue; + } + + if (rtt == RTT_ROAD && owner == OWNER_TOWN) { + error.MakeError(STR_ERROR_INCOMPATIBLE_ROAD); + continue; + } + } + + /* There are 2 pieces on *every* tile of the bridge or tunnel */ + uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * 2; + cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type)); + + if (flags & DC_EXEC) { + /* Update the company infrastructure counters. */ + if (CanConvertRoadType(owner, rtt) && owner != OWNER_TOWN) { + ConvertRoadTypeOwner(tile, num_pieces, owner, from_type, to_type); + ConvertRoadTypeOwner(endtile, num_pieces, owner, from_type, to_type); + SetTunnelBridgeOwner(tile, endtile, _current_company); + } + + /* Perform the conversion */ + SetRoadType(tile, rtt, to_type); + SetRoadType(endtile, rtt, to_type); + + FindVehicleOnPos(tile, &affected_rvs, &UpdateRoadVehPowerProc); + FindVehicleOnPos(endtile, &affected_rvs, &UpdateRoadVehPowerProc); + + if (IsBridge(tile)) { + MarkBridgeDirty(tile); + } else { + MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(endtile); + } + } + } + } + + if (flags & DC_EXEC) { + /* Roadtype changed, update roadvehicles as when entering different track */ + for (RoadVehicle *v : affected_rvs) { + v->CargoChanged(); + } + } + + delete iter; + return (cost.GetCost() == 0) ? error : cost; +} + + /** Tile callback functions for road tiles */ extern const TileTypeProcs _tile_type_road_procs = { DrawTile_Road, // draw_tile_proc diff --git a/src/road_func.h b/src/road_func.h index 06be7c4aa0..ebb68de69d 100644 --- a/src/road_func.h +++ b/src/road_func.h @@ -13,29 +13,9 @@ #define ROAD_FUNC_H #include "core/bitmath_func.hpp" -#include "road_type.h" +#include "road.h" #include "economy_func.h" - -/** - * Iterate through each set RoadType in a RoadTypes value. - * For more informations see FOR_EACH_SET_BIT_EX. - * - * @param var Loop index variable that stores fallowing set road type. Must be of type RoadType. - * @param road_types The value to iterate through (any expression). - * - * @see FOR_EACH_SET_BIT_EX - */ -#define FOR_EACH_SET_ROADTYPE(var, road_types) FOR_EACH_SET_BIT_EX(RoadType, var, RoadTypes, road_types) - -/** - * Whether the given roadtype is valid. - * @param rt the roadtype to check for validness - * @return true if and only if valid - */ -static inline bool IsValidRoadType(RoadType rt) -{ - return rt == ROADTYPE_ROAD || rt == ROADTYPE_TRAM; -} +#include "transparency.h" /** * Whether the given roadtype is valid. @@ -47,32 +27,6 @@ static inline bool IsValidRoadBits(RoadBits r) return r < ROAD_END; } -/** - * Maps a RoadType to the corresponding RoadTypes value - * - * @param rt the roadtype to get the roadtypes from - * @return the roadtypes with the given roadtype - */ -static inline RoadTypes RoadTypeToRoadTypes(RoadType rt) -{ - assert(IsValidRoadType(rt)); - return (RoadTypes)(1 << rt); -} - -/** - * Returns the RoadTypes which are not present in the given RoadTypes - * - * This function returns the complement of a given RoadTypes. - * - * @param r The given RoadTypes - * @return The complement of the given RoadTypes - */ -static inline RoadTypes ComplementRoadTypes(RoadTypes r) -{ - return (RoadTypes)(ROADTYPES_ALL ^ r); -} - - /** * Calculate the complement of a RoadBits value * @@ -167,18 +121,44 @@ static inline RoadBits AxisToRoadBits(Axis a) * Calculates the maintenance cost of a number of road bits. * @param roadtype Road type to get the cost for. * @param num Number of road bits. + * @param total_num Total number of road bits of all road/tram-types. * @return Total cost. */ -static inline Money RoadMaintenanceCost(RoadType roadtype, uint32 num) +static inline Money RoadMaintenanceCost(RoadType roadtype, uint32 num, uint32 total_num) +{ + assert(roadtype < ROADTYPE_END); + return (_price[PR_INFRASTRUCTURE_ROAD] * GetRoadTypeInfo(roadtype)->maintenance_multiplier * num * (1 + IntSqrt(total_num))) >> 12; +} + +/** + * Test if a road type has catenary + * @param roadtype Road type to test + */ +static inline bool HasRoadCatenary(RoadType roadtype) { - assert(IsValidRoadType(roadtype)); - return (_price[PR_INFRASTRUCTURE_ROAD] * (roadtype == ROADTYPE_TRAM ? 3 : 2) * num * (1 + IntSqrt(num))) >> 9; // 2 bits fraction for the multiplier and 7 bits scaling. + assert(roadtype < ROADTYPE_END); + return HasBit(GetRoadTypeInfo(roadtype)->flags, ROTF_CATENARY); } -bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts); -bool ValParamRoadType(const RoadType rt); -RoadTypes GetCompanyRoadtypes(const CompanyID company); +/** + * Test if we should draw road catenary + * @param roadtype Road type to test + */ +static inline bool HasRoadCatenaryDrawn(RoadType roadtype) +{ + return HasRoadCatenary(roadtype) && !IsInvisibilitySet(TO_CATENARY); +} + +bool HasRoadTypeAvail(CompanyID company, RoadType roadtype); +bool ValParamRoadType(RoadType roadtype); +RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces = true); +RoadTypes GetRoadTypes(bool introduces); +RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date); void UpdateLevelCrossing(TileIndex tile, bool sound = true); +void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count); + +struct TileInfo; +void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rit, uint road_offset, uint tram_offset); #endif /* ROAD_FUNC_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index ca3bd6d08b..67132f0938 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -29,6 +29,10 @@ #include "hotkeys.h" #include "road_gui.h" #include "zoom_func.h" +#include "engine_base.h" +#include "strings_func.h" +#include "core/geometry_func.hpp" +#include "date_func.h" #include "widgets/road_widget.h" @@ -110,52 +114,6 @@ void CcBuildRoadTunnel(const CommandCost &result, TileIndex start_tile, uint32 p } } -/** Structure holding information per roadtype for several functions */ -struct RoadTypeInfo { - StringID err_build_road; ///< Building a normal piece of road - StringID err_remove_road; ///< Removing a normal piece of road - StringID err_depot; ///< Building a depot - StringID err_build_station[2]; ///< Building a bus or truck station - StringID err_remove_station[2]; ///< Removing of a bus or truck station - - StringID picker_title[2]; ///< Title for the station picker for bus or truck stations - StringID picker_tooltip[2]; ///< Tooltip for the station picker for bus or truck stations - - SpriteID cursor_nesw; ///< Cursor for building NE and SW bits - SpriteID cursor_nwse; ///< Cursor for building NW and SE bits - SpriteID cursor_autoroad; ///< Cursor for building autoroad -}; - -/** What errors/cursors must be shown for several types of roads */ -static const RoadTypeInfo _road_type_infos[] = { - { - STR_ERROR_CAN_T_BUILD_ROAD_HERE, - STR_ERROR_CAN_T_REMOVE_ROAD_FROM, - STR_ERROR_CAN_T_BUILD_ROAD_DEPOT, - { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_TRUCK_STATION }, - { STR_ERROR_CAN_T_REMOVE_BUS_STATION, STR_ERROR_CAN_T_REMOVE_TRUCK_STATION }, - { STR_STATION_BUILD_BUS_ORIENTATION, STR_STATION_BUILD_TRUCK_ORIENTATION }, - { STR_STATION_BUILD_BUS_ORIENTATION_TOOLTIP, STR_STATION_BUILD_TRUCK_ORIENTATION_TOOLTIP }, - - SPR_CURSOR_ROAD_NESW, - SPR_CURSOR_ROAD_NWSE, - SPR_CURSOR_AUTOROAD, - }, - { - STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE, - STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM, - STR_ERROR_CAN_T_BUILD_TRAM_DEPOT, - { STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION }, - { STR_ERROR_CAN_T_REMOVE_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_REMOVE_CARGO_TRAM_STATION }, - { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION }, - { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION_TOOLTIP, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION_TOOLTIP }, - - SPR_CURSOR_TRAMWAY_NESW, - SPR_CURSOR_TRAMWAY_NWSE, - SPR_CURSOR_AUTOTRAM, - }, -}; - /** * If required, connects a new structure to an existing road or tram by building the missing roadbit. * @param tile Tile containing the structure to connect. @@ -166,7 +124,7 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction) tile += TileOffsByDiagDir(direction); /* if there is a roadpiece just outside of the station entrance, build a connecting route */ if (IsNormalRoadTile(tile)) { - if (GetRoadBits(tile, _cur_roadtype) != ROAD_NONE) { + if (GetRoadBits(tile, GetRoadTramType(_cur_roadtype)) != ROAD_NONE) { DoCommandP(tile, _cur_roadtype << 4 | DiagDirToRoadBits(ReverseDiagDir(direction)), 0, CMD_BUILD_ROAD); } } @@ -190,9 +148,10 @@ void CcRoadDepot(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2 * bit 8..15: Length of the road stop. * @param p2 bit 0: 0 For bus stops, 1 for truck stops. * bit 1: 0 For normal stops, 1 for drive-through. - * bit 2..3: The roadtypes. - * bit 5: Allow stations directly adjacent to other stations. - * bit 6..7: Entrance direction (#DiagDirection). + * bit 2: Allow stations directly adjacent to other stations. + * bit 3..4: Entrance direction (#DiagDirection) for normal stops. + * bit 3: #Axis of the road for drive-through stops. + * bit 5..9: The roadtype. * bit 16..31: Station ID to join (NEW_STATION if build new one). * @see CmdBuildRoadStop */ @@ -200,7 +159,7 @@ void CcRoadStop(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) { if (result.Failed()) return; - DiagDirection dir = (DiagDirection)GB(p2, 6, 2); + DiagDirection dir = (DiagDirection)GB(p2, 3, 2); if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); TileArea roadstop_area(tile, GB(p1, 0, 8), GB(p1, 8, 8)); @@ -216,8 +175,8 @@ void CcRoadStop(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) * @param start_tile First tile of the area. * @param end_tile Last tile of the area. * @param p2 bit 0: 0 For bus stops, 1 for truck stops. - * bit 2..3: The roadtypes. - * bit 5: Allow stations directly adjacent to other stations. + * bit 2: Allow stations directly adjacent to other stations. + * bit 5..10: The roadtypes. * @param cmd Command to use. * @see CcRoadStop() */ @@ -230,7 +189,7 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, uint32 p2, u SetBit(p2, 1); // It's a drive-through stop. ddir -= DIAGDIR_END; // Adjust picker result to actual direction. } - p2 |= ddir << 6; // Set the DiagDirecion into p2 bits 6 and 7. + p2 |= ddir << 3; // Set the DiagDirecion into p2 bits 3 and 4. TileArea ta(start_tile, end_tile); CommandContainer cmdcont = { ta.tile, (uint32)(ta.w | ta.h << 8), p2, cmd, CcRoadStop, "" }; @@ -309,15 +268,20 @@ static bool RoadToolbar_CtrlChanged(Window *w) /** Road toolbar window handler. */ struct BuildRoadToolbarWindow : Window { - int last_started_action; ///< Last started user action. + RoadType roadtype; ///< Road type to build. + const RoadTypeInfo *rti; ///< Informations about current road type + int last_started_action; ///< Last started user action. BuildRoadToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { + this->Initialize(_cur_roadtype); this->InitNested(window_number); - this->SetWidgetsDisabledState(true, - WID_ROT_REMOVE, - WID_ROT_ONE_WAY, - WIDGET_LIST_END); + this->SetupRoadToolbar(); + this->SetWidgetDisabledState(WID_ROT_REMOVE, true); + + if (RoadTypeIsRoad(this->roadtype)) { + this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true); + } this->OnInvalidateData(); this->last_started_action = WIDGET_LIST_END; @@ -340,7 +304,8 @@ struct BuildRoadToolbarWindow : Window { { if (!gui_scope) return; - bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD); + if (_game_mode != GM_EDITOR && !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) delete this; + bool can_build = _game_mode != GM_EDITOR; this->SetWidgetsDisabledState(!can_build, WID_ROT_DEPOT, WID_ROT_BUS_STATION, @@ -353,6 +318,53 @@ struct BuildRoadToolbarWindow : Window { } } + void Initialize(RoadType roadtype) + { + assert(roadtype < ROADTYPE_END); + this->roadtype = roadtype; + this->rti = GetRoadTypeInfo(this->roadtype); + } + + /** + * Configures the road toolbar for roadtype given + * @param roadtype the roadtype to display + */ + void SetupRoadToolbar() + { + this->GetWidget(WID_ROT_ROAD_X)->widget_data = rti->gui_sprites.build_x_road; + this->GetWidget(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road; + this->GetWidget(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road; + if (_game_mode != GM_EDITOR) { + this->GetWidget(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot; + } + this->GetWidget(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road; + this->GetWidget(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel; + } + + /** + * Switch to another road type. + * @param roadtype New road type. + */ + void ModifyRoadType(RoadType roadtype) + { + this->Initialize(roadtype); + this->SetupRoadToolbar(); + this->ReInit(); + } + + void SetStringParameters(int widget) const override + { + if (widget == WID_ROT_CAPTION) { + if (this->rti->max_speed > 0) { + SetDParam(0, STR_TOOLBAR_RAILTYPE_VELOCITY); + SetDParam(1, this->rti->strings.toolbar_caption); + SetDParam(2, this->rti->max_speed / 2); + } else { + SetDParam(0, this->rti->strings.toolbar_caption); + } + } + } + /** * Update the remove button lowered state of the road toolbar * @@ -365,8 +377,11 @@ struct BuildRoadToolbarWindow : Window { * Both are only valid if they are able to apply as options. */ switch (clicked_widget) { case WID_ROT_REMOVE: - this->RaiseWidget(WID_ROT_ONE_WAY); - this->SetWidgetDirty(WID_ROT_ONE_WAY); + if (RoadTypeIsRoad(this->roadtype)) { + this->RaiseWidget(WID_ROT_ONE_WAY); + this->SetWidgetDirty(WID_ROT_ONE_WAY); + } + break; case WID_ROT_ONE_WAY: @@ -376,30 +391,30 @@ struct BuildRoadToolbarWindow : Window { case WID_ROT_BUS_STATION: case WID_ROT_TRUCK_STATION: - this->DisableWidget(WID_ROT_ONE_WAY); + if (RoadTypeIsRoad(this->roadtype)) this->DisableWidget(WID_ROT_ONE_WAY); this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget)); break; case WID_ROT_ROAD_X: case WID_ROT_ROAD_Y: case WID_ROT_AUTOROAD: - this->SetWidgetsDisabledState(!this->IsWidgetLowered(clicked_widget), - WID_ROT_REMOVE, - WID_ROT_ONE_WAY, - WIDGET_LIST_END); + this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget)); + if (RoadTypeIsRoad(this->roadtype)) { + this->SetWidgetDisabledState(WID_ROT_ONE_WAY, !this->IsWidgetLowered(clicked_widget)); + } break; default: /* When any other buttons than road/station, raise and * disable the removal button */ - this->SetWidgetsDisabledState(true, - WID_ROT_REMOVE, - WID_ROT_ONE_WAY, - WIDGET_LIST_END); - this->SetWidgetsLoweredState(false, - WID_ROT_REMOVE, - WID_ROT_ONE_WAY, - WIDGET_LIST_END); + this->SetWidgetDisabledState(WID_ROT_REMOVE, true); + this->SetWidgetLoweredState(WID_ROT_REMOVE, false); + + if (RoadTypeIsRoad(this->roadtype)) { + this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true); + this->SetWidgetLoweredState(WID_ROT_ONE_WAY, false); + } + break; } } @@ -410,17 +425,17 @@ struct BuildRoadToolbarWindow : Window { _one_way_button_clicked = false; switch (widget) { case WID_ROT_ROAD_X: - HandlePlacePushButton(this, WID_ROT_ROAD_X, _road_type_infos[_cur_roadtype].cursor_nwse, HT_RECT); + HandlePlacePushButton(this, WID_ROT_ROAD_X, this->rti->cursor.road_nwse, HT_RECT); this->last_started_action = widget; break; case WID_ROT_ROAD_Y: - HandlePlacePushButton(this, WID_ROT_ROAD_Y, _road_type_infos[_cur_roadtype].cursor_nesw, HT_RECT); + HandlePlacePushButton(this, WID_ROT_ROAD_Y, this->rti->cursor.road_swne, HT_RECT); this->last_started_action = widget; break; case WID_ROT_AUTOROAD: - HandlePlacePushButton(this, WID_ROT_AUTOROAD, _road_type_infos[_cur_roadtype].cursor_autoroad, HT_RECT); + HandlePlacePushButton(this, WID_ROT_AUTOROAD, this->rti->cursor.autoroad, HT_RECT); this->last_started_action = widget; break; @@ -430,15 +445,15 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: - if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return; - if (HandlePlacePushButton(this, WID_ROT_DEPOT, SPR_CURSOR_ROAD_DEPOT, HT_RECT)) { + if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return; + if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) { ShowRoadDepotPicker(this); this->last_started_action = widget; } break; case WID_ROT_BUS_STATION: - if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return; + if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return; if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT)) { ShowRVStationPicker(this, ROADSTOP_BUS); this->last_started_action = widget; @@ -446,7 +461,7 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_TRUCK_STATION: - if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return; + if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return; if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT)) { ShowRVStationPicker(this, ROADSTOP_TRUCK); this->last_started_action = widget; @@ -466,7 +481,7 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_BUILD_TUNNEL: - HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL, SPR_CURSOR_ROAD_TUNNEL, HT_SPECIAL); + HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL, this->rti->cursor.tunnel, HT_SPECIAL); this->last_started_action = widget; break; @@ -478,6 +493,11 @@ struct BuildRoadToolbarWindow : Window { if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); break; + case WID_ROT_CONVERT_ROAD: + HandlePlacePushButton(this, WID_ROT_CONVERT_ROAD, this->rti->cursor.convert_road, HT_RECT); + this->last_started_action = widget; + break; + default: NOT_REACHED(); } this->UpdateOptionWidgetStatus((RoadToolbarWidgets)widget); @@ -493,7 +513,7 @@ struct BuildRoadToolbarWindow : Window { void OnPlaceObject(Point pt, TileIndex tile) override { _remove_button_clicked = this->IsWidgetLowered(WID_ROT_REMOVE); - _one_way_button_clicked = this->IsWidgetLowered(WID_ROT_ONE_WAY); + _one_way_button_clicked = RoadTypeIsRoad(this->roadtype) ? this->IsWidgetLowered(WID_ROT_ONE_WAY) : false; switch (this->last_started_action) { case WID_ROT_ROAD_X: _place_road_flag = RF_DIR_X; @@ -520,7 +540,7 @@ struct BuildRoadToolbarWindow : Window { case WID_ROT_DEPOT: DoCommandP(tile, _cur_roadtype << 2 | _road_depot_orientation, 0, - CMD_BUILD_ROAD_DEPOT | CMD_MSG(_road_type_infos[_cur_roadtype].err_depot), CcRoadDepot); + CMD_BUILD_ROAD_DEPOT | CMD_MSG(this->rti->strings.err_depot), CcRoadDepot); break; case WID_ROT_BUS_STATION: @@ -536,10 +556,14 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_BUILD_TUNNEL: - DoCommandP(tile, RoadTypeToRoadTypes(_cur_roadtype) | (TRANSPORT_ROAD << 8), 0, + DoCommandP(tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0, CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE), CcBuildRoadTunnel); break; + case WID_ROT_CONVERT_ROAD: + VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD); + break; + default: NOT_REACHED(); } } @@ -549,12 +573,13 @@ struct BuildRoadToolbarWindow : Window { if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); this->RaiseButtons(); - this->SetWidgetsDisabledState(true, - WID_ROT_REMOVE, - WID_ROT_ONE_WAY, - WIDGET_LIST_END); + this->SetWidgetDisabledState(WID_ROT_REMOVE, true); this->SetWidgetDirty(WID_ROT_REMOVE); - this->SetWidgetDirty(WID_ROT_ONE_WAY); + + if (RoadTypeIsRoad(this->roadtype)) { + this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true); + this->SetWidgetDirty(WID_ROT_ONE_WAY); + } DeleteWindowById(WC_BUS_STATION, TRANSPORT_ROAD); DeleteWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD); @@ -613,7 +638,7 @@ struct BuildRoadToolbarWindow : Window { default: NOT_REACHED(); case DDSP_BUILD_BRIDGE: if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); - ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, RoadTypeToRoadTypes(_cur_roadtype)); + ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, _cur_roadtype); break; case DDSP_DEMOLISH_AREA: @@ -627,12 +652,16 @@ struct BuildRoadToolbarWindow : Window { * Use the first three bits (0x07) if dir == Y * else use the last 2 bits (X dir has * not the 3rd bit set) */ + + /* Even if _cur_roadtype_id is a uint8 we only use 5 bits so + * we could ignore the last 3 bits and reuse them for other + * flags */ _place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3)); - DoCommandP(start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 5), + DoCommandP(start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 10), _remove_button_clicked ? - CMD_REMOVE_LONG_ROAD | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_road) : - CMD_BUILD_LONG_ROAD | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_road), CcPlaySound_SPLAT_OTHER); + CMD_REMOVE_LONG_ROAD | CMD_MSG(this->rti->strings.err_remove_road) : + CMD_BUILD_LONG_ROAD | CMD_MSG(this->rti->strings.err_build_road), CcPlaySound_SPLAT_OTHER); break; case DDSP_BUILD_BUSSTOP: @@ -640,9 +669,9 @@ struct BuildRoadToolbarWindow : Window { if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); - DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER); + DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(this->rti->strings.err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER); } else { - 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, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(this->rti->strings.err_build_station[ROADSTOP_BUS])); } } break; @@ -652,19 +681,23 @@ struct BuildRoadToolbarWindow : Window { if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) { if (_remove_button_clicked) { TileArea ta(start_tile, end_tile); - DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER); + DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(this->rti->strings.err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER); } else { - 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, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(this->rti->strings.err_build_station[ROADSTOP_TRUCK])); } } break; + + case DDSP_CONVERT_ROAD: + DoCommandP(end_tile, start_tile, _cur_roadtype, CMD_CONVERT_ROAD | CMD_MSG(rti->strings.err_convert_road), CcPlaySound_SPLAT_OTHER); + break; } } } void OnPlacePresize(Point pt, TileIndex tile) override { - DoCommand(tile, RoadTypeToRoadTypes(_cur_roadtype) | (TRANSPORT_ROAD << 8), 0, DC_AUTO, CMD_BUILD_TUNNEL); + DoCommand(tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0, DC_AUTO, CMD_BUILD_TUNNEL); VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile); } @@ -674,34 +707,36 @@ struct BuildRoadToolbarWindow : Window { return ES_NOT_HANDLED; } - static HotkeyList hotkeys; + static HotkeyList road_hotkeys; + static HotkeyList tram_hotkeys; }; /** * Handler for global hotkeys of the BuildRoadToolbarWindow. * @param hotkey Hotkey + * @param last_build Last build road type * @return ES_HANDLED if hotkey was accepted. */ -static EventState RoadToolbarGlobalHotkeys(int hotkey) +static EventState RoadTramToolbarGlobalHotkeys(int hotkey, RoadType last_build) { - Window *w = nullptr; - switch (_game_mode) { - case GM_NORMAL: { - extern RoadType _last_built_roadtype; - w = ShowBuildRoadToolbar(_last_built_roadtype); - break; - } + Window *w = (_game_mode == GM_NORMAL) ? ShowBuildRoadToolbar(last_build) : ShowBuildRoadScenToolbar(last_build); + if (w == nullptr) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); +} - case GM_EDITOR: - w = ShowBuildRoadScenToolbar(); - break; +static EventState RoadToolbarGlobalHotkeys(int hotkey) +{ + if (_game_mode == GM_NORMAL && !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_ROAD)) return ES_NOT_HANDLED; - default: - break; - } + extern RoadType _last_built_roadtype; + return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_roadtype); +} - if (w == nullptr) return ES_NOT_HANDLED; - return w->OnHotkey(hotkey); +static EventState TramToolbarGlobalHotkeys(int hotkey) +{ + if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM)) return ES_NOT_HANDLED; + extern RoadType _last_built_tramtype; + return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_tramtype); } static Hotkey roadtoolbar_hotkeys[] = { @@ -716,15 +751,32 @@ static Hotkey roadtoolbar_hotkeys[] = { Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), Hotkey('R', "remove", WID_ROT_REMOVE), + Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), HOTKEY_LIST_END }; -HotkeyList BuildRoadToolbarWindow::hotkeys("roadtoolbar", roadtoolbar_hotkeys, RoadToolbarGlobalHotkeys); +HotkeyList BuildRoadToolbarWindow::road_hotkeys("roadtoolbar", roadtoolbar_hotkeys, RoadToolbarGlobalHotkeys); + +static Hotkey tramtoolbar_hotkeys[] = { + Hotkey('1', "build_x", WID_ROT_ROAD_X), + Hotkey('2', "build_y", WID_ROT_ROAD_Y), + Hotkey('3', "autoroad", WID_ROT_AUTOROAD), + Hotkey('4', "demolish", WID_ROT_DEMOLISH), + Hotkey('5', "depot", WID_ROT_DEPOT), + Hotkey('6', "bus_station", WID_ROT_BUS_STATION), + Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION), + Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE), + Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL), + Hotkey('R', "remove", WID_ROT_REMOVE), + Hotkey('C', "convert", WID_ROT_CONVERT_ROAD), + HOTKEY_LIST_END +}; +HotkeyList BuildRoadToolbarWindow::tram_hotkeys("tramtoolbar", tramtoolbar_hotkeys, TramToolbarGlobalHotkeys); static const NWidgetPart _nested_build_road_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), @@ -751,6 +803,8 @@ static const NWidgetPart _nested_build_road_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD), EndContainer(), }; @@ -759,13 +813,13 @@ static WindowDesc _build_road_desc( WC_BUILD_TOOLBAR, WC_NONE, WDF_CONSTRUCTION, _nested_build_road_widgets, lengthof(_nested_build_road_widgets), - &BuildRoadToolbarWindow::hotkeys + &BuildRoadToolbarWindow::road_hotkeys ); static const NWidgetPart _nested_build_tramway_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_TRAM_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), @@ -784,13 +838,14 @@ static const NWidgetPart _nested_build_tramway_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION), NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(), - NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_ROT_ONE_WAY), SetMinimalSize(0, 0), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE), SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM), EndContainer(), }; @@ -799,7 +854,7 @@ static WindowDesc _build_tramway_desc( WC_BUILD_TOOLBAR, WC_NONE, WDF_CONSTRUCTION, _nested_build_tramway_widgets, lengthof(_nested_build_tramway_widgets), - &BuildRoadToolbarWindow::hotkeys + &BuildRoadToolbarWindow::tram_hotkeys ); /** @@ -812,16 +867,18 @@ static WindowDesc _build_tramway_desc( Window *ShowBuildRoadToolbar(RoadType roadtype) { if (!Company::IsValidID(_local_company)) return nullptr; - _cur_roadtype = roadtype; + if (!ValParamRoadType(roadtype)) return nullptr; DeleteWindowByClass(WC_BUILD_TOOLBAR); - return AllocateWindowDescFront(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD); + _cur_roadtype = roadtype; + + return AllocateWindowDescFront(RoadTypeIsRoad(_cur_roadtype) ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD); } static const NWidgetPart _nested_build_road_scen_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), - NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), EndContainer(), NWidget(NWID_HORIZONTAL), @@ -842,6 +899,8 @@ static const NWidgetPart _nested_build_road_scen_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD), EndContainer(), }; @@ -850,17 +909,54 @@ static WindowDesc _build_road_scen_desc( WC_SCEN_BUILD_TOOLBAR, WC_NONE, WDF_CONSTRUCTION, _nested_build_road_scen_widgets, lengthof(_nested_build_road_scen_widgets), - &BuildRoadToolbarWindow::hotkeys + &BuildRoadToolbarWindow::road_hotkeys +); + +static const NWidgetPart _nested_build_tramway_scen_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE), + SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM), + EndContainer(), +}; + +static WindowDesc _build_tramway_scen_desc( + WDP_AUTO, "toolbar_tram_scen", 0, 0, + WC_SCEN_BUILD_TOOLBAR, WC_NONE, + WDF_CONSTRUCTION, + _nested_build_tramway_scen_widgets, lengthof(_nested_build_tramway_scen_widgets), + &BuildRoadToolbarWindow::tram_hotkeys ); /** * Show the road building toolbar in the scenario editor. * @return The just opened toolbar, or \c nullptr if the toolbar was already open. */ -Window *ShowBuildRoadScenToolbar() +Window *ShowBuildRoadScenToolbar(RoadType roadtype) { - _cur_roadtype = ROADTYPE_ROAD; - return AllocateWindowDescFront(&_build_road_scen_desc, TRANSPORT_ROAD); + DeleteWindowById(WC_SCEN_BUILD_TOOLBAR, TRANSPORT_ROAD); + _cur_roadtype = roadtype; + + return AllocateWindowDescFront(RoadTypeIsRoad(_cur_roadtype) ? &_build_road_scen_desc : &_build_tramway_scen_desc, TRANSPORT_ROAD); } struct BuildRoadDepotWindow : public PickerWindowBase { @@ -869,7 +965,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase { this->CreateNestedTree(); this->LowerWidget(_road_depot_orientation + WID_BROD_DEPOT_NE); - if ( _cur_roadtype == ROADTYPE_TRAM) { + if (RoadTypeIsTram(_cur_roadtype)) { this->GetWidget(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION; for (int i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) this->GetWidget(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP; } @@ -960,13 +1056,14 @@ struct BuildRoadStationWindow : public PickerWindowBase { this->CreateNestedTree(); /* Trams don't have non-drivethrough stations */ - if (_cur_roadtype == ROADTYPE_TRAM && _road_station_picker_orientation < DIAGDIR_END) { + if (RoadTypeIsTram(_cur_roadtype) && _road_station_picker_orientation < DIAGDIR_END) { _road_station_picker_orientation = DIAGDIR_END; } + const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype); + this->GetWidget(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs]; - this->GetWidget(WID_BROS_CAPTION)->widget_data = _road_type_infos[_cur_roadtype].picker_title[rs]; - for (uint i = (_cur_roadtype == ROADTYPE_TRAM ? WID_BROS_STATION_X : WID_BROS_STATION_NE); i < WID_BROS_LT_OFF; i++) { - this->GetWidget(i)->tool_tip = _road_type_infos[_cur_roadtype].picker_tooltip[rs]; + for (uint i = RoadTypeIsTram(_cur_roadtype) ? WID_BROS_STATION_X : WID_BROS_STATION_NE; i < WID_BROS_LT_OFF; i++) { + this->GetWidget(i)->tool_tip = rti->strings.picker_tooltip[rs]; } this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE); @@ -1022,7 +1119,7 @@ struct BuildRoadStationWindow : public PickerWindowBase { if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return; StationType st = (this->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK; - StationPickerDrawSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), st, INVALID_RAILTYPE, widget < WID_BROS_STATION_X ? ROADTYPE_ROAD : _cur_roadtype, widget - WID_BROS_STATION_NE); + StationPickerDrawSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE); } void OnClick(Point pt, int widget, int click_count) override @@ -1149,7 +1246,7 @@ static WindowDesc _tram_station_picker_desc( static void ShowRVStationPicker(Window *parent, RoadStopType rs) { - new BuildRoadStationWindow(_cur_roadtype == ROADTYPE_ROAD ? &_road_station_picker_desc : &_tram_station_picker_desc, parent, rs); + new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype) ? &_road_station_picker_desc : &_tram_station_picker_desc, parent, rs); } void InitializeRoadGui() @@ -1157,3 +1254,118 @@ void InitializeRoadGui() _road_depot_orientation = DIAGDIR_NW; _road_station_picker_orientation = DIAGDIR_NW; } + +/** + * I really don't know why rail_gui.cpp has this too, shouldn't be included in the other one? + */ +void InitializeRoadGUI() +{ + BuildRoadToolbarWindow *w = dynamic_cast(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_ROAD)); + if (w != nullptr) w->ModifyRoadType(_cur_roadtype); +} + +DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, bool all_option) +{ + RoadTypes used_roadtypes; + RoadTypes avail_roadtypes; + + const Company *c = Company::Get(_local_company); + + /* Find the used roadtypes. */ + if (for_replacement) { + avail_roadtypes = GetCompanyRoadTypes(c->index, false); + used_roadtypes = GetRoadTypes(false); + } else { + avail_roadtypes = c->avail_roadtypes; + used_roadtypes = GetRoadTypes(true); + } + + /* Filter listed road types */ + if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type; + if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type; + + DropDownList list; + + if (all_option) { + list.emplace_back(new DropDownListStringItem(STR_REPLACE_ALL_ROADTYPE, INVALID_ROADTYPE, false)); + } + + Dimension d = { 0, 0 }; + RoadType rt; + /* Get largest icon size, to ensure text is aligned on each menu item. */ + if (!for_replacement) { + FOR_ALL_SORTED_ROADTYPES(rt) { + if (!HasBit(used_roadtypes, rt)) continue; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); + } + } + + FOR_ALL_SORTED_ROADTYPES(rt) { + /* If it's not used ever, don't show it to the user. */ + if (!HasBit(used_roadtypes, rt)) continue; + + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + + DropDownListParamStringItem *item; + if (for_replacement) { + item = new DropDownListParamStringItem(rti->strings.replace_text, rt, !HasBit(avail_roadtypes, rt)); + } else { + StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING; + DropDownListIconItem *iconitem = new DropDownListIconItem(rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)); + iconitem->SetDimension(d); + item = iconitem; + } + item->SetParam(0, rti->strings.menu_text); + item->SetParam(1, rti->max_speed / 2); + list.emplace_back(item); + } + + if (list.size() == 0) { + /* Empty dropdowns are not allowed */ + list.emplace_back(new DropDownListStringItem(STR_NONE, INVALID_ROADTYPE, true)); + } + + return list; +} + +DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts) +{ + RoadTypes avail_roadtypes = GetRoadTypes(false); + avail_roadtypes = AddDateIntroducedRoadTypes(avail_roadtypes, _date); + RoadTypes used_roadtypes = GetRoadTypes(true); + + /* Filter listed road types */ + if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type; + if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type; + + DropDownList list; + + /* If it's not used ever, don't show it to the user. */ + Dimension d = { 0, 0 }; + RoadType rt; + FOR_ALL_SORTED_ROADTYPES(rt) { + if (!HasBit(used_roadtypes, rt)) continue; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road)); + } + FOR_ALL_SORTED_ROADTYPES(rt) { + if (!HasBit(used_roadtypes, rt)) continue; + + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + + StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING; + DropDownListIconItem *item = new DropDownListIconItem(rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)); + item->SetDimension(d); + item->SetParam(0, rti->strings.menu_text); + item->SetParam(1, rti->max_speed); + list.emplace_back(item); + } + + if (list.size() == 0) { + /* Empty dropdowns are not allowed */ + list.emplace_back(new DropDownListStringItem(STR_NONE, -1, true)); + } + + return list; +} diff --git a/src/road_gui.h b/src/road_gui.h index c56443c375..3f0b8f9006 100644 --- a/src/road_gui.h +++ b/src/road_gui.h @@ -15,9 +15,13 @@ #include "road_type.h" #include "tile_type.h" #include "direction_type.h" +#include "widgets/dropdown_type.h" struct Window *ShowBuildRoadToolbar(RoadType roadtype); -struct Window *ShowBuildRoadScenToolbar(); +struct Window *ShowBuildRoadScenToolbar(RoadType roadtype); void ConnectRoadToStructure(TileIndex tile, DiagDirection direction); +DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement = false, bool all_option = false); +DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts); +void InitializeRoadGUI(); #endif /* ROAD_GUI_H */ diff --git a/src/road_internal.h b/src/road_internal.h index 8da909e94a..79782e3bba 100644 --- a/src/road_internal.h +++ b/src/road_internal.h @@ -17,8 +17,8 @@ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb); -CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadType rt, DoCommandFlag flags, bool town_check = true); +CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlag flags, bool town_check = true); -void DrawRoadCatenary(const TileInfo *ti, RoadBits tram); +void DrawRoadCatenary(const TileInfo *ti); #endif /* ROAD_INTERNAL_H */ diff --git a/src/road_map.cpp b/src/road_map.cpp index 4984117bab..1ab95043ad 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -32,15 +32,15 @@ * @param straight_tunnel_bridge_entrance whether to return straight road bits for tunnels/bridges. * @return the road bits of the given tile */ -RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance) +RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance) { - if (!HasTileRoadType(tile, rt)) return ROAD_NONE; + if (!MayHaveRoad(tile) || !HasTileRoadType(tile, rtt)) return ROAD_NONE; switch (GetTileType(tile)) { case MP_ROAD: switch (GetRoadTileType(tile)) { default: - case ROAD_TILE_NORMAL: return GetRoadBits(tile, rt); + case ROAD_TILE_NORMAL: return GetRoadBits(tile, rtt); case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile); case ROAD_TILE_DEPOT: return DiagDirToRoadBits(GetRoadDepotDirection(tile)); } diff --git a/src/road_map.h b/src/road_map.h index 49526d37f2..f5600d590d 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -26,6 +26,24 @@ enum RoadTileType { ROAD_TILE_DEPOT, ///< Depot (one entrance) }; +/** + * Test whether a tile can have road/tram types. + * @param t Tile to query. + * @return true if tile can be queried about road/tram types. + */ +static inline bool MayHaveRoad(TileIndex t) +{ + switch (GetTileType(t)) { + case MP_ROAD: + case MP_STATION: + case MP_TUNNELBRIDGE: + return true; + + default: + return false; + } +} + /** * Get the type of the road tile. * @param t Tile to query. @@ -108,26 +126,11 @@ 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) +static inline RoadBits GetRoadBits(TileIndex t, RoadTramType rtt) { 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); - } -} - -/** - * Get all RoadBits set on a tile except from the given RoadType - * - * @param t The tile from which we want to get the RoadBits - * @param rt The RoadType which we exclude from the querry - * @return all set RoadBits of the tile which are not from the given RoadType - */ -static inline RoadBits GetOtherRoadBits(TileIndex t, RoadType rt) -{ - return GetRoadBits(t, rt == ROADTYPE_ROAD ? ROADTYPE_TRAM : ROADTYPE_ROAD); + if (rtt == RTT_TRAM) return (RoadBits)GB(_m[t].m3, 0, 4); + return (RoadBits)GB(_m[t].m5, 0, 4); } /** @@ -138,7 +141,7 @@ static inline RoadBits GetOtherRoadBits(TileIndex t, RoadType rt) */ static inline RoadBits GetAllRoadBits(TileIndex tile) { - return GetRoadBits(tile, ROADTYPE_ROAD) | GetRoadBits(tile, ROADTYPE_TRAM); + return GetRoadBits(tile, RTT_ROAD) | GetRoadBits(tile, RTT_TRAM); } /** @@ -148,96 +151,125 @@ static inline RoadBits GetAllRoadBits(TileIndex tile) * @param rt Road type. * @pre IsNormalRoad(t) */ -static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt) +static inline void SetRoadBits(TileIndex t, RoadBits r, RoadTramType rtt) { 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; + if (rtt == RTT_TRAM) { + SB(_m[t].m3, 0, 4, r); + } else { + SB(_m[t].m5, 0, 4, r); } } +static inline RoadType GetRoadTypeRoad(TileIndex t) +{ + assert(MayHaveRoad(t)); + return (RoadType)GB(_m[t].m4, 0, 6); +} + +static inline RoadType GetRoadTypeTram(TileIndex t) +{ + assert(MayHaveRoad(t)); + return (RoadType)GB(_me[t].m8, 6, 6); +} + +static inline RoadType GetRoadType(TileIndex t, RoadTramType rtt) +{ + return (rtt == RTT_TRAM) ? GetRoadTypeTram(t) : GetRoadTypeRoad(t); +} + /** * Get the present road types of a tile. * @param t The tile to query. * @return Present road types. */ -static inline RoadTypes GetRoadTypes(TileIndex t) +static inline RoadTypes GetPresentRoadTypes(TileIndex t) { - return (RoadTypes)GB(_me[t].m7, 6, 2); + RoadTypes result = ROADTYPES_NONE; + if (MayHaveRoad(t)) { + if (GetRoadTypeRoad(t) != INVALID_ROADTYPE) SetBit(result, GetRoadTypeRoad(t)); + if (GetRoadTypeTram(t) != INVALID_ROADTYPE) SetBit(result, GetRoadTypeTram(t)); + } + return result; +} + +static inline bool HasRoadTypeRoad(TileIndex t) +{ + return GetRoadTypeRoad(t) != INVALID_ROADTYPE; +} + +static inline bool HasRoadTypeTram(TileIndex t) +{ + return GetRoadTypeTram(t) != INVALID_ROADTYPE; } /** - * Set the present road types of a tile. - * @param t The tile to change. - * @param rt The new road types. + * Check if a tile has a road or a tram road type. + * @param t The tile to check. + * @param tram True to check tram, false to check road. + * @return True if the tile has the specified road type. */ -static inline void SetRoadTypes(TileIndex t, RoadTypes rt) +static inline bool HasTileRoadType(TileIndex t, RoadTramType rtt) { - assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); - SB(_me[t].m7, 6, 2, rt); + return GetRoadType(t, rtt) != INVALID_ROADTYPE; } /** - * Check if a tile has a specific road type. + * Check if a tile has one of the specified road types. * @param t The tile to check. - * @param rt Road type to check. - * @return True if the tile has the specified road type. + * @param rts Allowed road types. + * @return True if the tile has one of the specified road types. */ -static inline bool HasTileRoadType(TileIndex t, RoadType rt) +static inline bool HasTileAnyRoadType(TileIndex t, RoadTypes rts) { - return HasBit(GetRoadTypes(t), rt); + if (!MayHaveRoad(t)) return false; + return (GetPresentRoadTypes(t) & rts); } /** * Get the owner of a specific road type. * @param t The tile to query. - * @param rt The road type to get the owner of. + * @param rtt RoadTramType. * @return Owner of the given road type. */ -static inline Owner GetRoadOwner(TileIndex 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_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); - return o == OWNER_TOWN ? OWNER_NONE : o; - } - } +static inline Owner GetRoadOwner(TileIndex t, RoadTramType rtt) +{ + assert(MayHaveRoad(t)); + if (rtt == RTT_ROAD) return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5); + + /* 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); + return o == OWNER_TOWN ? OWNER_NONE : o; } /** * Set the owner of a specific road type. * @param t The tile to change. - * @param rt The road type to change the owner of. + * @param rtt RoadTramType. * @param o New owner of the given road type. */ -static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) +static inline void SetRoadOwner(TileIndex t, RoadTramType rtt, 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; + if (rtt == RTT_ROAD) { + SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o); + } else { + SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); } } /** * Check if a specific road type is owned by an owner. * @param t The tile to query. - * @param rt The road type to compare the owner of. + * @param tram True to check tram, false to check road. * @param o Owner to compare with. * @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) +static inline bool IsRoadOwner(TileIndex t, RoadTramType rtt, Owner o) { - assert(HasTileRoadType(t, rt)); - return (GetRoadOwner(t, rt) == o); + assert(HasTileRoadType(t, rtt)); + return (GetRoadOwner(t, rtt) == o); } /** @@ -248,7 +280,7 @@ static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o) */ static inline bool HasTownOwnedRoad(TileIndex t) { - return HasTileRoadType(t, ROADTYPE_ROAD) && IsRoadOwner(t, ROADTYPE_ROAD, OWNER_TOWN); + return HasTileRoadType(t, RTT_ROAD) && IsRoadOwner(t, RTT_ROAD, OWNER_TOWN); } /** Which directions are disallowed ? */ @@ -539,29 +571,80 @@ static inline DiagDirection GetRoadDepotDirection(TileIndex t) } -RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance = false); +RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); +/** + * Set the road road type of a tile. + * @param t The tile to change. + * @param rt The road type to set. + */ +static inline void SetRoadTypeRoad(TileIndex t, RoadType rt) +{ + assert(MayHaveRoad(t)); + assert(rt == INVALID_ROADTYPE || RoadTypeIsRoad(rt)); + SB(_m[t].m4, 0, 6, rt); +} + +/** + * Set the tram road type of a tile. + * @param t The tile to change. + * @param rt The road type to set. + */ +static inline void SetRoadTypeTram(TileIndex t, RoadType rt) +{ + assert(MayHaveRoad(t)); + assert(rt == INVALID_ROADTYPE || RoadTypeIsTram(rt)); + SB(_me[t].m8, 6, 6, rt); +} + +/** + * Set the road type of a tile. + * @param t The tile to change. + * @param rtt Set road or tram type. + * @param rt The road type to set. + */ +static inline void SetRoadType(TileIndex t, RoadTramType rtt, RoadType rt) +{ + if (rtt == RTT_TRAM) { + SetRoadTypeTram(t, rt); + } else { + SetRoadTypeRoad(t, rt); + } +} + +/** + * Set the present road types of a tile. + * @param t The tile to change. + * @param road_rt The road roadtype to set for the tile. + * @param tram_rt The tram roadtype to set for the tile. + */ +static inline void SetRoadTypes(TileIndex t, RoadType road_rt, RoadType tram_rt) +{ + SetRoadTypeRoad(t, road_rt); + SetRoadTypeTram(t, tram_rt); +} /** * Make a normal road tile. - * @param t Tile to make a normal road. - * @param bits Road bits to set for all present road types. - * @param rot New present road types. - * @param town Town ID if the road is a town-owned road. - * @param road New owner of road. - * @param tram New owner of tram tracks. + * @param t Tile to make a normal road. + * @param bits Road bits to set for all present road types. + * @param road_rt The road roadtype to set for the tile. + * @param tram_rt The tram roadtype to set for the tile. + * @param town Town ID if the road is a town-owned road. + * @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) +static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadType road_rt, RoadType tram_rt, 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; + _m[t].m3 = (tram_rt != INVALID_ROADTYPE ? bits : 0); + _m[t].m5 = (road_rt != INVALID_ROADTYPE ? bits : 0) | ROAD_TILE_NORMAL << 6; SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = rot << 6; - SetRoadOwner(t, ROADTYPE_TRAM, tram); + _me[t].m7 = 0; + SetRoadTypes(t, road_rt, tram_rt); + SetRoadOwner(t, RTT_TRAM, tram); } /** @@ -572,21 +655,23 @@ static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, Tow * @param rail New owner of the rail track. * @param roaddir Axis of the road. * @param rat New rail type. - * @param rot New present road types. + * @param road_rt The road roadtype to set for the tile. + * @param tram_rt The tram roadtype to set for the tile. * @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) +static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadType road_rt, RoadType tram_rt, uint town) { SetTileType(t, MP_ROAD); SetTileOwner(t, rail); _m[t].m2 = town; _m[t].m3 = 0; - _m[t].m4 = 0; + _m[t].m4 = INVALID_ROADTYPE; _m[t].m5 = ROAD_TILE_CROSSING << 6 | roaddir; SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = rot << 6 | road; - _me[t].m8 = rat; - SetRoadOwner(t, ROADTYPE_TRAM, tram); + _me[t].m7 = road; + _me[t].m8 = INVALID_ROADTYPE << 6 | rat; + SetRoadTypes(t, road_rt, tram_rt); + SetRoadOwner(t, RTT_TRAM, tram); } /** @@ -594,7 +679,7 @@ static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner r * @param t Tile to make a level crossing. * @param owner New owner of the depot. * @param did New depot ID. - * @param dir Direction of the depot exit. + * @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) @@ -603,11 +688,13 @@ static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDire SetTileOwner(t, owner); _m[t].m2 = did; _m[t].m3 = 0; - _m[t].m4 = 0; + _m[t].m4 = INVALID_ROADTYPE; _m[t].m5 = ROAD_TILE_DEPOT << 6 | dir; SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = RoadTypeToRoadTypes(rt) << 6 | owner; - SetRoadOwner(t, ROADTYPE_TRAM, owner); + _me[t].m7 = owner; + _me[t].m8 = INVALID_ROADTYPE << 6; + SetRoadType(t, GetRoadTramType(rt), rt); + SetRoadOwner(t, RTT_TRAM, owner); } #endif /* ROAD_MAP_H */ diff --git a/src/road_type.h b/src/road_type.h index 7056f6d62c..f01d1d0353 100644 --- a/src/road_type.h +++ b/src/road_type.h @@ -14,36 +14,37 @@ #include "core/enum_type.hpp" +typedef uint32 RoadTypeLabel; + +static const RoadTypeLabel ROADTYPE_ROAD_LABEL = 'ROAD'; +static const RoadTypeLabel ROADTYPE_TRAM_LABEL = 'TRAM'; + /** * The different roadtypes we support * * @note currently only ROADTYPE_ROAD and ROADTYPE_TRAM are supported. */ enum RoadType { - ROADTYPE_BEGIN = 0, ///< Used for iterations - ROADTYPE_ROAD = 0, ///< Basic road type - ROADTYPE_TRAM = 1, ///< Trams - ROADTYPE_END, ///< Used for iterations - INVALID_ROADTYPE = 0xFF, ///< flag for invalid roadtype + ROADTYPE_BEGIN = 0, ///< Used for iterations + ROADTYPE_ROAD = 0, ///< Basic road type + ROADTYPE_TRAM = 1, ///< Trams + ROADTYPE_END = 63, ///< Used for iterations + INVALID_ROADTYPE = 63, ///< flag for invalid roadtype }; DECLARE_POSTFIX_INCREMENT(RoadType) -template <> struct EnumPropsT : MakeEnumPropsT {}; +template <> struct EnumPropsT : MakeEnumPropsT {}; /** - * The different roadtypes we support, but then a bitmask of them - * @note currently only roadtypes with ROADTYPE_ROAD and ROADTYPE_TRAM are supported. + * The different roadtypes we support, but then a bitmask of them. + * @note Must be treated as a uint64 type, narrowing it causes bit membership tests to give wrong results. */ -enum RoadTypes : byte { +enum RoadTypes : uint64 { ROADTYPES_NONE = 0, ///< No roadtypes ROADTYPES_ROAD = 1 << ROADTYPE_ROAD, ///< Road ROADTYPES_TRAM = 1 << ROADTYPE_TRAM, ///< Trams - ROADTYPES_ALL = ROADTYPES_ROAD | ROADTYPES_TRAM, ///< Road + trams - ROADTYPES_END, ///< Used for iterations? - INVALID_ROADTYPES = 0xFF, ///< Invalid roadtypes + INVALID_ROADTYPES = UINT64_MAX, ///< Invalid roadtypes }; DECLARE_ENUM_AS_BIT_SET(RoadTypes) -template <> struct EnumPropsT : MakeEnumPropsT {}; - /** * Enumeration for the road parts on a tile. diff --git a/src/roadstop.cpp b/src/roadstop.cpp index acefa8362b..694e33f0dd 100644 --- a/src/roadstop.cpp +++ b/src/roadstop.cpp @@ -45,7 +45,7 @@ RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const { for (RoadStop *rs = this->next; rs != nullptr; rs = rs->next) { /* The vehicle cannot go to this roadstop (different roadtype) */ - if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue; + if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue; /* The vehicle is articulated and can therefore not go to a standard road stop. */ if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue; diff --git a/src/roadveh.h b/src/roadveh.h index 7c8f32d0f8..02eb48c8c2 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -16,7 +16,8 @@ #include "engine_base.h" #include "cargotype.h" #include "track_func.h" -#include "road_type.h" +#include "road.h" +#include "road_map.h" #include "newgrf_engine.h" #include @@ -115,8 +116,8 @@ struct RoadVehicle FINAL : public GroundVehicle { uint16 crashed_ctr; ///< Animation counter when the vehicle has crashed. @see RoadVehIsCrashed byte reverse_ctr; - RoadType roadtype; - RoadTypes compatible_roadtypes; + RoadType roadtype; //!< Roadtype of this vehicle. + RoadTypes compatible_roadtypes; //!< Roadtypes this consist is powered on. /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ RoadVehicle() : GroundVehicleBase() {} @@ -244,7 +245,7 @@ protected: // These functions should not be called outside acceleration code. { /* Trams have a slightly greater friction coefficient than trains. * The rest of road vehicles have bigger values. */ - uint32 coeff = (this->roadtype == ROADTYPE_TRAM) ? 40 : 75; + uint32 coeff = RoadTypeIsTram(this->roadtype) ? 40 : 75; /* The friction coefficient increases with speed in a way that * it doubles at 128 km/h, triples at 256 km/h and so on. */ return coeff * (128 + this->GetCurrentSpeed()) / 128; @@ -274,7 +275,7 @@ protected: // These functions should not be called outside acceleration code. */ inline uint16 GetMaxTrackSpeed() const { - return 0; + return GetRoadTypeInfo(GetRoadType(this->tile, GetRoadTramType(this->roadtype)))->max_speed; } /** @@ -283,7 +284,7 @@ protected: // These functions should not be called outside acceleration code. */ inline bool TileMayHaveSlopedTrack() const { - TrackStatus ts = GetTileTrackStatus(this->tile, TRANSPORT_ROAD, this->compatible_roadtypes); + TrackStatus ts = GetTileTrackStatus(this->tile, TRANSPORT_ROAD, GetRoadTramType(this->roadtype)); TrackBits trackbits = TrackStatusToTrackBits(ts); return trackbits == TRACK_BIT_X || trackbits == TRACK_BIT_Y; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 9f98c3e885..d323bc1ecb 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -260,7 +260,10 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) */ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret) { - if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); + /* Check that the vehicle can drive on the road in question */ + RoadType rt = e->u.road.roadtype; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); if (flags & DC_EXEC) { const RoadVehicleInfo *rvi = &e->u.road; @@ -304,8 +307,8 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->random_bits = VehicleRandomBits(); v->SetFrontEngine(); - v->roadtype = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - v->compatible_roadtypes = RoadTypeToRoadTypes(v->roadtype); + v->roadtype = rt; + v->compatible_roadtypes = rti->powered_roadtypes; v->gcache.cached_veh_length = VEHICLE_LENGTH; if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); @@ -437,16 +440,16 @@ void RoadVehicle::UpdateDeltaXY() */ inline int RoadVehicle::GetCurrentMaxSpeed() const { - int max_speed = this->vcache.cached_max_speed; + int max_speed = this->gcache.cached_max_track_speed; /* Limit speed to 50% while reversing, 75% in curves. */ for (const RoadVehicle *u = this; u != nullptr; u = u->Next()) { if (_settings_game.vehicle.roadveh_acceleration_model == AM_REALISTIC) { if (this->state <= RVSB_TRACKDIR_MASK && IsReversingRoadTrackdir((Trackdir)this->state)) { - max_speed = this->vcache.cached_max_speed / 2; + max_speed = this->gcache.cached_max_track_speed / 2; break; } else if ((u->direction & 1) == 0) { - max_speed = this->vcache.cached_max_speed * 3 / 4; + max_speed = this->gcache.cached_max_track_speed * 3 / 4; } } @@ -691,7 +694,7 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st) st->had_vehicle_of_type |= HVOT_BUS; SetDParam(0, st->index); AddVehicleNewsItem( - v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL, + RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL, (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER, v->index, st->index @@ -705,7 +708,7 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st) st->had_vehicle_of_type |= HVOT_TRUCK; SetDParam(0, st->index); AddVehicleNewsItem( - v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL, + RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL, (v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER, v->index, st->index @@ -783,7 +786,8 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data) */ static bool CheckRoadBlockedForOvertaking(OvertakeData *od) { - TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, od->v->compatible_roadtypes); + if (!HasTileAnyRoadType(od->tile, od->v->compatible_roadtypes)) return true; + TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, GetRoadTramType(od->v->roadtype)); TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts); TrackdirBits red_signals = TrackStatusToRedSignals(ts); // barred level crossing TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits); @@ -803,7 +807,7 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) od.u = u; /* Trams can't overtake other trams */ - if (v->roadtype == ROADTYPE_TRAM) return; + if (RoadTypeIsTram(v->roadtype)) return; /* Don't overtake in stations */ if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return; @@ -855,7 +859,7 @@ static void RoadZPosAffectSpeed(RoadVehicle *v, int old_z) v->cur_speed = v->cur_speed * 232 / 256; // slow down by ~10% } else { uint16 spd = v->cur_speed + 2; - if (spd <= v->vcache.cached_max_speed) v->cur_speed = spd; + if (spd <= v->gcache.cached_max_track_speed) v->cur_speed = spd; } } @@ -884,12 +888,12 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection Trackdir best_track; bool path_found = true; - TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes); + TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)); TrackdirBits red_signals = TrackStatusToRedSignals(ts); // crossing TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts); if (IsTileType(tile, MP_ROAD)) { - if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) { + if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) { /* Road depot owned by another company or with the wrong orientation */ trackdirs = TRACKDIR_BIT_NONE; } @@ -932,10 +936,10 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection if (v->reverse_ctr != 0) { bool reverse = true; - if (v->roadtype == ROADTYPE_TRAM) { + if (RoadTypeIsTram(v->roadtype)) { /* Trams may only reverse on a tile if it contains at least the straight * trackbits or when it is a valid turning tile (i.e. one roadbit) */ - RoadBits rb = GetAnyRoadBits(tile, ROADTYPE_TRAM); + RoadBits rb = GetAnyRoadBits(tile, RTT_TRAM); RoadBits straight = AxisToRoadBits(DiagDirToAxis(enterdir)); reverse = ((rb & straight) == straight) || (rb == DiagDirToRoadBits(enterdir)); @@ -1014,7 +1018,7 @@ static bool RoadVehLeaveDepot(RoadVehicle *v, bool first) v->direction = DiagDirToDir(dir); Trackdir tdir = DiagDirToDiagTrackdir(dir); - const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); @@ -1109,7 +1113,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl }; RoadBits required = required_roadbits[dir & 0x07]; - if ((required & GetAnyRoadBits(tile, v->roadtype, true)) == ROAD_NONE) { + if ((required & GetAnyRoadBits(tile, GetRoadTramType(v->roadtype), true)) == ROAD_NONE) { dir = INVALID_TRACKDIR; } @@ -1120,15 +1124,16 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl * Can a tram track build without destruction on the given tile? * @param c the company that would be building the tram tracks * @param t the tile to build on. + * @param rt the tram type to build. * @param r the road bits needed. * @return true when a track track can be build on 't' */ -static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadBits r) +static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadBits r) { /* The 'current' company is not necessarily the owner of the vehicle. */ Backup cur_company(_current_company, c, FILE_LINE); - CommandCost ret = DoCommand(t, ROADTYPE_TRAM << 4 | r, 0, DC_NO_WATER, CMD_BUILD_ROAD); + CommandCost ret = DoCommand(t, rt << 4 | r, 0, DC_NO_WATER, CMD_BUILD_ROAD); cur_company.Restore(); return ret.Succeeded(); @@ -1186,7 +1191,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) /* Get move position data for next frame. * For a drive-through road stop use 'straight road' move data. * In this case v->state is masked to give the road stop entry direction. */ - RoadDriveEntry rd = _road_drive_data[v->roadtype][( + RoadDriveEntry rd = _road_drive_data[GetRoadTramType(v->roadtype)][( (HasBit(v->state, RVS_IN_DT_ROAD_STOP) ? v->state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->state) + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1]; @@ -1196,7 +1201,11 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) if (v->IsFrontEngine()) { /* If this is the front engine, look for the right path. */ - dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + if (HasTileAnyRoadType(tile, v->compatible_roadtypes)) { + dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + } else { + dir = _road_reverse_table[(DiagDirection)(rd.x & 3)]; + } } else { dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false); } @@ -1214,7 +1223,7 @@ again: v->overtaking = 0; /* Turning around */ - if (v->roadtype == ROADTYPE_TRAM) { + if (RoadTypeIsTram(v->roadtype)) { /* Determine the road bits the tram needs to be able to turn around * using the 'big' corner loop. */ RoadBits needed; @@ -1227,7 +1236,8 @@ again: } if ((v->Previous() != nullptr && v->Previous()->tile == tile) || (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) && - (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) { + HasTileAnyRoadType(tile, v->compatible_roadtypes) && + (needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) { /* * Taking the 'big' corner for trams only happens when: * - The previous vehicle in this (articulated) tram chain is @@ -1238,7 +1248,7 @@ again: * going to cause the tram to split up. * - Or the front of the tram can drive over the next tile. */ - } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, needed) || ((~needed & GetAnyRoadBits(v->tile, ROADTYPE_TRAM, false)) == ROAD_NONE)) { + } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, v->roadtype, needed) || ((~needed & GetAnyRoadBits(v->tile, RTT_TRAM, false)) == ROAD_NONE)) { /* * Taking the 'small' corner for trams only happens when: * - We are not the from vehicle of an articulated tram. @@ -1266,7 +1276,7 @@ again: } /* Get position data for first frame on the new tile */ - const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking]; + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking]; int x = TileX(tile) * TILE_SIZE + rdp[start_frame].x; int y = TileY(tile) * TILE_SIZE + rdp[start_frame].y; @@ -1318,9 +1328,18 @@ again: } if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { + TileIndex old_tile = v->tile; + v->tile = tile; v->state = (byte)dir; v->frame = start_frame; + RoadTramType rtt = GetRoadTramType(v->roadtype); + if (GetRoadType(old_tile, rtt) != GetRoadType(tile, rtt)) { + if (v->IsFrontEngine()) { + RoadVehUpdateCache(v); + } + v->First()->CargoChanged(); + } } if (new_dir != v->direction) { v->direction = new_dir; @@ -1338,7 +1357,7 @@ again: Trackdir dir; uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME; - if (v->roadtype == ROADTYPE_TRAM && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, ROADTYPE_TRAM, true))) { + if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) { /* * The tram is turning around with one tram 'roadbit'. This means that * it is using the 'big' corner 'drive data'. However, to support the @@ -1370,7 +1389,7 @@ again: return false; } - const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir]; + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir]; int x = TileX(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].x; int y = TileY(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].y; @@ -1478,7 +1497,7 @@ again: TileIndex next_tile = TileAddByDir(v->tile, v->direction); /* Check if next inline bay is free and has compatible road. */ - if (RoadStop::IsDriveThroughRoadStopContinuation(v->tile, next_tile) && (GetRoadTypes(next_tile) & v->compatible_roadtypes) != 0) { + if (RoadStop::IsDriveThroughRoadStopContinuation(v->tile, next_tile) && HasTileAnyRoadType(next_tile, v->compatible_roadtypes)) { v->frame++; v->x_pos = x; v->y_pos = y; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 6b273e026d..662f5a77b3 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -282,6 +282,12 @@ static void InitializeWindowsAndCaches() (*it)->tile = t->xy; } } + RoadVehicle *rv; + FOR_ALL_ROADVEHICLES(rv) { + if (rv->IsFrontEngine()) { + rv->CargoChanged(); + } + } RecomputePrices(); @@ -461,8 +467,19 @@ static void FixOwnerOfRailTrack(TileIndex t) if (IsLevelCrossingTile(t)) { /* else change the crossing to normal road (road vehicles won't care) */ - MakeRoadNormal(t, GetCrossingRoadBits(t), GetRoadTypes(t), GetTownIndex(t), - GetRoadOwner(t, ROADTYPE_ROAD), GetRoadOwner(t, ROADTYPE_TRAM)); + Owner road = GetRoadOwner(t, RTT_ROAD); + Owner tram = GetRoadOwner(t, RTT_TRAM); + RoadBits bits = GetCrossingRoadBits(t); + bool hasroad = HasBit(_me[t].m7, 6); + bool hastram = HasBit(_me[t].m7, 7); + + /* MakeRoadNormal */ + SetTileType(t, MP_ROAD); + SetTileOwner(t, road); + _m[t].m3 = (hasroad ? bits : 0); + _m[t].m5 = (hastram ? bits : 0) | ROAD_TILE_NORMAL << 6; + SB(_me[t].m6, 2, 4, 0); + SetRoadOwner(t, RTT_TRAM, tram); return; } @@ -1052,18 +1069,18 @@ bool AfterLoadGame() break; case ROAD_TILE_DEPOT: break; } - SetRoadTypes(t, ROADTYPES_ROAD); + SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later. break; case MP_STATION: - if (IsRoadStop(t)) SetRoadTypes(t, ROADTYPES_ROAD); + if (IsRoadStop(t)) SB(_me[t].m7, 6, 2, 1); break; 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) { - SetRoadTypes(t, ROADTYPES_ROAD); + SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later. } break; @@ -1079,7 +1096,7 @@ 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)); + if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_me[t].m7, 5, 3)); SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert switch (GetRoadTileType(t)) { default: SlErrorCorrupt("Invalid road tile type"); @@ -1112,7 +1129,7 @@ bool AfterLoadGame() case MP_STATION: if (!IsRoadStop(t)) break; - if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3)); + if (fix_roadtypes) SB(_me[t].m7, 6, 2, (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; @@ -1121,7 +1138,7 @@ bool AfterLoadGame() 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 (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3)); Owner o = GetTileOwner(t); SB(_me[t].m7, 0, 5, o); // road owner @@ -1191,13 +1208,14 @@ bool AfterLoadGame() } else { TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, UINT_MAX)->index : 0; - MakeRoadNormal( - t, - axis == AXIS_X ? ROAD_Y : ROAD_X, - ROADTYPES_ROAD, - town, - GetTileOwner(t), OWNER_NONE - ); + /* MakeRoadNormal */ + SetTileType(t, MP_ROAD); + _m[t].m2 = town; + _m[t].m3 = 0; + _m[t].m5 = (axis == AXIS_X ? ROAD_Y : ROAD_X) | ROAD_TILE_NORMAL << 6; + SB(_me[t].m6, 2, 4, 0); + _me[t].m7 = 1 << 6; + SetRoadOwner(t, RTT_TRAM, OWNER_NONE); } } else { if (GB(_m[t].m5, 3, 2) == 0) { @@ -1252,6 +1270,35 @@ bool AfterLoadGame() } } + if (IsSavegameVersionBefore(SLV_ROAD_TYPES)) { + /* Add road subtypes */ + for (TileIndex t = 0; t < map_size; t++) { + bool has_road = false; + switch (GetTileType(t)) { + case MP_ROAD: + has_road = true; + break; + case MP_STATION: + has_road = IsRoadStop(t); + break; + case MP_TUNNELBRIDGE: + has_road = GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD; + break; + default: + break; + } + + if (has_road) { + RoadType road_rt = HasBit(_me[t].m7, 6) ? ROADTYPE_ROAD : INVALID_ROADTYPE; + RoadType tram_rt = HasBit(_me[t].m7, 7) ? ROADTYPE_TRAM : INVALID_ROADTYPE; + + assert(road_rt != INVALID_ROADTYPE || tram_rt != INVALID_ROADTYPE); + SetRoadTypes(t, road_rt, tram_rt); + SB(_me[t].m7, 6, 2, 0); // Clear pre-NRT road type bits. + } + } + } + /* Elrails got added in rev 24 */ if (IsSavegameVersionBefore(SLV_24)) { RailType min_rail = RAILTYPE_ELECTRIC; @@ -1375,7 +1422,7 @@ bool AfterLoadGame() Company *c; FOR_ALL_COMPANIES(c) { c->avail_railtypes = GetCompanyRailtypes(c->index); - c->avail_roadtypes = GetCompanyRoadtypes(c->index); + c->avail_roadtypes = GetCompanyRoadTypes(c->index); } if (!IsSavegameVersionBefore(SLV_27)) AfterLoadStations(); @@ -1846,10 +1893,10 @@ bool AfterLoadGame() } } else if (IsTileType(t, MP_ROAD)) { /* works for all RoadTileType */ - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { + FOR_ALL_ROADTRAMTYPES(rtt) { /* update even non-existing road types to update tile owner too */ - Owner o = GetRoadOwner(t, rt); - if (o < MAX_COMPANIES && !Company::IsValidID(o)) SetRoadOwner(t, rt, OWNER_NONE); + Owner o = GetRoadOwner(t, rtt); + if (o < MAX_COMPANIES && !Company::IsValidID(o)) SetRoadOwner(t, rtt, OWNER_NONE); } if (IsLevelCrossing(t)) { if (!Company::IsValidID(GetTileOwner(t))) FixOwnerOfRailTrack(t); @@ -2662,7 +2709,7 @@ bool AfterLoadGame() if (rv->state == RVSB_IN_DEPOT || rv->state == RVSB_WORMHOLE) break; - TrackStatus ts = GetTileTrackStatus(rv->tile, TRANSPORT_ROAD, rv->compatible_roadtypes); + TrackStatus ts = GetTileTrackStatus(rv->tile, TRANSPORT_ROAD, GetRoadTramType(rv->roadtype)); TrackBits trackbits = TrackStatusToTrackBits(ts); /* Only X/Y tracks can be sloped. */ @@ -2871,8 +2918,8 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { if (!IsStandardRoadStopTile(t)) continue; Owner o = GetTileOwner(t); - SetRoadOwner(t, ROADTYPE_ROAD, o); - SetRoadOwner(t, ROADTYPE_TRAM, o); + SetRoadOwner(t, RTT_ROAD, o); + SetRoadOwner(t, RTT_TRAM, o); } } diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 6c88db7272..f934848e9d 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -131,11 +131,12 @@ void AfterLoadCompanyStats() } /* Iterate all present road types as each can have a different owner. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rt)); + FOR_ALL_ROADTRAMTYPES(rtt) { + RoadType rt = GetRoadType(tile, rtt); + if (rt == INVALID_ROADTYPE) continue; + c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt)); /* A level crossings and depots have two road bits. */ - if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rt)) : 2; + if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2; } break; } @@ -153,9 +154,10 @@ void AfterLoadCompanyStats() case STATION_BUS: case STATION_TRUCK: { /* Iterate all present road types as each can have a different owner. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - c = Company::GetIfValid(GetRoadOwner(tile, rt)); + FOR_ALL_ROADTRAMTYPES(rtt) { + RoadType rt = GetRoadType(tile, rtt); + if (rt == INVALID_ROADTYPE) continue; + c = Company::GetIfValid(GetRoadOwner(tile, rtt)); if (c != nullptr) c->infrastructure.road[rt] += 2; // A road stop has two road bits. } break; @@ -210,9 +212,10 @@ void AfterLoadCompanyStats() case TRANSPORT_ROAD: { /* Iterate all present road types as each can have a different owner. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - c = Company::GetIfValid(GetRoadOwner(tile, rt)); + FOR_ALL_ROADTRAMTYPES(rtt) { + RoadType rt = GetRoadType(tile, rtt); + if (rt == INVALID_ROADTYPE) continue; + c = Company::GetIfValid(GetRoadOwner(tile, rtt)); if (c != nullptr) c->infrastructure.road[rt] += len * 2; // A full diagonal road has two road bits. } break; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 966de0c8a1..7ac602a540 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -298,6 +298,7 @@ enum SaveLoadVersion : uint16 { SLV_ROADVEH_PATH_CACHE, ///< 211 PR#7261 Add path cache for road vehicles. SLV_REMOVE_OPF, ///< 212 PR#7245 Remove OPF. SLV_TREES_WATER_CLASS, ///< 213 PR#7405 WaterClass update for tree tiles. + SLV_ROAD_TYPES, ///< 214 PR#6811 NewGRF road types. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index c42e3938f1..b08c1cb5ef 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -410,6 +410,14 @@ void AfterLoadVehicles(bool part_of_load) RoadVehicle *rv = RoadVehicle::From(v); if (rv->IsFrontEngine()) { rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed + + rv->roadtype = Engine::Get(rv->engine_type)->u.road.roadtype; + rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes; + for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) { + u->roadtype = rv->roadtype; + u->compatible_roadtypes = rv->compatible_roadtypes; + } + RoadVehUpdateCache(rv); if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) { rv->CargoChanged(); @@ -448,13 +456,7 @@ void AfterLoadVehicles(bool part_of_load) FOR_ALL_VEHICLES(v) { switch (v->type) { - case VEH_ROAD: { - RoadVehicle *rv = RoadVehicle::From(v); - rv->roadtype = HasBit(EngInfo(v->First()->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - rv->compatible_roadtypes = RoadTypeToRoadTypes(rv->roadtype); - FALLTHROUGH; - } - + case VEH_ROAD: case VEH_TRAIN: case VEH_SHIP: v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq); diff --git a/src/script/api/ai/ai_rail.hpp.sq b/src/script/api/ai/ai_rail.hpp.sq index ba9d7e6564..d7e421669f 100644 --- a/src/script/api/ai/ai_rail.hpp.sq +++ b/src/script/api/ai/ai_rail.hpp.sq @@ -56,7 +56,7 @@ void SQAIRail_Register(Squirrel *engine) ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, ScriptRail::ERR_UNSUITABLE_TRACK); ScriptError::RegisterErrorMap(STR_ERROR_THERE_ARE_NO_SIGNALS, ScriptRail::ERR_UNSUITABLE_TRACK); ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_STATION, ScriptRail::ERR_UNSUITABLE_TRACK); - ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING); + ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_RAIL, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING); ScriptError::RegisterErrorMapString(ScriptRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD"); ScriptError::RegisterErrorMapString(ScriptRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK"); diff --git a/src/script/api/ai/ai_road.hpp.sq b/src/script/api/ai/ai_road.hpp.sq index 3da4607c90..350fcdb7f1 100644 --- a/src/script/api/ai/ai_road.hpp.sq +++ b/src/script/api/ai/ai_road.hpp.sq @@ -26,6 +26,8 @@ void SQAIRoad_Register(Squirrel *engine) SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING"); + SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD"); SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD"); SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM"); SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID"); @@ -40,12 +42,19 @@ void SQAIRoad_Register(Squirrel *engine) ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_DIRECTION, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION); ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD); ScriptError::RegisterErrorMap(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS); + ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_ROAD, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING); + ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD); + ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_TRAMWAY, ScriptRoad::ERR_UNSUITABLE_ROAD); + ScriptError::RegisterErrorMap(STR_ERROR_INCOMPATIBLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING"); + ScriptError::RegisterErrorMapString(ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD"); + SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetName, "GetName", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetRoadVehicleTypeForCargo, "GetRoadVehicleTypeForCargo", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTile, "IsRoadTile", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, ".i"); @@ -54,6 +63,9 @@ void SQAIRoad_Register(Squirrel *engine) SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, "."); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, ".i"); + SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehCanRunOnRoad, "RoadVehCanRunOnRoad", 3, ".ii"); + SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehHasPowerOnRoad, "RoadVehHasPowerOnRoad", 3, ".ii"); + SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::ConvertRoadType, "ConvertRoadType", 4, ".iii"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::HasRoadType, "HasRoadType", 3, ".ii"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, ".ii"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, ".iaii"); @@ -74,6 +86,7 @@ void SQAIRoad_Register(Squirrel *engine) SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadStation, "RemoveRoadStation", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetBuildCost, "GetBuildCost", 3, ".ii"); + SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaxSpeed, "GetMaxSpeed", 2, ".i"); SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaintenanceCostFactor, "GetMaintenanceCostFactor", 2, ".i"); SQAIRoad.PostRegister(engine); diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp index c6a790f865..89922d4b2d 100644 --- a/src/script/api/ai_changelog.hpp +++ b/src/script/api/ai_changelog.hpp @@ -26,6 +26,11 @@ * \li AIGroup::GetSecondaryColour * \li AIVehicle::BuildVehicleWithRefit * \li AIVehicle::GetBuildWithRefitCapacity + * \li AIRoad::GetName + * \li AIRoad::RoadVehCanRunOnRoad + * \li AIRoad::RoadVehHasPowerOnRoad + * \li AIRoad::ConvertRoadType + * \li AIRoad::GetMaxSpeed * * \b 1.9.0 * @@ -46,6 +51,9 @@ * * No changes * + * API additions: + * \li AIRoad::ERR_ROADTYPE_DISALLOWS_CROSSING + * * \b 1.7.0 - 1.7.2 * * No changes diff --git a/src/script/api/game/game_rail.hpp.sq b/src/script/api/game/game_rail.hpp.sq index c057360300..f145e84818 100644 --- a/src/script/api/game/game_rail.hpp.sq +++ b/src/script/api/game/game_rail.hpp.sq @@ -56,7 +56,7 @@ void SQGSRail_Register(Squirrel *engine) ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, ScriptRail::ERR_UNSUITABLE_TRACK); ScriptError::RegisterErrorMap(STR_ERROR_THERE_ARE_NO_SIGNALS, ScriptRail::ERR_UNSUITABLE_TRACK); ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_STATION, ScriptRail::ERR_UNSUITABLE_TRACK); - ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING); + ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_RAIL, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING); ScriptError::RegisterErrorMapString(ScriptRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD"); ScriptError::RegisterErrorMapString(ScriptRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK"); diff --git a/src/script/api/game/game_road.hpp.sq b/src/script/api/game/game_road.hpp.sq index 51da4144eb..650d315046 100644 --- a/src/script/api/game/game_road.hpp.sq +++ b/src/script/api/game/game_road.hpp.sq @@ -26,6 +26,8 @@ void SQGSRoad_Register(Squirrel *engine) SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING"); + SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD"); SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD"); SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM"); SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID"); @@ -40,12 +42,19 @@ void SQGSRoad_Register(Squirrel *engine) ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_DIRECTION, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION); ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD); ScriptError::RegisterErrorMap(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS); + ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_ROAD, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING); + ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD); + ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_TRAMWAY, ScriptRoad::ERR_UNSUITABLE_ROAD); + ScriptError::RegisterErrorMap(STR_ERROR_INCOMPATIBLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD"); ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS"); + ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING"); + ScriptError::RegisterErrorMapString(ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD"); + SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetName, "GetName", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetRoadVehicleTypeForCargo, "GetRoadVehicleTypeForCargo", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTile, "IsRoadTile", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, ".i"); @@ -54,6 +63,9 @@ void SQGSRoad_Register(Squirrel *engine) SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, "."); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, ".i"); + SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehCanRunOnRoad, "RoadVehCanRunOnRoad", 3, ".ii"); + SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehHasPowerOnRoad, "RoadVehHasPowerOnRoad", 3, ".ii"); + SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::ConvertRoadType, "ConvertRoadType", 4, ".iii"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::HasRoadType, "HasRoadType", 3, ".ii"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, ".ii"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, ".iaii"); @@ -74,6 +86,7 @@ void SQGSRoad_Register(Squirrel *engine) SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadStation, "RemoveRoadStation", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetBuildCost, "GetBuildCost", 3, ".ii"); + SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaxSpeed, "GetMaxSpeed", 2, ".i"); SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaintenanceCostFactor, "GetMaintenanceCostFactor", 2, ".i"); SQGSRoad.PostRegister(engine); diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index 3d190f44f1..7fa2f62af3 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -227,8 +227,8 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_START_REPLACE, "WID_RV_START_REPLACE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_INFO_TAB, "WID_RV_INFO_TAB"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_STOP_REPLACE, "WID_RV_STOP_REPLACE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_RAIL_ROAD_TYPE_DROPDOWN, "WID_RV_RAIL_ROAD_TYPE_DROPDOWN"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, "WID_RV_TRAIN_ENGINEWAGON_DROPDOWN"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_RAILTYPE_DROPDOWN, "WID_RV_TRAIN_RAILTYPE_DROPDOWN"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_WAGONREMOVE_TOGGLE, "WID_RV_TRAIN_WAGONREMOVE_TOGGLE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BB_BACKGROUND, "WID_BB_BACKGROUND"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BAFD_QUESTION, "WID_BAFD_QUESTION"); @@ -385,6 +385,8 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_RAIL_COUNT, "WID_CI_RAIL_COUNT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_ROAD_DESC, "WID_CI_ROAD_DESC"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_ROAD_COUNT, "WID_CI_ROAD_COUNT"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_TRAM_DESC, "WID_CI_TRAM_DESC"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_TRAM_COUNT, "WID_CI_TRAM_COUNT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_WATER_DESC, "WID_CI_WATER_DESC"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_WATER_COUNT, "WID_CI_WATER_COUNT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_STATION_DESC, "WID_CI_STATION_DESC"); @@ -996,6 +998,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_WAYPOINT_MATRIX, "WID_BRW_WAYPOINT_MATRIX"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_WAYPOINT, "WID_BRW_WAYPOINT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_SCROLL, "WID_BRW_SCROLL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_CAPTION, "WID_ROT_CAPTION"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_ROAD_X, "WID_ROT_ROAD_X"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_ROAD_Y, "WID_ROT_ROAD_Y"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_AUTOROAD, "WID_ROT_AUTOROAD"); @@ -1007,6 +1010,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_BUILD_BRIDGE, "WID_ROT_BUILD_BRIDGE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_BUILD_TUNNEL, "WID_ROT_BUILD_TUNNEL"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_REMOVE, "WID_ROT_REMOVE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_CONVERT_ROAD, "WID_ROT_CONVERT_ROAD"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_CAPTION, "WID_BROD_CAPTION"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_DEPOT_NE, "WID_BROD_DEPOT_NE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_DEPOT_SE, "WID_BROD_DEPOT_SE"); @@ -1214,6 +1218,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_BUILDING_TOOLS_START, "WID_TN_BUILDING_TOOLS_START"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_RAILS, "WID_TN_RAILS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_ROADS, "WID_TN_ROADS"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_TRAMS, "WID_TN_TRAMS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_WATER, "WID_TN_WATER"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_AIR, "WID_TN_AIR"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_LANDSCAPE, "WID_TN_LANDSCAPE"); @@ -1237,6 +1242,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TOWN_GENERATE, "WID_TE_TOWN_GENERATE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_INDUSTRY, "WID_TE_INDUSTRY"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_ROADS, "WID_TE_ROADS"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TRAMS, "WID_TE_TRAMS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_WATER, "WID_TE_WATER"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TREES, "WID_TE_TREES"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_SIGNS, "WID_TE_SIGNS"); diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp index 04bc893c18..3befcaf97a 100644 --- a/src/script/api/game_changelog.hpp +++ b/src/script/api/game_changelog.hpp @@ -22,6 +22,11 @@ * API additions: * \li GSVehicle::BuildVehicleWithRefit * \li GSVehicle::GetBuildWithRefitCapacity + * \li GSRoad::GetName + * \li GSRoad::RoadVehCanRunOnRoad + * \li GSRoad::RoadVehHasPowerOnRoad + * \li GSRoad::ConvertRoadType + * \li GSRoad::GetMaxSpeed * * \b 1.9.0 * @@ -42,6 +47,9 @@ * * No changes * + * API additions: + * \li GSRoad::ERR_ROADTYPE_DISALLOWS_CROSSING + * * \b 1.7.0 - 1.7.2 * * No changes diff --git a/src/script/api/script_bridge.cpp b/src/script/api/script_bridge.cpp index 03787be2a0..d2d2a9c2df 100644 --- a/src/script/api/script_bridge.cpp +++ b/src/script/api/script_bridge.cpp @@ -83,7 +83,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance) switch (vehicle_type) { case ScriptVehicle::VT_ROAD: type |= (TRANSPORT_ROAD << 15); - type |= (::RoadTypeToRoadTypes((::RoadType)ScriptObject::GetRoadType()) << 8); + type |= (ScriptRoad::GetCurrentRoadType() << 8); break; case ScriptVehicle::VT_RAIL: type |= (TRANSPORT_RAIL << 15); @@ -114,7 +114,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance) DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start); DiagDirection dir_2 = ::ReverseDiagDir(dir_1); - return ScriptObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, nullptr, &::_DoCommandReturnBuildBridge2); + return ScriptObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (ScriptRoad::GetCurrentRoadType() << 4), 0, CMD_BUILD_ROAD, nullptr, &::_DoCommandReturnBuildBridge2); } /* static */ bool ScriptBridge::_BuildBridgeRoad2() @@ -126,7 +126,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance) DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start); DiagDirection dir_2 = ::ReverseDiagDir(dir_1); - return ScriptObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD); + return ScriptObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (ScriptRoad::GetCurrentRoadType() << 4), 0, CMD_BUILD_ROAD); } /* static */ bool ScriptBridge::RemoveBridge(TileIndex tile) diff --git a/src/script/api/script_engine.cpp b/src/script/api/script_engine.cpp index dfd15bee49..97f8c71dfb 100644 --- a/src/script/api/script_engine.cpp +++ b/src/script/api/script_engine.cpp @@ -224,7 +224,7 @@ if (!IsValidEngine(engine_id)) return ScriptRoad::ROADTYPE_INVALID; if (GetVehicleType(engine_id) != ScriptVehicle::VT_ROAD) return ScriptRoad::ROADTYPE_INVALID; - return HasBit(::EngInfo(engine_id)->misc_flags, EF_ROAD_TRAM) ? ScriptRoad::ROADTYPE_TRAM : ScriptRoad::ROADTYPE_ROAD; + return (ScriptRoad::RoadType)(uint)::RoadVehInfo(engine_id)->roadtype; } /* static */ ScriptRail::RailType ScriptEngine::GetRailType(EngineID engine_id) diff --git a/src/script/api/script_infrastructure.cpp b/src/script/api/script_infrastructure.cpp index d7da2747e1..35febbd58a 100644 --- a/src/script/api/script_infrastructure.cpp +++ b/src/script/api/script_infrastructure.cpp @@ -90,7 +90,8 @@ company = ScriptCompany::ResolveCompanyID(company); if (company == ScriptCompany::COMPANY_INVALID || (::RoadType)roadtype >= ROADTYPE_END || !_settings_game.economy.infrastructure_maintenance) return 0; - return ::RoadMaintenanceCost((::RoadType)roadtype, ::Company::Get((::CompanyID)company)->infrastructure.road[roadtype]); + const ::Company *c = ::Company::Get((::CompanyID)company); + return ::RoadMaintenanceCost((::RoadType)roadtype, c->infrastructure.road[roadtype], RoadTypeIsRoad((::RoadType)roadtype) ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal()); } /* static */ Money ScriptInfrastructure::GetMonthlyInfrastructureCosts(ScriptCompany::CompanyID company, Infrastructure infra_type) @@ -114,8 +115,9 @@ case INFRASTRUCTURE_ROAD: { Money cost; + uint32 road_total = c->infrastructure.GetRoadTotal(); for (::RoadType rt = ::ROADTYPE_BEGIN; rt != ::ROADTYPE_END; rt++) { - cost += RoadMaintenanceCost(rt, c->infrastructure.road[rt]); + cost += RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_total); } return cost; } diff --git a/src/script/api/script_rail.hpp b/src/script/api/script_rail.hpp index 7e2a59e7f7..d52a7bfc46 100644 --- a/src/script/api/script_rail.hpp +++ b/src/script/api/script_rail.hpp @@ -36,7 +36,7 @@ public: ERR_UNSUITABLE_TRACK, // [STR_ERROR_NO_SUITABLE_RAILROAD_TRACK, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, STR_ERROR_THERE_ARE_NO_SIGNALS, STR_ERROR_THERE_IS_NO_STATION] /** This railtype cannot have crossings */ - ERR_RAILTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED] + ERR_RAILTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED_RAIL] }; /** diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 74899f8bb3..327978f6a3 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -23,6 +23,13 @@ return ScriptCargo::HasCargoClass(cargo_type, ScriptCargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK; } +/* static */ char *ScriptRoad::GetName(RoadType road_type) +{ + if (!IsRoadTypeAvailable(road_type)) return nullptr; + + return GetString(GetRoadTypeInfo((::RoadType)road_type)->strings.name); +} + /* static */ bool ScriptRoad::IsRoadTile(TileIndex tile) { if (!::IsValidTile(tile)) return false; @@ -37,7 +44,7 @@ if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false; return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT && - (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; + HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType()); } /* static */ bool ScriptRoad::IsRoadStationTile(TileIndex tile) @@ -45,7 +52,7 @@ if (!::IsValidTile(tile)) return false; if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false; - return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; + return ::IsRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType()); } /* static */ bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile) @@ -53,12 +60,12 @@ if (!::IsValidTile(tile)) return false; if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false; - return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0; + return ::IsDriveThroughStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType()); } /* static */ bool ScriptRoad::IsRoadTypeAvailable(RoadType road_type) { - return ::IsValidRoadType((::RoadType)road_type) && ::HasRoadTypesAvail(ScriptObject::GetCompany(), ::RoadTypeToRoadTypes((::RoadType)road_type)); + return (::RoadType)road_type < ROADTYPE_END && ::HasRoadTypeAvail(ScriptObject::GetCompany(), (::RoadType)road_type); } /* static */ ScriptRoad::RoadType ScriptRoad::GetCurrentRoadType() @@ -73,11 +80,24 @@ ScriptObject::SetRoadType((::RoadType)road_type); } +/* static */ bool ScriptRoad::RoadVehCanRunOnRoad(RoadType engine_road_type, RoadType road_road_type) +{ + return RoadVehHasPowerOnRoad(engine_road_type, road_road_type); +} + +/* static */ bool ScriptRoad::RoadVehHasPowerOnRoad(RoadType engine_road_type, RoadType road_road_type) +{ + if (!IsRoadTypeAvailable(engine_road_type)) return false; + if (!IsRoadTypeAvailable(road_road_type)) return false; + + return ::HasPowerOnRoad((::RoadType)engine_road_type, (::RoadType)road_road_type); +} + /* static */ bool ScriptRoad::HasRoadType(TileIndex tile, RoadType road_type) { if (!ScriptMap::IsValidTile(tile)) return false; if (!IsRoadTypeAvailable(road_type)) return false; - return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE; + return ::GetAnyRoadBits(tile, GetRoadTramType((::RoadType)road_type), false) != ROAD_NONE; } /* static */ bool ScriptRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2) @@ -89,8 +109,9 @@ /* Tiles not neighbouring */ if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false; - RoadBits r1 = ::GetAnyRoadBits(t1, ScriptObject::GetRoadType()); - RoadBits r2 = ::GetAnyRoadBits(t2, ScriptObject::GetRoadType()); + RoadTramType rtt = GetRoadTramType(ScriptObject::GetRoadType()); + RoadBits r1 = ::GetAnyRoadBits(t1, rtt); // TODO + RoadBits r2 = ::GetAnyRoadBits(t2, rtt); // TODO uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3); uint dir_2 = 2 ^ dir_1; @@ -100,6 +121,16 @@ return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND); } +/* static */ bool ScriptRoad::ConvertRoadType(TileIndex start_tile, TileIndex end_tile, RoadType road_type) +{ + EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY); + EnforcePrecondition(false, ::IsValidTile(start_tile)); + EnforcePrecondition(false, ::IsValidTile(end_tile)); + EnforcePrecondition(false, IsRoadTypeAvailable(road_type)); + + return ScriptObject::DoCommand(start_tile, end_tile, (::RoadType)road_type, CMD_CONVERT_ROAD); +} + /* Helper functions for ScriptRoad::CanBuildConnectedRoadParts(). */ /** @@ -380,7 +411,7 @@ static bool NormaliseTileOffset(int32 *tile) if (::IsNormalRoadTile(tile)) { rb = ::GetAllRoadBits(tile); } else { - for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt); + rb = ::GetAnyRoadBits(tile, RTT_ROAD) | ::GetAnyRoadBits(tile, RTT_TRAM); } for (uint i = 0; i < lengthof(neighbours); i++) { if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i]; @@ -392,15 +423,15 @@ static bool NormaliseTileOffset(int32 *tile) /** * Check whether one can reach (possibly by building) a road piece the center * of the neighbouring tile. This includes roads and (drive through) stations. - * @param rts The road type we want to know reachability for + * @param rt The road type we want to know reachability for * @param start_tile The tile to "enter" the neighbouring tile. * @param neighbour The direction to the neighbouring tile to "enter". * @return true if and only if the tile is reachable. */ -static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour) +static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagDirection neighbour) { TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour); - if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false; + if (!HasBit(::GetPresentRoadTypes(neighbour_tile), rt)) return false; switch (::GetTileType(neighbour_tile)) { case MP_ROAD: @@ -422,13 +453,13 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia if (!::IsValidTile(tile)) return false; if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false; - ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()); + ::RoadType rt = (::RoadType)GetCurrentRoadType(); int32 neighbour = 0; - if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++; - if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++; - if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++; - if (TileY(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++; + if (TileX(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NE)) neighbour++; + if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SE)) neighbour++; + if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SW)) neighbour++; + if (TileY(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NW)) neighbour++; return neighbour; } @@ -460,10 +491,10 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia EnforcePrecondition(false, ::IsValidTile(start)); EnforcePrecondition(false, ::IsValidTile(end)); EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end)); - EnforcePrecondition(false, !one_way || ScriptObject::GetRoadType() == ::ROADTYPE_ROAD); + EnforcePrecondition(false, !one_way || RoadTypeIsRoad(ScriptObject::GetRoadType())); EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType())); - return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5) | 1 << 6, CMD_BUILD_LONG_ROAD); + return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 10) | 1 << 11, CMD_BUILD_LONG_ROAD); } /* static */ bool ScriptRoad::BuildRoad(TileIndex start, TileIndex end) @@ -520,11 +551,11 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0); } - uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 32; + uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 4; p2 |= drive_through ? 2 : 0; p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0; - p2 |= ::RoadTypeToRoadTypes(ScriptObject::GetRoadType()) << 2; - p2 |= entrance_dir << 6; + p2 |= ScriptObject::GetRoadType() << 5; + p2 |= entrance_dir << 3; p2 |= (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16; return ScriptObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP); } @@ -588,7 +619,7 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return -1; switch (build_type) { - case BT_ROAD: return ::GetPrice(PR_BUILD_ROAD, 1, nullptr); + case BT_ROAD: return ::RoadBuildCost((::RoadType)roadtype); case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, nullptr); case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, nullptr); case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, nullptr); @@ -596,9 +627,16 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia } } +/* static */ int32 ScriptRoad::GetMaxSpeed(RoadType road_type) +{ + if (!ScriptRoad::IsRoadTypeAvailable(road_type)) return 0; + + return GetRoadTypeInfo((::RoadType)road_type)->max_speed; +} + /* static */ uint16 ScriptRoad::GetMaintenanceCostFactor(RoadType roadtype) { if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return 0; - return roadtype == ROADTYPE_TRAM ? 3 : 2; + return GetRoadTypeInfo((::RoadType)roadtype)->maintenance_multiplier; } diff --git a/src/script/api/script_road.hpp b/src/script/api/script_road.hpp index ed4058f973..6da45acc25 100644 --- a/src/script/api/script_road.hpp +++ b/src/script/api/script_road.hpp @@ -36,16 +36,21 @@ public: /** Drive through roads can't be build on town owned roads */ ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, // [STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD] - /** One way roads can't have junctions */ ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, // [STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION] + + /** This roadtype cannot have crossings */ + ERR_ROADTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED_ROAD] + + /** No suitable road could be found */ + ERR_UNSUITABLE_ROAD, // [STR_ERROR_NO_SUITABLE_ROAD, STR_ERROR_NO_SUITABLE_TRAMWAY, STR_ERROR_INCOMPATIBLE_ROAD] }; /** * Types of road known to the game. */ enum RoadType { - /* Note: these values represent part of the in-game RoadType enum */ + /* Note: these values represent part of the in-game static values */ ROADTYPE_ROAD = ::ROADTYPE_ROAD, ///< Build road objects. ROADTYPE_TRAM = ::ROADTYPE_TRAM, ///< Build tram objects. @@ -71,6 +76,14 @@ public: BT_TRUCK_STOP, ///< Build a truck stop }; + /** + * Get the name of a road type. + * @param road_type The road type to get the name of. + * @pre IsRoadTypeAvailable(road_type). + * @return The name the road type has. + */ + static char *GetName(RoadType road_type); + /** * Determines whether a busstop or a truckstop is needed to transport a certain cargo. * @param cargo_type The cargo to test. @@ -137,6 +150,41 @@ public: */ static void SetCurrentRoadType(RoadType road_type); + /** + * Check if a road vehicle built for a road type can run on another road type. + * @param engine_road_type The road type the road vehicle is built for. + * @param track_road_type The road type you want to check. + * @pre ScriptRoad::IsRoadTypeAvailable(engine_road_type). + * @pre ScriptRoad::IsRoadTypeAvailable(road_road_type). + * @return Whether a road vehicle built for 'engine_road_type' can run on 'road_road_type'. + */ + static bool RoadVehCanRunOnRoad(ScriptRoad::RoadType engine_road_type, ScriptRoad::RoadType road_road_type); + + /** + * Check if a road vehicle built for a road type has power on another road type. + * @param engine_road_type The road type the road vehicle is built for. + * @param road_road_type The road type you want to check. + * @pre ScriptRoad::IsRoadTypeAvailable(engine_road_type). + * @pre ScriptRoad::IsRoadTypeAvailable(road_road_type). + * @return Whether a road vehicle built for 'engine_road_type' has power on 'road_road_type'. + */ + static bool RoadVehHasPowerOnRoad(ScriptRoad::RoadType engine_road_type, ScriptRoad::RoadType road_road_type); + + + /** + * Convert the road on all tiles within a rectangle to another RoadType. + * @param start_tile One corner of the rectangle. + * @param end_tile The opposite corner of the rectangle. + * @param road_type The RoadType you want to convert. + * @pre ScriptMap::IsValidTile(start_tile). + * @pre ScriptMap::IsValidTile(end_tile). + * @pre IsRoadTypeAvailable(road_type). + * @game @pre Valid ScriptCompanyMode active in scope. + * @exception ScriptRoad::ERR_UNSUITABLE_ROAD + * @return Whether at least some road has been converted successfully. + */ + static bool ConvertRoadType(TileIndex start_tile, TileIndex end_tile, RoadType road_type); + /** * Check if a given tile has RoadType. * @param tile The tile to check. @@ -482,16 +530,28 @@ public: /** * Get the baseprice of building a road-related object. - * @param roadtype the roadtype that is build (on) + * @param roadtype the roadtype of the object to build * @param build_type the type of object to build - * @pre IsRoadTypeAvailable(railtype) + * @pre IsRoadTypeAvailable(roadtype) * @return The baseprice of building the given object. */ static Money GetBuildCost(RoadType roadtype, BuildType build_type); /** - * Get the maintenance cost factor of a roadtype. - * @param roadtype The roadtype to get the maintenance factor of. + * Get the maximum speed of road vehicles running on this roadtype. + * @param road_type The roadtype to get the maximum speed of. + * @pre IsRoadTypeAvailable(road_type) + * @return The maximum speed road vehicles can run on this roadtype + * or 0 if there is no limit. + * @note The speed is in OpenTTD's internal speed unit. + * This is mph / 0.8, which is roughly 0.5 km/h. + * To get km/h multiply this number by 2.01168. + */ + static int32 GetMaxSpeed(RoadType road_type); + + /** + * Get the maintenance cost factor of a road type. + * @param roadtype The road type to get the maintenance factor of. * @pre IsRoadTypeAvailable(roadtype) * @return Maintenance cost factor of the roadtype. */ diff --git a/src/script/api/script_station.cpp b/src/script/api/script_station.cpp index 3b3bae33c6..e5536c6e04 100644 --- a/src/script/api/script_station.cpp +++ b/src/script/api/script_station.cpp @@ -211,13 +211,11 @@ template if (!IsValidStation(station_id)) return false; if (!ScriptRoad::IsRoadTypeAvailable(road_type)) return false; - ::RoadTypes r = RoadTypeToRoadTypes((::RoadType)road_type); - for (const RoadStop *rs = ::Station::Get(station_id)->GetPrimaryRoadStop(ROADSTOP_BUS); rs != nullptr; rs = rs->next) { - if ((::GetRoadTypes(rs->xy) & r) != 0) return true; + if (HasBit(::GetPresentRoadTypes(rs->xy), (::RoadType)road_type)) return true; } for (const RoadStop *rs = ::Station::Get(station_id)->GetPrimaryRoadStop(ROADSTOP_TRUCK); rs != nullptr; rs = rs->next) { - if ((::GetRoadTypes(rs->xy) & r) != 0) return true; + if (HasBit(::GetPresentRoadTypes(rs->xy), (::RoadType)road_type)) return true; } return false; diff --git a/src/script/api/script_tile.cpp b/src/script/api/script_tile.cpp index 1364617624..3d10b7d515 100644 --- a/src/script/api/script_tile.cpp +++ b/src/script/api/script_tile.cpp @@ -33,12 +33,12 @@ case MP_WATER: return IsCoast(tile); case MP_ROAD: /* Tram bits aren't considered buildable */ - if (::GetRoadTypes(tile) != ROADTYPES_ROAD) return false; + if (::GetRoadTypeTram(tile) != INVALID_ROADTYPE) return false; /* Depots and crossings aren't considered buildable */ if (::GetRoadTileType(tile) != ROAD_TILE_NORMAL) return false; - if (!HasExactlyOneBit(::GetRoadBits(tile, ROADTYPE_ROAD))) return false; - if (::IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) return true; - if (::IsRoadOwner(tile, ROADTYPE_ROAD, ScriptObject::GetCompany())) return true; + if (!HasExactlyOneBit(::GetRoadBits(tile, RTT_ROAD))) return false; + if (::IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN)) return true; + if (::IsRoadOwner(tile, RTT_ROAD, ScriptObject::GetCompany())) return true; return false; } } @@ -201,7 +201,12 @@ { if (!::IsValidTile(tile)) return false; - return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, UINT32_MAX)) != TRACKDIR_BIT_NONE; + if (transport_type == TRANSPORT_ROAD) { + return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE || + ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 1)) != TRACKDIR_BIT_NONE; + } else { + return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE; + } } /* static */ int32 ScriptTile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, int width, int height, int radius) diff --git a/src/script/api/script_tunnel.cpp b/src/script/api/script_tunnel.cpp index a8bfcbfd3c..ce94c17ee8 100644 --- a/src/script/api/script_tunnel.cpp +++ b/src/script/api/script_tunnel.cpp @@ -90,7 +90,7 @@ static void _DoCommandReturnBuildTunnel1(class ScriptInstance *instance) uint type = 0; if (vehicle_type == ScriptVehicle::VT_ROAD) { type |= (TRANSPORT_ROAD << 8); - type |= ::RoadTypeToRoadTypes((::RoadType)ScriptObject::GetRoadType()); + type |= ScriptRoad::GetCurrentRoadType(); } else { type |= (TRANSPORT_RAIL << 8); type |= ScriptRail::GetCurrentRailType(); diff --git a/src/script/api/script_vehicle.cpp b/src/script/api/script_vehicle.cpp index 15bc68a971..e0d129ad87 100644 --- a/src/script/api/script_vehicle.cpp +++ b/src/script/api/script_vehicle.cpp @@ -401,7 +401,7 @@ if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID; if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID; - return (ScriptRoad::RoadType)(::RoadVehicle::Get(vehicle_id))->roadtype; + return (ScriptRoad::RoadType)(int)(::RoadVehicle::Get(vehicle_id))->roadtype; } /* static */ int32 ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo) diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index 47c8488889..58226ea5b2 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -954,9 +954,11 @@ public: WID_RV_INFO_TAB = ::WID_RV_INFO_TAB, ///< Info tab. WID_RV_STOP_REPLACE = ::WID_RV_STOP_REPLACE, ///< Stop Replacing button. + /* Train/road only widgets */ + WID_RV_RAIL_ROAD_TYPE_DROPDOWN = ::WID_RV_RAIL_ROAD_TYPE_DROPDOWN, ///< Dropdown menu about the rail/roadtype. + /* Train only widgets. */ WID_RV_TRAIN_ENGINEWAGON_DROPDOWN = ::WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, ///< Dropdown to select engines and/or wagons. - WID_RV_TRAIN_RAILTYPE_DROPDOWN = ::WID_RV_TRAIN_RAILTYPE_DROPDOWN, ///< Dropdown menu about the railtype. WID_RV_TRAIN_WAGONREMOVE_TOGGLE = ::WID_RV_TRAIN_WAGONREMOVE_TOGGLE, ///< Button to toggle removing wagons. }; @@ -1170,6 +1172,8 @@ public: WID_CI_RAIL_COUNT = ::WID_CI_RAIL_COUNT, ///< Count of rail. WID_CI_ROAD_DESC = ::WID_CI_ROAD_DESC, ///< Description of road. WID_CI_ROAD_COUNT = ::WID_CI_ROAD_COUNT, ///< Count of road. + WID_CI_TRAM_DESC = ::WID_CI_TRAM_DESC, ///< Description of tram. + WID_CI_TRAM_COUNT = ::WID_CI_TRAM_COUNT, ///< Count of tram. WID_CI_WATER_DESC = ::WID_CI_WATER_DESC, ///< Description of water. WID_CI_WATER_COUNT = ::WID_CI_WATER_COUNT, ///< Count of water. WID_CI_STATION_DESC = ::WID_CI_STATION_DESC, ///< Description of station. @@ -2125,6 +2129,7 @@ public: /** Widgets of the #BuildRoadToolbarWindow class. */ enum RoadToolbarWidgets { /* Name starts with RO instead of R, because of collision with RailToolbarWidgets */ + WID_ROT_CAPTION = ::WID_ROT_CAPTION, ///< Caption of the window WID_ROT_ROAD_X = ::WID_ROT_ROAD_X, ///< Build road in x-direction. WID_ROT_ROAD_Y = ::WID_ROT_ROAD_Y, ///< Build road in y-direction. WID_ROT_AUTOROAD = ::WID_ROT_AUTOROAD, ///< Autorail. @@ -2136,6 +2141,7 @@ public: WID_ROT_BUILD_BRIDGE = ::WID_ROT_BUILD_BRIDGE, ///< Build bridge. WID_ROT_BUILD_TUNNEL = ::WID_ROT_BUILD_TUNNEL, ///< Build tunnel. WID_ROT_REMOVE = ::WID_ROT_REMOVE, ///< Remove road. + WID_ROT_CONVERT_ROAD = ::WID_ROT_CONVERT_ROAD, ///< Convert road. }; /** Widgets of the #BuildRoadDepotWindow class. */ @@ -2435,6 +2441,7 @@ public: WID_TN_BUILDING_TOOLS_START = ::WID_TN_BUILDING_TOOLS_START, ///< Helper for the offset of the building tools WID_TN_RAILS = ::WID_TN_RAILS, ///< Rail building menu. WID_TN_ROADS = ::WID_TN_ROADS, ///< Road building menu. + WID_TN_TRAMS = ::WID_TN_TRAMS, ///< Tram building menu. WID_TN_WATER = ::WID_TN_WATER, ///< Water building toolbar. WID_TN_AIR = ::WID_TN_AIR, ///< Airport building toolbar. WID_TN_LANDSCAPE = ::WID_TN_LANDSCAPE, ///< Landscaping toolbar. @@ -2462,11 +2469,11 @@ public: WID_TE_TOWN_GENERATE = ::WID_TE_TOWN_GENERATE, ///< Town building window. WID_TE_INDUSTRY = ::WID_TE_INDUSTRY, ///< Industry building window. WID_TE_ROADS = ::WID_TE_ROADS, ///< Road building menu. + WID_TE_TRAMS = ::WID_TE_TRAMS, ///< Tram building menu. WID_TE_WATER = ::WID_TE_WATER, ///< Water building toolbar. WID_TE_TREES = ::WID_TE_TREES, ///< Tree building toolbar. WID_TE_SIGNS = ::WID_TE_SIGNS, ///< Sign building. WID_TE_DATE_PANEL = ::WID_TE_DATE_PANEL, ///< Container for the date widgets. - /* The following three need to have the same actual widget number as the normal toolbar due to shared code. */ WID_TE_MUSIC_SOUND = ::WID_TE_MUSIC_SOUND, ///< Music/sound configuration menu. WID_TE_HELP = ::WID_TE_HELP, ///< Help menu. WID_TE_SWITCH_BAR = ::WID_TE_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets. diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index aa1e048155..e9747400d6 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -460,28 +460,51 @@ static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t) */ static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t) { - if (t == MP_STATION) { - switch (GetStationType(tile)) { - case STATION_RAIL: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN); - case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED); - case STATION_TRUCK: return MKCOLOUR_XXXX(PC_ORANGE); - case STATION_BUS: return MKCOLOUR_XXXX(PC_YELLOW); - case STATION_DOCK: return MKCOLOUR_XXXX(PC_LIGHT_BLUE); - default: return MKCOLOUR_FFFF; + switch (t) { + case MP_STATION: + switch (GetStationType(tile)) { + case STATION_RAIL: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN); + case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED); + case STATION_TRUCK: return MKCOLOUR_XXXX(PC_ORANGE); + case STATION_BUS: return MKCOLOUR_XXXX(PC_YELLOW); + case STATION_DOCK: return MKCOLOUR_XXXX(PC_LIGHT_BLUE); + default: return MKCOLOUR_FFFF; + } + + case MP_RAILWAY: { + AndOr andor = { + MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour), + _smallmap_contours_andor[t].mand + }; + + const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; + return ApplyMask(cs->default_colour, &andor); } - } else if (t == MP_RAILWAY) { - AndOr andor = { - MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour), - _smallmap_contours_andor[t].mand - }; - const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(cs->default_colour, &andor); - } + case MP_ROAD: { + const RoadTypeInfo *rti = nullptr; + if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) { + rti = GetRoadTypeInfo(GetRoadTypeRoad(tile)); + } else { + rti = GetRoadTypeInfo(GetRoadTypeTram(tile)); + } + if (rti != nullptr) { + AndOr andor = { + MKCOLOUR_0XX0(rti->map_colour), + _smallmap_contours_andor[t].mand + }; + + const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; + return ApplyMask(cs->default_colour, &andor); + } + FALLTHROUGH; + } - /* Ground colour */ - const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; - return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]); + default: + /* Ground colour */ + const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; + return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]); + } } /** diff --git a/src/station.cpp b/src/station.cpp index e8ae1bc208..096a3c3739 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -189,7 +189,7 @@ RoadStop *Station::GetPrimaryRoadStop(const RoadVehicle *v) const for (; rs != nullptr; rs = rs->next) { /* The vehicle cannot go to this roadstop (different roadtype) */ - if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue; + if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue; /* The vehicle is articulated and can therefore not go to a standard road stop. */ if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 4aa6401279..0ba824c0d0 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -41,6 +41,7 @@ #include "station_kdtree.h" #include "roadstop_base.h" #include "newgrf_railtype.h" +#include "newgrf_roadtype.h" #include "waypoint_base.h" #include "waypoint_func.h" #include "pbs.h" @@ -934,10 +935,10 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl * @param is_truck_stop True when building a truck stop, false otherwise. * @param axis Axis of a drive-through road stop. * @param station StationID to be queried and returned if available. - * @param rts Road types to build. + * @param rt Road type to build. * @return The cost in case of success, or an error code if it failed. */ -static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadTypes rts) +static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt) { CommandCost cost(EXPENSES_CONSTRUCTION); int allowed_z = -1; @@ -988,47 +989,54 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags } } - RoadTypes cur_rts = IsNormalRoadTile(cur_tile) ? GetRoadTypes(cur_tile) : ROADTYPES_NONE; - uint num_roadbits = 0; if (build_over_road) { /* There is a road, check if we can build road+tram stop over it. */ - if (HasBit(cur_rts, ROADTYPE_ROAD)) { - Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD); + RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD); + if (road_rt != INVALID_ROADTYPE) { + Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD); if (road_owner == OWNER_TOWN) { if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD); } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) { CommandCost ret = CheckOwnership(road_owner); if (ret.Failed()) return ret; } - num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD)); + uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD)); + + if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD); + + cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces)); + } else if (RoadTypeIsRoad(rt)) { + cost.AddCost(RoadBuildCost(rt) * 2); } if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD); /* There is a tram, check if we can build road+tram stop over it. */ - if (HasBit(cur_rts, ROADTYPE_TRAM)) { - Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM); + RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM); + if (tram_rt != INVALID_ROADTYPE) { + Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM); if (Company::IsValidID(tram_owner) && (!_settings_game.construction.road_stop_on_competitor_road || /* Disallow breaking end-of-line of someone else * so trams can still reverse on this tile. */ - HasExactlyOneBit(GetRoadBits(cur_tile, ROADTYPE_TRAM)))) { + HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) { CommandCost ret = CheckOwnership(tram_owner); if (ret.Failed()) return ret; } - num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_TRAM)); - } + uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM)); - /* Take into account existing roadbits. */ - rts |= cur_rts; + if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD); + + cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces)); + } else if (RoadTypeIsTram(rt)) { + cost.AddCost(RoadBuildCost(rt) * 2); + } } else { ret = DoCommand(cur_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); + cost.AddCost(RoadBuildCost(rt) * 2); } - - uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits; - cost.AddCost(_price[PR_BUILD_ROAD] * roadbits_to_build); } } @@ -1783,10 +1791,10 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio * bit 8..15: Length of the road stop. * @param p2 bit 0: 0 For bus stops, 1 for truck stops. * bit 1: 0 For normal stops, 1 for drive-through. - * bit 2..3: The roadtypes. - * bit 5: Allow stations directly adjacent to other stations. - * bit 6..7: Entrance direction (#DiagDirection) for normal stops. - * bit 6: #Axis of the road for drive-through stops. + * bit 2: Allow stations directly adjacent to other stations. + * bit 3..4: Entrance direction (#DiagDirection) for normal stops. + * bit 3: #Axis of the road for drive-through stops. + * bit 5..10: The roadtype. * bit 16..31: Station ID to join (NEW_STATION if build new one). * @param text Unused. * @return The cost of this operation or an error. @@ -1795,7 +1803,8 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin { bool type = HasBit(p2, 0); bool is_drive_through = HasBit(p2, 1); - RoadTypes rts = Extract(p2); + RoadType rt = Extract(p2); + if (!ValParamRoadType(rt)) return CMD_ERROR; StationID station_to_join = GB(p2, 16, 16); bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; @@ -1815,20 +1824,18 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; - if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR; - /* Trams only have drive through stops */ - if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR; + if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR; DiagDirection ddir; Axis axis; if (is_drive_through) { /* By definition axis is valid, due to there being 2 axes and reading 1 bit. */ - axis = Extract(p2); + axis = Extract(p2); ddir = AxisToDiagDir(axis); } else { /* By definition ddir is valid, due to there being 4 diagonal directions and reading 2 bits. */ - ddir = Extract(p2); + ddir = Extract(p2); axis = DiagDirToAxis(ddir); } @@ -1838,12 +1845,12 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin /* Total road stop cost. */ CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]); StationID est = INVALID_STATION; - ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type, axis, &est, rts); + ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type, axis, &est, rt); if (ret.Failed()) return ret; cost.AddCost(ret); Station *st = nullptr; - ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 5), roadstop_area, &st); + ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 2), roadstop_area, &st); if (ret.Failed()) return ret; /* Check if this number of road stops can be allocated. */ @@ -1855,9 +1862,11 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (flags & DC_EXEC) { /* Check every tile in the area. */ TILE_AREA_LOOP(cur_tile, roadstop_area) { - RoadTypes cur_rts = GetRoadTypes(cur_tile); - Owner road_owner = HasBit(cur_rts, ROADTYPE_ROAD) ? GetRoadOwner(cur_tile, ROADTYPE_ROAD) : _current_company; - Owner tram_owner = HasBit(cur_rts, ROADTYPE_TRAM) ? GetRoadOwner(cur_tile, ROADTYPE_TRAM) : _current_company; + /* Get existing road types and owners before any tile clearing */ + RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE; + RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE; + Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company; + Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company; if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) { RemoveRoadStop(cur_tile, flags); @@ -1881,23 +1890,27 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS; if (is_drive_through) { - /* Update company infrastructure counts. If the current tile is a normal - * road tile, count only the new road bits needed to get a full diagonal road. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, cur_rts | rts) { - Company *c = Company::GetIfValid(rt == ROADTYPE_ROAD ? road_owner : tram_owner); - if (c != nullptr) { - c->infrastructure.road[rt] += 2 - (IsNormalRoadTile(cur_tile) && HasBit(cur_rts, rt) ? CountBits(GetRoadBits(cur_tile, rt)) : 0); - DirtyCompanyInfrastructureWindows(c->index); - } + /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old + * bits first. */ + if (IsNormalRoadTile(cur_tile)) { + UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD))); + UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM))); } - MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts | cur_rts, axis); + if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt; + if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt; + + UpdateCompanyRoadInfrastructure(road_rt, road_owner, 2); + UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, 2); + + MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, road_rt, tram_rt, axis); road_stop->MakeDriveThrough(); } else { + if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt; + if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt; /* Non-drive-through stop never overbuild and always count as two road bits. */ - Company::Get(st->owner)->infrastructure.road[FIND_FIRST_BIT(rts)] += 2; - MakeRoadStop(cur_tile, st->owner, st->index, rs_type, rts, ddir); + Company::Get(st->owner)->infrastructure.road[rt] += 2; + MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir); } Company::Get(st->owner)->infrastructure.station++; @@ -1983,14 +1996,11 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) } /* Update company infrastructure counts. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - c->infrastructure.road[rt] -= 2; - DirtyCompanyInfrastructureWindows(c->index); - } + FOR_ALL_ROADTRAMTYPES(rtt) { + RoadType rt = GetRoadType(tile, rtt); + UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), -2); } + Company::Get(st->owner)->infrastructure.station--; DirtyCompanyInfrastructureWindows(st->owner); @@ -2064,16 +2074,16 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue; /* Save information on to-be-restored roads before the stop is removed. */ - RoadTypes rts = ROADTYPES_NONE; RoadBits road_bits = ROAD_NONE; + RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE }; Owner road_owner[] = { OWNER_NONE, OWNER_NONE }; - assert_compile(lengthof(road_owner) == ROADTYPE_END); if (IsDriveThroughStopTile(cur_tile)) { - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(cur_tile)) { - road_owner[rt] = GetRoadOwner(cur_tile, rt); + FOR_ALL_ROADTRAMTYPES(rtt) { + road_type[rtt] = GetRoadType(cur_tile, rtt); + if (road_type[rtt] == INVALID_ROADTYPE) continue; + road_owner[rtt] = GetRoadOwner(cur_tile, rtt); /* If we don't want to preserve our roads then restore only roads of others. */ - if (keep_drive_through_roads || road_owner[rt] != _current_company) SetBit(rts, rt); + if (!keep_drive_through_roads && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE; } road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile))); } @@ -2087,19 +2097,14 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui had_success = true; /* Restore roads. */ - if ((flags & DC_EXEC) && rts != ROADTYPES_NONE) { - MakeRoadNormal(cur_tile, road_bits, rts, ClosestTownFromTile(cur_tile, UINT_MAX)->index, - road_owner[ROADTYPE_ROAD], road_owner[ROADTYPE_TRAM]); + if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) { + MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index, + road_owner[RTT_ROAD], road_owner[RTT_TRAM]); /* Update company infrastructure counts. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, rts) { - Company *c = Company::GetIfValid(GetRoadOwner(cur_tile, rt)); - if (c != nullptr) { - c->infrastructure.road[rt] += CountBits(road_bits); - DirtyCompanyInfrastructureWindows(c->index); - } - } + int count = CountBits(road_bits); + UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count); + UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_ROAD], count); } } @@ -2701,7 +2706,6 @@ static void DrawTile_Station(TileInfo *ti) const NewGRFSpriteLayout *layout = nullptr; DrawTileSprites tmp_rail_layout; const DrawTileSprites *t = nullptr; - RoadTypes roadtypes; int32 total_offset; const RailtypeInfo *rti = nullptr; uint32 relocation = 0; @@ -2712,7 +2716,6 @@ static void DrawTile_Station(TileInfo *ti) if (HasStationRail(ti->tile)) { rti = GetRailTypeInfo(GetRailType(ti->tile)); - roadtypes = ROADTYPES_NONE; total_offset = rti->GetRailtypeSpriteOffset(); if (IsCustomStationSpecIndex(ti->tile)) { @@ -2739,7 +2742,6 @@ static void DrawTile_Station(TileInfo *ti) } } } else { - roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE; total_offset = 0; } @@ -2924,10 +2926,30 @@ draw_default_foundation: if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); - if (HasBit(roadtypes, ROADTYPE_TRAM)) { - Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y; - DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE); - DrawRoadCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y); + if (IsRoadStop(ti->tile)) { + RoadType road_rt = GetRoadTypeRoad(ti->tile); + RoadType tram_rt = GetRoadTypeTram(ti->tile); + const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); + const RoadTypeInfo* tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + + if (IsDriveThroughStopTile(ti->tile)) { + Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y; + uint sprite_offset = axis == AXIS_X ? 1 : 0; + + DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset); + } else { + /* Non-drivethrough road stops are only valid for roads. */ + assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE); + + if (road_rti->UsesOverlay()) { + DiagDirection dir = GetRoadStopDir(ti->tile); + SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP); + DrawGroundSprite(ground + dir, PAL_NONE); + } + } + + /* Draw road, tram catenary */ + DrawRoadCatenary(ti); } if (IsRailWaypoint(ti->tile)) { @@ -2960,8 +2982,29 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y); } - if (roadtype == ROADTYPE_TRAM) { - DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y); + if (roadtype != INVALID_ROADTYPE) { + const RoadTypeInfo* rti = GetRoadTypeInfo(roadtype); + if (image >= 4) { + /* Drive-through stop */ + uint sprite_offset = 5 - image; + + /* Road underlay takes precendence over tram */ + if (rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND); + DrawSprite(ground + sprite_offset, PAL_NONE, x, y); + + SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY); + if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y); + } else if (RoadTypeIsTram(roadtype)) { + DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y); + } + } else { + /* Drive-in stop */ + if (RoadTypeIsRoad(roadtype) && rti->UsesOverlay()) { + SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP); + DrawSprite(ground + image, PAL_NONE, x, y); + } + } } /* Default waypoint has no railtype specific sprites */ @@ -2981,28 +3024,44 @@ static Foundation GetFoundation_Station(TileIndex tile, Slope tileh) static void GetTileDesc_Station(TileIndex tile, TileDesc *td) { td->owner[0] = GetTileOwner(tile); - if (IsDriveThroughStopTile(tile)) { + + if (IsRoadStopTile(tile)) { + RoadType road_rt = GetRoadTypeRoad(tile); + RoadType tram_rt = GetRoadTypeTram(tile); Owner road_owner = INVALID_OWNER; Owner tram_owner = INVALID_OWNER; - RoadTypes rts = GetRoadTypes(tile); - if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); - - /* Is there a mix of owners? */ - if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) || - (road_owner != INVALID_OWNER && road_owner != td->owner[0])) { - uint i = 1; - if (road_owner != INVALID_OWNER) { - td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER; - td->owner[i] = road_owner; - i++; - } - if (tram_owner != INVALID_OWNER) { - td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER; - td->owner[i] = tram_owner; + if (road_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt); + td->roadtype = rti->strings.name; + td->road_speed = rti->max_speed / 2; + road_owner = GetRoadOwner(tile, RTT_ROAD); + } + + if (tram_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt); + td->tramtype = rti->strings.name; + td->tram_speed = rti->max_speed / 2; + tram_owner = GetRoadOwner(tile, RTT_TRAM); + } + + if (IsDriveThroughStopTile(tile)) { + /* Is there a mix of owners? */ + if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) || + (road_owner != INVALID_OWNER && road_owner != td->owner[0])) { + uint i = 1; + if (road_owner != INVALID_OWNER) { + td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER; + td->owner[i] = road_owner; + i++; + } + if (tram_owner != INVALID_OWNER) { + td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER; + td->owner[i] = tram_owner; + } } } } + td->build_date = BaseStation::GetByTile(tile)->build_date; if (HasStationTileRail(tile)) { @@ -3088,7 +3147,10 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode break; case TRANSPORT_ROAD: - if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) { + if (IsRoadStop(tile)) { + RoadTramType rtt = (RoadTramType)sub_mode; + if (!HasTileRoadType(tile, rtt)) break; + DiagDirection dir = GetRoadStopDir(tile); Axis axis = DiagDirToAxis(dir); @@ -3982,15 +4044,16 @@ void DeleteOilRig(TileIndex tile) static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner) { if (IsRoadStopTile(tile)) { - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { + FOR_ALL_ROADTRAMTYPES(rtt) { /* Update all roadtypes, no matter if they are present */ - if (GetRoadOwner(tile, rt) == old_owner) { - if (HasTileRoadType(tile, rt)) { + if (GetRoadOwner(tile, rtt) == old_owner) { + RoadType rt = GetRoadType(tile, rtt); + if (rt != INVALID_ROADTYPE) { /* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */ Company::Get(old_owner)->infrastructure.road[rt] -= 2; if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2; } - SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); + SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); } } } @@ -4071,17 +4134,16 @@ static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags) /* Yeah... water can always remove stops, right? */ if (_current_company == OWNER_WATER) return true; - RoadTypes rts = GetRoadTypes(tile); - if (HasBit(rts, ROADTYPE_TRAM)) { - Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) { + Owner tram_owner = GetRoadOwner(tile, RTT_TRAM); if (tram_owner != OWNER_NONE && CheckOwnership(tram_owner).Failed()) return false; } - if (HasBit(rts, ROADTYPE_ROAD)) { - Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); + if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) { + Owner road_owner = GetRoadOwner(tile, RTT_ROAD); if (road_owner != OWNER_TOWN) { if (road_owner != OWNER_NONE && CheckOwnership(road_owner).Failed()) return false; } else { - if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags).Failed()) return false; + if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags).Failed()) return false; } } @@ -4102,8 +4164,8 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags) case STATION_RAIL: return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD); case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); case STATION_AIRPORT: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST); - case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST); - case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST); + case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST); + case STATION_BUS: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST); case STATION_BUOY: return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY); case STATION_DOCK: return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST); case STATION_OILRIG: diff --git a/src/station_func.h b/src/station_func.h index bc18b038cd..a6d082b930 100644 --- a/src/station_func.h +++ b/src/station_func.h @@ -18,6 +18,7 @@ #include "vehicle_type.h" #include "economy_func.h" #include "rail.h" +#include "road.h" #include "linkgraph/linkgraph_type.h" #include "industry_type.h" diff --git a/src/station_map.h b/src/station_map.h index e591787e3b..35766ec699 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -17,6 +17,7 @@ #include "water_map.h" #include "station_func.h" #include "rail.h" +#include "road.h" typedef byte StationGfx; ///< Index of station graphics. @see _station_display_datas @@ -583,15 +584,16 @@ static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, * @param o the owner of the roadstop * @param sid the station to which this tile belongs * @param rst the type of roadstop to make this tile - * @param rt the roadtypes on this tile + * @param road_rt the road roadtype on this tile + * @param tram_rt the tram roadtype 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) +static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_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); + SetRoadTypes(t, road_rt, tram_rt); + SetRoadOwner(t, RTT_ROAD, o); + SetRoadOwner(t, RTT_TRAM, o); } /** @@ -602,15 +604,16 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp * @param tram the owner of the tram * @param sid the station to which this tile belongs * @param rst the type of roadstop to make this tile - * @param rt the roadtypes on this tile + * @param road_rt the road roadtype on this tile + * @param tram_rt the tram roadtype 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) +static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_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); + SetRoadTypes(t, road_rt, tram_rt); + SetRoadOwner(t, RTT_ROAD, road); + SetRoadOwner(t, RTT_TRAM, tram); } /** diff --git a/src/table/engines.h b/src/table/engines.h index a5acd28439..7a40e4f7ed 100644 --- a/src/table/engines.h +++ b/src/table/engines.h @@ -669,7 +669,7 @@ static const AircraftVehicleInfo _orig_aircraft_vehicle_info[] = { * Tractive effort coefficient by default is the same as TTDPatch, 0.30*256=76 * Air drag value depends on the top speed of the vehicle. */ -#define ROV(a, b, c, d, e, f, g, h) { a, b, c, PR_RUNNING_ROADVEH, d, e, f, g, h, 76, 0, VE_DEFAULT, 0 } +#define ROV(a, b, c, d, e, f, g, h) { a, b, c, PR_RUNNING_ROADVEH, d, e, f, g, h, 76, 0, VE_DEFAULT, 0, ROADTYPE_ROAD } static const RoadVehicleInfo _orig_road_vehicle_info[] = { /* image_index sfx max_speed power * | cost_factor | | capacity | diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 0b3c92499f..89c289355a 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -11,6 +11,7 @@ #include "../newgrf_house.h" #include "../newgrf_engine.h" +#include "../newgrf_roadtype.h" /* Helper for filling property tables */ #define NIP(prop, base, variable, type, name) { name, (ptrdiff_t)cpp_offsetof(base, variable), cpp_sizeof(base, variable), prop, type } @@ -58,7 +59,7 @@ static const NIVariable _niv_vehicles[] = { NIV(0x47, "vehicle cargo info"), NIV(0x48, "vehicle type info"), NIV(0x49, "year of construction"), - NIV(0x4A, "current rail type info"), + NIV(0x4A, "current rail/road type info"), NIV(0x4B, "long date of last service"), NIV(0x4C, "current max speed"), NIV(0x4D, "position in articulated vehicle"), @@ -554,6 +555,48 @@ static const NIFeature _nif_town = { new NIHTown(), }; +/*** NewGRF road types ***/ + +static const NIVariable _niv_roadtypes[] = { + NIV(0x40, "terrain type"), + NIV(0x41, "enhanced tunnels"), + NIV(0x42, "level crossing status"), + NIV(0x43, "construction date"), + NIV(0x44, "town zone"), + NIV_END() +}; + +class NIHRoadType : public NIHelper { + bool IsInspectable(uint index) const override { return true; } + uint GetParent(uint index) const override { return UINT32_MAX; } + const void *GetInstance(uint index) const override { return nullptr; } + const void *GetSpec(uint index) const override { return nullptr; } + void SetStringParameters(uint index) const override { this->SetObjectAtStringParameters(STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE, INVALID_STRING_ID, index); } + uint32 GetGRFID(uint index) const override { return 0; } + + uint Resolve(uint index, uint var, uint param, bool *avail) const override + { + /* There is no unique GRFFile for the tile. Multiple GRFs can define different parts of the railtype. + * However, currently the NewGRF Debug GUI does not display variables depending on the GRF (like 0x7F) anyway. */ + RoadTypeResolverObject ro(nullptr, index, TCX_NORMAL, ROTSG_END); + return ro.GetScope(VSG_SCOPE_SELF)->GetVariable(var, param, avail); + } +}; + +static const NIFeature _nif_roadtype = { + nullptr, + nullptr, + _niv_roadtypes, + new NIHRoadType(), +}; + +static const NIFeature _nif_tramtype = { + nullptr, + nullptr, + _niv_roadtypes, + new NIHRoadType(), +}; + /** Table with all NIFeatures. */ static const NIFeature * const _nifeatures[] = { &_nif_vehicle, // GSF_TRAINS @@ -574,6 +617,8 @@ static const NIFeature * const _nifeatures[] = { &_nif_object, // GSF_OBJECTS &_nif_railtype, // GSF_RAILTYPES &_nif_airporttile, // GSF_AIRPORTTILES + &_nif_roadtype, // GSF_ROADTYPES + &_nif_tramtype, // GSF_TRAMTYPES &_nif_town, // GSF_FAKE_TOWNS }; assert_compile(lengthof(_nifeatures) == GSF_FAKE_END); diff --git a/src/table/railtypes.h b/src/table/railtypes.h index 73b73ff802..829b223b5e 100644 --- a/src/table/railtypes.h +++ b/src/table/railtypes.h @@ -156,7 +156,7 @@ static const RailtypeInfo _original_railtypes[] = { STR_RAIL_MENU_ELRAIL_CONSTRUCTION, STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION, STR_REPLACE_ELRAIL_VEHICLES, - STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE, + STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE, }, /* Offset of snow tiles */ diff --git a/src/table/road_land.h b/src/table/road_land.h index 19b8f57119..2b3a4177f8 100644 --- a/src/table/road_land.h +++ b/src/table/road_land.h @@ -41,35 +41,6 @@ static const DrawTileSprites _road_depot[] = { { {0xA4A, PAL_NONE}, _road_depot_NW } }; -static const DrawTileSeqStruct _tram_depot_NE[] = { - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x35) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 15, 16, 1) - TILE_SEQ_END() -}; - -static const DrawTileSeqStruct _tram_depot_SE[] = { - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x31) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 0, 1, 16) - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x32) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 15, 0, 1, 16) - TILE_SEQ_END() -}; - -static const DrawTileSeqStruct _tram_depot_SW[] = { - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x33) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 0, 16, 1) - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x34) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 15, 16, 1) - TILE_SEQ_END() -}; - -static const DrawTileSeqStruct _tram_depot_NW[] = { - TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x36) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 15, 0, 1, 16) - TILE_SEQ_END() -}; - -static const DrawTileSprites _tram_depot[] = { - { {0xA4A, PAL_NONE}, _tram_depot_NE }, - { {0xA4A, PAL_NONE}, _tram_depot_SE }, - { {0xA4A, PAL_NONE}, _tram_depot_SW }, - { {0xA4A, PAL_NONE}, _tram_depot_NW } -}; - /* Sprite layout for level crossings. The SpriteIDs are actually offsets * from the base SpriteID returned from the NewGRF sprite resolver. */ static const DrawTileSeqStruct _crossing_layout_ALL[] = { diff --git a/src/table/roadtypes.h b/src/table/roadtypes.h new file mode 100644 index 0000000000..348cdb677b --- /dev/null +++ b/src/table/roadtypes.h @@ -0,0 +1,183 @@ +/* $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 . + */ + +/** + * @file roadtypes.h + * All the roadtype-specific information is stored here. + */ + +#ifndef ROADTYPES_H +#define ROADTYPES_H + +/** + * Global Roadtype definition + */ +static const RoadTypeInfo _original_roadtypes[] = { + /* Road */ + { + /* GUI sprites */ + { + SPR_IMG_ROAD_X_DIR, + SPR_IMG_ROAD_Y_DIR, + SPR_IMG_AUTOROAD, + SPR_IMG_ROAD_DEPOT, + SPR_IMG_ROAD_TUNNEL, + SPR_IMG_CONVERT_ROAD, + }, + + { + SPR_CURSOR_ROAD_NESW, + SPR_CURSOR_ROAD_NWSE, + SPR_CURSOR_AUTOROAD, + SPR_CURSOR_ROAD_DEPOT, + SPR_CURSOR_TUNNEL_RAIL, + SPR_CURSOR_CONVERT_ROAD, + }, + + /* strings */ + { + STR_ROAD_NAME_ROAD, + STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION, + STR_ROAD_MENU_ROAD_CONSTRUCTION, + STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION, + STR_REPLACE_ROAD_VEHICLES, + STR_ENGINE_PREVIEW_ROAD_VEHICLE, + + STR_ERROR_CAN_T_BUILD_ROAD_HERE, + STR_ERROR_CAN_T_REMOVE_ROAD_FROM, + STR_ERROR_CAN_T_BUILD_ROAD_DEPOT, + { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_TRUCK_STATION }, + { STR_ERROR_CAN_T_REMOVE_BUS_STATION, STR_ERROR_CAN_T_REMOVE_TRUCK_STATION }, + STR_ERROR_CAN_T_CONVERT_ROAD, + { STR_STATION_BUILD_BUS_ORIENTATION, STR_STATION_BUILD_TRUCK_ORIENTATION }, + { STR_STATION_BUILD_BUS_ORIENTATION_TOOLTIP, STR_STATION_BUILD_TRUCK_ORIENTATION_TOOLTIP }, + }, + + /* Powered roadtypes */ + ROADTYPES_ROAD, + + /* flags */ + ROTFB_TOWN_BUILD, + + /* cost multiplier */ + 8, + + /* maintenance cost multiplier */ + 16, + + /* max speed */ + 0, + + /* road type label */ + 'ROAD', + + /* alternate labels */ + RoadTypeLabelList(), + + /* map colour */ + 0x01, + + /* introduction date */ + MIN_YEAR, + + /* roadtypes required for this to be introduced */ + ROADTYPES_NONE, + + /* introduction road types */ + ROADTYPES_ROAD, + + /* sort order */ + 0x07, + + { nullptr }, + { nullptr }, + }, + + /* Electrified Tram */ + { + /* GUI sprites */ + { + SPR_IMG_TRAMWAY_X_DIR, + SPR_IMG_TRAMWAY_Y_DIR, + SPR_IMG_AUTOTRAM, + SPR_IMG_ROAD_DEPOT, + SPR_IMG_ROAD_TUNNEL, + SPR_IMG_CONVERT_TRAM, + }, + + { + SPR_CURSOR_TRAMWAY_NESW, + SPR_CURSOR_TRAMWAY_NWSE, + SPR_CURSOR_AUTOTRAM, + SPR_CURSOR_ROAD_DEPOT, + SPR_CURSOR_TUNNEL_RAIL, + SPR_CURSOR_CONVERT_TRAM, + }, + + /* strings */ + { + STR_ROAD_NAME_TRAM, + STR_ROAD_TOOLBAR_TRAM_CONSTRUCTION_CAPTION, + STR_ROAD_MENU_TRAM_CONSTRUCTION, + STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION, + STR_REPLACE_TRAM_VEHICLES, + STR_ENGINE_PREVIEW_TRAM_VEHICLE, + + STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE, + STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM, + STR_ERROR_CAN_T_BUILD_TRAM_DEPOT, + { STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION }, + { STR_ERROR_CAN_T_REMOVE_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_REMOVE_CARGO_TRAM_STATION }, + STR_ERROR_CAN_T_CONVERT_TRAMWAY, + { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION }, + { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION_TOOLTIP, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION_TOOLTIP }, + }, + + /* Powered roadtypes */ + ROADTYPES_TRAM, + + /* flags */ + ROTFB_CATENARY | ROTFB_NO_HOUSES, + + /* cost multiplier */ + 16, + + /* maintenance cost multiplier */ + 24, + + /* max speed */ + 0, + + /* road type label */ + 'ELRL', + + /* alternate labels */ + RoadTypeLabelList(), + + /* map colour */ + 0x01, + + /* introduction date */ + INVALID_DATE, + + /* roadtypes required for this to be introduced */ + ROADTYPES_NONE, + + /* introduction road types */ + ROADTYPES_TRAM, + + /* sort order */ + 0x17, + + { nullptr }, + { nullptr }, + }, +}; + +#endif /* ROADTYPES_H */ diff --git a/src/table/sprites.h b/src/table/sprites.h index a573269975..5f5aa47057 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -56,7 +56,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682; /** Extra graphic spritenumbers */ static const SpriteID SPR_OPENTTD_BASE = 4896; -static const uint16 OPENTTD_SPRITE_COUNT = 179; +static const uint16 OPENTTD_SPRITE_COUNT = 184; /* Halftile-selection sprites */ static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE; @@ -273,13 +273,15 @@ static const SpriteID SPR_TRAMWAY_BUS_STOP_DT_X_W = SPR_TRAMWAY_BASE + 24; static const SpriteID SPR_TRAMWAY_BUS_STOP_DT_X_E = SPR_TRAMWAY_BASE + 26; static const SpriteID SPR_TRAMWAY_PAVED_STRAIGHT_Y = SPR_TRAMWAY_BASE + 46; static const SpriteID SPR_TRAMWAY_PAVED_STRAIGHT_X = SPR_TRAMWAY_BASE + 47; +static const SpriteID SPR_TRAMWAY_DEPOT_WITH_TRACK = SPR_TRAMWAY_BASE + 49; static const SpriteID SPR_TRAMWAY_BACK_WIRES_STRAIGHT = SPR_TRAMWAY_BASE + 55; static const SpriteID SPR_TRAMWAY_FRONT_WIRES_STRAIGHT = SPR_TRAMWAY_BASE + 56; static const SpriteID SPR_TRAMWAY_BACK_WIRES_SLOPED = SPR_TRAMWAY_BASE + 72; static const SpriteID SPR_TRAMWAY_FRONT_WIRES_SLOPED = SPR_TRAMWAY_BASE + 68; static const SpriteID SPR_TRAMWAY_TUNNEL_WIRES = SPR_TRAMWAY_BASE + 80; static const SpriteID SPR_TRAMWAY_BRIDGE = SPR_TRAMWAY_BASE + 107; -static const uint16 TRAMWAY_SPRITE_COUNT = 113; +static const SpriteID SPR_TRAMWAY_DEPOT_NO_TRACK = SPR_TRAMWAY_BASE + 113; +static const uint16 TRAMWAY_SPRITE_COUNT = 119; /** One way road sprites */ static const SpriteID SPR_ONEWAY_BASE = SPR_TRAMWAY_BASE + TRAMWAY_SPRITE_COUNT; @@ -563,6 +565,7 @@ static const SpriteID SPR_ROAD_SLOPE_START = 1343; static const SpriteID SPR_ROAD_Y_SNOW = 1351; static const SpriteID SPR_ROAD_X_SNOW = 1352; /* see _road_sloped_sprites_offset in road_cmd.cpp for offsets for sloped road tiles */ +static const SpriteID SPR_ROAD_DEPOT = 1408; static const SpriteID SPR_EXCAVATION_X = 1414; static const SpriteID SPR_EXCAVATION_Y = 1415; @@ -1099,6 +1102,7 @@ static const SpriteID SPR_IMG_ZOOMIN = 735; static const SpriteID SPR_IMG_ZOOMOUT = 736; static const SpriteID SPR_IMG_BUILDRAIL = 727; static const SpriteID SPR_IMG_BUILDROAD = 728; +static const SpriteID SPR_IMG_BUILDTRAMS = SPR_OPENTTD_BASE + 179; static const SpriteID SPR_IMG_BUILDWATER = 729; static const SpriteID SPR_IMG_BUILDAIR = 730; static const SpriteID SPR_IMG_LANDSCAPING = 4083; @@ -1343,6 +1347,11 @@ 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; +static const SpriteID SPR_IMG_CONVERT_ROAD = SPR_OPENTTD_BASE + 180; +static const CursorID SPR_CURSOR_CONVERT_ROAD = SPR_OPENTTD_BASE + 181; +static const SpriteID SPR_IMG_CONVERT_TRAM = SPR_OPENTTD_BASE + 182; +static const CursorID SPR_CURSOR_CONVERT_TRAM = SPR_OPENTTD_BASE + 183; + /* intro_gui.cpp, genworld_gui.cpp */ static const SpriteID SPR_SELECT_TEMPERATE = 4882; static const SpriteID SPR_SELECT_TEMPERATE_PUSHED = 4883; diff --git a/src/tile_cmd.h b/src/tile_cmd.h index 855aa297da..80f8074ac2 100644 --- a/src/tile_cmd.h +++ b/src/tile_cmd.h @@ -64,7 +64,10 @@ struct TileDesc { uint64 dparam[2]; ///< Parameters of the \a str string StringID railtype; ///< Type of rail on the tile. uint16 rail_speed; ///< Speed limit of rail (bridges and track) - uint16 road_speed; ///< Speed limit of road (bridges) + StringID roadtype; ///< Type of road on the tile. + uint16 road_speed; ///< Speed limit of road (bridges and track) + StringID tramtype; ///< Type of tram on the tile. + uint16 tram_speed; ///< Speed limit of tram (bridges and track) }; /** diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 7cd5facf1d..23c10fb727 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -17,6 +17,7 @@ #include "command_func.h" #include "vehicle_gui.h" #include "rail_gui.h" +#include "road.h" #include "road_gui.h" #include "date_func.h" #include "vehicle_func.h" @@ -64,6 +65,7 @@ uint _toolbar_width = 0; RailType _last_built_railtype; RoadType _last_built_roadtype; +RoadType _last_built_tramtype; static ScreenshotType _confirmed_screenshot_type; ///< Screenshot type the current query is about to confirm. @@ -893,22 +895,7 @@ static CallBackFunction MenuClickBuildRail(int index) static CallBackFunction ToolbarBuildRoadClick(Window *w) { - const Company *c = Company::Get(_local_company); - DropDownList list; - - /* Road is always visible and available. */ - list.emplace_back(new DropDownListIconItem(SPR_IMG_ROAD_X_DIR, PAL_NONE, STR_ROAD_MENU_ROAD_CONSTRUCTION, ROADTYPE_ROAD, false)); - - /* Tram is only visible when there will be a tram, and available when that has been introduced. */ - Engine *e; - FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) { - if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue; - if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue; - - list.emplace_back(new DropDownListIconItem(SPR_IMG_TRAMWAY_X_DIR, PAL_NONE, STR_ROAD_MENU_TRAM_CONSTRUCTION, ROADTYPE_TRAM, !HasBit(c->avail_roadtypes, ROADTYPE_TRAM))); - break; - } - ShowDropDownList(w, std::move(list), _last_built_roadtype, WID_TN_ROADS, 140, true, true); + ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TN_ROADS, 140, true, true); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); return CBF_NONE; } @@ -926,6 +913,28 @@ static CallBackFunction MenuClickBuildRoad(int index) return CBF_NONE; } +/* --- Tram button menu --- */ + +static CallBackFunction ToolbarBuildTramClick(Window *w) +{ + ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TN_TRAMS, 140, true, true); + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + return CBF_NONE; +} + +/** + * Handle click on the entry in the Build Tram menu. + * + * @param index RoadType to show the build toolbar for. + * @return #CBF_NONE + */ +static CallBackFunction MenuClickBuildTram(int index) +{ + _last_built_tramtype = (RoadType)index; + ShowBuildRoadToolbar(_last_built_tramtype); + return CBF_NONE; +} + /* --- Water button menu --- */ static CallBackFunction ToolbarBuildWaterClick(Window *w) @@ -1251,9 +1260,41 @@ static CallBackFunction ToolbarScenGenIndustry(Window *w) static CallBackFunction ToolbarScenBuildRoadClick(Window *w) { - w->HandleButtonClick(WID_TE_ROADS); + ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TE_ROADS, 140, true, true); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); - ShowBuildRoadScenToolbar(); + return CBF_NONE; +} + +/** + * Handle click on the entry in the Build Road menu. + * + * @param index RoadType to show the build toolbar for. + * @return #CBF_NONE + */ +static CallBackFunction ToolbarScenBuildRoad(int index) +{ + _last_built_roadtype = (RoadType)index; + ShowBuildRoadScenToolbar(_last_built_roadtype); + return CBF_NONE; +} + +static CallBackFunction ToolbarScenBuildTramClick(Window *w) +{ + ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TE_TRAMS, 140, true, true); + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + return CBF_NONE; +} + +/** + * Handle click on the entry in the Build Tram menu. + * + * @param index RoadType to show the build toolbar for. + * @return #CBF_NONE + */ +static CallBackFunction ToolbarScenBuildTram(int index) +{ + _last_built_tramtype = (RoadType)index; + ShowBuildRoadScenToolbar(_last_built_tramtype); return CBF_NONE; } @@ -1311,12 +1352,13 @@ static MenuClickedProc * const _menu_clicked_procs[] = { nullptr, // 20 MenuClickBuildRail, // 21 MenuClickBuildRoad, // 22 - MenuClickBuildWater, // 23 - MenuClickBuildAir, // 24 - MenuClickForest, // 25 - MenuClickMusicWindow, // 26 - MenuClickNewspaper, // 27 - MenuClickHelp, // 28 + MenuClickBuildTram, // 23 + MenuClickBuildWater, // 24 + MenuClickBuildAir, // 25 + MenuClickForest, // 26 + MenuClickMusicWindow, // 27 + MenuClickNewspaper, // 28 + MenuClickHelp, // 29 }; /** Full blown container to make it behave exactly as we want :) */ @@ -1772,6 +1814,7 @@ class NWidgetMainToolbarContainer : public NWidgetToolbarContainer { WID_TN_ZOOM_OUT, WID_TN_RAILS, WID_TN_ROADS, + WID_TN_TRAMS, WID_TN_WATER, WID_TN_AIR, WID_TN_LANDSCAPE, @@ -1832,6 +1875,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer { WID_TE_TOWN_GENERATE, WID_TE_INDUSTRY, WID_TE_ROADS, + WID_TE_TRAMS, WID_TE_WATER, WID_TE_TREES, WID_TE_SIGNS, @@ -1851,6 +1895,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer { WID_TE_TOWN_GENERATE, WID_TE_INDUSTRY, WID_TE_ROADS, + WID_TE_TRAMS, WID_TE_WATER, WID_TE_TREES, WID_TE_SIGNS, @@ -1864,6 +1909,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer { WID_TE_TOWN_GENERATE, WID_TE_INDUSTRY, WID_TE_ROADS, + WID_TE_TRAMS, WID_TE_WATER, WID_TE_TREES, WID_TE_SIGNS, @@ -1939,6 +1985,7 @@ static ToolbarButtonProc * const _toolbar_button_procs[] = { ToolbarZoomOutClick, ToolbarBuildRailClick, ToolbarBuildRoadClick, + ToolbarBuildTramClick, ToolbarBuildWaterClick, ToolbarBuildAirClick, ToolbarForestClick, @@ -1973,6 +2020,7 @@ enum MainToolbarHotkeys { MTHK_ZOOM_OUT, MTHK_BUILD_RAIL, MTHK_BUILD_ROAD, + MTHK_BUILD_TRAM, MTHK_BUILD_DOCKS, MTHK_BUILD_AIRPORT, MTHK_BUILD_TREES, @@ -2017,7 +2065,7 @@ struct MainToolbarWindow : Window { /* If spectator, disable all construction buttons * ie : Build road, rail, ships, airports and landscaping * Since enabled state is the default, just disable when needed */ - this->SetWidgetsDisabledState(_local_company == COMPANY_SPECTATOR, WID_TN_RAILS, WID_TN_ROADS, WID_TN_WATER, WID_TN_AIR, WID_TN_LANDSCAPE, WIDGET_LIST_END); + this->SetWidgetsDisabledState(_local_company == COMPANY_SPECTATOR, WID_TN_RAILS, WID_TN_ROADS, WID_TN_TRAMS, WID_TN_WATER, WID_TN_AIR, WID_TN_LANDSCAPE, WIDGET_LIST_END); /* disable company list drop downs, if there are no companies */ this->SetWidgetsDisabledState(Company::GetNumItems() == 0, WID_TN_STATIONS, WID_TN_FINANCES, WID_TN_TRAINS, WID_TN_ROADVEHS, WID_TN_SHIPS, WID_TN_AIRCRAFT, WIDGET_LIST_END); @@ -2025,6 +2073,8 @@ struct MainToolbarWindow : Window { this->SetWidgetDisabledState(WID_TN_STORY, StoryPage::GetNumItems() == 0); this->SetWidgetDisabledState(WID_TN_RAILS, !CanBuildVehicleInfrastructure(VEH_TRAIN)); + this->SetWidgetDisabledState(WID_TN_ROADS, !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_ROAD)); + this->SetWidgetDisabledState(WID_TN_TRAMS, !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM)); this->SetWidgetDisabledState(WID_TN_AIR, !CanBuildVehicleInfrastructure(VEH_AIRCRAFT)); this->DrawWidgets(); @@ -2068,6 +2118,7 @@ struct MainToolbarWindow : Window { case MTHK_ZOOM_OUT: ToolbarZoomOutClick(this); break; case MTHK_BUILD_RAIL: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype); break; case MTHK_BUILD_ROAD: ShowBuildRoadToolbar(_last_built_roadtype); break; + case MTHK_BUILD_TRAM: if (CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM)) ShowBuildRoadToolbar(_last_built_tramtype); break; case MTHK_BUILD_DOCKS: ShowBuildDocksToolbar(); break; case MTHK_BUILD_AIRPORT: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break; case MTHK_BUILD_TREES: ShowBuildTreesToolbar(); break; @@ -2179,6 +2230,7 @@ static Hotkey maintoolbar_hotkeys[] = { Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTHK_ZOOM_OUT), Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL), Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD), + Hotkey((uint16)0, "build_tram", MTHK_BUILD_TRAM), Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS), Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT), Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES), @@ -2224,6 +2276,7 @@ static NWidgetBase *MakeMainToolbar(int *biggest_index) SPR_IMG_ZOOMOUT, // WID_TN_ZOOMOUT SPR_IMG_BUILDRAIL, // WID_TN_RAILS SPR_IMG_BUILDROAD, // WID_TN_ROADS + SPR_IMG_BUILDTRAMS, // WID_TN_TRAMS SPR_IMG_BUILDWATER, // WID_TN_WATER SPR_IMG_BUILDAIR, // WID_TN_AIR SPR_IMG_LANDSCAPING, // WID_TN_LANDSCAPE @@ -2282,14 +2335,15 @@ static MenuClickedProc * const _scen_toolbar_dropdown_procs[] = { nullptr, // 11 nullptr, // 12 nullptr, // 13 - nullptr, // 14 - nullptr, // 15 + ToolbarScenBuildRoad, // 14 + ToolbarScenBuildTram, // 15 nullptr, // 16 nullptr, // 17 nullptr, // 18 - MenuClickMusicWindow, // 19 - MenuClickHelp, // 20 - nullptr, // 21 + nullptr, // 19 + MenuClickMusicWindow, // 20 + MenuClickHelp, // 21 + nullptr, // 22 }; static ToolbarButtonProc * const _scen_toolbar_button_procs[] = { @@ -2308,6 +2362,7 @@ static ToolbarButtonProc * const _scen_toolbar_button_procs[] = { ToolbarScenGenTown, ToolbarScenGenIndustry, ToolbarScenBuildRoadClick, + ToolbarScenBuildTramClick, ToolbarScenBuildDocks, ToolbarScenPlantTrees, ToolbarScenPlaceSign, @@ -2326,6 +2381,7 @@ enum MainToolbarEditorHotkeys { MTEHK_GENTOWN, MTEHK_GENINDUSTRY, MTEHK_BUILD_ROAD, + MTEHK_BUILD_TRAM, MTEHK_BUILD_DOCKS, MTEHK_BUILD_TREES, MTEHK_SIGN, @@ -2366,6 +2422,8 @@ struct ScenarioEditorToolbarWindow : Window { { this->SetWidgetDisabledState(WID_TE_DATE_BACKWARD, _settings_game.game_creation.starting_year <= MIN_YEAR); this->SetWidgetDisabledState(WID_TE_DATE_FORWARD, _settings_game.game_creation.starting_year >= MAX_YEAR); + this->SetWidgetDisabledState(WID_TE_ROADS, (GetRoadTypes(true) & ~_roadtypes_type) == ROADTYPES_NONE); + this->SetWidgetDisabledState(WID_TE_TRAMS, (GetRoadTypes(true) & _roadtypes_type) == ROADTYPES_NONE); this->DrawWidgets(); } @@ -2432,6 +2490,7 @@ struct ScenarioEditorToolbarWindow : Window { case MTEHK_GENTOWN: ToolbarScenGenTown(this); break; case MTEHK_GENINDUSTRY: ToolbarScenGenIndustry(this); break; case MTEHK_BUILD_ROAD: ToolbarScenBuildRoadClick(this); break; + case MTEHK_BUILD_TRAM: ToolbarScenBuildTramClick(this); break; case MTEHK_BUILD_DOCKS: ToolbarScenBuildDocks(this); break; case MTEHK_BUILD_TREES: ToolbarScenPlantTrees(this); break; case MTEHK_SIGN: cbf = ToolbarScenPlaceSign(this); break; @@ -2535,6 +2594,7 @@ static Hotkey scenedit_maintoolbar_hotkeys[] = { Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN), Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY), Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD), + Hotkey((uint16)0, "build_tram", MTEHK_BUILD_TRAM), Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS), Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES), Hotkey(WKC_F10, "build_sign", MTEHK_SIGN), @@ -2577,7 +2637,8 @@ static const NWidgetPart _nested_toolb_scen_inner_widgets[] = { NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_LAND_GENERATE), SetDataTip(SPR_IMG_LANDSCAPING, STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_INDUSTRY), SetDataTip(SPR_IMG_INDUSTRY, STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION), + NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION), + NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_TRAMS), SetDataTip(SPR_IMG_BUILDTRAMS, STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_WATER), SetDataTip(SPR_IMG_BUILDWATER, STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TREES), SetDataTip(SPR_IMG_PLANTTREES, STR_SCENEDIT_TOOLBAR_PLANT_TREES), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_SIGNS), SetDataTip(SPR_IMG_SIGN, STR_SCENEDIT_TOOLBAR_PLACE_SIGN), @@ -2609,6 +2670,7 @@ void AllocateToolbar() { /* Clean old GUI values; railtype is (re)set by rail_gui.cpp */ _last_built_roadtype = ROADTYPE_ROAD; + _last_built_tramtype = ROADTYPE_TRAM; if (_game_mode == GM_EDITOR) { new ScenarioEditorToolbarWindow(&_toolb_scen_desc); diff --git a/src/town.h b/src/town.h index 5f0f4992f5..d669ccafb4 100644 --- a/src/town.h +++ b/src/town.h @@ -299,4 +299,6 @@ static inline uint16 TownTicksToGameTicks(uint16 ticks) { extern CargoTypes _town_cargoes_accepted; +RoadType GetTownRoadType(const Town *t); + #endif /* TOWN_H */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index f3acfb4a38..65032c1fb2 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -10,6 +10,7 @@ /** @file town_cmd.cpp Handling of town tiles. */ #include "stdafx.h" +#include "road.h" #include "road_internal.h" /* Cleaning up road bits */ #include "road_cmd.h" #include "landscape.h" @@ -891,7 +892,38 @@ static RoadBits GetTownRoadBits(TileIndex tile) { if (IsRoadDepotTile(tile) || IsStandardRoadStopTile(tile)) return ROAD_NONE; - return GetAnyRoadBits(tile, ROADTYPE_ROAD, true); + return GetAnyRoadBits(tile, RTT_ROAD, true); +} + +RoadType GetTownRoadType(const Town *t) +{ + RoadType best_rt = ROADTYPE_ROAD; + const RoadTypeInfo *best = nullptr; + const uint16 assume_max_speed = 50; + + for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { + if (RoadTypeIsTram(rt)) continue; + + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + + /* Unused road type. */ + if (rti->label == 0) continue; + + /* Can town build this road. */ + if (!HasBit(rti->flags, ROTF_TOWN_BUILD)) continue; + + /* Not yet introduced at this date. */ + if (IsInsideMM(rti->introduction_date, 0, MAX_DAY) && rti->introduction_date > _date) continue; + + if (best != nullptr) { + if ((rti->max_speed == 0 ? assume_max_speed : rti->max_speed) < (best->max_speed == 0 ? assume_max_speed : best->max_speed)) continue; + } + + best_rt = rt; + best = rti; + } + + return best_rt; } /** @@ -950,7 +982,8 @@ static bool IsRoadAllowedHere(Town *t, TileIndex tile, DiagDirection dir) /* No, try if we are able to build a road piece there. * If that fails clear the land, and if that fails exit. * This is to make sure that we can build a road here later. */ - if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() && + RoadType rt = GetTownRoadType(t); + if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X) | (rt << 4), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() && DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR).Failed()) { return false; } @@ -1118,7 +1151,8 @@ static bool GrowTownWithExtraHouse(Town *t, TileIndex tile) */ static bool GrowTownWithRoad(const Town *t, TileIndex tile, RoadBits rcmd) { - if (DoCommand(tile, rcmd, t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) { + RoadType rt = GetTownRoadType(t); + if (DoCommand(tile, rcmd | (rt << 4), t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) { _grow_town_result = GROWTH_SUCCEED; return true; } @@ -1181,8 +1215,9 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi byte bridge_type = RandomRange(MAX_BRIDGES - 1); /* Can we actually build the bridge? */ - if (DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) { - DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE); + RoadType rt = GetTownRoadType(t); + if (DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) { + DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE); _grow_town_result = GROWTH_SUCCEED; return true; } @@ -1191,6 +1226,37 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi return false; } + +/** + * Checks whether at least one surrounding roads allows to build a house here + * + * @param t the tile where the house will be built + * @return true if at least one surrounding roadtype allows building houses here + */ +static inline bool RoadTypesAllowHouseHere(TileIndex t) +{ + static const TileIndexDiffC tiles[] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} }; + bool allow = false; + + for (const TileIndexDiffC *ptr = tiles; ptr != endof(tiles); ++ptr) { + TileIndex cur_tile = t + ToTileIndexDiff(*ptr); + if (!IsValidTile(cur_tile)) continue; + + if (!(IsTileType(cur_tile, MP_ROAD) || IsTileType(cur_tile, MP_STATION))) continue; + allow = true; + + RoadType road_rt = GetRoadTypeRoad(cur_tile); + RoadType tram_rt = GetRoadTypeTram(cur_tile); + if (road_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(road_rt)->flags, ROTF_NO_HOUSES)) return true; + if (tram_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(tram_rt)->flags, ROTF_NO_HOUSES)) return true; + } + + /* If no road was found surrounding the tile we can allow building the house since there is + * nothing which forbids it, if a road was found but the execution reached this point, then + * all the found roads don't allow houses to be built */ + return !allow; +} + /** * Grows the given town. * There are at the moment 3 possible way's for @@ -1369,6 +1435,8 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t } } + allow_house &= RoadTypesAllowHouseHere(house_tile); + if (allow_house) { /* Build a house, but not if there already is a house there. */ if (!IsTileType(house_tile, MP_HOUSE)) { @@ -1508,14 +1576,14 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile) } tile = TileAddByDiagDir(tile, target_dir); - if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, ROADTYPE_ROAD)) { + if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, RTT_ROAD)) { /* Don't allow building over roads of other cities */ - if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) { + if (IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) { return false; - } else if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) { + } else if (IsRoadOwner(tile, RTT_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) { /* If we are in the SE, and this road-piece has no town owner yet, it just found an * owner :) (happy happy happy road now) */ - SetRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN); + SetRoadOwner(tile, RTT_ROAD, OWNER_TOWN); SetTownIndex(tile, t->index); } } @@ -1589,7 +1657,8 @@ static bool GrowTown(Town *t) /* Only work with plain land that not already has a house */ if (!IsTileType(tile, MP_HOUSE) && IsTileFlat(tile)) { if (DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR).Succeeded()) { - DoCommand(tile, GenRandomRoadBits(), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD); + RoadType rt = GetTownRoadType(t); + DoCommand(tile, GenRandomRoadBits() | (rt << 4), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD); cur_company.Restore(); return true; } @@ -2192,6 +2261,9 @@ static inline bool CanBuildHouseHere(TileIndex tile, bool noslope) Slope slope = GetTileSlope(tile); if ((noslope && slope != SLOPE_FLAT) || IsSteepSlope(slope)) return false; + /* at least one RoadTypes allow building the house here? */ + if (!RoadTypesAllowHouseHere(tile)) return false; + /* building under a bridge? */ if (IsBridgeAbove(tile)) return false; diff --git a/src/tunnel_map.h b/src/tunnel_map.h index d6f475d05c..a1df77f3ce 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -48,7 +48,7 @@ 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) +static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); @@ -59,9 +59,9 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp SB(_me[t].m6, 2, 4, 0); _me[t].m7 = 0; _me[t].m8 = 0; - SetRoadOwner(t, ROADTYPE_ROAD, o); - if (o != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, o); - SetRoadTypes(t, r); + SetRoadOwner(t, RTT_ROAD, o); + if (o != OWNER_TOWN) SetRoadOwner(t, RTT_TRAM, o); + SetRoadTypes(t, road_rt, tram_rt); } /** diff --git a/src/tunnelbridge.h b/src/tunnelbridge.h index 0a2c2293d5..93603acb59 100644 --- a/src/tunnelbridge.h +++ b/src/tunnelbridge.h @@ -13,6 +13,7 @@ #define TUNNELBRIDGE_H #include "map_func.h" +#include "tile_map.h" void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height); void MarkBridgeDirty(TileIndex tile); @@ -33,6 +34,18 @@ static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) return abs(x2 + y2 - x1 - y1) - 1; } +/** + * Sets the ownership of the bridge/tunnel ramps + * @param begin The begin of the tunnel or bridge. + * @param end The end of the tunnel or bridge. + * @param owner The new owner to set + */ +static inline void SetTunnelBridgeOwner(TileIndex begin, TileIndex end, Owner owner) +{ + SetTileOwner(begin, owner); + SetTileOwner(end, owner); +} + extern TileIndex _build_tunnel_endtile; #endif /* TUNNELBRIDGE_H */ diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 78586b1a2e..83cec4fd36 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -37,6 +37,7 @@ #include "pbs.h" #include "company_base.h" #include "newgrf_railtype.h" +#include "newgrf_roadtype.h" #include "object_base.h" #include "water.h" #include "company_gui.h" @@ -230,7 +231,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u CompanyID company = _current_company; RailType railtype = INVALID_RAILTYPE; - RoadTypes roadtypes = ROADTYPES_NONE; + RoadType roadtype = INVALID_ROADTYPE; /* unpack parameters */ BridgeType bridge_type = GB(p2, 0, 8); @@ -242,8 +243,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u /* type of bridge */ switch (transport_type) { case TRANSPORT_ROAD: - roadtypes = Extract(p2); - if (!HasExactlyOneBit(roadtypes) || !HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR; + roadtype = Extract(p2); + if (!ValParamRoadType(roadtype)) return CMD_ERROR; break; case TRANSPORT_RAIL: @@ -313,16 +314,41 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u CommandCost cost(EXPENSES_CONSTRUCTION); Owner owner; bool is_new_owner; + RoadType road_rt = INVALID_ROADTYPE; + RoadType tram_rt = INVALID_ROADTYPE; if (IsBridgeTile(tile_start) && IsBridgeTile(tile_end) && GetOtherBridgeEnd(tile_start) == tile_end && GetTunnelBridgeTransportType(tile_start) == transport_type) { /* Replace a current bridge. */ + switch (transport_type) { + case TRANSPORT_RAIL: + /* Keep the reservation, the path stays valid. */ + pbs_reservation = HasTunnelBridgeReservation(tile_start); + break; + + case TRANSPORT_ROAD: + /* Do not remove road types when upgrading a bridge */ + road_rt = GetRoadTypeRoad(tile_start); + tram_rt = GetRoadTypeTram(tile_start); + break; + + default: break; + } + /* If this is a railway bridge, make sure the railtypes match. */ if (transport_type == TRANSPORT_RAIL && GetRailType(tile_start) != railtype) { return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } + /* If this is a road bridge, make sure the roadtype matches. */ + if (transport_type == TRANSPORT_ROAD) { + RoadType existing_rt = RoadTypeIsRoad(roadtype) ? road_rt : tram_rt; + if (existing_rt != roadtype && existing_rt != INVALID_ROADTYPE) { + return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + } + } + /* Do not replace town bridges with lower speed bridges, unless in scenario editor. */ if (!(flags & DC_QUERY_COST) && IsTileOwner(tile_start, OWNER_TOWN) && GetBridgeSpec(bridge_type)->speed < GetBridgeSpec(GetBridgeType(tile_start))->speed && @@ -338,7 +364,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u } /* Do not replace the bridge with the same bridge type. */ - if (!(flags & DC_QUERY_COST) && (bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || (roadtypes & ~GetRoadTypes(tile_start)) == 0)) { + if (!(flags & DC_QUERY_COST) && (bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || road_rt == roadtype || tram_rt == roadtype)) { return_cmd_error(STR_ERROR_ALREADY_BUILT); } @@ -353,20 +379,6 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u /* If bridge belonged to bankrupt company, it has a new owner now */ is_new_owner = (owner == OWNER_NONE); if (is_new_owner) owner = company; - - switch (transport_type) { - case TRANSPORT_RAIL: - /* Keep the reservation, the path stays valid. */ - pbs_reservation = HasTunnelBridgeReservation(tile_start); - break; - - case TRANSPORT_ROAD: - /* Do not remove road types when upgrading a bridge */ - roadtypes |= GetRoadTypes(tile_start); - break; - - default: break; - } } else { /* Build a new bridge. */ @@ -471,6 +483,13 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u is_new_owner = true; } + bool hasroad = road_rt != INVALID_ROADTYPE; + bool hastram = tram_rt != INVALID_ROADTYPE; + if (transport_type == TRANSPORT_ROAD) { + if (RoadTypeIsRoad(roadtype)) road_rt = roadtype; + if (RoadTypeIsTram(roadtype)) tram_rt = roadtype; + } + /* do the drill? */ if (flags & DC_EXEC) { DiagDirection dir = AxisToDiagDir(direction); @@ -487,24 +506,26 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u break; case TRANSPORT_ROAD: { - RoadTypes prev_roadtypes = IsBridgeTile(tile_start) ? GetRoadTypes(tile_start) : ROADTYPES_NONE; if (is_new_owner) { /* Also give unowned present roadtypes to new owner */ - if (HasBit(prev_roadtypes, ROADTYPE_ROAD) && GetRoadOwner(tile_start, ROADTYPE_ROAD) == OWNER_NONE) ClrBit(prev_roadtypes, ROADTYPE_ROAD); - if (HasBit(prev_roadtypes, ROADTYPE_TRAM) && GetRoadOwner(tile_start, ROADTYPE_TRAM) == OWNER_NONE) ClrBit(prev_roadtypes, ROADTYPE_TRAM); + if (hasroad && GetRoadOwner(tile_start, RTT_ROAD) == OWNER_NONE) hasroad = false; + if (hastram && GetRoadOwner(tile_start, RTT_TRAM) == OWNER_NONE) hastram = false; } if (c != nullptr) { /* Add all new road types to the company infrastructure counter. */ - RoadType new_rt; - FOR_EACH_SET_ROADTYPE(new_rt, roadtypes ^ prev_roadtypes) { + if (!hasroad && road_rt != INVALID_ROADTYPE) { /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[new_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; + c->infrastructure.road[road_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; + } + if (!hastram && tram_rt != INVALID_ROADTYPE) { + /* A full diagonal road tile has two road bits. */ + c->infrastructure.road[tram_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; } } - Owner owner_road = HasBit(prev_roadtypes, ROADTYPE_ROAD) ? GetRoadOwner(tile_start, ROADTYPE_ROAD) : company; - Owner owner_tram = HasBit(prev_roadtypes, ROADTYPE_TRAM) ? GetRoadOwner(tile_start, ROADTYPE_TRAM) : company; - MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, roadtypes); - MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), roadtypes); + Owner owner_road = hasroad ? GetRoadOwner(tile_start, RTT_ROAD) : company; + Owner owner_tram = hastram ? GetRoadOwner(tile_start, RTT_TRAM) : company; + MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, road_rt, tram_rt); + MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), road_rt, tram_rt); break; } @@ -538,7 +559,15 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u bridge_len += 2; // begin and end tiles/ramps switch (transport_type) { - case TRANSPORT_ROAD: cost.AddCost(bridge_len * _price[PR_BUILD_ROAD] * 2 * CountBits(roadtypes)); break; + case TRANSPORT_ROAD: + if (road_rt != INVALID_ROADTYPE) { + cost.AddCost(bridge_len * 2 * RoadBuildCost(road_rt)); + } + if (tram_rt != INVALID_ROADTYPE) { + cost.AddCost(bridge_len * 2 * RoadBuildCost(tram_rt)); + } + break; + case TRANSPORT_RAIL: cost.AddCost(bridge_len * RailBuildCost(railtype)); break; default: break; } @@ -562,7 +591,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u * Build Tunnel. * @param start_tile start tile of tunnel * @param flags type of operation - * @param p1 bit 0-5 railtype or roadtypes + * @param p1 bit 0-5 railtype or roadtype * bit 8-9 transport type * @param p2 unused * @param text unused @@ -573,9 +602,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, CompanyID company = _current_company; TransportType transport_type = Extract(p1); - RailType railtype = INVALID_RAILTYPE; - RoadTypes rts = ROADTYPES_NONE; + RoadType roadtype = INVALID_ROADTYPE; _build_tunnel_endtile = 0; switch (transport_type) { case TRANSPORT_RAIL: @@ -584,8 +612,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, break; case TRANSPORT_ROAD: - rts = Extract(p1); - if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(company, rts)) return CMD_ERROR; + roadtype = Extract(p1); + if (!ValParamRoadType(roadtype)) return CMD_ERROR; break; default: return CMD_ERROR; @@ -713,7 +741,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) * RoadBuildCost(roadtype) * 2); break; case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break; default: NOT_REACHED(); } @@ -722,20 +750,17 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, Company *c = Company::GetIfValid(company); uint num_pieces = (tiles + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR; if (transport_type == TRANSPORT_RAIL) { - if (!IsTunnelTile(start_tile) && c != nullptr) c->infrastructure.rail[railtype] += num_pieces; + if (c != nullptr) c->infrastructure.rail[railtype] += num_pieces; MakeRailTunnel(start_tile, company, direction, railtype); MakeRailTunnel(end_tile, company, ReverseDiagDir(direction), railtype); AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, company); YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction)); } else { - if (c != nullptr) { - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, rts ^ (IsTunnelTile(start_tile) ? GetRoadTypes(start_tile) : ROADTYPES_NONE)) { - c->infrastructure.road[rt] += num_pieces * 2; // A full diagonal road has two road bits. - } - } - MakeRoadTunnel(start_tile, company, direction, rts); - MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), rts); + if (c != nullptr) c->infrastructure.road[roadtype] += num_pieces * 2; // A full diagonal road has two road bits. + RoadType road_rt = RoadTypeIsRoad(roadtype) ? roadtype : INVALID_ROADTYPE; + RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE; + MakeRoadTunnel(start_tile, company, direction, road_rt, tram_rt); + MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), road_rt, tram_rt); } DirtyCompanyInfrastructureWindows(company); } @@ -756,12 +781,13 @@ static inline CommandCost CheckAllowRemoveTunnelBridge(TileIndex tile) switch (GetTunnelBridgeTransportType(tile)) { case TRANSPORT_ROAD: { - RoadTypes rts = GetRoadTypes(tile); + RoadType road_rt = GetRoadTypeRoad(tile); + RoadType tram_rt = GetRoadTypeTram(tile); Owner road_owner = _current_company; Owner tram_owner = _current_company; - if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + if (road_rt != INVALID_ROADTYPE) road_owner = GetRoadOwner(tile, RTT_ROAD); + if (tram_rt != INVALID_ROADTYPE) tram_owner = GetRoadOwner(tile, RTT_TRAM); /* We can remove unowned road and if the town allows it */ if (road_owner == OWNER_TOWN && _current_company != OWNER_TOWN && !(_settings_game.construction.extra_dynamite || _cheats.magic_bulldozer.value)) { @@ -856,15 +882,9 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) if (v != nullptr) TryPathReserve(v); } else { - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - /* A full diagonal road tile has two road bits. */ - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; - DirtyCompanyInfrastructureWindows(c->index); - } - } + /* A full diagonal road tile has two road bits. */ + UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); + UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); DoClearSquare(tile); DoClearSquare(endtile); @@ -928,15 +948,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags) if (rail) { if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; } else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) { - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != nullptr) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; - DirtyCompanyInfrastructureWindows(c->index); - } - } + /* A full diagonal road tile has two road bits. */ + UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); + UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR)); } else { // Aqueduct if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; } @@ -1082,19 +1096,90 @@ static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis } /** - * Draws the trambits over an already drawn (lower end) of a bridge. - * @param x the x of the bridge - * @param y the y of the bridge - * @param z the z of the bridge - * @param offset number representing whether to level or sloped and the direction - * @param overlay do we want to still see the road? - * @param head are we drawing bridge head? + * Draws the road and trambits over an already drawn (lower end) of a bridge. + * @param head_tile bridge head tile with roadtype information + * @param x the x of the bridge + * @param y the y of the bridge + * @param z the z of the bridge + * @param offset sprite offset identifying flat to sloped bridge tiles + * @param head are we drawing bridge head? */ -static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bool head) +static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int offset, bool head) { - static const SpriteID tram_offsets[2][6] = { { 107, 108, 109, 110, 111, 112 }, { 4, 5, 15, 16, 17, 18 } }; - static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 }; - static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 }; + RoadType road_rt = GetRoadTypeRoad(head_tile); + RoadType tram_rt = GetRoadTypeTram(head_tile); + const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); + const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + + SpriteID seq_back[4] = { 0 }; + bool trans_back[4] = { false }; + SpriteID seq_front[4] = { 0 }; + bool trans_front[4] = { false }; + + static const SpriteID overlay_offsets[6] = { 0, 1, 11, 12, 13, 14 }; + static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 }; + static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 }; + + if (head || !IsInvisibilitySet(TO_BRIDGES)) { + /* Road underlay takes precendence over tram */ + trans_back[0] = !head && IsTransparencySet(TO_BRIDGES); + if (road_rti != nullptr) { + if (road_rti->UsesOverlay()) { + seq_back[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset; + } + } else if (tram_rti != nullptr) { + if (tram_rti->UsesOverlay()) { + seq_back[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset; + } else { + seq_back[0] = SPR_TRAMWAY_BRIDGE + offset; + } + } + + /* Draw road overlay */ + trans_back[1] = !head && IsTransparencySet(TO_BRIDGES); + if (road_rti != nullptr) { + if (road_rti->UsesOverlay()) { + seq_back[1] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE); + if (seq_back[1] != 0) seq_back[1] += overlay_offsets[offset]; + } + } + + /* Draw tram overlay */ + trans_back[2] = !head && IsTransparencySet(TO_BRIDGES); + if (tram_rti != nullptr) { + if (tram_rti->UsesOverlay()) { + seq_back[2] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE); + if (seq_back[2] != 0) seq_back[2] += overlay_offsets[offset]; + } else if (road_rti != nullptr) { + seq_back[2] = SPR_TRAMWAY_OVERLAY + overlay_offsets[offset]; + } + } + + /* Road catenary takes precendence over tram */ + trans_back[3] = IsTransparencySet(TO_CATENARY); + trans_front[0] = IsTransparencySet(TO_CATENARY); + if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) { + seq_back[3] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE); + seq_front[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE); + if (seq_back[3] == 0 || seq_front[0] == 0) { + seq_back[3] = SPR_TRAMWAY_BASE + back_offsets[offset]; + seq_front[0] = SPR_TRAMWAY_BASE + front_offsets[offset]; + } else { + seq_back[3] += 23 + offset; + seq_front[0] += 23 + offset; + } + } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) { + seq_back[3] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE); + seq_front[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE); + if (seq_back[3] == 0 || seq_front[0] == 0) { + seq_back[3] = SPR_TRAMWAY_BASE + back_offsets[offset]; + seq_front[0] = SPR_TRAMWAY_BASE + front_offsets[offset]; + } else { + seq_back[3] += 23 + offset; + seq_front[0] += 23 + offset; + } + } + } static const uint size_x[6] = { 1, 16, 16, 1, 16, 1 }; static const uint size_y[6] = { 16, 1, 1, 16, 1, 16 }; @@ -1103,28 +1188,25 @@ static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bo /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called * The bounding boxes here are the same as for bridge front/roof */ - if (head || !IsInvisibilitySet(TO_BRIDGES)) { - AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + tram_offsets[overlay][offset], PAL_NONE, - x, y, size_x[offset], size_y[offset], 0x28, z, - !head && IsTransparencySet(TO_BRIDGES)); - } - - /* Do not draw catenary if it is set invisible */ - if (!IsInvisibilitySet(TO_CATENARY)) { - AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + back_offsets[offset], PAL_NONE, - x, y, size_x[offset], size_y[offset], 0x28, z, - IsTransparencySet(TO_CATENARY)); + for (uint i = 0; i < lengthof(seq_back); ++i) { + if (seq_back[i] != 0) { + AddSortableSpriteToDraw(seq_back[i], PAL_NONE, + x, y, size_x[offset], size_y[offset], 0x28, z, + trans_back[i]); + } } /* Start a new SpriteCombine for the front part */ EndSpriteCombine(); StartSpriteCombine(); - /* For sloped sprites the bounding box needs to be higher, as the pylons stop on a higher point */ - if (!IsInvisibilitySet(TO_CATENARY)) { - AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + front_offsets[offset], PAL_NONE, - x, y, size_x[offset] + front_bb_offset_x[offset], size_y[offset] + front_bb_offset_y[offset], 0x28, z, - IsTransparencySet(TO_CATENARY), front_bb_offset_x[offset], front_bb_offset_y[offset]); + for (uint i = 0; i < lengthof(seq_front); ++i) { + if (seq_front[i] != 0) { + AddSortableSpriteToDraw(seq_front[i], PAL_NONE, + x, y, size_x[offset] + front_bb_offset_x[offset], size_y[offset] + front_bb_offset_y[offset], 0x28, z, + trans_front[i], + front_bb_offset_x[offset], front_bb_offset_y[offset]); + } } } @@ -1188,20 +1270,37 @@ static void DrawTile_TunnelBridge(TileInfo *ti) DrawGroundSprite(image, PAL_NONE); if (transport_type == TRANSPORT_ROAD) { - RoadTypes rts = GetRoadTypes(ti->tile); - - if (HasBit(rts, ROADTYPE_TRAM)) { - static const SpriteID tunnel_sprites[2][4] = { { 28, 78, 79, 27 }, { 5, 76, 77, 4 } }; - - DrawGroundSprite(SPR_TRAMWAY_BASE + tunnel_sprites[rts - ROADTYPES_TRAM][tunnelbridge_direction], PAL_NONE); - - /* Do not draw wires if they are invisible */ - if (!IsInvisibilitySet(TO_CATENARY)) { - catenary = true; - StartSpriteCombine(); - AddSortableSpriteToDraw(SPR_TRAMWAY_TUNNEL_WIRES + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR); + RoadType road_rt = GetRoadTypeRoad(ti->tile); + RoadType tram_rt = GetRoadTypeTram(ti->tile); + const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt); + const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt); + uint sprite_offset = DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? 1 : 0; + + DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset); + + /* Road catenary takes precendence over tram */ + SpriteID catenary_sprite_base = 0; + if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) { + catenary_sprite_base = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_CATENARY_FRONT); + if (catenary_sprite_base == 0) { + catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES; + } else { + catenary_sprite_base += 19; + } + } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) { + catenary_sprite_base = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_CATENARY_FRONT); + if (catenary_sprite_base == 0) { + catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES; + } else { + catenary_sprite_base += 19; } } + + if (catenary_sprite_base != 0) { + catenary = true; + StartSpriteCombine(); + AddSortableSpriteToDraw(catenary_sprite_base + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR); + } } else { const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); if (rti->UsesOverlay()) { @@ -1294,20 +1393,18 @@ static void DrawTile_TunnelBridge(TileInfo *ti) AddSortableSpriteToDraw(psid->sprite, psid->pal, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z); if (transport_type == TRANSPORT_ROAD) { - RoadTypes rts = GetRoadTypes(ti->tile); - - if (HasBit(rts, ROADTYPE_TRAM)) { - uint offset = tunnelbridge_direction; - int z = ti->z; - if (ti->tileh != SLOPE_FLAT) { - offset = (offset + 1) & 1; - z += TILE_HEIGHT; - } else { - offset += 2; - } - /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */ - DrawBridgeTramBits(ti->x, ti->y, z, offset, HasBit(rts, ROADTYPE_ROAD), true); + uint offset = tunnelbridge_direction; + int z = ti->z; + if (ti->tileh != SLOPE_FLAT) { + offset = (offset + 1) & 1; + z += TILE_HEIGHT; + } else { + offset += 2; } + + /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */ + DrawBridgeRoadBits(ti->tile, ti->x, ti->y, z, offset, true); + EndSpriteCombine(); } else if (transport_type == TRANSPORT_RAIL) { const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); @@ -1465,15 +1562,8 @@ void DrawBridgeMiddle(const TileInfo *ti) psid++; if (transport_type == TRANSPORT_ROAD) { - RoadTypes rts = GetRoadTypes(rampsouth); - - if (HasBit(rts, ROADTYPE_TRAM)) { - /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */ - DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, HasBit(rts, ROADTYPE_ROAD), false); - } else { - EndSpriteCombine(); - StartSpriteCombine(); - } + /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */ + DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false); } else if (transport_type == TRANSPORT_RAIL) { const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(rampsouth)); if (rti->UsesOverlay() && !IsInvisibilitySet(TO_BRIDGES)) { @@ -1593,9 +1683,20 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td) Owner road_owner = INVALID_OWNER; Owner tram_owner = INVALID_OWNER; - RoadTypes rts = GetRoadTypes(tile); - if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); - if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); + RoadType road_rt = GetRoadTypeRoad(tile); + RoadType tram_rt = GetRoadTypeTram(tile); + if (road_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt); + td->roadtype = rti->strings.name; + td->road_speed = rti->max_speed / 2; + road_owner = GetRoadOwner(tile, RTT_ROAD); + } + if (tram_rt != INVALID_ROADTYPE) { + const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt); + td->tramtype = rti->strings.name; + td->tram_speed = rti->max_speed / 2; + tram_owner = GetRoadOwner(tile, RTT_TRAM); + } /* Is there a mix of owners? */ if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) || @@ -1624,7 +1725,9 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td) } } } else if (tt == TRANSPORT_ROAD && !IsTunnel(tile)) { - td->road_speed = GetBridgeSpec(GetBridgeType(tile))->speed; + uint16 spd = GetBridgeSpec(GetBridgeType(tile))->speed; + if (road_rt != INVALID_ROADTYPE && (td->road_speed == 0 || spd < td->road_speed)) td->road_speed = spd; + if (tram_rt != INVALID_ROADTYPE && (td->tram_speed == 0 || spd < td->tram_speed)) td->tram_speed = spd; } } @@ -1660,7 +1763,7 @@ static void TileLoop_TunnelBridge(TileIndex tile) static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) { TransportType transport_type = GetTunnelBridgeTransportType(tile); - if (transport_type != mode || (transport_type == TRANSPORT_ROAD && (GetRoadTypes(tile) & sub_mode) == 0)) return 0; + if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)sub_mode))) return 0; DiagDirection dir = GetTunnelBridgeDirection(tile); if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0; @@ -1674,17 +1777,18 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner * don't want to update the infrastructure counts twice. */ uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0; - for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { + FOR_ALL_ROADTRAMTYPES(rtt) { /* Update all roadtypes, no matter if they are present */ - if (GetRoadOwner(tile, rt) == old_owner) { - if (HasBit(GetRoadTypes(tile), rt)) { + if (GetRoadOwner(tile, rtt) == old_owner) { + RoadType rt = GetRoadType(tile, rtt); + if (rt != INVALID_ROADTYPE) { /* Update company infrastructure counts. A full diagonal road tile has two road bits. * No need to dirty windows here, we'll redraw the whole screen anyway. */ Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2; if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2; } - SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); + SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 031b6f95e3..6e26c7bfe8 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1757,7 +1757,7 @@ UnitID GetFreeUnitNumber(VehicleType type) * @return true if there is any reason why you may build * the infrastructure for the given vehicle type */ -bool CanBuildVehicleInfrastructure(VehicleType type) +bool CanBuildVehicleInfrastructure(VehicleType type, byte subtype) { assert(IsCompanyBuildableVehicleType(type)); @@ -1770,7 +1770,10 @@ bool CanBuildVehicleInfrastructure(VehicleType type) if (!HasAnyRailtypesAvail(_local_company)) return false; max = _settings_game.vehicle.max_trains; break; - case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break; + case VEH_ROAD: + if (!HasAnyRoadTypesAvail(_local_company, (RoadTramType)subtype)) return false; + max = _settings_game.vehicle.max_roadveh; + break; case VEH_SHIP: max = _settings_game.vehicle.max_ships; break; case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break; default: NOT_REACHED(); @@ -1781,6 +1784,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type) /* Can we actually build the vehicle type? */ const Engine *e; FOR_ALL_ENGINES_OF_TYPE(e, type) { + if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue; if (HasBit(e->company_avail, _local_company)) return true; } return false; @@ -1789,6 +1793,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type) /* We should be able to build infrastructure when we have the actual vehicle type */ const Vehicle *v; FOR_ALL_VEHICLES(v) { + if (type == VEH_ROAD && GetRoadTramType(RoadVehicle::From(v)->roadtype) != (RoadTramType)subtype) continue; if (v->owner == _local_company && v->type == type) return true; } diff --git a/src/vehicle_func.h b/src/vehicle_func.h index e61f6e2336..bdd961bef3 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -71,7 +71,7 @@ UnitID GetFreeUnitNumber(VehicleType type); void VehicleEnterDepot(Vehicle *v); -bool CanBuildVehicleInfrastructure(VehicleType type); +bool CanBuildVehicleInfrastructure(VehicleType type, byte subtype = 0); /** Position information of a vehicle after it moved */ struct GetNewVehiclePosResult { diff --git a/src/viewport_type.h b/src/viewport_type.h index 74cd88d95d..f5544679a5 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -121,6 +121,7 @@ enum ViewportDragDropSelectionProcess { DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks) DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses) DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks) + DDSP_CONVERT_ROAD, ///< Road conversion }; diff --git a/src/widgets/autoreplace_widget.h b/src/widgets/autoreplace_widget.h index 4b761ca45d..8432b1a7f2 100644 --- a/src/widgets/autoreplace_widget.h +++ b/src/widgets/autoreplace_widget.h @@ -34,9 +34,11 @@ enum ReplaceVehicleWidgets { WID_RV_INFO_TAB, ///< Info tab. WID_RV_STOP_REPLACE, ///< Stop Replacing button. + /* Train/road only widgets */ + WID_RV_RAIL_ROAD_TYPE_DROPDOWN, ///< Dropdown menu about the rail/roadtype. + /* Train only widgets. */ WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, ///< Dropdown to select engines and/or wagons. - WID_RV_TRAIN_RAILTYPE_DROPDOWN, ///< Dropdown menu about the railtype. WID_RV_TRAIN_WAGONREMOVE_TOGGLE, ///< Button to toggle removing wagons. }; diff --git a/src/widgets/company_widget.h b/src/widgets/company_widget.h index ceb81ae195..8b0d8ee0b6 100644 --- a/src/widgets/company_widget.h +++ b/src/widgets/company_widget.h @@ -177,6 +177,8 @@ enum CompanyInfrastructureWidgets { WID_CI_RAIL_COUNT, ///< Count of rail. WID_CI_ROAD_DESC, ///< Description of road. WID_CI_ROAD_COUNT, ///< Count of road. + WID_CI_TRAM_DESC, ///< Description of tram. + WID_CI_TRAM_COUNT, ///< Count of tram. WID_CI_WATER_DESC, ///< Description of water. WID_CI_WATER_COUNT, ///< Count of water. WID_CI_STATION_DESC, ///< Description of station. diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h index f022489e53..60068317ab 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -15,6 +15,7 @@ /** Widgets of the #BuildRoadToolbarWindow class. */ enum RoadToolbarWidgets { /* Name starts with RO instead of R, because of collision with RailToolbarWidgets */ + WID_ROT_CAPTION, ///< Caption of the window WID_ROT_ROAD_X, ///< Build road in x-direction. WID_ROT_ROAD_Y, ///< Build road in y-direction. WID_ROT_AUTOROAD, ///< Autorail. @@ -26,6 +27,7 @@ enum RoadToolbarWidgets { WID_ROT_BUILD_BRIDGE, ///< Build bridge. WID_ROT_BUILD_TUNNEL, ///< Build tunnel. WID_ROT_REMOVE, ///< Remove road. + WID_ROT_CONVERT_ROAD, ///< Convert road. }; /** Widgets of the #BuildRoadDepotWindow class. */ diff --git a/src/widgets/toolbar_widget.h b/src/widgets/toolbar_widget.h index 5f0c1b5a4f..5b9d1e1568 100644 --- a/src/widgets/toolbar_widget.h +++ b/src/widgets/toolbar_widget.h @@ -39,6 +39,7 @@ enum ToolbarNormalWidgets { WID_TN_BUILDING_TOOLS_START, ///< Helper for the offset of the building tools WID_TN_RAILS = WID_TN_BUILDING_TOOLS_START, ///< Rail building menu. WID_TN_ROADS, ///< Road building menu. + WID_TN_TRAMS, ///< Tram building menu. WID_TN_WATER, ///< Water building toolbar. WID_TN_AIR, ///< Airport building toolbar. WID_TN_LANDSCAPE, ///< Landscaping toolbar. @@ -66,6 +67,7 @@ enum ToolbarEditorWidgets { WID_TE_TOWN_GENERATE, ///< Town building window. WID_TE_INDUSTRY, ///< Industry building window. WID_TE_ROADS, ///< Road building menu. + WID_TE_TRAMS, ///< Tram building menu. WID_TE_WATER, ///< Water building toolbar. WID_TE_TREES, ///< Tree building toolbar. WID_TE_SIGNS, ///< Sign building.
        OOOX XXXX XXXX XXXX XXXX XXXX XXXX XXXXOOOO OOOOOOXX XXXX XXXX XXXX OOXX XOOOXXXO XXXXOOOO OOOO OOOO OOOOOOXO XXXXOOOO XXXX XXOO OOOO
        level crossingOOOO OOOO XXXX OOOX OOXX XOOOXXXX XXXXOOOO OOOO OOXX XXXXOOXX XXXXOOOO XXXX XXXX XXXX
        road depot-inherit- -inherit- XXXX OOOOOOOO OOOO-inherit- XXOO OOXX OOOO OOOOXXXO XXXXOOOO OOOO OOOO OOOOOOXO XXXX-inherit-
        3-inherit- -inherit- XXXX OOOOOOOO OOOOOOXX XXXX ~~~~ ~XXX OOXX XOOOXXOX XXXXOOOO OOOO OOOO OOOOOOOX XXXXOOOO XXXX XXOO OOOO
        dockOOOX XXXX OOOO OOOO OOOO OOOO XXXX OOOOOOOO OOOOOOXX XXXX XOOX XXXX OOOO OOOOXXXX XXXXOOOO OOOO OOXX XXXXOOXX XXXXOOOO XXXX XXXX XXXX
        bridge ramp-inherit- OOOO OOOO OOOO OOOO -inherit-OOOO OOOO-inherit- -inherit- OOXX XXOO -inherit-