diff --git a/.gitignore b/.gitignore index 5aa8cb784..0c5a57ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin/ai/* !bin/data bin/baseset/* !bin/baseset/openttd.grf +!bin/baseset/clipboard.grf !bin/baseset/opntitle.dat !bin/baseset/orig_extra.grf !bin/baseset/orig_*.obg diff --git a/config.lib b/config.lib index 3d10aaa3f..316678157 100644 --- a/config.lib +++ b/config.lib @@ -1739,7 +1739,8 @@ make_cflags_and_ldflags() { CFLAGS="$CFLAGS `$freetype_config --cflags | tr '\n\r' ' '`" if [ "$enable_static" != "0" ]; then - LIBS="$LIBS `$freetype_config --libs --static | tr '\n\r' ' '`" + # Is it possible to do static with freetype, if so: how? + LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" else LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" fi diff --git a/docs/tile_index_transformations.svg b/docs/tile_index_transformations.svg new file mode 100644 index 000000000..47fffb238 --- /dev/null +++ b/docs/tile_index_transformations.svg @@ -0,0 +1,1849 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ROTATE 90° CW (example) + + + + + + 1 + 1 + 2 + + 2 + northern tile of the area (TileAreaT::tile)transformed northern tile of the area (transformed_north)tile of the area (tile)transformed tile of the area (transformed_tile)northern tile of the transformed area (dst_area_north) + + TileAreaT::TransformTile:TileAreaT::ReverseTransformTile:TileAreaT::TransformedNorth:TileAreaT::ReverseTransformedNorth: + + + 2 + + + + 2 + + + + 1 + + 2 + , + 2 + + + + 2 + + 2 + , + 1 + Explanation on TileAreaT transformations + + + + N + S + E + W + + + x + y + + ( + ) + ( + ) + 1 + 1 + 2 + 2 + northern tile of the source area + tile of the source area + source tile of the northern tile of the transformed area + source area + transformation + + 1 + + 1 + + 1 + + transformed northern tile of the source area + transformed tile of the source area + northern tile of the transformed area + transformed area + inverted transformation + + 2 + + 2 + + 2 + + .TransformTile( + , + , + ) → + + 1 + + 2 + + + 2 + .TransformTile( + , + , + ) → + + 1 + + 2 + + + 2 + + diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 9c3b10786..48db1068f 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -313,6 +313,7 @@ + @@ -424,6 +425,9 @@ + + + @@ -436,6 +440,7 @@ + @@ -568,6 +573,8 @@ + + @@ -633,6 +640,7 @@ + @@ -707,6 +715,7 @@ + @@ -764,6 +773,7 @@ + @@ -817,6 +827,7 @@ + @@ -824,6 +835,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index 06800ffda..c561077ac 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -132,6 +132,9 @@ Source Files + + Source Files + Source Files @@ -465,6 +468,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + Header Files @@ -501,6 +513,9 @@ Header Files + + Header Files + Header Files @@ -897,6 +912,12 @@ Header Files + + Header Files + + + Header Files + Header Files @@ -1092,6 +1113,9 @@ Header Files + + Header Files + Header Files @@ -1314,6 +1338,9 @@ GUI Source Code + + GUI Source Code + GUI Source Code @@ -1485,6 +1512,9 @@ Widgets + + Widgets + Widgets @@ -1644,6 +1674,9 @@ Command handlers + + Command handlers + Command handlers @@ -1665,6 +1698,9 @@ Command handlers + + Command handlers + Command handlers diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 70dcab226..c7f6aa61e 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -475,6 +475,10 @@ > + + @@ -923,6 +927,18 @@ > + + + + + + @@ -971,6 +987,10 @@ > + + @@ -1499,6 +1519,14 @@ > + + + + @@ -1759,6 +1787,10 @@ > + + @@ -2063,6 +2095,10 @@ > + + @@ -2295,6 +2331,10 @@ > + + @@ -2511,6 +2551,10 @@ > + + @@ -2539,6 +2583,10 @@ > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index dd722d085..83acba849 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -472,6 +472,10 @@ > + + @@ -920,6 +924,18 @@ > + + + + + + @@ -968,6 +984,10 @@ > + + @@ -1496,6 +1516,14 @@ > + + + + @@ -1756,6 +1784,10 @@ > + + @@ -2060,6 +2092,10 @@ > + + @@ -2292,6 +2328,10 @@ > + + @@ -2508,6 +2548,10 @@ > + + @@ -2536,6 +2580,10 @@ > + + diff --git a/source.list b/source.list index df35cdd26..7a1c40274 100644 --- a/source.list +++ b/source.list @@ -9,6 +9,7 @@ cargomonitor.cpp cargopacket.cpp cargotype.cpp cheat.cpp +clipboard.cpp command.cpp console.cpp console_cmds.cpp @@ -151,6 +152,9 @@ cargotype.h cheat_func.h cheat_type.h clear_func.h +clipboard_func.h +clipboard_gui.h +clipboard_type.h cmd_helper.h command_func.h command_type.h @@ -163,6 +167,7 @@ console_func.h console_gui.h console_internal.h console_type.h +copypaste_cmd.h cpu.h crashlog.h currency.h @@ -295,6 +300,8 @@ order_backup.h order_base.h order_func.h order_type.h +overlay.h +overlay_cmd.h pbs.h progress.h querystring_gui.h @@ -360,6 +367,7 @@ textfile_type.h tgp.h tile_cmd.h tile_type.h +tilearea_func.h tilearea_type.h tilehighlight_func.h tilehighlight_type.h @@ -453,6 +461,7 @@ bootstrap_gui.cpp bridge_gui.cpp build_vehicle_gui.cpp cheat_gui.cpp +clipboard_gui.cpp company_gui.cpp console_gui.cpp date_gui.cpp @@ -512,6 +521,7 @@ widgets/bootstrap_widget.h widgets/bridge_widget.h widgets/build_vehicle_widget.h widgets/cheat_widget.h +widgets/clipboard_widget.h widgets/company_widget.h widgets/console_widget.h widgets/date_widget.h @@ -567,6 +577,7 @@ widgets/waypoint_widget.h aircraft_cmd.cpp autoreplace_cmd.cpp clear_cmd.cpp +copypaste_cmd.cpp company_cmd.cpp depot_cmd.cpp group_cmd.cpp @@ -574,6 +585,7 @@ industry_cmd.cpp misc_cmd.cpp object_cmd.cpp order_cmd.cpp +overlay_cmd.cpp rail_cmd.cpp road_cmd.cpp roadveh_cmd.cpp diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 6437f236c..1ba911594 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -57,7 +57,7 @@ void CcBuildAirport(const CommandCost &result, TileIndex tile, uint32 p1, uint32 static void PlaceAirport(TileIndex tile) { if (_selected_airport_index == -1) return; - uint32 p2 = _ctrl_pressed; + uint32 p2 = _settings_game.station.adjacent_stations && _ctrl_pressed; // adjacent? SB(p2, 16, 16, INVALID_STATION); // no station to join uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); diff --git a/src/bridge.h b/src/bridge.h index badf045e3..2c7fc080f 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -73,6 +73,7 @@ static inline const BridgeSpec *GetBridgeSpec(BridgeType i) void DrawBridgeMiddle(const TileInfo *ti); CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlag flags = DC_NONE); +BridgeType FastestAvailableBridgeType(uint bridge_len); int CalcBridgeLenCostFactor(int x); void ResetBridges(); diff --git a/src/bridge_map.cpp b/src/bridge_map.cpp index d1e0d6024..c76374512 100644 --- a/src/bridge_map.cpp +++ b/src/bridge_map.cpp @@ -21,9 +21,10 @@ * @param tile the bridge tile to find the bridge ramp for * @param dir the direction to search in */ -static TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) +template +static typename TileIndexT::T GetBridgeEnd(typename TileIndexT::T tile, DiagDirection dir) { - TileIndexDiff delta = TileOffsByDiagDir(dir); + TileIndexDiff delta = TileOffsByDiagDir(dir, MapOf(tile)); dir = ReverseDiagDir(dir); do { @@ -32,6 +33,10 @@ static TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) return tile; } +/** @copydoc GetBridgeEnd(TileIndexT::T,DiagDirection) */ +static inline TileIndex GetBridgeEnd(TileIndex t, DiagDirection dir) { return GetBridgeEnd(t, dir); } +/** @copydoc GetBridgeEnd(TileIndexT::T,DiagDirection) */ +static inline GenericTileIndex GetBridgeEnd(GenericTileIndex t, DiagDirection dir) { return GetBridgeEnd(t, dir); } /** @@ -58,18 +63,23 @@ TileIndex GetSouthernBridgeEnd(TileIndex t) * Starting at one bridge end finds the other bridge end * @param t the bridge ramp tile to find the other bridge ramp for */ -TileIndex GetOtherBridgeEnd(TileIndex tile) +template +typename TileIndexT::T GetOtherBridgeEnd(typename TileIndexT::T tile) { assert(IsBridgeTile(tile)); return GetBridgeEnd(tile, GetTunnelBridgeDirection(tile)); } +/* instantiate */ +template TileIndex GetOtherBridgeEnd(TileIndex tile); +template GenericTileIndex GetOtherBridgeEnd(GenericTileIndex tile); /** * Get the height ('z') of a bridge. * @param tile the bridge ramp tile to get the bridge height from * @return the height of the bridge. */ -int GetBridgeHeight(TileIndex t) +template +int GetBridgeHeight(typename TileIndexT::T t) { int h; Slope tileh = GetTileSlope(t, &h); @@ -78,3 +88,6 @@ int GetBridgeHeight(TileIndex t) /* one height level extra for the ramp */ return h + 1 + ApplyFoundationToSlope(f, &tileh); } +/* instantiate */ +template int GetBridgeHeight(TileIndex t); +template int GetBridgeHeight(GenericTileIndex t); diff --git a/src/bridge_map.h b/src/bridge_map.h index 74c6974db..c2e4d9b23 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -21,31 +21,46 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return true if the structure is a bridge one */ -static inline bool IsBridge(TileIndex t) +template +static inline bool IsBridge(typename TileIndexT::T t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return HasBit(_m[t].m5, 7); + return HasBit(GetTile(t)->m5, 7); } +/** @copydoc IsBridge(TileIndexT::T) */ +static inline bool IsBridge(TileIndex t) { return IsBridge(t); } +/** @copydoc IsBridge(TileIndexT::T) */ +static inline bool IsBridge(GenericTileIndex t) { return IsBridge(t); } /** * checks if there is a bridge on this tile * @param t The tile to analyze * @return true if a bridge is present */ -static inline bool IsBridgeTile(TileIndex t) +template +static inline bool IsBridgeTile(typename TileIndexT::T t) { return IsTileType(t, MP_TUNNELBRIDGE) && IsBridge(t); } +/** @copydoc IsBridgeTile(TileIndexT::T) */ +static inline bool IsBridgeTile(TileIndex t) { return IsBridgeTile(t); } +/** @copydoc IsBridgeTile(TileIndexT::T) */ +static inline bool IsBridgeTile(GenericTileIndex t) { return IsBridgeTile(t); } /** * checks if a bridge is set above the ground of this tile * @param t The tile to analyze * @return true if a bridge is detected above */ -static inline bool IsBridgeAbove(TileIndex t) +template +static inline bool IsBridgeAbove(typename TileIndexT::T t) { - return GB(_m[t].type, 2, 2) != 0; + return GB(GetTile(t)->type, 2, 2) != 0; } +/** @copydoc IsBridgeAbove(TileIndexT::T) */ +static inline bool IsBridgeAbove(TileIndex t) { return IsBridgeAbove(t); } +/** @copydoc IsBridgeAbove(TileIndexT::T) */ +static inline bool IsBridgeAbove(GenericTileIndex t) { return IsBridgeAbove(t); } /** * Determines the type of bridge on a tile @@ -53,11 +68,16 @@ static inline bool IsBridgeAbove(TileIndex t) * @pre IsBridgeTile(t) * @return The bridge type */ -static inline BridgeType GetBridgeType(TileIndex t) +template +static inline BridgeType GetBridgeType(typename TileIndexT::T t) { assert(IsBridgeTile(t)); - return GB(_me[t].m6, 2, 4); + return GB(GetTileEx(t)->m6, 2, 4); } +/** @copydoc GetBridgeType(TileIndexT::T) */ +static inline BridgeType GetBridgeType(TileIndex t) { return GetBridgeType(t); } +/** @copydoc GetBridgeType(TileIndexT::T) */ +static inline BridgeType GetBridgeType(GenericTileIndex t) { return GetBridgeType(t); } /** * Get the axis of the bridge that goes over the tile. Not the axis or the ramp. @@ -65,17 +85,34 @@ static inline BridgeType GetBridgeType(TileIndex t) * @pre IsBridgeAbove(t) * @return the above mentioned axis */ -static inline Axis GetBridgeAxis(TileIndex t) +template +static inline Axis GetBridgeAxis(typename TileIndexT::T t) { assert(IsBridgeAbove(t)); - return (Axis)(GB(_m[t].type, 2, 2) - 1); + return (Axis)(GB(GetTile(t)->type, 2, 2) - 1); } +/** @copydoc GetBridgeAxis(TileIndexT::T) */ +static inline Axis GetBridgeAxis(TileIndex t) { return GetBridgeAxis(t); } +/** @copydoc GetBridgeAxis(TileIndexT::T) */ +static inline Axis GetBridgeAxis(GenericTileIndex t) { return GetBridgeAxis(t); } TileIndex GetNorthernBridgeEnd(TileIndex t); TileIndex GetSouthernBridgeEnd(TileIndex t); -TileIndex GetOtherBridgeEnd(TileIndex t); -int GetBridgeHeight(TileIndex tile); +template +typename TileIndexT::T GetOtherBridgeEnd(typename TileIndexT::T t); +/** @copydoc GetOtherBridgeEnd(TileIndexT::T) */ +static inline TileIndex GetOtherBridgeEnd(TileIndex t) { return GetOtherBridgeEnd(t); } +/** @copydoc GetOtherBridgeEnd(TileIndexT::T) */ +static inline GenericTileIndex GetOtherBridgeEnd(GenericTileIndex t) { return GetOtherBridgeEnd(t); } + +template +int GetBridgeHeight(typename TileIndexT::T tile); +/** @copydoc GetBridgeHeight(TileIndexT::T) */ +static inline int GetBridgeHeight(TileIndex t) { return GetBridgeHeight(t); } +/** @copydoc GetBridgeHeight(TileIndexT::T) */ +static inline int GetBridgeHeight(GenericTileIndex t) { return GetBridgeHeight(t); } + /** * Get the height ('z') of a bridge in pixels. * @param tile the bridge ramp tile to get the bridge height from @@ -93,7 +130,7 @@ static inline int GetBridgePixelHeight(TileIndex tile) */ static inline void ClearSingleBridgeMiddle(TileIndex t, Axis a) { - ClrBit(_m[t].type, 2 + a); + ClrBit(GetTile(t)->type, 2 + a); } /** @@ -111,10 +148,15 @@ static inline void ClearBridgeMiddle(TileIndex t) * @param t the tile to add the bridge to * @param a the axis of the bridge to add */ -static inline void SetBridgeMiddle(TileIndex t, Axis a) +template +static inline void SetBridgeMiddle(typename TileIndexT::T t, Axis a) { - SetBit(_m[t].type, 2 + a); + SetBit(GetTile(t)->type, 2 + a); } +/** @copydoc SetBridgeMiddle(TileIndexT::T,Axis) */ +static inline void SetBridgeMiddle(TileIndex t, Axis a) { return SetBridgeMiddle(t, a); } +/** @copydoc SetBridgeMiddle(TileIndexT::T,Axis) */ +static inline void SetBridgeMiddle(GenericTileIndex t, Axis a) { return SetBridgeMiddle(t, a); } /** * Generic part to make a bridge ramp for both roads and rails. @@ -126,17 +168,22 @@ static inline void SetBridgeMiddle(TileIndex t, Axis a) * @param rt the road or rail type * @note this function should not be called directly. */ -static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) +template +static inline void MakeBridgeRamp(typename TileIndexT::T t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = rt; - _m[t].m4 = 0; - _m[t].m5 = 1 << 7 | tt << 2 | d; - SB(_me[t].m6, 2, 4, bridgetype); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = rt; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = 1 << 7 | tt << 2 | d; + SB(GetTileEx(t)->m6, 2, 4, bridgetype); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeBridgeRamp(TileIndexT::T,Owner,BridgeType,DiagDirection,TransportType,uint)*/ +static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) { return MakeBridgeRamp(t, o, bridgetype, d, tt, rt); } +/** @copydoc MakeBridgeRamp(TileIndexT::T,Owner,BridgeType,DiagDirection,TransportType,uint)*/ +static inline void MakeBridgeRamp(GenericTileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, TransportType tt, uint rt) { return MakeBridgeRamp(t, o, bridgetype, d, tt, rt); } /** * Make a bridge ramp for roads. @@ -148,13 +195,18 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D * @param d the direction this ramp must be facing * @param r the road type of the bridge */ -static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) +template +static inline void MakeRoadBridgeRamp(typename TileIndexT::T t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) { MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD, 0); SetRoadOwner(t, ROADTYPE_ROAD, owner_road); if (owner_tram != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, owner_tram); SetRoadTypes(t, r); } +/** @copydoc MakeRoadBridgeRamp(TileIndexT::T,Owner,Owner,Owner,BridgeType,DiagDirection,RoadTypes) */ +static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) { return MakeRoadBridgeRamp(t, o, owner_road, owner_tram, bridgetype, d, r); } +/** @copydoc MakeRoadBridgeRamp(TileIndexT::T,Owner,Owner,Owner,BridgeType,DiagDirection,RoadTypes) */ +static inline void MakeRoadBridgeRamp(GenericTileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes r) { return MakeRoadBridgeRamp(t, o, owner_road, owner_tram, bridgetype, d, r); } /** * Make a bridge ramp for rails. @@ -164,10 +216,15 @@ static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Ow * @param d the direction this ramp must be facing * @param r the rail type of the bridge */ -static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) +template +static inline void MakeRailBridgeRamp(typename TileIndexT::T t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) { MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL, r); } +/** @copydoc MakeRailBridgeRamp(TileIndexT::T,Owner,BridgeType,DiagDirection,RailType) */ +static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) { return MakeRailBridgeRamp(t, o, bridgetype, d, r); } +/** @copydoc MakeRailBridgeRamp(TileIndexT::T,Owner,BridgeType,DiagDirection,RailType) */ +static inline void MakeRailBridgeRamp(GenericTileIndex t, Owner o, BridgeType bridgetype, DiagDirection d, RailType r) { return MakeRailBridgeRamp(t, o, bridgetype, d, r); } /** * Make a bridge ramp for aqueducts. @@ -175,9 +232,14 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp * @param o the new owner of the bridge ramp * @param d the direction this ramp must be facing */ -static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) +template +static inline void MakeAqueductBridgeRamp(typename TileIndexT::T t, Owner o, DiagDirection d) { MakeBridgeRamp(t, o, 0, d, TRANSPORT_WATER, 0); } +/** @copydoc MakeAqueductBridgeRamp(TileIndexT::T,Owner,DiagDirection) */ +static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) { return MakeAqueductBridgeRamp(t, o, d); } +/** @copydoc MakeAqueductBridgeRamp(TileIndexT::T,Owner,DiagDirection) */ +static inline void MakeAqueductBridgeRamp(GenericTileIndex t, Owner o, DiagDirection d) { return MakeAqueductBridgeRamp(t, o, d); } #endif /* BRIDGE_MAP_H */ diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index f9eb88df5..a32806ca2 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -126,6 +126,7 @@ static void DrawTile_Clear(TileInfo *ti) break; } + DrawOverlay(ti, MP_CLEAR); DrawBridgeMiddle(ti); } @@ -399,4 +400,5 @@ extern const TileTypeProcs _tile_type_clear_procs = { NULL, ///< vehicle_enter_tile_proc GetFoundation_Clear, ///< get_foundation_proc TerraformTile_Clear, ///< terraform_tile_proc + NULL, ///< copypaste_tile_proc }; diff --git a/src/clear_map.h b/src/clear_map.h index 76b1e82d0..b8ddb34be 100644 --- a/src/clear_map.h +++ b/src/clear_map.h @@ -37,7 +37,7 @@ enum ClearGround { static inline bool IsSnowTile(TileIndex t) { assert(IsTileType(t, MP_CLEAR)); - return HasBit(_m[t].m3, 4); + return HasBit(GetTile(t)->m3, 4); } /** @@ -49,7 +49,7 @@ static inline bool IsSnowTile(TileIndex t) static inline ClearGround GetRawClearGround(TileIndex t) { assert(IsTileType(t, MP_CLEAR)); - return (ClearGround)GB(_m[t].m5, 2, 3); + return (ClearGround)GB(GetTile(t)->m5, 2, 3); } /** @@ -85,7 +85,7 @@ static inline bool IsClearGround(TileIndex t, ClearGround ct) static inline uint GetClearDensity(TileIndex t) { assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 0, 2); + return GB(GetTile(t)->m5, 0, 2); } /** @@ -97,7 +97,7 @@ static inline uint GetClearDensity(TileIndex t) static inline void AddClearDensity(TileIndex t, int d) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += d; + GetTile(t)->m5 += d; } /** @@ -109,7 +109,7 @@ static inline void AddClearDensity(TileIndex t, int d) static inline void SetClearDensity(TileIndex t, uint d) { assert(IsTileType(t, MP_CLEAR)); - SB(_m[t].m5, 0, 2, d); + SB(GetTile(t)->m5, 0, 2, d); } @@ -122,7 +122,7 @@ static inline void SetClearDensity(TileIndex t, uint d) static inline uint GetClearCounter(TileIndex t) { assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 5, 3); + return GB(GetTile(t)->m5, 5, 3); } /** @@ -134,7 +134,7 @@ static inline uint GetClearCounter(TileIndex t) static inline void AddClearCounter(TileIndex t, int c) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += c << 5; + GetTile(t)->m5 += c << 5; } /** @@ -146,7 +146,7 @@ static inline void AddClearCounter(TileIndex t, int c) static inline void SetClearCounter(TileIndex t, uint c) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - SB(_m[t].m5, 5, 3, c); + SB(GetTile(t)->m5, 5, 3, c); } @@ -160,7 +160,7 @@ static inline void SetClearCounter(TileIndex t, uint c) static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density) { assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 = 0 << 5 | type << 2 | density; + GetTile(t)->m5 = 0 << 5 | type << 2 | density; } @@ -173,7 +173,7 @@ static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint den static inline uint GetFieldType(TileIndex t) { assert(GetClearGround(t) == CLEAR_FIELDS); - return GB(_m[t].m3, 0, 4); + return GB(GetTile(t)->m3, 0, 4); } /** @@ -185,7 +185,7 @@ static inline uint GetFieldType(TileIndex t) static inline void SetFieldType(TileIndex t, uint f) { assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete - SB(_m[t].m3, 0, 4, f); + SB(GetTile(t)->m3, 0, 4, f); } /** @@ -197,7 +197,7 @@ static inline void SetFieldType(TileIndex t, uint f) static inline IndustryID GetIndustryIndexOfField(TileIndex t) { assert(GetClearGround(t) == CLEAR_FIELDS); - return(IndustryID) _m[t].m2; + return(IndustryID) GetTile(t)->m2; } /** @@ -209,7 +209,7 @@ static inline IndustryID GetIndustryIndexOfField(TileIndex t) static inline void SetIndustryIndexOfField(TileIndex t, IndustryID i) { assert(GetClearGround(t) == CLEAR_FIELDS); - _m[t].m2 = i; + GetTile(t)->m2 = i; } @@ -225,10 +225,10 @@ static inline uint GetFence(TileIndex t, DiagDirection side) assert(IsClearGround(t, CLEAR_FIELDS)); switch (side) { default: NOT_REACHED(); - case DIAGDIR_SE: return GB(_m[t].m4, 2, 3); - case DIAGDIR_SW: return GB(_m[t].m4, 5, 3); - case DIAGDIR_NE: return GB(_m[t].m3, 5, 3); - case DIAGDIR_NW: return GB(_me[t].m6, 2, 3); + case DIAGDIR_SE: return GB(GetTile(t)->m4, 2, 3); + case DIAGDIR_SW: return GB(GetTile(t)->m4, 5, 3); + case DIAGDIR_NE: return GB(GetTile(t)->m3, 5, 3); + case DIAGDIR_NW: return GB(GetTileEx(t)->m6, 2, 3); } } @@ -244,10 +244,10 @@ static inline void SetFence(TileIndex t, DiagDirection side, uint h) assert(IsClearGround(t, CLEAR_FIELDS)); switch (side) { default: NOT_REACHED(); - case DIAGDIR_SE: SB(_m[t].m4, 2, 3, h); break; - case DIAGDIR_SW: SB(_m[t].m4, 5, 3, h); break; - case DIAGDIR_NE: SB(_m[t].m3, 5, 3, h); break; - case DIAGDIR_NW: SB(_me[t].m6, 2, 3, h); break; + case DIAGDIR_SE: SB(GetTile(t)->m4, 2, 3, h); break; + case DIAGDIR_SW: SB(GetTile(t)->m4, 5, 3, h); break; + case DIAGDIR_NE: SB(GetTile(t)->m3, 5, 3, h); break; + case DIAGDIR_NW: SB(GetTileEx(t)->m6, 2, 3, h); break; } } @@ -261,14 +261,14 @@ static inline void SetFence(TileIndex t, DiagDirection side, uint h) static inline void MakeClear(TileIndex t, ClearGround g, uint density) { SetTileType(t, MP_CLEAR); - _m[t].m1 = 0; + GetTile(t)->m1 = 0; SetTileOwner(t, OWNER_NONE); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0 << 5 | 0 << 2; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0 << 5 | 0 << 2; SetClearGroundDensity(t, g, density); // Sets m5 - _me[t].m6 = 0; - _me[t].m7 = 0; + GetTileEx(t)->m6 = 0; + GetTileEx(t)->m7 = 0; } @@ -281,14 +281,14 @@ static inline void MakeClear(TileIndex t, ClearGround g, uint density) static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) { SetTileType(t, MP_CLEAR); - _m[t].m1 = 0; + GetTile(t)->m1 = 0; SetTileOwner(t, OWNER_NONE); - _m[t].m2 = industry; - _m[t].m3 = field_type; - _m[t].m4 = 0 << 5 | 0 << 2; + GetTile(t)->m2 = industry; + GetTile(t)->m3 = field_type; + GetTile(t)->m4 = 0 << 5 | 0 << 2; SetClearGroundDensity(t, CLEAR_FIELDS, 3); - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } /** @@ -300,7 +300,7 @@ static inline void MakeField(TileIndex t, uint field_type, IndustryID industry) static inline void MakeSnow(TileIndex t, uint density = 0) { assert(GetClearGround(t) != CLEAR_SNOW); - SetBit(_m[t].m3, 4); + SetBit(GetTile(t)->m3, 4); if (GetRawClearGround(t) == CLEAR_FIELDS) { SetClearGroundDensity(t, CLEAR_GRASS, density); } else { @@ -316,7 +316,7 @@ static inline void MakeSnow(TileIndex t, uint density = 0) static inline void ClearSnow(TileIndex t) { assert(GetClearGround(t) == CLEAR_SNOW); - ClrBit(_m[t].m3, 4); + ClrBit(GetTile(t)->m3, 4); SetClearDensity(t, 3); } diff --git a/src/clipboard.cpp b/src/clipboard.cpp new file mode 100644 index 000000000..4981a0590 --- /dev/null +++ b/src/clipboard.cpp @@ -0,0 +1,293 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard.cpp Implementaion of clipboard related to both copying and pasting. */ + +#include "stdafx.h" +#include "core/alloc_func.hpp" +#include "core/mem_func.hpp" +#include "clipboard_func.h" +#include "tilearea_type.h" +#include "station_map.h" +#include "void_map.h" +#include "newgrf_airport.h" + +static Map _clipboard_buffers[NUM_CLIPBOARD_BUFFERS]; +static ClipboardStationList _clipboard_stations[NUM_CLIPBOARD_BUFFERS]; + +/** + * Get the list of stations associated to a given clipboard buffer. + * @param buffer the buffer + * @return the list + * + * @pre IsClipboardBuffer(buffer) + */ +static ClipboardStationList GetClipboardStationList(Map *buffer) +{ + uint index = GetClipboardBufferIndex(buffer); + assert(index < lengthof(_clipboard_stations)); + return _clipboard_stations[index]; +} + +/** + * Associate a list of stations to a given clipboard buffer. + * @param list the list + * @param buffer the buffer + * + * @pre IsClipboardBuffer(buffer) + */ +static void SetClipboardStationList(ClipboardStationList list, Map *buffer) +{ + uint index = GetClipboardBufferIndex(buffer); + assert(index < lengthof(_clipboard_stations)); + FreeClipboardStationList(&_clipboard_stations[index]); + _clipboard_stations[index] = list; +} + +/** + * Free a list of clipboard stations. + * @param list the list + */ +void FreeClipboardStationList(ClipboardStationList *list) +{ + for (ClipboardStation *item = *list, *next; item != NULL; item = next) { + next = item->next; + delete item; + } + *list = NULL; +} + +/** + * Test whether a given #Map is a clipboard buffer. + * @return if the map a clipboard buffer + */ +bool IsClipboardBuffer(const Map *map) +{ + return (size_t)(map - _clipboard_buffers) < NUM_CLIPBOARD_BUFFERS; +} + +/** + * Get a clipboard buffer by it's index. + * @param index the index + * @return the buffer + * + * @pre index < NUM_CLIPBOARD_BUFFERS + */ +Map *GetClipboardBuffer(uint index) +{ + assert(index < NUM_CLIPBOARD_BUFFERS); + return &_clipboard_buffers[index]; +} + +/** + * Get the index of a clipboard buffer. + * @param buffer the buffer + * @return the index + * + * @pre IsClipboardBuffer(buffer) + */ +uint GetClipboardBufferIndex(const Map *buffer) +{ + assert(IsClipboardBuffer(buffer)); + return buffer - _clipboard_buffers; +} + +/** + * Test if a clipboard buffer is empty. + * @param buffer the buffer + * @return true iff there is no content in the buffer + * + * @pre IsClipboardBuffer(buffer) + */ +bool IsClipboardBufferEmpty(const Map *buffer) +{ + assert(IsClipboardBuffer(buffer)); + return buffer->m == NULL; +}; + +/** + * Clear content of a clipboard buffer. + * @param buffer the buffer + * + * @pre IsClipboardBuffer(buffer) + */ +void EmptyClipboardBuffer(Map *buffer) +{ + if (IsClipboardBufferEmpty(buffer)) return; + + SetClipboardStationList(NULL, buffer); + + buffer->size_x = 0; + buffer->size_y = 0; + buffer->size = 0; + + free(buffer->m); + buffer->m = NULL; + free(buffer->me); + buffer->me = NULL; +} + +/** + * Allocate space in a clipboard buffer. + * @param buffer the buffer + * @param content_size_x X size of the content (excluding MP_VOID tiles on southern borders) + * @param content_size_y Y size of the content (excluding MP_VOID tiles on southern borders) + * + * @pre IsClipboardBuffer(buffer) + */ +void AllocateClipboardBuffer(Map *buffer, uint content_size_x, uint content_size_y) +{ + assert(IsClipboardBuffer(buffer)); + assert(IsInsideMM(content_size_x, 1, INT_MAX - 1)); + assert(IsInsideMM(content_size_y, 1, INT_MAX - 1)); + + SetClipboardStationList(NULL, buffer); + + buffer->size_x = content_size_x + 1; + buffer->size_y = content_size_y + 1; + buffer->size = buffer->size_x * buffer->size_y; + + free(buffer->m); + free(buffer->me); + buffer->m = CallocT(buffer->size); + buffer->me = CallocT(buffer->size); + + GENERIC_TILE_AREA_LOOP(iter, GenericTileArea(TileXY(buffer->size_x - 1, 0, buffer), 1, buffer->size_y)) { + MakeVoid(iter); + } + GENERIC_TILE_AREA_LOOP(iter, GenericTileArea(TileXY(0, buffer->size_y - 1, buffer), buffer->size_x - 1, 1)) { + MakeVoid(iter); + } +} + +/** + * Get #ClipboardStation by a given ID. + * @param id the ID of the station + * @param buffer clipboard buffer to get the station from + * + * @pre IsClipboardBuffer(buffer) + */ +/* static */ ClipboardStation *ClipboardStation::Get(StationID id, Map *buffer) +{ + for (ClipboardStation *ret = GetClipboardStationList(buffer); ret != NULL; ret = ret->next) { + if (ret->id == id) return ret; + } + return NULL; +} + +/** + * Get #ClipboardStation by a given tile. + * @param tile any tile that belongs to the station + * @return station pointer or NULL if the tile is not a station + * + * @pre IsClipboardBuffer(MapOf(tile)) + */ +/* static */ ClipboardStation *ClipboardStation::GetByTile(GenericTileIndex tile) +{ + return ClipboardStation::Get(GetStationIndex(tile), MapOf(tile)); +} + +ClipboardStation::ClipboardStation() +{ + this->id = INVALID_STATION; + this->airport.tile = INVALID_TILE_INDEX; + this->airport.w = 0; + this->airport.h = 0; + this->airport.type = AT_INVALID; + this->airport.layout = 0; + this->num_specs = 0; + this->speclist = NULL; + this->next = NULL; +} + +ClipboardStation::~ClipboardStation() +{ + free(this->speclist); +} + +ClipboardStation **ClipboardStationsBuilder::FindStation(StationID sid) +{ + ClipboardStation **ret = &this->stations; + while (*ret != NULL) { + if ((*ret)->id == sid) break; + ret = &((*ret)->next); + } + return ret; +} + +ClipboardStation *ClipboardStationsBuilder::AddStation(StationID sid) +{ + ClipboardStation **st_link = this->FindStation(sid); + ClipboardStation *st = *st_link; + if (st == NULL) { + st = new ClipboardStation; + st->id = sid; + *st_link = st; // put new item on the back of the list + } + return st; +} + +void ClipboardStationsBuilder::AddSpecToStation(ClipboardStation *st, StationClassID station_class, byte station_type, byte specindex) +{ + assert(specindex != 0 || (station_type == 0 && (station_class == STAT_CLASS_DFLT || station_class == STAT_CLASS_WAYP))); + + if (specindex >= st->num_specs) { + /* Add "empty" placeholders. */ + st->speclist = ReallocT(st->speclist, specindex + 1); + for (int i = st->num_specs; i < specindex; i++) { + st->speclist[i].stat_class = STAT_CLASS_DFLT; + st->speclist[i].stat_type = 0; + } + st->num_specs = specindex + 1; + } else { + /* We can override an "empty" placeholder, but if the spec was added before, it shouldn't change. */ + assert((st->speclist[specindex].stat_class == station_class && st->speclist[specindex].stat_type == station_type) || + (st->speclist[specindex].stat_class == STAT_CLASS_DFLT && st->speclist[specindex].stat_type == 0)); + } + st->speclist[specindex].stat_class = station_class; + st->speclist[specindex].stat_type = station_type; +} + +/** + * Add an airport part. + * + * @param sid id of the station + * @param tile northern tile of the airport + * @param type airport type + * @param layout airport layout + */ +void ClipboardStationsBuilder::AddAirportPart(StationID sid, RawTileIndex tile, AirportTypes type, byte layout) +{ + ClipboardStation *st = this->AddStation(sid); + + assert(st->airport.type == AT_INVALID); // single airport per station! + const AirportSpec *spec = AirportSpec::Get(type); + st->airport.tile = tile; + if (spec->rotation[layout] != DIR_E && spec->rotation[layout] != DIR_W) { + st->airport.w = spec->size_x; + st->airport.h = spec->size_y; + } else { + st->airport.w = spec->size_y; + st->airport.h = spec->size_x; + } + st->airport.type = type; + st->airport.layout = layout; +} + +/** + * Finish building and store results. + * @param buffer clipboard buffer to store the list in + * + * @pre IsClipboardBuffer(MapOf(tile)) + */ +void ClipboardStationsBuilder::BuildDone(Map *buffer) +{ + SetClipboardStationList(this->stations, buffer); + this->stations = NULL; +} diff --git a/src/clipboard_func.h b/src/clipboard_func.h new file mode 100644 index 000000000..63582ce11 --- /dev/null +++ b/src/clipboard_func.h @@ -0,0 +1,73 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_func.h Functions related to the clipboad. */ + +#ifndef CLIPBOARD_FUNC_H +#define CLIPBOARD_FUNC_H + +#include "clipboard_type.h" + +void FreeClipboardStationList(ClipboardStationList *list); + +/** Helper class to build a station list while copying to the clipboard. */ +class ClipboardStationsBuilder { +protected: + ClipboardStationList stations; ///< the list of stations + + ClipboardStation **FindStation(StationID sid); + ClipboardStation *AddStation(StationID sid); + void AddSpecToStation(ClipboardStation *st, StationClassID station_class, byte station_type, byte specindex); + +public: + ClipboardStationsBuilder() : stations(NULL) + { } + + ~ClipboardStationsBuilder() + { + FreeClipboardStationList(&this->stations); + } + + /** + * Add a "simple" station part (bus/truck/dock/buoy). + * @param sid id of the station + */ + inline void AddPart(StationID sid) + { + this->AddStation(sid); + } + + /** + * Add a rail station/waypoint part. + * @param sid id of the station + * @param station_class custom station class + * @param station_type type within the custom station class + * @param specindex index of the given station spec in the list of specs of this station (aka custom station spec index) + */ + inline void AddRailPart(StationID sid, StationClassID station_class, byte station_type, byte specindex) + { + this->AddSpecToStation(this->AddStation(sid), station_class, station_type, specindex); + } + + void AddAirportPart(StationID sid, RawTileIndex tile, AirportTypes type, byte layout); + + void BuildDone(Map *clipboard); +}; + +static const uint NUM_CLIPBOARD_BUFFERS = 5; ///< Total amount of clipboard buffers + +bool IsClipboardBuffer(const Map *map); +Map *GetClipboardBuffer(uint index); +uint GetClipboardBufferIndex(const Map *clipboard); +void AllocateClipboardBuffer(Map *clipboard, uint size_x, uint size_y); +bool IsClipboardBufferEmpty(const Map *clipboard); +void EmptyClipboardBuffer(Map *clipboard); +void ClearClipboard(); + +#endif /* CLIPBOARD_FUNC_H */ diff --git a/src/clipboard_gui.cpp b/src/clipboard_gui.cpp new file mode 100644 index 000000000..50cdc9696 --- /dev/null +++ b/src/clipboard_gui.cpp @@ -0,0 +1,730 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_gui.cpp GUIs related to the clipboard. */ + +#include "stdafx.h" +#include "core/geometry_func.hpp" +#include "network/network.h" +#include "widgets/clipboard_widget.h" +#include "clipboard_func.h" +#include "clipboard_gui.h" +#include "command_func.h" +#include "company_func.h" +#include "company_base.h" +#include "copypaste_cmd.h" +#include "direction_func.h" +#include "error.h" +#include "gfx_func.h" +#include "gui.h" +#include "hotkeys.h" +#include "rail.h" +#include "rail_gui.h" +#include "rail_map.h" +#include "road_map.h" +#include "slope_func.h" +#include "sound_func.h" +#include "station_map.h" +#include "strings_func.h" +#include "terraform_gui.h" +#include "tilearea_type.h" +#include "tilehighlight_func.h" +#include "track_func.h" +#include "tunnelbridge_map.h" +#include "viewport_func.h" +#include "window_gui.h" +#include "window_func.h" + +#include "table/sprites.h" +#include "table/strings.h" + +static const int CLIPBOARD_ADDITIONAL_HEIGHT_MAX = 7; +static const int CLIPBOARD_ADDITIONAL_HEIGHT_MIN = -7; +static const uint NUM_USER_CLIPBOARDS = NUM_CLIPBOARD_BUFFERS - 1; ///< Number of clipboards available + +/** Clipboard parameters. */ +struct ClipboardProps { + TileArea copy_area; ///< Area on the main map selected as source of a copy operation + CopyPasteMode mode; ///< Various flags that will be applied when pasting + RailType railtype; ///< #Railtype to convert to when pasting + DirTransformation transformation; ///< Rotation/reflection to apply when pasting + int additional_height_delta; ///< Additional amount of tile heights to add when pasting + + ClipboardProps() + : copy_area(INVALID_TILE, 0, 0) + , mode(CPM_DEFAULT) + , railtype(INVALID_RAILTYPE) + , transformation(DTR_IDENTITY) + , additional_height_delta(0) + { } +}; + +ClipboardProps _clipboard_props[NUM_USER_CLIPBOARDS]; ///< Clipboard parameters selected via GUI +ClipboardProps *_current_clipboard = &_clipboard_props[0]; ///< Currently selected clipboard +static TileArea _clipboard_paste_area = TileArea(INVALID_TILE, 0, 0); ///< Area on the main map selected as destination for a paste operation + +/** Clear entire clipboard. */ +void ClearClipboard() +{ + for (uint i = 0; i < NUM_CLIPBOARD_BUFFERS; i++) EmptyClipboardBuffer(GetClipboardBuffer(i)); + for (uint i = 0; i < NUM_USER_CLIPBOARDS; i++) _clipboard_props[i].copy_area = TileArea(INVALID_TILE, 0, 0); +} + +/** + * Whether the copy/paste operations are performed with the clipboard buffer, or instantantly. + * + * If true, clipboard buffer is on. Each "copy" user action moves selected area to the clipboard + * (to the buffer) and each "paste" tries to reproduce contents of the clipboard on the main map. + * + * If false, clipboard buffer is off. "copy" user action just selects area and + * "paste" makes an instant copy&paste from the selected area to pointed place. + * + * @return whether the clipboard buffer is available for local company + */ +static inline bool IsClipboardBufferOn() { return !_networking; } + +static inline Map *GetCurrentClipboardBuffer() +{ + return IsClipboardBufferOn() ? GetClipboardBuffer(_current_clipboard - _clipboard_props) : NULL; +} + +static inline bool IsClipboardCopyAreaSelected() +{ + return _current_clipboard->copy_area.tile != INVALID_TILE; +} + +static inline bool IsClipboardPasteSourceSet() +{ + return IsClipboardBufferOn() ? !IsClipboardBufferEmpty(GetCurrentClipboardBuffer()) : IsClipboardCopyAreaSelected(); +} + +static void ClipboardRecalcPasteAreaSize() +{ + assert(IsClipboardPasteSourceSet()); + + Dimension size; + if (IsClipboardBufferOn()) { + size.width = GetCurrentClipboardBuffer()->size_x - 1; + size.height = GetCurrentClipboardBuffer()->size_y - 1; + } else { + size.width = _current_clipboard->copy_area.w; + size.height = _current_clipboard->copy_area.h; + } + size = TransformDimension(size, _current_clipboard->transformation); + + _clipboard_paste_area.w = size.width; + _clipboard_paste_area.h = size.height; +} + +void CcPaste(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + if (_paste_err_tile != INVALID_TILE) SetRedErrorSquare(_paste_err_tile); + + if (result.Succeeded()) { + if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile); + if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); + } +} + +void GetTilePastePreview(TileIndex tile, TilePastePreview *ret) +{ + _clipboard_paste_area.tile = TileVirtXY(_thd.pos.x, _thd.pos.y); + + extern bool TestRailTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company, TileContentPastePreview *preview); + extern bool TestRoadTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company, TileContentPastePreview *preview); + extern bool TestWaterTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *object_rect, CompanyID company, TileContentPastePreview *preview); + extern bool TestTunnelBridgeTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileIndex *other_end, CompanyID company, TileContentPastePreview *preview); + extern bool TestStationTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *station_part_area, CompanyID company, TileContentPastePreview *preview); + + Map *clipboard = GetCurrentClipboardBuffer(); + + /* the area we are copying from */ + GenericTileArea src_area = IsClipboardBufferOn() ? + GenericTileArea(TileXY(0, 0, clipboard), clipboard->size_x - 1, clipboard->size_y - 1) : + GenericTileArea(_current_clipboard->copy_area); + + DirTransformation inv_dtr = InvertDirTransform(_current_clipboard->transformation); + /* area containing all tile corners (also those at SW and SE borders) */ + TileArea paste_area_corners(_clipboard_paste_area.tile, _clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); + /* source corner of the most norther corner */ + GenericTileIndex src_of_north_corner = paste_area_corners.TransformedNorth(src_area.tile, inv_dtr); + /* source corner of the tile corner (source of it's height) */ + GenericTileIndex src_of_tile_corner = paste_area_corners.TransformTile(tile, src_of_north_corner, inv_dtr); + /* calculate the height difference between areas */ + int height_delta = TileHeight(paste_area_corners.tile) - TileHeight(src_of_north_corner) + _current_clipboard->additional_height_delta; + + if (_clipboard_paste_area.Contains(tile)) { + /* source tile of the tile */ + GenericTileIndex src_tile = _clipboard_paste_area.TransformTile(tile, _clipboard_paste_area.TransformedNorth(src_area.tile, inv_dtr), inv_dtr); + + bool has_preview = false; + switch(GetTileType(src_tile)) { + case MP_RAILWAY: has_preview = TestRailTileCopyability(src_tile, _current_clipboard->mode, _local_company, ret); break; + case MP_ROAD: has_preview = TestRoadTileCopyability(src_tile, _current_clipboard->mode, _local_company, ret); break; + case MP_STATION: has_preview = TestStationTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; + case MP_WATER: has_preview = TestWaterTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; + case MP_TUNNELBRIDGE: has_preview = TestTunnelBridgeTileCopyability(src_tile, src_area, _current_clipboard->mode, NULL, _local_company, ret); break; + default: MemSetT(ret, 0); break; + } + + if (has_preview) ret->highlight_track_bits = TransformTrackBits(ret->highlight_track_bits, _current_clipboard->transformation); + } else { + assert(paste_area_corners.Contains(tile)); + MemSetT(ret, 0); + } + + ret->tile_height = TileHeight(src_of_tile_corner) + height_delta; +} + +struct ClipboardToolbarWindow : Window { + static HotkeyList hotkeys; + + static CopyPasteMode FlagButtonToFlagBit(int button) + { + switch (button) { + case WID_CT_WITH_RAIL: return CPM_WITH_RAIL_TRANSPORT; + case WID_CT_WITH_ROAD: return CPM_WITH_ROAD_TRANSPORT; + case WID_CT_WITH_WATER: return CPM_WITH_WATER_TRANSPORT; + case WID_CT_WITH_AIR: return CPM_WITH_AIR_TRANSPORT; + case WID_CT_MIRROR_SIGNALS: return CPM_MIRROR_SIGNALS; + case WID_CT_UPGRADE_BRIDGES: return CPM_UPGRADE_BRIDGES; + case WID_CT_WITH_STATIONS: return CPM_WITH_STATIONS; + default: NOT_REACHED(); break; + }; + return CPM_NONE; + } + + ClipboardToolbarWindow(WindowDesc *desc) : Window(desc) + { + this->InitNested(); + + if (!IsClipboardBufferOn()) { + NWidgetCore *button = this->GetWidget(WID_CT_COPY); + button->widget_data = SPR_IMG_CLIPBOARD_SELECT_COPY_AREA; // instead of SPR_IMG_CLIPBOARD_COPY + button->tool_tip = STR_CLIPBOARD_TOOLTIP_SELECT_COPY_AREA; // instead of STR_CLIPBOARD_TOOLTIP_COPY + + button = this->GetWidget(WID_CT_PASTE); + button->widget_data = SPR_IMG_CLIPBOARD_INSTANT_COPY_PASTE; // instead of SPR_IMG_CLIPBOARD_PASTE + button->tool_tip = STR_CLIPBOARD_TOOLTIP_INSTANT_COPY_PASTE; // instead of STR_CLIPBOARD_TOOLTIP_PASTE + } + + /* select another railtype if the one that was used last time is invalid/unavailable */ + for (uint i = 0; i < lengthof(_clipboard_props); i++) { + if (!IsInsideMM(_clipboard_props[i].railtype, RAILTYPE_BEGIN, RAILTYPE_END)) { + _clipboard_props[i].railtype = RAILTYPE_BEGIN; + } + RailType rt = _clipboard_props[i].railtype; + while (!HasRailtypeAvail(_local_company, rt)) { + rt++; + if (rt >= RAILTYPE_END) rt = RAILTYPE_BEGIN; + + if (rt == _clipboard_props[i].railtype) { // did we get back to the point where we started? + rt = INVALID_RAILTYPE; + _clipboard_props[i].mode &= ~CPM_CONVERT_RAILTYPE; + break; + } + } + _clipboard_props[i].railtype = rt; + } + + this->UpdateButtons(); + + if (_settings_client.gui.link_terraform_toolbar) ShowTerraformToolbar(this); + } + + ~ClipboardToolbarWindow() + { + if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); + } + + void UpdateButtons() + { + /* lower clipboard index indicator */ + this->SetWidgetLoweredState(WID_CT_CLIPBOARD_1, _current_clipboard == &_clipboard_props[0]); + this->SetWidgetLoweredState(WID_CT_CLIPBOARD_2, _current_clipboard == &_clipboard_props[1]); + this->SetWidgetLoweredState(WID_CT_CLIPBOARD_3, _current_clipboard == &_clipboard_props[2]); + this->SetWidgetLoweredState(WID_CT_CLIPBOARD_4, _current_clipboard == &_clipboard_props[3]); + /* disable the paste button if there is nothing to paste */ + this->SetWidgetDisabledState(WID_CT_PASTE, !IsClipboardPasteSourceSet()); + /* lower on/off buttons */ + for (int widget = WID_CT_PASTE_FLAG_BUTTON_BEGIN; widget < WID_CT_PASTE_FLAG_BUTTON_END; widget++) { + this->SetWidgetLoweredState(widget, (_current_clipboard->mode & ClipboardToolbarWindow::FlagButtonToFlagBit(widget)) != 0); + } + this->SetWidgetLoweredState(WID_CT_TERRAFORM, (_current_clipboard->mode & CPM_TERRAFORM_MASK) != CPM_TERRAFORM_NONE); + /* set the sprite on the railtype button */ + this->GetWidget(WID_CT_CONVERT_RAILTYPE)->widget_data = + (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? + GetRailTypeInfo(_current_clipboard->railtype)->gui_sprites.convert_rail : + SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION; + + this->SetDirty(); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + int offset = this->IsWidgetLowered(widget) ? 2 : 1; + switch (widget) { + case WID_CT_WITH_RAIL: + case WID_CT_WITH_ROAD: + case WID_CT_WITH_WATER: + case WID_CT_WITH_AIR: { + offset++; + DrawSprite(SPR_BLOT, this->IsWidgetLowered(widget) ? PALETTE_TO_GREEN : PALETTE_TO_RED, r.left + offset, r.top + offset); + break; + } + + case WID_CT_TERRAFORM: { + offset++; + PaletteID pal; + switch (_current_clipboard->mode & CPM_TERRAFORM_MASK) { + case CPM_TERRAFORM_FULL: pal = PALETTE_TO_GREEN; break; + case CPM_TERRAFORM_MINIMAL: pal = PALETTE_TO_YELLOW; break; + default: pal = PALETTE_TO_RED; break; + } + DrawSprite(SPR_BLOT, pal, r.left + offset, r.top + offset); + break; + } + + case WID_CT_TRANSFORMATION: + DrawSprite(SPR_IMG_TRANFORMATION_IDENTITY + _current_clipboard->transformation, PAL_NONE, r.left + offset, r.top + offset); + break; + + case WID_CT_HEIGHT_DIFF_GLYPH: + DrawSprite(SPR_IMG_CLIPBOARD_HEIGHT_PANEL, PAL_NONE, r.left, r.top); + break; + + default: + break; + } + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + Dimension d; + + switch (widget) { + case WID_CT_CLIPBOARD_1: + case WID_CT_CLIPBOARD_2: + case WID_CT_CLIPBOARD_3: + case WID_CT_CLIPBOARD_4: + d.width = GetDigitWidth() + 4; + d.height = FONT_HEIGHT_NORMAL; + break; + + case WID_CT_HEIGHT_DIFF_GLYPH: + d = GetSpriteSize(SPR_IMG_CLIPBOARD_HEIGHT_PANEL); + break; + + case WID_CT_HEIGHT_DIFF: { + /* Backup the height delta. The variable will be used to calculate the size of the widget. */ + int backup = _current_clipboard->additional_height_delta; + /* calculate the size */ + d.width = d.height = 0; + for (_current_clipboard->additional_height_delta = CLIPBOARD_ADDITIONAL_HEIGHT_MIN; _current_clipboard->additional_height_delta <= CLIPBOARD_ADDITIONAL_HEIGHT_MAX; _current_clipboard->additional_height_delta++) { + this->SetStringParameters(WID_CT_HEIGHT_DIFF); // additional_height_delta will be used there + d = maxdim(d, GetStringBoundingBox(this->GetWidget(WID_CT_HEIGHT_DIFF)->widget_data)); + } + d.width += 1; + /* restore */ + _current_clipboard->additional_height_delta = backup; + break; + } + + default: + return; + } + + d.width += padding.width; + d.height += padding.height; + *size = maxdim(*size, d); + } + + virtual void SetStringParameters(int widget) const + { + switch (widget) { + case WID_CT_CLIPBOARD_1: + case WID_CT_CLIPBOARD_2: + case WID_CT_CLIPBOARD_3: + case WID_CT_CLIPBOARD_4: + SetDParam(0, widget - WID_CT_CLIPBOARD_1 + 1); + break; + + case WID_CT_HEIGHT_DIFF: + SetDParam(0, (StringID)(STR_CLIPBOARD_HEIGHT_DIFF_NEUTRAL + sgn(_current_clipboard->additional_height_delta))); + SetDParam(1, abs(_current_clipboard->additional_height_delta)); + break; + } + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + if (this->IsWidgetDisabled(widget)) return; + + DirTransformation add_clipboard_transformation = DTR_IDENTITY; // additional transformation + + switch (widget) { + case WID_CT_CLIPBOARD_1: + case WID_CT_CLIPBOARD_2: + case WID_CT_CLIPBOARD_3: + case WID_CT_CLIPBOARD_4: + /* switch to another clipboard */ + assert(IsInsideMM(widget - WID_CT_CLIPBOARD_1, 0, lengthof(_clipboard_props))); + _current_clipboard = &_clipboard_props[widget - WID_CT_CLIPBOARD_1]; + this->UpdateButtons(); + + if (this->IsWidgetLowered(WID_CT_PASTE)) { + if (IsClipboardPasteSourceSet()) { + /* update paste preview */ + ClipboardRecalcPasteAreaSize(); + SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); + UpdateTileSelection(); + MarkWholeScreenDirty(); + } else { + ResetObjectToPlace(); // current clipboard is empty! + } + } + break; + + case WID_CT_COPY: + if (HandlePlacePushButton(this, widget, SPR_CURSOR_COPY, HT_RECT)) { + this->SetWidgetDirty(widget); + } + return; + + case WID_CT_PASTE: + if (HandlePlacePushButton(this, widget, _ctrl_pressed ? SPR_CURSOR_ADJUST_HEIGHT : SPR_CURSOR_PASTE, HT_POINT | HT_PASTE_PREVIEW)) { + ClipboardRecalcPasteAreaSize(); + SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); + this->SetWidgetDirty(widget); + } + return; + + case WID_CT_TERRAFORM: { + switch (_current_clipboard->mode & CPM_TERRAFORM_MASK) { + case CPM_TERRAFORM_NONE: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_FULL; break; + case CPM_TERRAFORM_MINIMAL: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_NONE; break; + case CPM_TERRAFORM_FULL: (_current_clipboard->mode &= ~CPM_TERRAFORM_MASK) |= CPM_TERRAFORM_MINIMAL; break; + default: NOT_REACHED(); + } + this->UpdateButtons(); + break; + } + + case WID_CT_TRANSFORMATION: + /* reset transformation - combined with its inversion will give identity */ + add_clipboard_transformation = InvertDirTransform(_current_clipboard->transformation); + break; + + case WID_CT_ROTATE_LEFT: add_clipboard_transformation = DTR_ROTATE_90_L; break; + case WID_CT_ROTATE_RIGHT: add_clipboard_transformation = DTR_ROTATE_90_R; break; + case WID_CT_REFLECT_NE_SW: add_clipboard_transformation = DTR_REFLECT_NE_SW; break; + case WID_CT_REFLECT_NW_SE: add_clipboard_transformation = DTR_REFLECT_NW_SE; break; + + case WID_CT_WITH_RAIL: + case WID_CT_WITH_ROAD: + case WID_CT_WITH_WATER: + case WID_CT_WITH_AIR: + case WID_CT_MIRROR_SIGNALS: + case WID_CT_UPGRADE_BRIDGES: + case WID_CT_WITH_STATIONS: + _current_clipboard->mode ^= ClipboardToolbarWindow::FlagButtonToFlagBit(widget); + this->UpdateButtons(); + break; + + case WID_CT_CONVERT_RAILTYPE: + ShowDropDownList(this, GetRailTypeDropDownList(), + (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? INVALID_RAILTYPE : _current_clipboard->railtype, + WID_CT_CONVERT_RAILTYPE, 140, true, true); + break; + + case WID_CT_HEIGHT_DIFF_INCREASE: this->ModifyAdditionalHeightDelta(+1); break; + case WID_CT_HEIGHT_DIFF_DECREASE: this->ModifyAdditionalHeightDelta(-1); break; + + default: + return; + } + + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + + if (add_clipboard_transformation != DTR_IDENTITY) { + _current_clipboard->transformation = CombineDirTransform(_current_clipboard->transformation, add_clipboard_transformation); + this->SetWidgetDirty(WID_CT_TRANSFORMATION); + if (this->IsWidgetLowered(WID_CT_PASTE)) { + ClipboardRecalcPasteAreaSize(); + SetTileSelectSize(_clipboard_paste_area.w + 1, _clipboard_paste_area.h + 1); + } + } + } + + virtual EventState OnHotkey(int hotkey) + { + switch (hotkey) { + case WID_CT_CONVERT_RAILTYPE: + this->OnDropdownSelect(WID_CT_CONVERT_RAILTYPE, (_current_clipboard->mode & CPM_CONVERT_RAILTYPE) ? INVALID_RAILTYPE : _current_clipboard->railtype); + this->SetWidgetDirty(WID_CT_CONVERT_RAILTYPE); + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + return ES_HANDLED; + + case WID_CT_WITH_RAIL: + case WID_CT_WITH_ROAD: + case WID_CT_WITH_WATER: + case WID_CT_WITH_AIR: + case WID_CT_TERRAFORM: + case WID_CT_WITH_STATIONS: + if (this->IsWidgetLowered(WID_CT_PASTE)) MarkWholeScreenDirty(); // redraw tile selection + break; + + default: + break; + } + + return this->Window::OnHotkey(hotkey); + } + + virtual void OnDropdownSelect(int widget, int index) + { + assert(widget == WID_CT_CONVERT_RAILTYPE); + if (index == INVALID_RAILTYPE) { + _current_clipboard->mode &= ~CPM_CONVERT_RAILTYPE; + } else { + _current_clipboard->mode |= CPM_CONVERT_RAILTYPE; + _current_clipboard->railtype = (RailType)index; + } + this->UpdateButtons(); + } + + virtual EventState OnCTRLStateChange() + { + if (this->IsWidgetLowered(WID_CT_PASTE)) SetMouseCursor(_ctrl_pressed ? SPR_CURSOR_ADJUST_HEIGHT : SPR_CURSOR_PASTE, PAL_NONE); + + return ES_NOT_HANDLED; + } + + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + if (this->IsWidgetLowered(WID_CT_COPY)) { + /* start copy area dragging */ + VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_COPY_TO_CLIPBOARD); + VpSetPlaceSizingLimit(_settings_game.construction.clipboard_capacity); + } else { + _clipboard_paste_area.tile = tile; + + /* do paste */ + assert(IsClipboardPasteSourceSet()); + + uint32 p1 = 0, p2 = 0; + SB(p1, 28, 4, _current_clipboard->railtype); + SB(p2, 12, 4, _current_clipboard->additional_height_delta); + SB(p2, 16, 3, _current_clipboard->transformation); + SB(p2, 19, 10, _current_clipboard->mode); + if (IsClipboardBufferOn()) { + /* copy/paste clipboard-to-map */ + SB(p1, 0, 2, GetClipboardBufferIndex(GetCurrentClipboardBuffer())); + SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, STR_ERROR_CAN_T_PASTE_HERE); + DoCommandP(tile, p1, p2, CMD_PASTE_FROM_CLIPBOARD | CMD_MSG(STR_COPY_PASTE_ERROR_SUMMARY), CcPaste); + } else { + /* copy/paste map-to-map */ + SB(p1, 0, 28, _current_clipboard->copy_area.tile); + SB(p2, 0, 6, _current_clipboard->copy_area.w); + SB(p2, 6, 6, _current_clipboard->copy_area.h); + SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, STR_ERROR_CAN_T_PASTE_HERE); + DoCommandP(tile, p1, p2, CMD_INSTANT_COPY_PASTE | CMD_MSG(STR_COPY_PASTE_ERROR_SUMMARY), CcPaste); + } + + MarkWholeScreenDirty(); // redraw tile selection + } + } + + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + VpSelectTilesWithMethod(pt.x, pt.y, select_method); + } + + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (pt.x != -1) { + switch (select_proc) { + case DDSP_COPY_TO_CLIPBOARD: { + TileArea ta = TileArea(start_tile, end_tile); + + /* do copy */ + if (IsClipboardBufferOn()) { + /* copy into the buffer */ + uint32 p1 = 0, p2 = 0; + SB(p1, 0, 2, GetClipboardBufferIndex(GetCurrentClipboardBuffer())); + SB(p2, 0, 6, ta.w); // source area width + SB(p2, 6, 6, ta.h); // source area height + if (!DoCommandP(ta.tile, p1, p2, CMD_COPY_TO_CLIPBOARD) || _shift_pressed) return; // leave copy tool opened + } + ResetObjectToPlace(); + + /* select copy area */ + _current_clipboard->copy_area = ta; + + /* reset transformation and update buttons */ + _current_clipboard->transformation = DTR_IDENTITY; + this->ModifyAdditionalHeightDelta(-_current_clipboard->additional_height_delta); + this->UpdateButtons(); + break; + } + + default: + NOT_REACHED(); + } + } + } + + virtual void OnPlaceObjectAbort() + { + /* Unclick "copy" and "paste" buttons */ + this->RaiseWidget(WID_CT_COPY); + this->RaiseWidget(WID_CT_PASTE); + this->SetWidgetDirty(WID_CT_COPY); + this->SetWidgetDirty(WID_CT_PASTE); + } + + EventState OnPlaceMouseWheel(Point pt, int mousewheel) + { + if (mousewheel == 0 || !_ctrl_pressed || !this->IsWidgetLowered(WID_CT_PASTE)) return ES_NOT_HANDLED; + this->ModifyAdditionalHeightDelta(-sgn(mousewheel)); + return ES_HANDLED; + } + + void ModifyAdditionalHeightDelta(int diff) + { + _current_clipboard->additional_height_delta = Clamp(_current_clipboard->additional_height_delta + diff, CLIPBOARD_ADDITIONAL_HEIGHT_MIN, CLIPBOARD_ADDITIONAL_HEIGHT_MAX); + this->SetWidgetDirty(WID_CT_HEIGHT_DIFF); + } +}; + +EventState ClipboardGlobalHotkeys(int hotkey) +{ + Window *w = ShowClipboardToolbar(); + if (w == NULL) return ES_NOT_HANDLED; + return w->OnHotkey(hotkey); +} + +static Hotkey _clipboard_hotkeys[] = { + Hotkey('C' | WKC_CTRL | WKC_GLOBAL_HOTKEY, "copy", WID_CT_COPY), + Hotkey('V' | WKC_CTRL | WKC_GLOBAL_HOTKEY, "paste", WID_CT_PASTE), + Hotkey('1', "clipboard1", WID_CT_CLIPBOARD_1), + Hotkey('2', "clipboard2", WID_CT_CLIPBOARD_2), + Hotkey('3', "clipboard3", WID_CT_CLIPBOARD_3), + Hotkey('4', "clipboard4", WID_CT_CLIPBOARD_4), + Hotkey('5', "rail", WID_CT_WITH_RAIL), + Hotkey('6', "road", WID_CT_WITH_ROAD), + Hotkey('7', "water", WID_CT_WITH_WATER), + Hotkey('8', "air", WID_CT_WITH_AIR), + Hotkey('9', "terrain", WID_CT_TERRAFORM), + Hotkey('0', "rail_conversion", WID_CT_CONVERT_RAILTYPE), + Hotkey('S', "signal_mirror", WID_CT_MIRROR_SIGNALS), + Hotkey('B', "bridge_upgrade", WID_CT_UPGRADE_BRIDGES), + Hotkey('N', "with_stations", WID_CT_WITH_STATIONS), + HOTKEY_LIST_END +}; +HotkeyList ClipboardToolbarWindow::hotkeys("clipboard", _clipboard_hotkeys, ClipboardGlobalHotkeys); + +static const NWidgetPart _nested_clipboard_toolbar_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_CLIPBOARD_TOOLBAR_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), + EndContainer(), + NWidget(NWID_HORIZONTAL), + /* CLIPBOARD INDEX BUTTONS */ + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_1), + SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_1ST_CLIPBOARD), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_2), + SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_2ND_CLIPBOARD), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_3), + SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_3RD_CLIPBOARD), + NWidget(WWT_TEXTBTN, COLOUR_DARK_GREEN, WID_CT_CLIPBOARD_4), + SetFill(0, 1), SetMinimalSize(8, 22), SetDataTip(STR_BLACK_INT, STR_CLIPBOARD_TOOLTIP_SWITCH_TO_4TH_CLIPBOARD), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), + + /* COPY / PASTE BUTTONS */ + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_COPY), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_COPY, STR_CLIPBOARD_TOOLTIP_COPY), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_PASTE), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_PASTE, STR_CLIPBOARD_TOOLTIP_PASTE), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), + + /* COPY/PASTE MODE SELECTORS */ + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_RAIL), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDRAIL, STR_CLIPBOARD_TOOLTIP_COPY_WITH_RAIL_TRANSPORT), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_ROAD), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDROAD, STR_CLIPBOARD_TOOLTIP_COPY_WITH_ROAD_TRANSPORT), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_WATER), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDWATER, STR_CLIPBOARD_TOOLTIP_COPY_WITH_WATER_TRANSPORT), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_AIR), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUILDAIR, STR_CLIPBOARD_TOOLTIP_COPY_WITH_AIR_TRANSPORT), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_TERRAFORM), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_LANDSCAPING, STR_CLIPBOARD_TOOLTIP_TERRAFORM), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_CONVERT_RAILTYPE), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION, STR_CLIPBOARD_TOOLTIP_CONVERT_RAIL), + NWidget(WWT_IMGBTN_2, COLOUR_DARK_GREEN, WID_CT_MIRROR_SIGNALS), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_OFF, STR_CLIPBOARD_TOOLTIP_MIRROR_SIGNALS), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_UPGRADE_BRIDGES), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_UPGRADE_BRIDGES, STR_CLIPBOARD_TOOLTIP_UPGRADE_BRIDGES), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_CT_WITH_STATIONS), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_COMPANY_LIST, STR_CLIPBOARD_TOOLTIP_WITH_STATIONS), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), + + /* TRANSFORMATIONS */ + NWidget(WWT_PUSHBTN, COLOUR_DARK_GREEN, WID_CT_TRANSFORMATION), + SetFill(0, 1), SetMinimalSize(23, 22), SetDataTip(0, STR_CLIPBOARD_TOOLTIP_TRANSFORMATION), + NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_ROTATE_LEFT), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_ROTATE_LEFT, STR_CLIPBOARD_TOOLTIP_ROTATE_LEFT), + NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_ROTATE_RIGHT), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_ROTATE_RIGHT, STR_CLIPBOARD_TOOLTIP_ROTATE_RIGHT), + NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_REFLECT_NE_SW), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_REFLECT_NE_SW, STR_CLIPBOARD_TOOLTIP_REFLECT_NE_SW), + NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_CT_REFLECT_NW_SE), + SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CLIPBOARD_REFLECT_NW_SE, STR_CLIPBOARD_TOOLTIP_REFLECT_NW_SE), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + SetFill(0, 1), SetMinimalSize(4, 22), EndContainer(), + + /* HEIGHT MANIPULATOR */ + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(0, 22), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_CT_HEIGHT_DIFF_GLYPH), SetDataTip(STR_EMPTY, STR_NULL), SetFill(0, 1), + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_CT_HEIGHT_DIFF), SetDataTip(STR_CLIPBOARD_HEIGHT_DIFF, STR_NULL), SetFill(0, 1), + NWidget(NWID_VERTICAL), SetPIP(3, 0, 3), + NWidget(NWID_HORIZONTAL), SetPIP(0, 1, 3), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CT_HEIGHT_DIFF_INCREASE), SetDataTip(SPR_ARROW_UP, STR_NULL), SetFill(0, 1), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_CT_HEIGHT_DIFF_DECREASE), SetDataTip(SPR_ARROW_DOWN, STR_NULL), SetFill(0, 1), + EndContainer(), + EndContainer(), + EndContainer(), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _clipboard_toolbar_desc( + WDP_ALIGN_TOOLBAR, "toolbar_clipboard", 0, 0, + WC_BUILD_TOOLBAR, WC_NONE, + WDF_CONSTRUCTION, + _nested_clipboard_toolbar_widgets, lengthof(_nested_clipboard_toolbar_widgets), + &ClipboardToolbarWindow::hotkeys +); + + +/** + * Open the clipboard toolbar to copy and paste map pieces. + * @return newly opened clipboard toolbar, or NULL if the toolbar could not be opened. + */ +Window *ShowClipboardToolbar() +{ + if (!Company::IsValidID(_local_company)) return NULL; + DeleteWindowByClass(WC_BUILD_TOOLBAR); + return new ClipboardToolbarWindow(&_clipboard_toolbar_desc); +} diff --git a/src/clipboard_gui.h b/src/clipboard_gui.h new file mode 100644 index 000000000..4cd232047 --- /dev/null +++ b/src/clipboard_gui.h @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_gui.h GUIs related to the clipboard. */ + +#ifndef CLIPBOARD_GUI_H +#define CLIPBOARD_GUI_H + +#include "tile_type.h" +#include "road_type.h" +#include "track_type.h" + +struct TileContentPastePreview { + bool highlight_tile_rect; ///< Whether to highlight tile borders + TrackBitsByte highlight_track_bits; ///< Rail tracks to highlight +}; + +struct TilePastePreview : TileContentPastePreview { + int tile_height; ///< Destination height of the tile +}; + +void GetTilePastePreview(TileIndex tile, TilePastePreview *ret); + +#endif /* CLIPBOARD_GUI_H */ diff --git a/src/clipboard_type.h b/src/clipboard_type.h new file mode 100644 index 000000000..e3b19803d --- /dev/null +++ b/src/clipboard_type.h @@ -0,0 +1,46 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_type.h Types related to the clipboard. */ + +#ifndef CLIPBOARD_TYPE_H +#define CLIPBOARD_TYPE_H + +#include "airport.h" +#include "newgrf_station.h" +#include "station_type.h" +#include "tilearea_type.h" + +struct ClipboardStation { + struct Spec { + StationClassIDByte stat_class; + byte stat_type; + }; + + struct AirportPart : RawTileArea { + SimpleTinyEnumT type; ///< Airport type + byte layout; ///< Airport layout + }; + + StationID id; ///< ID + AirportPart airport; ///< Airport details + uint8 num_specs; ///< Number of specs in the speclist + Spec *speclist; ///< List of station specs of this station + ClipboardStation *next; ///< "Next" pointer to make a linked list + + static ClipboardStation *Get(StationID id, Map *clipboard); + static ClipboardStation *GetByTile(GenericTileIndex tile); + + ClipboardStation(); + ~ClipboardStation(); +}; + +typedef ClipboardStation *ClipboardStationList; + +#endif /* CLIPBOARD_TYPE_H */ diff --git a/src/command.cpp b/src/command.cpp index 959610cd2..58e7ed610 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -34,6 +34,7 @@ CommandProc CmdBuildRailroadTrack; CommandProc CmdRemoveRailroadTrack; CommandProc CmdBuildSingleRail; +CommandProc CmdBuildSingleRails; CommandProc CmdRemoveSingleRail; CommandProc CmdLandscapeClear; @@ -198,6 +199,10 @@ CommandProc CmdSetTimetableStart; CommandProc CmdOpenCloseAirport; +CommandProc CmdInstantCopyPaste; +CommandProc CmdCopyToClipboard; +CommandProc CmdPasteFromClipboard; + #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} /** @@ -211,6 +216,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdBuildRailroadTrack, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_RAILROAD_TRACK DEF_CMD(CmdRemoveRailroadTrack, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_RAILROAD_TRACK DEF_CMD(CmdBuildSingleRail, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_SINGLE_RAIL + DEF_CMD(CmdBuildSingleRails, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_MULTI_RAIL DEF_CMD(CmdRemoveSingleRail, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SINGLE_RAIL DEF_CMD(CmdLandscapeClear, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_LANDSCAPE_CLEAR DEF_CMD(CmdBuildBridge, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_BRIDGE @@ -354,6 +360,10 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT + + DEF_CMD(CmdCopyToClipboard, CMD_OFFLINE, CMDT_OTHER_MANAGEMENT ), // CMD_COPY_TO_CLIPBOARD + DEF_CMD(CmdPasteFromClipboard,CMD_OFFLINE|CMD_NO_TEST|CMD_AUTO,CMDT_LANDSCAPE_CONSTRUCTION),// CMD_PASTE_FROM_CLIPBOARD + DEF_CMD(CmdInstantCopyPaste, CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_INSTANT_COPY_PASTE }; /*! diff --git a/src/command_func.h b/src/command_func.h index 336947567..de037e68b 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -125,4 +125,7 @@ CommandCallback CcFoundRandomTown; CommandCallback CcBuildPrimaryVehicle; CommandCallback CcStartStopVehicle; +/* clipboard_gui.cpp */ +CommandCallback CcPaste; + #endif /* COMMAND_FUNC_H */ diff --git a/src/command_type.h b/src/command_type.h index f318216ac..091585b4e 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -162,6 +162,21 @@ public: { return !this->success; } + + /** + * Compare equeal. + * + * In case of two successes, money and type of expenses are comapred. + * In case of two failures, error message is comapred. + * + * @param cost the other cost to compare to + * @return whether booth costs are equeal + */ + bool operator == (const CommandCost &cost) const + { + if (!this->success) return !cost.success && this->message == cost.message; + return cost.success && this->expense_type == cost.expense_type && this->cost == cost.cost; + } }; /** @@ -178,6 +193,7 @@ enum Commands { CMD_BUILD_RAILROAD_TRACK, ///< build a rail track CMD_REMOVE_RAILROAD_TRACK, ///< remove a rail track CMD_BUILD_SINGLE_RAIL, ///< build a single rail track + CMD_BUILD_SINGLE_RAILS, ///< build a set of rail tracks on a single tile CMD_REMOVE_SINGLE_RAIL, ///< remove a single rail track CMD_LANDSCAPE_CLEAR, ///< demolish a tile CMD_BUILD_BRIDGE, ///< build a bridge @@ -329,6 +345,10 @@ enum Commands { CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft + CMD_COPY_TO_CLIPBOARD, ///< copy selected tile area into the clipboard + CMD_PASTE_FROM_CLIPBOARD, ///< paste content of the clipboard onto the map + CMD_INSTANT_COPY_PASTE, ///< copy selected tile area and instantly paste it at a given location + CMD_END, ///< Must ALWAYS be on the end of this list!! (period) }; @@ -338,19 +358,20 @@ enum Commands { * This enums defines some flags which can be used for the commands. */ enum DoCommandFlag { - DC_NONE = 0x000, ///< no flag is set - DC_EXEC = 0x001, ///< execute the given command - DC_AUTO = 0x002, ///< don't allow building on structures - DC_QUERY_COST = 0x004, ///< query cost only, don't build. - DC_NO_WATER = 0x008, ///< don't allow building on water - DC_NO_RAIL_OVERLAP = 0x010, ///< don't allow overlap of rails (used in buildrail) - DC_NO_TEST_TOWN_RATING = 0x020, ///< town rating does not disallow you from building - DC_BANKRUPT = 0x040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases - DC_AUTOREPLACE = 0x080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) - DC_NO_CARGO_CAP_CHECK = 0x100, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo - DC_ALL_TILES = 0x200, ///< allow this command also on MP_VOID tiles - DC_NO_MODIFY_TOWN_RATING = 0x400, ///< do not change town rating - DC_FORCE_CLEAR_TILE = 0x800, ///< do not only remove the object on the tile, but also clear any water left on it + DC_NONE = 0x0000, ///< no flag is set + DC_EXEC = 0x0001, ///< execute the given command + DC_AUTO = 0x0002, ///< don't allow building on structures + DC_QUERY_COST = 0x0004, ///< query cost only, don't build. + DC_NO_WATER = 0x0008, ///< don't allow building on water + DC_NO_RAIL_OVERLAP = 0x0010, ///< don't allow overlap of rails (used in buildrail) + DC_NO_TEST_TOWN_RATING = 0x0020, ///< town rating does not disallow you from building + DC_BANKRUPT = 0x0040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases + DC_AUTOREPLACE = 0x0080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) + DC_NO_CARGO_CAP_CHECK = 0x0100, ///< when autoreplace/autorenew is in progress, this shall prevent truncating the amount of cargo in the vehicle to prevent testing the command to remove cargo + DC_ALL_TILES = 0x0200, ///< allow this command also on MP_VOID tiles + DC_NO_MODIFY_TOWN_RATING = 0x0400, ///< do not change town rating + DC_FORCE_CLEAR_TILE = 0x0800, ///< do not only remove the object on the tile, but also clear any water left on it + DC_PASTE = 0x1000, ///< this command is a part of a paste process }; DECLARE_ENUM_AS_BIT_SET(DoCommandFlag) diff --git a/src/copypaste_cmd.cpp b/src/copypaste_cmd.cpp new file mode 100644 index 000000000..5b6acbe00 --- /dev/null +++ b/src/copypaste_cmd.cpp @@ -0,0 +1,689 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_cmd.cpp Helper functions for copy/paste commands. */ + +#include "stdafx.h" +#include "core/alloc_func.hpp" +#include "core/geometry_func.hpp" +#include "network/network.h" +#include "clear_map.h" +#include "clipboard_func.h" +#include "command_func.h" +#include "company_base.h" +#include "company_func.h" +#include "copypaste_cmd.h" +#include "error.h" +#include "gui.h" +#include "rail.h" +#include "rail_map.h" +#include "strings_func.h" +#include "tile_cmd.h" +#include "tilearea_func.h" +#include "tunnelbridge_map.h" +#include + +#include "table/strings.h" + +static const uint INSTANT_COPY_PASTE_BUFFER = NUM_CLIPBOARD_BUFFERS - 1; ///< Index of the buffer reserved for the CmdInstantCopyPaste (temporary buffer) + +PasteCmdHelper *_current_pasting = NULL; ///< State of currently executed paste command +TileIndex _paste_err_tile = INVALID_TILE; ///< Tile where occured error of the last paste command + +/** + * Importance of an error in context of pasting. Bigger value is bigger importance. + * + * Various command errors may be encountered when copy/pasting. The importance decides which one + * to show to the user - it will be one of most important errors, the one that was encountered + * first. Errors with importance PEI_CRITICAL cancel a paste operation e.g. company run out of money. + */ +typedef int PasteErrorImportance; + +static const PasteErrorImportance PEI_CRITICAL = 0x100; ///< Critical paste error + +/** + * Get importance of a certain error message. + * + * @param error_msg The error. + * @return Importance of the error. + * + * @see PasteCmdHelper::DoCommand + * @see PasteCmdHelper::IsInterrupted + */ +static PasteErrorImportance GetPasteErrorImportance(StringID error_msg) +{ + switch (error_msg) { + /* Ignored errors, these will be newer stored as they are less important then the default error. */ + case STR_ERROR_ALREADY_LEVELLED: + case STR_ERROR_ALREADY_BUILT: + return -1; + + /* The default error which is set initially right before copy/pasting. */ + case STR_ERROR_NOTHING_TO_DO: + return 0; + + /* "Can't distant join" must be the least important error among all non-ignored and non-default + * errors. We must be able to reset it to the default one (see AfterPasteingStations). */ + case STR_ERROR_CAN_T_DISTANT_JOIN: + return 1; + + /* Messageless CMD_ERROR, it's not descriptive so it has a very low importance. */ + case INVALID_STRING_ID: + return 2; + + /* Low importance errors */ + case STR_ERROR_MUST_REMOVE_RAILWAY_STATION_FIRST: + case STR_ERROR_BUILDING_MUST_BE_DEMOLISHED: + case STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST: + case STR_ERROR_MUST_REMOVE_ROAD_STOP_FIRST: + case STR_ERROR_MUST_DEMOLISH_DOCK_FIRST: + case STR_ERROR_BUOY_IN_THE_WAY: + return 3; + + /* High importance errors */ + default: + return 4; + + /* Critical errors */ + case STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY: + return PEI_CRITICAL; + } +} + +/** + * Check if the current paste operations is interrupted. + * + * @return True if pasting is interrupted, false if we can continue pasting. + * + * @see PasteErrorImportance + * @see PasteCmdHelper::DoCommand + */ +bool PasteCmdHelper::IsInterrupted() const +{ + return GetPasteErrorImportance(this->err_message) >= PEI_CRITICAL; +} + +/** + * Call a given command as an ingredient of a paste operation. + * + * Costs and possible errors will be aggregated. After return, call PasteCmdHelper::IsInterrupted to + * test if the paste operation is disallowed to be continued. + * + * @param tile The tile to apply the command on. + * @param p1 Additional data for the command. + * @param p2 Additional data for the command. + * @param cmd The command-id to execute (a value of the CMD_* enums) and the error summary message (CMD_MSG). + * @return The cost of this operation or an error. + * + * @pre The command is not flagged with CMD_NO_TEST. + * @pre The type of the command is CMDT_LANDSCAPE_CONSTRUCTION. + * + * @see PasteCmdHelper::IsInterrupted + * @see PasteCmdHelper::CollectCost + * @see PasteCmdHelper::CollectError + */ +void PasteCmdHelper::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) +{ + /* make sure we are still allowed to paste */ + if (this->IsInterrupted()) { + this->last_result = CMD_ERROR; // mark that the command didn't succeed + return; + } + + /* PasteCmdHelper::DoCommand can handle only fully predictable commands, those without + * CMD_NO_TEST flag. Unpredictable command have to be handled separately. */ + assert(!(GetCommandFlags(cmd) & CMD_NO_TEST)); + + /* ignore some of the given flags, instead use those from the command proc table */ + DoCommandFlag flags = this->dc_flags; + flags &= ~DC_AUTO & ~DC_NO_WATER & ~DC_ALL_TILES; + flags |= CommandFlagsToDCFlags(GetCommandFlags(cmd)); + + /* use given error message or the default one */ + StringID summary_error_msg = GB(cmd, 16, 16); + if (summary_error_msg == 0) summary_error_msg = STR_ERROR_CAN_T_PASTE_HERE; + + /* test the command, output is the return value */ + CommandCost ret = ::DoCommand(tile, p1, p2, flags & ~DC_EXEC, cmd); + + /* apply if exec'ing */ + if (ret.Succeeded() && (flags & DC_EXEC)) { + /* check if there is enough money */ + if (ret.GetCost() > 0 && this->GetAvailableMoney() < ret.GetCost()) { + SetDParam(0, ret.GetCost()); + ret = CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + } else { + CommandCost ret2 = ::DoCommand(tile, p1, p2, flags, cmd); + assert(ret == ret2); + } + } + + /* aggregate costs */ + this->CollectCost(ret, tile, summary_error_msg); +} + +/** + * Aggreagate paste command costs without calling PasteCmdHelper::DoCommand. + * + * The function works similarly to the PasteCmdHelper::DoCommand but doesn't actually execute any + * commands, it just collects a given result. + * + * When collecting a success, cost must be of type EXPENSES_CONSTRUCTION. A success also makes + * STR_ERROR_NOTHING_TO_DO no more applies (we "did" something). + * + * Call PasteCmdHelper::IsInterrupted to test whether the paste operation can be continued. + * + * @param cost The return value of the command, a cost or an error. + * @param tile The tile the error concerns. + * @param error_message Summary message of the error. + * + * @pre The company has enough money if DC_EXEC'ing. + * + * @see PasteCmdHelper::IsInterrupted + * @see PasteCmdHelper::CollectError + * @see PasteCmdHelper::DoCommand + */ +void PasteCmdHelper::CollectCost(const CommandCost &cost, TileIndex tile, StringID error_summary) +{ + if (cost.Succeeded()) { + assert(!this->IsInterrupted()); + /* Currently only EXPENSES_CONSTRUCTION expenses are allowed when copy/pasting. If this + * is not sufficient, some upgrade will be required. To allow proper update of finacial + * statistics, the overal cost of paste operation will have to be stored separatly for + * each supported type of expenses. */ + assert(cost.GetExpensesType() == EXPENSES_CONSTRUCTION); + + /* make sure we are not expending too much */ + assert(!(this->dc_flags & DC_EXEC) || cost.GetCost() <= 0 || this->GetAvailableMoney() >= 0); + + this->had_success = true; // mark that we had a succes and STR_ERROR_NOTHING_TO_DO no more applies + this->overal_cost += cost.GetCost(); + this->last_result = cost; + } else { + this->CollectError(tile, cost.GetErrorMessage(), error_summary); + } +} + +/** + * Collect a paste error without calling PasteCmdHelper::DoCommand or PasteCmdHelper::CollectCost. + * + * The function works similary to PasteCmdHelper::DoCommand and PasteCmdHelper::CollectCost, + * but it only generates an error. After return, call PasteCmdHelper::IsInterrupted to test whether + * the paste operation is allowd to be continued. + * + * @param tile The tile the error concerns. + * @param error_message Error message. + * @param error_message Summary error message. + * + * @see PasteCmdHelper::IsInterrupted + * @see PasteCmdHelper::CollectCost + * @see PasteCmdHelper::DoCommand + */ +void PasteCmdHelper::CollectError(TileIndex tile, StringID error_message, StringID error_summary) +{ + /* store the error only if it is more important then the previous one */ + if (GetPasteErrorImportance(error_message) > GetPasteErrorImportance(this->err_message)) { + this->err_tile = IsValidTile(tile) ? tile : INVALID_TILE; + this->err_message = error_message; + this->err_summary = error_summary; + CopyOutDParam(this->err_params, 0, lengthof(this->err_params)); + } + + this->last_result = CommandCost(error_message); +} + +/** + * Calculate how far tiles can be altered beyond a given paste area bound. + * + * When pasting, some tiles around the paste area may be altered (during terraforming). + * The function return the limit on how far it can happen. Calculations are not exact, + * the goal is to give a safe range that will include any possible case. + * + * Result is based on current and desired heights at neighbour corners of the paste area. + * + * @param curr_h1 Current height on the first corner. + * @param curr_h2 Current height on the second corner. + * @param new_h1 Desired height on the first corner. + * @param new_h2 Desired height on the second corner. + * @param length Distance (in tiles) between corners. + * @return How far (in tiles) terraforming can reach beyond the given bound. + * + * @pre Tile heights and the length can't create an impossible layout, heights can't differ + * too much: \n + * Delta(curr_h1, curr_h2) <= length \n + * Delta(new_h1, new_h2) <= length \n + * + * @see CopyPasteAreasMayColide + */ +static uint CalcMaxPasteRange(uint curr_h1, uint new_h1, uint curr_h2, uint new_h2, uint length) +{ + uint min_curr_h = CeilDiv(max(curr_h1 + curr_h2 - length, 0), 2); + uint max_curr_h = min((curr_h1 + curr_h2 + length) / 2, MAX_TILE_HEIGHT); + uint min_new_h = CeilDiv(max(new_h1 + new_h2 - length, 0), 2); + uint max_new_h = min((new_h1 + new_h2 + length) / 2, MAX_TILE_HEIGHT); + + return max(Delta(max_new_h, min_curr_h), Delta(max_curr_h, min_new_h)); +} + +/** + * Test if this is safe to copy and paste contents of the map instantly, without + * using an intermediate buffer. + * + * If the copy and the paste areas are close enough (especially when they intersect), + * sequential copy-pasting may alter at some point of time those tile of the copy + * area which hasn't been copied yet. In this case, further copy-pasting will read + * modified values, not the original and this is somthing we don't want to happen. + * We can deal with it by firstly copying all the content to the clipboard buffer and + * then pasting it onto the map. This function tels us whether we should use the + * clipboard as an intermediate buffer because there may happen such a colision. + * + * @param copy_paste What, where and how we are copying. + * @return \c true if intermediate buffer might be required, \c false if it's surely not required + * + * @pre booth the source area and the destination area are on the main map + * + * @see CalcMaxPasteRange + */ +static bool CopyPasteAreasMayColide(const CopyPasteParams ©_paste) +{ + /* No need to check surroundings if we are not terraforming. Just test for content intersection. */ + if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_NONE) return copy_paste.src_area.Intersects(copy_paste.dst_area); + + /* As we are interested in tile heights, increase areas to include all tile + * corners, also these at SW and SE borders. */ + TileArea src_corner_area(AsMainMapTile(copy_paste.src_area.tile), copy_paste.src_area.w + 1, copy_paste.src_area.h + 1); + TileArea dst_corner_area(AsMainMapTile(copy_paste.dst_area.tile), copy_paste.dst_area.w + 1, copy_paste.dst_area.h + 1); + + DirTransformation inv_transformation = InvertDirTransform(copy_paste.transformation); + /* source of the destination area most northern tile corner */ + TileIndex source_of_north = dst_corner_area.TransformedNorth(src_corner_area.tile, inv_transformation); + + /* calculate current and new heights on destination area corners */ + /* N */ + TileIndex dst_corner = dst_corner_area.tile; + TileIndex src_corner = source_of_north; + uint curr_n = TileHeight(dst_corner); + uint new_n = TileHeight(src_corner) + copy_paste.height_delta; + /* W */ + dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, 0); + src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); + uint curr_w = TileHeight(dst_corner); + uint new_w = TileHeight(src_corner) + copy_paste.height_delta; + /* S */ + dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, dst_corner_area.h); + src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); + uint curr_s = TileHeight(dst_corner); + uint new_s = TileHeight(src_corner) + copy_paste.height_delta; + /* E */ + dst_corner = TILE_ADDXY(dst_corner_area.tile, 0, dst_corner_area.h); + src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation); + uint curr_e = TileHeight(dst_corner); + uint new_e = TileHeight(src_corner) + copy_paste.height_delta; + + /* calculate how far tiles can be altered beyon the paste area (safe approximation) */ + uint range_ne = CalcMaxPasteRange(curr_n, new_n, curr_e, new_e, dst_corner_area.h - 1); + uint range_sw = CalcMaxPasteRange(curr_s, new_s, curr_w, new_w, dst_corner_area.h - 1); + uint range_nw = CalcMaxPasteRange(curr_n, new_n, curr_w, new_w, dst_corner_area.w - 1); + uint range_se = CalcMaxPasteRange(curr_s, new_s, curr_e, new_e, dst_corner_area.w - 1); + + /* calculate the exact area which may be altered by the paste process */ + uint x = TileX(dst_corner_area.tile); + uint y = TileY(dst_corner_area.tile); + range_ne = max(range_ne, x); // cut the area at the NE border (don't let x to go below 0) + range_nw = max(range_nw, y); // cut the area at the NW border (don't let y to go below 0) + TileArea forbidden_area( + TileXY(x - range_ne, y - range_nw), + dst_corner_area.w + range_ne + range_sw, + dst_corner_area.h + range_nw + range_se); + + /* test if the source area is within the paste range */ + return src_corner_area.Intersects(forbidden_area); +} + +/** + * Calculate how much to add to each height of a tile while copy-pasteing. + * @param src_area Area we are copying from. + * @param dst_area Area we are pasting at. + * @param transformation Transformation to perform. + * @param additional_height Extra amount of units to add. + */ +static inline int CalcCopyPasteHeightDelta(const GenericTileArea &src_area, const GenericTileArea &dst_area, DirTransformation transformation, int additional_height) +{ + GenericTileArea dst_corners(dst_area.tile, dst_area.w + 1, dst_area.h + 1); + GenericTileIndex source_of_north = dst_corners.TransformedNorth(src_area.tile, InvertDirTransform(transformation)); + return TileHeight(dst_corners.tile) - TileHeight(source_of_north) + additional_height; +} + +/** + * Do a sequential copy-pasting by calling appropriate CopyPasteCommandProc on each selected tile. + * @param copy_paste What, where and how we are copying. + */ +static inline void DoCopyPaste(const CopyPasteParams ©_paste) +{ + /* Copying to the clipboard buffer should always success. + * Some content may be intransformable (e.g. airport) so we can't use any transformation. */ + assert(IsMainMapTile(copy_paste.dst_area.tile) || (copy_paste.transformation == DTR_IDENTITY && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_FULL)); + + if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_FULL) { + CopyPasteHeights(copy_paste.src_area, copy_paste.dst_area.tile, copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + } + + for (TransformationTileIteratorT ti(copy_paste.src_area, copy_paste.src_area.TransformedNorth(copy_paste.dst_area.tile, copy_paste.transformation), copy_paste.transformation); IsValidTileIndex(ti); ++ti) { + CopyPasteTileProc *proc = _tile_type_procs[GetTileType(ti.SrcTile())]->copy_paste_tile_proc; + if (proc == NULL) continue; + proc(ti.SrcTile(), ti.DstTile(), copy_paste); + if (IsPastingInterrupted()) break; + } + + if (IsMainMapTile(copy_paste.dst_area.tile)) { + AfterPastingStations(copy_paste); + } else { + AfterCopyingStations(copy_paste); + } +} + +/** + * Test if a given TileArea is valid. + * @return \c true if the area is non-empty and fits inside the map, \c false otherwise. + */ +static CommandCost ValParamCopyPasteArea(const GenericTileArea &ta) +{ + if (!IsValidTile(ta.tile) || + !IsInsideBS(ta.w, 1, _settings_game.construction.clipboard_capacity) || + !IsInsideBS(ta.h, 1, _settings_game.construction.clipboard_capacity)) { + return CMD_ERROR; + } + + if (TileX(ta.tile) + ta.w > MapMaxX(MapOf(ta.tile)) || + TileY(ta.tile) + ta.h > MapMaxY(MapOf(ta.tile))) { + return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP_SUB); + } + + return CommandCost(); +} + +/** + * Test if a CopyPasteMode is valid. + * @return \c true if does, \c false otherwise + */ +static bool ValParamCopyPasteMode(CopyPasteMode mode) +{ + if (mode & ~CPM_MASK) return false; + mode &= CPM_TERRAFORM_MASK; + return mode == CPM_TERRAFORM_NONE || mode == CPM_TERRAFORM_MINIMAL || mode == CPM_TERRAFORM_FULL; +} + +/** + * Copy content of a given tile area into the clipboard buffer. + * @param buffer Clipboard buffer to copy into + * @param ta The area to copy + */ +static void CopyToClipboard(Map *buffer, const TileArea &ta) +{ + AllocateClipboardBuffer(buffer, ta.w, ta.h); + + CopyPasteParams copy_paste = { + GenericTileArea(ta), // src_area + GenericTileArea(TileXY(0, 0, buffer), ta.w, ta.h), // dst_area + CPM_COPY, // mode + INVALID_RAILTYPE, // railtype + DTR_IDENTITY, // transformation + 0 // height_delta + }; + + DoCopyPaste(copy_paste); +} + +/** + * Begin a paste process. + * @param flags Flags for the command. + * @param params What, where and how to paste. + */ +static void InitializePasting(DoCommandFlag flags, const CopyPasteParams ¶ms) +{ + assert(_current_pasting == NULL); + _current_pasting = new PasteCmdHelper; + + _current_pasting->dc_flags = flags | DC_PASTE; + _current_pasting->overal_cost = 0; + _current_pasting->had_success = false; + _current_pasting->err_summary = STR_ERROR_CAN_T_PASTE_HERE; + _current_pasting->err_message = STR_ERROR_NOTHING_TO_DO; + MemSetT(_current_pasting->err_params, 0, lengthof(_current_pasting->err_params)); + _current_pasting->err_tile = INVALID_TILE; + _current_pasting->last_result = CommandCost(STR_ERROR_NOTHING_TO_DO); +} + +/** + * Finish a paste process. + * @return Total cost. + */ +static CommandCost FinalizePasting() +{ + /* Set error string parameters */ + CopyInDParam(0, _current_pasting->err_params, lengthof(_current_pasting->err_params)); + /* Set error summary message (see COPY_PASTE_ERR_SUMMARY_PARAM for details). */ + SetDParam(COPY_PASTE_ERR_SUMMARY_PARAM, _current_pasting->err_summary); + /* Store the error tile so the GUI (CcPaste) can highlight it. */ + _paste_err_tile = _current_pasting->err_tile; + + CommandCost ret; + if (_current_pasting->had_success) { + /* Return overal cost of the operation */ + ret = CommandCost(EXPENSES_CONSTRUCTION, _current_pasting->overal_cost); + /* Here we are about to return a success. However, there could occured some meaningful + * errors (those except "already built", "already leveled" etc.) and we should inform + * the user that not everything went right. Show the message now. */ + if ((_current_pasting->dc_flags & DC_EXEC) && IsLocalCompany() && GetPasteErrorImportance(_current_pasting->err_message) > GetPasteErrorImportance(STR_ERROR_NOTHING_TO_DO)) { + ShowErrorMessage(_current_pasting->err_summary, _current_pasting->err_message, WL_INFO); + } else { + /* If we are not showing error message then clear the error tile to prevent GUI + * (CcPaste) from higlighting it. */ + _paste_err_tile = INVALID_TILE; + } + } else { + /* Return an error if we didn't have any success. */ + ret = CommandCost(_current_pasting->err_message); + } + + /* cleanup */ + delete _current_pasting; + _current_pasting = NULL; + + return ret; +} + +/** + * Paste onto the main map content of a clipboard buffer. + * @param buffer The buffer. + * @param tile Tile where to paste (most northern). + * @param flags Flags for the command. + * @param mode Copy-paste mode. + * @param transformation Transformation to perform. + * @param railtype #RailType to convert to. + * @param additional_height_delta Additional amount of units to add to each tile height. + * @return Total cost. + */ +static CommandCost PasteFromClipboard(Map *buffer, TileIndex tile, DoCommandFlag flags, CopyPasteMode mode, DirTransformation transformation, RailType railtype, int additional_height_delta) +{ + assert(!IsClipboardBufferEmpty(buffer)); + + CopyPasteParams copy_paste; + + /* calculate and validate copy/paste area */ + copy_paste.src_area = GenericTileArea(TileXY(0, 0, buffer), MapMaxX(buffer), MapMaxY(buffer)); + copy_paste.dst_area = TransformTileArea(copy_paste.src_area, GenericTileIndex(tile), transformation); + CommandCost ret = ValParamCopyPasteArea(copy_paste.dst_area); + if (ret.Failed()) return ret; + + copy_paste.mode = mode; + copy_paste.railtype = railtype; + copy_paste.transformation = transformation; + copy_paste.height_delta = CalcCopyPasteHeightDelta(copy_paste.src_area, copy_paste.dst_area, transformation, additional_height_delta); + + /* do sequential copy-pasting */ + InitializePasting(flags, copy_paste); + DoCopyPaste(copy_paste); + return FinalizePasting(); +} + +/** + * Copy tile area into clipboard. + * + * @param tile Northern tile of the area to copy. + * @param flags Command flags. + * @param p1 Various bits: + * \li bits 0..1 [2] - clipboard buffer index + * \li bits 2..31 [30] - [ unused ] + * @param p2 Various bits: + * \li bits 0..5 [6] - width of area to copy + * \li bits 6..11 [6] - height of area to copy + * \li bits 12..31 [20] - [ unused ] + * @param text Unused. + * @return The cost of this operation or an error. + */ +CommandCost CmdCopyToClipboard(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + /* clipboard is available only in a sigle player game and only to the local company */ + if (_networking || !IsLocalCompany()) return CMD_ERROR; + + /* extract and validate clipboard buffer index */ + uint index = GB(p1, 0, 2); + if (index >= NUM_CLIPBOARD_BUFFERS || index == INSTANT_COPY_PASTE_BUFFER) return CMD_ERROR; + + /* calculate and validate source area */ + TileArea src_area(tile, GB(p2, 0, 6), GB(p2, 6, 6)); + CommandCost ret = ValParamCopyPasteArea(src_area); + if (ret.Failed()) return ret; + + /* copy to clipboard only when executing (DC_EXEC) */ + if (flags & DC_EXEC) CopyToClipboard(GetClipboardBuffer(index), src_area); + + /* return the cost */ + return CommandCost(INVALID_EXPENSES, 0); +} + +/** + * Paste clipboard contents onto the map. + * + * @param tile Tile where to paste (northern). + * @param flags Command flags. + * @param p1 Various bits: + * \li bits 0..1 [2] - clipboard buffer index + * \li bits 2..27 [26] - [ unused ] + * \li bits 28..31 [4] - rail type (RailType) to convert to, ignored if CPM_CONVERT_RAILTYPE mode is off + * @param p2 Various bits: + * \li bits 0..11 [12] - [ unused ] + * \li bits 12..15 [4] - additional amount of tile heights to add to each tile (-8..7) + * \li bits 16..18 [3] - transformation to perform (DirTransformation) + * \li bits 19..28 [10] - mode (CopyPasteMode) + * \li bits 29..31 [3] - [ unused ] + * @param text Unused. + * @return The cost of this operation or an error. + */ +CommandCost CmdPasteFromClipboard(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + /* extract and validate clipboard buffer index */ + uint index = GB(p1, 0, 2); + if (index >= NUM_CLIPBOARD_BUFFERS || index == INSTANT_COPY_PASTE_BUFFER) return CMD_ERROR; + + /* clipboard is available only in a sigle player game and only to the local company */ + if (_networking || !IsLocalCompany() || IsClipboardBufferEmpty(GetClipboardBuffer(index))) return CMD_ERROR; + + /* extract and validate copy/paste mode */ + CopyPasteMode mode = (CopyPasteMode)GB(p2, 19, 10); + if (!ValParamCopyPasteMode(mode)) return CMD_ERROR; + + /* extract and validate rail type */ + RailType railtype = (RailType)GB(p1, 28, 4); + if (!ValParamRailtype(railtype)) return CMD_ERROR; + + /* extract transformation and additional height delta */ + DirTransformation transformation = (DirTransformation)GB(p2, 16, 3); + int additional_height_delta = GB(p2, 12, 4); + additional_height_delta |= -(additional_height_delta & (1 << 3)); // propagate sign bit + + return PasteFromClipboard(GetClipboardBuffer(index), tile, flags, mode, transformation, railtype, additional_height_delta); +} + +/** + * Copy a piece of map and instantly paste at given location. + * + * @param tile Tile where to paste (northern). + * @param flags Command flags. + * @param p1 Various bits: + * \li bits 0..27 [28] - northern tile of the source area + * \li bits 28..31 [4] - rail type (RailType) to convert to, ignored if CPM_CONVERT_RAILTYPE mode is off + * @param p2 Various bits: + * \li bits 0..5 [6] - source area width + * \li bits 6..11 [6] - source area height + * \li bits 12..15 [4] - additional amount of tile heights to add to each tile (-8..7) + * \li bits 16..18 [3] - transformation to perform (DirTransformation) + * \li bits 19..28 [10] - mode (CopyPasteMode) + * \li bits 29..31 [3] - [ unused ] + * @param text Unused. + * @return The cost of this operation or an error. + */ +CommandCost CmdInstantCopyPaste(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + CopyPasteParams copy_paste; + + /* extract and validate source area */ + copy_paste.src_area.tile = GenericTileIndex(TileIndex(GB(p1, 0, 28))); + copy_paste.src_area.w = GB(p2, 0, 6); + copy_paste.src_area.h = GB(p2, 6, 6); + CommandCost ret = ValParamCopyPasteArea(copy_paste.src_area); + if (ret.Failed()) return ret; + + /* calculate and validate destination area */ + copy_paste.dst_area = TransformTileArea(copy_paste.src_area, GenericTileIndex(tile), copy_paste.transformation); + ret = ValParamCopyPasteArea(copy_paste.dst_area); + if (ret.Failed()) return ret; + + /* extract and validate copy/paste mode */ + copy_paste.mode = (CopyPasteMode)GB(p2, 19, 10); + if (!ValParamCopyPasteMode(copy_paste.mode)) return CMD_ERROR; + + /* extract and validate rail type */ + copy_paste.railtype = (RailType)GB(p1, 28, 4); + if (!ValParamRailtype(copy_paste.railtype)) return CMD_ERROR; + + /* extract transformation */ + copy_paste.transformation = (DirTransformation)GB(p2, 16, 3); + + /* extract the additional number of height units */ + int additional_height_delta = GB(p2, 12, 4); // this is a 4-bit SIGNED integer (-8..7) + additional_height_delta |= -(additional_height_delta & (1 << 3)); // propagate the sign bit + + /* calculate the height */ + copy_paste.height_delta = CalcCopyPasteHeightDelta(copy_paste.src_area, copy_paste.dst_area, copy_paste.transformation, additional_height_delta); + + /* when copy and paste areas are too close each other, firstly + * copy to the clipboard and then from the clipboard to the map */ + if (CopyPasteAreasMayColide(copy_paste)) { + Map *clipboard = GetClipboardBuffer(INSTANT_COPY_PASTE_BUFFER); + /* Copy to a buffer, but only in the first stage of the command. + * In a single player game and also while we are a server, the first one is non-DC_EXEC + * stage (which is fallowed then by a DC_EXEC stage). When we are a client, there is only + * one stage which is either a single non-DC_EXEC stage (shift pressed), or a single DC_EXEC + * stage (command comming from the network). */ + if ((_networking && !_network_server) || !(flags & DC_EXEC)) { + CopyToClipboard(clipboard, copy_paste.src_area); + } + /* paste from the clipboard */ + ret = PasteFromClipboard(clipboard, tile, flags, copy_paste.mode, copy_paste.transformation, copy_paste.railtype, additional_height_delta); + } else { + /* copy/paste directly */ + InitializePasting(flags, copy_paste); + DoCopyPaste(copy_paste); + ret = FinalizePasting(); + } + return ret; +} diff --git a/src/copypaste_cmd.h b/src/copypaste_cmd.h new file mode 100644 index 000000000..66e8cf596 --- /dev/null +++ b/src/copypaste_cmd.h @@ -0,0 +1,116 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file copypaste_cmd.h Helper functions for copy/paste commands. */ + +#ifndef COPYPASTE_CMD_H +#define COPYPASTE_CMD_H + +#include "core/enum_type.hpp" +#include "command_func.h" +#include "tilearea_type.h" +#include "station_map.h" +#include "newgrf_station.h" + +#include "table/strings.h" + +/** Pasting modifiers. */ +enum CopyPasteMode { + CPM_WITH_RAIL_TRANSPORT = 1 << 0, ///< copy-paste rail transport infrastructure + CPM_WITH_ROAD_TRANSPORT = 1 << 1, ///< copy-paste road transport infrastructure + CPM_WITH_WATER_TRANSPORT = 1 << 2, ///< copy-paste water transport infrastructure + CPM_WITH_AIR_TRANSPORT = 1 << 3, ///< copy-paste air transport infrastructure + CPM_ALL_TRANSPORT_MASK = 0xF << 0, ///< bitmask with all transport types + + CPM_TERRAFORM_NONE = 0 << 4, ///< do not alter tile heights + CPM_TERRAFORM_MINIMAL = 1 << 4, ///< terraform as less as possible to paste all objects at right heights + CPM_TERRAFORM_FULL = 2 << 4, ///< copy-paste all tile heights + CPM_TERRAFORM_MASK = 0x3 << 4, ///< bitmask to extract terraforming modes + + CPM_CONVERT_RAILTYPE = 1 << 6, ///< convert rails to a given rail type + CPM_MIRROR_SIGNALS = 1 << 7, ///< mirror signals direction + CPM_UPGRADE_BRIDGES = 1 << 8, ///< upgrade bridge types to fastes possible + CPM_WITH_STATIONS = 1 << 9, ///< also copy and pase stations and waypoints + CPM_FLAGS_MASK = 0xF << 6, ///< bitmask to mask all flag-like bits + + CPM_NONE = 0, ///< empty set of modes + CPM_MASK = CPM_ALL_TRANSPORT_MASK | CPM_FLAGS_MASK | CPM_TERRAFORM_MASK, ///< all possible bits + CPM_DEFAULT = CPM_ALL_TRANSPORT_MASK | CPM_TERRAFORM_MINIMAL | CPM_WITH_STATIONS, ///< defult paste mode + CPM_COPY = CPM_ALL_TRANSPORT_MASK | CPM_TERRAFORM_FULL | CPM_WITH_STATIONS, ///< mode used when copying to the clipboard +}; +DECLARE_ENUM_AS_BIT_SET(CopyPasteMode) + +/** Land leveling type used in e.g. #LevelPasteLand. */ +enum CopyPasteLevelVariant { + CPLV_LEVEL_ABOVE, ///< Lower the land until a given height reached. + CPLV_LEVEL_BELOW, ///< Raise the land until a given height reached. +}; + +/** Parameters of a copy/paste command. */ +struct CopyPasteParams { + GenericTileArea src_area; ///< The area we are copying from + GenericTileArea dst_area; ///< The area we are pasting at + CopyPasteMode mode; ///< Various flags telling what to copy and how to paste + RailType railtype; ///< Convert all rails to a given rail type (only in #CPM_CONVERT_RAILTYPE mode) + DirTransformation transformation; ///< Transformation to perform on the content while copy-pasting + int height_delta; ///< Amount of units to add to the height of each tile (appropriate terraforming mode must be set e.g. #CPM_TERRAFORM_FULL) +}; + +/** Summary error message for copy/paste command may vary depending on encountered errors. + * While firing copy/paste command the summary messsage given with CMD_MSG is expected to + * be STR_COPY_PASTE_ERROR_SUMMARY (which is "{8:STRING}") so a true message can be set + * later through param #8. The constant below is the index of the param. */ +static const int COPY_PASTE_ERR_SUMMARY_PARAM = 8; + +/** Executes commands and gathers results of a paste process. */ +struct PasteCmdHelper { + DoCommandFlag dc_flags; ///< Flags to use when executing commands + Money overal_cost; ///< Overal cost of currently executed paste command. + CommandCost last_result; ///< Result of the most recent PasteCmdHelper::DoCommand / PasteCmdHelper::CollectCost / PasteCmdHelper::CollectError. + bool had_success; ///< If currently executed paste command had a successful action (at least once). + StringID err_summary; ///< Summary message of the paste error. + StringID err_message; ///< Detailed message of the paste error. + TileIndex err_tile; ///< Tile where the last paste error occured. + uint64 err_params[COPY_PASTE_ERR_SUMMARY_PARAM]; ///< Parameters for the paste error + + void DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd); + void CollectCost(const CommandCost &cost, TileIndex tile, StringID error_summary = STR_ERROR_CAN_T_PASTE_HERE); + void CollectError(TileIndex tile, StringID error_message, StringID error_summary = STR_ERROR_CAN_T_PASTE_HERE); + bool IsInterrupted() const; + + inline Money GetAvailableMoney() const + { + return GetAvailableMoneyForCommand() - this->overal_cost; + } +}; + +extern PasteCmdHelper *_current_pasting; +extern TileIndex _paste_err_tile; + +/** + * Check if it is allowed to continue pasting. + * @return True if pasting is interrupted, false otherwise + */ +static inline bool IsPastingInterrupted() +{ + return _current_pasting != NULL && _current_pasting->IsInterrupted(); +} + +void LevelPasteLand(const TileArea &ta, uint height, CopyPasteLevelVariant variant); +void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta); + +void CopyPastePlaceTracks(GenericTileIndex tile, RailType railtype, TrackBits tracks); +void CopyPastePlaceCannal(GenericTileIndex tile); +void CopyPastePlaceRailWaypoint(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent); +void CopyPastePlaceBuoy(GenericTileIndex tile, StationID sid, WaterClass wc); + +void AfterCopyingStations(const CopyPasteParams ©_paste); +void AfterPastingStations(const CopyPasteParams ©_paste); + +#endif /* COPYPASTE_CMD_H */ diff --git a/src/core/bitmath_func.hpp b/src/core/bitmath_func.hpp index 31e679b00..af8015b96 100644 --- a/src/core/bitmath_func.hpp +++ b/src/core/bitmath_func.hpp @@ -321,6 +321,39 @@ static inline T ROR(const T x, const uint8 n) } /** + * Swap odd and even bits in a variable. + * + * Bit 0 is swapped with bit 1, bit 2 with bit 3 and so on. + * + * @param x The value in which we want to swap bits + * @return The bit swapped value + */ +template +static inline T SwapOddEvenBits(T x) +{ + return (T)(((x >> 1) & (T)0x5555555555555555LL) | ((x & (T)0x5555555555555555LL) << 1)); +} + +/** + * Interleave bits (a.k.a. Morton code) of two 8-bit integers \c x and \c y. + * + * The result is computed by putting consecutive \c x bits at even positions and consecutive \c y + * bits at odd positions: + * + * (X7 X6 ... X1 X0, Y7 Y6 ... Y1 Y0) -> Y7 X7 Y6 X6 ... Y1 X1 Y0 X0 + * + * @param x The bits to put at even positions. + * @param y The bits to put at odd positions. + * @return The interleaved value. + */ +static inline uint16 InterleaveBits8(uint8 x, uint8 y) +{ + /* Found on the "Bit Twiddling Hacks" webpage: http://graphics.stanford.edu/~seander/bithacks.html */ + return (((x * 0x0101010101010101ULL & 0x8040201008040201ULL) * 0x0102040810204081ULL >> 49) & 0x5555) | + (((y * 0x0101010101010101ULL & 0x8040201008040201ULL) * 0x0102040810204081ULL >> 48) & 0xAAAA); +} + +/** * Do an operation for each set bit in a value. * * This macros is used to do an operation for each set diff --git a/src/core/geometry_func.cpp b/src/core/geometry_func.cpp index 86f317a37..9fc1465d4 100644 --- a/src/core/geometry_func.cpp +++ b/src/core/geometry_func.cpp @@ -12,6 +12,7 @@ #include "../stdafx.h" #include "geometry_func.hpp" #include "math_func.hpp" +#include "../map_func.h" #include "../safeguards.h" @@ -28,3 +29,24 @@ Dimension maxdim(const Dimension &d1, const Dimension &d2) d.height = max(d1.height, d2.height); return d; } + +/** + * Transform a given Point. + * + * The center point of the transformation is (0, 0). + * For example, point (1, 2) rotated 90 degree left is (-2, 1). + * + * @param point The Point to transform. + * @param transformation Transformation to perform. + * @return The transformed Point. + */ +Point TransformPoint(Point point, DirTransformation transformation) +{ + TileIndexDiffC diff_from_x = TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, transformation)); + TileIndexDiffC diff_from_y = TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SE, transformation)); + Point ret = { + point.x * diff_from_x.x + point.y * diff_from_y.x, + point.x * diff_from_x.y + point.y * diff_from_y.y + }; + return ret; +} diff --git a/src/core/geometry_func.hpp b/src/core/geometry_func.hpp index e7c53251c..8891e1141 100644 --- a/src/core/geometry_func.hpp +++ b/src/core/geometry_func.hpp @@ -13,7 +13,26 @@ #define GEOMETRY_FUNC_HPP #include "geometry_type.hpp" +#include "math_func.hpp" +#include "../direction_func.h" Dimension maxdim(const Dimension &d1, const Dimension &d2); +/** + * Transform a given Dimension. + * + * The width and the height will be swapped or will stay unchanged based on used transformation. + * + * @param dim The Dimension to transform. + * @param transformation Transformation to perform. + * @return The transformed Dimension. + */ +static inline Dimension TransformDimension(Dimension dim, DirTransformation transformation) +{ + if (TransformAxis(AXIS_X, transformation) != AXIS_X) Swap(dim.width, dim.height); + return dim; +} + +Point TransformPoint(Point point, DirTransformation transformation); + #endif /* GEOMETRY_FUNC_HPP */ diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp index df9142462..cb6c98983 100644 --- a/src/core/math_func.hpp +++ b/src/core/math_func.hpp @@ -86,6 +86,18 @@ static inline T abs(const T a) } /** + * Returns the sign of a value. + * + * @param x The value + * @return -1 if the x is negative, +1 if positive, 0 otherwise + */ +template +static inline int sgn(const T x) +{ + return (int)(x > (T)0) - (int)(x < (T)0); +} + +/** * Return the smallest multiple of n equal or greater than x * * @note n must be a power of 2 diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 53a85e442..c441321c2 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -380,7 +380,7 @@ bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const { /* If the map array doesn't exist, saving will fail too. If the map got * initialised, there is a big chance the rest is initialised too. */ - if (_m == NULL) return false; + if (_main_map.m == NULL) return false; try { GamelogEmergency(); diff --git a/src/depot_map.h b/src/depot_map.h index c304790f8..97012c172 100644 --- a/src/depot_map.h +++ b/src/depot_map.h @@ -55,7 +55,7 @@ static inline DepotID GetDepotIndex(TileIndex t) { /* Hangars don't have a Depot class, thus store no DepotID. */ assert(IsRailDepotTile(t) || IsRoadDepotTile(t) || IsShipDepotTile(t)); - return _m[t].m2; + return GetTile(t)->m2; } /** diff --git a/src/direction_func.h b/src/direction_func.h index 12aee5863..327c29871 100644 --- a/src/direction_func.h +++ b/src/direction_func.h @@ -13,6 +13,7 @@ #define DIRECTION_FUNC_H #include "direction_type.h" +#include "core/math_func.hpp" /** * Checks if an integer value is a valid DiagDirection @@ -278,4 +279,118 @@ static inline bool IsDiagonalDirection(Direction dir) return (dir & 1) != 0; } +/** + * Checks if an integer value is a valid DirTransformation. + * + * @param transformation The value to check. + * @return True if the value belongs to a DirTransformation, false otherwise. + */ +static inline bool IsValidDirTransform(DirTransformation transformation) +{ + return IsInsideMM(transformation, DTR_BEGIN, DTR_END); +} + +/** + * Combine two direction transformations into one. + * @param a First transformation. + * @param b Second transformation. + * @return Transformation that works like firstly applying the 'a' transformation and then the 'b' transformation. + */ +static inline DirTransformation CombineDirTransform(DirTransformation a, DirTransformation b) +{ + /* DirTransformation bit lauout: + * 0000irrr + * where: + * i - DTR_REFLECTION_BIT + * rrr - DTR_ROTATION_MASK + * + * DirTransformation transformation can be expressed as a function of an angle: + * f(x) = I * x + R + * where + * x - direction expressed in angle units (e.g. DiagDir) + * I - reflection, -1 to reflect before rotating (DTR_REFLECTION_BIT set), +1 otherwise + * R - rotation, number of angle units to add (bits of mask DTR_ROTATION_MASK) + * + * 1 angle unit is 90 degree. As we work on angles we must use modular arithmetic for + * calculations. Modulus is 4 because 360 degree is 4 our angle units. To apply + * modulus we can simply bitmask result with DTR_ROTATION_MASK. + * + * To combine two transformations + * a(x) = IA * x + RA + * b(x) = IB * x + RB + * into one + * c(x) = IC * x + RC + * we must compose functions + * c(x) = b(a(x)) = IB * (IA * x + RA) + RB = IA * IB * x + IB * RA + RB + * From above + * IC = IA * IB - so we can XOR reflection bits together to get resulting reflection bit + * RC = IB * RA + RB - so we evaluate RB+RA or RB-RA based on reflection bit of transformation B to get resulting rotation bits */ + return (DirTransformation)(((a ^ b) & DTR_REFLECTION_BIT) | // calculate reflection bit + (((b & DTR_REFLECTION_BIT) ? (b - a) : (b + a)) & DTR_ROTATION_MASK)); // calculate rotation bits +} + +/** + * Invert given transformation. + * @param transformation Tranformation to invert. + * @return Inverted transformation transformation. + */ +static inline DirTransformation InvertDirTransform(DirTransformation transformation) +{ + /* to revert a reflection reflect again, transformation is the same (involution) */ + if (transformation & DTR_REFLECTION_BIT) return transformation; + + /* to revert a rotation rotate in opposite direction */ + return (DirTransformation)((-transformation) & DTR_ROTATION_MASK); +} + +static inline DirTransformation DirRotation(DiagDirDiff angle) +{ + return (DirTransformation)((uint)angle % DIAGDIR_END); +} + +static inline DirTransformation DirReflection(Direction axis) +{ + return (DirTransformation)(((axis - DiagDirToDir(DIAGDIR_BEGIN)) & DTR_ROTATION_MASK) | DTR_REFLECTION_BIT); +} + +static inline DirTransformation DirReflection(Axis axis) +{ + return (DirTransformation)((2 * axis) | DTR_REFLECTION_BIT); +} + +/** + * Transform Axis by a given transformation. + * @param axis Axis to transform. + * @param transformation Transformation to use. + * @return Transformed Axis. + */ +static inline Axis TransformAxis(Axis axis, DirTransformation transformation) +{ + return (Axis)(axis ^ (transformation & 1)); +} + +/** + * Transform Direction by given transformation. + * @param direction Direction to transform. + * @param transformation Transformation to use. + * @return Transformed Direction. + */ +static inline Direction TransformDir(Direction direction, DirTransformation transformation) +{ + if (transformation & DTR_REFLECTION_BIT) direction = (Direction)(2 * DIR_NE - direction); // reflect against X-axis + return ChangeDir(direction, (DirDiff)(2 * transformation)); // rotate and cut off overflowing bits +} + +/** + * Transform DiagDirection by a given transformation. + * @param diag_dir DiagDirection to transform. + * @param transformation Transformation to use. + * @return Transformed DiagDirection. + */ +static inline DiagDirection TransformDiagDir(DiagDirection diag_dir, DirTransformation transformation) +{ + if (transformation & DTR_REFLECTION_BIT) diag_dir = (DiagDirection)(2 * DIAGDIR_NE - diag_dir); // reflect against X-axis + return ChangeDiagDir(diag_dir, (DiagDirDiff)transformation); // rotate and cut off overflowing bits +} + #endif /* DIRECTION_FUNC_H */ diff --git a/src/direction_type.h b/src/direction_type.h index e6e08a182..9c3a5b147 100644 --- a/src/direction_type.h +++ b/src/direction_type.h @@ -115,6 +115,25 @@ enum DiagDirDiff { /** Allow incrementing of DiagDirDiff variables */ DECLARE_POSTFIX_INCREMENT(DiagDirDiff) +/** Represents transformations to rotate/reflect directions. */ +enum DirTransformation { + DTR_BEGIN = 0, ///< Used for iterations. + + DTR_IDENTITY = 0, ///< Identity transformation (no tranformation at all). + DTR_ROTATE_90_R = 1, ///< Rotate by 90 degree clockwise. + DTR_ROTATE_180 = 2, ///< Rotate by 180 degree. + DTR_ROTATE_90_L = 3, ///< Rotate by 90 degree counterclockwise. + + DTR_REFLECT_NE_SW = 4, ///< Reflect in respect to a Northeast-Southwest axis. + DTR_REFLECT_W_E = 5, ///< Reflect in respect to a West-East axis. + DTR_REFLECT_NW_SE = 6, ///< Reflect in respect to a Northwest-Southeast axis. + DTR_REFLECT_N_S = 7, ///< Reflect in respect to a North-South axis. + + DTR_END = 8, ///< Used for iterations. + + DTR_ROTATION_MASK = 0x03, ///< Rotation mask. The number formed by thiese bits tells how much to rotate object clockwise (in 90 degree units). + DTR_REFLECTION_BIT = 0x04, ///< Reflection bit. Indicates if to reflect object in respect to X-axis before rotating it. +}; /** * Enumeration for the two axis X and Y diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 79eaa89b1..3d370711e 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -193,10 +193,11 @@ struct BuildDocksToolbarWindow : Window { break; case WID_DT_STATION: { // Build station button + uint32 p1 = _settings_game.station.adjacent_stations && _ctrl_pressed; // adjacent? uint32 p2 = (uint32)INVALID_STATION << 16; // no station to join /* tile is always the land tile, so need to evaluate _thd.pos */ - CommandContainer cmdcont = { tile, _ctrl_pressed, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; + CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; /* Determine the watery part of the dock. */ DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 359709e36..896ecd921 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -20,6 +20,7 @@ #include "sound_func.h" #include "fios.h" #include "string_func.h" +#include "gui.h" #include "widgets/dropdown_type.h" #include "widgets/dropdown_func.h" #include "querystring_gui.h" @@ -282,12 +283,37 @@ static void LandscapeGenerationCallback(Window *w, bool confirmed) if (confirmed) StartGeneratingLandscape((GenerateLandscapeWindowMode)w->window_number); } -static DropDownList *BuildMapsizeDropDown() +/** + * Check if map size set lies in allowed boundaries. + * @param print_warning If set to true, messagebox with warning is printed out if size is outside limits. + * @return true if size is ok, false otherwise. + */ +static bool CheckMapSize(bool print_warning = true) +{ + uint64 tiles = 1ULL << (_settings_newgame.game_creation.map_x + _settings_newgame.game_creation.map_y); + + if (_settings_newgame.game_creation.map_x + _settings_newgame.game_creation.map_y > MAX_MAP_TILES_BITS) { + if (print_warning) { + SetDParam(0, MAX_MAP_TILES); + SetDParam(1, tiles); + ShowErrorMessage(STR_MAPGEN_TOO_MANY_TILES_MESSAGE, INVALID_STRING_ID, WL_ERROR, 0, 0); + } + return false; + } + return true; +} + +/** + * Build dropdown list with map sizes + * Dimension selected in the other dropdown is used to suggest which choices are 'valid' + * @param other_dimension Dimension specified by the second dropdown. + */ +static DropDownList *BuildMapsizeDropDown(int other_dimension) { DropDownList *list = new DropDownList(); for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) { - DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); + DropDownListParamStringItem *item = new DropDownListParamStringItem((i + other_dimension > MAX_MAP_TILES_BITS) ? STR_RED_INT : STR_JUST_INT, i, false); item->SetParam(0, 1 << i); *list->Append() = item; } @@ -315,6 +341,14 @@ struct GenerateLandscapeWindow : public Window { char name[64]; GenerateLandscapeWindowMode mode; + void SetDropDownColor() + { + /* Draw sizes in mapsize selection dropdowns in red if too large size is selected */ + bool mapsize_valid = CheckMapSize(false); + this->GetWidget(WID_GL_MAPSIZE_X_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; + this->GetWidget(WID_GL_MAPSIZE_Y_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; + } + GenerateLandscapeWindow(WindowDesc *desc, WindowNumber number = 0) : Window(desc) { this->InitNested(number); @@ -323,6 +357,8 @@ struct GenerateLandscapeWindow : public Window { this->mode = (GenerateLandscapeWindowMode)this->window_number; + SetDropDownColor(); + /* Disable town, industry and trees in SE */ this->SetWidgetDisabledState(WID_GL_TOWN_PULLDOWN, _game_mode == GM_EDITOR); this->SetWidgetDisabledState(WID_GL_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR); @@ -539,11 +575,11 @@ struct GenerateLandscapeWindow : public Window { break; case WID_GL_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_x, WID_GL_MAPSIZE_X_PULLDOWN); + ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_y), _settings_newgame.game_creation.map_x, WID_GL_MAPSIZE_X_PULLDOWN); break; case WID_GL_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_GL_MAPSIZE_Y_PULLDOWN); + ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_x), _settings_newgame.game_creation.map_y, WID_GL_MAPSIZE_Y_PULLDOWN); break; case WID_GL_TOWN_PULLDOWN: // Number of towns @@ -555,6 +591,7 @@ struct GenerateLandscapeWindow : public Window { break; case WID_GL_GENERATE_BUTTON: { // Generate + if (!CheckMapSize()) break; /* Get rotated map size. */ uint map_x; uint map_y; @@ -717,8 +754,14 @@ struct GenerateLandscapeWindow : public Window { virtual void OnDropdownSelect(int widget, int index) { switch (widget) { - case WID_GL_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break; - case WID_GL_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break; + case WID_GL_MAPSIZE_X_PULLDOWN: + _settings_newgame.game_creation.map_x = index; + SetDropDownColor(); + break; + case WID_GL_MAPSIZE_Y_PULLDOWN: + _settings_newgame.game_creation.map_y = index; + SetDropDownColor(); + break; case WID_GL_TREE_PULLDOWN: _settings_newgame.game_creation.tree_placer = index; break; case WID_GL_RIVER_PULLDOWN: _settings_newgame.game_creation.amount_of_rivers = index; break; case WID_GL_SMOOTHNESS_PULLDOWN: _settings_newgame.game_creation.tgen_smoothness = index; break; @@ -882,10 +925,19 @@ struct CreateScenarioWindow : public Window { uint widget_id; + void SetDropDownColor() + { + /* Draw sizes in mapsize selection dropdowns in red if too large size is selected */ + bool mapsize_valid = CheckMapSize(false); + this->GetWidget(WID_CS_MAPSIZE_X_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; + this->GetWidget(WID_CS_MAPSIZE_Y_PULLDOWN)->widget_data = mapsize_valid ? STR_JUST_INT : STR_RED_INT; + } + CreateScenarioWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { this->InitNested(window_number); this->LowerWidget(_settings_newgame.game_creation.landscape + WID_CS_TEMPERATE); + SetDropDownColor(); } virtual void SetStringParameters(int widget) const @@ -909,6 +961,8 @@ struct CreateScenarioWindow : public Window } } + + virtual void OnPaint() { this->SetWidgetDisabledState(WID_CS_START_DATE_DOWN, _settings_newgame.game_creation.starting_year <= MIN_YEAR); @@ -962,18 +1016,20 @@ struct CreateScenarioWindow : public Window break; case WID_CS_MAPSIZE_X_PULLDOWN: // Mapsize X - ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_x, WID_CS_MAPSIZE_X_PULLDOWN); + ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_y), _settings_newgame.game_creation.map_x, WID_CS_MAPSIZE_X_PULLDOWN); break; case WID_CS_MAPSIZE_Y_PULLDOWN: // Mapsize Y - ShowDropDownList(this, BuildMapsizeDropDown(), _settings_newgame.game_creation.map_y, WID_CS_MAPSIZE_Y_PULLDOWN); + ShowDropDownList(this, BuildMapsizeDropDown(_settings_newgame.game_creation.map_x), _settings_newgame.game_creation.map_y, WID_CS_MAPSIZE_Y_PULLDOWN); break; case WID_CS_EMPTY_WORLD: // Empty world / flat world + if (!CheckMapSize()) break; StartGeneratingLandscape(GLWM_SCENARIO); break; case WID_CS_RANDOM_WORLD: // Generate + if (!CheckMapSize()) break; ShowGenerateLandscape(); break; @@ -1032,6 +1088,8 @@ struct CreateScenarioWindow : public Window case WID_CS_MAPSIZE_X_PULLDOWN: _settings_newgame.game_creation.map_x = index; break; case WID_CS_MAPSIZE_Y_PULLDOWN: _settings_newgame.game_creation.map_y = index; break; } + SetDropDownColor(); + this->SetDirty(); } diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 06534ad29..c9ee5661a 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -193,6 +193,9 @@ static void LoadSpriteTables() ); } + /* Load clipboard graphics */ + LoadGrfFile("clipboard.grf", SPR_CLIPBOARD_BASE, i++); + /* Initialize the unicode to sprite mapping table */ InitializeUnicodeGlyphMap(); diff --git a/src/group_gui.cpp b/src/group_gui.cpp index d3e1eafbb..464f17b20 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -75,6 +75,7 @@ static const NWidgetPart _nested_group_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_GL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA), NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_GL_GROUP_INFO), SetMinimalSize(200, 25), SetFill(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_MATRIX, COLOUR_GREY, WID_GL_LIST_VEHICLE), SetMinimalSize(248, 0), SetMatrixDataTip(1, 0, STR_NULL), SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_GL_LIST_VEHICLE_SCROLLBAR), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_GL_LIST_VEHICLE_SCROLLBAR), @@ -365,6 +366,7 @@ public: /* Minimum height is the height of the list widget minus all and default vehicles... */ size->height = 4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - 2 * this->tiny_step_height; + size->height += 25; /* ... minus the buttons at the bottom ... */ uint max_icon_height = GetSpriteSize(this->GetWidget(WID_GL_CREATE_GROUP)->widget_data).height; @@ -570,7 +572,28 @@ public: SetDParam(0, occupancy / vehicle_count); DrawString(left, right, y, STR_GROUP_OCCUPANCY_VALUE, TC_BLACK, SA_RIGHT); } + break; + } + + case WID_GL_GROUP_INFO: { + Money this_year = 0; + Money last_year = 0; + + for (uint i = 0; i < this->vehicles.Length(); i++) { + const Vehicle *v = this->vehicles[i]; + assert(v->owner == this->owner); + + if (this->vli.index == ALL_GROUP || v->group_id == this->vli.index) { + this_year += v->GetDisplayProfitThisYear(); + last_year += v->GetDisplayProfitLastYear(); + } + } + + SetDParam(0, this_year); + DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + 1, STR_GROUP_PROFIT_THIS_YEAR, TC_BLACK); + SetDParam(0, last_year); + DrawString(r.left + WD_FRAMERECT_LEFT + 8, r.right - WD_FRAMERECT_RIGHT - 8, r.top + WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + 2, STR_GROUP_PROFIT_LAST_YEAR, TC_BLACK); break; } @@ -932,7 +955,6 @@ public: } }; - static WindowDesc _other_group_desc( WDP_AUTO, "list_groups", 460, 246, WC_INVALID, WC_NONE, diff --git a/src/gui.h b/src/gui.h index 39f1ea661..9a83d2ca2 100644 --- a/src/gui.h +++ b/src/gui.h @@ -39,6 +39,9 @@ Window *ShowBuildDocksScenToolbar(); /* airport_gui.cpp */ Window *ShowBuildAirToolbar(); +/* clipboard_gui.cpp */ +Window *ShowClipboardToolbar(); + /* tgp_gui.cpp */ void ShowGenerateLandscape(); void ShowHeightmapLoad(); diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 5971964fd..47dc3e55e 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -343,6 +343,8 @@ static void DrawTile_Industry(TileInfo *ti) DrawGroundSprite(image, GroundSpritePaletteTransform(image, dits->ground.pal, GENERAL_SPRITE_COLOUR(ind->random_colour))); } + DrawOverlay(ti, MP_INDUSTRY); + /* If industries are transparent and invisible, do not draw the upper part */ if (IsInvisibilitySet(TO_INDUSTRIES)) return; @@ -2859,4 +2861,5 @@ extern const TileTypeProcs _tile_type_industry_procs = { NULL, // vehicle_enter_tile_proc GetFoundation_Industry, // get_foundation_proc TerraformTile_Industry, // terraform_tile_proc + NULL, // copypaste_tile_proc }; diff --git a/src/industry_map.h b/src/industry_map.h index 9d2e3de21..e0193f0bf 100644 --- a/src/industry_map.h +++ b/src/industry_map.h @@ -65,7 +65,7 @@ enum IndustryGraphics { static inline IndustryID GetIndustryIndex(TileIndex t) { assert(IsTileType(t, MP_INDUSTRY)); - return _m[t].m2; + return GetTile(t)->m2; } /** @@ -77,7 +77,7 @@ static inline IndustryID GetIndustryIndex(TileIndex t) static inline bool IsIndustryCompleted(TileIndex t) { assert(IsTileType(t, MP_INDUSTRY)); - return HasBit(_m[t].m1, 7); + return HasBit(GetTile(t)->m1, 7); } IndustryType GetIndustryType(TileIndex tile); @@ -90,7 +90,7 @@ IndustryType GetIndustryType(TileIndex tile); static inline void SetIndustryCompleted(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 7, 1, 1); + SB(GetTile(tile)->m1, 7, 1, 1); } /** @@ -102,7 +102,7 @@ static inline void SetIndustryCompleted(TileIndex tile) static inline byte GetIndustryConstructionStage(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(_m[tile].m1, 0, 2); + return IsIndustryCompleted(tile) ? (byte)INDUSTRY_COMPLETED : GB(GetTile(tile)->m1, 0, 2); } /** @@ -114,7 +114,7 @@ static inline byte GetIndustryConstructionStage(TileIndex tile) static inline void SetIndustryConstructionStage(TileIndex tile, byte value) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 2, value); + SB(GetTile(tile)->m1, 0, 2, value); } /** @@ -127,7 +127,7 @@ static inline void SetIndustryConstructionStage(TileIndex tile, byte value) static inline IndustryGfx GetCleanIndustryGfx(TileIndex t) { assert(IsTileType(t, MP_INDUSTRY)); - return _m[t].m5 | (GB(_me[t].m6, 2, 1) << 8); + return GetTile(t)->m5 | (GB(GetTileEx(t)->m6, 2, 1) << 8); } /** @@ -151,8 +151,8 @@ static inline IndustryGfx GetIndustryGfx(TileIndex t) static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) { assert(IsTileType(t, MP_INDUSTRY)); - _m[t].m5 = GB(gfx, 0, 8); - SB(_me[t].m6, 2, 1, GB(gfx, 8, 1)); + GetTile(t)->m5 = GB(gfx, 0, 8); + SB(GetTileEx(t)->m6, 2, 1, GB(gfx, 8, 1)); } /** @@ -164,7 +164,7 @@ static inline void SetIndustryGfx(TileIndex t, IndustryGfx gfx) static inline byte GetIndustryConstructionCounter(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return GB(_m[tile].m1, 2, 2); + return GB(GetTile(tile)->m1, 2, 2); } /** @@ -176,7 +176,7 @@ static inline byte GetIndustryConstructionCounter(TileIndex tile) static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 2, 2, value); + SB(GetTile(tile)->m1, 2, 2, value); } /** @@ -189,8 +189,8 @@ static inline void SetIndustryConstructionCounter(TileIndex tile, byte value) static inline void ResetIndustryConstructionStage(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 4, 0); - SB(_m[tile].m1, 7, 1, 0); + SB(GetTile(tile)->m1, 0, 4, 0); + SB(GetTile(tile)->m1, 7, 1, 0); } /** @@ -201,7 +201,7 @@ static inline void ResetIndustryConstructionStage(TileIndex tile) static inline byte GetIndustryAnimationLoop(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return _m[tile].m4; + return GetTile(tile)->m4; } /** @@ -213,7 +213,7 @@ static inline byte GetIndustryAnimationLoop(TileIndex tile) static inline void SetIndustryAnimationLoop(TileIndex tile, byte count) { assert(IsTileType(tile, MP_INDUSTRY)); - _m[tile].m4 = count; + GetTile(tile)->m4 = count; } /** @@ -226,7 +226,7 @@ static inline void SetIndustryAnimationLoop(TileIndex tile, byte count) static inline byte GetIndustryRandomBits(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return _m[tile].m3; + return GetTile(tile)->m3; } /** @@ -239,7 +239,7 @@ static inline byte GetIndustryRandomBits(TileIndex tile) static inline void SetIndustryRandomBits(TileIndex tile, byte bits) { assert(IsTileType(tile, MP_INDUSTRY)); - _m[tile].m3 = bits; + GetTile(tile)->m3 = bits; } /** @@ -252,7 +252,7 @@ static inline void SetIndustryRandomBits(TileIndex tile, byte bits) static inline byte GetIndustryTriggers(TileIndex tile) { assert(IsTileType(tile, MP_INDUSTRY)); - return GB(_me[tile].m6, 3, 3); + return GB(GetTileEx(tile)->m6, 3, 3); } @@ -266,7 +266,7 @@ static inline byte GetIndustryTriggers(TileIndex tile) static inline void SetIndustryTriggers(TileIndex tile, byte triggers) { assert(IsTileType(tile, MP_INDUSTRY)); - SB(_me[tile].m6, 3, 3, triggers); + SB(GetTileEx(tile)->m6, 3, 3, triggers); } /** @@ -280,14 +280,14 @@ static inline void SetIndustryTriggers(TileIndex tile, byte triggers) static inline void MakeIndustry(TileIndex t, IndustryID index, IndustryGfx gfx, uint8 random, WaterClass wc) { SetTileType(t, MP_INDUSTRY); - _m[t].m1 = 0; - _m[t].m2 = index; + GetTile(t)->m1 = 0; + GetTile(t)->m2 = index; SetIndustryRandomBits(t, random); // m3 - _m[t].m4 = 0; + GetTile(t)->m4 = 0; SetIndustryGfx(t, gfx); // m5, part of m6 SetIndustryTriggers(t, 0); // rest of m6 SetWaterClass(t, wc); - _me[t].m7 = 0; + GetTileEx(t)->m7 = 0; } #endif /* INDUSTRY_MAP_H */ diff --git a/src/landscape.cpp b/src/landscape.cpp index 185e84a80..e574c0799 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -724,12 +724,12 @@ void RunTileLoop() * shift register (LFSR). This allows a deterministic pseudorandom ordering, but * still with minimal state and fast iteration. */ - /* Maximal length LFSR feedback terms, from 12-bit (for 64x64 maps) to 24-bit (for 4096x4096 maps). + /* Maximal length LFSR feedback terms, from 12-bit (for 64x64 maps) to 26-bit. * Extracted from http://www.ece.cmu.edu/~koopman/lfsr/ */ static const uint32 feedbacks[] = { - 0xD8F, 0x1296, 0x2496, 0x4357, 0x8679, 0x1030E, 0x206CD, 0x403FE, 0x807B8, 0x1004B2, 0x2006A8, 0x4004B2, 0x800B87 + 0xD8F, 0x1296, 0x2496, 0x4357, 0x8679, 0x1030E, 0x206CD, 0x403FE, 0x807B8, 0x1004B2, 0x2006A8, 0x4004B2, 0x800B87, 0x10004F3, 0x200072D }; - assert_compile(lengthof(feedbacks) == 2 * MAX_MAP_SIZE_BITS - 2 * MIN_MAP_SIZE_BITS + 1); + assert_compile(lengthof(feedbacks) == MAX_MAP_TILES_BITS - 2 * MIN_MAP_SIZE_BITS + 1); const uint32 feedback = feedbacks[MapLogX() + MapLogY() - 2 * MIN_MAP_SIZE_BITS]; /* We update every tile every 256 ticks, so divide the map size by 2^8 = 256 */ diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index 19140416b..113ada8f7 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -4873,10 +4873,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Spektator, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/basque.txt b/src/lang/basque.txt index d31feeeb9..862214fb3 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -4736,10 +4736,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Ikuslea, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index aeac5a18c..790956874 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -4888,10 +4888,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :{G=Masculin}Espectador, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/danish.txt b/src/lang/danish.txt index a6f2b072b..8dfb772ec 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -4872,10 +4872,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Tilskuer, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index 1bdaded5a..d39e52f44 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -4875,10 +4875,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Toeschouwer, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/english.txt b/src/lang/english.txt index 5d794483c..bbc4d0047 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -232,6 +232,7 @@ STR_TOOLTIP_GROUP_ORDER :{BLACK}Select g STR_TOOLTIP_SORT_ORDER :{BLACK}Select sorting order (descending/ascending) STR_TOOLTIP_SORT_CRITERIA :{BLACK}Select sorting criteria STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Select filtering criteria +STR_BUTTON_COVERAGE :{BLACK}Coverage STR_BUTTON_SORT_BY :{BLACK}Sort by STR_BUTTON_LOCATION :{BLACK}Location STR_BUTTON_RENAME :{BLACK}Rename @@ -453,6 +454,7 @@ STR_AIRCRAFT_MENU_AIRPORT_CONSTRUCTION :Airport constru ############ range for landscaping menu starts STR_LANDSCAPING_MENU_LANDSCAPING :Landscaping +STR_LANDSCAPING_MENU_CLIPBOARD :Clipboard STR_LANDSCAPING_MENU_PLANT_TREES :Plant trees STR_LANDSCAPING_MENU_PLACE_SIGN :Place sign ############ range ends here @@ -884,6 +886,8 @@ STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copy to STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copy the location of the main view to this viewport STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Paste from viewport STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Paste the location of this viewport to the main view +STR_EXTRA_VIEW_FOLLOW_CURSOR :{BLACK}Follow cursor +STR_EXTRA_VIEW_FOLLOW_CURSOR_TT :{BLACK}Follow the position of the cursor in this view # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Game Options @@ -910,7 +914,7 @@ STR_GAME_OPTIONS_CURRENCY_ISK :Icelandic Krona STR_GAME_OPTIONS_CURRENCY_ITL :Italian Lira (ITL) STR_GAME_OPTIONS_CURRENCY_NLG :Dutch Guilder (NLG) STR_GAME_OPTIONS_CURRENCY_NOK :Norwegian Krone (NOK) -STR_GAME_OPTIONS_CURRENCY_PLN :Polish Złoty (PLN) +STR_GAME_OPTIONS_CURRENCY_PLN :Polish Z?oty (PLN) STR_GAME_OPTIONS_CURRENCY_RON :Romanian Leu (RON) STR_GAME_OPTIONS_CURRENCY_RUR :Russian Rubles (RUR) STR_GAME_OPTIONS_CURRENCY_SIT :Slovenian Tolar (SIT) @@ -1253,6 +1257,9 @@ STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Allow construct STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Changing this setting is not possible when there are vehicles STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Infrastructure maintenance: {STRING2} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :When enabled, infrastructure causes maintenance costs. The cost grows over-proportional with the network size, thus affecting bigger companies more than smaller ones +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY :Clipboard capacity: {STRING2} +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE :{0:NUM}x{0:NUM} tiles +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT :Maximum allowed width/height of area that can be copied into clipboard STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Airports never expire: {STRING2} STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Enabling this setting makes each airport type stay available forever after its introduction @@ -1287,6 +1294,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Show town popul STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Display the population of towns in their label on the map STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Thickness of lines in graphs: {STRING2} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Width of the line in the graphs. A thin line is more precisely readable, a thicker line is easier to see and colours are easier to distinguish +STR_CONFIG_SETTING_FORECAST_DISPLAY :{LTBLUE}Display supply and demand forecasting on station building: {ORANGE}{STRING2} +STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT :Display supply and demand forecasting on station building? STR_CONFIG_SETTING_LANDSCAPE :Landscape: {STRING2} STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landscapes define basic gameplay scenarios with different cargos and town growth requirements. NewGRF and Game Scripts allow finer control though @@ -1413,6 +1422,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Keep building t STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Keep the building tools for bridges, tunnels, etc. open after use STR_CONFIG_SETTING_EXPENSES_LAYOUT :Group expenses in company finance window: {STRING2} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Define the layout for the company expenses window +STR_CONFIG_SETTING_TOWNRATING_INDICATOR :Show town rating indicators: {STRING2} STR_CONFIG_SETTING_SOUND_TICKER :News ticker: {STRING2} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Play sound for summarised news messages @@ -1462,6 +1472,7 @@ STR_CONFIG_SETTING_AI_IN_MULTIPLAYER_HELPTEXT :Allow AI comput STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES :#opcodes before scripts are suspended: {STRING2} STR_CONFIG_SETTING_SCRIPT_MAX_OPCODES_HELPTEXT :Maximum number of computation steps that a script can take in one turn +STR_CONFIG_SETTING_SIMULATE_SIGNALS :{LTBLUE}Simulate signals in tunnels, bridges every: {ORANGE}{STRING1} tile{P 0:1 "" s} STR_CONFIG_SETTING_SERVINT_ISPERCENT :Service intervals are in percents: {STRING2} STR_CONFIG_SETTING_SERVINT_ISPERCENT_HELPTEXT :Choose whether servicing of vehicles is triggered by the time passed since last service or by reliability dropping by a certain percentage of the maximum reliability STR_CONFIG_SETTING_SERVINT_TRAINS :Default service interval for trains: {STRING2} @@ -2278,6 +2289,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Toggle t STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Toggle transparency for structures like lighthouses and antennas. Ctrl+Click to lock STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Toggle transparency for catenary. Ctrl+Click to lock STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Toggle transparency for loading indicators. Ctrl+Click to lock +STR_TRANSPARENT_TUNNELS_TOOLTIP :{BLACK}Toggle transparency for vehicles in tunnels. Ctrl+Click to lock. STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Set objects invisible instead of transparent # Linkgraph legend window @@ -2388,6 +2400,35 @@ STR_BRIDGE_NAME_CONCRETE :Concrete STR_BRIDGE_NAME_TUBULAR_STEEL :Tubular, Steel STR_BRIDGE_TUBULAR_SILICON :Tubular, Silicon +# Clipboard toolbar window +STR_CLIPBOARD_TOOLBAR_CAPTION :{WHITE}Clipboard +STR_CLIPBOARD_TOOLTIP_SWITCH_TO_1ST_CLIPBOARD :{BLACK}Switch to first clipboard +STR_CLIPBOARD_TOOLTIP_SWITCH_TO_2ND_CLIPBOARD :{BLACK}Switch to second clipboard +STR_CLIPBOARD_TOOLTIP_SWITCH_TO_3RD_CLIPBOARD :{BLACK}Switch to third clipboard +STR_CLIPBOARD_TOOLTIP_SWITCH_TO_4TH_CLIPBOARD :{BLACK}Switch to fourth clipboard +STR_CLIPBOARD_TOOLTIP_COPY :{BLACK}Select area to copy +STR_CLIPBOARD_TOOLTIP_PASTE :{BLACK}Paste selected area +STR_CLIPBOARD_TOOLTIP_SELECT_COPY_AREA :{BLACK}Select source area to be copied +STR_CLIPBOARD_TOOLTIP_INSTANT_COPY_PASTE :{BLACK}Copy previously selected area and past it at a given location +STR_CLIPBOARD_TOOLTIP_COPY_WITH_RAIL_TRANSPORT :{BLACK}Paste rail transport infrastructure +STR_CLIPBOARD_TOOLTIP_COPY_WITH_ROAD_TRANSPORT :{BLACK}Paste road transport infrastructure +STR_CLIPBOARD_TOOLTIP_COPY_WITH_WATER_TRANSPORT :{BLACK}Paste water transport infrastructure +STR_CLIPBOARD_TOOLTIP_COPY_WITH_AIR_TRANSPORT :{BLACK}Paste air transport infrastructure +STR_CLIPBOARD_TOOLTIP_TERRAFORM :{BLACK}Terraform land when pasting{}(red) no terraforming{}(yellow) minimal terraforming{}(green) full terraforming +STR_CLIPBOARD_TOOLTIP_CONVERT_RAIL :{BLACK}Converts rail infrastructure to{}current rail type when pasting +STR_CLIPBOARD_TOOLTIP_MIRROR_SIGNALS :{BLACK}Mirror signals when pasting +STR_CLIPBOARD_TOOLTIP_UPGRADE_BRIDGES :{BLACK}Upgrade bridges when pasting +STR_CLIPBOARD_TOOLTIP_WITH_STATIONS :{BLACK}Paste stations and waypoints too +STR_CLIPBOARD_TOOLTIP_STOP_ON_FAILURE :{BLACK}Stop pasting immidiately if something is failing +STR_CLIPBOARD_TOOLTIP_TRANSFORMATION :{BLACK}Current transformation,{}click to reset +STR_CLIPBOARD_TOOLTIP_ROTATE_LEFT :{BLACK}Rotate by 90 degree anticlockwise +STR_CLIPBOARD_TOOLTIP_ROTATE_RIGHT :{BLACK}Rotate by 90 degree clockwise +STR_CLIPBOARD_TOOLTIP_REFLECT_NE_SW :{BLACK}Reflect against NW-SE axis +STR_CLIPBOARD_TOOLTIP_REFLECT_NW_SE :{BLACK}Reflect against NE-SW axis +STR_CLIPBOARD_HEIGHT_DIFF :{GOLD}{STRING1} +STR_CLIPBOARD_HEIGHT_DIFF_NEGATIVE :-{NUM} +STR_CLIPBOARD_HEIGHT_DIFF_NEUTRAL : {NUM} +STR_CLIPBOARD_HEIGHT_DIFF_POSITIVE :+{NUM} # Road construction toolbar STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION :{WHITE}Road Construction @@ -2477,6 +2518,7 @@ STR_LANDSCAPING_TOOLBAR :{WHITE}Landscap STR_LANDSCAPING_TOOLTIP_LOWER_A_CORNER_OF_LAND :{BLACK}Lower a corner of land. Dragging lowers the first selected corner and levels the selected area to the new corner height. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate STR_LANDSCAPING_TOOLTIP_RAISE_A_CORNER_OF_LAND :{BLACK}Raise a corner of land. Dragging raises the first selected corner and levels the selected area to the new corner height. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate STR_LANDSCAPING_LEVEL_LAND_TOOLTIP :{BLACK}Level an area of land to the height of the first selected corner. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate +STR_LANDSCAPING_TOOLTIP_SHOW_CLIPBOARD_TOOLBAR :{BLACK}Show clipboard toolbar STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase land for future use. Shift toggles building/showing cost estimate # Object construction window @@ -2553,6 +2595,12 @@ STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospect STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Build STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Fund +# Town rating indicators +STR_TOWN_RATING_INCREASED_TINY :{TINY_FONT}{GREEN}Rating increased to: {STRING} +STR_TOWN_RATING_INCREASED :{GREEN}Rating increased to: {STRING} +STR_TOWN_RATING_DECREASED_TINY :{TINY_FONT}{RED}Rating decreased to: {STRING} +STR_TOWN_RATING_DECREASED :{RED}Rating decreased to: {STRING} + # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Industry chain for {STRING} industry STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Industry chain for {STRING} cargo @@ -2662,8 +2710,10 @@ STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT :Ship depot # Industries come directly from their industry names STR_LAI_TUNNEL_DESCRIPTION_RAILROAD :Railway tunnel +STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL :Railway tunnel with signal simulation STR_LAI_TUNNEL_DESCRIPTION_ROAD :Road tunnel +STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL :Railway bridge with signal simulation STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL :Steel suspension rail bridge STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL :Steel girder rail bridge STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL :Steel cantilever rail bridge @@ -2757,6 +2807,7 @@ STR_MAPGEN_HEIGHTMAP_ROTATION :{BLACK}Heightma STR_MAPGEN_HEIGHTMAP_NAME :{BLACK}Heightmap name: STR_MAPGEN_HEIGHTMAP_SIZE_LABEL :{BLACK}Size: STR_MAPGEN_HEIGHTMAP_SIZE :{ORANGE}{NUM} x {NUM} +STR_MAPGEN_TOO_MANY_TILES_MESSAGE :{YELLOW}Too many tiles in map. Maximum number of tiles is {NUM}, you have selected {NUM} STR_MAPGEN_MAX_HEIGHTLEVEL_QUERY_CAPT :{WHITE}Change maximum map height STR_MAPGEN_SNOW_LINE_QUERY_CAPT :{WHITE}Change snow line height @@ -3161,6 +3212,8 @@ STR_CARGO_RATING_OUTSTANDING :Outstanding STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Centre main view on station location. Ctrl+Click opens a new viewport on station location STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Change name of station +STR_STATION_VIEW_COVERAGE :{BLACK}Coverage +STR_STATION_VIEW_COVERAGE_TIP :{BLACK}Show station's area coverage STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Show all trains which have this station on their schedule STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP :{BLACK}Show all road vehicles which have this station on their schedule STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP :{BLACK}Show all aircraft which have this station on their schedule @@ -3369,8 +3422,8 @@ STR_GROUP_REMOVE_ALL_VEHICLES :Remove all vehi STR_GROUP_RENAME_CAPTION :{BLACK}Rename a group -STR_GROUP_PROFIT_THIS_YEAR :Profit this year: -STR_GROUP_PROFIT_LAST_YEAR :Profit last year: +STR_GROUP_PROFIT_THIS_YEAR :{BLACK}Profit this year: {CURRENCY_SHORT} +STR_GROUP_PROFIT_LAST_YEAR :{BLACK}Profit last year: {CURRENCY_SHORT} STR_GROUP_OCCUPANCY :Current usage: STR_GROUP_OCCUPANCY_VALUE :{NUM}% @@ -4142,6 +4195,8 @@ STR_ERROR_TREE_PLANT_LIMIT_REACHED :{WHITE}... tree STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}Name must be unique STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{1:STRING} in the way STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}Not allowed while paused +STR_ERROR_NOTHING_TO_DO :{WHITE}... there is nothing to do +STR_ERROR_INAPPLICABLE_TRANSFORMATION :{WHITE}... inapplicable transformation # Local authority errors STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}{TOWN} local authority refuses to allow this @@ -4221,6 +4276,8 @@ STR_ERROR_CAN_T_BUILD_DOCK_HERE :{WHITE}Can't bu STR_ERROR_CAN_T_BUILD_AIRPORT_HERE :{WHITE}Can't build airport here... STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING :{WHITE}Adjoins more than one existing station/loading area +STR_ERROR_ADJOINS_OTHER_STATION :{WHITE}Adjoins other station/loading area +STR_ERROR_CAN_T_DISTANT_JOIN :{WHITE}Can't distant-join station/loading area STR_ERROR_STATION_TOO_SPREAD_OUT :{WHITE}... station too spread out STR_ERROR_TOO_MANY_STATIONS_LOADING :{WHITE}Too many stations/loading areas STR_ERROR_TOO_MANY_STATION_SPECS :{WHITE}Too many railway station parts @@ -4370,6 +4427,10 @@ STR_ERROR_COMPANY_HEADQUARTERS_IN :{WHITE}... comp STR_ERROR_CAN_T_PURCHASE_THIS_LAND :{WHITE}Can't purchase this land area... STR_ERROR_YOU_ALREADY_OWN_IT :{WHITE}... you already own it! +# Clipboard related errors +STR_COPY_PASTE_ERROR_SUMMARY :{WHITE}{8:STRING} +STR_ERROR_CAN_T_PASTE_HERE :{WHITE}Can't paste here... + # Group related errors STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Can't create group... STR_ERROR_GROUP_CAN_T_DELETE :{WHITE}Can't delete this group... @@ -4893,10 +4954,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Spectator, {1:STRING1} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} @@ -4954,6 +5023,7 @@ STR_DATE_LONG_SMALL :{TINY_FONT}{BLA STR_TINY_GROUP :{TINY_FONT}{GROUP} STR_BLACK_INT :{BLACK}{NUM} STR_ORANGE_INT :{ORANGE}{NUM} +STR_RED_INT :{RED}{NUM} STR_WHITE_SIGN :{WHITE}{SIGN} STR_TINY_BLACK_STATION :{TINY_FONT}{BLACK}{STATION} STR_BLACK_STRING :{BLACK}{STRING} diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index 4de30054a..b4c5ec8d6 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -4930,10 +4930,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Vaatleja, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index c683d46c0..ea6bf1150 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -4378,10 +4378,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Eygleiðari, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/german.txt b/src/lang/german.txt index f35e2eca9..1dd3230e6 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -233,6 +233,7 @@ STR_TOOLTIP_GROUP_ORDER :{BLACK}Gruppier STR_TOOLTIP_SORT_ORDER :{BLACK}Sortierreihenfolge auswählen (absteigend/aufsteigend) STR_TOOLTIP_SORT_CRITERIA :{BLACK}Sortierkriterien auswählen STR_TOOLTIP_FILTER_CRITERIA :{BLACK}Filterkriterien auswählen +STR_BUTTON_COVERAGE :{BLACK}Einzugsbereich STR_BUTTON_SORT_BY :{BLACK}Sortieren nach STR_BUTTON_LOCATION :{BLACK}Standort STR_BUTTON_RENAME :{BLACK}Umbenennen @@ -885,6 +886,8 @@ STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}In Zusat STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Aktuelle Position der Hauptansicht in diese Zusatzansicht kopieren STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Aus Zusatzansicht einfügen STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Hauptansicht zur Position dieser Zusatzansicht scrollen +STR_EXTRA_VIEW_FOLLOW_CURSOR :{BLACK}Dem Mauszeiger folgen +STR_EXTRA_VIEW_FOLLOW_CURSOR_TT :{BLACK}Dem Mauszeiger in diesem Fenster folgen # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Spieleinstellungen @@ -1254,6 +1257,9 @@ STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :Erlaube die Err STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}Diese Einstellung kann nicht geändert werden solange Fahrzeuge im Spiel sind STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :Instandhaltung der Infrastruktur: {STRING} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT :Unterhaltskosten für Infrastruktur einschalten. Die Unterhaltskosten wachsen mit zunehmender Netzwerkgröße überproportional an, so dass sie größere Firmen stärker belasten als kleinere +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY :Clipboard Kapazität: {STRING} +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE :{0:NUM}x{0:NUM} Felder +STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT :Maximal erlaubte Länge/Breite, die ins Clipboard kopiert werden kann STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS :Flughäfen veralten nie: {STRING} STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS_HELPTEXT :Wird diese Option aktiviert, können Flughäfen, die einmal eingeführt wurden, das ganze Spiel über gebaut werden und veralten nie @@ -1288,6 +1294,8 @@ STR_CONFIG_SETTING_POPULATION_IN_LABEL :Zeige die Einwo STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT :Zeige die Einwohneranzahl neben den Städtenamen auf der Karte an STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS :Linienstärke in Diagrammen: {STRING} STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT :Strichdicke der Linien in Diagrammen. Dünnere Linien sind genauer ablesbar, dickere Linien sind besser sichtbar und erlauben es, Farben leichter zu unterscheiden +STR_CONFIG_SETTING_FORECAST_DISPLAY :{LTBLUE}Angebots- und Bedarfsvorhersage für Station anzeigen: {ORANGE}{STRING} +STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT :Angebots- und Bedarfsvorhersage für Station anzeigen? STR_CONFIG_SETTING_LANDSCAPE :Landschaftstyp: {STRING} STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landschaftstype definiert grundlegende Spielscenarien in Bezug auf verfügbare Fracht und Wachstumsvoraussetzungen für Städte. NewGRFs und Spielskripte erlauben weitgehendere Kontrolle dieser Parameter @@ -1412,6 +1420,7 @@ STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS :Belasse Bauwerk STR_CONFIG_SETTING_PERSISTENT_BUILDINGTOOLS_HELPTEXT :Die Bauwerkzeuge für Brücken, Tunnel, etc. nach Benutzung weiter aktiviert lassen STR_CONFIG_SETTING_EXPENSES_LAYOUT :Zwischensummen für Kategorien bei Firmenausgaben:{STRING} STR_CONFIG_SETTING_EXPENSES_LAYOUT_HELPTEXT :Lege das Layout für das Fenster mit den Firmenausgaben fest +STR_CONFIG_SETTING_TOWNRATING_INDICATOR :Zeige Indikatoren für Bewertung: {STRING} STR_CONFIG_SETTING_SOUND_TICKER :Nachrichtenticker: {STRING} STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT :Soundeffekte für Kurzfassungen von Nachrichten abspielen (Ticker) @@ -2276,6 +2285,7 @@ STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}Transpar STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}Transparenz für Bauten wie Leuchttürme und Sendemasten einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}Transparenz für Oberleitungen einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben STR_TRANSPARENT_LOADING_TOOLTIP :{BLACK}Transparenz für Ladestandsanzeige einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben +STR_TRANSPARENT_TUNNELS_TOOLTIP :{BLACK}Transparenz für Fahrzeuge in Tunneln einstellen. Strg+Klick, um Umschalten zu verhindern bzw. wieder zu erlauben STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}Objekte unsichtbar statt transparent machen # Linkgraph legend window @@ -2551,6 +2561,12 @@ STR_FUND_INDUSTRY_PROSPECT_NEW_INDUSTRY :{BLACK}Prospekt STR_FUND_INDUSTRY_BUILD_NEW_INDUSTRY :{BLACK}Bauen STR_FUND_INDUSTRY_FUND_NEW_INDUSTRY :{BLACK}Finanzieren +# Town rating indicators +STR_TOWN_RATING_INCREASED_TINY :{TINY_FONT}{GREEN}Bewertung verbessert zu: {STRING} +STR_TOWN_RATING_INCREASED :{GREEN}Bewertung verbessert zu: {STRING} +STR_TOWN_RATING_DECREASED_TINY :{TINY_FONT}{RED}Bewertung verschlechtert zu: {STRING} +STR_TOWN_RATING_DECREASED :{RED}Bewertung verschlechtert zu: {STRING} + # Industry cargoes window STR_INDUSTRY_CARGOES_INDUSTRY_CAPTION :{WHITE}Produktionskette für {STRING} STR_INDUSTRY_CARGOES_CARGO_CAPTION :{WHITE}Produktionskette für {STRING} @@ -3158,6 +3174,8 @@ STR_CARGO_RATING_OUTSTANDING :Hervorragend STR_STATION_VIEW_CENTER_TOOLTIP :{BLACK}Hauptansicht zur Station scrollen. Strg+Klick öffnet neue Zusatzansicht bei der Station STR_STATION_VIEW_RENAME_TOOLTIP :{BLACK}Namen der Station ändern +STR_STATION_VIEW_COVERAGE :{BLACK}Einzugsbereich +STR_STATION_VIEW_COVERAGE_TIP :{BLACK}Einzugsbereich der Station anzeigen STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP :{BLACK}Alle Züge, die diesen Bahnhof anfahren, anzeigen STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP :{BLACK}Alle Straßenfahrzeuge, die diese Station anfahren, anzeigen STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP :{BLACK}Alle Flugzeuge, die diesen Flughafen anfliegen, anzeigen @@ -3361,6 +3379,8 @@ STR_GROUP_REMOVE_ALL_VEHICLES :Liste leeren STR_GROUP_RENAME_CAPTION :{BLACK}Gruppe umbenennen +STR_GROUP_PROFIT_THIS_YEAR :{BLACK}Profit im letzten Jahr: {CURRENCY_SHORT} +STR_GROUP_PROFIT_LAST_YEAR :{BLACK}Profit in diesem Jahr: {CURRENCY_SHORT} # Build vehicle window STR_BUY_VEHICLE_TRAIN_RAIL_CAPTION :Neue Schienenfahrzeuge @@ -4873,10 +4893,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Zuschauer, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index e4cd73062..213d41997 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -4631,10 +4631,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Áhorfandi, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 4b7dcafd2..34f5fb91a 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -4868,10 +4868,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Penonton, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/italian.txt b/src/lang/italian.txt index e610e1dc0..37d653183 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -4917,10 +4917,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Spettatore, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 818e79745..ce44b04be 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -4887,10 +4887,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Zuschauer, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index 50eb2d809..ee39abe23 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -4786,10 +4786,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Tilskuar, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index da8eb3afa..8716aafbb 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -4873,10 +4873,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Espectador, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 7cffa0d74..e7f55a172 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -4875,10 +4875,18 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Espectador, {1:STRING} # Viewport strings +STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING :{WHITE}{TOWN} {RED}({COMMA}) +STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING :{WHITE}{TOWN} {ORANGE}({COMMA}) +STR_VIEWPORT_TOWN_POP_GOOD_RATING :{WHITE}{TOWN} {YELLOW}({COMMA}) STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) +STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING :{WHITE}{TOWN} {GREEN}({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} +STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING :{TINY_FONT}{RED}{TOWN} +STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING :{TINY_FONT}{ORANGE}{TOWN} +STR_VIEWPORT_TOWN_TINY_GOOD_RATING :{TINY_FONT}{YELLOW}{TOWN} STR_VIEWPORT_TOWN_TINY_WHITE :{TINY_FONT}{WHITE}{TOWN} +STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING :{TINY_FONT}{GREEN}{TOWN} STR_VIEWPORT_SIGN_SMALL_BLACK :{TINY_FONT}{BLACK}{SIGN} STR_VIEWPORT_SIGN_SMALL_WHITE :{TINY_FONT}{WHITE}{SIGN} diff --git a/src/map.cpp b/src/map.cpp index 252f20b60..8fad25e57 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -22,16 +22,7 @@ extern "C" _CRTIMP void __cdecl _assert(void *, void *, unsigned); #endif -uint _map_log_x; ///< 2^_map_log_x == _map_size_x -uint _map_log_y; ///< 2^_map_log_y == _map_size_y -uint _map_size_x; ///< Size of the map along the X -uint _map_size_y; ///< Size of the map along the Y -uint _map_size; ///< The number of tiles on the map -uint _map_tile_mask; ///< _map_size - 1 (to mask the mapsize) - -Tile *_m = NULL; ///< Tiles of the map -TileExtended *_me = NULL; ///< Extended Tiles of the map - +MainMap _main_map; ///< The main tile array. /** * (Re)allocates a map with the given dimension @@ -40,10 +31,14 @@ TileExtended *_me = NULL; ///< Extended Tiles of the map */ void AllocateMap(uint size_x, uint size_y) { + DEBUG(map, 2, "Min/max map size %d/%d, max map tiles %d", MIN_MAP_SIZE, MAX_MAP_SIZE, MAX_MAP_TILES); + DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); + /* Make sure that the map size is within the limits and that * size of both axes is a power of 2. */ - if (!IsInsideMM(size_x, MIN_MAP_SIZE, MAX_MAP_SIZE + 1) || - !IsInsideMM(size_y, MIN_MAP_SIZE, MAX_MAP_SIZE + 1) || + if (size_x * size_y > MAX_MAP_TILES || + size_x < MIN_MAP_SIZE || + size_y < MIN_MAP_SIZE || (size_x & (size_x - 1)) != 0 || (size_y & (size_y - 1)) != 0) { error("Invalid map size"); @@ -51,18 +46,18 @@ void AllocateMap(uint size_x, uint size_y) DEBUG(map, 1, "Allocating map of size %dx%d", size_x, size_y); - _map_log_x = FindFirstBit(size_x); - _map_log_y = FindFirstBit(size_y); - _map_size_x = size_x; - _map_size_y = size_y; - _map_size = size_x * size_y; - _map_tile_mask = _map_size - 1; + _main_map.log_x = FindFirstBit(size_x); + _main_map.log_y = FindFirstBit(size_y); + _main_map.size_x = size_x; + _main_map.size_y = size_y; + _main_map.size = size_x * size_y; + _main_map.tile_mask = _main_map.size - 1; - free(_m); - free(_me); + free(_main_map.m); + free(_main_map.me); - _m = CallocT(_map_size); - _me = CallocT(_map_size); + _main_map.m = CallocT(_main_map.size); + _main_map.me = CallocT(_main_map.size); } @@ -98,7 +93,25 @@ TileIndex TileAdd(TileIndex tile, TileIndexDiff add, return TileXY(x, y); } + +GenericTileIndex TileAddXY(GenericTileIndex tile, int dx, int dy, const char *exp, const char *file, int line) +{ + uint x = TileX(tile) + dx; + uint y = TileY(tile) + dy; + + if (x >= MapSizeX(MapOf(tile)) || y >= MapSizeY(MapOf(tile))) { + char buf[512]; + snprintf(buf, lengthof(buf), "TILE_ADDXY(%s) when adding 0x%.4X and <0x%.4X, 0x%.4X> failed", exp, IndexOf(tile), dx, dy); +#if !defined(_MSC_VER) || defined(WINCE) + fprintf(stderr, "%s:%d %s\n", file, line, buf); +#else + _assert(buf, (char*)file, line); #endif + } + + return TileXY(x, y, MapOf(tile)); +} +#endif /* _DEBUG */ /** * This function checks if we add addx/addy to tile, if we diff --git a/src/map_func.h b/src/map_func.h index 9198c2cd1..51b0cc228 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -12,38 +12,122 @@ #ifndef MAP_FUNC_H #define MAP_FUNC_H +#include "core/bitmath_func.hpp" #include "core/math_func.hpp" #include "tile_type.h" #include "map_type.h" #include "direction_func.h" -extern uint _map_tile_mask; +extern MainMap _main_map; /** * 'Wraps' the given tile to it is within the map. It does * this by masking the 'high' bits of. * @param x the tile to 'wrap' */ +#define TILE_MASK(x) ((x) & _main_map.tile_mask) -#define TILE_MASK(x) ((x) & _map_tile_mask) +void AllocateMap(uint size_x, uint size_y); /** - * Pointer to the tile-array. - * - * This variable points to the tile-array which contains the tiles of - * the map. + * Get the tile map that is bounded to a given tile index. + * @param tile tile index of a tile + * @return the map that contains the tile */ -extern Tile *_m; +template +static inline Map *MapOf(typename TileIndexT::T tile); + +template <> +inline Map *MapOf(TileIndex tile) +{ + return &_main_map; +} + +template <> +inline Map *MapOf(GenericTileIndex tile) +{ + return tile.map; +} + +/** @copydoc MapOf(TileIndexT::T) */ +static inline Map *MapOf(TileIndex tile) { return MapOf(tile); } +/** @copydoc MapOf(TileIndexT::T) */ +static inline Map *MapOf(GenericTileIndex tile) { return MapOf(tile); } /** - * Pointer to the extended tile-array. - * - * This variable points to the extended tile-array which contains the tiles - * of the map. + * Access the "raw" value (offset into map array) of a given tile index. + * @param tile tile index to query + * @return reference to the "raw" value of the index */ -extern TileExtended *_me; +template +static inline RawTileIndex &IndexOf(typename TileIndexT::T &tile); -void AllocateMap(uint size_x, uint size_y); +/** @copydoc IndexOf(TileIndexT::T&) */ +template +static inline const RawTileIndex &IndexOf(const typename TileIndexT::T &tile); + +template <> +inline RawTileIndex &IndexOf(TileIndex &tile) +{ + return tile; +} + +template <> +inline RawTileIndex &IndexOf(GenericTileIndex &tile) +{ + return tile.index; +} + +template <> +inline const RawTileIndex &IndexOf(const TileIndex &tile) +{ + return tile; +} + +template <> +inline const RawTileIndex &IndexOf(const GenericTileIndex &tile) +{ + return tile.index; +} + +/** @copydoc IndexOf(TileIndexT::T&) */ +static inline RawTileIndex &IndexOf(TileIndex &tile) { return IndexOf(tile); } +/** @copydoc IndexOf(TileIndexT::T&) */ +static inline RawTileIndex &IndexOf(GenericTileIndex &tile) { return IndexOf(tile); } +/** @copydoc IndexOf(TileIndexT::T&) */ +static inline const RawTileIndex &IndexOf(const TileIndex &tile) { return IndexOf(tile); } +/** @copydoc IndexOf(TileIndexT::T&) */ +static inline const RawTileIndex &IndexOf(const GenericTileIndex &tile) { return IndexOf(tile); } + +/** + * Get the data of a tile. + * @param tile index of the tile + * @return the tile data + */ +template +static inline Tile *GetTile(typename TileIndexT::T tile) +{ + return &(MapOf(tile)->m[IndexOf(tile)]); +} +/** @copydoc GetTile(TileIndexT::T) */ +static inline Tile *GetTile(TileIndex tile) { return GetTile(tile); } +/** @copydoc GetTile(TileIndexT::T) */ +static inline Tile *GetTile(GenericTileIndex tile) { return GetTile(tile); } + +/** + * Get the extended data of a tile. + * @param tile index of the tile + * @return the extended tile data + */ +template +static inline TileExtended *GetTileEx(typename TileIndexT::T tile) +{ + return &(MapOf(tile)->me[IndexOf(tile)]); +} +/** @copydoc GetTileEx(TileIndexT::T) */ +static inline TileExtended *GetTileEx(TileIndex tile) { return GetTileEx(tile); } +/** @copydoc GetTileEx(TileIndexT::T) */ +static inline TileExtended *GetTileEx(GenericTileIndex tile) { return GetTileEx(tile); } /** * Logarithm of the map size along the X side. @@ -52,8 +136,7 @@ void AllocateMap(uint size_x, uint size_y); */ static inline uint MapLogX() { - extern uint _map_log_x; - return _map_log_x; + return _main_map.log_x; } /** @@ -63,56 +146,57 @@ static inline uint MapLogX() */ static inline uint MapLogY() { - extern uint _map_log_y; - return _map_log_y; + return _main_map.log_y; } /** - * Get the size of the map along the X + * Get the size of a map along the X + * @param map the map * @return the number of tiles along the X of the map */ -static inline uint MapSizeX() +static inline uint MapSizeX(Map *map = &_main_map) { - extern uint _map_size_x; - return _map_size_x; + return map->size_x; } /** - * Get the size of the map along the Y + * Get the size of a map along the Y + * @param map the map * @return the number of tiles along the Y of the map */ -static inline uint MapSizeY() +static inline uint MapSizeY(Map *map = &_main_map) { - extern uint _map_size_y; - return _map_size_y; + return map->size_y; } /** - * Get the size of the map + * Get the size of a map + * @param map the map * @return the number of tiles of the map */ -static inline uint MapSize() +static inline uint MapSize(Map *map = &_main_map) { - extern uint _map_size; - return _map_size; + return map->size; } /** - * Gets the maximum X coordinate within the map, including MP_VOID + * Gets the maximum X coordinate within a map, including MP_VOID + * @param map the map * @return the maximum X coordinate */ -static inline uint MapMaxX() +static inline uint MapMaxX(Map *map = &_main_map) { - return MapSizeX() - 1; + return MapSizeX(map) - 1; } /** - * Gets the maximum Y coordinate within the map, including MP_VOID + * Gets the maximum Y coordinate within a map, including MP_VOID + * @param map the map * @return the maximum Y coordinate */ -static inline uint MapMaxY() +static inline uint MapMaxY(Map *map = &_main_map) { - return MapSizeY() - 1; + return MapSizeY(map) - 1; } /** @@ -151,11 +235,114 @@ static inline uint ScaleByMapSize1D(uint n) * the resulting tileindex of the start tile applied * with this saved difference. * - * @see TileDiffXY(int, int) + * @see TileDiffXY */ typedef int32 TileIndexDiff; /** + * Test if a given tile index is a main map tile index. + * @param tile the tile index to test + * @return \c true if the index points to the main map, \c false otherwise + */ +template +static inline bool IsMainMapTile(typename TileIndexT::T tile); + +template <> +inline bool IsMainMapTile(TileIndex tile) +{ + return true; +} + +template <> +inline bool IsMainMapTile(GenericTileIndex tile) +{ + return MapOf(tile) == &_main_map; +} + +/** @copydoc IsMainMapTile(TileIndexT::T) */ +static inline bool IsMainMapTile(TileIndex tile) { return IsMainMapTile(tile); } +/** @copydoc IsMainMapTile(TileIndexT::T) */ +static inline bool IsMainMapTile(GenericTileIndex tile) { return IsMainMapTile(tile); } + +/** + * Convert a given tile index to a main map tile index. + * + * @param tile the index to convert + * @return the converted index + * + * @pre IsMainMapTile(tile) + */ +template +static inline TileIndex AsMainMapTile(typename TileIndexT::T tile) +{ + assert(IsMainMapTile(tile)); + return (TileIndex)IndexOf(tile); +} +/** @copydoc AsMainMapTile(TileIndexT::T) */ +static inline TileIndex AsMainMapTile(TileIndex tile) { return AsMainMapTile(tile); } +/** @copydoc AsMainMapTile(TileIndexT::T) */ +static inline TileIndex AsMainMapTile(GenericTileIndex tile) { return AsMainMapTile(tile); } + +/** + * Test whether two tile indices point to the same tile map. + * @param a the first tile + * @param b the second tile + * @return MapOf(a) == MapOf(b) + */ +template +static inline bool IsSameMap(typename TileIndexT::T a, typename TileIndexT::T b) +{ + return MapOf(a) == MapOf(b); +} +/** @copydoc IsSameMap(TileIndexT::T,TileIndexT::T) */ +static inline bool IsSameMap(TileIndex a, TileIndex b) { return true; } +/** @copydoc IsSameMap(TileIndexT::T,TileIndexT::T) */ +static inline bool IsSameMap(GenericTileIndex a, GenericTileIndex b) { return IsSameMap(a, b); } + +/** + * Test if a given tile index points to an existing tile. + * + * @param tile the tile index + * @return whether we can access content of this tile + * + * @note the function returns \c true also for #MP_VOID tiles + * @see IsValidTile + * @see IsInnerTile + */ +template +static inline bool IsValidTileIndex(typename TileIndexT::T tile) +{ + return MapOf(tile) != NULL && IndexOf(tile) < MapSize(MapOf(tile)); +} +/** @copydoc IsValidTileIndex(TileIndexT::T) */ +static inline bool IsValidTileIndex(TileIndex tile) { return IsValidTileIndex(tile); } +/** @copydoc IsValidTileIndex(TileIndexT::T) */ +static inline bool IsValidTileIndex(GenericTileIndex tile) { return IsValidTileIndex(tile); } + +/** + * Create a tile index. + * @param index the index of the tile + * @param map the map of the tile + * + * @pre Tgeneric || map == &_main_map + */ +template +static inline typename TileIndexT::T MakeTileIndex(RawTileIndex index, Map *map); + +template <> +inline TileIndex MakeTileIndex(RawTileIndex index, Map *map) +{ + assert(map == &_main_map); + return TileIndex(index); +} + +template <> +inline GenericTileIndex MakeTileIndex(RawTileIndex index, Map *map) +{ + return GenericTileIndex(index, map); +} + +/** * Returns the TileIndex of a coordinate. * * @param x The x coordinate of the tile @@ -168,6 +355,42 @@ static inline TileIndex TileXY(uint x, uint y) } /** + * Returns the tile index of a coordinate. + * + * @param x The x coordinate of the tile + * @param y The y coordinate of the tile + * @param map The map of the tile + * @return The tile index calculated by the coordinate + * + * @pre Tgeneric || map == &_main_map + */ +template +static inline typename TileIndexT::T TileXY(uint x, uint y, Map *map); + +template <> +inline TileIndex TileXY(uint x, uint y, Map *map) +{ + assert(map == &_main_map); + return TileXY(x, y); +} + +template <> +inline GenericTileIndex TileXY(uint x, uint y, Map *map) +{ + return GenericTileIndex(y * MapSizeX(map) + x, map); +} + +/** + * Returns the tile index of a coordinate. + * + * @param x The x coordinate of the tile + * @param y The y coordinate of the tile + * @param map The map of the tile + * @return The tile index calculated by the coordinate + */ +static inline GenericTileIndex TileXY(uint x, uint y, Map *map) { return TileXY(x, y, map); } + +/** * Calculates an offset for the given coordinate(-offset). * * This function calculate an offset value which can be added to an @@ -178,13 +401,13 @@ static inline TileIndex TileXY(uint x, uint y) * @return The resulting offset value of the given coordinate * @see ToTileIndexDiff(TileIndexDiffC) */ -static inline TileIndexDiff TileDiffXY(int x, int y) +static inline TileIndexDiff TileDiffXY(int x, int y, Map *map = &_main_map) { /* Multiplication gives much better optimization on MSVC than shifting. * 0 << shift isn't optimized to 0 properly. * Typically x and y are constants, and then this doesn't result * in any actual multiplication in the assembly code.. */ - return (y * MapSizeX()) + x; + return (y * MapSizeX(map)) + x; } /** @@ -204,60 +427,152 @@ static inline TileIndex TileVirtXY(uint x, uint y) * @param tile the tile to get the X component of * @return the X component */ -static inline uint TileX(TileIndex tile) +template +static inline uint TileX(typename TileIndexT::T tile); + +template <> +inline uint TileX(TileIndex tile) { return tile & MapMaxX(); } +template <> +inline uint TileX(GenericTileIndex tile) +{ + return IndexOf(tile) % MapSizeX(MapOf(tile)); +} + +/** @copydoc TileX(TileIndexT::T) */ +static inline uint TileX(TileIndex tile) { return TileX(tile); } +/** @copydoc TileX(TileIndexT::T) */ +static inline uint TileX(GenericTileIndex tile) { return TileX(tile); } + /** * Get the Y component of a tile * @param tile the tile to get the Y component of * @return the Y component */ -static inline uint TileY(TileIndex tile) +template +static inline uint TileY(typename TileIndexT::T tile); + +template <> +inline uint TileY(TileIndex tile) { return tile >> MapLogX(); } +template <> +inline uint TileY(GenericTileIndex tile) +{ + return IndexOf(tile) / MapSizeX(MapOf(tile)); +} + +/** @copydoc TileX(TileIndexT::T) */ +static inline uint TileY(TileIndex tile) { return TileY(tile); } +/** @copydoc TileX(TileIndexT::T) */ +static inline uint TileY(GenericTileIndex tile) { return TileY(tile); } + /** * Return the offset between to tiles from a TileIndexDiffC struct. * - * This function works like #TileDiffXY(int, int) and returns the + * This function works like #TileDiffXY and returns the * difference between two tiles. * * @param tidc The coordinate of the offset as TileIndexDiffC * @return The difference between two tiles. - * @see TileDiffXY(int, int) + * @see TileDiffXY */ static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc) { return (tidc.y << MapLogX()) + tidc.x; } +/** + * Return the offset between two tiles from a TileIndexDiffC struct. + * + * This function works like #TileDiffXY and returns the + * difference between two tiles. + * + * @param tidc The coordinate of the offset as TileIndexDiffC + * @param map The map array of the tile + * @return The difference between two tiles. + * + * @pre Tgeneric || map == &_main_map + * + * @see TileDiffXY + */ +template +static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map); + +template <> +inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map) +{ + assert(map == &_main_map); + return ToTileIndexDiff(tidc); +} + +template <> +inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map) +{ + return (tidc.y * MapSizeX(map)) + tidc.x; +} + +/** + * Return the offset between two tiles from a TileIndexDiffC struct. + * + * This function works like #TileDiffXY and returns the + * difference between two tiles. + * + * @param tidc The coordinate of the offset as TileIndexDiffC + * @param map The map array of the tile + * @return The difference between two tiles. + * + * @see TileDiffXY + */ +static inline TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc, Map *map) { return ToTileIndexDiff(tidc, map); } + #ifndef _DEBUG /** - * Adds to tiles together. + * Adds a given offset to a tile. + * + * @param tile The tile to add to + * @param delta The offset to add + * @return The resulting tile + */ + #define TILE_ADD(tile, delta) ((tile) + (delta)) + + /** + * Adds a given XY offset to a tile. * - * @param x One tile - * @param y Another tile to add - * @return The resulting tile(index) + * @param tile The tile to add an offset on it + * @param x The x offset to add to the tile + * @param y The y offset to add to the tile + * @return The resulting tile */ - #define TILE_ADD(x, y) ((x) + (y)) + #define TILE_ADDXY(tile, x, y) ((tile) + TileDiffXY(x, y, MapOf(tile))) #else extern TileIndex TileAdd(TileIndex tile, TileIndexDiff add, const char *exp, const char *file, int line); - #define TILE_ADD(x, y) (TileAdd((x), (y), #x " + " #y, __FILE__, __LINE__)) -#endif -/** - * Adds a given offset to a tile. - * - * @param tile The tile to add an offset on it - * @param x The x offset to add to the tile - * @param y The y offset to add to the tile - */ -#define TILE_ADDXY(tile, x, y) TILE_ADD(tile, TileDiffXY(x, y)) + /** + * Adds a given offset to a tile. + * + * @param tile The tile to add to + * @param delta The offset to add + * @return The resulting tile + */ + #define TILE_ADD(tile, delta) (TileAdd((tile), (delta), #tile " + " #delta, __FILE__, __LINE__)) + + GenericTileIndex TileAddXY(GenericTileIndex tile, int x, int y, const char *exp, const char *file, int line); + + static inline TileIndex TileAddXY(TileIndex tile, int x, int y, const char *exp, const char *file, int line) + { + return AsMainMapTile(TileAddXY(GenericTileIndex(tile), x, y, exp, file, line)); + } + + #define TILE_ADDXY(tile, ...) TileAddXY((tile), __VA_ARGS__, #tile " + <" #__VA_ARGS__ ">", __FILE__, __LINE__) +#endif TileIndex TileAddWrap(TileIndex tile, int addx, int addy); @@ -325,6 +640,40 @@ static inline TileIndexDiffC TileIndexToTileIndexDiffC(TileIndex tile_a, TileInd return difference; } +/** + * Get the offset of transformed northern tile corner. + * + * When transforming a tile, it's nothern corner can move to other location. + * The function retuns difference (TileIndexDiffC) between new and old + * locations e.g. when rotating 90 degree left, northern corner becomes + * western and the difference is (1, 0). + * + * Scheme of a tile with corners and offsets:
+ *               N  (0, 0)
+ *             /   \
+ *    (1, 0)  W     E  (0, 1)
+ *             \   /
+ *               S  (1, 1)
+ * 
+ * + * @param transformation The transformation. + * @return Offset to new location of northern corner. + * + * @see TileIndexTransformations + */ +static inline TileIndexDiffC TransformedNorthCornerDiffC(DirTransformation transformation) +{ + /* lookup tables (bit arrays) + * N E S W E S W N */ + static const uint8 DIFF_X = 0 << DTR_IDENTITY | 0 << DTR_ROTATE_90_R | 1 << DTR_ROTATE_180 | 1 << DTR_ROTATE_90_L | 0 << DTR_REFLECT_NE_SW | 1 << DTR_REFLECT_W_E | 1 << DTR_REFLECT_NW_SE | 0 << DTR_REFLECT_N_S; + static const uint8 DIFF_Y = 0 << DTR_IDENTITY | 1 << DTR_ROTATE_90_R | 1 << DTR_ROTATE_180 | 0 << DTR_ROTATE_90_L | 1 << DTR_REFLECT_NE_SW | 1 << DTR_REFLECT_W_E | 0 << DTR_REFLECT_NW_SE | 0 << DTR_REFLECT_N_S; + + assert(IsValidDirTransform(transformation)); + + TileIndexDiffC ret = { (int16)GB(DIFF_X, transformation, 1), (int16)GB(DIFF_Y, transformation, 1) }; + return ret; +} + /* Functions to calculate distances */ uint DistanceManhattan(TileIndex, TileIndex); ///< also known as L1-Norm. Is the shortest distance one could go over diagonal tracks (or roads) uint DistanceSquare(TileIndex, TileIndex); ///< euclidian- or L2-Norm squared @@ -337,15 +686,32 @@ uint DistanceFromEdgeDir(TileIndex, DiagDirection); ///< distance from the map e * Convert a DiagDirection to a TileIndexDiff * * @param dir The DiagDirection + * @param map The tile map (result will be valid only there) * @return The resulting TileIndexDiff + * + * @pre Tgeneric || map == &_main_map + * * @see TileIndexDiffCByDiagDir */ -static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir) +template +static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir, Map *map) { extern const TileIndexDiffC _tileoffs_by_diagdir[DIAGDIR_END]; assert(IsValidDiagDirection(dir)); - return ToTileIndexDiff(_tileoffs_by_diagdir[dir]); + return ToTileIndexDiff(_tileoffs_by_diagdir[dir], map); +} + +/** + * Convert a DiagDirection to a TileIndexDiff + * + * @param dir The DiagDirection + * @return The resulting TileIndexDiff + * @see TileIndexDiffCByDiagDir + */ +static inline TileIndexDiff TileOffsByDiagDir(DiagDirection dir) +{ + return TileOffsByDiagDir(dir, &_main_map); } /** @@ -362,6 +728,15 @@ static inline TileIndexDiff TileOffsByDir(Direction dir) return ToTileIndexDiff(_tileoffs_by_dir[dir]); } +template +static inline TileIndexDiff TileOffsByDir(Direction dir, Map *map) +{ + extern const TileIndexDiffC _tileoffs_by_dir[DIR_END]; + + assert(IsValidDirection(dir)); + return ToTileIndexDiff(_tileoffs_by_dir[dir], map); +} + /** * Adds a DiagDir to a tile. * @@ -369,10 +744,15 @@ static inline TileIndexDiff TileOffsByDir(Direction dir) * @param dir The direction in which we want to step * @return the moved tile */ -static inline TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir) +template +static inline typename TileIndexT::T TileAddByDiagDir(typename TileIndexT::T tile, DiagDirection dir) { - return TILE_ADD(tile, TileOffsByDiagDir(dir)); + return TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x, TileIndexDiffCByDiagDir(dir).y); } +/** @copydoc TileAddByDiagDir(TileIndexT::T,DiagDirection) */ +static inline TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir) { return TileAddByDiagDir(tile, dir); } +/** @copydoc TileAddByDiagDir(TileIndexT::T,DiagDirection) */ +static inline GenericTileIndex TileAddByDiagDir(GenericTileIndex tile, DiagDirection dir) { return TileAddByDiagDir(tile, dir); } /** * Determines the DiagDirection to get from one tile to another. diff --git a/src/map_type.h b/src/map_type.h index 620885e5d..ee419d1f1 100644 --- a/src/map_type.h +++ b/src/map_type.h @@ -37,6 +37,22 @@ struct TileExtended { byte m7; ///< Primarily used for newgrf support }; +/** Tile array. */ +struct Map { + uint size_x; ///< Size of the map along the X + uint size_y; ///< Size of the map along the Y + uint size; ///< The number of tiles on the map + Tile *m; ///< Tiles of the map + TileExtended *me; ///< Extended Tiles of the map +}; + +/** Main tile array. */ +struct MainMap : Map { + uint log_x; ///< 2^log_x == size_x + uint log_y; ///< 2^log_y == size_y + uint tile_mask; ///< size - 1 (to mask the mapsize) +}; + /** * An offset value between to tiles. * @@ -62,9 +78,11 @@ struct TileIndexDiffC { /** Minimal and maximal map width and height */ static const uint MIN_MAP_SIZE_BITS = 6; ///< Minimal size of map is equal to 2 ^ MIN_MAP_SIZE_BITS -static const uint MAX_MAP_SIZE_BITS = 12; ///< Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS +static const uint MAX_MAP_SIZE_BITS = 20; ///< Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS +static const uint MAX_MAP_TILES_BITS = 26; ///< Maximal number of tiles in a map is equal to 2 ^ MAX_MAP_TILES_BITS. static const uint MIN_MAP_SIZE = 1 << MIN_MAP_SIZE_BITS; ///< Minimal map size = 64 -static const uint MAX_MAP_SIZE = 1 << MAX_MAP_SIZE_BITS; ///< Maximal map size = 4096 +static const uint MAX_MAP_SIZE = 1 << MAX_MAP_SIZE_BITS; ///< Maximal map size = 8192 +static const uint MAX_MAP_TILES = 1 << MAX_MAP_TILES_BITS;///< Maximal number of tiles in a map = 2048 * 2048 /** * Approximation of the length of a straight track, relative to a diagonal diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 2d3871ffb..52865aff7 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -123,15 +123,15 @@ public: # define LANDINFOD_LEVEL 1 #endif DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile)); - DEBUG(misc, LANDINFOD_LEVEL, "type = %#x", _m[tile].type); - DEBUG(misc, LANDINFOD_LEVEL, "height = %#x", _m[tile].height); - DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", _m[tile].m1); - DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", _m[tile].m2); - DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", _m[tile].m3); - DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", _m[tile].m4); - DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", _m[tile].m5); - DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", _me[tile].m6); - DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", _me[tile].m7); + DEBUG(misc, LANDINFOD_LEVEL, "type = %#x", GetTile(tile)->type); + DEBUG(misc, LANDINFOD_LEVEL, "height = %#x", GetTile(tile)->height); + DEBUG(misc, LANDINFOD_LEVEL, "m1 = %#x", GetTile(tile)->m1); + DEBUG(misc, LANDINFOD_LEVEL, "m2 = %#x", GetTile(tile)->m2); + DEBUG(misc, LANDINFOD_LEVEL, "m3 = %#x", GetTile(tile)->m3); + DEBUG(misc, LANDINFOD_LEVEL, "m4 = %#x", GetTile(tile)->m4); + DEBUG(misc, LANDINFOD_LEVEL, "m5 = %#x", GetTile(tile)->m5); + DEBUG(misc, LANDINFOD_LEVEL, "m6 = %#x", GetTileEx(tile)->m6); + DEBUG(misc, LANDINFOD_LEVEL, "m7 = %#x", GetTileEx(tile)->m7); #undef LANDINFOD_LEVEL } diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index bc08bc5f1..cd26c733b 100644 --- a/src/network/network_command.cpp +++ b/src/network/network_command.cpp @@ -51,6 +51,7 @@ static CommandCallback * const _callback_table[] = { /* 0x19 */ CcStartStopVehicle, /* 0x1A */ CcGame, /* 0x1B */ CcAddVehicleNewGroup, + /* 0x1C */ CcPaste, }; /** diff --git a/src/newgrf_airport.h b/src/newgrf_airport.h index 5a917c6bd..5ec7c4264 100644 --- a/src/newgrf_airport.h +++ b/src/newgrf_airport.h @@ -28,10 +28,14 @@ struct AirportTileTable { }; /** Iterator to iterate over all tiles belonging to an airport spec. */ -class AirportTileTableIterator : public TileIterator { +template +class AirportTileTableIteratorT : public TileIteratorT { +public: + typedef typename TileIteratorT::TileIndexType TileIndexType; + private: const AirportTileTable *att; ///< The offsets. - TileIndex base_tile; ///< The tile we base the offsets off. + TileIndexType base_tile; ///< The tile we base the offsets off. public: /** @@ -39,17 +43,17 @@ public: * @param att The TileTable we want to iterate over. * @param base_tile The basetile for all offsets. */ - AirportTileTableIterator(const AirportTileTable *att, TileIndex base_tile) : TileIterator(base_tile + ToTileIndexDiff(att->ti)), att(att), base_tile(base_tile) + AirportTileTableIteratorT(const AirportTileTable *att, TileIndexType base_tile) : TileIteratorT(base_tile + ToTileIndexDiff(att->ti, MapOf(base_tile))), att(att), base_tile(base_tile) { } - inline TileIterator& operator ++() + inline TileIteratorT& operator ++() { this->att++; if (this->att->ti.x == -0x80) { - this->tile = INVALID_TILE; + IndexOf(this->tile) = INVALID_TILE_INDEX; } else { - this->tile = this->base_tile + ToTileIndexDiff(this->att->ti); + this->tile = this->base_tile + ToTileIndexDiff(this->att->ti, MapOf(this->base_tile)); } return *this; } @@ -60,12 +64,14 @@ public: return this->att->gfx; } - virtual AirportTileTableIterator *Clone() const + virtual AirportTileTableIteratorT *Clone() const { - return new AirportTileTableIterator(*this); + return new AirportTileTableIteratorT(*this); } }; +typedef AirportTileTableIteratorT AirportTileTableIterator; + /** List of default airport classes. */ enum AirportClassID { APC_BEGIN = 0, ///< Lowest valid airport class id diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 75b06967f..906994480 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -56,7 +56,7 @@ NewGrfDebugSpritePicker _newgrf_debug_sprite_picker = { SPM_NONE, NULL, 0, Small */ static inline uint GetFeatureIndex(uint window_number) { - return GB(window_number, 0, 24); + return GB(window_number, 0, 27); } /** @@ -68,8 +68,8 @@ static inline uint GetFeatureIndex(uint window_number) */ static inline uint GetInspectWindowNumber(GrfSpecFeature feature, uint index) { - assert((index >> 24) == 0); - return (feature << 24) | index; + assert((index >> 27) == 0); + return (feature << 27) | index; } /** @@ -246,7 +246,7 @@ struct NIFeature { */ static inline GrfSpecFeature GetFeatureNum(uint window_number) { - return (GrfSpecFeature)GB(window_number, 24, 8); + return (GrfSpecFeature)GB(window_number, 27, 5); } /** diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 2bc85f9ac..112a5e5d4 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -470,6 +470,8 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette)); } + DrawOverlay(ti, MP_HOUSE); + DrawNewGRFTileSeq(ti, dts, TO_HOUSES, stage, palette); } diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 90a17550d..779b44e0e 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -184,6 +184,8 @@ static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGro } } + DrawOverlay(ti, MP_INDUSTRY); + DrawNewGRFTileSeq(ti, dts, TO_INDUSTRIES, stage, GENERAL_SPRITE_COLOUR(rnd_colour)); } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index cd5dad7b4..8fd51a725 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -676,6 +676,23 @@ CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_til return GetErrorMessageFromLocationCallbackResult(cb_res, statspec->grf_prop.grffile, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } +/** + * Find out whether a given station type has its own layout. + * + * The default TTD station layout can be overloaded by property 0E ("define custom layout"), + * property 0F ("copy custom layout") or by providing callback 24 ("custom station layout"). + * + * @param statspec Station spec. + * @return \c false if the station always uses the default TTD layout, \c true otherwise. + */ +bool IsCustomLayoutStation(const StationSpec *statspec) +{ + if (statspec->lengths != 0) return true; + + StationResolverObject object(statspec, NULL, INVALID_TILE, CBID_STATION_TILE_LAYOUT); + const SpriteGroup *group = object.Resolve(); + return group != NULL && group->type == SGT_CALLBACK; +} /** * Allocate a StationSpec to a Station. This is called once per build operation. diff --git a/src/newgrf_station.h b/src/newgrf_station.h index ffb827cb3..30b43a5e2 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -175,6 +175,8 @@ SpriteID GetCustomStationFoundationRelocation(const StationSpec *statspec, BaseS uint16 GetStationCallback(CallbackID callback, uint32 param1, uint32 param2, const StationSpec *statspec, BaseStation *st, TileIndex tile); CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, byte plat_len, byte numtracks); +bool IsCustomLayoutStation(const StationSpec *statspec); + /* Allocate a StationSpec to a Station. This is called once per build operation. */ int AllocateSpecToStation(const StationSpec *statspec, BaseStation *st, bool exec); diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index f7ba0d995..a2d674e57 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -847,4 +847,5 @@ extern const TileTypeProcs _tile_type_object_procs = { NULL, // vehicle_enter_tile_proc GetFoundation_Object, // get_foundation_proc TerraformTile_Object, // terraform_tile_proc + NULL, // copypaste_tile_proc }; diff --git a/src/object_map.h b/src/object_map.h index 1aaf98434..2258d9a8e 100644 --- a/src/object_map.h +++ b/src/object_map.h @@ -49,7 +49,7 @@ static inline bool IsObjectTypeTile(TileIndex t, ObjectType type) static inline ObjectID GetObjectIndex(TileIndex t) { assert(IsTileType(t, MP_OBJECT)); - return _m[t].m2 | _m[t].m5 << 16; + return GetTile(t)->m2 | GetTile(t)->m5 << 16; } /** @@ -61,7 +61,7 @@ static inline ObjectID GetObjectIndex(TileIndex t) static inline byte GetObjectRandomBits(TileIndex t) { assert(IsTileType(t, MP_OBJECT)); - return _m[t].m3; + return GetTile(t)->m3; } @@ -78,12 +78,12 @@ static inline void MakeObject(TileIndex t, Owner o, ObjectID index, WaterClass w SetTileType(t, MP_OBJECT); SetTileOwner(t, o); SetWaterClass(t, wc); - _m[t].m2 = index; - _m[t].m3 = random; - _m[t].m4 = 0; - _m[t].m5 = index >> 16; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = index; + GetTile(t)->m3 = random; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = index >> 16; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } #endif /* OBJECT_MAP_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index 10c31e904..f423fe764 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -57,6 +57,7 @@ #include "hotkeys.h" #include "newgrf.h" #include "misc/getoptdata.h" +#include "clipboard_func.h" #include "game/game.hpp" #include "game/game_config.hpp" #include "town.h" @@ -962,6 +963,24 @@ static void MakeNewGameDone() MarkWholeScreenDirty(); } +/* + * Too large size may be stored in settings (especially if switching between between OpenTTD + * versions with different map size limits), we have to check if it is valid before generating world. + * Simple separate checking of X and Y map sizes is not enough, as their sum is what counts for the limit. + * Check the size and decrease the larger of the sizes till the size is in limit. + */ +static void FixConfigMapSize() +{ + while (_settings_game.game_creation.map_x + _settings_game.game_creation.map_y > MAX_MAP_TILES_BITS) { + /* Repeat reducing larger of X/Y dimensions until the map size is within allowable limits */ + if (_settings_game.game_creation.map_x > _settings_game.game_creation.map_y) { + _settings_game.game_creation.map_x--; + } else { + _settings_game.game_creation.map_y--; + } + } +} + static void MakeNewGame(bool from_heightmap, bool reset_settings) { _game_mode = GM_NORMAL; @@ -969,6 +988,7 @@ static void MakeNewGame(bool from_heightmap, bool reset_settings) ResetGRFConfig(true); GenerateWorldSetCallback(&MakeNewGameDone); + FixConfigMapSize(); GenerateWorld(from_heightmap ? GWM_HEIGHTMAP : GWM_NEWGAME, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y, reset_settings); } @@ -984,6 +1004,7 @@ static void MakeNewEditorWorld() ResetGRFConfig(true); GenerateWorldSetCallback(&MakeNewEditorWorldDone); + FixConfigMapSize(); GenerateWorld(GWM_EMPTY, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); } @@ -1072,8 +1093,13 @@ void SwitchToMode(SwitchMode new_mode) } } #endif /* ENABLE_NETWORK */ - /* Make sure all AI controllers are gone at quitting game */ - if (new_mode != SM_SAVE_GAME) AI::KillAll(); + if (new_mode != SM_SAVE_GAME) { + /* Make sure all AI controllers are gone at quitting game */ + AI::KillAll(); + + /* Clear the clipboard */ + ClearClipboard(); + } switch (new_mode) { case SM_EDITOR: // Switch to scenario editor @@ -1130,6 +1156,7 @@ void SwitchToMode(SwitchMode new_mode) case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor SetLocalCompany(OWNER_NONE); + FixConfigMapSize(); GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); MarkWholeScreenDirty(); break; @@ -1172,6 +1199,7 @@ void SwitchToMode(SwitchMode new_mode) case SM_GENRANDLAND: // Generate random land within scenario editor SetLocalCompany(OWNER_NONE); + FixConfigMapSize(); GenerateWorld(GWM_RANDOM, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y); /* XXX: set date */ MarkWholeScreenDirty(); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 57b29f3f5..18e634425 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1897,14 +1897,14 @@ restart: /** * Checks if a vehicle has a depot in its order list. - * @return True iff at least one order is a depot order. + * @return True iff at least one order is a depot order or a jump with condition on requires service. */ bool Vehicle::HasDepotOrder() const { const Order *order; FOR_VEHICLE_ORDERS(this, order) { - if (order->IsType(OT_GOTO_DEPOT)) return true; + if (order->IsType(OT_GOTO_DEPOT) || (order->IsType(OT_CONDITIONAL) && (order->GetConditionVariable() == OCV_REQUIRES_SERVICE))) return true; } return false; diff --git a/src/overlay.h b/src/overlay.h new file mode 100644 index 000000000..f7d8e2d75 --- /dev/null +++ b/src/overlay.h @@ -0,0 +1,73 @@ +/* $Id$ */ + +/** @file overlay.h Functions related to overlays. */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "stdafx.h" +#include "openttd.h" +#include "core/bitmath_func.hpp" +#include "gfx_func.h" + +/** + * Transparency option bits: which position in _transparency_opt stands for which transparency. + * If you change the order, change the order of the ShowTransparencyToolbar() stuff in transparency_gui.cpp too. + * If you add or remove an option don't forget to change the transparency 'hot keys' in main_gui.cpp. + */ +enum OverlayOption { + OO_COVERAGES = 0, ///< coverage + OO_END, +}; + +typedef uint OverlayOptionBits; ///< overlay option bits +extern OverlayOptionBits _overlay_opt; +extern OverlayOptionBits _overlay_lock; + +/** + * Check if the overlay option bit is set + * and if we aren't in the game menu (there's no overlay) + * + * @param to the structure which overlay option is ask for + */ +static inline bool IsOverlaySet(OverlayOption to) +{ + return (HasBit(_overlay_opt, to) && _game_mode != GM_MENU); +} + +/** + * Toggle the overlay option bit + * + * @param to the overlay option to be toggled + */ +static inline void ToggleOverlay(OverlayOption to) +{ + ToggleBit(_overlay_opt, to); +} + +/** + * Toggle the overlay lock bit + * + * @param to the overlay option to be locked or unlocked + */ +static inline void ToggleOverlayLock(OverlayOption to) +{ + ToggleBit(_overlay_lock, to); +} + +/** Set or clear all non-locked overlay options */ +static inline void ResetRestoreAllOverlays() +{ + /* if none of the non-locked options are set */ + if ((_overlay_opt & ~_overlay_lock) == 0) { + /* set all non-locked options */ + _overlay_opt |= GB(~_overlay_lock, 0, OO_END); + } else { + /* clear all non-locked options */ + _overlay_opt &= _overlay_lock; + } + + MarkWholeScreenDirty(); +} + +#endif /* OVERLAY_H */ \ No newline at end of file diff --git a/src/overlay_cmd.cpp b/src/overlay_cmd.cpp new file mode 100644 index 000000000..059121d8f --- /dev/null +++ b/src/overlay_cmd.cpp @@ -0,0 +1,65 @@ +/* $Id$ */ + +/** @file overlay_cmd.cpp Handling of overlays. */ + +#include "stdafx.h" +#include "tile_type.h" +#include "tile_cmd.h" +#include "overlay.h" +#include "station_func.h" +#include "viewport_func.h" +#include "overlay_cmd.h" + +Overlays* Overlays::instance = NULL; + +Overlays* Overlays::Instance() +{ + if (instance == NULL) + instance = new Overlays(); + return instance; +}; + +void Overlays::AddStation(const Station* st) +{ + this->catchmentOverlay.insert(st); +}; + +void Overlays::RemoveStation(const Station* st) +{ + this->catchmentOverlay.erase(st); +}; + +void Overlays::ToggleStation(const Station* st) +{ + if(this->HasStation(st)) { + this->RemoveStation(st); + } else { + this->AddStation(st); + } +}; + +void Overlays::Clear() +{ + this->catchmentOverlay.clear(); +}; + +bool Overlays::IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) +{ + for(std::set::iterator iter = catchmentOverlay.begin();iter != catchmentOverlay.end();) { + const Station *st = *iter; + if( st->IsTileInCatchmentArea(ti, type)) + return true; + iter++; + } + return false; +}; + +bool Overlays::HasStation(const Station* st) +{ + return (this->catchmentOverlay.find(st) != this->catchmentOverlay.end()); +}; + +Overlays::~Overlays() +{ + this->catchmentOverlay.clear(); +}; \ No newline at end of file diff --git a/src/overlay_cmd.h b/src/overlay_cmd.h new file mode 100644 index 000000000..a8b43832f --- /dev/null +++ b/src/overlay_cmd.h @@ -0,0 +1,38 @@ +/* $Id$ */ + +/** @file overlay_cmd.h Functions related to overlays. */ + +#ifndef OVERLAY_CMD_H +#define OVERLAY_CMD_H + +#include "tile_type.h" +#include "tile_cmd.h" +#include "station_base.h" +#include + +class Overlays { + + std::set catchmentOverlay; + +protected: + static Overlays* instance; + +public: + static Overlays* Instance(); + + void AddStation(const Station* st); + + void RemoveStation(const Station *st); + + void ToggleStation(const Station* st); + + void Clear(); + + bool IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type); + + bool HasStation(const Station* st); + + virtual ~Overlays(); +}; + +#endif // OVERLAY_CMD_H \ No newline at end of file diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 9f19b029c..76f33407f 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -358,7 +358,7 @@ protected: if (IsTunnel(m_new_tile)) { if (!m_is_tunnel) { DiagDirection tunnel_enterdir = GetTunnelBridgeDirection(m_new_tile); - if (tunnel_enterdir != m_exitdir) { + if (tunnel_enterdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { m_err = EC_NO_WAY; return false; } @@ -366,7 +366,7 @@ protected: } else { // IsBridge(m_new_tile) if (!m_is_bridge) { DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile); - if (ramp_enderdir != m_exitdir) { + if (ramp_enderdir != m_exitdir || IsTunnelBridgeExit(m_new_tile)) { m_err = EC_NO_WAY; return false; } diff --git a/src/rail.cpp b/src/rail.cpp index d538064a2..fff67c68c 100644 --- a/src/rail.cpp +++ b/src/rail.cpp @@ -151,10 +151,23 @@ extern const TrackdirBits _uphill_trackdirs[] = { TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE, ///< 30 SLOPE_STEEP_E -> inclined for diagonal track }; +/** Lookup table to transform a Track by a given DirTransformation. */ +extern const byte _track_transformation_map[DTR_END][TRACK_END] = { + { TRACK_X, TRACK_Y, TRACK_UPPER, TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT }, // DTR_IDENTITY + { TRACK_Y, TRACK_X, TRACK_RIGHT, TRACK_LEFT, TRACK_UPPER, TRACK_LOWER }, // DTR_ROTATE_90_R + { TRACK_X, TRACK_Y, TRACK_LOWER, TRACK_UPPER, TRACK_RIGHT, TRACK_LEFT }, // DTR_ROTATE_180 + { TRACK_Y, TRACK_X, TRACK_LEFT, TRACK_RIGHT, TRACK_LOWER, TRACK_UPPER }, // DTR_ROTATE_90_L + { TRACK_X, TRACK_Y, TRACK_RIGHT, TRACK_LEFT, TRACK_LOWER, TRACK_UPPER }, // DTR_REFLECT_NE_SW + { TRACK_Y, TRACK_X, TRACK_LOWER, TRACK_UPPER, TRACK_LEFT, TRACK_RIGHT }, // DTR_REFLECT_W_E + { TRACK_X, TRACK_Y, TRACK_LEFT, TRACK_RIGHT, TRACK_UPPER, TRACK_LOWER }, // DTR_REFLECT_NW_SE + { TRACK_Y, TRACK_X, TRACK_UPPER, TRACK_LOWER, TRACK_RIGHT, TRACK_LEFT } // DTR_REFLECT_N_S +}; + /** * Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile. */ -RailType GetTileRailType(TileIndex tile) +template +RailType GetTileRailType(typename TileIndexT::T tile) { switch (GetTileType(tile)) { case MP_RAILWAY: @@ -178,6 +191,9 @@ RailType GetTileRailType(TileIndex tile) } return INVALID_RAILTYPE; } +/* instantiate */ +template RailType GetTileRailType(TileIndex tile); +template RailType GetTileRailType(GenericTileIndex tile); /** * Finds out if a company has a certain railtype available diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 5582666b1..477549f6a 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "cmd_helper.h" +#include "copypaste_cmd.h" #include "viewport_func.h" #include "command_func.h" #include "depot_base.h" @@ -33,6 +34,7 @@ #include "strings_func.h" #include "company_gui.h" #include "object_map.h" +#include "clipboard_gui.h" #include "table/strings.h" #include "table/railtypes.h" @@ -431,24 +433,27 @@ static inline bool ValParamTrackOrientation(Track track) } /** - * Build a single piece of rail - * @param tile tile to build on + * Build a set of rail tracks on a given tile + * @param tile tile to build on * @param flags operation to perform * @param p1 railtype of being built piece (normal, mono, maglev) - * @param p2 rail track to build + * @param p2 TrackBits to build * @param text unused * @return the cost of this operation or an error */ -CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdBuildSingleRails(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { RailType railtype = Extract(p1); - Track track = Extract(p2); + TrackBits trackbits = (TrackBits)GB(p2, 0, 6); CommandCost cost(EXPENSES_CONSTRUCTION); - if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; + if (!ValParamRailtype(railtype)) return CMD_ERROR; Slope tileh = GetTileSlope(tile); - TrackBits trackbit = TrackToTrackBits(track); + TrackBits currbits = TRACK_BIT_NONE; + + /* whether the tile needs to be cleared and built from scratch */ + bool make_new_rail_tile = true; switch (GetTileType(tile)) { case MP_RAILWAY: { @@ -459,11 +464,12 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION); - ret = CheckTrackCombination(tile, trackbit, flags); - if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track); + currbits = GetTrackBits(tile); + ret = CheckTrackCombination(tile, trackbits, flags); + if (ret.Succeeded()) ret = EnsureNoTrainOnTrackBits(tile, trackbits & ~currbits); if (ret.Failed()) return ret; - ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile); + ret = CheckRailSlope(tileh, trackbits, GetTrackBits(tile), tile); if (ret.Failed()) return ret; cost.AddCost(ret); @@ -480,20 +486,21 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u } } - if (flags & DC_EXEC) { + if ((trackbits & ~currbits) && (flags & DC_EXEC)) { SetRailGroundType(tile, RAIL_GROUND_BARREN); - TrackBits bits = GetTrackBits(tile); - SetTrackBits(tile, bits | trackbit); + SetTrackBits(tile, currbits | trackbits); /* Subtract old infrastructure count. */ - uint pieces = CountBits(bits); - if (TracksOverlap(bits)) pieces *= pieces; + uint pieces = CountBits(currbits); + if (TracksOverlap(currbits)) pieces *= pieces; Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces; /* Add new infrastructure count. */ - pieces = CountBits(bits | trackbit); - if (TracksOverlap(bits | trackbit)) pieces *= pieces; + pieces = CountBits(currbits | trackbits); + if (TracksOverlap(currbits | trackbits)) pieces *= pieces; Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces; DirtyCompanyInfrastructureWindows(GetTileOwner(tile)); } + + make_new_rail_tile = false; break; } @@ -514,8 +521,8 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u RoadTypes roadtypes = GetRoadTypes(tile); RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); - if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) || - (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) { + if ((trackbits == TRACK_BIT_X && ((road | tram) & ROAD_X) == 0) || + (trackbits == TRACK_BIT_Y && ((road | tram) & ROAD_Y) == 0)) { Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); /* Disallow breaking end-of-line of someone else @@ -534,7 +541,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u cost.AddCost((num_new_road_pieces + num_new_tram_pieces) * _price[PR_BUILD_ROAD]); if (flags & DC_EXEC) { - MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); + MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (trackbits == TRACK_BIT_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); UpdateLevelCrossing(tile, false); Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); @@ -547,54 +554,76 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u DirtyCompanyInfrastructureWindows(tram_owner); } } - break; + make_new_rail_tile = false; } + } else if (IsLevelCrossing(tile)) { + currbits = GetCrossingRailBits(tile); + if (trackbits == currbits) return_cmd_error(STR_ERROR_ALREADY_BUILT); } - - if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) { - return_cmd_error(STR_ERROR_ALREADY_BUILT); - } - /* FALL THROUGH */ + break; } - default: { - /* Will there be flat water on the lower halftile? */ - bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); + default: + break; + } - CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); - if (ret.Failed()) return ret; - cost.AddCost(ret); + if (make_new_rail_tile) { + /* Will there be flat water on the lower halftile? */ + bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); - ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); - if (ret.Failed()) return ret; - cost.AddCost(ret); + CommandCost ret = CheckRailSlope(tileh, trackbits, TRACK_BIT_NONE, tile); + if (ret.Failed()) return ret; + cost.AddCost(ret); - if (water_ground) { - cost.AddCost(-_price[PR_CLEAR_WATER]); - cost.AddCost(_price[PR_CLEAR_ROUGH]); - } + ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret.Failed()) return ret; + cost.AddCost(ret); + currbits = TRACK_BIT_NONE; // the tile is clear now - if (flags & DC_EXEC) { - MakeRailNormal(tile, _current_company, trackbit, railtype); - if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); - Company::Get(_current_company)->infrastructure.rail[railtype]++; - DirtyCompanyInfrastructureWindows(_current_company); - } - break; + if (water_ground) { + cost.AddCost(-_price[PR_CLEAR_WATER]); + cost.AddCost(_price[PR_CLEAR_ROUGH]); + } + + if (flags & DC_EXEC) { + MakeRailNormal(tile, _current_company, trackbits, railtype); + if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); + Company::Get(_current_company)->infrastructure.rail[railtype]++; + DirtyCompanyInfrastructureWindows(_current_company); } } if (flags & DC_EXEC) { MarkTileDirtyByTile(tile); - AddTrackToSignalBuffer(tile, track, _current_company); - YapfNotifyTrackLayoutChange(tile, track); + Track track; + FOR_EACH_SET_TRACK(track, trackbits & ~currbits) { + AddTrackToSignalBuffer(tile, track, _current_company); + YapfNotifyTrackLayoutChange(tile, track); + } } - cost.AddCost(RailBuildCost(railtype)); + cost.AddCost(CountBits(trackbits & ~currbits) * RailBuildCost(railtype)); return cost; } /** + * Build a single piece of rail + * @param tile tile to build on + * @param flags operation to perform + * @param p1 railtype of being built piece (normal, mono, maglev) + * @param p2 rail track to build + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + Track track = Extract(p2); + if (!ValParamTrackOrientation(track)) return CMD_ERROR; + + return CmdBuildSingleRails(tile, flags, p1, TrackToTrackBits(track), text); +} + +/** * Remove a single piece of track * @param tile tile to remove track from * @param flags operation to perform @@ -1043,9 +1072,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, if (sigtype > SIGTYPE_LAST) return CMD_ERROR; if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR; - /* You can only build signals on plain rail tiles, and the selected track must exist */ - if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || - !HasTrack(tile, track)) { + /* You can only build signals on plain rail tiles or tunnel/bridges, and the selected track must exist */ + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR; + CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track); + if (ret.Failed()) return ret; + } else if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); } /* Protect against invalid signal copying */ @@ -1054,6 +1086,52 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; + CommandCost cost(EXPENSES_CONSTRUCTION); + /* handle signals simulation on tunnel/bridge. */ + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + TileIndex tile_exit = GetOtherTunnelBridgeEnd(tile); + cost = CommandCost(); + if (!HasWormholeSignals(tile)) { // toggle signal zero costs. + if (p2 != 12) cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2)); // minimal 1 + } + if (flags & DC_EXEC) { + if (p2 == 0 && HasWormholeSignals(tile)){ // Toggle signal if already signals present. + if (IsTunnelBridgeEntrance (tile)) { + ClrBitTunnelBridgeSignal(tile); + ClrBitTunnelBridgeExit(tile_exit); + SetBitTunnelBridgeExit(tile); + SetBitTunnelBridgeSignal(tile_exit); + } else { + ClrBitTunnelBridgeSignal(tile_exit); + ClrBitTunnelBridgeExit(tile); + SetBitTunnelBridgeExit(tile_exit); + SetBitTunnelBridgeSignal(tile); + } + } else{ + /* Create one direction tunnel/bridge if required. */ + if (p2 == 0) { + SetBitTunnelBridgeSignal(tile); + SetBitTunnelBridgeExit(tile_exit); + } else if (p2 == 4 || p2 == 8) { + DiagDirection tbdir = GetTunnelBridgeDirection(tile); + /* If signal only on one side build accoringly one-way tunnel/bridge. */ + if ((p2 == 8 && (tbdir == DIAGDIR_NE || tbdir == DIAGDIR_SE)) || + (p2 == 4 && (tbdir == DIAGDIR_SW || tbdir == DIAGDIR_NW))) { + SetBitTunnelBridgeSignal(tile); + SetBitTunnelBridgeExit(tile_exit); + } else { + SetBitTunnelBridgeSignal(tile_exit); + SetBitTunnelBridgeExit(tile); + } + } + } + MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile_exit); + AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company); + YapfNotifyTrackLayoutChange(tile, track); + } + return cost; + } /* See if this is a valid track combination for signals (no overlap) */ if (TracksOverlap(GetTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); @@ -1063,28 +1141,25 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, /* you can not convert a signal if no signal is on track */ if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); - CommandCost cost; if (!HasSignalOnTrack(tile, track)) { /* build new signals */ - cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); + cost.AddCost(_price[PR_BUILD_SIGNALS]); } else { if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) { /* convert signals <-> semaphores */ - cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); - + cost.AddCost(_price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); } else if (convert_signal) { /* convert button pressed */ if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) { /* convert electric <-> semaphore */ - cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); - } else { + cost .AddCost(_price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); + } else if (GetSignalType(tile, track) != sigtype) { /* it is free to change signal type: normal-pre-exit-combo */ - cost = CommandCost(); + } else { + return_cmd_error(STR_ERROR_ALREADY_BUILT); } - } else { /* it is free to change orientation/pre-exit-combo signals */ - cost = CommandCost(); } } @@ -1221,6 +1296,7 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal return true; case MP_TUNNELBRIDGE: { + if (!remove && HasWormholeSignals(tile)) return false; TileIndex orig_tile = tile; // backup old value if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false; @@ -1332,7 +1408,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin bool had_success = false; for (;;) { /* only build/remove signals with the specified density */ - if (remove || minimise_gaps || signal_ctr % signal_density == 0) { + if (remove || minimise_gaps || signal_ctr % signal_density == 0 || IsTileType(tile, MP_TUNNELBRIDGE)) { uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3); SB(p1, 3, 1, mode); SB(p1, 4, 1, semaphores); @@ -1371,6 +1447,14 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin /* Be user-friendly and try placing signals as much as possible */ if (ret.Succeeded()) { had_success = true; + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + if ((!autofill && GetTunnelBridgeDirection(tile) == TrackdirToExitdir(trackdir)) || + (autofill && GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir))) { + total_cost.AddCost(ret); + } + } else { + total_cost.AddCost(ret); + } total_cost.AddCost(ret); last_used_ctr = last_suitable_ctr; last_suitable_tile = INVALID_TILE; @@ -1445,12 +1529,26 @@ CommandCost CmdBuildSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Track track = Extract(p1); + Money cost = _price[PR_CLEAR_SIGNALS]; - if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { - return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); - } - if (!HasSignalOnTrack(tile, track)) { - return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + TileIndex end = GetOtherTunnelBridgeEnd(tile); + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); + if (!HasWormholeSignals(tile)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); + + cost *= ((GetTunnelBridgeLength(tile, end) + 4) >> 2); + + CommandCost ret = EnsureNoTrainOnTrack(GetOtherTunnelBridgeEnd(tile), track); + if (ret.Failed()) return ret; + } else { + if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) { + return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK); + } + if (!HasSignalOnTrack(tile, track)) { + return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS); + } + CommandCost ret = EnsureNoTrainOnTrack(tile, track); + if (ret.Failed()) return ret; } /* Only water can remove signals from anyone */ @@ -1461,6 +1559,20 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1 /* Do it? */ if (flags & DC_EXEC) { + + if (HasWormholeSignals(tile)) { // handle tunnel/bridge signals. + TileIndex end = GetOtherTunnelBridgeEnd(tile); + ClrBitTunnelBridgeExit(tile); + ClrBitTunnelBridgeExit(end); + ClrBitTunnelBridgeSignal(tile); + ClrBitTunnelBridgeSignal(end); + GetTile(tile)->m2 = 0; + GetTile(end)->m2 = 0; + MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(end); + return CommandCost(EXPENSES_CONSTRUCTION, cost); + } + Train *v = NULL; if (HasReservedTracks(tile, TrackToTrackBits(track))) { v = GetTrainForReservation(tile, track); @@ -1496,7 +1608,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1 MarkTileDirtyByTile(tile); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]); + return CommandCost(EXPENSES_CONSTRUCTION, cost); } /** @@ -2402,6 +2514,8 @@ static void DrawTile_Track(TileInfo *ti) if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti); + DrawOverlay(ti, MP_RAILWAY); + if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti); @@ -2477,6 +2591,8 @@ static void DrawTile_Track(TileInfo *ti) int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT); relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset(); + DrawOverlay(ti, MP_RAILWAY); + if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette); @@ -3034,6 +3150,205 @@ static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); } +void CopyPastePlaceTracks(GenericTileIndex tile, RailType railtype, TrackBits tracks) +{ + if (IsMainMapTile(tile)) { + _current_pasting->DoCommand(AsMainMapTile(tile), railtype, tracks, CMD_BUILD_SINGLE_RAILS | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK)); + } else { + MakeRailNormal(tile, OWNER_NONE, tracks, railtype); + } +} + +/** + * Estimate signal paste cost. + * @param tile The tile where to paste. + * @param tile The track where to put the signal (it may not exist, yet). + * @param variant The variant of the signal (semaphore/electric) + * @param type The type of the signal (normal/combo/pbs) + * @param no_sig_trackdir Signal orientation, the trackdir where a signal is NOT present (INVALID_TRACKDIR = two way signal) + */ +static CommandCost EstimateSignalPasteCost(TileIndex tile, Track track, SignalVariant variant, SignalType type, Trackdir no_sig_trackdir) +{ + switch (GetTileType(tile)) { + case MP_CLEAR: + case MP_TREES: + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); + + case MP_RAILWAY: + if (!IsPlainRail(tile)) return CMD_ERROR; + if (TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); + if (!HasSignalOnTrack(tile, track)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); + if (GetSignalVariant(tile, track) != variant) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS] + _price[PR_BUILD_SIGNALS]); + if (GetSignalType(tile, track) != type) return CommandCost(EXPENSES_CONSTRUCTION, 0); + if (IsValidTrackdir(no_sig_trackdir)) { + assert(TrackdirToTrack(no_sig_trackdir) == track); + if (HasSignalOnTrackdir(tile, no_sig_trackdir)) return CommandCost(EXPENSES_CONSTRUCTION, 0); + } else { + assert(!IsPbsSignal(type)); + if (!HasSignalOnTrackdir(tile, TrackToTrackdir(track)) || !HasSignalOnTrackdir(tile, ReverseTrackdir(TrackToTrackdir(track)))) { + return CommandCost(EXPENSES_CONSTRUCTION, 0); + } + } + return_cmd_error(STR_ERROR_ALREADY_BUILT); + + case MP_ROAD: + return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST); + + default: + break; + } + return CMD_ERROR; +} + +static void CopyPastePlaceSignal(GenericTileIndex tile, Track track, SignalVariant variant, SignalType type, Trackdir no_sig_trackdir) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + if (!(_current_pasting->dc_flags & DC_EXEC)) { + _current_pasting->CollectCost(EstimateSignalPasteCost(t, track, variant, type, no_sig_trackdir), t, STR_ERROR_CAN_T_BUILD_SIGNALS_HERE); + } else { + /* build the signal */ + uint32 p1 = track | (variant << 4) | (type << 5); + if (IsTileType(t, MP_RAILWAY) && HasSignalOnTrack(t, track)) p1 |= (1 << 8); // convert existing + _current_pasting->DoCommand(t, p1, 0, CMD_BUILD_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_BUILD_SIGNALS_HERE)); + + /* cycle until the proper signal side */ + if ((_current_pasting->last_result.Succeeded() || _current_pasting->last_result.GetErrorMessage() == STR_ERROR_ALREADY_BUILT)) { + if (IsValidTrackdir(no_sig_trackdir)) { + assert(TrackdirToTrack(no_sig_trackdir) == track); + while (HasSignalOnTrackdir(t, no_sig_trackdir)) { + _current_pasting->DoCommand(t, track, 0, CMD_BUILD_SIGNALS); + if (_current_pasting->last_result.Failed()) break; + } + } else { + assert(!IsPbsSignal(GetSignalType(t, track))); + while (!HasSignalOnTrackdir(t, TrackToTrackdir(track)) || + !HasSignalOnTrackdir(t, ReverseTrackdir(TrackToTrackdir(track)))) { + _current_pasting->DoCommand(t, track, 0, CMD_BUILD_SIGNALS); + if (_current_pasting->last_result.Failed()) break; + } + } + } + } + } else { + SetHasSignals(tile, true); + SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track)); + SetSignalVariant(tile, track, variant); + SetSignalType(tile, track, type); + if (IsValidTrackdir(no_sig_trackdir)) { + assert(TrackdirToTrack(no_sig_trackdir) == track); + while (HasSignalOnTrackdir(tile, no_sig_trackdir)) CycleSignalSide(tile, track); + } + } +} + +static void CopyPastePlaceRailDepot(GenericTileIndex tile, RailType railtype, DiagDirection dir) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + if (IsRailDepotTile(t) && IsTileOwner(t, _current_company) && + GetRailDepotDirection(t) == dir && GetRailType(t) == railtype) { + _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT); + } else { + _current_pasting->DoCommand(t, railtype, dir, CMD_BUILD_TRAIN_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_DEPOT)); + } + } else { + MakeRailDepot(tile, OWNER_NONE, 0, dir, railtype); + } +} + +/** + * Test a given rail tile if there is any contented to be copied from it. + * @param tile the tile to test + * @param mode copy-paste mode + * @param company the #Company to check ownership against to + * @param preview (out, may be NULL) information on how to higlight preview of the tile + * @return whether this tile needs to be copy-pasted + */ +bool TestRailTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) +{ + if (preview != NULL) MemSetT(preview, 0); + + if (!(mode & CPM_WITH_RAIL_TRANSPORT)) return false; + if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; + + if (preview != NULL) { + switch (GetRailTileType(tile)) { + case RAIL_TILE_NORMAL: + case RAIL_TILE_SIGNALS: + preview->highlight_track_bits = GetTrackBits(tile); + break; + + case RAIL_TILE_DEPOT: + preview->highlight_tile_rect = true; + preview->highlight_track_bits = TrackToTrackBits(GetRailDepotTrack(tile)); + break; + } + } + + return true; +} + +void CopyPasteTile_Rail(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) +{ + if (!TestRailTileCopyability(src_tile, copy_paste.mode)) return; + + /* Terraform tiles if needed */ + if (IsMainMapTile(dst_tile) && ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL)) { + CopyPasteHeights(GenericTileArea(src_tile, 1, 1), dst_tile, copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + } + + RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); + + switch (GetRailTileType(src_tile)) { + case RAIL_TILE_NORMAL: + /* copy/paste tracks */ + CopyPastePlaceTracks(dst_tile, railtype, TransformTrackBits(GetTrackBits(src_tile), copy_paste.transformation)); + break; + + case RAIL_TILE_SIGNALS: { + /* copy/paste tracks */ + CopyPastePlaceTracks(dst_tile, railtype, TransformTrackBits(GetTrackBits(src_tile), copy_paste.transformation)); + if (IsPastingInterrupted()) return; + + /* copy/paste signals */ + Track src_track; + uint signal_bits = GetPresentSignals(src_tile); + FOR_EACH_SET_TRACK(src_track, GetTrackBits(src_tile)) { + /* extract signal bits related to curent track */ + uint track_signal_bits = signal_bits & SignalOnTrack(src_track); + if (track_signal_bits == 0) continue; + signal_bits &= ~track_signal_bits; + + /* calculate trackdir pointing to the back of the signal (INVALID_TRACKDIR for two-way signals) */ + Trackdir dst_no_sig_trackdir; + if (HasExactlyOneBit(track_signal_bits)) { + dst_no_sig_trackdir = TransformTrackdir(TrackToTrackdir(src_track), copy_paste.transformation); + if (HasSignalOnTrackdir(src_tile, TrackToTrackdir(src_track)) != ((copy_paste.mode & CPM_MIRROR_SIGNALS) != 0)) { + dst_no_sig_trackdir = ReverseTrackdir(dst_no_sig_trackdir); + } + } else { + dst_no_sig_trackdir = INVALID_TRACKDIR; + } + + /* place the signal */ + CopyPastePlaceSignal(dst_tile, TransformTrack(src_track, copy_paste.transformation), GetSignalVariant(src_tile, src_track), GetSignalType(src_tile, src_track), dst_no_sig_trackdir); + if (IsPastingInterrupted()) return; + + if (signal_bits == 0) break; // no more signals to paste + } + break; + } + + case RAIL_TILE_DEPOT: // Rail depot + CopyPastePlaceRailDepot(dst_tile, railtype, TransformDiagDir(GetRailDepotDirection(src_tile), copy_paste.transformation)); + break; + + default: + NOT_REACHED(); // corrupted tile data? + } +} extern const TileTypeProcs _tile_type_rail_procs = { DrawTile_Track, // draw_tile_proc @@ -3050,4 +3365,5 @@ extern const TileTypeProcs _tile_type_rail_procs = { VehicleEnter_Track, // vehicle_enter_tile_proc GetFoundation_Track, // get_foundation_proc TerraformTile_Track, // terraform_tile_proc + CopyPasteTile_Rail, // copypaste_tile_proc }; diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 73fe29da0..f7c208319 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -192,7 +192,7 @@ static void PlaceRail_Station(TileIndex tile) VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); VpSetPlaceSizingLimit(_settings_game.station.station_spread); } else { - uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | _ctrl_pressed << 24; + uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | (_settings_game.station.adjacent_stations && _ctrl_pressed) << 24; uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; int w = _settings_client.gui.station_numtracks; @@ -886,7 +886,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); - uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | _ctrl_pressed << 24; + uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | (_settings_game.station.adjacent_stations && _ctrl_pressed) << 24; uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" }; diff --git a/src/rail_map.h b/src/rail_map.h index 2431a7920..b185d155f 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -34,11 +34,16 @@ enum RailTileType { * @pre IsTileType(t, MP_RAILWAY) * @return the RailTileType */ -static inline RailTileType GetRailTileType(TileIndex t) +template +static inline RailTileType GetRailTileType(typename TileIndexT::T t) { assert(IsTileType(t, MP_RAILWAY)); - return (RailTileType)GB(_m[t].m5, 6, 2); + return (RailTileType)GB(GetTile(t)->m5, 6, 2); } +/** @copydoc GetRailTileType(TileIndexT::T) */ +static inline RailTileType GetRailTileType(TileIndex t) { return GetRailTileType(t); } +/** @copydoc GetRailTileType(TileIndexT::T) */ +static inline RailTileType GetRailTileType(GenericTileIndex t) { return GetRailTileType(t); } /** * Returns whether this is plain rails, with or without signals. Iow, if this @@ -47,21 +52,31 @@ static inline RailTileType GetRailTileType(TileIndex t) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is normal rail (with or without signals) */ -static inline bool IsPlainRail(TileIndex t) +template +static inline bool IsPlainRail(typename TileIndexT::T t) { RailTileType rtt = GetRailTileType(t); return rtt == RAIL_TILE_NORMAL || rtt == RAIL_TILE_SIGNALS; } +/** @copydoc IsPlainRail(TileIndexT::T) */ +static inline bool IsPlainRail(TileIndex t) { return IsPlainRail(t); } +/** @copydoc IsPlainRail(TileIndexT::T) */ +static inline bool IsPlainRail(GenericTileIndex t) { return IsPlainRail(t); } /** * Checks whether the tile is a rail tile or rail tile with signals. * @param t the tile to get the information from * @return true if and only if the tile is normal rail (with or without signals) */ -static inline bool IsPlainRailTile(TileIndex t) +template +static inline bool IsPlainRailTile(typename TileIndexT::T t) { return IsTileType(t, MP_RAILWAY) && IsPlainRail(t); } +/** @copydoc IsPlainRailTile(TileIndexT::T) */ +static inline bool IsPlainRailTile(TileIndex t) { return IsPlainRailTile(t); } +/** @copydoc IsPlainRailTile(TileIndexT::T) */ +static inline bool IsPlainRailTile(GenericTileIndex t) { return IsPlainRailTile(t); } /** @@ -81,11 +96,16 @@ static inline bool HasSignals(TileIndex t) * @param signals whether the rail tile should have signals or not * @pre IsPlainRailTile(tile) */ -static inline void SetHasSignals(TileIndex tile, bool signals) +template +static inline void SetHasSignals(typename TileIndexT::T tile, bool signals) { assert(IsPlainRailTile(tile)); - SB(_m[tile].m5, 6, 1, signals); + SB(GetTile(tile)->m5, 6, 1, signals); } +/** @copydoc SetHasSignals(TileIndexT::T,bool) */ +static inline void SetHasSignals(TileIndex tile, bool signals) { SetHasSignals(tile, signals); } +/** @copydoc SetHasSignals(TileIndexT::T,bool) */ +static inline void SetHasSignals(GenericTileIndex tile, bool signals) { SetHasSignals(tile, signals); } /** * Is this rail tile a rail depot? @@ -93,40 +113,60 @@ static inline void SetHasSignals(TileIndex tile, bool signals) * @pre IsTileType(t, MP_RAILWAY) * @return true if and only if the tile is a rail depot */ -static inline bool IsRailDepot(TileIndex t) +template +static inline bool IsRailDepot(typename TileIndexT::T t) { return GetRailTileType(t) == RAIL_TILE_DEPOT; } +/** @copydoc IsRailDepot(TileIndexT::T) */ +static inline bool IsRailDepot(TileIndex t) { return IsRailDepot(t); } +/** @copydoc IsRailDepot(TileIndexT::T) */ +static inline bool IsRailDepot(GenericTileIndex t) { return IsRailDepot(t); } /** * Is this tile rail tile and a rail depot? * @param t the tile to get the information from * @return true if and only if the tile is a rail depot */ -static inline bool IsRailDepotTile(TileIndex t) +template +static inline bool IsRailDepotTile(typename TileIndexT::T t) { return IsTileType(t, MP_RAILWAY) && IsRailDepot(t); } +/** @copydoc IsRailDepotTile(TileIndexT::T) */ +static inline bool IsRailDepotTile(TileIndex t) { return IsRailDepotTile(t); } +/** @copydoc IsRailDepotTile(TileIndexT::T) */ +static inline bool IsRailDepotTile(GenericTileIndex t) { return IsRailDepotTile(t); } /** * Gets the rail type of the given tile * @param t the tile to get the rail type from * @return the rail type of the tile */ -static inline RailType GetRailType(TileIndex t) +template +static inline RailType GetRailType(typename TileIndexT::T t) { - return (RailType)GB(_m[t].m3, 0, 4); + return (RailType)GB(GetTile(t)->m3, 0, 4); } +/** @copydoc GetRailType(TileIndexT::T) */ +static inline RailType GetRailType(TileIndex t) { return GetRailType(t); } +/** @copydoc GetRailType(TileIndexT::T) */ +static inline RailType GetRailType(GenericTileIndex t) { return GetRailType(t); } /** * Sets the rail type of the given tile * @param t the tile to set the rail type of * @param r the new rail type for the tile */ -static inline void SetRailType(TileIndex t, RailType r) +template +static inline void SetRailType(typename TileIndexT::T t, RailType r) { - SB(_m[t].m3, 0, 4, r); + SB(GetTile(t)->m3, 0, 4, r); } +/** @copydoc SetRailType(TileIndexT::T,RailType) */ +static inline void SetRailType(TileIndex t, RailType r) { SetRailType(t, r); } +/** @copydoc SetRailType(TileIndexT::T,RailType) */ +static inline void SetRailType(GenericTileIndex t, RailType r) { SetRailType(t, r); } /** @@ -134,22 +174,32 @@ static inline void SetRailType(TileIndex t, RailType r) * @param tile the tile to get the track bits from * @return the track bits of the tile */ -static inline TrackBits GetTrackBits(TileIndex tile) +template +static inline TrackBits GetTrackBits(typename TileIndexT::T tile) { assert(IsPlainRailTile(tile)); - return (TrackBits)GB(_m[tile].m5, 0, 6); + return (TrackBits)GB(GetTile(tile)->m5, 0, 6); } +/** @copydoc GetTrackBits(TileIndexT::T) */ +static inline TrackBits GetTrackBits(TileIndex tile) { return GetTrackBits(tile); } +/** @copydoc GetTrackBits(TileIndexT::T) */ +static inline TrackBits GetTrackBits(GenericTileIndex tile) { return GetTrackBits(tile); } /** * Sets the track bits of the given tile * @param t the tile to set the track bits of * @param b the new track bits for the tile */ -static inline void SetTrackBits(TileIndex t, TrackBits b) +template +static inline void SetTrackBits(typename TileIndexT::T t, TrackBits b) { assert(IsPlainRailTile(t)); - SB(_m[t].m5, 0, 6, b); + SB(GetTile(t)->m5, 0, 6, b); } +/** @copydoc SetTrackBits(TileIndexT::T,TrackBits) */ +static inline void SetTrackBits(TileIndex t, TrackBits b) { SetTrackBits(t, b); } +/** @copydoc SetTrackBits(TileIndexT::T,TrackBits) */ +static inline void SetTrackBits(GenericTileIndex t, TrackBits b) { SetTrackBits(t, b); } /** * Returns whether the given track is present on the given tile. @@ -158,10 +208,15 @@ static inline void SetTrackBits(TileIndex t, TrackBits b) * @pre IsPlainRailTile(tile) * @return true if and only if the given track exists on the tile */ -static inline bool HasTrack(TileIndex tile, Track track) +template +static inline bool HasTrack(typename TileIndexT::T tile, Track track) { return HasBit(GetTrackBits(tile), track); } +/** @copydoc HasTrack(TileIndexT::T,Track) */ +static inline bool HasTrack(TileIndex tile, Track track) { return HasTrack(tile, track); } +/** @copydoc HasTrack(TileIndexT::T,Track) */ +static inline bool HasTrack(GenericTileIndex tile, Track track) { return HasTrack(tile, track); } /** * Returns the direction the depot is facing to @@ -169,10 +224,15 @@ static inline bool HasTrack(TileIndex tile, Track track) * @pre IsRailDepotTile(t) * @return the direction the depot is facing */ -static inline DiagDirection GetRailDepotDirection(TileIndex t) +template +static inline DiagDirection GetRailDepotDirection(typename TileIndexT::T t) { - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(GetTile(t)->m5, 0, 2); } +/** @copydoc GetRailDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetRailDepotDirection(TileIndex t) { return GetRailDepotDirection(t); } +/** @copydoc GetRailDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetRailDepotDirection(GenericTileIndex t) { return GetRailDepotDirection(t); } /** * Returns the track of a depot, ignoring direction @@ -180,10 +240,15 @@ static inline DiagDirection GetRailDepotDirection(TileIndex t) * @param t the tile to get the depot track from * @return the track of the depot */ -static inline Track GetRailDepotTrack(TileIndex t) +template +static inline Track GetRailDepotTrack(typename TileIndexT::T t) { return DiagDirToDiagTrack(GetRailDepotDirection(t)); } +/** @copydoc GetRailDepotTrack(TileIndexT::T) */ +static inline Track GetRailDepotTrack(TileIndex t) { return GetRailDepotTrack(t); } +/** @copydoc GetRailDepotTrack(TileIndexT::T) */ +static inline Track GetRailDepotTrack(GenericTileIndex t) { return GetRailDepotTrack(t); } /** @@ -195,10 +260,10 @@ static inline Track GetRailDepotTrack(TileIndex t) static inline TrackBits GetRailReservationTrackBits(TileIndex t) { assert(IsPlainRailTile(t)); - byte track_b = GB(_m[t].m2, 8, 3); + byte track_b = GB(GetTile(t)->m2, 8, 3); Track track = (Track)(track_b - 1); // map array saves Track+1 if (track_b == 0) return TRACK_BIT_NONE; - return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); + return (TrackBits)(TrackToTrackBits(track) | (HasBit(GetTile(t)->m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0)); } /** @@ -213,8 +278,8 @@ static inline void SetTrackReservation(TileIndex t, TrackBits b) assert(b != INVALID_TRACK_BIT); assert(!TracksOverlap(b)); Track track = RemoveFirstTrack(&b); - SB(_m[t].m2, 8, 3, track == INVALID_TRACK ? 0 : track + 1); - SB(_m[t].m2, 11, 1, (byte)(b != TRACK_BIT_NONE)); + SB(GetTile(t)->m2, 8, 3, track == INVALID_TRACK ? 0 : track + 1); + SB(GetTile(t)->m2, 11, 1, (byte)(b != TRACK_BIT_NONE)); } /** @@ -259,7 +324,7 @@ static inline void UnreserveTrack(TileIndex tile, Track t) static inline bool HasDepotReservation(TileIndex t) { assert(IsRailDepot(t)); - return HasBit(_m[t].m5, 4); + return HasBit(GetTile(t)->m5, 4); } /** @@ -271,7 +336,7 @@ static inline bool HasDepotReservation(TileIndex t) static inline void SetDepotReservation(TileIndex t, bool b) { assert(IsRailDepot(t)); - SB(_m[t].m5, 4, 1, (byte)b); + SB(GetTile(t)->m5, 4, 1, (byte)b); } /** @@ -291,20 +356,30 @@ static inline bool IsPbsSignal(SignalType s) return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY; } -static inline SignalType GetSignalType(TileIndex t, Track track) +template +static inline SignalType GetSignalType(typename TileIndexT::T t, Track track) { assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; - return (SignalType)GB(_m[t].m2, pos, 3); + return (SignalType)GB(GetTile(t)->m2, pos, 3); } +/** @copydoc GetSignalType(TileIndexT::T,Track) */ +static inline SignalType GetSignalType(TileIndex t, Track track) { return GetSignalType(t, track); } +/** @copydoc GetSignalType(TileIndexT::T,Track) */ +static inline SignalType GetSignalType(GenericTileIndex t, Track track) { return GetSignalType(t, track); } -static inline void SetSignalType(TileIndex t, Track track, SignalType s) +template +static inline void SetSignalType(typename TileIndexT::T t, Track track, SignalType s) { assert(GetRailTileType(t) == RAIL_TILE_SIGNALS); byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0; - SB(_m[t].m2, pos, 3, s); - if (track == INVALID_TRACK) SB(_m[t].m2, 4, 3, s); + SB(GetTile(t)->m2, pos, 3, s); + if (track == INVALID_TRACK) SB(GetTile(t)->m2, 4, 3, s); } +/** @copydoc SetSignalType(TileIndexT::T,Track,SignalType) */ +static inline void SetSignalType(TileIndex t, Track track, SignalType s) { SetSignalType(t, track, s); } +/** @copydoc SetSignalType(TileIndexT::T,Track,SignalType) */ +static inline void SetSignalType(GenericTileIndex t, Track track, SignalType s) { SetSignalType(t, track, s); } static inline bool IsPresignalEntry(TileIndex t, Track track) { @@ -322,28 +397,43 @@ static inline bool IsOnewaySignal(TileIndex t, Track track) return GetSignalType(t, track) != SIGTYPE_PBS; } -static inline void CycleSignalSide(TileIndex t, Track track) +template +static inline void CycleSignalSide(typename TileIndexT::T t, Track track) { byte sig; byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 6; - sig = GB(_m[t].m3, pos, 2); + sig = GB(GetTile(t)->m3, pos, 2); if (--sig == 0) sig = IsPbsSignal(GetSignalType(t, track)) ? 2 : 3; - SB(_m[t].m3, pos, 2, sig); + SB(GetTile(t)->m3, pos, 2, sig); } +/** @copydoc CycleSignalSide(TileIndexT::T,Track) */ +static inline void CycleSignalSide(TileIndex t, Track track) { CycleSignalSide(t, track); } +/** @copydoc CycleSignalSide(TileIndexT::T,Track) */ +static inline void CycleSignalSide(GenericTileIndex t, Track track) { CycleSignalSide(t, track); } -static inline SignalVariant GetSignalVariant(TileIndex t, Track track) +template +static inline SignalVariant GetSignalVariant(typename TileIndexT::T t, Track track) { byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; - return (SignalVariant)GB(_m[t].m2, pos, 1); + return (SignalVariant)GB(GetTile(t)->m2, pos, 1); } +/** @copydoc GetSignalVariant(TileIndexT::T,Track) */ +static inline SignalVariant GetSignalVariant(TileIndex t, Track track) { return GetSignalVariant(t, track); } +/** @copydoc GetSignalVariant(TileIndexT::T,Track) */ +static inline SignalVariant GetSignalVariant(GenericTileIndex t, Track track) { return GetSignalVariant(t, track); } -static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) +template +static inline void SetSignalVariant(typename TileIndexT::T t, Track track, SignalVariant v) { byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3; - SB(_m[t].m2, pos, 1, v); - if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v); + SB(GetTile(t)->m2, pos, 1, v); + if (track == INVALID_TRACK) SB(GetTile(t)->m2, 7, 1, v); } +/** @copydoc SetSignalVariant(TileIndexT::T,Track,SignalVariant) */ +static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) { SetSignalVariant(t, track, v); } +/** @copydoc SetSignalVariant(TileIndexT::T,Track,SignalVariant) */ +static inline void SetSignalVariant(GenericTileIndex t, Track track, SignalVariant v) { SetSignalVariant(t, track, v); } /** * Set the states of the signals (Along/AgainstTrackDir) @@ -352,7 +442,7 @@ static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v) */ static inline void SetSignalStates(TileIndex tile, uint state) { - SB(_m[tile].m4, 4, 4, state); + SB(GetTile(tile)->m4, 4, 4, state); } /** @@ -362,7 +452,7 @@ static inline void SetSignalStates(TileIndex tile, uint state) */ static inline uint GetSignalStates(TileIndex tile) { - return GB(_m[tile].m4, 4, 4); + return GB(GetTile(tile)->m4, 4, 4); } /** @@ -381,20 +471,30 @@ static inline SignalState GetSingleSignalState(TileIndex t, byte signalbit) * @param tile the tile to set the present signals for * @param signals the signals that have to be present */ -static inline void SetPresentSignals(TileIndex tile, uint signals) +template +static inline void SetPresentSignals(typename TileIndexT::T tile, uint signals) { - SB(_m[tile].m3, 4, 4, signals); + SB(GetTile(tile)->m3, 4, 4, signals); } +/** @copydoc SetPresentSignals(TileIndexT::T,uint) */ +static inline void SetPresentSignals(TileIndex tile, uint signals) { SetPresentSignals(tile, signals); } +/** @copydoc SetPresentSignals(TileIndexT::T,uint) */ +static inline void SetPresentSignals(GenericTileIndex tile, uint signals) { SetPresentSignals(tile, signals); } /** * Get whether the given signals are present (Along/AgainstTrackDir) * @param tile the tile to get the present signals for * @return the signals that are present */ -static inline uint GetPresentSignals(TileIndex tile) +template +static inline uint GetPresentSignals(typename TileIndexT::T tile) { - return GB(_m[tile].m3, 4, 4); + return GB(GetTile(tile)->m3, 4, 4); } +/** @copydoc GetPresentSignals(TileIndexT::T) */ +static inline uint GetPresentSignals(TileIndex tile) { return GetPresentSignals(tile); } +/** @copydoc GetPresentSignals(TileIndexT::T) */ +static inline uint GetPresentSignals(GenericTileIndex tile) { return GetPresentSignals(tile); } /** * Checks whether the given signals is present @@ -411,11 +511,16 @@ static inline bool IsSignalPresent(TileIndex t, byte signalbit) * Checks for the presence of signals (either way) on the given track on the * given rail tile. */ -static inline bool HasSignalOnTrack(TileIndex tile, Track track) +template +static inline bool HasSignalOnTrack(typename TileIndexT::T tile, Track track) { assert(IsValidTrack(track)); return GetRailTileType(tile) == RAIL_TILE_SIGNALS && (GetPresentSignals(tile) & SignalOnTrack(track)) != 0; } +/** @copydoc HasSignalOnTrack(TileIndexT::T,Track) */ +static inline bool HasSignalOnTrack(TileIndex tile, Track track) { return HasSignalOnTrack(tile, track); } +/** @copydoc HasSignalOnTrack(TileIndexT::T,Track) */ +static inline bool HasSignalOnTrack(GenericTileIndex tile, Track track) { return HasSignalOnTrack(tile, track); } /** * Checks for the presence of signals along the given trackdir on the given @@ -424,11 +529,16 @@ static inline bool HasSignalOnTrack(TileIndex tile, Track track) * Along meaning if you are currently driving on the given trackdir, this is * the signal that is facing us (for which we stop when it's red). */ -static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) +template +static inline bool HasSignalOnTrackdir(typename TileIndexT::T tile, Trackdir trackdir) { - assert (IsValidTrackdir(trackdir)); + assert(IsValidTrackdir(trackdir)); return GetRailTileType(tile) == RAIL_TILE_SIGNALS && GetPresentSignals(tile) & SignalAlongTrackdir(trackdir); } +/** @copydoc HasSignalOnTrackdir(TileIndexT::T,Trackdir) */ +static inline bool HasSignalOnTrackdir(TileIndex tile, Trackdir trackdir) { return HasSignalOnTrackdir(tile, trackdir); } +/** @copydoc HasSignalOnTrackdir(TileIndexT::T,Trackdir) */ +static inline bool HasSignalOnTrackdir(GenericTileIndex tile, Trackdir trackdir) { return HasSignalOnTrackdir(tile, trackdir); } /** * Gets the state of the signal along the given trackdir. @@ -480,7 +590,12 @@ static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td) } -RailType GetTileRailType(TileIndex tile); +template +RailType GetTileRailType(typename TileIndexT::T tile); +/** @copydoc GetTileRailType(TileIndexT::T) */ +static inline RailType GetTileRailType(TileIndex tile) { return GetTileRailType(tile); } +/** @copydoc GetTileRailType(TileIndexT::T) */ +static inline RailType GetTileRailType(GenericTileIndex tile) { return GetTileRailType(tile); } /** The ground 'under' the rail */ enum RailGroundType { @@ -503,12 +618,12 @@ enum RailGroundType { static inline void SetRailGroundType(TileIndex t, RailGroundType rgt) { - SB(_m[t].m4, 0, 4, rgt); + SB(GetTile(t)->m4, 0, 4, rgt); } static inline RailGroundType GetRailGroundType(TileIndex t) { - return (RailGroundType)GB(_m[t].m4, 0, 4); + return (RailGroundType)GB(GetTile(t)->m4, 0, 4); } static inline bool IsSnowRailGround(TileIndex t) @@ -517,29 +632,38 @@ static inline bool IsSnowRailGround(TileIndex t) } -static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) +template +static inline void MakeRailNormal(typename TileIndexT::T t, Owner o, TrackBits b, RailType r) { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = r; - _m[t].m4 = 0; - _m[t].m5 = RAIL_TILE_NORMAL << 6 | b; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = r; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = RAIL_TILE_NORMAL << 6 | b; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeRailNormal(TileIndexT::T,Owner,TrackBits,RailType) */ +static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) { MakeRailNormal(t, o, b, r); } +/** @copydoc MakeRailNormal(TileIndexT::T,Owner,TrackBits,RailType) */ +static inline void MakeRailNormal(GenericTileIndex t, Owner o, TrackBits b, RailType r) { MakeRailNormal(t, o, b, r); } - -static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) +template +inline void MakeRailDepot(typename TileIndexT::T t, Owner o, DepotID did, DiagDirection d, RailType r) { SetTileType(t, MP_RAILWAY); SetTileOwner(t, o); - _m[t].m2 = did; - _m[t].m3 = r; - _m[t].m4 = 0; - _m[t].m5 = RAIL_TILE_DEPOT << 6 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; -} + GetTile(t)->m2 = did; + GetTile(t)->m3 = r; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = RAIL_TILE_DEPOT << 6 | d; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; +} +/** @copydoc MakeRailDepot(TileIndexT::T,Owner,DepotID,DiagDirection,RailType) */ +static inline void MakeRailDepot(TileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) { MakeRailDepot(t, o, did, d, r); } +/** @copydoc MakeRailDepot(TileIndexT::T,Owner,DepotID,DiagDirection,RailType) */ +static inline void MakeRailDepot(GenericTileIndex t, Owner o, DepotID did, DiagDirection d, RailType r) { MakeRailDepot(t, o, did, d, r); } #endif /* RAIL_MAP_H */ diff --git a/src/rev.cpp.in b/src/rev.cpp.in index b6bddeacf..01dafdba6 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -39,7 +39,7 @@ bool IsReleasedVersion() * norev000 is for non-releases that are made on systems without * subversion or sources that are not a checkout of subversion. */ -const char _openttd_revision[] = "!!VERSION!!"; +const char _openttd_revision[] = "!!VERSION!!-CLIPBOARD"; /** * The text version of OpenTTD's build date. diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 5e432a2e6..7ee8acac8 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "cmd_helper.h" +#include "copypaste_cmd.h" #include "road_internal.h" #include "viewport_func.h" #include "command_func.h" @@ -35,6 +36,7 @@ #include "date_func.h" #include "genworld.h" #include "company_gui.h" +#include "clipboard_gui.h" #include "table/strings.h" @@ -1328,11 +1330,14 @@ static void DrawTile_Road(TileInfo *ti) switch (GetRoadTileType(ti->tile)) { case ROAD_TILE_NORMAL: DrawRoadBits(ti); + DrawOverlay(ti, MP_ROAD); break; case ROAD_TILE_CROSSING: { if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); + DrawOverlay(ti, MP_ROAD); + PaletteID pal = PAL_NONE; const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); @@ -1408,6 +1413,7 @@ static void DrawTile_Road(TileInfo *ti) } DrawGroundSprite(dts->ground.sprite, PAL_NONE); + DrawOverlay(ti, MP_ROAD); DrawOrigTileSeq(ti, dts, TO_BUILDINGS, palette); break; } @@ -1854,6 +1860,176 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); } +static void CopyPastePlaceRoad(GenericTileIndex tile, RoadBits roadbits_road, DisallowedRoadDirections drd, RoadBits roadbits_tram) +{ + if (IsMainMapTile(tile)) { + /* road */ + if (roadbits_road != ROAD_NONE) { + if (IsNormalRoadTile(tile) && HasTileRoadType(tile, ROADTYPE_ROAD) && + IsRoadOwner(tile, ROADTYPE_ROAD, _current_company) && + (GetRoadBits(tile, ROADTYPE_ROAD) == roadbits_road)) { + drd &= ~GetDisallowedRoadDirections(tile); + if (drd == DRD_NONE) return; + } + _current_pasting->DoCommand(AsMainMapTile(tile), roadbits_road | (ROADTYPE_ROAD << 4) | (drd << 6), 0, CMD_BUILD_ROAD | CMD_MSG(STR_ERROR_CAN_T_BUILD_ROAD_HERE)); + } + /* tram */ + if (roadbits_tram != ROAD_NONE) { + _current_pasting->DoCommand(AsMainMapTile(tile), roadbits_tram | (ROADTYPE_TRAM << 4), 0, CMD_BUILD_ROAD | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE)); + } + } else { + RoadTypes rt = ROADTYPES_NONE; + if (roadbits_road != ROAD_NONE) rt |= ROADTYPES_ROAD; + if (roadbits_tram != ROAD_NONE) rt |= ROADTYPES_TRAM; + assert(rt != ROADTYPES_NONE); + + MakeRoadNormal(tile, ROAD_NONE, rt, 0, OWNER_NONE, OWNER_NONE); + SetRoadBits(tile, roadbits_road, ROADTYPE_ROAD); + SetDisallowedRoadDirections(tile, drd); + SetRoadBits(tile, roadbits_tram, ROADTYPE_TRAM); + } +} + +static void CopyPastePlaceRoadCrossing(GenericTileIndex tile, RoadTypes road_types, Axis road_axis, RailType railtype) +{ + if (IsMainMapTile(tile)) { + CopyPastePlaceRoad(tile, HasBit(road_types, ROADTYPE_ROAD) ? AxisToRoadBits(road_axis) : ROAD_NONE, + DRD_NONE, HasBit(road_types, ROADTYPE_TRAM) ? AxisToRoadBits(road_axis) : ROAD_NONE); + CopyPastePlaceTracks(tile, railtype, AxisToTrackBits(OtherAxis(road_axis))); + } else { + MakeRoadCrossing(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, road_axis, railtype, road_types, 0); + } +} + +static void CopyPastePlaceRoadDepot(GenericTileIndex tile, RoadType rt, DiagDirection dir) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + if (IsRoadDepotTile(t) && IsTileOwner(t, _current_company) && + GetRoadDepotDirection(t) == dir && GetRoadTypes(t) == RoadTypeToRoadTypes(rt)) { + _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_ROAD_DEPOT); + } else { + _current_pasting->DoCommand(t, dir | (rt << 2), 0, CMD_BUILD_ROAD_DEPOT | CMD_MSG(rt + STR_ERROR_CAN_T_BUILD_ROAD_DEPOT)); + } + } else { + MakeRoadDepot(tile, OWNER_NONE, 0, dir, rt); + } +} + +static bool IsRoadCopyable(GenericTileIndex tile, RoadType rt, CompanyID company = _current_company) +{ + return HasTileRoadType(tile, rt) && (!IsMainMapTile(tile) || IsRoadOwner(tile, rt, company)); +} + +/** + * Test a given road tile if there is any contented to be copied from it. + * @param tile the tile to test + * @param mode copy-paste mode + * @param company the #Company to check ownership against to + * @param preview (out, may be NULL) information on how to higlight preview of the tile + * @return whether this tile needs to be copy-pasted + */ +bool TestRoadTileCopyability(GenericTileIndex tile, CopyPasteMode mode, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) +{ + if (preview != NULL) MemSetT(preview, 0); + + if (!(mode & (CPM_WITH_ROAD_TRANSPORT | CPM_WITH_RAIL_TRANSPORT))) return false; + + switch (GetRoadTileType(tile)) { + case ROAD_TILE_NORMAL: + if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; + if (!IsRoadCopyable(tile, ROADTYPE_ROAD, company) && !IsRoadCopyable(tile, ROADTYPE_TRAM, company)) return false; + if (preview != NULL) preview->highlight_tile_rect = true; + break; + + case ROAD_TILE_CROSSING: { + bool road_ok = (mode & CPM_WITH_ROAD_TRANSPORT) && (IsRoadCopyable(tile, ROADTYPE_ROAD, company) || IsRoadCopyable(tile, ROADTYPE_TRAM, company)); + bool rail_ok = (mode & CPM_WITH_RAIL_TRANSPORT) && (!IsMainMapTile(tile) || IsTileOwner(tile, company)); + if (!road_ok && !rail_ok) return false; + if (preview != NULL) { + if (road_ok) preview->highlight_tile_rect = true; + if (rail_ok) preview->highlight_track_bits = GetCrossingRailBits(tile); + } + break; + } + + case ROAD_TILE_DEPOT: + if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; + if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; + if (preview != NULL) preview->highlight_tile_rect = true; + break; + } + + return true; +} + +void CopyPasteTile_Road(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) +{ + if (!TestRoadTileCopyability(src_tile, copy_paste.mode)) return; + + /* Terraform tile if needed */ + if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { + CopyPasteHeights(GenericTileArea(src_tile, 1, 1), dst_tile, copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + } + + switch (GetRoadTileType(src_tile)) { + case ROAD_TILE_NORMAL: { + RoadBits roadbits_road = IsRoadCopyable(src_tile, ROADTYPE_ROAD) ? + TransformRoadBits(GetRoadBits(src_tile, ROADTYPE_ROAD), copy_paste.transformation) : ROAD_NONE; + RoadBits roadbits_tram = IsRoadCopyable(src_tile, ROADTYPE_TRAM) ? + TransformRoadBits(GetRoadBits(src_tile, ROADTYPE_TRAM), copy_paste.transformation) : ROAD_NONE; + + /* transform DisallowedRoadDirections */ + DisallowedRoadDirections drd = GetDisallowedRoadDirections(src_tile); + if (copy_paste.transformation != DTR_IDENTITY && (drd == DRD_SOUTHBOUND || drd == DRD_NORTHBOUND)) { + /* investigate current direction */ + DiagDirection dir = (GetRoadBits(src_tile, ROADTYPE_ROAD) & ROAD_X) ? DIAGDIR_SW : DIAGDIR_SE; + if (drd == DRD_NORTHBOUND) dir = ReverseDiagDir(dir); + /* transform */ + dir = TransformDiagDir(dir, copy_paste.transformation); + /* convert result to DisallowedRoadDirections */ + drd = (dir == DIAGDIR_SW || dir == DIAGDIR_SE) ? DRD_SOUTHBOUND : DRD_NORTHBOUND; + } + + /* paste roads */ + CopyPastePlaceRoad(dst_tile, roadbits_road, drd, roadbits_tram); + break; + } + + case ROAD_TILE_CROSSING: { + Axis road_axis = TransformAxis(GetCrossingRoadAxis(src_tile), copy_paste.transformation); + RoadTypes road_types = ROADTYPES_NONE; + if (copy_paste.mode & CPM_WITH_ROAD_TRANSPORT) { + if (IsRoadCopyable(src_tile, ROADTYPE_ROAD)) SetBit(road_types, ROADTYPE_ROAD); + if (IsRoadCopyable(src_tile, ROADTYPE_TRAM)) SetBit(road_types, ROADTYPE_TRAM); + } + + if ((copy_paste.mode & CPM_WITH_RAIL_TRANSPORT) && (!IsMainMapTile(src_tile) || IsTileOwner(src_tile, _current_company))) { + RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); + if (road_types != ROADTYPES_NONE) { + CopyPastePlaceRoadCrossing(dst_tile, road_types, road_axis, railtype); + } else { + CopyPastePlaceTracks(dst_tile, railtype, AxisToTrackBits(OtherAxis(road_axis))); + } + } else if (road_types != ROADTYPES_NONE) { + CopyPastePlaceRoad(dst_tile, HasBit(road_types, ROADTYPE_ROAD) ? AxisToRoadBits(road_axis) : ROAD_NONE, + DRD_NONE, HasBit(road_types, ROADTYPE_TRAM) ? AxisToRoadBits(road_axis) : ROAD_NONE); + } + break; + } + + case ROAD_TILE_DEPOT: + /* paste depot */ + CopyPastePlaceRoadDepot(dst_tile, (RoadType)FIND_FIRST_BIT(GetRoadTypes(src_tile)), + TransformDiagDir(GetRoadDepotDirection(src_tile), copy_paste.transformation)); + break; + + default: + NOT_REACHED(); // corrupted tile data? + } +} + /** Tile callback functions for road tiles */ extern const TileTypeProcs _tile_type_road_procs = { DrawTile_Road, // draw_tile_proc @@ -1870,4 +2046,5 @@ extern const TileTypeProcs _tile_type_road_procs = { VehicleEnter_Road, // vehicle_enter_tile_proc GetFoundation_Road, // get_foundation_proc TerraformTile_Road, // terraform_tile_proc + CopyPasteTile_Road, // copypaste_tile_proc }; diff --git a/src/road_func.h b/src/road_func.h index c4af229d5..c6ea68ccb 100644 --- a/src/road_func.h +++ b/src/road_func.h @@ -14,6 +14,7 @@ #include "core/bitmath_func.hpp" #include "road_type.h" +#include "direction_func.h" #include "economy_func.h" /** @@ -114,10 +115,37 @@ static inline RoadBits MirrorRoadBits(RoadBits r) static inline RoadBits RotateRoadBits(RoadBits r, DiagDirDiff rot) { assert(IsValidRoadBits(r)); - for (; rot > (DiagDirDiff)0; rot--) { - r = (RoadBits)(GB(r, 0, 1) << 3 | GB(r, 1, 3)); + rot = (DiagDirDiff)((uint)rot % DIAGDIR_END); + return (RoadBits)((r | (r << DIAGDIR_END)) >> rot) & ROAD_ALL; +} + +/** + * Transform RoadBits by given transformation. + * @param road_bits The RoadBits to transform. + * @param transformation Transformation to perform. + * @return The transformed RoadBits. + */ +static inline RoadBits TransformRoadBits(RoadBits road_bits, DirTransformation transformation) +{ + /* reflect agains X axis before rotating */ + if (transformation & DTR_REFLECTION_BIT) { + /* firstly reflect against W-E axis by swapping odd and even bits (the numbers are bit positions) + * + * [ROAD_NW] [ROAD_NE] 0 3 1 2 /N\ + * ------------------- ----- --reflect-against-W-E--> ----- W-+-E + * [ROAD_SW] [ROAD_SE] 1 2 0 3 \S/ + * + * bit 0 (ROAD_NW) swaps with bit 1 (ROAD_SW) + * bit 2 (ROAD_SE) swaps with bit 3 (ROAD_NE) */ + road_bits = SwapOddEvenBits(road_bits); + /* Now we have reflection agains W-E axis. To get reflection agains X axis we must rotate the + * result left by 90 degree. To do that we can simply add 3 to the number of 90-degree + * right rotations that we will be doing in the next step. We can safely overflow. */ + transformation = (DirTransformation)(transformation + 3); } - return r; + + /* rotate */ + return RotateRoadBits(road_bits, (DiagDirDiff)transformation); } /** diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 801d33435..5dcd5ffa8 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -633,11 +633,11 @@ struct BuildRoadToolbarWindow : Window { break; case DDSP_BUILD_BUSSTOP: - PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); + PlaceRoadStop(start_tile, end_tile, ((_settings_game.station.adjacent_stations && _ctrl_pressed) << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); break; case DDSP_BUILD_TRUCKSTOP: - PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); + PlaceRoadStop(start_tile, end_tile, ((_settings_game.station.adjacent_stations && _ctrl_pressed) << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); break; case DDSP_REMOVE_BUSSTOP: { diff --git a/src/road_map.h b/src/road_map.h index 693730294..d1bfaec8c 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -32,11 +32,16 @@ enum RoadTileType { * @pre IsTileType(t, MP_ROAD) * @return The road tile type. */ -static inline RoadTileType GetRoadTileType(TileIndex t) +template +static inline RoadTileType GetRoadTileType(typename TileIndexT::T t) { assert(IsTileType(t, MP_ROAD)); - return (RoadTileType)GB(_m[t].m5, 6, 2); + return (RoadTileType)GB(GetTile(t)->m5, 6, 2); } +/** @copydoc GetRoadTileType(TileIndexT::T) */ +static inline RoadTileType GetRoadTileType(TileIndex t) { return GetRoadTileType(t); } +/** @copydoc GetRoadTileType(TileIndexT::T) */ +static inline RoadTileType GetRoadTileType(GenericTileIndex t) { return GetRoadTileType(t); } /** * Return whether a tile is a normal road. @@ -44,20 +49,30 @@ static inline RoadTileType GetRoadTileType(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if normal road. */ -static inline bool IsNormalRoad(TileIndex t) +template +static inline bool IsNormalRoad(typename TileIndexT::T t) { return GetRoadTileType(t) == ROAD_TILE_NORMAL; } +/** @copydoc IsNormalRoad(TileIndexT::T) */ +static inline bool IsNormalRoad(TileIndex t) { return IsNormalRoad(t); } +/** @copydoc IsNormalRoad(TileIndexT::T) */ +static inline bool IsNormalRoad(GenericTileIndex t) { return IsNormalRoad(t); } /** * Return whether a tile is a normal road tile. * @param t Tile to query. * @return True if normal road tile. */ -static inline bool IsNormalRoadTile(TileIndex t) +template +static inline bool IsNormalRoadTile(typename TileIndexT::T t) { return IsTileType(t, MP_ROAD) && IsNormalRoad(t); } +/** @copydoc IsNormalRoadTile(TileIndexT::T) */ +static inline bool IsNormalRoadTile(TileIndex t) { return IsNormalRoadTile(t); } +/** @copydoc IsNormalRoadTile(TileIndexT::T) */ +static inline bool IsNormalRoadTile(GenericTileIndex t) { return IsNormalRoadTile(t); } /** * Return whether a tile is a level crossing. @@ -65,20 +80,30 @@ static inline bool IsNormalRoadTile(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if level crossing. */ -static inline bool IsLevelCrossing(TileIndex t) +template +static inline bool IsLevelCrossing(typename TileIndexT::T t) { return GetRoadTileType(t) == ROAD_TILE_CROSSING; } +/** @copydoc IsLevelCrossing(TileIndexT::T) */ +static inline bool IsLevelCrossing(TileIndex t) { return IsLevelCrossing(t); } +/** @copydoc IsLevelCrossing(TileIndexT::T) */ +static inline bool IsLevelCrossing(GenericTileIndex t) { return IsLevelCrossing(t); } /** * Return whether a tile is a level crossing tile. * @param t Tile to query. * @return True if level crossing tile. */ -static inline bool IsLevelCrossingTile(TileIndex t) +template +static inline bool IsLevelCrossingTile(typename TileIndexT::T t) { return IsTileType(t, MP_ROAD) && IsLevelCrossing(t); } +/** @copydoc IsLevelCrossingTile(TileIndexT::T) */ +static inline bool IsLevelCrossingTile(TileIndex t) { return IsLevelCrossingTile(t); } +/** @copydoc IsLevelCrossingTile(TileIndexT::T) */ +static inline bool IsLevelCrossingTile(GenericTileIndex t) { return IsLevelCrossingTile(t); } /** * Return whether a tile is a road depot. @@ -86,10 +111,15 @@ static inline bool IsLevelCrossingTile(TileIndex t) * @pre IsTileType(t, MP_ROAD) * @return True if road depot. */ -static inline bool IsRoadDepot(TileIndex t) +template +static inline bool IsRoadDepot(typename TileIndexT::T t) { return GetRoadTileType(t) == ROAD_TILE_DEPOT; } +/** @copydoc IsRoadDepot(TileIndexT::T) */ +static inline bool IsRoadDepot(TileIndex t) { return IsRoadDepot(t); } +/** @copydoc IsRoadDepot(TileIndexT::T) */ +static inline bool IsRoadDepot(GenericTileIndex t) { return IsRoadDepot(t); } /** * Return whether a tile is a road depot tile. @@ -108,15 +138,20 @@ static inline bool IsRoadDepotTile(TileIndex t) * @pre IsNormalRoad(t) * @return The present road bits for the road type. */ -static inline RoadBits GetRoadBits(TileIndex t, RoadType rt) +template +static inline RoadBits GetRoadBits(typename TileIndexT::T t, RoadType rt) { assert(IsNormalRoad(t)); switch (rt) { default: NOT_REACHED(); - case ROADTYPE_ROAD: return (RoadBits)GB(_m[t].m5, 0, 4); - case ROADTYPE_TRAM: return (RoadBits)GB(_m[t].m3, 0, 4); + case ROADTYPE_ROAD: return (RoadBits)GB(GetTile(t)->m5, 0, 4); + case ROADTYPE_TRAM: return (RoadBits)GB(GetTile(t)->m3, 0, 4); } } +/** @copydoc GetRoadBits(TileIndexT::T,RoadType) */ +static inline RoadBits GetRoadBits(TileIndex t, RoadType rt) { return GetRoadBits(t, rt); } +/** @copydoc GetRoadBits(TileIndexT::T,RoadType) */ +static inline RoadBits GetRoadBits(GenericTileIndex t, RoadType rt) { return GetRoadBits(t, rt); } /** * Get all RoadBits set on a tile except from the given RoadType @@ -148,36 +183,51 @@ static inline RoadBits GetAllRoadBits(TileIndex tile) * @param rt Road type. * @pre IsNormalRoad(t) */ -static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt) +template +static inline void SetRoadBits(typename TileIndexT::T t, RoadBits r, RoadType rt) { assert(IsNormalRoad(t)); // XXX incomplete switch (rt) { default: NOT_REACHED(); - case ROADTYPE_ROAD: SB(_m[t].m5, 0, 4, r); break; - case ROADTYPE_TRAM: SB(_m[t].m3, 0, 4, r); break; + case ROADTYPE_ROAD: SB(GetTile(t)->m5, 0, 4, r); break; + case ROADTYPE_TRAM: SB(GetTile(t)->m3, 0, 4, r); break; } } +/** @copydoc SetRoadBits(TileIndexT::T,RoadBits,RoadType) */ +static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt) { SetRoadBits(t, r, rt); } +/** @copydoc SetRoadBits(TileIndexT::T,RoadBits,RoadType) */ +static inline void SetRoadBits(GenericTileIndex t, RoadBits r, RoadType rt) { SetRoadBits(t, r, rt); } /** * Get the present road types of a tile. * @param t The tile to query. * @return Present road types. */ -static inline RoadTypes GetRoadTypes(TileIndex t) +template +static inline RoadTypes GetRoadTypes(typename TileIndexT::T t) { - return (RoadTypes)GB(_me[t].m7, 6, 2); + return (RoadTypes)GB(GetTileEx(t)->m7, 6, 2); } +/** @copydoc GetRoadTypes(TileIndexT::T) */ +static inline RoadTypes GetRoadTypes(TileIndex t) { return GetRoadTypes(t); } +/** @copydoc GetRoadTypes(TileIndexT::T) */ +static inline RoadTypes GetRoadTypes(GenericTileIndex t) { return GetRoadTypes(t); } /** * Set the present road types of a tile. * @param t The tile to change. * @param rt The new road types. */ -static inline void SetRoadTypes(TileIndex t, RoadTypes rt) +template +static inline void SetRoadTypes(typename TileIndexT::T t, RoadTypes rt) { assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); - SB(_me[t].m7, 6, 2, rt); + SB(GetTileEx(t)->m7, 6, 2, rt); } +/** @copydoc SetRoadTypes(TileIndexT::T,RoadTypes) */ +static inline void SetRoadTypes(TileIndex t, RoadTypes rt) { SetRoadTypes(t, rt); } +/** @copydoc SetRoadTypes(TileIndexT::T,RoadTypes) */ +static inline void SetRoadTypes(GenericTileIndex t, RoadTypes rt) { SetRoadTypes(t, rt); } /** * Check if a tile has a specific road type. @@ -185,10 +235,15 @@ static inline void SetRoadTypes(TileIndex t, RoadTypes rt) * @param rt Road type to check. * @return True if the tile has the specified road type. */ -static inline bool HasTileRoadType(TileIndex t, RoadType rt) +template +static inline bool HasTileRoadType(typename TileIndexT::T t, RoadType rt) { return HasBit(GetRoadTypes(t), rt); } +/** @copydoc HasTileRoadType(TileIndexT::T,RoadType) */ +static inline bool HasTileRoadType(TileIndex t, RoadType rt) { return HasTileRoadType(t, rt); } +/** @copydoc HasTileRoadType(TileIndexT::T,RoadType) */ +static inline bool HasTileRoadType(GenericTileIndex t, RoadType rt) { return HasTileRoadType(t, rt); } /** * Get the owner of a specific road type. @@ -196,20 +251,25 @@ static inline bool HasTileRoadType(TileIndex t, RoadType rt) * @param rt The road type to get the owner of. * @return Owner of the given road type. */ -static inline Owner GetRoadOwner(TileIndex t, RoadType rt) +template +static inline Owner GetRoadOwner(typename TileIndexT::T t, RoadType rt) { assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE)); switch (rt) { default: NOT_REACHED(); - case ROADTYPE_ROAD: return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5); + case ROADTYPE_ROAD: return (Owner)GB(IsNormalRoadTile(t) ? GetTile(t)->m1 : GetTileEx(t)->m7, 0, 5); case ROADTYPE_TRAM: { /* Trams don't need OWNER_TOWN, and remapping OWNER_NONE * to OWNER_TOWN makes it use one bit less */ - Owner o = (Owner)GB(_m[t].m3, 4, 4); + Owner o = (Owner)GB(GetTile(t)->m3, 4, 4); return o == OWNER_TOWN ? OWNER_NONE : o; } } } +/** @copydoc GetRoadOwner(TileIndexT::T,RoadType) */ +static inline Owner GetRoadOwner(TileIndex t, RoadType rt) { return GetRoadOwner(t, rt); } +/** @copydoc GetRoadOwner(TileIndexT::T,RoadType) */ +static inline Owner GetRoadOwner(GenericTileIndex t, RoadType rt) { return GetRoadOwner(t, rt); } /** * Set the owner of a specific road type. @@ -217,14 +277,19 @@ static inline Owner GetRoadOwner(TileIndex t, RoadType rt) * @param rt The road type to change the owner of. * @param o New owner of the given road type. */ -static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) +template +static inline void SetRoadOwner(typename TileIndexT::T t, RoadType rt, Owner o) { switch (rt) { default: NOT_REACHED(); - case ROADTYPE_ROAD: SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o); break; - case ROADTYPE_TRAM: SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); break; + case ROADTYPE_ROAD: SB(IsNormalRoadTile(t) ? GetTile(t)->m1 : GetTileEx(t)->m7, 0, 5, o); break; + case ROADTYPE_TRAM: SB(GetTile(t)->m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); break; } } +/** @copydoc SetRoadOwner(TileIndexT::T,RoadType,Owner) */ +static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) { SetRoadOwner(t, rt, o); } +/** @copydoc SetRoadOwner(TileIndexT::T,RoadType,Owner) */ +static inline void SetRoadOwner(GenericTileIndex t, RoadType rt, Owner o) { SetRoadOwner(t, rt, o); } /** * Check if a specific road type is owned by an owner. @@ -234,11 +299,16 @@ static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o) * @pre HasTileRoadType(t, rt) * @return True if the road type is owned by the given owner. */ -static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o) +template +static inline bool IsRoadOwner(typename TileIndexT::T t, RoadType rt, Owner o) { assert(HasTileRoadType(t, rt)); return (GetRoadOwner(t, rt) == o); } +/** @copydoc IsRoadOwner(TileIndexT::T,RoadType,Owner) */ +static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o) { return IsRoadOwner(t, rt, o); } +/** @copydoc IsRoadOwner(TileIndexT::T,RoadType,Owner) */ +static inline bool IsRoadOwner(GenericTileIndex t, RoadType rt, Owner o) { return IsRoadOwner(t, rt, o); } /** * Checks if given tile has town owned road @@ -268,23 +338,33 @@ template <> struct EnumPropsT : MakeEnumPropsT +static inline DisallowedRoadDirections GetDisallowedRoadDirections(typename TileIndexT::T t) { assert(IsNormalRoad(t)); - return (DisallowedRoadDirections)GB(_m[t].m5, 4, 2); + return (DisallowedRoadDirections)GB(GetTile(t)->m5, 4, 2); } +/** @copydoc GetDisallowedRoadDirections(TileIndexT::T) */ +static inline DisallowedRoadDirections GetDisallowedRoadDirections(TileIndex t) { return GetDisallowedRoadDirections(t); } +/** @copydoc GetDisallowedRoadDirections(TileIndexT::T) */ +static inline DisallowedRoadDirections GetDisallowedRoadDirections(GenericTileIndex t) { return GetDisallowedRoadDirections(t); } /** * Sets the disallowed directions * @param t the tile to set the directions for * @param drd the disallowed directions */ -static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd) +template +static inline void SetDisallowedRoadDirections(typename TileIndexT::T t, DisallowedRoadDirections drd) { assert(IsNormalRoad(t)); assert(drd < DRD_END); - SB(_m[t].m5, 4, 2, drd); + SB(GetTile(t)->m5, 4, 2, drd); } +/** @copydoc SetDisallowedRoadDirections(TileIndexT::T,DisallowedRoadDirections) */ +static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirections drd) { SetDisallowedRoadDirections(t, drd); } +/** @copydoc SetDisallowedRoadDirections(TileIndexT::T,DisallowedRoadDirections) */ +static inline void SetDisallowedRoadDirections(GenericTileIndex t, DisallowedRoadDirections drd) { SetDisallowedRoadDirections(t, drd); } /** * Get the road axis of a level crossing. @@ -292,11 +372,16 @@ static inline void SetDisallowedRoadDirections(TileIndex t, DisallowedRoadDirect * @pre IsLevelCrossing(t) * @return The axis of the road. */ -static inline Axis GetCrossingRoadAxis(TileIndex t) +template +static inline Axis GetCrossingRoadAxis(typename TileIndexT::T t) { assert(IsLevelCrossing(t)); - return (Axis)GB(_m[t].m5, 0, 1); + return (Axis)GB(GetTile(t)->m5, 0, 1); } +/** @copydoc GetCrossingRoadAxis(TileIndexT::T) */ +static inline Axis GetCrossingRoadAxis(TileIndex t) { return GetCrossingRoadAxis(t); } +/** @copydoc GetCrossingRoadAxis(TileIndexT::T) */ +static inline Axis GetCrossingRoadAxis(GenericTileIndex t) { return GetCrossingRoadAxis(t); } /** * Get the rail axis of a level crossing. @@ -304,21 +389,31 @@ static inline Axis GetCrossingRoadAxis(TileIndex t) * @pre IsLevelCrossing(t) * @return The axis of the rail. */ -static inline Axis GetCrossingRailAxis(TileIndex t) +template +static inline Axis GetCrossingRailAxis(typename TileIndexT::T t) { assert(IsLevelCrossing(t)); return OtherAxis((Axis)GetCrossingRoadAxis(t)); } +/** @copydoc GetCrossingRailAxis(TileIndexT::T) */ +static inline Axis GetCrossingRailAxis(TileIndex t) { return GetCrossingRailAxis(t); } +/** @copydoc GetCrossingRailAxis(TileIndexT::T) */ +static inline Axis GetCrossingRailAxis(GenericTileIndex t) { return GetCrossingRailAxis(t); } /** * Get the road bits of a level crossing. * @param tile The tile to query. * @return The present road bits. */ -static inline RoadBits GetCrossingRoadBits(TileIndex tile) +template +static inline RoadBits GetCrossingRoadBits(typename TileIndexT::T tile) { return GetCrossingRoadAxis(tile) == AXIS_X ? ROAD_X : ROAD_Y; } +/** @copydoc GetCrossingRoadBits(TileIndexT::T) */ +static inline RoadBits GetCrossingRoadBits(TileIndex tile) { return GetCrossingRoadBits(tile); } +/** @copydoc GetCrossingRoadBits(TileIndexT::T) */ +static inline RoadBits GetCrossingRoadBits(GenericTileIndex tile) { return GetCrossingRoadBits(tile); } /** * Get the rail track of a level crossing. @@ -335,10 +430,15 @@ static inline Track GetCrossingRailTrack(TileIndex tile) * @param tile The tile to query. * @return The rail track bits. */ -static inline TrackBits GetCrossingRailBits(TileIndex tile) +template +static inline TrackBits GetCrossingRailBits(typename TileIndexT::T tile) { return AxisToTrackBits(GetCrossingRailAxis(tile)); } +/** @copydoc GetCrossingRailBits(TileIndexT::T) */ +static inline TrackBits GetCrossingRailBits(TileIndex tile) { return GetCrossingRailBits(tile); } +/** @copydoc GetCrossingRailBits(TileIndexT::T) */ +static inline TrackBits GetCrossingRailBits(GenericTileIndex tile) { return GetCrossingRailBits(tile); } /** @@ -350,7 +450,7 @@ static inline TrackBits GetCrossingRailBits(TileIndex tile) static inline bool HasCrossingReservation(TileIndex t) { assert(IsLevelCrossingTile(t)); - return HasBit(_m[t].m5, 4); + return HasBit(GetTile(t)->m5, 4); } /** @@ -363,7 +463,7 @@ static inline bool HasCrossingReservation(TileIndex t) static inline void SetCrossingReservation(TileIndex t, bool b) { assert(IsLevelCrossingTile(t)); - SB(_m[t].m5, 4, 1, b ? 1 : 0); + SB(GetTile(t)->m5, 4, 1, b ? 1 : 0); } /** @@ -386,7 +486,7 @@ static inline TrackBits GetCrossingReservationTrackBits(TileIndex t) static inline bool IsCrossingBarred(TileIndex t) { assert(IsLevelCrossing(t)); - return HasBit(_m[t].m5, 5); + return HasBit(GetTile(t)->m5, 5); } /** @@ -398,7 +498,7 @@ static inline bool IsCrossingBarred(TileIndex t) static inline void SetCrossingBarred(TileIndex t, bool barred) { assert(IsLevelCrossing(t)); - SB(_m[t].m5, 5, 1, barred ? 1 : 0); + SB(GetTile(t)->m5, 5, 1, barred ? 1 : 0); } /** @@ -428,7 +528,7 @@ static inline void BarCrossing(TileIndex t) */ static inline bool IsOnSnow(TileIndex t) { - return HasBit(_me[t].m7, 5); + return HasBit(GetTileEx(t)->m7, 5); } /** Toggle the snow/desert state of a road tile. */ @@ -439,7 +539,7 @@ static inline bool IsOnSnow(TileIndex t) */ static inline void ToggleSnow(TileIndex t) { - ToggleBit(_me[t].m7, 5); + ToggleBit(GetTileEx(t)->m7, 5); } @@ -461,7 +561,7 @@ enum Roadside { */ static inline Roadside GetRoadside(TileIndex tile) { - return (Roadside)GB(_me[tile].m6, 3, 3); + return (Roadside)GB(GetTileEx(tile)->m6, 3, 3); } /** @@ -471,7 +571,7 @@ static inline Roadside GetRoadside(TileIndex tile) */ static inline void SetRoadside(TileIndex tile, Roadside s) { - SB(_me[tile].m6, 3, 3, s); + SB(GetTileEx(tile)->m6, 3, 3, s); } /** @@ -491,9 +591,9 @@ static inline bool HasRoadWorks(TileIndex t) */ static inline bool IncreaseRoadWorksCounter(TileIndex t) { - AB(_me[t].m7, 0, 4, 1); + AB(GetTileEx(t)->m7, 0, 4, 1); - return GB(_me[t].m7, 0, 4) == 15; + return GB(GetTileEx(t)->m7, 0, 4) == 15; } /** @@ -522,7 +622,7 @@ static inline void TerminateRoadWorks(TileIndex t) assert(HasRoadWorks(t)); SetRoadside(t, (Roadside)(GetRoadside(t) - ROADSIDE_GRASS_ROAD_WORKS + ROADSIDE_GRASS)); /* Stop the counter */ - SB(_me[t].m7, 0, 4, 0); + SB(GetTileEx(t)->m7, 0, 4, 0); } @@ -531,11 +631,16 @@ static inline void TerminateRoadWorks(TileIndex t) * @param t The tile to query. * @return Diagonal direction of the depot exit. */ -static inline DiagDirection GetRoadDepotDirection(TileIndex t) +template +static inline DiagDirection GetRoadDepotDirection(typename TileIndexT::T t) { assert(IsRoadDepot(t)); - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(GetTile(t)->m5, 0, 2); } +/** @copydoc GetRoadDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetRoadDepotDirection(TileIndex t) { return GetRoadDepotDirection(t); } +/** @copydoc GetRoadDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetRoadDepotDirection(GenericTileIndex t) { return GetRoadDepotDirection(t); } RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance = false); @@ -550,18 +655,23 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge * @param road New owner of road. * @param tram New owner of tram tracks. */ -static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) +template +static inline void MakeRoadNormal(typename TileIndexT::T t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) { SetTileType(t, MP_ROAD); SetTileOwner(t, road); - _m[t].m2 = town; - _m[t].m3 = (HasBit(rot, ROADTYPE_TRAM) ? bits : 0); - _m[t].m4 = 0; - _m[t].m5 = (HasBit(rot, ROADTYPE_ROAD) ? bits : 0) | ROAD_TILE_NORMAL << 6; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = rot << 6; + GetTile(t)->m2 = town; + GetTile(t)->m3 = (HasBit(rot, ROADTYPE_TRAM) ? bits : 0); + GetTile(t)->m4 = 0; + GetTile(t)->m5 = (HasBit(rot, ROADTYPE_ROAD) ? bits : 0) | ROAD_TILE_NORMAL << 6; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = rot << 6; SetRoadOwner(t, ROADTYPE_TRAM, tram); } +/** @copydoc MakeRoadNormal(TileIndexT::T,RoadBits,RoadTypes,TownID,Owner,Owner) */ +static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) { MakeRoadNormal(t, bits, rot, town, road, tram); } +/** @copydoc MakeRoadNormal(TileIndexT::T,RoadBits,RoadTypes,TownID,Owner,Owner) */ +static inline void MakeRoadNormal(GenericTileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram) { MakeRoadNormal(t, bits, rot, town, road, tram); } /** * Make a level crossing. @@ -574,18 +684,23 @@ static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, Tow * @param rot New present road types. * @param town Town ID if the road is a town-owned road. */ -static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) +template +static inline void MakeRoadCrossing(typename TileIndexT::T t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) { SetTileType(t, MP_ROAD); SetTileOwner(t, rail); - _m[t].m2 = town; - _m[t].m3 = rat; - _m[t].m4 = 0; - _m[t].m5 = ROAD_TILE_CROSSING << 6 | roaddir; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = rot << 6 | road; + GetTile(t)->m2 = town; + GetTile(t)->m3 = rat; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = ROAD_TILE_CROSSING << 6 | roaddir; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = rot << 6 | road; SetRoadOwner(t, ROADTYPE_TRAM, tram); } +/** @copydoc MakeRoadCrossing(TileIndexT::T,Owner,Owner,Owner,Axis,RailType,RoadTypes,uint) */ +static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) { MakeRoadCrossing(t, road, tram, rail, roaddir, rat, rot, town); } +/** @copydoc MakeRoadCrossing(TileIndexT::T,Owner,Owner,Owner,Axis,RailType,RoadTypes,uint) */ +static inline void MakeRoadCrossing(GenericTileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town) { MakeRoadCrossing(t, road, tram, rail, roaddir, rat, rot, town); } /** * Make a road depot. @@ -595,17 +710,22 @@ static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner r * @param dir Direction of the depot exit. * @param rt Road type of the depot. */ -static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) +template +static inline void MakeRoadDepot(typename TileIndexT::T t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { SetTileType(t, MP_ROAD); SetTileOwner(t, owner); - _m[t].m2 = did; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = ROAD_TILE_DEPOT << 6 | dir; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = RoadTypeToRoadTypes(rt) << 6 | owner; + GetTile(t)->m2 = did; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = ROAD_TILE_DEPOT << 6 | dir; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = RoadTypeToRoadTypes(rt) << 6 | owner; SetRoadOwner(t, ROADTYPE_TRAM, owner); } +/** @copydoc MakeRoadDepot(TileIndexT::T,Owner,DepotID,DiagDirection,RoadType) */ +static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { MakeRoadDepot(t, owner, did, dir, rt); } +/** @copydoc MakeRoadDepot(TileIndexT::T,Owner,DepotID,DiagDirection,RoadType) */ +static inline void MakeRoadDepot(GenericTileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt) { MakeRoadDepot(t, owner, did, dir, rt); } #endif /* ROAD_MAP_H */ diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 7adc532b6..ab34c6c9a 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1153,7 +1153,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); - if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); + if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); return true; } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 8b5967175..56ca8679a 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -125,7 +125,7 @@ void SetWaterClassDependingOnSurroundings(TileIndex t, bool include_invalid_wate case MP_TREES: /* trees on shore */ - has_water |= (GB(_m[neighbour].m2, 4, 2) == TREE_GROUND_SHORE); + has_water |= (GB(_main_map.m[neighbour].m2, 4, 2) == TREE_GROUND_SHORE); break; default: break; @@ -151,13 +151,13 @@ static void ConvertTownOwner() for (TileIndex tile = 0; tile != MapSize(); tile++) { switch (GetTileType(tile)) { case MP_ROAD: - if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) { - _m[tile].m3 = OWNER_TOWN; + if (GB(_main_map.m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_main_map.m[tile].m3, 7)) { + _main_map.m[tile].m3 = OWNER_TOWN; } /* FALL THROUGH */ case MP_TUNNELBRIDGE: - if (_m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN); + if (_main_map.m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN); break; default: break; @@ -572,8 +572,8 @@ bool AfterLoadGame() } for (TileIndex t = 0; t < map_size; t++) { if (!IsTileType(t, MP_STATION)) continue; - if (_m[t].m5 > 7) continue; // is it a rail station tile? - st = Station::Get(_m[t].m2); + if (_main_map.m[t].m5 > 7) continue; // is it a rail station tile? + st = Station::Get(_main_map.m[t].m2); assert(st->train_station.tile != 0); int dx = TileX(t) - TileX(st->train_station.tile); int dy = TileY(t) - TileY(st->train_station.tile); @@ -588,14 +588,14 @@ bool AfterLoadGame() /* In old savegame versions, the heightlevel was coded in bits 0..3 of the type field */ for (TileIndex t = 0; t < map_size; t++) { - _m[t].height = GB(_m[t].type, 0, 4); - SB(_m[t].type, 0, 2, GB(_me[t].m6, 0, 2)); - SB(_me[t].m6, 0, 2, 0); + GetTile(t)->height = GB(GetTile(t)->type, 0, 4); + SB(GetTile(t)->type, 0, 2, GB(GetTileEx(t)->m6, 0, 2)); + SB(GetTileEx(t)->m6, 0, 2, 0); if (MayHaveBridgeAbove(t)) { - SB(_m[t].type, 2, 2, GB(_me[t].m6, 6, 2)); - SB(_me[t].m6, 6, 2, 0); + SB(GetTile(t)->type, 2, 2, GB(GetTileEx(t)->m6, 6, 2)); + SB(GetTileEx(t)->m6, 6, 2, 0); } else { - SB(_m[t].type, 2, 2, 0); + SB(GetTile(t)->type, 2, 2, 0); } } } @@ -827,7 +827,7 @@ bool AfterLoadGame() break; case MP_STATION: { - if (HasBit(_me[t].m6, 3)) SetBit(_me[t].m6, 2); + if (HasBit(_main_map.me[t].m6, 3)) SetBit(_main_map.me[t].m6, 2); StationGfx gfx = GetStationGfx(t); StationType st; if ( IsInsideMM(gfx, 0, 8)) { // Rail station @@ -865,7 +865,7 @@ bool AfterLoadGame() ResetSignalHandlers(); return false; } - SB(_me[t].m6, 3, 3, st); + SB(_main_map.me[t].m6, 3, 3, st); break; } } @@ -884,6 +884,9 @@ bool AfterLoadGame() if (!Station::IsExpected(bst)) break; Station *st = Station::From(bst); + /* Set up station catchment */ + st->catchment.BeforeAddTile(t, st->GetCatchmentRadius()); + switch (GetStationType(t)) { case STATION_TRUCK: case STATION_BUS: @@ -946,13 +949,13 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { switch (GetTileType(t)) { case MP_HOUSE: - _m[t].m4 = _m[t].m2; + _main_map.m[t].m4 = _main_map.m[t].m2; SetTownIndex(t, CalcClosestTownFromTile(t)->index); break; case MP_ROAD: - _m[t].m4 |= (_m[t].m2 << 4); - if ((GB(_m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { + _main_map.m[t].m4 |= (_main_map.m[t].m2 << 4); + if ((GB(_main_map.m[t].m5, 4, 2) == ROAD_TILE_CROSSING ? (Owner)_main_map.m[t].m3 : GetTileOwner(t)) == OWNER_TOWN) { SetTownIndex(t, CalcClosestTownFromTile(t)->index); } else { SetTownIndex(t, 0); @@ -1006,20 +1009,20 @@ bool AfterLoadGame() if (IsPlainRail(t)) { /* Swap ground type and signal type for plain rail tiles, so the * ground type uses the same bits as for depots and waypoints. */ - uint tmp = GB(_m[t].m4, 0, 4); - SB(_m[t].m4, 0, 4, GB(_m[t].m2, 0, 4)); - SB(_m[t].m2, 0, 4, tmp); - } else if (HasBit(_m[t].m5, 2)) { + uint tmp = GB(_main_map.m[t].m4, 0, 4); + SB(_main_map.m[t].m4, 0, 4, GB(_main_map.m[t].m2, 0, 4)); + SB(_main_map.m[t].m2, 0, 4, tmp); + } else if (HasBit(_main_map.m[t].m5, 2)) { /* Split waypoint and depot rail type and remove the subtype. */ - ClrBit(_m[t].m5, 2); - ClrBit(_m[t].m5, 6); + ClrBit(_main_map.m[t].m5, 2); + ClrBit(_main_map.m[t].m5, 6); } break; case MP_ROAD: /* Swap m3 and m4, so the track type for rail crossings is the * same as for normal rail. */ - Swap(_m[t].m3, _m[t].m4); + Swap(_main_map.m[t].m3, _main_map.m[t].m4); break; default: break; @@ -1033,16 +1036,16 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { switch (GetTileType(t)) { case MP_ROAD: - SB(_m[t].m5, 6, 2, GB(_m[t].m5, 4, 2)); + SB(_main_map.m[t].m5, 6, 2, GB(_main_map.m[t].m5, 4, 2)); switch (GetRoadTileType(t)) { default: SlErrorCorrupt("Invalid road tile type"); case ROAD_TILE_NORMAL: - SB(_m[t].m4, 0, 4, GB(_m[t].m5, 0, 4)); - SB(_m[t].m4, 4, 4, 0); - SB(_me[t].m6, 2, 4, 0); + SB(_main_map.m[t].m4, 0, 4, GB(_main_map.m[t].m5, 0, 4)); + SB(_main_map.m[t].m4, 4, 4, 0); + SB(_main_map.me[t].m6, 2, 4, 0); break; case ROAD_TILE_CROSSING: - SB(_m[t].m4, 5, 2, GB(_m[t].m5, 2, 2)); + SB(_main_map.m[t].m4, 5, 2, GB(_main_map.m[t].m5, 2, 2)); break; case ROAD_TILE_DEPOT: break; } @@ -1055,8 +1058,8 @@ bool AfterLoadGame() case MP_TUNNELBRIDGE: /* Middle part of "old" bridges */ - if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; - if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + if (old_bridge && IsBridge(t) && HasBit(_main_map.m[t].m5, 6)) break; + if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_main_map.m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { SetRoadTypes(t, ROADTYPES_ROAD); } break; @@ -1073,24 +1076,24 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { switch (GetTileType(t)) { case MP_ROAD: - if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_me[t].m7, 5, 3)); - SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert + if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.me[t].m7, 5, 3)); + SB(_main_map.me[t].m7, 5, 1, GB(_main_map.m[t].m3, 7, 1)); // snow/desert switch (GetRoadTileType(t)) { default: SlErrorCorrupt("Invalid road tile type"); case ROAD_TILE_NORMAL: - SB(_me[t].m7, 0, 4, GB(_m[t].m3, 0, 4)); // road works - SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground - SB(_m[t].m3, 0, 4, GB(_m[t].m4, 4, 4)); // tram bits - SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner - SB(_m[t].m5, 0, 4, GB(_m[t].m4, 0, 4)); // road bits + SB(_main_map.me[t].m7, 0, 4, GB(_main_map.m[t].m3, 0, 4)); // road works + SB(_main_map.me[t].m6, 3, 3, GB(_main_map.m[t].m3, 4, 3)); // ground + SB(_main_map.m[t].m3, 0, 4, GB(_main_map.m[t].m4, 4, 4)); // tram bits + SB(_main_map.m[t].m3, 4, 4, GB(_main_map.m[t].m5, 0, 4)); // tram owner + SB(_main_map.m[t].m5, 0, 4, GB(_main_map.m[t].m4, 0, 4)); // road bits break; case ROAD_TILE_CROSSING: - SB(_me[t].m7, 0, 5, GB(_m[t].m4, 0, 5)); // road owner - SB(_me[t].m6, 3, 3, GB(_m[t].m3, 4, 3)); // ground - SB(_m[t].m3, 4, 4, GB(_m[t].m5, 0, 4)); // tram owner - SB(_m[t].m5, 0, 1, GB(_m[t].m4, 6, 1)); // road axis - SB(_m[t].m5, 5, 1, GB(_m[t].m4, 5, 1)); // crossing state + SB(_main_map.me[t].m7, 0, 5, GB(_main_map.m[t].m4, 0, 5)); // road owner + SB(_main_map.me[t].m6, 3, 3, GB(_main_map.m[t].m3, 4, 3)); // ground + SB(_main_map.m[t].m3, 4, 4, GB(_main_map.m[t].m5, 0, 4)); // tram owner + SB(_main_map.m[t].m5, 0, 1, GB(_main_map.m[t].m4, 6, 1)); // road axis + SB(_main_map.m[t].m5, 5, 1, GB(_main_map.m[t].m4, 5, 1)); // crossing state break; case ROAD_TILE_DEPOT: @@ -1100,32 +1103,32 @@ bool AfterLoadGame() const Town *town = CalcClosestTownFromTile(t); if (town != NULL) SetTownIndex(t, town->index); } - _m[t].m4 = 0; + _main_map.m[t].m4 = 0; break; case MP_STATION: if (!IsRoadStop(t)) break; - if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3)); - SB(_me[t].m7, 0, 5, HasBit(_me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t)); - SB(_m[t].m3, 4, 4, _m[t].m1); - _m[t].m4 = 0; + if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.m[t].m3, 0, 3)); + SB(_main_map.me[t].m7, 0, 5, HasBit(_main_map.me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t)); + SB(_main_map.m[t].m3, 4, 4, _main_map.m[t].m1); + _main_map.m[t].m4 = 0; break; case MP_TUNNELBRIDGE: - if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break; - if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { - if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3)); + if (old_bridge && IsBridge(t) && HasBit(_main_map.m[t].m5, 6)) break; + if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_main_map.m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) { + if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_main_map.m[t].m3, 0, 3)); Owner o = GetTileOwner(t); - SB(_me[t].m7, 0, 5, o); // road owner - SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner + SB(_main_map.me[t].m7, 0, 5, o); // road owner + SB(_main_map.m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); // tram owner } - SB(_me[t].m6, 2, 4, GB(_m[t].m2, 4, 4)); // bridge type - SB(_me[t].m7, 5, 1, GB(_m[t].m4, 7, 1)); // snow/desert + SB(_main_map.me[t].m6, 2, 4, GB(_main_map.m[t].m2, 4, 4)); // bridge type + SB(_main_map.me[t].m7, 5, 1, GB(_main_map.m[t].m4, 7, 1)); // snow/desert - _m[t].m2 = 0; - _m[t].m4 = 0; + _main_map.m[t].m2 = 0; + _main_map.m[t].m4 = 0; break; default: break; @@ -1139,11 +1142,11 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { if (MayHaveBridgeAbove(t)) ClearBridgeMiddle(t); if (IsBridgeTile(t)) { - if (HasBit(_m[t].m5, 6)) { // middle part - Axis axis = (Axis)GB(_m[t].m5, 0, 1); + if (HasBit(_main_map.m[t].m5, 6)) { // middle part + Axis axis = (Axis)GB(_main_map.m[t].m5, 0, 1); - if (HasBit(_m[t].m5, 5)) { // transport route under bridge? - if (GB(_m[t].m5, 3, 2) == TRANSPORT_RAIL) { + if (HasBit(_main_map.m[t].m5, 5)) { // transport route under bridge? + if (GB(_main_map.m[t].m5, 3, 2) == TRANSPORT_RAIL) { MakeRailNormal( t, GetTileOwner(t), @@ -1162,7 +1165,7 @@ bool AfterLoadGame() ); } } else { - if (GB(_m[t].m5, 3, 2) == 0) { + if (GB(_main_map.m[t].m5, 3, 2) == 0) { MakeClear(t, CLEAR_GRASS, 3); } else { if (!IsTileFlat(t)) { @@ -1178,12 +1181,12 @@ bool AfterLoadGame() } SetBridgeMiddle(t, axis); } else { // ramp - Axis axis = (Axis)GB(_m[t].m5, 0, 1); - uint north_south = GB(_m[t].m5, 5, 1); + Axis axis = (Axis)GB(_main_map.m[t].m5, 0, 1); + uint north_south = GB(_main_map.m[t].m5, 5, 1); DiagDirection dir = ReverseDiagDir(XYNSToDiagDir(axis, north_south)); - TransportType type = (TransportType)GB(_m[t].m5, 1, 2); + TransportType type = (TransportType)GB(_main_map.m[t].m5, 1, 2); - _m[t].m5 = 1 << 7 | type << 2 | dir; + _main_map.m[t].m5 = 1 << 7 | type << 2 | dir; } } } @@ -1289,23 +1292,23 @@ bool AfterLoadGame() * (see the code somewhere above) so don't use m4, use m2 instead. */ /* convert PBS signals to combo-signals */ - if (HasBit(_m[t].m2, 2)) SB(_m[t].m2, 0, 2, SIGTYPE_COMBO); + if (HasBit(_main_map.m[t].m2, 2)) SB(_main_map.m[t].m2, 0, 2, SIGTYPE_COMBO); /* move the signal variant back */ - SB(_m[t].m2, 2, 1, HasBit(_m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 3); + SB(_main_map.m[t].m2, 2, 1, HasBit(_main_map.m[t].m2, 3) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(_main_map.m[t].m2, 3); } /* Clear PBS reservation on track */ if (!IsRailDepotTile(t)) { - SB(_m[t].m4, 4, 4, 0); + SB(_main_map.m[t].m4, 4, 4, 0); } else { - ClrBit(_m[t].m3, 6); + ClrBit(_main_map.m[t].m3, 6); } break; case MP_STATION: // Clear PBS reservation on station - ClrBit(_m[t].m3, 6); + ClrBit(_main_map.m[t].m3, 6); break; default: break; @@ -1414,31 +1417,31 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(53)) { for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_HOUSE)) { - if (GB(_m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { + if (GB(_main_map.m[t].m3, 6, 2) != TOWN_HOUSE_COMPLETED) { /* Move the construction stage from m3[7..6] to m5[5..4]. * The construction counter does not have to move. */ - SB(_m[t].m5, 3, 2, GB(_m[t].m3, 6, 2)); - SB(_m[t].m3, 6, 2, 0); + SB(_main_map.m[t].m5, 3, 2, GB(_main_map.m[t].m3, 6, 2)); + SB(_main_map.m[t].m3, 6, 2, 0); /* The "house is completed" bit is now in m6[2]. */ SetHouseCompleted(t, false); } else { /* The "lift has destination" bit has been moved from * m5[7] to m7[0]. */ - SB(_me[t].m7, 0, 1, HasBit(_m[t].m5, 7)); - ClrBit(_m[t].m5, 7); + SB(_main_map.me[t].m7, 0, 1, HasBit(_main_map.m[t].m5, 7)); + ClrBit(_main_map.m[t].m5, 7); /* The "lift is moving" bit has been removed, as it does * the same job as the "lift has destination" bit. */ - ClrBit(_m[t].m1, 7); + ClrBit(_main_map.m[t].m1, 7); /* The position of the lift goes from m1[7..0] to m6[7..2], * making m1 totally free, now. The lift position does not * have to be a full byte since the maximum value is 36. */ - SetLiftPosition(t, GB(_m[t].m1, 0, 6 )); + SetLiftPosition(t, GB(_main_map.m[t].m1, 0, 6 )); - _m[t].m1 = 0; - _m[t].m3 = 0; + _main_map.m[t].m1 = 0; + _main_map.m[t].m3 = 0; SetHouseCompleted(t, true); } } @@ -1453,19 +1456,19 @@ bool AfterLoadGame() if (IsTileType(t, MP_INDUSTRY)) { switch (GetIndustryGfx(t)) { case GFX_POWERPLANT_SPARKS: - _m[t].m3 = GB(_m[t].m1, 2, 5); + _main_map.m[t].m3 = GB(_main_map.m[t].m1, 2, 5); break; case GFX_OILWELL_ANIMATED_1: case GFX_OILWELL_ANIMATED_2: case GFX_OILWELL_ANIMATED_3: - _m[t].m3 = GB(_m[t].m1, 0, 2); + _main_map.m[t].m3 = GB(_main_map.m[t].m1, 0, 2); break; case GFX_COAL_MINE_TOWER_ANIMATED: case GFX_COPPER_MINE_TOWER_ANIMATED: case GFX_GOLD_MINE_TOWER_ANIMATED: - _m[t].m3 = _m[t].m1; + _main_map.m[t].m3 = _main_map.m[t].m1; break; default: // No animation states to change @@ -1513,8 +1516,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(52)) { for (TileIndex t = 0; t < map_size; t++) { - if (IsTileType(t, MP_OBJECT) && _m[t].m5 == OBJECT_STATUE) { - _m[t].m2 = CalcClosestTownFromTile(t)->index; + if (IsTileType(t, MP_OBJECT) && _main_map.m[t].m5 == OBJECT_STATUE) { + _main_map.m[t].m2 = CalcClosestTownFromTile(t)->index; } } } @@ -1577,10 +1580,10 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_RAILWAY) && HasSignals(t)) { /* move signal states */ - SetSignalStates(t, GB(_m[t].m2, 4, 4)); - SB(_m[t].m2, 4, 4, 0); + SetSignalStates(t, GB(_main_map.m[t].m2, 4, 4)); + SB(_main_map.m[t].m2, 4, 4, 0); /* clone signal type and variant */ - SB(_m[t].m2, 4, 3, GB(_m[t].m2, 0, 3)); + SB(_main_map.m[t].m2, 4, 3, GB(_main_map.m[t].m2, 0, 3)); } } } @@ -1623,7 +1626,7 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(83)) { for (TileIndex t = 0; t < map_size; t++) { if (IsShipDepotTile(t)) { - _m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; + _main_map.m[t].m4 = (TileHeight(t) == 0) ? OWNER_WATER : OWNER_NONE; } } } @@ -1659,8 +1662,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(81)) { for (TileIndex t = 0; t < map_size; t++) { if (GetTileType(t) == MP_TREES) { - TreeGround groundType = (TreeGround)GB(_m[t].m2, 4, 2); - if (groundType != TREE_GROUND_SNOW_DESERT) SB(_m[t].m2, 6, 2, 3); + TreeGround groundType = (TreeGround)GB(_main_map.m[t].m2, 4, 2); + if (groundType != TREE_GROUND_SNOW_DESERT) SB(_main_map.m[t].m2, 6, 2, 3); } } } @@ -1727,8 +1730,8 @@ bool AfterLoadGame() case STATION_OILRIG: case STATION_DOCK: case STATION_BUOY: - SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); - SB(_m[t].m3, 0, 2, 0); + SetWaterClass(t, (WaterClass)GB(_main_map.m[t].m3, 0, 2)); + SB(_main_map.m[t].m3, 0, 2, 0); break; default: @@ -1738,8 +1741,8 @@ bool AfterLoadGame() break; case MP_WATER: - SetWaterClass(t, (WaterClass)GB(_m[t].m3, 0, 2)); - SB(_m[t].m3, 0, 2, 0); + SetWaterClass(t, (WaterClass)GB(_main_map.m[t].m3, 0, 2)); + SB(_main_map.m[t].m3, 0, 2, 0); break; case MP_OBJECT: @@ -1766,7 +1769,7 @@ bool AfterLoadGame() MakeCanal(t, o, Random()); } } else if (IsShipDepot(t)) { - Owner o = (Owner)_m[t].m4; // Original water owner + Owner o = (Owner)_main_map.m[t].m4; // Original water owner SetWaterClass(t, o == OWNER_WATER ? WATER_CLASS_SEA : WATER_CLASS_CANAL); } } @@ -1855,8 +1858,8 @@ bool AfterLoadGame() /* Increase HouseAnimationFrame from 5 to 7 bits */ for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_HOUSE) && GetHouseType(t) >= NEW_HOUSE_OFFSET) { - SB(_me[t].m6, 2, 6, GB(_me[t].m6, 3, 5)); - SB(_m[t].m3, 5, 1, 0); + SB(_main_map.me[t].m6, 2, 6, GB(_main_map.me[t].m6, 3, 5)); + SB(_main_map.m[t].m3, 5, 1, 0); } } } @@ -1889,7 +1892,7 @@ bool AfterLoadGame() /* Replace "house construction year" with "house age" */ if (IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)) { - _m[t].m5 = Clamp(_cur_year - (_m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); + _main_map.m[t].m5 = Clamp(_cur_year - (_main_map.m[t].m5 + ORIGINAL_BASE_YEAR), 0, 0xFF); } } } @@ -1903,10 +1906,10 @@ bool AfterLoadGame() case MP_RAILWAY: if (HasSignals(t)) { /* move the signal variant */ - SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); - SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); - ClrBit(_m[t].m2, 2); - ClrBit(_m[t].m2, 6); + SetSignalVariant(t, TRACK_UPPER, HasBit(_main_map.m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC); + SetSignalVariant(t, TRACK_LOWER, HasBit(_main_map.m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC); + ClrBit(_main_map.m[t].m2, 2); + ClrBit(_main_map.m[t].m2, 6); } /* Clear PBS reservation on track */ @@ -1998,11 +2001,11 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { /* Check for HQ bit being set, instead of using map accessor, * since we've already changed it code-wise */ - if (IsTileType(t, MP_OBJECT) && HasBit(_m[t].m5, 7)) { + if (IsTileType(t, MP_OBJECT) && HasBit(_main_map.m[t].m5, 7)) { /* Move size and part identification of HQ out of the m5 attribute, * on new locations */ - _m[t].m3 = GB(_m[t].m5, 0, 5); - _m[t].m5 = OBJECT_HQ; + _main_map.m[t].m3 = GB(_main_map.m[t].m5, 0, 5); + _main_map.m[t].m5 = OBJECT_HQ; } } } @@ -2011,13 +2014,13 @@ bool AfterLoadGame() if (!IsTileType(t, MP_OBJECT)) continue; /* Reordering/generalisation of the object bits. */ - ObjectType type = _m[t].m5; - SB(_me[t].m6, 2, 4, type == OBJECT_HQ ? GB(_m[t].m3, 2, 3) : 0); - _m[t].m3 = type == OBJECT_HQ ? GB(_m[t].m3, 1, 1) | GB(_m[t].m3, 0, 1) << 4 : 0; + ObjectType type = _main_map.m[t].m5; + SB(_main_map.me[t].m6, 2, 4, type == OBJECT_HQ ? GB(_main_map.m[t].m3, 2, 3) : 0); + _main_map.m[t].m3 = type == OBJECT_HQ ? GB(_main_map.m[t].m3, 1, 1) | GB(_main_map.m[t].m3, 0, 1) << 4 : 0; /* Make sure those bits are clear as well! */ - _m[t].m4 = 0; - _me[t].m7 = 0; + _main_map.m[t].m4 = 0; + _main_map.me[t].m7 = 0; } } @@ -2030,15 +2033,15 @@ bool AfterLoadGame() /* No towns, so remove all objects! */ DoClearSquare(t); } else { - uint offset = _m[t].m3; + uint offset = _main_map.m[t].m3; /* Also move the animation state. */ - _m[t].m3 = GB(_me[t].m6, 2, 4); - SB(_me[t].m6, 2, 4, 0); + _main_map.m[t].m3 = GB(_main_map.me[t].m6, 2, 4); + SB(_main_map.me[t].m6, 2, 4, 0); if (offset == 0) { /* No offset, so make the object. */ - ObjectType type = _m[t].m5; + ObjectType type = _main_map.m[t].m5; int size = type == OBJECT_HQ ? 2 : 1; if (!Object::CanAllocateItem()) { @@ -2052,14 +2055,14 @@ bool AfterLoadGame() o->location.w = size; o->location.h = size; o->build_date = _date; - o->town = type == OBJECT_STATUE ? Town::Get(_m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX); - _m[t].m2 = o->index; + o->town = type == OBJECT_STATUE ? Town::Get(_main_map.m[t].m2) : CalcClosestTownFromTile(t, UINT_MAX); + _main_map.m[t].m2 = o->index; Object::IncTypeCount(type); } else { /* We're at an offset, so get the ID from our "root". */ TileIndex northern_tile = t - TileXY(GB(offset, 0, 4), GB(offset, 4, 4)); assert(IsTileType(northern_tile, MP_OBJECT)); - _m[t].m2 = _m[northern_tile].m2; + _main_map.m[t].m2 = _main_map.m[northern_tile].m2; } } } @@ -2274,8 +2277,8 @@ bool AfterLoadGame() if (IsSavegameVersionBefore(128)) { const Depot *d; FOR_ALL_DEPOTS(d) { - _m[d->xy].m2 = d->index; - if (IsTileType(d->xy, MP_WATER)) _m[GetOtherShipDepotTile(d->xy)].m2 = d->index; + _main_map.m[d->xy].m2 = d->index; + if (IsTileType(d->xy, MP_WATER)) _main_map.m[GetOtherShipDepotTile(d->xy)].m2 = d->index; } } @@ -2297,16 +2300,16 @@ bool AfterLoadGame() if (IsTileType(t, MP_CLEAR)) { if (GetRawClearGround(t) == CLEAR_SNOW) { SetClearGroundDensity(t, CLEAR_GRASS, GetClearDensity(t)); - SetBit(_m[t].m3, 4); + SetBit(_main_map.m[t].m3, 4); } else { - ClrBit(_m[t].m3, 4); + ClrBit(_main_map.m[t].m3, 4); } } if (IsTileType(t, MP_TREES)) { - uint density = GB(_m[t].m2, 6, 2); - uint ground = GB(_m[t].m2, 4, 2); - uint counter = GB(_m[t].m2, 0, 4); - _m[t].m2 = ground << 6 | density << 4 | counter; + uint density = GB(_main_map.m[t].m2, 6, 2); + uint ground = GB(_main_map.m[t].m2, 4, 2); + uint counter = GB(_main_map.m[t].m2, 0, 4); + _main_map.m[t].m2 = ground << 6 | density << 4 | counter; } } } @@ -2420,23 +2423,23 @@ bool AfterLoadGame() switch (GetTileType(t)) { case MP_HOUSE: if (GetHouseType(t) >= NEW_HOUSE_OFFSET) { - uint per_proc = _me[t].m7; - _me[t].m7 = GB(_me[t].m6, 2, 6) | (GB(_m[t].m3, 5, 1) << 6); - SB(_m[t].m3, 5, 1, 0); - SB(_me[t].m6, 2, 6, min(per_proc, 63)); + uint per_proc = _main_map.me[t].m7; + _main_map.me[t].m7 = GB(_main_map.me[t].m6, 2, 6) | (GB(_main_map.m[t].m3, 5, 1) << 6); + SB(_main_map.m[t].m3, 5, 1, 0); + SB(_main_map.me[t].m6, 2, 6, min(per_proc, 63)); } break; case MP_INDUSTRY: { - uint rand = _me[t].m7; - _me[t].m7 = _m[t].m3; - _m[t].m3 = rand; + uint rand = _main_map.me[t].m7; + _main_map.me[t].m7 = _main_map.m[t].m3; + _main_map.m[t].m3 = rand; break; } case MP_OBJECT: - _me[t].m7 = _m[t].m3; - _m[t].m3 = 0; + _main_map.me[t].m7 = _main_map.m[t].m3; + _main_map.m[t].m3 = 0; break; default: @@ -2761,16 +2764,16 @@ bool AfterLoadGame() for (TileIndex t = 0; t < map_size; t++) { if (!IsTileType(t, MP_CLEAR) && !IsTileType(t, MP_TREES)) continue; if (IsTileType(t, MP_CLEAR) && IsClearGround(t, CLEAR_FIELDS)) continue; - uint fence = GB(_m[t].m4, 5, 3); + uint fence = GB(_main_map.m[t].m4, 5, 3); if (fence != 0 && IsTileType(TILE_ADDXY(t, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 1, 0), CLEAR_FIELDS)) { SetFence(TILE_ADDXY(t, 1, 0), DIAGDIR_NE, fence); } - fence = GB(_m[t].m4, 2, 3); + fence = GB(_main_map.m[t].m4, 2, 3); if (fence != 0 && IsTileType(TILE_ADDXY(t, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(t, 0, 1), CLEAR_FIELDS)) { SetFence(TILE_ADDXY(t, 0, 1), DIAGDIR_NW, fence); } - SB(_m[t].m4, 2, 3, 0); - SB(_m[t].m4, 5, 3, 0); + SB(_main_map.m[t].m4, 2, 3, 0); + SB(_main_map.m[t].m4, 5, 3, 0); } } @@ -2887,9 +2890,9 @@ bool AfterLoadGame() /* Move ObjectType from map to pool */ for (TileIndex t = 0; t < map_size; t++) { if (IsTileType(t, MP_OBJECT)) { - Object *o = Object::Get(_m[t].m2); - o->type = _m[t].m5; - _m[t].m5 = 0; // zero upper bits of (now bigger) ObjectID + Object *o = Object::Get(_main_map.m[t].m2); + o->type = _main_map.m[t].m5; + _main_map.m[t].m5 = 0; // zero upper bits of (now bigger) ObjectID } } } diff --git a/src/saveload/map_sl.cpp b/src/saveload/map_sl.cpp index 86a185ca4..3a37173d4 100644 --- a/src/saveload/map_sl.cpp +++ b/src/saveload/map_sl.cpp @@ -56,7 +56,7 @@ static void Load_MAPT() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].type = buf[j]; } } @@ -67,7 +67,7 @@ static void Save_MAPT() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].type; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].type; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -79,7 +79,7 @@ static void Load_MAPH() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) GetTile(i++)->height = buf[j]; } } @@ -90,7 +90,7 @@ static void Save_MAPH() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].height; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = GetTile(i++)->height; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -102,7 +102,7 @@ static void Load_MAP1() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m1 = buf[j]; } } @@ -113,7 +113,7 @@ static void Save_MAP1() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m1; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m1; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -128,7 +128,7 @@ static void Load_MAP2() /* In those versions the m2 was 8 bits */ IsSavegameVersionBefore(5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16 ); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m2 = buf[j]; } } @@ -139,7 +139,7 @@ static void Save_MAP2() SlSetLength(size * sizeof(uint16)); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m2; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m2; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT16); } } @@ -151,7 +151,7 @@ static void Load_MAP3() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m3 = buf[j]; } } @@ -162,7 +162,7 @@ static void Save_MAP3() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m3; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m3; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -174,7 +174,7 @@ static void Load_MAP4() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m4 = buf[j]; } } @@ -185,7 +185,7 @@ static void Save_MAP4() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m4; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m4; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -197,7 +197,7 @@ static void Load_MAP5() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.m[i++].m5 = buf[j]; } } @@ -208,7 +208,7 @@ static void Save_MAP5() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _m[i++].m5; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.m[i++].m5; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -223,16 +223,16 @@ static void Load_MAP6() /* 1024, otherwise we overflow on 64x64 maps! */ SlArray(buf, 1024, SLE_UINT8); for (uint j = 0; j != 1024; j++) { - _me[i++].m6 = GB(buf[j], 0, 2); - _me[i++].m6 = GB(buf[j], 2, 2); - _me[i++].m6 = GB(buf[j], 4, 2); - _me[i++].m6 = GB(buf[j], 6, 2); + _main_map.me[i++].m6 = GB(buf[j], 0, 2); + _main_map.me[i++].m6 = GB(buf[j], 2, 2); + _main_map.me[i++].m6 = GB(buf[j], 4, 2); + _main_map.me[i++].m6 = GB(buf[j], 6, 2); } } } else { for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.me[i++].m6 = buf[j]; } } } @@ -244,7 +244,7 @@ static void Save_MAP6() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m6; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.me[i++].m6; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } @@ -256,7 +256,7 @@ static void Load_MAP7() for (TileIndex i = 0; i != size;) { SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j]; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _main_map.me[i++].m7 = buf[j]; } } @@ -267,7 +267,7 @@ static void Save_MAP7() SlSetLength(size); for (TileIndex i = 0; i != size;) { - for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _me[i++].m7; + for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = _main_map.me[i++].m7; SlArray(buf, MAP_SL_BUF_SIZE, SLE_UINT8); } } diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp index 0c5716681..30040629d 100644 --- a/src/saveload/oldloader_sl.cpp +++ b/src/saveload/oldloader_sl.cpp @@ -47,55 +47,55 @@ void FixOldMapArray() { /* TTO/TTD/TTDP savegames could have buoys at tile 0 * (without assigned station struct) */ - MemSetT(&_m[0], 0); + MemSetT(&_main_map.m[0], 0); SetTileType(0, MP_WATER); SetTileOwner(0, OWNER_WATER); } static void FixTTDMapArray() { - /* _old_map3 is moved to _m::m3 and _m::m4 */ + /* _old_map3 is moved to m3 and m4 */ for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { - _m[t].m3 = _old_map3[t * 2]; - _m[t].m4 = _old_map3[t * 2 + 1]; + _main_map.m[t].m3 = _old_map3[t * 2]; + _main_map.m[t].m4 = _old_map3[t * 2 + 1]; } for (TileIndex t = 0; t < OLD_MAP_SIZE; t++) { switch (GetTileType(t)) { case MP_STATION: - _m[t].m4 = 0; // We do not understand this TTDP station mapping (yet) - switch (_m[t].m5) { + _main_map.m[t].m4 = 0; // We do not understand this TTDP station mapping (yet) + switch (_main_map.m[t].m5) { /* We have drive through stops at a totally different place */ - case 0x53: case 0x54: _m[t].m5 += 170 - 0x53; break; // Bus drive through - case 0x57: case 0x58: _m[t].m5 += 168 - 0x57; break; // Truck drive through - case 0x55: case 0x56: _m[t].m5 += 170 - 0x55; break; // Bus tram stop - case 0x59: case 0x5A: _m[t].m5 += 168 - 0x59; break; // Truck tram stop + case 0x53: case 0x54: _main_map.m[t].m5 += 170 - 0x53; break; // Bus drive through + case 0x57: case 0x58: _main_map.m[t].m5 += 168 - 0x57; break; // Truck drive through + case 0x55: case 0x56: _main_map.m[t].m5 += 170 - 0x55; break; // Bus tram stop + case 0x59: case 0x5A: _main_map.m[t].m5 += 168 - 0x59; break; // Truck tram stop default: break; } break; case MP_RAILWAY: /* We save presignals different from TTDPatch, convert them */ - if (GB(_m[t].m5, 6, 2) == 1) { // RAIL_TILE_SIGNALS + if (GB(_main_map.m[t].m5, 6, 2) == 1) { // RAIL_TILE_SIGNALS /* This byte is always zero in TTD for this type of tile */ - if (_m[t].m4) { // Convert the presignals to our own format - _m[t].m4 = (_m[t].m4 >> 1) & 7; + if (_main_map.m[t].m4) { // Convert the presignals to our own format + _main_map.m[t].m4 = (_main_map.m[t].m4 >> 1) & 7; } } /* TTDPatch stores PBS things in L6 and all elsewhere; so we'll just * clear it for ourselves and let OTTD's rebuild PBS itself */ - _m[t].m4 &= 0xF; // Only keep the lower four bits; upper four is PBS + _main_map.m[t].m4 &= 0xF; // Only keep the lower four bits; upper four is PBS break; case MP_WATER: /* if water class == 3, make river there */ - if (GB(_m[t].m3, 0, 2) == 3) { + if (GB(_main_map.m[t].m3, 0, 2) == 3) { SetTileType(t, MP_WATER); SetTileOwner(t, OWNER_WATER); - _m[t].m2 = 0; - _m[t].m3 = 2; // WATER_CLASS_RIVER - _m[t].m4 = Random(); - _m[t].m5 = 0; + _main_map.m[t].m2 = 0; + _main_map.m[t].m3 = 2; // WATER_CLASS_RIVER + _main_map.m[t].m4 = Random(); + _main_map.m[t].m5 = 0; } break; @@ -199,7 +199,7 @@ void FixOldVehicles() RoadVehicle *rv = RoadVehicle::From(v); if (rv->state != RVSB_IN_DEPOT && rv->state != RVSB_WORMHOLE) { ClrBit(rv->state, 2); - if (IsTileType(rv->tile, MP_STATION) && _m[rv->tile].m5 >= 168) { + if (IsTileType(rv->tile, MP_STATION) && _main_map.m[rv->tile].m5 >= 168) { /* Update the vehicle's road state to show we're in a drive through road stop. */ SetBit(rv->state, RVS_IN_DT_ROAD_STOP); } @@ -229,9 +229,9 @@ static bool FixTTOMapArray() if (tt == 11) { /* TTO has a different way of storing monorail. * Instead of using bits in m3 it uses a different tile type. */ - _m[t].m3 = 1; // rail type = monorail (in TTD) + _main_map.m[t].m3 = 1; // rail type = monorail (in TTD) SetTileType(t, MP_RAILWAY); - _m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS + _main_map.m[t].m2 = 1; // set monorail ground to RAIL_GROUND_GRASS tt = MP_RAILWAY; } @@ -240,18 +240,18 @@ static bool FixTTOMapArray() break; case MP_RAILWAY: - switch (GB(_m[t].m5, 6, 2)) { + switch (GB(_main_map.m[t].m5, 6, 2)) { case 0: // RAIL_TILE_NORMAL break; case 1: // RAIL_TILE_SIGNALS - _m[t].m4 = (~_m[t].m5 & 1) << 2; // signal variant (present only in OTTD) - SB(_m[t].m2, 6, 2, GB(_m[t].m5, 3, 2)); // signal status - _m[t].m3 |= 0xC0; // both signals are present - _m[t].m5 = HasBit(_m[t].m5, 5) ? 2 : 1; // track direction (only X or Y) - _m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS + _main_map.m[t].m4 = (~_main_map.m[t].m5 & 1) << 2; // signal variant (present only in OTTD) + SB(_main_map.m[t].m2, 6, 2, GB(_main_map.m[t].m5, 3, 2)); // signal status + _main_map.m[t].m3 |= 0xC0; // both signals are present + _main_map.m[t].m5 = HasBit(_main_map.m[t].m5, 5) ? 2 : 1; // track direction (only X or Y) + _main_map.m[t].m5 |= 0x40; // RAIL_TILE_SIGNALS break; case 3: // RAIL_TILE_DEPOT - _m[t].m2 = 0; + _main_map.m[t].m2 = 0; break; default: return false; @@ -259,12 +259,12 @@ static bool FixTTOMapArray() break; case MP_ROAD: // road (depot) or level crossing - switch (GB(_m[t].m5, 4, 4)) { + switch (GB(_main_map.m[t].m5, 4, 4)) { case 0: // ROAD_TILE_NORMAL - if (_m[t].m2 == 4) _m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES + if (_main_map.m[t].m2 == 4) _main_map.m[t].m2 = 5; // 'small trees' -> ROADSIDE_TREES break; case 1: // ROAD_TILE_CROSSING (there aren't monorail crossings in TTO) - _m[t].m3 = _m[t].m1; // set owner of road = owner of rail + _main_map.m[t].m3 = _main_map.m[t].m1; // set owner of road = owner of rail break; case 2: // ROAD_TILE_DEPOT break; @@ -274,69 +274,69 @@ static bool FixTTOMapArray() break; case MP_HOUSE: - _m[t].m3 = _m[t].m2 & 0xC0; // construction stage - _m[t].m2 &= 0x3F; // building type - if (_m[t].m2 >= 5) _m[t].m2++; // skip "large office block on snow" + _main_map.m[t].m3 = _main_map.m[t].m2 & 0xC0; // construction stage + _main_map.m[t].m2 &= 0x3F; // building type + if (_main_map.m[t].m2 >= 5) _main_map.m[t].m2++; // skip "large office block on snow" break; case MP_TREES: - _m[t].m3 = GB(_m[t].m5, 3, 3); // type of trees - _m[t].m5 &= 0xC7; // number of trees and growth status + _main_map.m[t].m3 = GB(_main_map.m[t].m5, 3, 3); // type of trees + _main_map.m[t].m5 &= 0xC7; // number of trees and growth status break; case MP_STATION: - _m[t].m3 = (_m[t].m5 >= 0x08 && _m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) - if (_m[t].m5 >= 8) _m[t].m5 -= 8; // shift for monorail - if (_m[t].m5 >= 0x42) _m[t].m5++; // skip heliport + _main_map.m[t].m3 = (_main_map.m[t].m5 >= 0x08 && _main_map.m[t].m5 <= 0x0F) ? 1 : 0; // monorail -> 1, others 0 (rail, road, airport, dock) + if (_main_map.m[t].m5 >= 8) _main_map.m[t].m5 -= 8; // shift for monorail + if (_main_map.m[t].m5 >= 0x42) _main_map.m[t].m5++; // skip heliport break; case MP_WATER: - _m[t].m3 = _m[t].m2 = 0; + _main_map.m[t].m3 = _main_map.m[t].m2 = 0; break; case MP_VOID: - _m[t].m2 = _m[t].m3 = _m[t].m5 = 0; + _main_map.m[t].m2 = _main_map.m[t].m3 = _main_map.m[t].m5 = 0; break; case MP_INDUSTRY: - _m[t].m3 = 0; - switch (_m[t].m5) { + _main_map.m[t].m3 = 0; + switch (_main_map.m[t].m5) { case 0x24: // farm silo - _m[t].m5 = 0x25; + _main_map.m[t].m5 = 0x25; break; case 0x25: case 0x27: // farm case 0x28: case 0x29: case 0x2A: case 0x2B: // factory - _m[t].m5--; + _main_map.m[t].m5--; break; default: - if (_m[t].m5 >= 0x2C) _m[t].m5 += 3; // iron ore mine, steel mill or bank + if (_main_map.m[t].m5 >= 0x2C) _main_map.m[t].m5 += 3; // iron ore mine, steel mill or bank break; } break; case MP_TUNNELBRIDGE: - if (HasBit(_m[t].m5, 7)) { // bridge - byte m5 = _m[t].m5; - _m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1 - if (GB(m5, 1, 2) == 1) _m[t].m5 |= 0x02; // road bridge - if (GB(m5, 1, 2) == 3) _m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge + if (HasBit(_main_map.m[t].m5, 7)) { // bridge + byte m5 = _main_map.m[t].m5; + _main_map.m[t].m5 = m5 & 0xE1; // copy bits 7..5, 1 + if (GB(m5, 1, 2) == 1) _main_map.m[t].m5 |= 0x02; // road bridge + if (GB(m5, 1, 2) == 3) _main_map.m[t].m2 |= 0xA0; // monorail bridge -> tubular, steel bridge if (!HasBit(m5, 6)) { // bridge head - _m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) + _main_map.m[t].m3 = (GB(m5, 1, 2) == 3) ? 1 : 0; // track subtype (1 for monorail, 0 for others) } else { // middle bridge part - _m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge - if (GB(m5, 3, 2) == 3) _m[t].m3 |= 1; // track subtype under bridge - if (GB(m5, 3, 2) == 1) _m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear) + _main_map.m[t].m3 = HasBit(m5, 2) ? 0x10 : 0; // track subtype on bridge + if (GB(m5, 3, 2) == 3) _main_map.m[t].m3 |= 1; // track subtype under bridge + if (GB(m5, 3, 2) == 1) _main_map.m[t].m5 |= 0x08; // set for road/water under (0 for rail/clear) } } else { // tunnel entrance/exit - _m[t].m2 = 0; - _m[t].m3 = HasBit(_m[t].m5, 3); // monorail - _m[t].m5 &= HasBit(_m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) + _main_map.m[t].m2 = 0; + _main_map.m[t].m3 = HasBit(_main_map.m[t].m5, 3); // monorail + _main_map.m[t].m5 &= HasBit(_main_map.m[t].m5, 3) ? 0x03 : 0x07 ; // direction, transport type (== 0 for rail) } break; case MP_OBJECT: - _m[t].m2 = 0; - _m[t].m3 = 0; + _main_map.m[t].m2 = 0; + _main_map.m[t].m3 = 0; break; default: @@ -1477,15 +1477,15 @@ static bool LoadOldGameDifficulty(LoadgameState *ls, int num) static bool LoadOldMapPart1(LoadgameState *ls, int num) { if (_savegame_type == SGT_TTO) { - MemSetT(_m, 0, OLD_MAP_SIZE); - MemSetT(_me, 0, OLD_MAP_SIZE); + MemSetT(_main_map.m, 0, OLD_MAP_SIZE); + MemSetT(_main_map.me, 0, OLD_MAP_SIZE); } for (uint i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m1 = ReadByte(ls); + _main_map.m[i].m1 = ReadByte(ls); } for (uint i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m2 = ReadByte(ls); + _main_map.m[i].m2 = ReadByte(ls); } if (_savegame_type != SGT_TTO) { @@ -1495,10 +1495,10 @@ static bool LoadOldMapPart1(LoadgameState *ls, int num) } for (uint i = 0; i < OLD_MAP_SIZE / 4; i++) { byte b = ReadByte(ls); - _me[i * 4 + 0].m6 = GB(b, 0, 2); - _me[i * 4 + 1].m6 = GB(b, 2, 2); - _me[i * 4 + 2].m6 = GB(b, 4, 2); - _me[i * 4 + 3].m6 = GB(b, 6, 2); + _main_map.me[i * 4 + 0].m6 = GB(b, 0, 2); + _main_map.me[i * 4 + 1].m6 = GB(b, 2, 2); + _main_map.me[i * 4 + 2].m6 = GB(b, 4, 2); + _main_map.me[i * 4 + 3].m6 = GB(b, 6, 2); } } @@ -1510,10 +1510,10 @@ static bool LoadOldMapPart2(LoadgameState *ls, int num) uint i; for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].type = ReadByte(ls); + _main_map.m[i].type = ReadByte(ls); } for (i = 0; i < OLD_MAP_SIZE; i++) { - _m[i].m5 = ReadByte(ls); + _main_map.m[i].m5 = ReadByte(ls); } return true; diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 391ba30a8..2c3ee383e 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -95,7 +95,7 @@ void MoveBuoysToWaypoints() TILE_AREA_LOOP(t, train_st) { if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue; - SB(_me[t].m6, 3, 3, STATION_WAYPOINT); + SB(_main_map.me[t].m6, 3, 3, STATION_WAYPOINT); wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE); } diff --git a/src/saveload/waypoint_sl.cpp b/src/saveload/waypoint_sl.cpp index 0f9396985..0abd04cbf 100644 --- a/src/saveload/waypoint_sl.cpp +++ b/src/saveload/waypoint_sl.cpp @@ -75,10 +75,10 @@ void MoveWaypointsToBaseStations() if (wp->delete_ctr != 0) continue; // The waypoint was deleted /* Waypoint indices were not added to the map prior to this. */ - _m[wp->xy].m2 = (StationID)wp->index; + _main_map.m[wp->xy].m2 = (StationID)wp->index; - if (HasBit(_m[wp->xy].m3, 4)) { - wp->spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_m[wp->xy].m4 + 1); + if (HasBit(_main_map.m[wp->xy].m3, 4)) { + wp->spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_main_map.m[wp->xy].m4 + 1); } } } else { @@ -111,12 +111,12 @@ void MoveWaypointsToBaseStations() new_wp->string_id = STR_SV_STNAME_WAYPOINT; TileIndex t = wp->xy; - if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _m[t].m2 == wp->index) { + if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _main_map.m[t].m2 == wp->index) { /* The tile might've been reserved! */ - bool reserved = !IsSavegameVersionBefore(100) && HasBit(_m[t].m5, 4); + bool reserved = !IsSavegameVersionBefore(100) && HasBit(_main_map.m[t].m5, 4); /* The tile really has our waypoint, so reassign the map array */ - MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_m[t].m5, 0, 1), 0, GetRailType(t)); + MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_main_map.m[t].m5, 0, 1), 0, GetRailType(t)); new_wp->facilities |= FACIL_TRAIN; new_wp->owner = GetTileOwner(t); diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index f52de827d..0f0069d37 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -250,6 +250,28 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BV_BUILD_SEL, "WID_BV_BUILD_SEL"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BV_RENAME, "WID_BV_RENAME"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_PANEL, "WID_C_PANEL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_COPY, "WID_CT_COPY"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE, "WID_CT_PASTE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE_FLAG_BUTTON_BEGIN, "WID_CT_PASTE_FLAG_BUTTON_BEGIN"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_RAIL, "WID_CT_WITH_RAIL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_ROAD, "WID_CT_WITH_ROAD"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_WATER, "WID_CT_WITH_WATER"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_AIR, "WID_CT_WITH_AIR"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_MIRROR_SIGNALS, "WID_CT_MIRROR_SIGNALS"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_UPGRADE_BRIDGES, "WID_CT_UPGRADE_BRIDGES"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_WITH_STATIONS, "WID_CT_WITH_STATIONS"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_PASTE_FLAG_BUTTON_END, "WID_CT_PASTE_FLAG_BUTTON_END"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_CONVERT_RAILTYPE, "WID_CT_CONVERT_RAILTYPE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_TERRAFORM, "WID_CT_TERRAFORM"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_TRANSFORMATION, "WID_CT_TRANSFORMATION"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_ROTATE_LEFT, "WID_CT_ROTATE_LEFT"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_ROTATE_RIGHT, "WID_CT_ROTATE_RIGHT"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_REFLECT_NE_SW, "WID_CT_REFLECT_NE_SW"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_REFLECT_NW_SE, "WID_CT_REFLECT_NW_SE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_GLYPH, "WID_CT_HEIGHT_DIFF_GLYPH"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF, "WID_CT_HEIGHT_DIFF"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_INCREASE, "WID_CT_HEIGHT_DIFF_INCREASE"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CT_HEIGHT_DIFF_DECREASE, "WID_CT_HEIGHT_DIFF_DECREASE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_CAPTION, "WID_C_CAPTION"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_FACE, "WID_C_FACE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_C_FACE_TITLE, "WID_C_FACE_TITLE"); @@ -1123,6 +1145,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_LOWER_LAND, "WID_TT_LOWER_LAND"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_RAISE_LAND, "WID_TT_RAISE_LAND"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_LEVEL_LAND, "WID_TT_LEVEL_LAND"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_CLIPBOARD, "WID_TT_CLIPBOARD"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_DEMOLISH, "WID_TT_DEMOLISH"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_BUY_LAND, "WID_TT_BUY_LAND"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_PLANT_TREES, "WID_TT_PLANT_TREES"); diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index fbdffb54b..1825eb4b1 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -23,6 +23,7 @@ #include "../../widgets/bridge_widget.h" #include "../../widgets/build_vehicle_widget.h" #include "../../widgets/cheat_widget.h" +#include "../../widgets/clipboard_widget.h" #include "../../widgets/company_widget.h" #include "../../widgets/console_widget.h" #include "../../widgets/date_widget.h" @@ -993,6 +994,43 @@ public: WID_C_PANEL = ::WID_C_PANEL, ///< Panel where all cheats are shown in. }; + /* automatically generated from ../../widgets/clipboard_widget.h */ + /** Widgets of the #ClipboardToolbarWindow class. */ + enum ClipboardToolbarWidgets { + WID_CT_CLIPBOARD_1 = ::WID_CT_CLIPBOARD_1, ///< Button to switch to clipboard #1 + WID_CT_CLIPBOARD_2 = ::WID_CT_CLIPBOARD_2, ///< Button to switch to clipboard #2 + WID_CT_CLIPBOARD_3 = ::WID_CT_CLIPBOARD_3, ///< Button to switch to clipboard #3 + WID_CT_CLIPBOARD_4 = ::WID_CT_CLIPBOARD_4, ///< Button to switch to clipboard #4 + + WID_CT_COPY = ::WID_CT_COPY, ///< Copy button (single player) + WID_CT_PASTE = ::WID_CT_PASTE, ///< Paste button (single player) + + WID_CT_PASTE_FLAG_BUTTON_BEGIN = ::WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< First button to toggle copy-paste flag + WID_CT_WITH_RAIL = ::WID_CT_WITH_RAIL, ///< Toggle rails button + WID_CT_WITH_ROAD = ::WID_CT_WITH_ROAD, ///< Toggle roads button + WID_CT_WITH_WATER = ::WID_CT_WITH_WATER, ///< Toggle water button + WID_CT_WITH_AIR = ::WID_CT_WITH_AIR, ///< Toggle air button + WID_CT_MIRROR_SIGNALS = ::WID_CT_MIRROR_SIGNALS, ///< Toggle signal mirrorig button + WID_CT_UPGRADE_BRIDGES = ::WID_CT_UPGRADE_BRIDGES, ///< Toggle bridge upgrading button + WID_CT_WITH_STATIONS = ::WID_CT_WITH_STATIONS, ///< Toggle stations button + WID_CT_PASTE_FLAG_BUTTON_END = ::WID_CT_PASTE_FLAG_BUTTON_END, ///< Past-the-last button to toggle copy-paste flag + + WID_CT_CONVERT_RAILTYPE = ::WID_CT_CONVERT_RAILTYPE, ///< Button to select railtype to convert to + + WID_CT_TERRAFORM = ::WID_CT_TERRAFORM, ///< Button to select terraforming mode + + WID_CT_TRANSFORMATION = ::WID_CT_TRANSFORMATION, ///< Button to show/reset clipboard transformation + WID_CT_ROTATE_LEFT = ::WID_CT_ROTATE_LEFT, ///< Rotate left button + WID_CT_ROTATE_RIGHT = ::WID_CT_ROTATE_RIGHT, ///< Rotate right button + WID_CT_REFLECT_NE_SW = ::WID_CT_REFLECT_NE_SW, ///< Reflect against NE-SW axis button + WID_CT_REFLECT_NW_SE = ::WID_CT_REFLECT_NW_SE, ///< Reflect against NW-SE axis button + + WID_CT_HEIGHT_DIFF_GLYPH = ::WID_CT_HEIGHT_DIFF_GLYPH, ///< Image in front of buttons to increase/decrease height level + WID_CT_HEIGHT_DIFF = ::WID_CT_HEIGHT_DIFF, ///< Panel with buttons to increase/decrease height level + WID_CT_HEIGHT_DIFF_INCREASE = ::WID_CT_HEIGHT_DIFF_INCREASE, ///< Button to increase height level + WID_CT_HEIGHT_DIFF_DECREASE = ::WID_CT_HEIGHT_DIFF_DECREASE, ///< Button to decrease height level + }; + /* automatically generated from ../../widgets/company_widget.h */ /** Widgets of the #CompanyWindow class. */ enum CompanyWidgets { @@ -2310,6 +2348,7 @@ public: WID_TT_LOWER_LAND = ::WID_TT_LOWER_LAND, ///< Lower land button. WID_TT_RAISE_LAND = ::WID_TT_RAISE_LAND, ///< Raise land button. WID_TT_LEVEL_LAND = ::WID_TT_LEVEL_LAND, ///< Level land button. + WID_TT_CLIPBOARD = ::WID_TT_CLIPBOARD, ///< Button to open the clipboard toolbar WID_TT_DEMOLISH = ::WID_TT_DEMOLISH, ///< Demolish aka dynamite button. WID_TT_BUY_LAND = ::WID_TT_BUY_LAND, ///< Buy land button. WID_TT_PLANT_TREES = ::WID_TT_PLANT_TREES, ///< Plant trees button (note: opens separate window, no place-push-button). diff --git a/src/script/api/template/template_window.hpp.sq b/src/script/api/template/template_window.hpp.sq index a21a75ab8..14cac3dd8 100644 --- a/src/script/api/template/template_window.hpp.sq +++ b/src/script/api/template/template_window.hpp.sq @@ -47,6 +47,8 @@ namespace SQConvert { template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::BuildVehicleWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::CheatWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CheatWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::CheatWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::ClipboardToolbarWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::ClipboardToolbarWidgets)tmp; } + template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::ClipboardToolbarWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::CompanyWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CompanyWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::CompanyWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::CompanyFinancesWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::CompanyFinancesWidgets)tmp; } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 6cde709b0..69966f204 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1483,6 +1483,7 @@ static SettingsContainer &GetSettingsTree() graphics->Add(new SettingEntry("gui.zoom_max")); graphics->Add(new SettingEntry("gui.smallmap_land_colour")); graphics->Add(new SettingEntry("gui.graph_line_thickness")); + graphics->Add(new SettingEntry("gui.forecast_display")); } SettingsPage *sound = main->Add(new SettingsPage(STR_CONFIG_SETTING_SOUND)); @@ -1540,6 +1541,7 @@ static SettingsContainer &GetSettingsTree() construction->Add(new SettingEntry("gui.quick_goto")); construction->Add(new SettingEntry("gui.default_rail_type")); construction->Add(new SettingEntry("gui.disable_unsuitable_building")); + construction->Add(new SettingEntry("construction.clipboard_capacity")); } interface->Add(new SettingEntry("gui.autosave")); @@ -1567,6 +1569,7 @@ static SettingsContainer &GetSettingsTree() advisors->Add(new SettingEntry("gui.vehicle_income_warn")); advisors->Add(new SettingEntry("gui.lost_vehicle_warn")); advisors->Add(new SettingEntry("gui.show_finances")); + advisors->Add(new SettingEntry("gui.townrating_indicator")); advisors->Add(new SettingEntry("news_display.economy")); advisors->Add(new SettingEntry("news_display.subsidies")); advisors->Add(new SettingEntry("news_display.open")); diff --git a/src/settings_type.h b/src/settings_type.h index 2dc9ec92e..60709e63a 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -108,6 +108,7 @@ struct GUISettings { uint8 date_format_in_default_names; ///< should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) or ISO dates (2008-12-31) byte max_num_autosaves; ///< controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1) bool population_in_label; ///< show the population of a town in his label? + bool forecast_display; ///< show the supply and demand forecasting on station building uint8 right_mouse_btn_emulation; ///< should we emulate right mouse clicking? uint8 scrollwheel_scrolling; ///< scrolling using the scroll wheel? uint8 scrollwheel_multiplier; ///< how much 'wheel' per incoming event from the OS? @@ -120,6 +121,7 @@ struct GUISettings { bool timetable_in_ticks; ///< whether to show the timetable in ticks rather than days bool quick_goto; ///< Allow quick access to 'goto button' in vehicle orders window bool auto_euro; ///< automatically switch to euro in 2002 + byte simulated_wormhole_signals; ///< simulate signals in tunnel byte drag_signals_density; ///< many signals density bool drag_signals_fixed_distance; ///< keep fixed distance between signals when dragging Year semaphore_build_before; ///< build semaphore signals automatically before this year @@ -157,6 +159,7 @@ struct GUISettings { bool scenario_developer; ///< activate scenario developer: allow modifying NewGRFs in an existing game uint8 settings_restriction_mode; ///< selected restriction mode in adv. settings GUI. @see RestrictionMode bool newgrf_show_old_versions; ///< whether to show old versions in the NewGRF list + bool townrating_indicator; ///< Whether to show the town rating indicators. uint8 newgrf_default_palette; ///< default palette to use for NewGRFs without action 14 palette information /** @@ -312,6 +315,7 @@ struct ConstructionSettings { bool freeform_edges; ///< allow terraforming the tiles at the map edges uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused + uint8 clipboard_capacity; ///< maximum copy/paste area size (width/height) uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index de1bf4f8c..f7009ea86 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -609,7 +609,7 @@ static void ShipController(Ship *v) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); - if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); + if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); return; } } diff --git a/src/signal.cpp b/src/signal.cpp index 8e870b53d..059a3fb03 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -197,6 +197,14 @@ static Vehicle *TrainOnTileEnum(Vehicle *v, void *) return v; } +/** Check whether there is a train only on ramp. */ +static Vehicle *TrainInWormholeTileEnum(Vehicle *v, void *data) +{ + /* Only look for front engine or last wagon. */ + if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; + if (*(TileIndex *)data != TileVirtXY(v->x_pos, v->y_pos)) return NULL; + return v; +} /** * Perform some operations before adding data into Todo set @@ -376,17 +384,39 @@ static SigFlags ExploreSegment(Owner owner) if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; DiagDirection dir = GetTunnelBridgeDirection(tile); - if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole - if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; - enterdir = dir; - exitdir = ReverseDiagDir(dir); - tile += TileOffsByDiagDir(exitdir); // just skip to next tile - } else { // NOT incoming from the wormhole! - if (ReverseDiagDir(enterdir) != dir) continue; - if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; - tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile - enterdir = INVALID_DIAGDIR; - exitdir = INVALID_DIAGDIR; + if (HasWormholeSignals(tile)) { + if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole + if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { // tunnel entrence is ignored + if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; + } + enterdir = dir; + exitdir = ReverseDiagDir(dir); + tile += TileOffsByDiagDir(exitdir); // just skip to next tile + } else { // NOT incoming from the wormhole! + if (ReverseDiagDir(enterdir) != dir) continue; + if (!(flags & SF_TRAIN)) { + if (HasVehicleOnPos(tile, &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && IsTunnelBridgeExit(tile)) { + if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(tile), &tile, &TrainInWormholeTileEnum)) flags |= SF_TRAIN; + } + } + continue; + } + } else { + if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; + enterdir = dir; + exitdir = ReverseDiagDir(dir); + tile += TileOffsByDiagDir(exitdir); // just skip to next tile + } else { // NOT incoming from the wormhole! + if (ReverseDiagDir(enterdir) != dir) continue; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; + tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile + enterdir = INVALID_DIAGDIR; + exitdir = INVALID_DIAGDIR; + } + } } break; @@ -494,7 +524,9 @@ static SigSegState UpdateSignalsInBuffer(Owner owner) assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL); assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile))); _tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre - _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); + if (!HasWormholeSignals(tile)) { // Don't worry with other side of tunnel. + _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); + } break; case MP_RAILWAY: diff --git a/src/signs.cpp b/src/signs.cpp index 2a23a43c9..83179eeac 100644 --- a/src/signs.cpp +++ b/src/signs.cpp @@ -49,7 +49,7 @@ void Sign::UpdateVirtCoord() { Point pt = RemapCoords(this->x, this->y, this->z); SetDParam(0, this->index); - this->sign.UpdatePosition(pt.x, pt.y - 6 * ZOOM_LVL_BASE, STR_WHITE_SIGN); + this->sign.UpdatePosition(pt.x, pt.y - 6 * ZOOM_LVL_BASE, STR_WHITE_SIGN, STR_WHITE_SIGN); } /** Update the coordinates of all signs */ diff --git a/src/slope_func.h b/src/slope_func.h index 4aff6b9d3..40431b3a2 100644 --- a/src/slope_func.h +++ b/src/slope_func.h @@ -189,6 +189,48 @@ static inline Corner OppositeCorner(Corner corner) } /** + * Transform a Corner. + * @param c The Corner to transform. + * @param transformation Transformation to perform. + * @return The transformed Corner. + */ +static inline Corner TransformCorner(Corner c, DirTransformation transformation) +{ + if (transformation & DTR_REFLECTION_BIT) c = (Corner)(1 - c); // reflect against X-axis (let overflow) + return (Corner)((uint)(c + transformation) % CORNER_END); // rotate and cut off overflowing bits +} + +/** + * Transform a Slope. + * @param s The Slope to transform. + * @param transformation Transformation to perform. + * @return The transformed Slope. + */ +static inline Slope TransformSlope(Slope s, DirTransformation transformation) +{ + assert((s & ~(SLOPE_ELEVATED | SLOPE_STEEP)) == 0); + + Slope steep_bit = s & SLOPE_STEEP; // store the "steep" bit + s &= SLOPE_ELEVATED; // only "corner" bits need to be transformed + + /* reflect agains X axis before rotating */ + if (transformation & DTR_REFLECTION_BIT) { + /* reflect by swapping odd and even bits (the numbers are bit positions): + * [N] 3/ 2/ + * [W] [E] 0/2 --reflect-against-x-axis--> 1/3 + * [S] /1 /0 + * SLOPE_W (bit 0) needs to be swapped with SLOPE_S (bit 1) + * SLOPE_E (bit 2) needs to be swapped with SLOPE_N (bit 3) */ + s = SwapOddEvenBits(s); + } + + /* rotate */ + s = (Slope)((s | (s << 4)) >> (transformation & DTR_ROTATION_MASK)) & SLOPE_ELEVATED; + + return s | steep_bit; +} + +/** * Tests if a specific slope has exactly three corners raised. * * @param s The #Slope diff --git a/src/station.cpp b/src/station.cpp index 456262dea..593096cbc 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -24,6 +24,7 @@ #include "roadstop_base.h" #include "industry.h" #include "core/random_func.hpp" +#include "overlay_cmd.h" #include "linkgraph/linkgraph.h" #include "linkgraph/linkgraphschedule.h" @@ -132,6 +133,10 @@ Station::~Station() InvalidateWindowData(WC_STATION_LIST, this->owner, 0); } + if (Overlays::Instance()->HasStation(Station::Get(this->index))) { + Overlays::Instance()->ToggleStation(Station::Get(this->index)); + }; + DeleteWindowById(WC_STATION_VIEW, index); /* Now delete all orders that go to the station */ @@ -147,6 +152,25 @@ Station::~Station() CargoPacket::InvalidateAllFrom(this->index); } +/** + * Evaluate if a tile is in station catchment area. + * @param ti the tile info + * @param type the catchment type of the station + * @return true/false if the tile is in catchment + */ +bool Station::IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) const +{ + switch (type) { + case ACCEPTANCE: + return this->rect.PtInExtendedRect(TileX(ti->tile),TileY(ti->tile),this->GetCatchmentRadius()); + case PRODUCTION: + return this->catchment.IsTileInCatchment(ti->tile); + case INDUSTRY: + return false; + default: + NOT_REACHED(); + } +} /** * Invalidating of the JoinStation window has to be done @@ -227,6 +251,23 @@ void Station::MarkTilesDirty(bool cargo_change) const } } +/** + * Marks the acceptance tiles of the station as dirty. + * + * @ingroup dirty + */ +void Station::MarkAcceptanceTilesDirty() const +{ + Rect rec = this->GetCatchmentRect(); + TileIndex top_left = TileXY(rec.left, rec.top); + int width = rec.right - rec.left + 1; + int height = rec.bottom - rec.top + 1; + + TILE_AREA_LOOP(tile, TileArea(top_left, width, height) ) { + MarkTileDirtyByTile(tile); + } +} + /* virtual */ uint Station::GetPlatformLength(TileIndex tile) const { assert(this->TileBelongsToRailStation(tile)); @@ -568,3 +609,88 @@ Money AirportMaintenanceCost(Owner owner) /* 3 bits fraction for the maintenance cost factor. */ return total_cost >> 3; } + +/************************************************************************/ +/* StationCatchment implementation */ +/************************************************************************/ + +StationCatchment::StationCatchment() +{ +} + +/** + * Determines whether a given point (x, y) is within the station catchment area + * @param tile TileIndex to test + * @return true if the point is within the station catchment area + */ +bool StationCatchment::IsTileInCatchment(TileIndex tile) const +{ + return this->catchmentTiles.find(tile) != this->catchmentTiles.end(); +} + +bool StationCatchment::IsEmpty() const +{ + return this->catchmentTiles.empty(); +} + +void StationCatchment::BeforeAddTile(TileIndex tile, uint catchmentRadius) +{ + int x = TileX(tile); + int y = TileY(tile); + TileIndex top_left = TileXY(max(x - catchmentRadius,0),max(y - catchmentRadius, 0)); + int w = min(x + catchmentRadius, MapMaxX()) - TileX(top_left) + 1; + int h = min(y + catchmentRadius, MapMaxY()) - TileY(top_left) + 1; + if (IsEmpty()) { + /* we are adding the first station tile */ + TILE_AREA_LOOP(t, TileArea(top_left, w, h) ) { + std::set fromSet; + fromSet.insert(tile); + this->catchmentTiles[t] = fromSet; + } + } else { + TILE_AREA_LOOP(t, TileArea(top_left, w, h) ) { + std::map >::iterator found = this->catchmentTiles.find(t); + if ( found == this->catchmentTiles.end()) { + std::set fromSet; + fromSet.insert(tile); + this->catchmentTiles[t] = fromSet; + } else if ((*found).second.find(tile) == (*found).second.end()) { + (*found).second.insert(tile); + } + } + } +} + +void StationCatchment::BeforeAddRect(TileIndex tile, int w, int h, uint catchmentRadius) +{ + TILE_AREA_LOOP(t, TileArea(tile, w, h) ) { + this->BeforeAddTile(t, catchmentRadius); + } +} + +void StationCatchment::AfterRemoveTile(TileIndex tile, uint catchmentRadius) +{ + int x = TileX(tile); + int y = TileY(tile); + TileIndex top_left = TileXY(max(x - catchmentRadius,0),max(y - catchmentRadius, 0)); + int w = min(x + catchmentRadius, MapMaxX()) - TileX(top_left) + 1; + int h = min(y + catchmentRadius, MapMaxY()) - TileY(top_left) + 1; + TILE_AREA_LOOP(t, TileArea(top_left, w, h)) { + std::map >::iterator found = this->catchmentTiles.find(t); + assert(found != this->catchmentTiles.end()); + std::set::iterator stTileIter = (*found).second.find(tile); + assert(stTileIter != (*found).second.end()); + (*found).second.erase(stTileIter); + if ((*found).second.empty()) { + // tile t is no longer in StationCatchment + this->catchmentTiles.erase(found); + } + } +} + +void StationCatchment::AfterRemoveRect(TileIndex tile, int w, int h, uint catchmentRadius) +{ + TILE_AREA_LOOP(t, TileArea(tile, w, h)) { + this->AfterRemoveTile(t, catchmentRadius); + } +} \ No newline at end of file diff --git a/src/station_base.h b/src/station_base.h index af4d206ba..f017f2722 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -19,7 +19,9 @@ #include "industry_type.h" #include "linkgraph/linkgraph_type.h" #include "newgrf_storage.h" +#include "core/smallvec_type.hpp" #include +#include typedef Pool StationPool; extern StationPool _station_pool; @@ -301,6 +303,25 @@ struct GoodsEntry { } }; +enum CatchmentType { + ACCEPTANCE = 0, + PRODUCTION = 1, + INDUSTRY = 2 +}; + +struct StationCatchment { + std::map > catchmentTiles; +public: + StationCatchment(); + void MakeEmpty(); + bool IsTileInCatchment(TileIndex tile) const; + bool IsEmpty() const; + void BeforeAddTile(TileIndex tile, uint catchmentRadius); + void BeforeAddRect(TileIndex tile, int w, int h, uint catchmentRadius); + void AfterRemoveTile(TileIndex tile, uint catchmentRadius); + void AfterRemoveRect(TileIndex tile, int w, int h, uint catchmentRadius); +}; + /** All airport-related information. Only valid if tile != INVALID_TILE. */ struct Airport : public TileArea { Airport() : TileArea(INVALID_TILE, 0, 0) {} @@ -474,6 +495,8 @@ public: IndustryVector industries_near; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry() + StationCatchment catchment; + Station(TileIndex tile = INVALID_TILE); ~Station(); @@ -489,6 +512,11 @@ public: static void RecomputeIndustriesNearForAll(); uint GetCatchmentRadius() const; + + bool IsTileInCatchmentArea(const TileInfo* ti, CatchmentType type) const; + + void MarkAcceptanceTilesDirty() const; + Rect GetCatchmentRect() const; /* virtual */ inline bool TileBelongsToRailStation(TileIndex tile) const diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index ea890688e..963572a99 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -13,6 +13,9 @@ #include "aircraft.h" #include "bridge_map.h" #include "cmd_helper.h" +#include "copypaste_cmd.h" +#include "clipboard_func.h" +#include "clipboard_gui.h" #include "viewport_func.h" #include "command_func.h" #include "town.h" @@ -42,6 +45,7 @@ #include "waypoint_base.h" #include "waypoint_func.h" #include "pbs.h" +#include "overlay_cmd.h" #include "debug.h" #include "core/random_func.hpp" #include "company_base.h" @@ -53,11 +57,17 @@ #include "linkgraph/linkgraph_base.h" #include "linkgraph/refresh.h" #include "widgets/station_widget.h" +#include "tilearea_func.h" #include "table/strings.h" #include "safeguards.h" +#include + +int _station_cmd_gfx_to_paste = -1; ///< station graphics (#StationGfx) to be set on currently being pasted rail station tile, -1 to use default graphics +int _station_cmd_specindex_to_paste = -1; ///< custom spec index to be used by currently being pasted rail station tile or waypoint part, -1 to generate new index + /** * Static instance of FlowStat::SharesMap. * Note: This instance is created on task start. @@ -93,19 +103,25 @@ bool IsHangar(TileIndex t) * @param ta the area to search over * @param closest_station the closest station found so far * @param st to 'return' the found station + * @param station_mask if not INVALID_STATION, search for this exact station only and ignore other stations * @return Succeeded command (if zero or one station found) or failed command (for two or more stations found). */ template -CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st) +CommandCost GetStationAround(TileArea ta, StationID closest_station, T **st, byte radius = 1, StationID station_mask = INVALID_STATION) { - ta.tile -= TileDiffXY(1, 1); - ta.w += 2; - ta.h += 2; + int x = Clamp(TileX(ta.tile) - radius, 0, MapMaxX()); + int y = Clamp(TileY(ta.tile) - radius, 0, MapMaxY()); + ta.w = Clamp(TileX(ta.tile) + ta.w + radius, x, MapMaxX()) - x; + ta.h = Clamp(TileY(ta.tile) + ta.h + radius, y, MapMaxY()) - y; + ta.tile = TileXY(x, y); + + if (station_mask != INVALID_STATION && closest_station != station_mask) closest_station = INVALID_STATION; /* check around to see if there's any stations there */ TILE_AREA_LOOP(tile_cur, ta) { if (IsTileType(tile_cur, MP_STATION)) { StationID t = GetStationIndex(tile_cur); + if (station_mask != INVALID_STATION && t != station_mask) continue; if (!T::IsValidID(t)) continue; if (closest_station == INVALID_STATION) { @@ -420,7 +436,7 @@ void Station::UpdateVirtCoord() SetDParam(0, this->index); SetDParam(1, this->facilities); - this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION); + this->sign.UpdatePosition(pt.x, pt.y, STR_VIEWPORT_STATION, STR_VIEWPORT_STATION); SetWindowDirty(WC_STATION_VIEW, this->index); } @@ -555,6 +571,126 @@ CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint3 } /** + * Get the rate of cargo being produced around the tile (in a rectangle). + * @param tile Northtile of area + * @param w X extent of the area + * @param h Y extent of the area + * @param rad Search radius in addition to the given area + */ +CargoArray GetProductionRateAroundTiles(TileIndex tile, int w, int h, int rad) +{ + CargoArray production_rate; + + int x = TileX(tile); + int y = TileY(tile); + + /* expand the region by rad tiles on each side + * while making sure that we remain inside the board. */ + int x2 = min(x + w + rad, MapSizeX()); + int x1 = max(x - rad, 0); + + int y2 = min(y + h + rad, MapSizeY()); + int y1 = max(y - rad, 0); + + assert(x1 < x2); + assert(y1 < y2); + assert(w > 0); + assert(h > 0); + + TileArea ta(TileXY(x1, y1), TileXY(x2 - 1, y2 - 1)); + + /* Loop over all tiles to get the produced cargo of + * everything except industries */ + TILE_AREA_LOOP(tile, ta) { + if (GetTileType(tile) == MP_HOUSE) { + if (!IsHouseCompleted(tile)) continue; + + const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile)); + + /* Use expected values to calculate supply forecasting since there is a random factor + * in the equation. + * E[x] = x1p1 + x2p2 + ... + xkpk + * random number ranges from 0 to 255. However, all the ones above population are dropped. + * All probabilities p1...pk are the same ( = 1 / 256 ) + * Thus, E[x] = (1 + 2 + ... + pop - 1) / 256 + */ + uint sum = 0; + for (uint i = 1; i < hs->population; i++) { + sum += i; + } + /* Bitshift to the right by 8 is from the above equation and 3 is + * to divide by 8. For details, look at TileLoop_Town() in town_cmd.cpp */ + uint amt = (sum >> 11) + 1; + if (EconomyIsInRecession()) amt = (amt + 1) >> 1; + production_rate[CT_PASSENGERS] += amt; + + sum = 0; + for (uint i = 1; i < hs->mail_generation; i++) { + sum += i; + } + amt = (sum >> 11) + 1; + if (EconomyIsInRecession()) amt = (amt + 1) >> 1; + production_rate[CT_MAIL] += amt; + } + } + + /* Loop over the industries. They produce cargo for + * anything that is within 'rad' from their bounding + * box. As such if you have e.g. a oil well the tile + * area loop might not hit an industry tile while + * the industry would produce cargo for the station. + */ + const Industry *i; + FOR_ALL_INDUSTRIES(i) { + if (!ta.Intersects(i->location)) continue; + + for (uint j = 0; j < lengthof(i->produced_cargo); j++) { + CargoID cargo = i->produced_cargo[j]; + + if (cargo != CT_INVALID) production_rate[cargo] += i->last_month_production[j]; + } + } + + return production_rate; +} + +/** + * Get the acceptance rate of cargoes around the tile. + * @param tile Center of the search area + * @param w X extent of area + * @param h Y extent of area + * @param rad Search radius in addition to given area + * @param always_accepted bitmask of cargo accepted by houses and headquarters; can be NULL + */ +CargoArray GetAcceptanceRateAroundTiles(TileIndex tile, int w, int h, int rad) +{ + CargoArray acceptance_rate; + + int x = TileX(tile); + int y = TileY(tile); + + /* expand the region by rad tiles on each side + * while making sure that we remain inside the board. */ + int x2 = min(x + w + rad, MapSizeX()); + int y2 = min(y + h + rad, MapSizeY()); + int x1 = max(x - rad, 0); + int y1 = max(y - rad, 0); + + assert(x1 < x2); + assert(y1 < y2); + assert(w > 0); + assert(h > 0); + + for (int yc = y1; yc != y2; yc++) { + for (int xc = x1; xc != x2; xc++) { + TileIndex tile = TileXY(xc, yc); + AddAcceptedCargo(tile, acceptance_rate, NULL); + } + } + + return acceptance_rate; +} +/** * Update the acceptance for a station. * @param st Station to update * @param show_msg controls whether to display a message that acceptance was changed. @@ -639,6 +775,7 @@ void UpdateStationAcceptance(Station *st, bool show_msg) /* redraw the station view since acceptance changed */ SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_ACCEPT_RATING_LIST); + if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); } static void UpdateStationSignCoord(BaseStation *st) @@ -708,11 +845,16 @@ static CommandCost BuildStationPart(Station **st, DoCommandFlag flags, bool reus static void DeleteStationIfEmpty(BaseStation *st) { if (!st->IsInUse()) { + if (Station::IsExpected(st)) Overlays::Instance()->RemoveStation((Station *)st); st->delete_ctr = 0; InvalidateWindowData(WC_STATION_LIST, st->owner, 0); } /* station remains but it probably lost some parts - station sign should stay in the station boundaries */ UpdateStationSignCoord(st); + + if (Station::IsExpected(st)) { + MarkWholeScreenDirty(); + } } CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags); @@ -1081,24 +1223,24 @@ template CommandCost FindJoiningBaseStation(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, T **st) { assert(*st == NULL); - bool check_surrounding = true; - - if (_settings_game.station.adjacent_stations) { - if (existing_station != INVALID_STATION) { - if (adjacent && existing_station != station_to_join) { - /* You can't build an adjacent station over the top of one that - * already exists. */ - return_cmd_error(error_message); - } else { - /* Extend the current station, and don't check whether it will - * be near any other stations. */ - *st = T::GetIfValid(existing_station); - check_surrounding = (*st == NULL); - } + bool check_surrounding = !adjacent || !_settings_game.station.adjacent_stations; + + if (existing_station != INVALID_STATION) { + if (station_to_join == INVALID_STATION) { + /* You can't build an adjacent station over the top of one that + * already exists. */ + if (adjacent) return_cmd_error(error_message); } else { - /* There's no station here. Don't check the tiles surrounding this - * one if the company wanted to build an adjacent station. */ - if (adjacent) check_surrounding = false; + /* You can't join to a station while building over the top of + * some other station. */ + if (existing_station != station_to_join) return_cmd_error(error_message); + } + + if (!adjacent && _settings_game.station.adjacent_stations) { + /* Extend the current station, and don't check whether it will + * be near any other stations. */ + *st = T::GetIfValid(existing_station); + check_surrounding = (*st == NULL); } } @@ -1108,8 +1250,23 @@ CommandCost FindJoiningBaseStation(StationID existing_station, StationID station if (ret.Failed()) return ret; } + /* Fail if we would have to join to other station then we want. */ + if (*st != NULL && (station_to_join == INVALID_STATION ? adjacent : (*st)->index != station_to_join)) { + *st = NULL; + return_cmd_error(STR_ERROR_ADJOINS_OTHER_STATION); + } + /* Distant join */ - if (*st == NULL && station_to_join != INVALID_STATION) *st = T::GetIfValid(station_to_join); + if (*st == NULL && station_to_join != INVALID_STATION) { + /* Test if we are not breaking the distant-join rule. */ + if (!_settings_game.station.distant_join_stations && ( + check_surrounding || // surrounding already cheked? + GetStationAround(ta, INVALID_STATION, st, station_to_join).Failed() || // search surrounding tiles + *st == NULL)) { // station not found? + return_cmd_error(STR_ERROR_CAN_T_DISTANT_JOIN); + } + *st = T::GetIfValid(station_to_join); + } return CommandCost(); } @@ -1218,10 +1375,8 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; - bool distant_join = (station_to_join != INVALID_STATION); - - if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; + if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR; /* these values are those that will be stored in train_tile and station_platforms */ @@ -1251,14 +1406,22 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 /* Check if we can allocate a custom stationspec to this station */ const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); - int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0); - if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); + int specindex; + if ((flags & DC_PASTE) && (flags & DC_EXEC) && (_station_cmd_specindex_to_paste != -1)) { + assert(_station_cmd_specindex_to_paste == 0 || (_station_cmd_specindex_to_paste < st->num_specs && st->speclist[_station_cmd_specindex_to_paste].spec == statspec)); + specindex = _station_cmd_specindex_to_paste; + } else { + specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0); + if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); + } if (statspec != NULL) { /* Perform NewStation checks */ /* Check if the station size is permitted */ - if (HasBit(statspec->disallowed_platforms, min(numtracks - 1, 7)) || HasBit(statspec->disallowed_lengths, min(plat_len - 1, 7))) { + if (!(flags & DC_PASTE) && ( + HasBit(statspec->disallowed_platforms, min(numtracks - 1, 7)) || + HasBit(statspec->disallowed_lengths, min(plat_len - 1, 7)))) { return CMD_ERROR; } @@ -1279,6 +1442,7 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 st->AddFacility(FACIL_TRAIN, new_location.tile); st->rect.BeforeAddRect(tile_org, w_org, h_org, StationRect::ADD_TRY); + st->catchment.BeforeAddRect(tile_org, w_org, h_org, CA_TRAIN); if (statspec != NULL) { /* Include this station spec's animation trigger bitmask @@ -1330,7 +1494,9 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 if (!IsStationTileBlocked(tile)) c->infrastructure.rail[rt]++; c->infrastructure.station++; - if (statspec != NULL) { + if ((flags & DC_PASTE) && (_station_cmd_gfx_to_paste != -1)) { + SetStationGfx(tile, _station_cmd_gfx_to_paste); + } else if (statspec != NULL) { /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */ uint32 platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false); @@ -1343,7 +1509,9 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 ErrorUnknownCallbackResult(statspec->grf_prop.grffile->grfid, CBID_STATION_TILE_LAYOUT, callback); } } + } + if (statspec != NULL) { /* Trigger station animation -- after building? */ TriggerStationAnimation(st, tile, SAT_BUILT); } @@ -1517,6 +1685,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector &affected Track track = GetRailStationTrack(tile); Owner owner = GetTileOwner(tile); RailType rt = GetRailType(tile); + if (Station::IsExpected(st)) ((Station *)st)->catchment.AfterRemoveTile(tile, CA_TRAIN); Train *v = NULL; if (HasStationReservation(tile)) { @@ -1530,6 +1699,7 @@ CommandCost RemoveFromRailBaseStation(TileArea ta, SmallVector &affected DoClearSquare(tile); DeleteNewGRFInspectWindow(GSF_STATIONS, tile); if (build_rail) MakeRailNormal(tile, owner, TrackToTrackBits(track), rt); + if (Station::IsExpected(st) && Overlays::Instance()->HasStation((Station *)st)) ((Station *)st)->MarkAcceptanceTilesDirty(); Company::Get(owner)->infrastructure.station--; DirtyCompanyInfrastructureWindows(owner); @@ -1596,6 +1766,7 @@ CommandCost CmdRemoveFromRailStation(TileIndex start, DoCommandFlag flags, uint3 Station *st = *stp; if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_TRAINS); + if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); st->MarkTilesDirty(false); st->RecomputeIndustriesNear(); } @@ -1762,7 +1933,6 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin StationID station_to_join = GB(p2, 16, 16); bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; - bool distant_join = (station_to_join != INVALID_STATION); uint8 width = (uint8)GB(p1, 0, 8); uint8 lenght = (uint8)GB(p1, 8, 8); @@ -1776,7 +1946,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin TileArea roadstop_area(tile, width, lenght); - if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; + if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR; @@ -1841,6 +2011,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin st->AddFacility((type) ? FACIL_TRUCK_STOP : FACIL_BUS_STOP, cur_tile); st->rect.BeforeAddTile(cur_tile, StationRect::ADD_TRY); + st->catchment.BeforeAddTile(cur_tile, type ? CA_TRUCK : CA_BUS); RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS; if (is_drive_through) { @@ -1970,6 +2141,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) DoClearSquare(tile); } + if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_ROADVEHS); delete cur_stop; @@ -1983,6 +2155,7 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) } st->rect.AfterRemoveTile(st, tile); + st->catchment.AfterRemoveTile(tile, is_truck ? CA_TRUCK : CA_BUS); st->UpdateVirtCoord(); st->RecomputeIndustriesNear(); @@ -2191,11 +2364,10 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint StationID station_to_join = GB(p2, 16, 16); bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; - bool distant_join = (station_to_join != INVALID_STATION); byte airport_type = GB(p1, 0, 8); byte layout = GB(p1, 8, 8); - if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; + if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; if (airport_type >= NUM_AIRPORTS) return CMD_ERROR; @@ -2256,9 +2428,6 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p2, 0), airport_area, &st); if (ret.Failed()) return ret; - /* Distant join */ - if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); - ret = BuildStationPart(&st, flags, reuse, airport_area, (GetAirport(airport_type)->flags & AirportFTAClass::AIRPLANES) ? STATIONNAMING_AIRPORT : STATIONNAMING_HELIPORT); if (ret.Failed()) return ret; @@ -2286,6 +2455,7 @@ CommandCost CmdBuildAirport(TileIndex tile, DoCommandFlag flags, uint32 p1, uint MakeAirport(iter, st->owner, st->index, iter.GetStationGfx(), WATER_CLASS_INVALID); SetStationTileRandomBits(iter, GB(Random(), 0, 4)); st->airport.Add(iter); + st->catchment.BeforeAddTile(iter, as->catchment); if (AirportTileSpec::Get(GetTranslatedAirportTileID(iter.GetStationGfx()))->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(iter); } @@ -2359,8 +2529,10 @@ static CommandCost RemoveAirport(TileIndex tile, DoCommandFlag flags) cost.AddCost(_price[PR_CLEAR_STATION_AIRPORT]); if (flags & DC_EXEC) { + const AirportSpec *as = st->airport.GetSpec(); if (IsHangarTile(tile_cur)) OrderBackup::Reset(tile_cur, false); DeleteAnimatedTile(tile_cur); + st->catchment.AfterRemoveTile(tile_cur, as->catchment); DoClearSquare(tile_cur); DeleteNewGRFInspectWindow(GSF_AIRPORTTILES, tile_cur); } @@ -2470,9 +2642,8 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 StationID station_to_join = GB(p2, 16, 16); bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; - bool distant_join = (station_to_join != INVALID_STATION); - if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR; + if (station_to_join != INVALID_STATION && !Station::IsValidID(station_to_join)) return CMD_ERROR; DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile)); if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); @@ -2491,21 +2662,30 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 TileIndex tile_cur = tile + TileOffsByDiagDir(direction); - if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { - return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + /* Get the water class of the water tile before it is cleared. */ + WaterClass wc; + /* When pasting a dock, there may be no water yet (a canal will be placed when DC_EXE'ing). + * Ignore that there is no water so we can calculate the cost more precisely. */ + if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { + wc = WATER_CLASS_INVALID; + } else { + if (!IsTileType(tile_cur, MP_WATER)) { + return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + } + wc = GetWaterClass(tile_cur); } + if (!IsTileFlat(tile_cur)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); if (IsBridgeAbove(tile_cur)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - /* Get the water class of the water tile before it is cleared.*/ - WaterClass wc = GetWaterClass(tile_cur); - ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; - tile_cur += TileOffsByDiagDir(direction); - if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { - return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + if (!(flags & DC_PASTE)) { + tile_cur += TileOffsByDiagDir(direction); + if (!IsTileType(tile_cur, MP_WATER) || !IsTileFlat(tile_cur)) { + return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + } } TileArea dock_area = TileArea(tile + ToTileIndexDiff(_dock_tileoffs_chkaround[direction]), @@ -2516,9 +2696,6 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 ret = FindJoiningStation(INVALID_STATION, station_to_join, HasBit(p1, 0), dock_area, &st); if (ret.Failed()) return ret; - /* Distant join */ - if (st == NULL && distant_join) st = Station::GetIfValid(station_to_join); - ret = BuildStationPart(&st, flags, reuse, dock_area, STATIONNAMING_DOCK); if (ret.Failed()) return ret; @@ -2529,6 +2706,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 st->AddFacility(FACIL_DOCK, tile); st->rect.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, StationRect::ADD_TRY); + st->catchment.BeforeAddRect(dock_area.tile, dock_area.w, dock_area.h, CA_DOCK); /* If the water part of the dock is on a canal, update infrastructure counts. * This is needed as we've unconditionally cleared that tile before. */ @@ -2538,6 +2716,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 Company::Get(st->owner)->infrastructure.station += 2; DirtyCompanyInfrastructureWindows(st->owner); + assert(wc != WATER_CLASS_INVALID); MakeDock(tile, st->owner, st->index, direction, wc); st->UpdateVirtCoord(); @@ -2573,10 +2752,13 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags) if (ret.Failed()) return ret; if (flags & DC_EXEC) { + st->catchment.AfterRemoveTile(tile1, CA_DOCK); + st->catchment.AfterRemoveTile(tile2, CA_DOCK); DoClearSquare(tile1); MarkTileDirtyByTile(tile1); MakeWaterKeepingClass(tile2, st->owner); + if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); st->rect.AfterRemoveTile(st, tile1); st->rect.AfterRemoveTile(st, tile2); @@ -2901,6 +3083,8 @@ draw_default_foundation: } } + DrawOverlay(ti, MP_STATION); + if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); if (HasBit(roadtypes, ROADTYPE_TRAM)) { @@ -3659,6 +3843,615 @@ void StationMonthlyLoop() } +/** Maps station IDs and platform layouts (custom spec indices) + * from sources to their destinations while copy-pasting stations. */ +struct StationIDPasteMap { +private: + typedef SmallVector SpecIndexMap; + + struct StationPasteID { + StationID dest_sid; ///< ID of the destination station + bool overbuilding; ///< whether the destination is being overbuilt + SpecIndexMap specindices; ///< maps custom spec indices (source -> destination) + }; + + typedef std::map PasteMap; + + PasteMap map; + +public: + /** + * Get the destination station ID assigned to a given source station ID. + * @param src_sid the source ID + * @param overbuilding (out) whether the destination station has been overbuilt (see #SetOverbuilding) + * @return the destination ID or #NEW_STATION if the destination hasn't been chosen yet. + * @see ConfirmIDForStation + */ + StationID QueryIDForStation(StationID src_sid, bool *overbuilding) const + { + assert(src_sid != INVALID_STATION); + + PasteMap::const_iterator it = this->map.find(src_sid); + if (it != this->map.end()) { + *overbuilding = it->second.overbuilding; + return it->second.dest_sid; + } else { + *overbuilding = false; + return NEW_STATION; + } + } + + /** + * Assign destination station ID to a source station ID. + * @param src_sid the source ID + * @param src_sid the destination ID (after buildng) + * @pre the choice cannot be changed, only the same args can be passed more then once + * @see QueryIDForStation + */ + void ConfirmIDForStation(StationID src_sid, StationID dst_sid) + { + assert(src_sid != INVALID_STATION && dst_sid != INVALID_STATION && dst_sid != NEW_STATION); + + PasteMap::iterator it = this->map.find(src_sid); + if (it != this->map.end()) { + assert(it->second.dest_sid == dst_sid); + } else { + StationPasteID &item = this->map[src_sid]; + item.dest_sid = src_sid; + item.dest_sid = dst_sid; + item.overbuilding = false; + } + } + + /** + * Get the destination spec index assigned to a given source spec index. + * @param src_sid ID of the source station + * @param src_specindex spec index of the source station part + * @return the destination spec index or -1 if the destination hasn't been chosen yet. + * @see ConfirmSpecIndexForStationPart + */ + int QuerySpecIndexForStationPart(StationID src_sid, byte src_specindex) const + { + assert(src_sid != INVALID_STATION); + if (src_specindex == 0) return 0; // reserved for the default station class + + PasteMap::const_iterator it = this->map.find(src_sid); + if (it != this->map.end() && src_specindex < it->second.specindices.Length()) { + return it->second.specindices[src_specindex]; + } + return -1; // allocate new specindex + } + + /** + * Assign destination spec index to a source spec index. + * @param src_sid ID of the source station + * @param src_specindex spec index of the source station part + * @param dst_specindex spec index of the destination station part (after buildng) + * @pre the choice cannot be changed, only the same args can be passed more then once + * @pre the destination ID of this station must be already set (see #ConfirmIDForStation) + * @see QuerySpecIndexForStationPart + */ + void ConfirmSpecIndexForStationPart(StationID src_sid, byte src_specindex, byte dst_specindex) + { + if (src_specindex == 0) return; + PasteMap::iterator it = this->map.find(src_sid); + assert(it != this->map.end()); + + SpecIndexMap &indices = it->second.specindices; + if (indices.Length() == 0) *indices.Append() = 0; // default station + while (src_specindex >= indices.Length()) *indices.Append() = -1; // placeholders + + assert(indices[src_specindex] == -1 || indices[src_specindex] == dst_specindex); + indices[src_specindex] = dst_specindex; + } + + /** + * Mark that a certain station overbuilt it's destination station. + * @param src_sid the source ID of the overbuilding station + * @pre the destination ID of this station must be already set (see #ConfirmIDForStation) + */ + void SetOverbuilding(StationID src_sid) + { + PasteMap::iterator it = this->map.find(src_sid); + assert(it != this->map.end()); + it->second.overbuilding = true; + } +}; + +struct StationPartPasteInfo { + GenericTileIndex src_tile; ///< source tile + TileIndex dst_tile; ///< destination tile + StationID adjoining_station; ///< the station that is being overbuilt (#MULTIPLE_STATIONS if many), the adjacent station if not overbuilding (#INVALID_STATION if none, #MULTIPLE_STATIONS if many) + bool overbuilding; ///< whether there is a station on the destination tile already +}; +typedef std::deque StationPartPasteQueue; + +static const StationID MULTIPLE_STATIONS = NEW_STATION; + +static StationIDPasteMap _copy_paste_station_id_paste_map; ///< map of station IDs and specindices currently being pasted onto the main map +static StationPartPasteQueue _copy_paste_station_part_paste_queue; ///< station parts queued to be pasted onto the main map +ClipboardStationsBuilder _clipboard_stations_builder; ///< for collecting metadata about stations (sizes, facilities, ...) while copying them to the clipboard + +static void GetSpecFromGenericStation(GenericTileIndex tile, StationClassID *stat_class, byte *stat_type) +{ + assert(HasStationTileRail(tile)); + + byte specindex = GetCustomStationSpecIndex(tile); + if (specindex == 0) { + *stat_class = IsRailWaypointTile(tile) ? STAT_CLASS_WAYP : STAT_CLASS_DFLT; + *stat_type = 0; + } else if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + const StationSpecList &statspec = BaseStation::GetByTile(t)->speclist[specindex]; + *stat_class = statspec.spec->cls_id; + int stat_type_int; + StationClass::GetByGrf(statspec.grfid, statspec.localidx, &stat_type_int); + *stat_type = (byte)stat_type_int; + } else { + const ClipboardStation::Spec &statspec = ClipboardStation::GetByTile(tile)->speclist[specindex]; + *stat_class = statspec.stat_class; + *stat_type = statspec.stat_type; + } +} + +static void GetTypeLayoutFromGenericAirport(GenericTileIndex tile, AirportTypes *type, byte *layout) +{ + if (IsMainMapTile(tile)) { + Station *st = Station::GetByTile(AsMainMapTile(tile)); + *type = (AirportTypes)st->airport.type; + *layout = st->airport.layout; + } else { + ClipboardStation *st = ClipboardStation::GetByTile(tile); + *type = st->airport.type; + *layout = st->airport.layout; + } +} + +/** + * Test a given station tile if there is any contented to be copied from it. + * + * Stations are copy/pasted part by part, where a part is a minimal station piece that we can move + * e.g. a single rail station tile or a whole airport. The function writes bounds of that piece to + * location pointed by \c station_part_area but only once per a piece - when a cartin tile is being + * tested: + * - in case of docks, it's the tile with land section + * - in other cases, it's the most norhern tile + * For the rest of tiles the function still returns \c true but writes "invalid" area. + * + * If the funtion returns \c false, \c object_rect remains unchanged. + * + * @param tile the tile to test + * @param src_area the area we are copying + * @param mode copy-paste mode + * @param station_part_area (out, may be NULL) bounds of the station part or "invalid" area, depending on which tile was given + * @param company the #Company to check ownership against to + * @param preview (out, may be NULL) information on how to higlight preview of the tile + * @return whether this tile needs to be copy-pasted + */ +bool TestStationTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *station_part_area, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) +{ + if (preview != NULL) MemSetT(preview, 0); + if (!(mode & CPM_WITH_STATIONS)) return false; + + StationType type = GetStationType(tile); + if (type != STATION_BUOY && IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; + + switch (type) { + case STATION_WAYPOINT: + case STATION_RAIL: + if (!(mode & CPM_WITH_RAIL_TRANSPORT)) return false; + if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); + if (preview != NULL) preview->highlight_track_bits = GetRailStationTrackBits(tile); + break; + + case STATION_AIRPORT: + if (!(mode & CPM_WITH_AIR_TRANSPORT)) return false; + if (IsMainMapTile(tile) || station_part_area != NULL) { + GenericTileArea area; + if (IsMainMapTile(tile)) { + area = Station::GetByTile(AsMainMapTile(tile))->airport; + if (!src_area.Contains(area)) return false; + } else { + area = GenericTileArea(ClipboardStation::GetByTile(tile)->airport, MapOf(tile)); + } + + if (station_part_area != NULL) { + if (tile != area.tile) { + *station_part_area = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); + } else { + *station_part_area = area; + } + } + } + break; + + case STATION_TRUCK: + case STATION_BUS: + if (!(mode & CPM_WITH_ROAD_TRANSPORT)) return false; + if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); + break; + + case STATION_OILRIG: + return false; + + case STATION_DOCK: { + if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; + if (IsMainMapTile(tile) || station_part_area != NULL) { + GenericTileIndex other_tile = GetOtherDockTile(tile); + if (IsMainMapTile(tile) && !src_area.Contains(other_tile)) return false; + if (station_part_area != NULL) *station_part_area = IsLandDockSection(tile) ? GenericTileArea(tile, other_tile) : GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); + } + break; + } + + case STATION_BUOY: + if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; + if (station_part_area != NULL) *station_part_area = GenericTileArea(tile, 1, 1); + break; + + default: + return false; + } + + if (preview != NULL) preview->highlight_tile_rect = true; + return true; +} + +static void TransformRailStation(StationGfx *gfx, DirTransformation transformation, StationClassID *stat_class, byte *stat_type) +{ + if (transformation == DTR_IDENTITY) return; + + if (*stat_class != STAT_CLASS_DFLT && *stat_class != STAT_CLASS_WAYP && + IsCustomLayoutStation(StationClass::Get(*stat_class)->GetSpec(*stat_type))) { + /* convert to a default station if we dont know how to transform station graphics */ + *stat_class = STAT_CLASS_DFLT; + *stat_type = 0; + *gfx = TransformAxis((Axis)(*gfx & 1), transformation); + return; + } + + if (*stat_class == STAT_CLASS_WAYP || *gfx < 4) { + /* change axis */ + *gfx ^= TransformAxis(AXIS_X, transformation); + } else { + /* change direction */ + DiagDirection dir = (DiagDirection)((*gfx - 1) & 0x3); // down the roof + dir = TransformDiagDir(dir, transformation); + *gfx = ((dir + 1) & 0x3) + 4; + } +} + +static bool IsAirportTransformable(AirportTypes type, DirTransformation dtr) +{ + if (type >= NEW_AIRPORT_OFFSET) return dtr == DTR_IDENTITY; + if (TransformAxis(AXIS_X, dtr) == AXIS_X) return true; + const AirportSpec *as = AirportSpec::Get(type); + return as->size_x == as->size_y; +} + +static const StringID _paste_station_errors[] = { + STR_ERROR_CAN_T_BUILD_RAILROAD_STATION, // STATION_RAIL + STR_ERROR_CAN_T_BUILD_AIRPORT_HERE, // STATION_AIRPORT + INVALID_STRING_ID, // STATION_TRUCK + INVALID_STRING_ID, // STATION_BUS + INVALID_STRING_ID, // STATION_OILRIG + STR_ERROR_CAN_T_BUILD_DOCK_HERE, // STATION_DOCK + STR_ERROR_CAN_T_POSITION_BUOY_HERE, // STATION_BUOY + STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT, // STATION_WAYPOINT +}; + +static const StringID _paste_truck_bus_station_errors[][ROADTYPE_END] = { + /* ROADTYPE_ROAD ROADTYPE_TRAM */ + { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION}, // ROADSTOP_BUS + { STR_ERROR_CAN_T_BUILD_TRUCK_STATION, STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION }, // ROADSTOP_TRUCK +}; + +static StringID GetPasteStationErrorMsg(GenericTileIndex src_tile) +{ + StationType type = GetStationType(src_tile); + if (type != STATION_TRUCK && type != STATION_BUS) { + assert((size_t)type < lengthof(_paste_station_errors)); + return _paste_station_errors[type]; + } else { + RoadStopType rst = (RoadStopType)(STATION_BUS - type); + RoadType rt = (RoadType)FindLastBit(GetRoadTypes(src_tile)); + assert((size_t)rst < lengthof(_paste_truck_bus_station_errors)); + assert((size_t)rt < lengthof(_paste_truck_bus_station_errors[0])); + return _paste_truck_bus_station_errors[rst][rt]; + } +} + +static void CopyPastePlaceRailStation(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent) +{ + if (IsMainMapTile(tile)) { + _station_cmd_gfx_to_paste = gfx; + _station_cmd_specindex_to_paste = specindex; + uint32 p1 = 0; + SB(p1, 0, 4, rt); + SB(p1, 4, 1, axis); + SB(p1, 8, 8, 1); // number of tracks + SB(p1, 16, 8, 1); // platform length + SB(p1, 24, 1, adjacent); + uint32 p2 = 0; + SB(p2, 0, 8, stat_class); + SB(p2, 8, 8, stat_type); + SB(p2, 16, 16, sid); + _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION)); + } else { + MakeRailStation(tile, OWNER_NONE, sid, axis, gfx & ~1, rt); + assert(IsInsideMM(specindex, 0, MAX_UVALUE(byte) + 1)); + SetCustomStationSpecIndex(tile, (byte)specindex); + _clipboard_stations_builder.AddRailPart(sid, stat_class, stat_type, (byte)specindex); + } +} + +static void CopyPastePlaceAirport(GenericTileIndex tile, StationID sid, AirportTypes type, byte layout, bool adjacent) +{ + if (IsMainMapTile(tile)) { + uint32 p1 = 0; + SB(p1, 0, 8, type); + SB(p1, 8, 8, layout); + uint32 p2 = 0; + SB(p2, 0, 1, adjacent); + SB(p2, 16, 16, sid); + _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE)); + } else { + for (AirportTileTableIteratorT iter(AirportSpec::Get(type)->table[layout], tile); IsValidTileIndex(iter); ++iter) { + MakeAirport(iter, OWNER_NONE, sid, 0, WATER_CLASS_INVALID); + } + _clipboard_stations_builder.AddAirportPart(sid, IndexOf(tile), type, layout); + } +} + +static void CopyPastePlaceRoadStop(GenericTileIndex tile, StationID sid, bool drive_through, RoadStopType rst, RoadTypes rt, DiagDirection dir, bool adjacent) +{ + if (drive_through) dir = (DiagDirection)DiagDirToAxis(dir); + + if (IsMainMapTile(tile)) { + uint32 p1 = 0; + SB(p1, 0 , 8, 1); // width + SB(p1, 8 , 8, 1); // height + uint32 p2 = 0; + SB(p2, 0 , 1, rst); + SB(p2, 1 , 1, drive_through); + SB(p2, 2 , 2, rt); + SB(p2, 5 , 1, adjacent); // + SB(p2, 6 , 2, dir); + SB(p2, 16 , 16, sid); + _current_pasting->DoCommand(AsMainMapTile(tile), p1, p2, CMD_BUILD_ROAD_STOP | CMD_MSG(STR_ERROR_CAN_T_BUILD_BUS_STATION + rst)); + } else { + if (drive_through) { + MakeDriveThroughRoadStop(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, sid, rst, rt, DiagDirToAxis(dir)); + } else { + MakeRoadStop(tile, OWNER_NONE, sid, rst, rt, dir); + } + _clipboard_stations_builder.AddPart(sid); + } +} + +static void CopyPastePlaceDock(GenericTileIndex tile, StationID sid, DiagDirection dir, WaterClass wc, bool adjacent) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + TileIndex t_lower = TileAddByDiagDir(t, dir); + if (!HasTileWaterGround(t_lower)) { + CopyPastePlaceCannal(GenericTileIndex(t_lower)); + if (_current_pasting->last_result.Failed()) return; + } + + uint32 p1 = 0; + SB(p1, 0, 1, adjacent); + uint32 p2 = 0; + SB(p2, 16 , 16, sid); + _current_pasting->DoCommand(t, p1, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE)); + } else { + MakeDock(tile, OWNER_NONE, sid, dir, wc); + _clipboard_stations_builder.AddPart(sid); + } +} + +static void CopyPasteStation(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste, StationID dst_sid, int dst_specindex, bool adjacent = false) +{ + StationType station_type = GetStationType(src_tile); + switch (station_type) { + case STATION_RAIL: + case STATION_WAYPOINT: { + StationGfx gfx = GetStationGfx(src_tile); + Axis axis = TransformAxis(GetRailStationAxis(src_tile), copy_paste.transformation); + RailType railtype = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); + StationClassID stat_class; + byte stat_type; + GetSpecFromGenericStation(src_tile, &stat_class, &stat_type); + + TransformRailStation(&gfx, copy_paste.transformation, &stat_class, &stat_type); + + switch (station_type) { + case STATION_RAIL: CopyPastePlaceRailStation(dst_tile, dst_sid, axis, railtype, gfx, stat_class, stat_type, dst_specindex, adjacent); break; + case STATION_WAYPOINT: CopyPastePlaceRailWaypoint(dst_tile, dst_sid, axis, railtype, gfx, stat_class, stat_type, dst_specindex, adjacent); break; + default: NOT_REACHED(); + } + + break; + } + + case STATION_AIRPORT: { + AirportTypes type; + byte layout; + GetTypeLayoutFromGenericAirport(src_tile, &type, &layout); + if (!IsAirportTransformable(type, copy_paste.transformation)) { + assert(IsMainMapTile(dst_tile)); // copying should be always successful + _current_pasting->CollectError(AsMainMapTile(dst_tile), STR_ERROR_INAPPLICABLE_TRANSFORMATION, STR_ERROR_CAN_T_BUILD_AIRPORT_HERE); + return; + } + CopyPastePlaceAirport(dst_tile, dst_sid, type, layout, adjacent); + break; + } + + case STATION_TRUCK: + case STATION_BUS: + CopyPastePlaceRoadStop(dst_tile, dst_sid, IsDriveThroughStopTile(src_tile), GetRoadStopType(src_tile), + GetRoadTypes(src_tile), TransformDiagDir(GetRoadStopDir(src_tile), copy_paste.transformation), adjacent); + break; + + case STATION_DOCK: CopyPastePlaceDock(dst_tile, dst_sid, TransformDiagDir(GetDockDirection(src_tile), copy_paste.transformation), GetWaterClass(src_tile), adjacent); break; + case STATION_BUOY: CopyPastePlaceBuoy(dst_tile, dst_sid, GetWaterClass(src_tile)); break; + + default: + NOT_REACHED(); + } +} + +void CopyPasteTile_Station(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) +{ + GenericTileArea part_src_rect; + if (!TestStationTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &part_src_rect)) return; + if (part_src_rect.tile.index == INVALID_TILE_INDEX) return; // copy this part only once + + if (!IsMainMapTile(dst_tile)) { + /* When copying to the clipboard, keep original station ID's and specindices. */ + int specindex = HasStationTileRail(src_tile) ? GetCustomStationSpecIndex(src_tile) : -1; + CopyPasteStation(src_tile, dst_tile, copy_paste, GetStationIndex(src_tile), specindex); + } else { + /* Calculate the destination area for current station part. */ + TileIndex t = copy_paste.src_area.ReverseTransformTile(src_tile, AsMainMapTile(dst_tile), copy_paste.transformation); // transformed northern tile of the copy_paste.src_area + t = copy_paste.src_area.TransformTile(part_src_rect.tile, t, copy_paste.transformation); // transformed northern tile of the part_src_rect + t = part_src_rect.ReverseTransformedNorth(t, copy_paste.transformation); // northern tile of the transformed part_src_rect + TileArea part_dst_rect = TransformTileArea(part_src_rect, t, copy_paste.transformation); // transformed part_src_rect + + /* Terraform tiles */ + if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { + CopyPasteHeights(part_src_rect, GenericTileIndex(part_dst_rect.tile), copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + } + + StationType station_type = GetStationType(src_tile); + if ((station_type == STATION_BUOY) || !(_current_pasting->dc_flags & DC_EXEC)) { + /* Paste the part */ + CopyPasteStation(src_tile, dst_tile, copy_paste, NEW_STATION, -1, false); + } else { + /* Queue for later pasting. We must find station candidates to to be joined to before + * we try to build any station parts to avoid joining pasted stations together. */ + StationPartPasteInfo info = { + src_tile, + AsMainMapTile(dst_tile), + INVALID_STATION, + false + }; + /* Station parts that overbuild other stations go to the front of the queue, + * they will be tried firstly. */ + BaseStation *st = NULL; + CommandCost ret = (station_type != STATION_WAYPOINT) ? + GetStationAround(part_dst_rect, INVALID_STATION, (Station**)&st, 0) : + GetStationAround(part_dst_rect, INVALID_STATION, (Waypoint**)&st, 0); + if (ret.Failed() || st != NULL) { + info.adjoining_station = ret.Failed() ? MULTIPLE_STATIONS : st->index; + info.overbuilding = true; + _copy_paste_station_part_paste_queue.push_front(info); + } else { + /* Joining parts go behind overbuilding parts. They are next to try. */ + ret = (station_type != STATION_WAYPOINT) ? + GetStationAround(part_dst_rect, INVALID_STATION, (Station**)&st, 1) : + GetStationAround(part_dst_rect, INVALID_STATION, (Waypoint**)&st, 1); + if (ret.Failed() || st != NULL) { + info.adjoining_station = ret.Failed() ? MULTIPLE_STATIONS : st->index; + StationPartPasteQueue::iterator pos = _copy_paste_station_part_paste_queue.begin(); + while (pos < _copy_paste_station_part_paste_queue.end() && pos->overbuilding) pos++; + _copy_paste_station_part_paste_queue.insert(pos, info); + } else { + /* Non-joining parts go to the back. */ + _copy_paste_station_part_paste_queue.push_back(info); + } + } + } + } +} + +static void ProcessStationPartPasteQueue(const CopyPasteParams ©_paste) +{ + if (_copy_paste_station_part_paste_queue.empty()) return; + + for (;;) { + uint orig_queue_size = _copy_paste_station_part_paste_queue.size(); + for (uint i = 0; i < orig_queue_size; i++) { + if (IsPastingInterrupted()) break; + StationPartPasteInfo part = _copy_paste_station_part_paste_queue.front(); + _copy_paste_station_part_paste_queue.pop_front(); + + bool overbuilding = false; // did current station overbuild some other station already? + StationID src_sid = GetStationIndex(part.src_tile); + StationID dst_sid = _copy_paste_station_id_paste_map.QueryIDForStation(src_sid, &overbuilding); + + int src_specindex = -1; + int dst_specindex = -1; + if (HasStationTileRail(part.src_tile)) { + src_specindex = GetCustomStationSpecIndex(part.src_tile); + dst_specindex = _copy_paste_station_id_paste_map.QuerySpecIndexForStationPart(src_sid, src_specindex); + } + + if (part.overbuilding) { // would this stations part overbuild other stations? + if ((part.adjoining_station == MULTIPLE_STATIONS) || (overbuilding && dst_sid != part.adjoining_station)) { + /* Can't paste this part because current station would overbuild multiple different stations. */ + _current_pasting->CollectError(part.dst_tile, STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING, GetPasteStationErrorMsg(part.src_tile)); + } else { + /* Try to overbuild a station. */ + CopyPasteStation(part.src_tile, GenericTileIndex(part.dst_tile), copy_paste, dst_sid, dst_specindex, false); + } + } else { + if (!overbuilding && (part.adjoining_station == MULTIPLE_STATIONS || ( + dst_sid != NEW_STATION && part.adjoining_station != INVALID_STATION && dst_sid != part.adjoining_station))) { + /* can't paste this part because current station would have to join multiple different stations */ + _current_pasting->CollectError(part.dst_tile, STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING, GetPasteStationErrorMsg(part.src_tile)); + } else { + if (dst_sid == NEW_STATION && part.adjoining_station != INVALID_STATION) { // is the destination station not yet determined? + dst_sid = part.adjoining_station; // try to join a new station + } + CopyPasteStation(part.src_tile, GenericTileIndex(part.dst_tile), copy_paste, dst_sid, dst_specindex, true); + } + } + + if (_current_pasting->dc_flags & DC_EXEC) { + if (_current_pasting->last_result.Succeeded()) { + /* Confirm that station will be using certain ID and specindex. */ + _copy_paste_station_id_paste_map.ConfirmIDForStation(src_sid, GetStationIndex(part.dst_tile)); + if (src_specindex != -1) _copy_paste_station_id_paste_map.ConfirmSpecIndexForStationPart(src_sid, src_specindex, GetCustomStationSpecIndex(part.dst_tile)); + if (part.overbuilding) _copy_paste_station_id_paste_map.SetOverbuilding(src_sid); + } else if (_current_pasting->last_result.GetErrorMessage() == STR_ERROR_CAN_T_DISTANT_JOIN) { + /* If we can't distant-join now then perhaps we will be able to do it later, after other parts. */ + if (_current_pasting->err_message == STR_ERROR_CAN_T_DISTANT_JOIN) { + /* discard the "can't distatnt-join" error */ + _current_pasting->err_tile = INVALID_TILE; + _current_pasting->err_message = STR_ERROR_NOTHING_TO_DO; + } + StationPartPasteInfo info = { part.src_tile, part.dst_tile, INVALID_STATION, false }; + _copy_paste_station_part_paste_queue.push_back(info); + } + } + } + if (IsPastingInterrupted()) break; + if (orig_queue_size == _copy_paste_station_part_paste_queue.size()) break; // don't retry if the queue didn't shrink + } + + /* set the "can't distatnt-join" error if not all retries were successfull */ + if (!IsPastingInterrupted() && !_copy_paste_station_part_paste_queue.empty()) { + _current_pasting->CollectError(_copy_paste_station_part_paste_queue.back().dst_tile, STR_ERROR_CAN_T_DISTANT_JOIN, GetPasteStationErrorMsg(_copy_paste_station_part_paste_queue.back().src_tile)); + } + + _copy_paste_station_part_paste_queue.clear(); +} + +void AfterPastingStations(const CopyPasteParams ©_paste) +{ + ProcessStationPartPasteQueue(copy_paste); + + _copy_paste_station_id_paste_map = StationIDPasteMap(); // clear +} + +void AfterCopyingStations(const CopyPasteParams ©_paste) +{ + _clipboard_stations_builder.BuildDone(MapOf(copy_paste.dst_area.tile)); +} + + void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius) { Station *st; @@ -3921,6 +4714,7 @@ void BuildOilRig(TileIndex tile) st->build_date = _date; st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE); + st->catchment.BeforeAddTile(tile, st->GetCatchmentRadius()); st->UpdateVirtCoord(); UpdateStationAcceptance(st, false); @@ -3931,6 +4725,7 @@ void DeleteOilRig(TileIndex tile) { Station *st = Station::GetByTile(tile); + st->catchment.AfterRemoveTile(tile, st->GetCatchmentRadius()); MakeWaterKeepingClass(tile, OWNER_NONE); st->dock_tile = INVALID_TILE; @@ -3938,6 +4733,7 @@ void DeleteOilRig(TileIndex tile) st->facilities &= ~(FACIL_AIRPORT | FACIL_DOCK); st->airport.flags = 0; + if (Overlays::Instance()->HasStation(st)) st->MarkAcceptanceTilesDirty(); st->rect.AfterRemoveTile(st, tile); st->UpdateVirtCoord(); @@ -4536,4 +5332,5 @@ extern const TileTypeProcs _tile_type_station_procs = { VehicleEnter_Station, // vehicle_enter_tile_proc GetFoundation_Station, // get_foundation_proc TerraformTile_Station, // terraform_tile_proc + CopyPasteTile_Station, // copypaste_tile_proc }; diff --git a/src/station_func.h b/src/station_func.h index f33dbd21f..fedd91f8a 100644 --- a/src/station_func.h +++ b/src/station_func.h @@ -30,6 +30,9 @@ void UpdateAllStationVirtCoords(); CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad); CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, uint32 *always_accepted = NULL); +CargoArray GetProductionRateAroundTiles(TileIndex tile, int w, int h, int rad); +CargoArray GetAcceptanceRateAroundTiles(TileIndex tile, int w, int h, int rad); + void UpdateStationAcceptance(Station *st, bool show_msg); const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx); diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 7399fe006..5ca6773c6 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -30,6 +30,8 @@ #include "sortlist_type.h" #include "core/geometry_func.hpp" #include "vehiclelist.h" +#include "core/math_func.hpp" +#include "overlay_cmd.h" #include "town.h" #include "linkgraph/linkgraph.h" #include "zoom_func.h" @@ -37,6 +39,7 @@ #include "widgets/station_widget.h" #include "table/strings.h" +#include "table/control_codes.h" #include #include @@ -59,10 +62,13 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp uint32 cargo_mask = 0; if (_thd.drawstyle == HT_RECT && tile < MapSize()) { CargoArray cargoes; + CargoArray rates; if (supplies) { cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); + rates = GetProductionRateAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); } else { cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); + rates = GetAcceptanceRateAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad); } /* Convert cargo counts to a set of cargo bits, and draw the result. */ @@ -74,9 +80,18 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp default: NOT_REACHED(); } if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i); + + if (i == CT_PASSENGERS) { + SetDParam(2, rates[i]); + } else if (i == CT_MAIL) { + SetDParam(3, rates[i]); + } } } SetDParam(0, cargo_mask); + + /* SCC_CARGO_LIST works as a magic number to let FormatString() know it's being called from here. */ + SetDParam(1, SCC_CARGO_LIST); return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO); } @@ -777,6 +792,8 @@ static const NWidgetPart _nested_station_view_widgets[] = { NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_ACCEPT_RATING_LIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_COVERAGE), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1), + SetDataTip(STR_BUTTON_COVERAGE, STR_STATION_VIEW_COVERAGE_TIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_LOCATION), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1), @@ -1311,6 +1328,9 @@ struct StationViewWindow : public Window { ~StationViewWindow() { + Overlays::Instance()->RemoveStation(Station::Get(this->window_number)); + MarkWholeScreenDirty(); + Owner owner = Station::Get(this->window_number)->owner; DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, this->owner, this->window_number).Pack(), false); DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, this->owner, this->window_number).Pack(), false); DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, this->owner, this->window_number).Pack(), false); @@ -1404,6 +1424,9 @@ struct StationViewWindow : public Window { this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company || st->owner == OWNER_NONE); // Also consider SE, where _local_company == OWNER_NONE this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, (st->facilities & FACIL_AIRPORT) && (st->airport.flags & AIRPORT_CLOSED_block) != 0); + /* check lowered stated for some buttons */ + this->SetWidgetLoweredState(WID_SV_COVERAGE, Overlays::Instance()->HasStation(st)); + this->DrawWidgets(); if (!this->IsShaded()) { @@ -1893,6 +1916,11 @@ struct StationViewWindow : public Window { } break; + case WID_SV_COVERAGE: + Overlays::Instance()->ToggleStation(Station::Get(this->window_number)); + MarkWholeScreenDirty(); + break; + case WID_SV_ACCEPTS_RATINGS: { /* Swap between 'accepts' and 'ratings' view. */ int height_change; @@ -2075,6 +2103,8 @@ struct StationViewWindow : public Window { } } } +protected: + void Get(WindowNumber window_number); }; const StringID StationViewWindow::_sort_names[] = { @@ -2109,7 +2139,12 @@ static WindowDesc _station_view_desc( */ void ShowStationViewWindow(StationID station) { - AllocateWindowDescFront(&_station_view_desc, station); + if (_ctrl_pressed) { + Overlays::Instance()->ToggleStation(Station::Get(station)); + MarkWholeScreenDirty(); + } else { + AllocateWindowDescFront(&_station_view_desc, station); + } } /** Struct containing TileIndex and StationID */ diff --git a/src/station_map.h b/src/station_map.h index 7ca9bd720..9dbc7bf49 100644 --- a/src/station_map.h +++ b/src/station_map.h @@ -26,11 +26,16 @@ typedef byte StationGfx; ///< Index of station graphics. @see _station_display_d * @pre IsTileType(t, MP_STATION) * @return Station ID of the station at \a t */ -static inline StationID GetStationIndex(TileIndex t) +template +static inline StationID GetStationIndex(typename TileIndexT::T t) { assert(IsTileType(t, MP_STATION)); - return (StationID)_m[t].m2; + return (StationID)GetTile(t)->m2; } +/** @copydoc GetStationIndex(TileIndexT::T) */ +static inline StationID GetStationIndex(TileIndex t) { return GetStationIndex(t); } +/** @copydoc GetStationIndex(TileIndexT::T) */ +static inline StationID GetStationIndex(GenericTileIndex t) { return GetStationIndex(t); } static const int GFX_DOCK_BASE_WATER_PART = 4; ///< The offset for the water parts. @@ -42,11 +47,16 @@ static const int GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET = 4; ///< The offset for the * @pre IsTileType(t, MP_STATION) * @return the station type */ -static inline StationType GetStationType(TileIndex t) +template +static inline StationType GetStationType(typename TileIndexT::T t) { assert(IsTileType(t, MP_STATION)); - return (StationType)GB(_me[t].m6, 3, 3); + return (StationType)GB(GetTileEx(t)->m6, 3, 3); } +/** @copydoc GetStationType(TileIndexT::T) */ +static inline StationType GetStationType(TileIndex t) { return GetStationType(t); } +/** @copydoc GetStationType(TileIndexT::T) */ +static inline StationType GetStationType(GenericTileIndex t) { return GetStationType(t); } /** * Get the road stop type of this tile @@ -54,11 +64,16 @@ static inline StationType GetStationType(TileIndex t) * @pre GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS * @return the road stop type */ -static inline RoadStopType GetRoadStopType(TileIndex t) +template +static inline RoadStopType GetRoadStopType(typename TileIndexT::T t) { assert(GetStationType(t) == STATION_TRUCK || GetStationType(t) == STATION_BUS); return GetStationType(t) == STATION_TRUCK ? ROADSTOP_TRUCK : ROADSTOP_BUS; } +/** @copydoc GetRoadStopType(TileIndexT::T) */ +static inline RoadStopType GetRoadStopType(TileIndex t) { return GetRoadStopType(t); } +/** @copydoc GetRoadStopType(TileIndexT::T) */ +static inline RoadStopType GetRoadStopType(GenericTileIndex t) { return GetRoadStopType(t); } /** * Get the station graphics of this tile @@ -66,11 +81,16 @@ static inline RoadStopType GetRoadStopType(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return the station graphics */ -static inline StationGfx GetStationGfx(TileIndex t) +template +static inline StationGfx GetStationGfx(typename TileIndexT::T t) { assert(IsTileType(t, MP_STATION)); - return _m[t].m5; + return GetTile(t)->m5; } +/** @copydoc GetStationGfx(TileIndexT::T) */ +static inline StationGfx GetStationGfx(TileIndex t) { return GetStationGfx(t); } +/** @copydoc GetStationGfx(TileIndexT::T) */ +static inline StationGfx GetStationGfx(GenericTileIndex t) { return GetStationGfx(t); } /** * Set the station graphics of this tile @@ -78,11 +98,16 @@ static inline StationGfx GetStationGfx(TileIndex t) * @param gfx the new graphics * @pre IsTileType(t, MP_STATION) */ -static inline void SetStationGfx(TileIndex t, StationGfx gfx) +template +static inline void SetStationGfx(typename TileIndexT::T t, StationGfx gfx) { assert(IsTileType(t, MP_STATION)); - _m[t].m5 = gfx; + GetTile(t)->m5 = gfx; } +/** @copydoc SetStationGfx(TileIndexT::T,StationGfx) */ +static inline void SetStationGfx(TileIndex t, StationGfx gfx) { SetStationGfx(t, gfx); } +/** @copydoc SetStationGfx(TileIndexT::T,StationGfx) */ +static inline void SetStationGfx(GenericTileIndex t, StationGfx gfx) { SetStationGfx(t, gfx); } /** * Is this station tile a rail station? @@ -90,20 +115,30 @@ static inline void SetStationGfx(TileIndex t, StationGfx gfx) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is a rail station */ -static inline bool IsRailStation(TileIndex t) +template +static inline bool IsRailStation(typename TileIndexT::T t) { return GetStationType(t) == STATION_RAIL; } +/** @copydoc IsRailStation(TileIndexT::T) */ +static inline bool IsRailStation(TileIndex t) { return IsRailStation(t); } +/** @copydoc IsRailStation(TileIndexT::T) */ +static inline bool IsRailStation(GenericTileIndex t) { return IsRailStation(t); } /** * Is this tile a station tile and a rail station? * @param t the tile to get the information from * @return true if and only if the tile is a rail station */ -static inline bool IsRailStationTile(TileIndex t) +template +static inline bool IsRailStationTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && IsRailStation(t); } +/** @copydoc IsRailStationTile(TileIndexT::T) */ +static inline bool IsRailStationTile(TileIndex t) { return IsRailStationTile(t); } +/** @copydoc IsRailStationTile(TileIndexT::T) */ +static inline bool IsRailStationTile(GenericTileIndex t) { return IsRailStationTile(t); } /** * Is this station tile a rail waypoint? @@ -111,20 +146,30 @@ static inline bool IsRailStationTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is a rail waypoint */ -static inline bool IsRailWaypoint(TileIndex t) +template +static inline bool IsRailWaypoint(typename TileIndexT::T t) { return GetStationType(t) == STATION_WAYPOINT; } +/** @copydoc IsRailWaypoint(TileIndexT::T) */ +static inline bool IsRailWaypoint(TileIndex t) { return IsRailWaypoint(t); } +/** @copydoc IsRailWaypoint(TileIndexT::T) */ +static inline bool IsRailWaypoint(GenericTileIndex t) { return IsRailWaypoint(t); } /** * Is this tile a station tile and a rail waypoint? * @param t the tile to get the information from * @return true if and only if the tile is a rail waypoint */ -static inline bool IsRailWaypointTile(TileIndex t) +template +static inline bool IsRailWaypointTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && IsRailWaypoint(t); } +/** @copydoc IsRailWaypointTile(TileIndexT::T) */ +static inline bool IsRailWaypointTile(TileIndex t) { return IsRailWaypointTile(t); } +/** @copydoc IsRailWaypointTile(TileIndexT::T) */ +static inline bool IsRailWaypointTile(GenericTileIndex t) { return IsRailWaypointTile(t); } /** * Has this station tile a rail? In other words, is this station @@ -133,10 +178,15 @@ static inline bool IsRailWaypointTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile has rail */ -static inline bool HasStationRail(TileIndex t) +template +static inline bool HasStationRail(typename TileIndexT::T t) { return IsRailStation(t) || IsRailWaypoint(t); } +/** @copydoc HasStationRail(TileIndexT::T) */ +static inline bool HasStationRail(TileIndex t) { return HasStationRail(t); } +/** @copydoc HasStationRail(TileIndexT::T) */ +static inline bool HasStationRail(GenericTileIndex t) { return HasStationRail(t); } /** * Has this station tile a rail? In other words, is this station @@ -144,10 +194,15 @@ static inline bool HasStationRail(TileIndex t) * @param t the tile to check * @return true if and only if the tile is a station tile and has rail */ -static inline bool HasStationTileRail(TileIndex t) +template +static inline bool HasStationTileRail(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && HasStationRail(t); } +/** @copydoc HasStationTileRail(TileIndexT::T) */ +static inline bool HasStationTileRail(TileIndex t) { return HasStationTileRail(t); } +/** @copydoc HasStationTileRail(TileIndexT::T) */ +static inline bool HasStationTileRail(GenericTileIndex t) { return HasStationTileRail(t); } /** * Is this station tile an airport? @@ -155,20 +210,30 @@ static inline bool HasStationTileRail(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return true if and only if the tile is an airport */ -static inline bool IsAirport(TileIndex t) +template +static inline bool IsAirport(typename TileIndexT::T t) { return GetStationType(t) == STATION_AIRPORT; } +/** @copydoc IsAirport(TileIndexT::T) */ +static inline bool IsAirport(TileIndex t) { return IsAirport(t); } +/** @copydoc IsAirport(TileIndexT::T) */ +static inline bool IsAirport(GenericTileIndex t) { return IsAirport(t); } /** * Is this tile a station tile and an airport tile? * @param t the tile to get the information from * @return true if and only if the tile is an airport */ -static inline bool IsAirportTile(TileIndex t) +template +static inline bool IsAirportTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && IsAirport(t); } +/** @copydoc IsAirportTile(TileIndexT::T) */ +static inline bool IsAirportTile(TileIndex t) { return IsAirportTile(t); } +/** @copydoc IsAirportTile(TileIndexT::T) */ +static inline bool IsAirportTile(GenericTileIndex t) { return IsAirportTile(t); } bool IsHangar(TileIndex t); @@ -178,10 +243,15 @@ bool IsHangar(TileIndex t); * @pre IsTileType(t, MP_STATION) * @return \c true if station is a truck stop, \c false otherwise */ -static inline bool IsTruckStop(TileIndex t) +template +static inline bool IsTruckStop(typename TileIndexT::T t) { return GetStationType(t) == STATION_TRUCK; } +/** @copydoc IsTruckStop(TileIndexT::T) */ +static inline bool IsTruckStop(TileIndex t) { return IsTruckStop(t); } +/** @copydoc IsTruckStop(TileIndexT::T) */ +static inline bool IsTruckStop(GenericTileIndex t) { return IsTruckStop(t); } /** * Is the station at \a t a bus stop? @@ -189,10 +259,15 @@ static inline bool IsTruckStop(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if station is a bus stop, \c false otherwise */ -static inline bool IsBusStop(TileIndex t) +template +static inline bool IsBusStop(typename TileIndexT::T t) { return GetStationType(t) == STATION_BUS; } +/** @copydoc IsBusStop(TileIndexT::T) */ +static inline bool IsBusStop(TileIndex t) { return IsBusStop(t); } +/** @copydoc IsBusStop(TileIndexT::T) */ +static inline bool IsBusStop(GenericTileIndex t) { return IsBusStop(t); } /** * Is the station at \a t a road station? @@ -200,41 +275,61 @@ static inline bool IsBusStop(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if station at the tile is a bus top or a truck stop, \c false otherwise */ -static inline bool IsRoadStop(TileIndex t) +template +static inline bool IsRoadStop(typename TileIndexT::T t) { assert(IsTileType(t, MP_STATION)); return IsTruckStop(t) || IsBusStop(t); } +/** @copydoc IsRoadStop(TileIndexT::T) */ +static inline bool IsRoadStop(TileIndex t) { return IsRoadStop(t); } +/** @copydoc IsRoadStop(TileIndexT::T) */ +static inline bool IsRoadStop(GenericTileIndex t) { return IsRoadStop(t); } /** * Is tile \a t a road stop station? * @param t Tile to check * @return \c true if the tile is a station tile and a road stop */ -static inline bool IsRoadStopTile(TileIndex t) +template +static inline bool IsRoadStopTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && IsRoadStop(t); } +/** @copydoc IsRoadStopTile(TileIndexT::T) */ +static inline bool IsRoadStopTile(TileIndex t) { return IsRoadStopTile(t); } +/** @copydoc IsRoadStopTile(TileIndexT::T) */ +static inline bool IsRoadStopTile(GenericTileIndex t) { return IsRoadStopTile(t); } /** * Is tile \a t a standard (non-drive through) road stop station? * @param t Tile to check * @return \c true if the tile is a station tile and a standard road stop */ -static inline bool IsStandardRoadStopTile(TileIndex t) +template +static inline bool IsStandardRoadStopTile(typename TileIndexT::T t) { return IsRoadStopTile(t) && GetStationGfx(t) < GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; } +/** @copydoc IsStandardRoadStopTile(TileIndexT::T) */ +static inline bool IsStandardRoadStopTile(TileIndex t) { return IsStandardRoadStopTile(t); } +/** @copydoc IsStandardRoadStopTile(TileIndexT::T) */ +static inline bool IsStandardRoadStopTile(GenericTileIndex t) { return IsStandardRoadStopTile(t); } /** * Is tile \a t a drive through road stop station? * @param t Tile to check * @return \c true if the tile is a station tile and a drive through road stop */ -static inline bool IsDriveThroughStopTile(TileIndex t) +template +static inline bool IsDriveThroughStopTile(typename TileIndexT::T t) { return IsRoadStopTile(t) && GetStationGfx(t) >= GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET; } +/** @copydoc IsDriveThroughStopTile(TileIndexT::T) */ +static inline bool IsDriveThroughStopTile(TileIndex t) { return IsDriveThroughStopTile(t); } +/** @copydoc IsDriveThroughStopTile(TileIndexT::T) */ +static inline bool IsDriveThroughStopTile(GenericTileIndex t) { return IsDriveThroughStopTile(t); } /** * Get the station graphics of this airport tile @@ -255,7 +350,8 @@ static inline StationGfx GetAirportGfx(TileIndex t) * @pre IsRoadStopTile(t) * @return the direction of the entrance */ -static inline DiagDirection GetRoadStopDir(TileIndex t) +template +static inline DiagDirection GetRoadStopDir(typename TileIndexT::T t) { StationGfx gfx = GetStationGfx(t); assert(IsRoadStopTile(t)); @@ -265,6 +361,10 @@ static inline DiagDirection GetRoadStopDir(TileIndex t) return (DiagDirection)(gfx - GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET); } } +/** @copydoc GetRoadStopDir(TileIndexT::T) */ +static inline DiagDirection GetRoadStopDir(TileIndex t) { return GetRoadStopDir(t); } +/** @copydoc GetRoadStopDir(TileIndexT::T) */ +static inline DiagDirection GetRoadStopDir(GenericTileIndex t) { return GetRoadStopDir(t); } /** * Is tile \a t part of an oilrig? @@ -283,20 +383,30 @@ static inline bool IsOilRig(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if the tile is a dock */ -static inline bool IsDock(TileIndex t) +template +static inline bool IsDock(typename TileIndexT::T t) { return GetStationType(t) == STATION_DOCK; } +/** @copydoc IsDock(TileIndexT::T) */ +static inline bool IsDock(TileIndex t) { return IsDock(t); } +/** @copydoc IsDock(TileIndexT::T) */ +static inline bool IsDock(GenericTileIndex t) { return IsDock(t); } /** * Is tile \a t a dock tile? * @param t Tile to check * @return \c true if the tile is a dock */ -static inline bool IsDockTile(TileIndex t) +template +static inline bool IsDockTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && GetStationType(t) == STATION_DOCK; } +/** @copydoc IsDockTile(TileIndexT::T) */ +static inline bool IsDockTile(TileIndex t) { return IsDockTile(t); } +/** @copydoc IsDockTile(TileIndexT::T) */ +static inline bool IsDockTile(GenericTileIndex t) { return IsDockTile(t); } /** * Is tile \a t a buoy tile? @@ -304,20 +414,30 @@ static inline bool IsDockTile(TileIndex t) * @pre IsTileType(t, MP_STATION) * @return \c true if the tile is a buoy */ -static inline bool IsBuoy(TileIndex t) +template +static inline bool IsBuoy(typename TileIndexT::T t) { return GetStationType(t) == STATION_BUOY; } +/** @copydoc IsBuoy(TileIndexT::T) */ +static inline bool IsBuoy(TileIndex t) { return IsBuoy(t); } +/** @copydoc IsBuoy(TileIndexT::T) */ +static inline bool IsBuoy(GenericTileIndex t) { return IsBuoy(t); } /** * Is tile \a t a buoy tile? * @param t Tile to check * @return \c true if the tile is a buoy */ -static inline bool IsBuoyTile(TileIndex t) +template +static inline bool IsBuoyTile(typename TileIndexT::T t) { return IsTileType(t, MP_STATION) && IsBuoy(t); } +/** @copydoc IsBuoyTile(TileIndexT::T) */ +static inline bool IsBuoyTile(TileIndex t) { return IsBuoyTile(t); } +/** @copydoc IsBuoyTile(TileIndexT::T) */ +static inline bool IsBuoyTile(GenericTileIndex t) { return IsBuoyTile(t); } /** * Is tile \a t an hangar tile? @@ -335,11 +455,16 @@ static inline bool IsHangarTile(TileIndex t) * @pre HasStationRail(t) * @return The direction of the rails on tile \a t. */ -static inline Axis GetRailStationAxis(TileIndex t) +template +static inline Axis GetRailStationAxis(typename TileIndexT::T t) { assert(HasStationRail(t)); return HasBit(GetStationGfx(t), 0) ? AXIS_Y : AXIS_X; } +/** @copydoc GetRailStationAxis(TileIndexT::T) */ +static inline Axis GetRailStationAxis(TileIndex t) { return GetRailStationAxis(t); } +/** @copydoc GetRailStationAxis(TileIndexT::T) */ +static inline Axis GetRailStationAxis(GenericTileIndex t) { return GetRailStationAxis(t); } /** * Get the rail track of a rail station tile. @@ -347,10 +472,15 @@ static inline Axis GetRailStationAxis(TileIndex t) * @pre HasStationRail(t) * @return The rail track of the rails on tile \a t. */ -static inline Track GetRailStationTrack(TileIndex t) +template +static inline Track GetRailStationTrack(typename TileIndexT::T t) { return AxisToTrack(GetRailStationAxis(t)); } +/** @copydoc GetRailStationTrack(TileIndexT::T) */ +static inline Track GetRailStationTrack(TileIndex t) { return GetRailStationTrack(t); } +/** @copydoc GetRailStationTrack(TileIndexT::T) */ +static inline Track GetRailStationTrack(GenericTileIndex t) { return GetRailStationTrack(t); } /** * Get the trackbits of a rail station tile. @@ -358,10 +488,15 @@ static inline Track GetRailStationTrack(TileIndex t) * @pre HasStationRail(t) * @return The trackbits of the rails on tile \a t. */ -static inline TrackBits GetRailStationTrackBits(TileIndex t) +template +static inline TrackBits GetRailStationTrackBits(typename TileIndexT::T t) { return AxisToTrackBits(GetRailStationAxis(t)); } +/** @copydoc GetRailStationTrackBits(TileIndexT::T) */ +static inline TrackBits GetRailStationTrackBits(TileIndex t) { return GetRailStationTrackBits(t); } +/** @copydoc GetRailStationTrackBits(TileIndexT::T) */ +static inline TrackBits GetRailStationTrackBits(GenericTileIndex t) { return GetRailStationTrackBits(t); } /** * Check if a tile is a valid continuation to a railstation tile. @@ -394,7 +529,7 @@ static inline bool IsCompatibleTrainStationTile(TileIndex test_tile, TileIndex s static inline bool HasStationReservation(TileIndex t) { assert(HasStationRail(t)); - return HasBit(_me[t].m6, 2); + return HasBit(GetTileEx(t)->m6, 2); } /** @@ -403,11 +538,16 @@ static inline bool HasStationReservation(TileIndex t) * @param t the station tile * @param b the reservation state */ -static inline void SetRailStationReservation(TileIndex t, bool b) +template +static inline void SetRailStationReservation(typename TileIndexT::T t, bool b) { assert(HasStationRail(t)); - SB(_me[t].m6, 2, 1, b ? 1 : 0); + SB(GetTileEx(t)->m6, 2, 1, b ? 1 : 0); } +/** @copydoc SetRailStationReservation(TileIndexT::T) */ +static inline void SetRailStationReservation(TileIndex t, bool b) { SetRailStationReservation(t, b); } +/** @copydoc SetRailStationReservation(TileIndexT::T) */ +static inline void SetRailStationReservation(GenericTileIndex t, bool b) { SetRailStationReservation(t, b); } /** * Get the reserved track bits for a waypoint @@ -421,18 +561,56 @@ static inline TrackBits GetStationReservationTrackBits(TileIndex t) } /** + * Test whether a given water dock tile is the land part of the dock + * @param t the water dock tile + * @return if the given tile is the land part of a dock + * @pre IsDockTile(t) + */ +template +static inline bool IsLandDockSection(typename TileIndexT::T t) +{ + assert(IsDockTile(t)); + return GetStationGfx(t) < GFX_DOCK_BASE_WATER_PART; +} +/** @copydoc IsLandDockSection */ +static inline bool IsLandDockSection(TileIndex t) { return IsLandDockSection(t); } +/** @copydoc IsLandDockSection */ +static inline bool IsLandDockSection(GenericTileIndex t) { return IsLandDockSection(t); } + +/** * Get the direction of a dock. * @param t Tile to query - * @pre IsDock(t) + * @pre IsLandDockSection(t) * @pre \a t is the land part of the dock * @return The direction of the dock on tile \a t. */ -static inline DiagDirection GetDockDirection(TileIndex t) +template +static inline DiagDirection GetDockDirection(typename TileIndexT::T t) { - StationGfx gfx = GetStationGfx(t); - assert(IsDock(t) && gfx < GFX_DOCK_BASE_WATER_PART); - return (DiagDirection)(gfx); + assert(IsLandDockSection(t)); + return (DiagDirection)GetStationGfx(t); +} +/** @copydoc GetDockDirection(TileIndexT::T) */ +static inline DiagDirection GetDockDirection(TileIndex t) { return GetDockDirection(t); } +/** @copydoc GetDockDirection(TileIndexT::T) */ +static inline DiagDirection GetDockDirection(GenericTileIndex t) { return GetDockDirection(t); } + +/** + * Get the other tile of a dock. + * @param t Tile to query + * @pre IsDockTile(t) + * @return The other tile of the dock. + */ +template +static inline typename TileIndexT::T GetOtherDockTile(typename TileIndexT::T t) +{ + TileIndexDiff delta = ToTileIndexDiff(TileIndexDiffCByDiagDir(AxisToDiagDir((Axis)(GetStationGfx(t) & 0x1))), MapOf(t)); + return IsDockTile(t + delta) ? t + delta : t - delta; } +/** @copydoc GetOtherDockTile */ +static inline TileIndex GetOtherDockTile(TileIndex t) { return GetOtherDockTile(t); } +/** @copydoc GetOtherDockTile */ +static inline GenericTileIndex GetOtherDockTile(GenericTileIndex t) { return GetOtherDockTile(t); } /** * Get the tileoffset from this tile a ship should target to get to this dock. @@ -467,11 +645,16 @@ static inline TileIndexDiffC GetDockOffset(TileIndex t) * @pre HasStationTileRail(t) * @return True if this station is part of a newgrf station. */ -static inline bool IsCustomStationSpecIndex(TileIndex t) +template +static inline bool IsCustomStationSpecIndex(typename TileIndexT::T t) { assert(HasStationTileRail(t)); - return _m[t].m4 != 0; + return GetTile(t)->m4 != 0; } +/** @copydoc IsCustomStationSpecIndex(TileIndexT::T) */ +static inline bool IsCustomStationSpecIndex(TileIndex t) { return IsCustomStationSpecIndex(t); } +/** @copydoc IsCustomStationSpecIndex(TileIndexT::T) */ +static inline bool IsCustomStationSpecIndex(GenericTileIndex t) { return IsCustomStationSpecIndex(t); } /** * Set the custom station spec for this tile. @@ -479,11 +662,16 @@ static inline bool IsCustomStationSpecIndex(TileIndex t) * @param specindex The new spec. * @pre HasStationTileRail(t) */ -static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) +template +static inline void SetCustomStationSpecIndex(typename TileIndexT::T t, byte specindex) { assert(HasStationTileRail(t)); - _m[t].m4 = specindex; + GetTile(t)->m4 = specindex; } +/** @copydoc SetCustomStationSpecIndex(TileIndexT::T,byte) */ +static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) { return SetCustomStationSpecIndex(t, specindex); } +/** @copydoc SetCustomStationSpecIndex(TileIndexT::T,byte) */ +static inline void SetCustomStationSpecIndex(GenericTileIndex t, byte specindex) { return SetCustomStationSpecIndex(t, specindex); } /** * Get the custom station spec for this tile. @@ -491,11 +679,16 @@ static inline void SetCustomStationSpecIndex(TileIndex t, byte specindex) * @pre HasStationTileRail(t) * @return The custom station spec of this tile. */ -static inline uint GetCustomStationSpecIndex(TileIndex t) +template +static inline uint GetCustomStationSpecIndex(typename TileIndexT::T t) { assert(HasStationTileRail(t)); - return _m[t].m4; + return GetTile(t)->m4; } +/** @copydoc GetCustomStationSpecIndex(TileIndexT::T) */ +static inline uint GetCustomStationSpecIndex(TileIndex t) { return GetCustomStationSpecIndex(t); } +/** @copydoc GetCustomStationSpecIndex(TileIndexT::T) */ +static inline uint GetCustomStationSpecIndex(GenericTileIndex t) { return GetCustomStationSpecIndex(t); } /** * Set the random bits for a station tile. @@ -506,7 +699,7 @@ static inline uint GetCustomStationSpecIndex(TileIndex t) static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) { assert(IsTileType(t, MP_STATION)); - SB(_m[t].m3, 4, 4, random_bits); + SB(GetTile(t)->m3, 4, 4, random_bits); } /** @@ -518,7 +711,7 @@ static inline void SetStationTileRandomBits(TileIndex t, byte random_bits) static inline byte GetStationTileRandomBits(TileIndex t) { assert(IsTileType(t, MP_STATION)); - return GB(_m[t].m3, 4, 4); + return GB(GetTile(t)->m3, 4, 4); } /** @@ -530,19 +723,24 @@ static inline byte GetStationTileRandomBits(TileIndex t) * @param section the StationGfx to be used for this tile * @param wc The water class of the station */ -static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) +template +static inline void MakeStation(typename TileIndexT::T t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { SetTileType(t, MP_STATION); SetTileOwner(t, o); SetWaterClass(t, wc); - _m[t].m2 = sid; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = section; - SB(_me[t].m6, 2, 1, 0); - SB(_me[t].m6, 3, 3, st); - _me[t].m7 = 0; + GetTile(t)->m2 = sid; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = section; + SB(GetTileEx(t)->m6, 2, 1, 0); + SB(GetTileEx(t)->m6, 3, 3, st); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeStation(TileIndexT::T,Owner,StationID,StationType,byte,WaterClass) */ +static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { MakeStation(t, o, sid, st, section, wc); } +/** @copydoc MakeStation(TileIndexT::T,Owner,StationID,StationType,byte,WaterClass) */ +static inline void MakeStation(GenericTileIndex t, Owner o, StationID sid, StationType st, byte section, WaterClass wc = WATER_CLASS_INVALID) { MakeStation(t, o, sid, st, section, wc); } /** * Make the given tile a rail station tile. @@ -553,12 +751,17 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType * @param section the StationGfx to be used for this tile * @param rt the railtype of this tile */ -static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) +template +static inline void MakeRailStation(typename TileIndexT::T t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeStation(t, o, sid, STATION_RAIL, section + a); SetRailType(t, rt); SetRailStationReservation(t, false); } +/** @copydoc MakeRailStation(TileIndexT::T,Owner,StationID,Axis,byte,RailType) */ +static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailStation(t, o, sid, a, section, rt); } +/** @copydoc MakeRailStation(TileIndexT::T,Owner,StationID,Axis,byte,RailType) */ +static inline void MakeRailStation(GenericTileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailStation(t, o, sid, a, section, rt); } /** * Make the given tile a rail waypoint tile. @@ -569,12 +772,17 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a, * @param section the StationGfx to be used for this tile * @param rt the railtype of this tile */ -static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) +template +static inline void MakeRailWaypoint(typename TileIndexT::T t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeStation(t, o, sid, STATION_WAYPOINT, section + a); SetRailType(t, rt); SetRailStationReservation(t, false); } +/** @copydoc MakeRailWaypoint(TileIndexT::T,Owner,StationID,Axis,byte,RailType) */ +static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailWaypoint(t, o, sid, a, section, rt); } +/** @copydoc MakeRailWaypoint(TileIndexT::T,Owner,StationID,Axis,byte,RailType) */ +static inline void MakeRailWaypoint(GenericTileIndex t, Owner o, StationID sid, Axis a, byte section, RailType rt) { MakeRailWaypoint(t, o, sid, a, section, rt); } /** * Make the given tile a roadstop tile. @@ -585,13 +793,18 @@ static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a, * @param rt the roadtypes on this tile * @param d the direction of the roadstop */ -static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) +template +static inline void MakeRoadStop(typename TileIndexT::T t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) { MakeStation(t, o, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), d); SetRoadTypes(t, rt); SetRoadOwner(t, ROADTYPE_ROAD, o); SetRoadOwner(t, ROADTYPE_TRAM, o); } +/** @copydoc MakeRoadStop(TileIndexT::T,Owner,StationID,RoadStopType,RoadTypes,DiagDirection) */ +static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) { MakeRoadStop(t, o, sid, rst, rt, d); } +/** @copydoc MakeRoadStop(TileIndexT::T,Owner,StationID,RoadStopType,RoadTypes,DiagDirection) */ +static inline void MakeRoadStop(GenericTileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d) { MakeRoadStop(t, o, sid, rst, rt, d); } /** * Make the given tile a drivethrough roadstop tile. @@ -604,13 +817,18 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp * @param rt the roadtypes on this tile * @param a the direction of the roadstop */ -static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) +template +static inline void MakeDriveThroughRoadStop(typename TileIndexT::T t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) { MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a); SetRoadTypes(t, rt); SetRoadOwner(t, ROADTYPE_ROAD, road); SetRoadOwner(t, ROADTYPE_TRAM, tram); } +/** @copydoc MakeDriveThroughRoadStop(TileIndexT::T,Owner,Owner,Owner,StationID,RoadStopType,RoadTypes,Axis) */ +static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) { MakeDriveThroughRoadStop(t, station, road, tram, sid, rst, rt, a); } +/** @copydoc MakeDriveThroughRoadStop(TileIndexT::T,Owner,Owner,Owner,StationID,RoadStopType,RoadTypes,Axis) */ +static inline void MakeDriveThroughRoadStop(GenericTileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a) { MakeDriveThroughRoadStop(t, station, road, tram, sid, rst, rt, a); } /** * Make the given tile an airport tile. @@ -620,10 +838,15 @@ static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner ro * @param section the StationGfx to be used for this tile * @param wc the type of water on this tile */ -static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section, WaterClass wc) +template +static inline void MakeAirport(typename TileIndexT::T t, Owner o, StationID sid, byte section, WaterClass wc) { MakeStation(t, o, sid, STATION_AIRPORT, section, wc); } +/** @copydoc MakeAirport(TileIndexT::T,Owner,StationID,byte,WaterClass) */ +static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section, WaterClass wc) { MakeAirport(t, o, sid, section, wc); } +/** @copydoc MakeAirport(TileIndexT::T,Owner,StationID,byte,WaterClass) */ +static inline void MakeAirport(GenericTileIndex t, Owner o, StationID sid, byte section, WaterClass wc) { MakeAirport(t, o, sid, section, wc); } /** * Make the given tile a buoy tile. @@ -631,13 +854,18 @@ static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section * @param sid the station to which this tile belongs * @param wc the type of water on this tile */ -static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) +template +static inline void MakeBuoy(typename TileIndexT::T t, StationID sid, WaterClass wc) { /* Make the owner of the buoy tile the same as the current owner of the * water tile. In this way, we can reset the owner of the water to its * original state when the buoy gets removed. */ MakeStation(t, GetTileOwner(t), sid, STATION_BUOY, 0, wc); } +/** @copydoc MakeBuoy(TileIndexT::T,StationID,WaterClass) */ +static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) { MakeBuoy(t, sid, wc); } +/** @copydoc MakeBuoy(TileIndexT::T,StationID,WaterClass) */ +static inline void MakeBuoy(GenericTileIndex t, StationID sid, WaterClass wc) { MakeBuoy(t, sid, wc); } /** * Make the given tile a dock tile. @@ -647,11 +875,16 @@ static inline void MakeBuoy(TileIndex t, StationID sid, WaterClass wc) * @param d the direction of the dock * @param wc the type of water on this tile */ -static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) +template +static inline void MakeDock(typename TileIndexT::T t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeStation(t, o, sid, STATION_DOCK, d); - MakeStation(t + TileOffsByDiagDir(d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); + MakeStation(TileAddByDiagDir(t, d), o, sid, STATION_DOCK, GFX_DOCK_BASE_WATER_PART + DiagDirToAxis(d), wc); } +/** @copydoc MakeDock(TileIndexT::T,Owner,StationID,DiagDirection,WaterClass) */ +static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeDock(t, o, sid, d, wc); } +/** @copydoc MakeDock(TileIndexT::T,Owner,StationID,DiagDirection,WaterClass) */ +static inline void MakeDock(GenericTileIndex t, Owner o, StationID sid, DiagDirection d, WaterClass wc) { MakeDock(t, o, sid, d, wc); } /** * Make the given tile an oilrig tile. diff --git a/src/strings.cpp b/src/strings.cpp index 1c539d934..08e4210da 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1150,6 +1150,8 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg const CargoSpec *cs; FOR_ALL_SORTED_CARGOSPECS(cs) { + int n; + if (!HasBit(cmask, cs->Index())) continue; if (buff >= last - 2) break; // ',' and ' ' @@ -1163,6 +1165,20 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } buff = GetStringWithArgs(buff, cs->name, args, last, next_substr_case_index, game_script); + + if (args->GetParam(1) != SCC_CARGO_LIST || !_settings_client.gui.forecast_display) { + continue; + } + + /* Shows only passengers and mails since other cargoes provide no useful value. (all 1) */ + if (cs->Index() == CT_PASSENGERS || cs->Index() == CT_MAIL) { + if (cs->Index() == CT_PASSENGERS) { + n = seprintf(buff, last, "(%d)", (int) args->GetParam(2)); + } else { + n = seprintf(buff, last, "(%d)", (int) args->GetParam(3)); + } + buff += n; + } } /* If first is still true then no cargo is accepted */ diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini index 52ca2d16e..fda2f29aa 100644 --- a/src/table/misc_settings.ini +++ b/src/table/misc_settings.ini @@ -255,7 +255,7 @@ type = SLE_UINT var = _transparency_opt def = 0 min = 0 -max = 0x1FF +max = 0x3FF cat = SC_BASIC [SDTG_VAR] @@ -264,7 +264,7 @@ type = SLE_UINT var = _transparency_lock def = 0 min = 0 -max = 0x1FF +max = 0x3FF cat = SC_BASIC [SDTG_VAR] diff --git a/src/table/settings.ini b/src/table/settings.ini index 23fca32a4..5af8bbb65 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -418,6 +418,20 @@ strval = STR_CONFIG_SETTING_COMMAND_PAUSE_LEVEL_NO_ACTIONS [SDT_VAR] base = GameSettings +var = construction.clipboard_capacity +type = SLE_UINT8 +flags = SLF_NOT_IN_SAVE +guiflags = SGF_NEWGAME_ONLY +def = 63 +min = 1 +max = 63 +interval = 1 +str = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY +strhelp = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_HELPTEXT +strval = STR_CONFIG_SETTING_CLIPBOARD_CAPACITY_VALUE + +[SDT_VAR] +base = GameSettings var = construction.terraform_per_64k_frames type = SLE_UINT32 from = 156 @@ -2727,6 +2741,13 @@ strhelp = STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT proc = PopulationInLabelActive [SDTC_BOOL] +var = gui.forecast_display +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_FORECAST_DISPLAY +strhelp = STR_CONFIG_SETTING_FORECAST_DISPLAY_HELPTEXT + +[SDTC_BOOL] var = gui.link_terraform_toolbar flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = false @@ -2866,6 +2887,12 @@ strval = STR_CONFIG_SETTING_DEFAULT_RAIL_TYPE_FIRST cat = SC_BASIC [SDTC_BOOL] +var = gui.townrating_indicator +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_TOWNRATING_INDICATOR + +[SDTC_BOOL] var = gui.enable_signal_gui flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = true @@ -2888,6 +2915,16 @@ strval = STR_JUST_INT cat = SC_EXPERT [SDTC_VAR] +var = gui.simulated_wormhole_signals +type = SLE_UINT8 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = 4 +min = 1 +max = 16 +str = STR_CONFIG_SETTING_SIMULATE_SIGNALS +proc = RedrawScreen + +[SDTC_VAR] var = gui.drag_signals_density type = SLE_UINT8 flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC diff --git a/src/table/sprites.h b/src/table/sprites.h index 81d5388f9..221eb4ca8 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -163,7 +163,10 @@ static const SpriteID SPR_WINDOW_DEFSIZE = SPR_OPENTTD_BASE + 168; static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174; -static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; +static const SpriteID SPR_CLIPBOARD_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; +static const SpriteID CLIPBOARD_SPRITE_COUNT = 25; + +static const SpriteID SPR_SIGNALS_BASE = SPR_CLIPBOARD_BASE + CLIPBOARD_SPRITE_COUNT; static const uint16 PRESIGNAL_SPRITE_COUNT = 48; static const uint16 PRESIGNAL_AND_SEMAPHORE_SPRITE_COUNT = 112; static const uint16 PRESIGNAL_SEMAPHORE_AND_PBS_SPRITE_COUNT = 240; @@ -1332,6 +1335,30 @@ static const SpriteID SPR_IMG_GOAL = SPR_OPENTTD_BASE + 171; static const SpriteID SPR_IMG_GOAL_COMPLETED = SPR_OPENTTD_BASE + 172; static const SpriteID SPR_IMG_GOAL_BROKEN_REF= SPR_OPENTTD_BASE + 173; +/* clipboard_gui.cpp */ +static const SpriteID SPR_IMG_CLIPBOARD = SPR_CLIPBOARD_BASE + 0; +static const SpriteID SPR_IMG_CLIPBOARD_COPY = SPR_CLIPBOARD_BASE + 1; +static const SpriteID SPR_IMG_CLIPBOARD_PASTE = SPR_CLIPBOARD_BASE + 2; +static const SpriteID SPR_IMG_CLIPBOARD_SELECT_COPY_AREA = SPR_CLIPBOARD_BASE + 3; +static const SpriteID SPR_IMG_CLIPBOARD_INSTANT_COPY_PASTE = SPR_CLIPBOARD_BASE + 4; +static const SpriteID SPR_IMG_CLIPBOARD_NO_RAIL_CONVERTION = SPR_CLIPBOARD_BASE + 5; +static const SpriteID SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_OFF = SPR_CLIPBOARD_BASE + 6; +static const SpriteID SPR_IMG_CLIPBOARD_MIRROR_SIGNALS_ON = SPR_CLIPBOARD_BASE + 7; +static const SpriteID SPR_IMG_CLIPBOARD_UPGRADE_BRIDGES = SPR_CLIPBOARD_BASE + 8; +static const SpriteID SPR_IMG_CLIPBOARD_ROTATE_LEFT = SPR_CLIPBOARD_BASE + 9; +static const SpriteID SPR_IMG_CLIPBOARD_ROTATE_RIGHT = SPR_CLIPBOARD_BASE + 10; +static const SpriteID SPR_IMG_CLIPBOARD_REFLECT_NE_SW = SPR_CLIPBOARD_BASE + 11; +static const SpriteID SPR_IMG_CLIPBOARD_REFLECT_NW_SE = SPR_CLIPBOARD_BASE + 12; +static const SpriteID SPR_IMG_TRANFORMATION_IDENTITY = SPR_CLIPBOARD_BASE + 13; +static const SpriteID SPR_IMG_TRANFORMATION_ROT_90_R = SPR_CLIPBOARD_BASE + 14; +static const SpriteID SPR_IMG_TRANFORMATION_ROT_180 = SPR_CLIPBOARD_BASE + 15; +static const SpriteID SPR_IMG_TRANFORMATION_ROT_90_L = SPR_CLIPBOARD_BASE + 16; +static const SpriteID SPR_IMG_TRANFORMATION_REF_NE_SW = SPR_CLIPBOARD_BASE + 17; +static const SpriteID SPR_IMG_TRANFORMATION_REF_W_E = SPR_CLIPBOARD_BASE + 18; +static const SpriteID SPR_IMG_TRANFORMATION_REF_NW_SE = SPR_CLIPBOARD_BASE + 19; +static const SpriteID SPR_IMG_TRANFORMATION_REF_N_S = SPR_CLIPBOARD_BASE + 20; +static const SpriteID SPR_IMG_CLIPBOARD_HEIGHT_PANEL = SPR_CLIPBOARD_BASE + 21; + /* intro_gui.cpp, genworld_gui.cpp */ static const SpriteID SPR_SELECT_TEMPERATE = 4882; static const SpriteID SPR_SELECT_TEMPERATE_PUSHED = 4883; @@ -1440,6 +1467,11 @@ static const CursorID SPR_CURSOR_CLONE_ROADVEH = SPR_OPENTTD_BASE + 111; static const CursorID SPR_CURSOR_CLONE_SHIP = SPR_OPENTTD_BASE + 112; static const CursorID SPR_CURSOR_CLONE_AIRPLANE = SPR_OPENTTD_BASE + 113; +/* Clipboard cursors */ +static const CursorID SPR_CURSOR_COPY = SPR_CLIPBOARD_BASE + 22; +static const CursorID SPR_CURSOR_PASTE = SPR_CLIPBOARD_BASE + 23; +static const CursorID SPR_CURSOR_ADJUST_HEIGHT = SPR_CLIPBOARD_BASE + 24; + /** Animation macro in table/animcursors.h (_animcursors[]) */ static const CursorID SPR_CURSOR_DEMOLISH_FIRST = 704; diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index aad982282..5c090bfba 100644 --- a/src/terraform_cmd.cpp +++ b/src/terraform_cmd.cpp @@ -10,7 +10,9 @@ /** @file terraform_cmd.cpp Commands related to terraforming. */ #include "stdafx.h" +#include "core/geometry_func.hpp" #include "command_func.h" +#include "copypaste_cmd.h" #include "tunnel_map.h" #include "bridge_map.h" #include "viewport_func.h" @@ -18,6 +20,7 @@ #include "object_base.h" #include "company_base.h" #include "company_func.h" +#include "strings_func.h" #include "table/strings.h" @@ -408,6 +411,121 @@ CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uin return total_cost; } +/** Tile iterator for terraforming purposes. */ +class TerraformingIterator : public TileIterator { +public: + TerraformingIterator(TileIndex tile) : TileIterator(tile) + { } + + /* + * Get the target height of currently iterated tile. + * @return target height for the tile + */ + virtual int GetTileTargetHeight() const = 0; + + virtual TerraformingIterator *Clone() const { NOT_REACHED(); /* not implemented */ }; +}; + +/** Terraforming iterator for leveling an area. */ +class LandLevelingIterator : public TerraformingIterator { +public: + int target_height; + + LandLevelingIterator(TileIndex tile, int target_height) + : TerraformingIterator(tile), target_height(target_height) + { } + + virtual int GetTileTargetHeight() const + { + return this->target_height; + } +}; + +/** Orthogonal variant of a #LandLevelingIterator. */ +class OrthogonalLandLevelingIterator : public LandLevelingIterator, protected OrthogonalTileIteratorController { +public: + OrthogonalLandLevelingIterator(const OrthogonalTileArea &ta, int target_height) : LandLevelingIterator(ta.tile, target_height) + { + this->Init(this->MyIndex(), ta.w, ta.h); + } + + TileIterator &operator ++ () + { + this->Advance(this->MyIndex(), this->MyMap()); + return *this; + } +}; + +/** Diagonal variant of a #LandLevelingIterator. */ +class DiagonalLandLevelingIterator : public LandLevelingIterator, protected DiagonalTileIteratorController { +public: + DiagonalLandLevelingIterator(const DiagonalTileArea &ta, int target_height) : LandLevelingIterator(ta.tile, target_height) + { + this->Init(this->MyIndex(), ta.a, ta.b, this->MyMap()); + } + + TileIterator &operator ++ () + { + this->Advance(this->MyIndex(), this->MyMap()); + return *this; + } +}; + +/** Terraforming iterator for leveling an area for pasting purposes. */ +class PasteLandLevelingIterator : public OrthogonalLandLevelingIterator { +protected: + CopyPasteLevelVariant variant; + +public: + PasteLandLevelingIterator(const TileArea &ta, int target_height, CopyPasteLevelVariant variant) + : OrthogonalLandLevelingIterator(ta, target_height), variant(variant) + { + } + + virtual int GetTileTargetHeight() const + { + uint ret = this->target_height; + switch (this->variant) { + case CPLV_LEVEL_ABOVE: ret = min(ret, TileHeight(*this)); break; + case CPLV_LEVEL_BELOW: ret = max(ret, TileHeight(*this)); break; + } + return ret; + } +}; + +/** Terraforming iterator for copy-pasting tile heights. */ +class HeightsCopyPastingIterator : public TerraformingIterator, protected TransformationTileIteratorController { +protected: + GenericTileIndex src_tile; ///< Current tile of the source area. + int height_delta; ///< Amount of units to add to each height + +public: + HeightsCopyPastingIterator(const GenericTileArea &src_area, TileIndex transformed_north, DirTransformation transformation, int height_delta) + : TerraformingIterator(transformed_north), src_tile(src_area.tile), height_delta(height_delta) + { + this->Init(&IndexOf(this->src_tile), this->MyIndex(), src_area.w, src_area.h, transformation); + } + + virtual TileIterator &operator ++() + { + this->Advance(&IndexOf(this->src_tile), MapOf(this->src_tile), this->MyIndex(), this->MyMap()); + return *this; + } + + virtual int GetTileTargetHeight() const + { + return TileHeight(this->src_tile) + this->height_delta; + } +}; + +/** Compound result of a terraform process. */ +struct TerraformTilesResult { + Money cost; ///< Overal cost. + bool had_success; ///< Whether any success occured. + StringID last_error; ///< Last error, STR_NULL if there were no errors. +}; + +static TerraformTilesResult TerraformTiles(TerraformingIterator *iter, DoCommandFlag flags, Money available_money = GetAvailableMoneyForCommand()); /** * Levels a selected (rectangle) area of land @@ -424,41 +542,146 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 { if (p1 >= MapSize()) return CMD_ERROR; - _terraform_err_tile = INVALID_TILE; - - /* remember level height */ - uint oldh = TileHeight(p1); - /* compute new height */ - uint h = oldh; - LevelMode lm = (LevelMode)GB(p2, 1, 2); - switch (lm) { + int h = TileHeight(p1); + switch ((LevelMode)GB(p2, 1, 2)) { case LM_LEVEL: break; case LM_RAISE: h++; break; case LM_LOWER: h--; break; default: return CMD_ERROR; } - /* Check range of destination height */ - if (h > _settings_game.construction.max_heightlevel) return_cmd_error((oldh == 0) ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH); + TerraformTilesResult ret; + if (HasBit(p2, 0)) { + DiagonalLandLevelingIterator iter(DiagonalTileArea(tile, p1), h); + ret = TerraformTiles(&iter, flags); + } else { + OrthogonalLandLevelingIterator iter(OrthogonalTileArea(tile, p1), h); + ret = TerraformTiles(&iter, flags); + } + + /* If there were only errors then fail with the last one. */ + if (!ret.had_success && ret.last_error != STR_NULL) return_cmd_error(ret.last_error); + /* Return overal cost. */ + return CommandCost(EXPENSES_CONSTRUCTION, ret.cost); +} + +/** + * Terraform tiles as a part of a pasting process. + * @param iter iterator to use when terraforming + */ +static void TerraformPasteTiles(TerraformingIterator *iter) +{ + TileIndex start_tile = *iter; + + /* Do actual terraforming. */ + TerraformTilesResult ret = TerraformTiles(iter, _current_pasting->dc_flags | DC_ALL_TILES, _current_pasting->GetAvailableMoney()); + + /* When copy-pasting, we want to higlight error tiles more frequently. TerraformTiles + * doesn't always set the _terraform_err_tile (on some errors it's just INVALID_TILE). + * We will assume the start tile in these cases. This will give a better overview on + * what area failed to paste. */ + if (_terraform_err_tile == INVALID_TILE) _terraform_err_tile = start_tile; + + /* Collect overal cost of the operation. */ + if (ret.had_success) { + _current_pasting->CollectCost(CommandCost(EXPENSES_CONSTRUCTION, ret.cost), _terraform_err_tile, STR_ERROR_CAN_T_LEVEL_LAND_HERE); + } + + /* Handle _additional_cash_required */ + if ((_current_pasting->dc_flags & DC_EXEC) && _additional_cash_required > 0) { + SetDParam(0, _additional_cash_required); + _current_pasting->CollectError(_terraform_err_tile, STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, STR_ERROR_CAN_T_LEVEL_LAND_HERE); + } + + /* Collect last error, if any. */ + if (ret.last_error != STR_NULL) { + _current_pasting->CollectError(_terraform_err_tile, ret.last_error, STR_ERROR_CAN_T_LEVEL_LAND_HERE); + } +} + +/** + * Level land (as a part of a pasting process). + * + * @param ta Area of tiles corners to level. + * @param height Desired height. + * @param variant Leveling variant. + */ +void LevelPasteLand(const TileArea &ta, uint height, CopyPasteLevelVariant variant) +{ + PasteLandLevelingIterator iter(ta, height, variant); + TerraformPasteTiles(&iter); +} - Money money = GetAvailableMoneyForCommand(); - CommandCost cost(EXPENSES_CONSTRUCTION); - CommandCost last_error(lm == LM_LEVEL ? STR_ERROR_ALREADY_LEVELLED : INVALID_STRING_ID); - bool had_success = false; +/** + * Copy and paste heights from one map to another. + * + * @param src_area Area to read heights from. It consists of tiles, not of tile corners + * e.g. if you pass a single tile area then 4 corners will be terraformed. + * @param dst_area_north Norhern tile of the area to write heigths at. + * @param transformation Transformation to perform on tile indices. + * @param height_delta Offset, number of units to add to each height. + */ +void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta) +{ + /* include also corners at SW and SE edges */ + GenericTileArea src_corners(src_area.tile, src_area.w + 1, src_area.h + 1); + /* transform the most northern corner */ + GenericTileIndex transformed_north_corner = src_corners.TransformedNorth(dst_area_north, transformation); + +#ifdef WITH_ASSERT + { + assert(IsValidTileIndex(dst_area_north)); + uint x = TileX(dst_area_north); + uint y = TileY(dst_area_north); + assert(!IsMainMapTile(dst_area_north) || !_settings_game.construction.freeform_edges || (x > 0 && y > 0)); + Dimension dst_dim = { src_corners.w, src_corners.h }; + dst_dim = TransformDimension(dst_dim, transformation); + assert(x + dst_dim.width <= MapSizeX(MapOf(dst_area_north)) && y + dst_dim.height <= MapSizeY(MapOf(dst_area_north))); + } +#endif /* WITH_ASSERT */ + + if (IsMainMapTile(dst_area_north)) { + HeightsCopyPastingIterator iter(src_corners, AsMainMapTile(transformed_north_corner), transformation, height_delta); + TerraformPasteTiles(&iter); + } else { + for (TransformationTileIteratorT iter(src_corners, transformed_north_corner, transformation); IsValidTileIndex(iter); ++iter) { + SetTileHeight(iter.DstTile(), TileHeight(iter.SrcTile())); + } + } +} + +/** + * Terraform multiple tiles. + * + * @param iter Iterator pointing tiles to terraform and their target heights. + * @return The cost of all successfull operations and the last error. + * + * @note _terraform_err_tile will be set to the tile where the last error occured + * + * @warning Note non-standard return behaviour - booth the cost \b and the error combined. + */ +static TerraformTilesResult TerraformTiles(TerraformingIterator *iter, DoCommandFlag flags, Money available_money) +{ + TerraformTilesResult result = { + 0, // cost + false, // had_success + STR_NULL // last_error + }; + TileIndex last_err_tile = INVALID_TILE; const Company *c = Company::GetIfValid(_current_company); int limit = (c == NULL ? INT32_MAX : GB(c->terraform_limit, 16, 16)); - if (limit == 0) return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED); + if (limit == 0) result.last_error = STR_ERROR_TERRAFORM_LIMIT_REACHED; - TileIterator *iter = HasBit(p2, 0) ? (TileIterator *)new DiagonalTileIterator(tile, p1) : new OrthogonalTileIterator(tile, p1); - for (; *iter != INVALID_TILE; ++(*iter)) { + for (; *iter != INVALID_TILE && limit > 0; ++(*iter)) { + int h = iter->GetTileTargetHeight(); TileIndex t = *iter; - uint curh = TileHeight(t); - while (curh != h) { + for (int curh = TileHeight(t); curh != h; curh += (curh > h) ? -1 : 1) { CommandCost ret = DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); if (ret.Failed()) { - last_error = ret; + result.last_error = ret.GetErrorMessage(); + last_err_tile = _terraform_err_tile; /* Did we reach the limit? */ if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0; @@ -466,11 +689,11 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } if (flags & DC_EXEC) { - money -= ret.GetCost(); - if (money < 0) { + available_money -= ret.GetCost(); + if (available_money < 0) { _additional_cash_required = ret.GetCost(); - delete iter; - return cost; + _terraform_err_tile = t; + return result; } DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); } else { @@ -479,20 +702,22 @@ CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 * when it's near the terraforming limit. Even then, the estimation is * completely off due to it basically counting terraforming double, so it being * cut off earlier might even give a better estimate in some cases. */ - if (--limit <= 0) { - had_success = true; + if (--limit <= 0) { + result.had_success = true; break; } } - cost.AddCost(ret); - curh += (curh > h) ? -1 : 1; - had_success = true; + result.cost += ret.GetCost(); + result.had_success = true; } + } - if (limit <= 0) break; + if (!result.had_success && result.last_error == STR_NULL) { + result.last_error = STR_ERROR_ALREADY_LEVELLED; + last_err_tile = INVALID_TILE; } - delete iter; - return had_success ? cost : last_error; + _terraform_err_tile = last_err_tile; + return result; } diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 6274de8cd..2cd1064ba 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -190,6 +190,12 @@ struct TerraformToolbarWindow : Window { this->last_user_action = widget; break; + case WID_TT_CLIPBOARD: // Show the clipboard toolbar + /* This button is NOT a place-push-button, so don't treat it as such */ + this->HandleButtonClick(WID_TT_CLIPBOARD); + ShowClipboardToolbar(); + break; + case WID_TT_DEMOLISH: // Demolish aka dynamite button HandlePlacePushButton(this, WID_TT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL); this->last_user_action = widget; @@ -325,6 +331,8 @@ static const NWidgetPart _nested_terraform_widgets[] = { NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), EndContainer(), + NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_TT_CLIPBOARD), SetMinimalSize(22, 22), + SetFill(0, 1), SetDataTip(SPR_IMG_CLIPBOARD, STR_LANDSCAPING_TOOLTIP_SHOW_CLIPBOARD_TOOLBAR), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_DEMOLISH), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_BUY_LAND), SetMinimalSize(22, 22), diff --git a/src/tile_cmd.h b/src/tile_cmd.h index 6b8d6c69f..ea31575d7 100644 --- a/src/tile_cmd.h +++ b/src/tile_cmd.h @@ -18,6 +18,8 @@ #include "track_type.h" #include "tile_map.h" +struct CopyPasteParams; + /** The returned bits of VehicleEnterTile. */ enum VehicleEnterTileStatus { VETS_ENTERED_STATION = 1, ///< The vehicle entered a station @@ -138,6 +140,17 @@ typedef Foundation GetFoundationProc(TileIndex tile, Slope tileh); typedef CommandCost TerraformTileProc(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new); /** + * Tile callback function signature for copy-pasting tile content. + * + * @param src_tile Tile to copy content from. + * @param dst_tile Tile where to paste the content. + * @param copy_paste What, where and how we are copying. + * @param flags Command flags passed to the copy-paste command. + */ +typedef void CopyPasteTileProc(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste); + + +/** * Set of callback functions for performing tile operations of a given tile type. * @see TileType */ @@ -156,6 +169,7 @@ struct TileTypeProcs { VehicleEnterTileProc *vehicle_enter_tile_proc; ///< Called when a vehicle enters a tile GetFoundationProc *get_foundation_proc; TerraformTileProc *terraform_tile_proc; ///< Called when a terraforming operation is about to take place + CopyPasteTileProc *copy_paste_tile_proc; ///< Called to copy-paste content of a tile }; extern const TileTypeProcs * const _tile_type_procs[16]; diff --git a/src/tile_map.cpp b/src/tile_map.cpp index c566ad02c..911f8bc4b 100644 --- a/src/tile_map.cpp +++ b/src/tile_map.cpp @@ -112,9 +112,10 @@ static Slope GetTileSlopeGivenHeight(int hnorth, int hwest, int heast, int hsout * @param h If not \c NULL, pointer to storage of z height * @return Slope of the tile, except for the HALFTILE part */ -Slope GetTileSlope(TileIndex tile, int *h) +template +Slope GetTileSlope(typename TileIndexT::T tile, int *h) { - assert(tile < MapSize()); + assert(IsValidTileIndex(tile)); uint x = TileX(tile); uint y = TileY(tile); @@ -123,10 +124,10 @@ Slope GetTileSlope(TileIndex tile, int *h) return SLOPE_FLAT; } - int hnorth = TileHeight(tile); // Height of the North corner. - int hwest = TileHeight(tile + TileDiffXY(1, 0)); // Height of the West corner. - int heast = TileHeight(tile + TileDiffXY(0, 1)); // Height of the East corner. - int hsouth = TileHeight(tile + TileDiffXY(1, 1)); // Height of the South corner. + int hnorth = TileHeight(tile); // Height of the North corner. + int hwest = TileHeight(tile + TileDiffXY(1, 0, MapOf(tile))); // Height of the West corner. + int heast = TileHeight(tile + TileDiffXY(0, 1, MapOf(tile))); // Height of the East corner. + int hsouth = TileHeight(tile + TileDiffXY(1, 1, MapOf(tile))); // Height of the South corner. return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h); } @@ -149,6 +150,9 @@ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h) if (h != NULL) *h *= TILE_HEIGHT; return s; } +/* instantiate */ +template Slope GetTileSlope(TileIndex tile, int *h); +template Slope GetTileSlope(GenericTileIndex tile, int *h); /** * Check if a given tile is flat @@ -179,17 +183,21 @@ bool IsTileFlat(TileIndex tile, int *h) * @param tile Tile to compute height of * @return Minimum height of the tile */ -int GetTileZ(TileIndex tile) +template +int GetTileZ(typename TileIndexT::T tile) { - if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return 0; + if (TileX(tile) == MapMaxX(MapOf(tile)) || TileY(tile) == MapMaxY(MapOf(tile))) return 0; int h = TileHeight(tile); // N corner - h = min(h, TileHeight(tile + TileDiffXY(1, 0))); // W corner - h = min(h, TileHeight(tile + TileDiffXY(0, 1))); // E corner - h = min(h, TileHeight(tile + TileDiffXY(1, 1))); // S corner + h = min(h, TileHeight(tile + TileDiffXY(1, 0, MapOf(tile)))); // W corner + h = min(h, TileHeight(tile + TileDiffXY(0, 1, MapOf(tile)))); // E corner + h = min(h, TileHeight(tile + TileDiffXY(1, 1, MapOf(tile)))); // S corner return h; } +/* instantiate */ +template int GetTileZ(TileIndex tile); +template int GetTileZ(GenericTileIndex tile); /** * Get bottom height of the tile outside map. @@ -212,14 +220,15 @@ int GetTilePixelZOutsideMap(int x, int y) * @param t Tile to compute height of * @return Maximum height of the tile */ -int GetTileMaxZ(TileIndex t) +template +int GetTileMaxZ(typename TileIndexT::T t) { - if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return TileHeightOutsideMap(TileX(t), TileY(t)); + if (TileX(t) == MapMaxX(MapOf(t)) || TileY(t) == MapMaxY(MapOf(t))) return TileHeightOutsideMap(TileX(t), TileY(t)); int h = TileHeight(t); // N corner - h = max(h, TileHeight(t + TileDiffXY(1, 0))); // W corner - h = max(h, TileHeight(t + TileDiffXY(0, 1))); // E corner - h = max(h, TileHeight(t + TileDiffXY(1, 1))); // S corner + h = max(h, TileHeight(t + TileDiffXY(1, 0, MapOf(t)))); // W corner + h = max(h, TileHeight(t + TileDiffXY(0, 1, MapOf(t)))); // E corner + h = max(h, TileHeight(t + TileDiffXY(1, 1, MapOf(t)))); // S corner return h; } @@ -241,3 +250,7 @@ int GetTileMaxPixelZOutsideMap(int x, int y) return h * TILE_HEIGHT; } + +/* instantiate */ +template int GetTileMaxZ(TileIndex t); +template int GetTileMaxZ(GenericTileIndex t); diff --git a/src/tile_map.h b/src/tile_map.h index 4d5891d7b..c0a34b53b 100644 --- a/src/tile_map.h +++ b/src/tile_map.h @@ -26,13 +26,18 @@ * * @param tile The tile to get the height from * @return the height of the tile - * @pre tile < MapSize() + * @pre IsValidTileIndex(tile) */ -static inline uint TileHeight(TileIndex tile) +template +static inline uint TileHeight(typename TileIndexT::T tile) { - assert(tile < MapSize()); - return _m[tile].height; + assert(IsValidTileIndex(tile)); + return GetTile(tile)->height; } +/** @copydoc TileHeight(TileIndexT::T) */ +static inline uint TileHeight(TileIndex tile) { return TileHeight(tile); } +/** @copydoc TileHeight(TileIndexT::T) */ +static inline uint TileHeight(GenericTileIndex tile) { return TileHeight(tile); } uint TileHeightOutsideMap(int x, int y); @@ -43,15 +48,20 @@ uint TileHeightOutsideMap(int x, int y); * * @param tile The tile to change the height * @param height The new height value of the tile - * @pre tile < MapSize() + * @pre IsValidTileIndex(tile) * @pre heigth <= MAX_TILE_HEIGHT */ -static inline void SetTileHeight(TileIndex tile, uint height) +template +static inline void SetTileHeight(typename TileIndexT::T tile, uint height) { - assert(tile < MapSize()); + assert(IsValidTileIndex(tile)); assert(height <= MAX_TILE_HEIGHT); - _m[tile].height = height; + GetTile(tile)->height = height; } +/** @copydoc SetTileHeight(TileIndexT::T,uint) */ +static inline void SetTileHeight(TileIndex tile, uint height) { SetTileHeight(tile, height); } +/** @copydoc SetTileHeight(TileIndexT::T,uint) */ +static inline void SetTileHeight(GenericTileIndex tile, uint height) { SetTileHeight(tile, height); } /** * Returns the height of a tile in pixels. @@ -61,10 +71,15 @@ static inline void SetTileHeight(TileIndex tile, uint height) * @param tile The tile to get the height * @return The height of the tile in pixel */ -static inline uint TilePixelHeight(TileIndex tile) +template +static inline uint TilePixelHeight(typename TileIndexT::T tile) { return TileHeight(tile) * TILE_HEIGHT; } +/** @copydoc TilePixelHeight(TileIndexT::T) */ +static inline uint TilePixelHeight(TileIndex tile) { return TilePixelHeight(tile); } +/** @copydoc TilePixelHeight(TileIndexT::T) */ +static inline uint TilePixelHeight(GenericTileIndex tile) { return TilePixelHeight(tile); } /** * Returns the tile height for a coordinate outside map. Such a height is @@ -84,30 +99,41 @@ static inline uint TilePixelHeightOutsideMap(int x, int y) * * @param tile The tile to get the TileType * @return The tiletype of the tile - * @pre tile < MapSize() + * @pre IsValidTileIndex(tile) */ -static inline TileType GetTileType(TileIndex tile) +template +static inline TileType GetTileType(typename TileIndexT::T tile) { - assert(tile < MapSize()); - return (TileType)GB(_m[tile].type, 4, 4); + assert(IsValidTileIndex(tile)); + return (TileType)GB(GetTile(tile)->type, 4, 4); } +/** @copydoc GetTileType(TileIndexT::T) */ +static inline TileType GetTileType(TileIndex tile) { return GetTileType(tile); } +/** @copydoc GetTileType(TileIndexT::T) */ +static inline TileType GetTileType(GenericTileIndex tile) { return GetTileType(tile); } /** * Check if a tile is within the map (not a border) * * @param tile The tile to check * @return Whether the tile is in the interior of the map - * @pre tile < MapSize() + * @pre IsValidTileIndex(tile) */ -static inline bool IsInnerTile(TileIndex tile) +template +static inline bool IsInnerTile(typename TileIndexT::T tile) { - assert(tile < MapSize()); + assert(IsValidTileIndex(tile)); uint x = TileX(tile); uint y = TileY(tile); - return x < MapMaxX() && y < MapMaxY() && ((x > 0 && y > 0) || !_settings_game.construction.freeform_edges); + return x < MapMaxX(MapOf(tile)) && y < MapMaxY(MapOf(tile)) && + ((x > 0 && y > 0) || !IsMainMapTile(tile) || !_settings_game.construction.freeform_edges); } +/** @copydoc IsInnerTile(TileIndexT::T) */ +static inline bool IsInnerTile(TileIndex tile) { return IsInnerTile(tile); } +/** @copydoc IsInnerTile(TileIndexT::T) */ +static inline bool IsInnerTile(GenericTileIndex tile) { return IsInnerTile(tile); } /** * Set the type of a tile @@ -118,18 +144,23 @@ static inline bool IsInnerTile(TileIndex tile) * * @param tile The tile to save the new type * @param type The type to save - * @pre tile < MapSize() + * @pre IsValidTileIndex(tile) * @pre type MP_VOID <=> tile is on the south-east or south-west edge. */ -static inline void SetTileType(TileIndex tile, TileType type) +template +static inline void SetTileType(typename TileIndexT::T tile, TileType type) { - assert(tile < MapSize()); + assert(IsValidTileIndex(tile)); /* VOID tiles (and no others) are exactly allowed at the lower left and right * edges of the map. If _settings_game.construction.freeform_edges is true, * the upper edges of the map are also VOID tiles. */ assert(IsInnerTile(tile) == (type != MP_VOID)); - SB(_m[tile].type, 4, 4, type); + SB(GetTile(tile)->type, 4, 4, type); } +/** @copydoc SetTileType(TileIndexT::T,TileType) */ +static inline void SetTileType(TileIndex tile, TileType type) { return SetTileType(tile, type); } +/** @copydoc SetTileType(TileIndexT::T) */ +static inline void SetTileType(GenericTileIndex tile, TileType type) { return SetTileType(tile, type); } /** * Checks if a tile is a give tiletype. @@ -140,10 +171,15 @@ static inline void SetTileType(TileIndex tile, TileType type) * @param type The type to check against * @return true If the type matches against the type of the tile */ -static inline bool IsTileType(TileIndex tile, TileType type) +template +static inline bool IsTileType(typename TileIndexT::T tile, TileType type) { return GetTileType(tile) == type; } +/** @copydoc IsTileType(TileIndexT::T,TileType) */ +static inline bool IsTileType(TileIndex tile, TileType type) { return IsTileType(tile, type); } +/** @copydoc IsTileType(TileIndexT::T,TileType) */ +static inline bool IsTileType(GenericTileIndex tile, TileType type) { return IsTileType(tile, type); } /** * Checks if a tile is valid @@ -151,10 +187,15 @@ static inline bool IsTileType(TileIndex tile, TileType type) * @param tile The tile to check * @return True if the tile is on the map and not one of MP_VOID. */ -static inline bool IsValidTile(TileIndex tile) +template +static inline bool IsValidTile(typename TileIndexT::T tile) { - return tile < MapSize() && !IsTileType(tile, MP_VOID); + return IsValidTileIndex(tile) && !IsTileType(tile, MP_VOID); } +/** @copydoc IsValidTile(TileIndexT::T) */ +static inline bool IsValidTile(TileIndex tile) { return IsValidTile(tile); } +/** @copydoc IsValidTile(TileIndexT::T) */ +static inline bool IsValidTile(GenericTileIndex tile) { return IsValidTile(tile); } /** * Returns the owner of a tile @@ -168,14 +209,19 @@ static inline bool IsValidTile(TileIndex tile) * @pre IsValidTile(tile) * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY */ -static inline Owner GetTileOwner(TileIndex tile) +template +static inline Owner GetTileOwner(typename TileIndexT::T tile) { assert(IsValidTile(tile)); assert(!IsTileType(tile, MP_HOUSE)); assert(!IsTileType(tile, MP_INDUSTRY)); - return (Owner)GB(_m[tile].m1, 0, 5); + return (Owner)GB(GetTile(tile)->m1, 0, 5); } +/** @copydoc GetTileOwner(TileIndexT::T) */ +static inline Owner GetTileOwner(TileIndex tile) { return GetTileOwner(tile); } +/** @copydoc GetTileOwner(TileIndexT::T) */ +static inline Owner GetTileOwner(GenericTileIndex tile) { return GetTileOwner(tile); } /** * Sets the owner of a tile @@ -188,14 +234,19 @@ static inline Owner GetTileOwner(TileIndex tile) * @pre IsValidTile(tile) * @pre The type of the tile must not be MP_HOUSE and MP_INDUSTRY */ -static inline void SetTileOwner(TileIndex tile, Owner owner) +template +static inline void SetTileOwner(typename TileIndexT::T tile, Owner owner) { assert(IsValidTile(tile)); assert(!IsTileType(tile, MP_HOUSE)); assert(!IsTileType(tile, MP_INDUSTRY)); - SB(_m[tile].m1, 0, 5, owner); + SB(GetTile(tile)->m1, 0, 5, owner); } +/** @copydoc SetTileOwner(TileIndexT::T,Owner) */ +static inline void SetTileOwner(TileIndex tile, Owner owner) { SetTileOwner(tile, owner); } +/** @copydoc SetTileOwner(TileIndexT::T,Owner) */ +static inline void SetTileOwner(GenericTileIndex tile, Owner owner) { SetTileOwner(tile, owner); } /** * Checks if a tile belongs to the given owner @@ -204,10 +255,15 @@ static inline void SetTileOwner(TileIndex tile, Owner owner) * @param owner The owner to check against * @return True if a tile belongs the the given owner */ -static inline bool IsTileOwner(TileIndex tile, Owner owner) +template +static inline bool IsTileOwner(typename TileIndexT::T tile, Owner owner) { return GetTileOwner(tile) == owner; } +/** @copydoc IsTileOwner(TileIndexT::T,Owner) */ +static inline bool IsTileOwner(TileIndex tile, Owner owner) { return IsTileOwner(tile, owner); } +/** @copydoc IsTileOwner(TileIndexT::T,Owner) */ +static inline bool IsTileOwner(GenericTileIndex tile, Owner owner) { return IsTileOwner(tile, owner); } /** * Set the tropic zone @@ -219,7 +275,7 @@ static inline void SetTropicZone(TileIndex tile, TropicZone type) { assert(tile < MapSize()); assert(!IsTileType(tile, MP_VOID) || type == TROPICZONE_NORMAL); - SB(_m[tile].type, 0, 2, type); + SB(GetTile(tile)->type, 0, 2, type); } /** @@ -231,7 +287,7 @@ static inline void SetTropicZone(TileIndex tile, TropicZone type) static inline TropicZone GetTropicZone(TileIndex tile) { assert(tile < MapSize()); - return (TropicZone)GB(_m[tile].type, 0, 2); + return (TropicZone)GB(GetTile(tile)->type, 0, 2); } /** @@ -243,7 +299,7 @@ static inline TropicZone GetTropicZone(TileIndex tile) static inline byte GetAnimationFrame(TileIndex t) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); - return _me[t].m7; + return GetTileEx(t)->m7; } /** @@ -255,12 +311,29 @@ static inline byte GetAnimationFrame(TileIndex t) static inline void SetAnimationFrame(TileIndex t, byte frame) { assert(IsTileType(t, MP_HOUSE) || IsTileType(t, MP_OBJECT) || IsTileType(t, MP_INDUSTRY) ||IsTileType(t, MP_STATION)); - _me[t].m7 = frame; + GetTileEx(t)->m7 = frame; } -Slope GetTileSlope(TileIndex tile, int *h = NULL); -int GetTileZ(TileIndex tile); -int GetTileMaxZ(TileIndex tile); +template +Slope GetTileSlope(typename TileIndexT::T tile, int *h = NULL); +/** @copydoc GetTileSlope(TileIndexT::T,int*) */ +static inline Slope GetTileSlope(TileIndex tile, int *h = NULL) { return GetTileSlope(tile, h); } +/** @copydoc GetTileSlope(TileIndexT::T,int*) */ +static inline Slope GetTileSlope(GenericTileIndex tile, int *h = NULL) { return GetTileSlope(tile, h); } + +template +int GetTileZ(typename TileIndexT::T tile); +/** @copydoc GetTileZ(TileIndexT::T) */ +static inline int GetTileZ(TileIndex tile) { return GetTileZ(tile); } +/** @copydoc GetTileZ(TileIndexT::T) */ +static inline int GetTileZ(GenericTileIndex tile) { return GetTileZ(tile); } + +template +int GetTileMaxZ(typename TileIndexT::T tile); +/** @copydoc GetTileMaxZ(TileIndexT::T) */ +static inline int GetTileMaxZ(TileIndex tile) { return GetTileMaxZ(tile); } +/** @copydoc GetTileMaxZ(TileIndexT::T) */ +static inline int GetTileMaxZ(GenericTileIndex tile) { return GetTileMaxZ(tile); } bool IsTileFlat(TileIndex tile, int *h = NULL); @@ -270,12 +343,17 @@ bool IsTileFlat(TileIndex tile, int *h = NULL); * @param h If not \c NULL, pointer to storage of z height * @return Slope of the tile, except for the HALFTILE part */ -static inline Slope GetTilePixelSlope(TileIndex tile, int *h) +template +static inline Slope GetTilePixelSlope(typename TileIndexT::T tile, int *h) { Slope s = GetTileSlope(tile, h); if (h != NULL) *h *= TILE_HEIGHT; return s; } +/** @copydoc GetTilePixelSlope(TileIndexT::T,int*) */ +static inline Slope GetTilePixelSlope(TileIndex tile, int *h) { return GetTilePixelSlope(tile, h); } +/** @copydoc GetTilePixelSlope(TileIndexT::T,int*) */ +static inline Slope GetTilePixelSlope(GenericTileIndex tile, int *h) { return GetTilePixelSlope(tile, h); } Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h); @@ -284,10 +362,15 @@ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h); * @param tile Tile to compute height of * @return Minimum height of the tile */ -static inline int GetTilePixelZ(TileIndex tile) +template +static inline int GetTilePixelZ(typename TileIndexT::T tile) { return GetTileZ(tile) * TILE_HEIGHT; } +/** @copydoc GetTilePixelZ(TileIndexT::T) */ +static inline int GetTilePixelZ(TileIndex tile) { return GetTilePixelZ(tile); } +/** @copydoc GetTilePixelZ(TileIndexT::T) */ +static inline int GetTilePixelZ(GenericTileIndex tile) { return GetTilePixelZ(tile); } int GetTilePixelZOutsideMap(int x, int y); @@ -296,10 +379,15 @@ int GetTilePixelZOutsideMap(int x, int y); * @param t Tile to compute height of * @return Maximum height of the tile */ -static inline int GetTileMaxPixelZ(TileIndex tile) +template +static inline int GetTileMaxPixelZ(typename TileIndexT::T tile) { return GetTileMaxZ(tile) * TILE_HEIGHT; } +/** @copydoc GetTileMaxPixelZ(TileIndexT::T) */ +static inline int GetTileMaxPixelZ(TileIndex tile) { return GetTileMaxPixelZ(tile); } +/** @copydoc GetTileMaxPixelZ(TileIndexT::T) */ +static inline int GetTileMaxPixelZ(GenericTileIndex tile) { return GetTileMaxPixelZ(tile); } int GetTileMaxPixelZOutsideMap(int x, int y); diff --git a/src/tile_type.h b/src/tile_type.h index 0d720925d..b258fcea9 100644 --- a/src/tile_type.h +++ b/src/tile_type.h @@ -12,6 +12,8 @@ #ifndef TILE_TYPE_H #define TILE_TYPE_H +#include "map_type.h" + static const uint TILE_SIZE = 16; ///< Tile size in world coordinates. static const uint TILE_UNIT_MASK = TILE_SIZE - 1; ///< For masking in/out the inner-tile world coordinate units. static const uint TILE_PIXELS = 32; ///< Pixel distance between tile columns/rows in #ZOOM_LVL_BASE. @@ -72,14 +74,91 @@ enum TropicZone { TROPICZONE_RAINFOREST = 2, ///< Rainforest tile }; +typedef uint32 RawTileIndex; ///< general purpose tile index, not bounded to any map +static const RawTileIndex INVALID_TILE_INDEX = (RawTileIndex)-1; + /** - * The index/ID of a Tile. + * The index/ID of a Tile on the main map. + * + * While this is just another name for RawTileIndex type, it should be used + * in context of tiles of the main tile array. */ -typedef uint32 TileIndex; +typedef RawTileIndex TileIndex; /** * The very nice invalid tile marker */ static const TileIndex INVALID_TILE = (TileIndex)-1; +/** The index/ID of a tile bounded to a given map. */ +struct GenericTileIndex { + RawTileIndex index; ///< position of the tile in array + Map *map; ///< the map that this index is bounded to + + inline GenericTileIndex() : map(NULL) { } + inline GenericTileIndex(const GenericTileIndex &tile) : index(tile.index), map(tile.map) { } + inline GenericTileIndex(RawTileIndex index, Map *map) : index(index), map(map) { } + + inline explicit GenericTileIndex(const TileIndex &tile) : index(tile) + { + extern MainMap _main_map; + this->map = &_main_map; + } + + inline GenericTileIndex &operator += (TileIndexDiff diff) { return this->index += diff, *this; } + inline GenericTileIndex &operator -= (TileIndexDiff diff) { return this->index -= diff, *this; } + inline GenericTileIndex operator + (TileIndexDiff diff) const { return GenericTileIndex(this->index + diff, this->map); } + inline GenericTileIndex operator - (TileIndexDiff diff) const { return GenericTileIndex(this->index - diff, this->map); } + + inline GenericTileIndex &operator ++ () { return ++this->index, *this; } + inline GenericTileIndex &operator -- () { return --this->index, *this; } + inline GenericTileIndex operator ++ (int) { return GenericTileIndex(this->index++, this->map); } + inline GenericTileIndex operator -- (int) { return GenericTileIndex(this->index--, this->map); } + + inline bool operator == (const GenericTileIndex &tile) const { return this->index == tile.index && this->map == tile.map; } + inline bool operator != (const GenericTileIndex &tile) const { return this->index != tile.index || this->map != tile.map; } + + inline bool operator <= (const GenericTileIndex &tile) const + { + assert(this->map == tile.map); + return this->index <= tile.index; + } + + inline bool operator >= (const GenericTileIndex &tile) const + { + assert(this->map == tile.map); + return this->index >= tile.index; + } + + inline bool operator < (const GenericTileIndex &tile) const + { + assert(this->map == tile.map); + return this->index < tile.index; + } + + inline bool operator > (const GenericTileIndex &tile) const + { + assert(this->map == tile.map); + return this->index > tile.index; + } + +}; + +/** + * Helper class to construct templatized functions operating on different + * types of tile indices. + */ +template +struct TileIndexT; + +template <> +struct TileIndexT { + typedef TileIndex T; +}; + +template <> +struct TileIndexT { + typedef GenericTileIndex T; +}; + #endif /* TILE_TYPE_H */ diff --git a/src/tilearea.cpp b/src/tilearea.cpp index ec3b9aafb..f46759d3a 100644 --- a/src/tilearea.cpp +++ b/src/tilearea.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" +#include "core/geometry_func.hpp" #include "tilearea_type.h" #include "safeguards.h" @@ -20,10 +21,12 @@ * @param start the start of the area * @param end the end of the area */ -OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) +template +OrthogonalTileAreaT::OrthogonalTileAreaT(TileIndexType start, TileIndexType end) { - assert(start < MapSize()); - assert(end < MapSize()); + assert(IsSameMap(start, end)); + assert(IsValidTileIndex(start)); + assert(IsValidTileIndex(end)); uint sx = TileX(start); uint sy = TileY(start); @@ -33,7 +36,7 @@ OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) if (sx > ex) Swap(sx, ex); if (sy > ey) Swap(sy, ey); - this->tile = TileXY(sx, sy); + this->tile = TileXY(sx, sy, MapOf(start)); this->w = ex - sx + 1; this->h = ey - sy + 1; } @@ -42,9 +45,10 @@ OrthogonalTileArea::OrthogonalTileArea(TileIndex start, TileIndex end) * Add a single tile to a tile area; enlarge if needed. * @param to_add The tile to add */ -void OrthogonalTileArea::Add(TileIndex to_add) +template +void OrthogonalTileAreaT::Add(TileIndexType to_add) { - if (this->tile == INVALID_TILE) { + if (!IsValidTileIndex(this->tile)) { this->tile = to_add; this->w = 1; this->h = 1; @@ -64,7 +68,7 @@ void OrthogonalTileArea::Add(TileIndex to_add) ex = max(ax, ex); ey = max(ay, ey); - this->tile = TileXY(sx, sy); + this->tile = TileXY(sx, sy, MapOf(to_add)); this->w = ex - sx + 1; this->h = ey - sy + 1; } @@ -74,11 +78,13 @@ void OrthogonalTileArea::Add(TileIndex to_add) * @param ta the other tile area to check against. * @return true if they intersect. */ -bool OrthogonalTileArea::Intersects(const OrthogonalTileArea &ta) const +template +bool OrthogonalTileAreaT::Intersects(const OrthogonalTileAreaT &ta) const { if (ta.w == 0 || this->w == 0) return false; assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0); + assert(IsSameMap(this->tile, ta.tile)); uint left1 = TileX(this->tile); uint top1 = TileY(this->tile); @@ -99,15 +105,47 @@ bool OrthogonalTileArea::Intersects(const OrthogonalTileArea &ta) const } /** + * Does this tile area contains another? + * @param ta the other tile area to check against. + * @return true if the other area is fully contained. + */ +template +bool OrthogonalTileAreaT::Contains(const OrthogonalTileAreaT &ta) const +{ + if (ta.w == 0 || this->w == 0) return false; + + assert(ta.w != 0 && ta.h != 0 && this->w != 0 && this->h != 0); + assert(IsSameMap(this->tile, ta.tile)); + + uint left1 = TileX(this->tile); + uint top1 = TileY(this->tile); + uint right1 = left1 + this->w - 1; + uint bottom1 = top1 + this->h - 1; + + uint left2 = TileX(ta.tile); + uint top2 = TileY(ta.tile); + uint right2 = left2 + ta.w - 1; + uint bottom2 = top2 + ta.h - 1; + + return + left2 >= left1 && + right2 <= right1 && + top2 >= top1 && + bottom2 <= bottom1; +} + +/** * Does this tile area contain a tile? * @param tile Tile to test for. * @return True if the tile is inside the area. */ -bool OrthogonalTileArea::Contains(TileIndex tile) const +template +bool OrthogonalTileAreaT::Contains(TileIndexType tile) const { if (this->w == 0) return false; assert(this->w != 0 && this->h != 0); + assert(IsSameMap(this->tile, tile)); uint left = TileX(this->tile); uint top = TileY(this->tile); @@ -120,11 +158,62 @@ bool OrthogonalTileArea::Contains(TileIndex tile) const /** * Clamp the tile area to map borders. */ -void OrthogonalTileArea::ClampToMap() +template +void OrthogonalTileAreaT::ClampToMap() { - assert(this->tile < MapSize()); - this->w = min(this->w, MapSizeX() - TileX(this->tile)); - this->h = min(this->h, MapSizeY() - TileY(this->tile)); + assert(IsValidTileIndex(this->tile)); + this->w = min(this->w, MapSizeX(MapOf(this->tile)) - TileX(this->tile)); + this->h = min(this->h, MapSizeY(MapOf(this->tile)) - TileY(this->tile)); +} + +/** + * Get coordinates of transformed nothern tile of this area relative to the + * northern tile of transformed area. + * + * When transforming this area into another, the northern tile becomes some other + * tile in the transformed area. The function returns coordinates of this other tile + * relative to the transformed area. + * + * Note that calculation are independent from desired position of the transformed area. + * + * @param transformation transformation to perform + * @return the distance + */ +template +TileIndexDiffC OrthogonalTileAreaT::TransformedNorthOffset(DirTransformation transformation) const +{ + Dimension distance = { this->w - 1, this->h - 1 }; + distance = TransformDimension(distance, transformation); + TileIndexDiffC ret = TransformedNorthCornerDiffC(transformation); + ret.x *= distance.width; + ret.y *= distance.height; + return ret; +} + +/** + * Get coordinates of a transformed tile of this area relative to the transformed + * northern tile of this area. + * + * The function takes x/y coordinates of a tile relative to this area and performs + * a transformation on them. + * + * Note that calculation are independent from desired position of the transformed area. + * + * @param tile the tile to transform + * @param transformation transformation to perform + * @return the distance + */ +template +TileIndexDiffC OrthogonalTileAreaT::TransformedTileOffset(TileIndexType tile, DirTransformation transformation) const +{ + assert(IsSameMap(this->tile, tile)); + + /* calculate coordinates of the tile relative to the northern tile of the area */ + Point coords = { TileX(tile) - TileX(this->tile), TileY(tile) - TileY(this->tile) }; + /* transform coordinates, now they will be relative to the transformed northern tile of the area */ + coords = TransformPoint(coords, transformation); + TileIndexDiffC ret = { (uint16)coords.x, (uint16)coords.y }; + return ret; } /** @@ -132,10 +221,12 @@ void OrthogonalTileArea::ClampToMap() * @param start First corner of the area. * @param end Second corner of the area. */ -DiagonalTileArea::DiagonalTileArea(TileIndex start, TileIndex end) : tile(start) +template +DiagonalTileAreaT::DiagonalTileAreaT(TileIndexType start, TileIndexType end) : tile(start) { - assert(start < MapSize()); - assert(end < MapSize()); + assert(IsSameMap(start, end)); + assert(IsValidTileIndex(start)); + assert(IsValidTileIndex(end)); /* Unfortunately we can't find a new base and make all a and b positive because * the new base might be a "flattened" corner where there actually is no single @@ -165,8 +256,11 @@ DiagonalTileArea::DiagonalTileArea(TileIndex start, TileIndex end) : tile(start) * @param tile Tile to test for. * @return True if the tile is inside the area. */ -bool DiagonalTileArea::Contains(TileIndex tile) const +template +bool DiagonalTileAreaT::Contains(TileIndexType tile) const { + assert(IsSameMap(this->tile, tile)); + int a = TileY(tile) + TileX(tile); int b = TileY(tile) - TileX(tile); @@ -192,11 +286,13 @@ bool DiagonalTileArea::Contains(TileIndex tile) const } /** - * Move ourselves to the next tile in the rectangle on the map. + * Perform single iteration step. + * @param my_index Pointer to the tile index of the iterator. + * @param my_map The map that iterator iterates through. */ -TileIterator &DiagonalTileIterator::operator++() +void DiagonalTileIteratorController::Advance(RawTileIndex *my_index, Map *my_map) { - assert(this->tile != INVALID_TILE); + assert(*my_index != INVALID_TILE_INDEX); /* Determine the next tile, while clipping at map borders */ bool new_line = false; @@ -237,9 +333,55 @@ TileIterator &DiagonalTileIterator::operator++() uint x = this->base_x + (this->a_cur - this->b_cur) / 2; uint y = this->base_y + (this->b_cur + this->a_cur) / 2; /* Prevent wrapping around the map's borders. */ - this->tile = x >= MapSizeX() || y >= MapSizeY() ? INVALID_TILE : TileXY(x, y); - } while (this->tile > MapSize() && this->b_max != this->b_cur); + *my_index = x >= MapSizeX(my_map) || y >= MapSizeY(my_map) ? INVALID_TILE_INDEX : TileXY(x, y, my_map).index; + } while (!IsValidTileIndex(GenericTileIndex(*my_index, my_map)) && this->b_max != this->b_cur); + + if (this->b_max == this->b_cur) *my_index = INVALID_TILE_INDEX; +} + +/** + * Initialize iteration. + * @param src_index Pointer to the source tile index of the iterator. The index must be set to the northern tile of the source area before you call Init. + * @param dst_index Pointer to the destination tile index of the iterator. The index must be set to the transformed northern tile of the source area before you call Init. + * @param src_w The width of the source area. + * @param src_h The height of the source area. + * @param transformation Transformation to perform. + */ +void TransformationTileIteratorController::Init(RawTileIndex *src_index, RawTileIndex *dst_index, uint16 src_w, uint16 src_h, DirTransformation transformation) +{ + assert((*src_index != INVALID_TILE_INDEX) == (*dst_index != INVALID_TILE_INDEX)); - if (this->b_max == this->b_cur) this->tile = INVALID_TILE; - return *this; + this->OrthogonalTileIteratorController::Init(src_index, src_w, src_h); + this->transformation = transformation; } + +/** + * Perform single iteration step. + * @param src_index Pointer to the source tile index of the iterator. + * @param src_map The source map that iterator iterates through. + * @param dst_index Pointer to the destination tile index of the iterator. + * @param dst_map The destination map that iterator iterates through. + */ +void TransformationTileIteratorController::Advance(RawTileIndex *src_index, Map *src_map, RawTileIndex *dst_index, Map *dst_map) +{ + assert(*src_index != INVALID_TILE_INDEX); + + if (--this->x > 0) { + ++*src_index; + *dst_index += ToTileIndexDiff(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, this->transformation)), dst_map); + } else if (--this->y > 0) { + this->x = this->w; + *src_index += TileDiffXY(1, 1, src_map) - this->w; + *dst_index -= ToTileIndexDiff(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SW, this->transformation)), dst_map) * (this->w - 1); + *dst_index += ToTileIndexDiff(TileIndexDiffCByDiagDir(TransformDiagDir(DIAGDIR_SE, this->transformation)), dst_map); + } else { + *src_index = INVALID_TILE_INDEX; + *dst_index = INVALID_TILE_INDEX; + } +} + +/* instantiate */ +template struct OrthogonalTileAreaT; +template struct OrthogonalTileAreaT; +template struct DiagonalTileAreaT; +template struct DiagonalTileAreaT; diff --git a/src/tilearea_func.h b/src/tilearea_func.h new file mode 100644 index 000000000..84cfd77fa --- /dev/null +++ b/src/tilearea_func.h @@ -0,0 +1,42 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file tilearea_type.h Functions related to tile areas. */ + +#ifndef TILEAREA_FUNC_H +#define TILEAREA_FUNC_H + +#include "core/math_func.hpp" +#include "direction_func.h" +#include "tilearea_type.h" + +/** + * Transform a tile area. + * @param trackdir The area to transform. + * @param transformation Transformation to perform. + * @return The transformed area. + */ +template +static OrthogonalTileAreaT TransformTileArea(const OrthogonalTileAreaT &ta, typename TileIndexT::T dst_area_north, DirTransformation transformation) +{ + OrthogonalTileAreaT ret(dst_area_north, ta.w, ta.h); + if (TransformAxis(AXIS_X, transformation) != AXIS_X) Swap(ret.w, ret.h); + return ret; +} + +/** @copydoc TransformTileArea */ +static inline TileArea TransformTileArea(const TileArea &ta, TileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea(ta, dst_area_north, transformation); } +/** @copydoc TransformTileArea */ +static inline GenericTileArea TransformTileArea(const TileArea &ta, GenericTileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea(ta, dst_area_north, transformation); } +/** @copydoc TransformTileArea */ +static inline TileArea TransformTileArea(const GenericTileArea &ta, TileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea(ta, dst_area_north, transformation); } +/** @copydoc TransformTileArea */ +static inline GenericTileArea TransformTileArea(const GenericTileArea &ta, GenericTileIndex dst_area_north, DirTransformation transformation) { return TransformTileArea(ta, dst_area_north, transformation); } + +#endif /* TILEAREA_FUNC_H */ diff --git a/src/tilearea_type.h b/src/tilearea_type.h index 45bfb3d4c..114d10a15 100644 --- a/src/tilearea_type.h +++ b/src/tilearea_type.h @@ -9,16 +9,55 @@ /** @file tilearea_type.h Type for storing the 'area' of something uses on the map. */ +/** + * @defgroup TileIndexTransformations Tile index transformations + * + * @image html tile_index_transformations.svg + */ + #ifndef TILEAREA_TYPE_H #define TILEAREA_TYPE_H #include "map_func.h" +#include "direction_type.h" -/** Represents the covered area of e.g. a rail station */ -struct OrthogonalTileArea { - TileIndex tile; ///< The base tile of the area - uint16 w; ///< The width of the area - uint16 h; ///< The height of the area +/** + * Set of coordinates representing rectangular piece of a tile map. + * + * This "raw" area does not point to any map. These are pure coordinates. Unless + * we bound them to a cartain map we cannot make most of calculations. + * + * @see RawTileIndex + * @see OrthogonalTileAreaT + */ +struct RawTileArea { + RawTileIndex tile; ///< The base (northern) tile of the area + uint16 w; ///< The width of the area + uint16 h; ///< The height of the area +}; + +/** + * Set of coordinates representing rectangular piece of a tile map e.g. a rail station. + * + * This tile area, based on template args, can represent a part of either the main map + * or any chosen map. + * + * @tparam Tgeneric If \c false, this area will represent tiles on the main map. + * If \c true, this area will be able to represent tiles on any map chosen at runtime. + * + * @note To use a specific overload there are #TileArea and #GenericTileArea to your + * disposition. Use OrthogonalTileAreaT type directly only when working with templates. + * + * @see RawOrthogonalTileArea + * @see TileIndexT + */ +template +struct OrthogonalTileAreaT { + typedef typename TileIndexT::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. + + TileIndexType tile; ///< The base tile of the area + uint16 w; ///< The width of the area + uint16 h; ///< The height of the area /** * Construct this tile area with some set values @@ -26,27 +65,48 @@ struct OrthogonalTileArea { * @param w the width * @param h the height */ - OrthogonalTileArea(TileIndex tile = INVALID_TILE, uint8 w = 0, uint8 h = 0) : tile(tile), w(w), h(h) + OrthogonalTileAreaT(TileIndexType tile = TileIndexType(INVALID_TILE), uint8 w = 0, uint8 h = 0) : tile(tile), w(w), h(h) + { + } + + /** + * Make a copy of a given tile area + * @param ta the area to copy + */ + template + OrthogonalTileAreaT(const OrthogonalTileAreaT &ta) + : tile(MakeTileIndex(IndexOf(ta.tile), MapOf(ta.tile))), w(ta.w), h(ta.h) + { + } + + /** + * Construct this tile area from a "raw" tile area and a given tile map + * @param ta the "raw" tile area + * @param map the map + */ + inline OrthogonalTileAreaT(const RawTileArea &ta, Map *map) + : tile(MakeTileIndex(ta.tile, map)), w(ta.w), h(ta.h) { } - OrthogonalTileArea(TileIndex start, TileIndex end); + OrthogonalTileAreaT(TileIndexType start, TileIndexType end); - void Add(TileIndex to_add); + void Add(TileIndexType to_add); /** * Clears the 'tile area', i.e. make the tile invalid. */ void Clear() { - this->tile = INVALID_TILE; - this->w = 0; - this->h = 0; + IndexOf(this->tile) = INVALID_TILE_INDEX; + this->w = 0; + this->h = 0; } - bool Intersects(const OrthogonalTileArea &ta) const; + bool Intersects(const OrthogonalTileAreaT &ta) const; - bool Contains(TileIndex tile) const; + bool Contains(const OrthogonalTileAreaT &ta) const; + bool Contains(TileIndexType tile) const; void ClampToMap(); @@ -54,16 +114,111 @@ struct OrthogonalTileArea { * Get the center tile. * @return The tile at the center, or just north of it. */ - TileIndex GetCenterTile() const + TileIndexType GetCenterTile() const { return TILE_ADDXY(this->tile, this->w / 2, this->h / 2); } + + TileIndexDiffC TransformedNorthOffset(DirTransformation transformation) const; + TileIndexDiffC TransformedTileOffset(TileIndexType tile, DirTransformation transformation) const; + + /** + * Transform northern tile of this area based on a given northern tile of transformed area. + * + * @param dst_area_north Point of reference, the northern tile of the transformed area. + * @param transformation Transformation to perform. + * @return Northern tile of this area after transformation. + * + * @see TileIndexTransformations + * + * @ingroup TileIndexTransformations + */ + template + typename TileIndexT::T TransformedNorth(typename TileIndexT::T dst_area_north, DirTransformation transformation) const + { + TileIndexDiffC offs = this->TransformedNorthOffset(transformation); + return TILE_ADDXY(dst_area_north, offs.x, offs.y); + } + /** @copydoc OrthogonalTileAreaT::TransformedNorth(TileIndexT::T,DirTransformation) */ + inline TileIndex TransformedNorth(TileIndex dst_area_north, DirTransformation transformation) const { return this->TransformedNorth(dst_area_north, transformation); } + /** @copydoc OrthogonalTileAreaT::TransformedNorth(TileIndexT::T,DirTransformation) */ + inline GenericTileIndex TransformedNorth(GenericTileIndex dst_area_north, DirTransformation transformation) const { return this->TransformedNorth(dst_area_north, transformation); } + + /** + * Calculate northern tile of transformed area based on transformed northern tile of this area. + * + * @param transformed_north Point of reference, northern tile of this area after transformation. + * @param transformation The transformation. + * @return Northern tile of the transformed area. + * + * @see TileIndexTransformations + * + * @ingroup TileIndexTransformations + */ + template + typename TileIndexT::T ReverseTransformedNorth(typename TileIndexT::T transformed_north, DirTransformation transformation) const + { + TileIndexDiffC offs = this->TransformedNorthOffset(transformation); + return TILE_ADDXY(transformed_north, -offs.x, -offs.y); + } + /** @copydoc OrthogonalTileAreaT::ReverseTransformedNorth(TileIndexT::T,DirTransformation) */ + inline TileIndex ReverseTransformedNorth(TileIndex transformed_north, DirTransformation transformation) const { return this->ReverseTransformedNorth(transformed_north, transformation); } + /** @copydoc OrthogonalTileAreaT::ReverseTransformedNorth(TileIndexT::T,DirTransformation) */ + inline GenericTileIndex ReverseTransformedNorth(GenericTileIndex transformed_north, DirTransformation transformation) const { return this->ReverseTransformedNorth(transformed_north, transformation); } + + /** + * Transform a given tile within this area. + * + * @param tile The tile to transform. + * @param transformed_north Point of reference, northern tile of this area after transformation. + * @param transformation Transformation to perform. + * @return Transformed tile. + * + * @see TileIndexTransformations + * + * @ingroup TileIndexTransformations + */ + template + typename TileIndexT::T TransformTile(TileIndexType tile, typename TileIndexT::T transformed_north, DirTransformation transformation) const + { + TileIndexDiffC offs = this->TransformedTileOffset(tile, transformation); + return TILE_ADDXY(transformed_north, offs.x, offs.y); + } + /** @copydoc OrthogonalTileAreaT::TransformTile(TileIndexType,TileIndexT::T,DirTransformation) */ + inline TileIndex TransformTile(TileIndexType tile, TileIndex transformed_north, DirTransformation transformation) const { return this->TransformTile(tile, transformed_north, transformation); } + /** @copydoc OrthogonalTileAreaT::TransformTile(TileIndexType,TileIndexT::T,DirTransformation) */ + inline GenericTileIndex TransformTile(TileIndexType tile, GenericTileIndex transformed_north, DirTransformation transformation) const { return this->TransformTile(tile, transformed_north, transformation); } + + /** + * Get the point of reference of a transfomation based on a given tile before and after transformation. + * + * @param tile The tile before transformation. + * @param transformed_tile The tile after transformation. + * @param transformation The transformation. + * @return The point of reference (northern tile of this area after transformation). + * + * @see TileIndexTransformations + * + * @ingroup TileIndexTransformations + */ + template + typename TileIndexT::T ReverseTransformTile(TileIndexType source_tile, typename TileIndexT::T transformed_tile, DirTransformation transformation) const + { + TileIndexDiffC offs = this->TransformedTileOffset(source_tile, transformation); + return TILE_ADDXY(transformed_tile, -offs.x, -offs.y); + } + /** @copydoc OrthogonalTileAreaT::ReverseTransformTile(TileIndexType,TileIndexT::T,DirTransformation) */ + inline TileIndex ReverseTransformTile(TileIndexType source_tile, TileIndex transformed_tile, DirTransformation transformation) const { return this->ReverseTransformTile(source_tile, transformed_tile, transformation); } + /** @copydoc OrthogonalTileAreaT::ReverseTransformTile(TileIndexType,TileIndexT::T,DirTransformation) */ + inline GenericTileIndex ReverseTransformTile(TileIndexType source_tile, GenericTileIndex transformed_tile, DirTransformation transformation) const { return this->ReverseTransformTile(source_tile, transformed_tile, transformation); } }; /** Represents a diagonal tile area. */ -struct DiagonalTileArea { +template +struct DiagonalTileAreaT { + typedef typename TileIndexT::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. - TileIndex tile; ///< Base tile of the area + TileIndexType tile; ///< Base tile of the area int16 a; ///< Extent in diagonal "x" direction (may be negative to signify the area stretches to the left) int16 b; ///< Extent in diagonal "y" direction (may be negative to signify the area stretches upwards) @@ -73,52 +228,96 @@ struct DiagonalTileArea { * @param a The "x" extent. * @param b The "y" estent. */ - DiagonalTileArea(TileIndex tile = INVALID_TILE, int8 a = 0, int8 b = 0) : tile(tile), a(a), b(b) + DiagonalTileAreaT(TileIndexType tile = TileIndexType(INVALID_TILE), int8 a = 0, int8 b = 0) : tile(tile), a(a), b(b) { } - DiagonalTileArea(TileIndex start, TileIndex end); + DiagonalTileAreaT(TileIndexType start, TileIndexType end); /** * Clears the TileArea by making the tile invalid and setting a and b to 0. */ void Clear() { - this->tile = INVALID_TILE; - this->a = 0; - this->b = 0; + IndexOf(this->tile) = INVALID_TILE_INDEX; + this->a = 0; + this->b = 0; } - bool Contains(TileIndex tile) const; + bool Contains(TileIndexType tile) const; }; +/** + * Set of coordinates representing rectangular piece of the main tile map. + * + * This is the most common type of tile area. It represents tiles on the main tile array. + * + * @see TileIndex + * @see GenericTileArea + */ +typedef OrthogonalTileAreaT OrthogonalTileArea; + /** Shorthand for the much more common orthogonal tile area. */ typedef OrthogonalTileArea TileArea; +/** + * Set of coordinates representing rectangular piece of a tile map. + * + * This "generic" tile area is able to represent part of any map chosen at runtime. + * + * @see GenericTileIndex + * @see TileArea + */ +typedef OrthogonalTileAreaT GenericTileArea; + +typedef DiagonalTileAreaT DiagonalTileArea; + /** Base class for tile iterators. */ -class TileIterator { +template +class TileIteratorT { +public: + typedef typename TileIndexT::T TileIndexType; ///< The type of tile indices, TileIndex or GenericTileIndex. + protected: - TileIndex tile; ///< The current tile we are at. + TileIndexType tile; ///< The current tile we are at. /** * Initialise the iterator starting at this tile. * @param tile The tile we start iterating from. */ - TileIterator(TileIndex tile = INVALID_TILE) : tile(tile) + TileIteratorT(TileIndexType tile = TileIndexType(INVALID_TILE)) : tile(tile) { } + /** + * Get the raw tile index of this iterator. + * @return Pointer to the index. + */ + inline RawTileIndex *MyIndex() + { + return &IndexOf(this->tile); + } + + /** + * Get the map of this iterator. + * @return The map that this iterator iterates through. + */ + inline Map *MyMap() const + { + return MapOf(this->tile); + } + public: /** Some compilers really like this. */ - virtual ~TileIterator() + virtual ~TileIteratorT() { } /** * Get the tile we are currently at. - * @return The tile we are at, or INVALID_TILE when we're done. + * @return The tile we are at, or "invalid" when we're done. */ - inline operator TileIndex () const + inline operator TileIndexType () const { return this->tile; } @@ -126,28 +325,71 @@ public: /** * Move ourselves to the next tile in the rectangle on the map. */ - virtual TileIterator& operator ++() = 0; + virtual TileIteratorT& operator ++() = 0; /** * Allocate a new iterator that is a copy of this one. */ - virtual TileIterator *Clone() const = 0; + virtual TileIteratorT *Clone() const = 0; }; -/** Iterator to iterate over a tile area (rectangle) of the map. */ -class OrthogonalTileIterator : public TileIterator { -private: - int w; ///< The width of the iterated area. - int x; ///< The current 'x' position in the rectangle. - int y; ///< The current 'y' position in the rectangle. +/** Base class for tile iterators of the main map. */ +typedef TileIteratorT TileIterator; + +/** Helper class to build diagonal tile iterators. */ +class OrthogonalTileIteratorController { +public: + int w; ///< The width of the iterated area. + int x; ///< The current 'x' position in the rectangle. + int y; ///< The current 'y' position in the rectangle. + + /** + * Initialize iteration. + * @param my_index Pointer to the tile index of the iterator. The index must be set to the first tile of the iteration before you call Init. + * @param w The width of the iterated area. + * @param h The height of the iterated area. + */ + void Init(RawTileIndex *my_index, uint w, uint h) + { + this->w = w; + this->x = w; + this->y = h; + if (w == 0 || h == 0) *my_index = INVALID_TILE_INDEX; + } + + /** + * Perform single iteration step. + * @param my_index Pointer to the tile index of the iterator. + * @param my_map The map that iterator iterates through. + */ + inline void Advance(RawTileIndex *my_index, Map *my_map) + { + assert(*my_index != INVALID_TILE_INDEX); + if (--this->x > 0) { + ++*my_index; + } else if (--this->y > 0) { + this->x = this->w; + *my_index += TileDiffXY(1, 1, my_map) - this->w; + } else { + *my_index = INVALID_TILE_INDEX; + } + } +}; + +/** Iterator to iterate over a tile area (rectangle) of a map. */ +template +class OrthogonalTileIteratorT : public TileIteratorT, protected OrthogonalTileIteratorController { public: + typedef typename TileIteratorT::TileIndexType TileIndexType; + /** * Construct the iterator. * @param ta Area, i.e. begin point and width/height of to-be-iterated area. */ - OrthogonalTileIterator(const OrthogonalTileArea &ta) : TileIterator(ta.w == 0 || ta.h == 0 ? INVALID_TILE : ta.tile), w(ta.w), x(ta.w), y(ta.h) + OrthogonalTileIteratorT(const OrthogonalTileAreaT &ta) : TileIteratorT(ta.tile) { + this->Init(this->MyIndex(), ta.w, ta.h); } /** @@ -155,38 +397,32 @@ public: * @param corner1 Tile from where to begin iterating. * @param corner2 Tile where to end the iterating. */ - OrthogonalTileIterator(TileIndex corner1, TileIndex corner2) + OrthogonalTileIteratorT(TileIndexType corner1, TileIndexType corner2) { - *this = OrthogonalTileIterator(OrthogonalTileArea(corner1, corner2)); + *this = OrthogonalTileIteratorT(OrthogonalTileAreaT(corner1, corner2)); } /** * Move ourselves to the next tile in the rectangle on the map. */ - inline TileIterator& operator ++() + inline TileIteratorT& operator ++() { - assert(this->tile != INVALID_TILE); - - if (--this->x > 0) { - this->tile++; - } else if (--this->y > 0) { - this->x = this->w; - this->tile += TileDiffXY(1, 1) - this->w; - } else { - this->tile = INVALID_TILE; - } + this->Advance(this->MyIndex(), this->MyMap()); return *this; } - virtual TileIterator *Clone() const + virtual TileIteratorT *Clone() const { - return new OrthogonalTileIterator(*this); + return new OrthogonalTileIteratorT(*this); } }; -/** Iterator to iterate over a diagonal area of the map. */ -class DiagonalTileIterator : public TileIterator { -private: +/** Iterator to iterate over a tile area (rectangle) of the main map. */ +typedef OrthogonalTileIteratorT OrthogonalTileIterator; + +/** Helper class to build diagonal tile iterators. */ +class DiagonalTileIteratorController { +public: uint base_x; ///< The base tile x coordinate from where the iterating happens. uint base_y; ///< The base tile y coordinate from where the iterating happens. int a_cur; ///< The current (rotated) x coordinate of the iteration. @@ -194,15 +430,32 @@ private: int a_max; ///< The (rotated) x coordinate of the end of the iteration. int b_max; ///< The (rotated) y coordinate of the end of the iteration. + void Init(RawTileIndex *my_index, int a_max, int b_max, Map *my_map) + { + this->base_x = TileX(GenericTileIndex(*my_index, my_map)); + this->base_y = TileY(GenericTileIndex(*my_index, my_map)); + this->a_cur = 0; + this->b_cur = 0; + this->a_max = a_max; + this->b_max = b_max; + } + + void Advance(RawTileIndex *my_index, Map *my_map); +}; + +/** Iterator to iterate over a diagonal area of a map. */ +template +class DiagonalTileIteratorT : public TileIteratorT, protected DiagonalTileIteratorController { public: + typedef typename TileIteratorT::TileIndexType TileIndexType; /** * Construct the iterator. * @param ta Area, i.e. begin point and (diagonal) width/height of to-be-iterated area. */ - DiagonalTileIterator(const DiagonalTileArea &ta) : - TileIterator(ta.tile), base_x(TileX(ta.tile)), base_y(TileY(ta.tile)), a_cur(0), b_cur(0), a_max(ta.a), b_max(ta.b) + DiagonalTileIteratorT(const DiagonalTileAreaT &ta) : TileIteratorT(ta.tile) { + this->Init(this->MyIndex(), ta.a, ta.b, this->MyMap()); } /** @@ -210,19 +463,94 @@ public: * @param corner1 Tile from where to begin iterating. * @param corner2 Tile where to end the iterating. */ - DiagonalTileIterator(TileIndex corner1, TileIndex corner2) + DiagonalTileIteratorT(TileIndexType corner1, TileIndexType corner2) { - *this = DiagonalTileIterator(DiagonalTileArea(corner1, corner2)); + *this = DiagonalTileIteratorT(DiagonalTileAreaT(corner1, corner2)); } - TileIterator& operator ++(); + inline TileIteratorT& operator ++() + { + this->Advance(this->MyIndex(), this->MyMap()); + return *this; + } - virtual TileIterator *Clone() const + virtual TileIteratorT *Clone() const { - return new DiagonalTileIterator(*this); + return new DiagonalTileIteratorT(*this); } }; +/** Iterator to iterate over a diagonal area of the main map. */ +typedef DiagonalTileIteratorT DiagonalTileIterator; + +/** Helper class to build transformative tile iterators. */ +class TransformationTileIteratorController : public OrthogonalTileIteratorController { +public: + DirTransformation transformation; ///< Transformation to perform. + + void Init(RawTileIndex *src_index, RawTileIndex *dst_index, uint16 src_w, uint16 src_h, DirTransformation transformation); + void Advance(RawTileIndex *src_index, Map *src_map, RawTileIndex *dst_index, Map *dst_map); +}; + +/** + * Iterator to iterate over a diagonal area of a map performing transformation on tile indices. + * + * Iterator will iterate over source area in the same way OrthogonalTileIteratorT do, additionally + * performing transformation on tile indices. You can call SrcTile or DstTile to get the tile before + * and after transformation. + * + * The tile of this iterator (it's base) is the transformed one. + */ +template +class TransformationTileIteratorT : public TileIteratorT, protected TransformationTileIteratorController { +public: + typedef typename TileIndexT::T DstTileIndexType; + typedef typename TileIndexT::T SrcTileIndexType; + +protected: + SrcTileIndexType src_tile; ///< Current tile of the source area. + +public: + /** + * Create a TransformationTileIteratorT. + * + * @param src_area Source area to be transformed and iterated over. + * @param transformed_north Transformed northern tile of the source area. + * @param transformation Transformation to perform. + */ + TransformationTileIteratorT(const OrthogonalTileAreaT &src_area, DstTileIndexType transformed_north, DirTransformation transformation) + : TileIteratorT(transformed_north), src_tile(src_area.tile) + { + this->Init(&IndexOf(this->src_tile), this->MyIndex(), src_area.w, src_area.h, transformation); + } + + /** + * The source tile of the transformation. + * @return Tile before transformation. + */ + inline const SrcTileIndexType &SrcTile() const { return this->src_tile; } + + /** + * The destination tile of the transformation (the tile of this iterator). + * @return Tile after transformation. + */ + inline const DstTileIndexType &DstTile() const { return this->tile; } + + virtual TileIteratorT &operator ++ () + { + this->Advance(&IndexOf(this->src_tile), MapOf(this->src_tile), this->MyIndex(), this->MyMap()); + return *this; + } + + virtual TileIteratorT *Clone() const + { + return new TransformationTileIteratorT(*this); + } +}; + +/** Iterator to iterate over a diagonal area of the main map performing transformation on tile indices. */ +typedef TransformationTileIteratorT TransformationTileIterator; + /** * A loop which iterates over the tiles of a TileArea. * @param var The name of the variable which contains the current tile. @@ -231,4 +559,12 @@ public: */ #define TILE_AREA_LOOP(var, ta) for (OrthogonalTileIterator var(ta); var != INVALID_TILE; ++var) +/** + * A loop which iterates over the tiles of a GenericTileArea. + * @param var The name of the variable which contains the current tile. + * This variable will be allocated in this \c for of this loop. + * @param ta The tile area to search over. + */ +#define GENERIC_TILE_AREA_LOOP(var, ta) for (OrthogonalTileIteratorT var(ta); IsValidTileIndex(var); ++var) + #endif /* TILEAREA_TYPE_H */ diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index 3d64248df..1157697d7 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -19,16 +19,17 @@ /** Highlighting draw styles */ enum HighLightStyle { - HT_NONE = 0x000, ///< default - HT_RECT = 0x010, ///< rectangle (stations, depots, ...) - HT_POINT = 0x020, ///< point (lower land, raise land, level land, ...) - HT_SPECIAL = 0x030, ///< special mode used for highlighting while dragging (and for tunnels/docks) - HT_DRAG = 0x040, ///< dragging items in the depot windows - HT_LINE = 0x008, ///< used for autorail highlighting (longer stretches), lower bits: direction - HT_RAIL = 0x080, ///< autorail (one piece), lower bits: direction - HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) - HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT. - HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes. + HT_NONE = 0x000, ///< default + HT_RECT = 0x010, ///< rectangle (stations, depots, ...) + HT_POINT = 0x020, ///< point (lower land, raise land, level land, ...) + HT_SPECIAL = 0x030, ///< special mode used for highlighting while dragging (and for tunnels/docks) + HT_DRAG = 0x040, ///< dragging items in the depot windows + HT_LINE = 0x008, ///< used for autorail highlighting (longer stretches), lower bits: direction + HT_RAIL = 0x080, ///< autorail (one piece), lower bits: direction + HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) + HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT. + HT_PASTE_PREVIEW = 0x400, ///< Preview of a paste result. Only usable in combination with #HT_POINT. + HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes. /* lower bits (used with HT_LINE and HT_RAIL): * (see ASCII art in table/autorail.h for a visual interpretation) */ diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index f253be8b3..b33f0c561 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -967,7 +967,7 @@ static CallBackFunction MenuClickBuildAir(int index) static CallBackFunction ToolbarForestClick(Window *w) { - PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 3); + PopupMainToolbMenu(w, WID_TN_LANDSCAPE, STR_LANDSCAPING_MENU_LANDSCAPING, 4); return CBF_NONE; } @@ -981,8 +981,9 @@ static CallBackFunction MenuClickForest(int index) { switch (index) { case 0: ShowTerraformToolbar(); break; - case 1: ShowBuildTreesToolbar(); break; - case 2: return SelectSignTool(); + case 1: ShowClipboardToolbar(); break; + case 2: ShowBuildTreesToolbar(); break; + case 3: return SelectSignTool(); } return CBF_NONE; } diff --git a/src/town.h b/src/town.h index 010c7c216..81651102a 100644 --- a/src/town.h +++ b/src/town.h @@ -15,6 +15,9 @@ #include "viewport_type.h" #include "town_map.h" #include "subsidy_type.h" +#include "openttd.h" +#include "table/strings.h" +#include "company_func.h" #include "newgrf_storage.h" #include "cargotype.h" #include "tilematrix_type.hpp" @@ -75,6 +78,7 @@ struct Town : TownPool::PoolItem<&_town_pool> { CompanyByte exclusivity; ///< which company has exclusivity uint8 exclusive_counter; ///< months till the exclusivity expires int16 ratings[MAX_COMPANIES]; ///< ratings of each company for this town + StringID town_label; ///< Label dependent on _local_company rating. TransportedCargoStat supplied[NUM_CARGO]; ///< Cargo statistics about supplied cargo. TransportedCargoStat received[NUM_TE]; ///< Cargo statistics about received cargotypes. @@ -113,6 +117,30 @@ struct Town : TownPool::PoolItem<&_town_pool> { void InitializeLayout(TownLayout layout); + void UpdateLabel(); + + /** + * Returns the correct town label, based on rating. + */ + StringID Label() const{ + if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { + return STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING + this->town_label; + } else { + return _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN; + } + } + + /** + * Returns the correct town small label, based on rating. + */ + StringID SmallLabel() const{ + if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { + return STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING + this->town_label; + } else { + return STR_VIEWPORT_TOWN_TINY_WHITE; + } + } + /** * Calculate the max town noise. * The value is counted using the population divided by the content of the @@ -145,6 +173,40 @@ void ShowTownViewWindow(TownID town); void ExpandTown(Town *t); /** + * Return the offset for a given town rating. + * + * This function returns an offset which can be + * added to the STR_CARGO_RATING_APPALLING value to get the + * corresponding StringID for the given town rating. + * + * @param rating The town rating + * @return The offset for the given rating + */ +static inline StringID OffsetByTownRating(int16 rating) +{ + int16 ratings[8] = { + RATING_APPALLING, + RATING_VERYPOOR, + RATING_POOR, + RATING_MEDIOCRE, + RATING_GOOD, + RATING_VERYGOOD, + RATING_EXCELLENT, + RATING_OUTSTANDING + }; + int offset = 0; + + for (;offset < 7; offset++) { + + if (rating <= ratings[offset]) { + break; + } + } + // if offset is 7 its Outstanding as nothing is above it + return offset; +} + +/** * Action types that a company must ask permission for to a town authority. * @see CheckforTownRating */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 66092ff34..7e3cf2803 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -26,6 +26,7 @@ #include "newgrf_debug.h" #include "newgrf_house.h" #include "newgrf_text.h" +#include "texteff.hpp" #include "autoslope.h" #include "tunnelbridge_map.h" #include "strings_func.h" @@ -163,6 +164,26 @@ void Town::InitializeLayout(TownLayout layout) } /** + * Updates the town label of the town after changes in rating. The colour scheme is: + * Red: Appalling and Very poor ratings. + * Orange: Poor and mediocre ratings. + * Yellow: Good rating. + * White: Very good rating (standard). + * Green: Excellent and outstanding ratings. + */ +void Town::UpdateLabel() +{ + if (!(_game_mode == GM_EDITOR) && (_local_company < MAX_COMPANIES)) { + int r = this->ratings[_local_company]; + (this->town_label = 0, r <= RATING_VERYPOOR) || // Appalling and Very Poor + (this->town_label++, r <= RATING_MEDIOCRE) || // Poor and Mediocre + (this->town_label++, r <= RATING_GOOD) || // Good + (this->town_label++, r <= RATING_VERYGOOD) || // Very Good + (this->town_label++, true); // Excellent and Outstanding + } +} + +/** * Get the cost for removing this house * @return the cost (inflation corrected etc) */ @@ -232,6 +253,8 @@ static void DrawTile_Town(TileInfo *ti) DrawGroundSprite(dcts->ground.sprite, dcts->ground.pal); + DrawOverlay(ti, MP_HOUSE); + /* If houses are invisible, do not draw the upper part */ if (IsInvisibilitySet(TO_HOUSES)) return; @@ -373,11 +396,11 @@ static bool IsCloseToTown(TileIndex tile, uint dist) */ void Town::UpdateVirtCoord() { + this->UpdateLabel(); Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); SetDParam(0, this->index); SetDParam(1, this->cache.population); - this->cache.sign.UpdatePosition(pt.x, pt.y - 24 * ZOOM_LVL_BASE, - _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, + this->cache.sign.UpdatePosition(pt.x, pt.y - 24 * ZOOM_LVL_BASE, this->Label(), STR_VIEWPORT_TOWN); SetWindowDirty(WC_TOWN_VIEW, this->index); @@ -2958,6 +2981,7 @@ static CommandCost TownActionBribe(Town *t, DoCommandFlag flags) */ if (t->ratings[_current_company] > RATING_BRIBE_DOWN_TO) { t->ratings[_current_company] = RATING_BRIBE_DOWN_TO; + t->UpdateVirtCoord(); SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } } else { @@ -3090,6 +3114,7 @@ static void UpdateTownRating(Town *t) t->ratings[i] = Clamp(t->ratings[i], RATING_MINIMUM, RATING_MAXIMUM); } + t->UpdateVirtCoord(); SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } @@ -3325,6 +3350,8 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags) } int rating = GetRating(t); + int old_rating = rating; + if (add < 0) { if (rating > max) { rating += add; @@ -3341,7 +3368,28 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags) } else { SetBit(t->have_ratings, _current_company); t->ratings[_current_company] = rating; + t->UpdateVirtCoord(); SetWindowDirty(WC_TOWN_AUTHORITY, t->index); + + if (_settings_client.gui.townrating_indicator && _current_company == _local_company) { + if (rating != old_rating) { // got a change + StringID msg = (rating > old_rating) ? STR_TOWN_RATING_INCREASED : STR_TOWN_RATING_DECREASED; + StringID display; + StringID old_display; + // rating -> string_rating + display = STR_CARGO_RATING_APPALLING + OffsetByTownRating(rating); + old_display = STR_CARGO_RATING_APPALLING + OffsetByTownRating(old_rating); + + if (display != old_display) { // change in rating, notice it (abundant check?) + // place the indicator 1 tile above the town tile results in + // showing the indicator above the town name + Point pt = RemapCoords2((TileX(t->xy) - 1) * TILE_SIZE - (display - STR_CARGO_RATING_APPALLING) * 7, + (TileY(t->xy) - 1) * TILE_SIZE - (display - STR_CARGO_RATING_APPALLING) * 7); + SetDParam(0, display); + AddTextEffect(msg, pt.x, pt.y, DAY_TICKS, TE_RISING); + } +} + } } } @@ -3456,6 +3504,7 @@ extern const TileTypeProcs _tile_type_town_procs = { NULL, // vehicle_enter_tile_proc GetFoundation_Town, // get_foundation_proc TerraformTile_Town, // terraform_tile_proc + NULL, // copypaste_tile_proc }; diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 142966b69..85e2281cf 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -154,15 +154,7 @@ public: SetDParam(1, c->index); int r = this->town->ratings[c->index]; - StringID str; - (str = STR_CARGO_RATING_APPALLING, r <= RATING_APPALLING) || // Apalling - (str++, r <= RATING_VERYPOOR) || // Very Poor - (str++, r <= RATING_POOR) || // Poor - (str++, r <= RATING_MEDIOCRE) || // Mediocore - (str++, r <= RATING_GOOD) || // Good - (str++, r <= RATING_VERYGOOD) || // Very Good - (str++, r <= RATING_EXCELLENT) || // Excellent - (str++, true); // Outstanding + StringID str = STR_CARGO_RATING_APPALLING + OffsetByTownRating(r); SetDParam(2, str); if (this->town->exclusivity == c->index) { diff --git a/src/town_map.h b/src/town_map.h index 016ff9a6d..4c648ad6f 100644 --- a/src/town_map.h +++ b/src/town_map.h @@ -24,7 +24,7 @@ static inline TownID GetTownIndex(TileIndex t) { assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); - return _m[t].m2; + return GetTile(t)->m2; } /** @@ -36,7 +36,7 @@ static inline TownID GetTownIndex(TileIndex t) static inline void SetTownIndex(TileIndex t, TownID index) { assert(IsTileType(t, MP_HOUSE) || (IsTileType(t, MP_ROAD) && !IsRoadDepot(t))); - _m[t].m2 = index; + GetTile(t)->m2 = index; } /** @@ -49,7 +49,7 @@ static inline void SetTownIndex(TileIndex t, TownID index) static inline HouseID GetCleanHouseType(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8); + return GetTile(t)->m4 | (GB(GetTile(t)->m3, 6, 1) << 8); } /** @@ -72,8 +72,8 @@ static inline HouseID GetHouseType(TileIndex t) static inline void SetHouseType(TileIndex t, HouseID house_id) { assert(IsTileType(t, MP_HOUSE)); - _m[t].m4 = GB(house_id, 0, 8); - SB(_m[t].m3, 6, 1, GB(house_id, 8, 1)); + GetTile(t)->m4 = GB(house_id, 0, 8); + SB(GetTile(t)->m3, 6, 1, GB(house_id, 8, 1)); } /** @@ -83,7 +83,7 @@ static inline void SetHouseType(TileIndex t, HouseID house_id) */ static inline bool LiftHasDestination(TileIndex t) { - return HasBit(_me[t].m7, 0); + return HasBit(GetTileEx(t)->m7, 0); } /** @@ -94,8 +94,8 @@ static inline bool LiftHasDestination(TileIndex t) */ static inline void SetLiftDestination(TileIndex t, byte dest) { - SetBit(_me[t].m7, 0); - SB(_me[t].m7, 1, 3, dest); + SetBit(GetTileEx(t)->m7, 0); + SB(GetTileEx(t)->m7, 1, 3, dest); } /** @@ -105,7 +105,7 @@ static inline void SetLiftDestination(TileIndex t, byte dest) */ static inline byte GetLiftDestination(TileIndex t) { - return GB(_me[t].m7, 1, 3); + return GB(GetTileEx(t)->m7, 1, 3); } /** @@ -116,7 +116,7 @@ static inline byte GetLiftDestination(TileIndex t) */ static inline void HaltLift(TileIndex t) { - SB(_me[t].m7, 0, 4, 0); + SB(GetTileEx(t)->m7, 0, 4, 0); } /** @@ -126,7 +126,7 @@ static inline void HaltLift(TileIndex t) */ static inline byte GetLiftPosition(TileIndex t) { - return GB(_me[t].m6, 2, 6); + return GB(GetTileEx(t)->m6, 2, 6); } /** @@ -136,7 +136,7 @@ static inline byte GetLiftPosition(TileIndex t) */ static inline void SetLiftPosition(TileIndex t, byte pos) { - SB(_me[t].m6, 2, 6, pos); + SB(GetTileEx(t)->m6, 2, 6, pos); } /** @@ -147,7 +147,7 @@ static inline void SetLiftPosition(TileIndex t, byte pos) static inline bool IsHouseCompleted(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return HasBit(_m[t].m3, 7); + return HasBit(GetTile(t)->m3, 7); } /** @@ -158,7 +158,7 @@ static inline bool IsHouseCompleted(TileIndex t) static inline void SetHouseCompleted(TileIndex t, bool status) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 7, 1, !!status); + SB(GetTile(t)->m3, 7, 1, !!status); } /** @@ -185,7 +185,7 @@ static inline void SetHouseCompleted(TileIndex t, bool status) static inline byte GetHouseBuildingStage(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(_m[t].m5, 3, 2); + return IsHouseCompleted(t) ? (byte)TOWN_HOUSE_COMPLETED : GB(GetTile(t)->m5, 3, 2); } /** @@ -197,7 +197,7 @@ static inline byte GetHouseBuildingStage(TileIndex t) static inline byte GetHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? 0 : GB(_m[t].m5, 0, 3); + return IsHouseCompleted(t) ? 0 : GB(GetTile(t)->m5, 0, 3); } /** @@ -210,9 +210,9 @@ static inline byte GetHouseConstructionTick(TileIndex t) static inline void IncHouseConstructionTick(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - AB(_m[t].m5, 0, 5, 1); + AB(GetTile(t)->m5, 0, 5, 1); - if (GB(_m[t].m5, 3, 2) == TOWN_HOUSE_COMPLETED) { + if (GB(GetTile(t)->m5, 3, 2) == TOWN_HOUSE_COMPLETED) { /* House is now completed. * Store the year of construction as well, for newgrf house purpose */ SetHouseCompleted(t, true); @@ -228,7 +228,7 @@ static inline void IncHouseConstructionTick(TileIndex t) static inline void ResetHouseAge(TileIndex t) { assert(IsTileType(t, MP_HOUSE) && IsHouseCompleted(t)); - _m[t].m5 = 0; + GetTile(t)->m5 = 0; } /** @@ -239,7 +239,7 @@ static inline void ResetHouseAge(TileIndex t) static inline void IncrementHouseAge(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - if (IsHouseCompleted(t) && _m[t].m5 < 0xFF) _m[t].m5++; + if (IsHouseCompleted(t) && GetTile(t)->m5 < 0xFF) GetTile(t)->m5++; } /** @@ -251,7 +251,7 @@ static inline void IncrementHouseAge(TileIndex t) static inline Year GetHouseAge(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return IsHouseCompleted(t) ? _m[t].m5 : 0; + return IsHouseCompleted(t) ? GetTile(t)->m5 : 0; } /** @@ -264,7 +264,7 @@ static inline Year GetHouseAge(TileIndex t) static inline void SetHouseRandomBits(TileIndex t, byte random) { assert(IsTileType(t, MP_HOUSE)); - _m[t].m1 = random; + GetTile(t)->m1 = random; } /** @@ -277,7 +277,7 @@ static inline void SetHouseRandomBits(TileIndex t, byte random) static inline byte GetHouseRandomBits(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return _m[t].m1; + return GetTile(t)->m1; } /** @@ -290,7 +290,7 @@ static inline byte GetHouseRandomBits(TileIndex t) static inline void SetHouseTriggers(TileIndex t, byte triggers) { assert(IsTileType(t, MP_HOUSE)); - SB(_m[t].m3, 0, 5, triggers); + SB(GetTile(t)->m3, 0, 5, triggers); } /** @@ -303,7 +303,7 @@ static inline void SetHouseTriggers(TileIndex t, byte triggers) static inline byte GetHouseTriggers(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_m[t].m3, 0, 5); + return GB(GetTile(t)->m3, 0, 5); } /** @@ -315,7 +315,7 @@ static inline byte GetHouseTriggers(TileIndex t) static inline byte GetHouseProcessingTime(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - return GB(_me[t].m6, 2, 6); + return GB(GetTileEx(t)->m6, 2, 6); } /** @@ -327,7 +327,7 @@ static inline byte GetHouseProcessingTime(TileIndex t) static inline void SetHouseProcessingTime(TileIndex t, byte time) { assert(IsTileType(t, MP_HOUSE)); - SB(_me[t].m6, 2, 6, time); + SB(GetTileEx(t)->m6, 2, 6, time); } /** @@ -338,7 +338,7 @@ static inline void SetHouseProcessingTime(TileIndex t, byte time) static inline void DecHouseProcessingTime(TileIndex t) { assert(IsTileType(t, MP_HOUSE)); - _me[t].m6 -= 1 << 2; + GetTileEx(t)->m6 -= 1 << 2; } /** @@ -356,12 +356,12 @@ static inline void MakeHouseTile(TileIndex t, TownID tid, byte counter, byte sta assert(IsTileType(t, MP_CLEAR)); SetTileType(t, MP_HOUSE); - _m[t].m1 = random_bits; - _m[t].m2 = tid; - _m[t].m3 = 0; + GetTile(t)->m1 = random_bits; + GetTile(t)->m2 = tid; + GetTile(t)->m3 = 0; SetHouseType(t, type); SetHouseCompleted(t, stage == TOWN_HOUSE_COMPLETED); - _m[t].m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); + GetTile(t)->m5 = IsHouseCompleted(t) ? 0 : (stage << 3 | counter); SetAnimationFrame(t, 0); SetHouseProcessingTime(t, HouseSpec::Get(type)->processing_time); } diff --git a/src/track_func.h b/src/track_func.h index 68de827ef..f36db07a0 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -16,6 +16,7 @@ #include "track_type.h" #include "direction_func.h" #include "slope_func.h" +#include "direction_func.h" /** * Iterate through each set Track in a TrackBits value. @@ -692,4 +693,43 @@ static inline bool IsUphillTrackdir(Slope slope, Trackdir dir) return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir); } +/** + * Transform a Track. + * @param track The Track to transform. + * @param transformation Transformation to perform. + * @return The transformed Track. + */ +static inline Track TransformTrack(Track track, DirTransformation transformation) +{ + extern const byte _track_transformation_map[DTR_END][TRACK_END]; + return (Track)_track_transformation_map[transformation][track]; +} + +/** + * Transform a TrackBits. + * @param track_bits The TrackBits to transform. + * @param transformation Transformation to perform. + * @return The transformed TrackBits. + */ +static inline TrackBits TransformTrackBits(TrackBits track_bits, DirTransformation transformation) +{ + TrackBits ret = track_bits & ~TRACK_BIT_ALL; + + Track t; + FOR_EACH_SET_TRACK(t, track_bits & TRACK_BIT_ALL) SetBit(ret, TransformTrack(t, transformation)); + + return ret; +} + +/** + * Transform a Trackdir. + * @param trackdir The Trackdir to transform. + * @param transformation Transformation to perform. + * @return The transformed Trackdir. + */ +static inline Trackdir TransformTrackdir(Trackdir trackdir, DirTransformation transformation) +{ + return TrackExitdirToTrackdir(TransformTrack(TrackdirToTrack(trackdir), transformation), TransformDiagDir(TrackdirToExitdir(trackdir), transformation)); +} + #endif /* TRACK_FUNC_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 608ce2fee..2dab5bb83 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1872,6 +1872,17 @@ void ReverseTrainDirection(Train *v) return; } + /* We are inside tunnel/bidge with signals, reversing will close the entrance. */ + if (HasWormholeSignals(v->tile)) { + /* Flip signal on tunnel entrance tile red. */ + SetBitTunnelBridgeExit(v->tile); + MarkTileDirtyByTile(v->tile); + /* Clear counters. */ + v->wait_counter = 0; + v->load_unload_ticks = 0; + return; + } + /* TrainExitDir does not always produce the desired dir for depots and * tunnels/bridges that is needed for UpdateSignalsOnSegment. */ DiagDirection dir = TrainExitDir(v->direction, v->track); @@ -2208,6 +2219,42 @@ static bool CheckTrainStayInDepot(Train *v) return false; } +static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDirection dir, bool free) +{ + if (IsBridge(end) && GetTile(end)->m2 > 0){ + /* Clearing last bridge signal. */ + uint16 m = GetTile(end)->m2; + byte i = 15; + while((m & 0x8000) == 0 && --i > 0) m <<= 1; + ClrBit(GetTile(end)->m2, i); + + uint x = TileX(end)* TILE_SIZE; + uint y = TileY(end)* TILE_SIZE; + uint distance = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) * ++i; + switch (dir) { + default: NOT_REACHED(); + case DIAGDIR_NE: MarkTileDirtyByTile(TileVirtXY(x - distance, y)); break; + case DIAGDIR_SE: MarkTileDirtyByTile(TileVirtXY(x, y + distance)); break; + case DIAGDIR_SW: MarkTileDirtyByTile(TileVirtXY(x + distance, y)); break; + case DIAGDIR_NW: MarkTileDirtyByTile(TileVirtXY(x, y - distance)); break; + } + MarkTileDirtyByTile(tile); + } + if (free) { + /* Open up the wormhole and clear m2. */ + GetTile(tile)->m2 = 0; + GetTile(end)->m2 = 0; + + if (IsTunnelBridgeWithSignRed(end)) { + ClrBitTunnelBridgeExit(end); + if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(end); + } else if (IsTunnelBridgeWithSignRed(tile)) { + ClrBitTunnelBridgeExit(tile); + if (!_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(tile); + } + } +} + /** * Clear the reservation of \a tile that was just left by a wagon on \a track_dir. * @param v %Train owning the reservation. @@ -2223,7 +2270,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { TileIndex end = GetOtherTunnelBridgeEnd(tile); - if (TunnelBridgeIsFree(tile, end, v).Succeeded()) { + bool free = TunnelBridgeIsFree(tile, end, v).Succeeded(); + if (free) { /* Free the reservation only if no other train is on the tiles. */ SetTunnelBridgeReservation(tile, false); SetTunnelBridgeReservation(end, false); @@ -2237,6 +2285,7 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ } } } + if (HasWormholeSignals(tile)) HandleLastTunnelBridgeSignals(tile, end, dir, free); } } else if (IsRailStationTile(tile)) { TileIndex new_tile = TileAddByDiagDir(tile, dir); @@ -3102,6 +3151,99 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) return t; } +/** Find train in front and keep distance between trains in tunnel/bridge. */ +static Vehicle *FindSpaceBetweenTrainsEnum(Vehicle *v, void *data) +{ + /* Don't look at wagons between front and back of train. */ + if (v->type != VEH_TRAIN || (v->Previous() != NULL && v->Next() != NULL)) return NULL; + + const Vehicle *u = (Vehicle*)data; + int32 a, b = 0; + + switch (u->direction) { + default: NOT_REACHED(); + case DIR_NE: a = u->x_pos; b = v->x_pos; break; + case DIR_SE: a = v->y_pos; b = u->y_pos; break; + case DIR_SW: a = v->x_pos; b = u->x_pos; break; + case DIR_NW: a = u->y_pos; b = v->y_pos; break; + } + + if (a > b && a <= (b + (int)(Train::From(u)->wait_counter)) + (int)(TILE_SIZE)) return v; + return NULL; +} + +static bool IsToCloseBehindTrain(Vehicle *v, TileIndex tile, bool check_endtile) +{ + Train *t = (Train *)v; + + if (t->force_proceed != 0) return false; + + if (HasVehicleOnPos(t->tile, v, &FindSpaceBetweenTrainsEnum)) { + /* Revert train if not going with tunnel direction. */ + if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { + v->cur_speed = 0; + ToggleBit(t->flags, VRF_REVERSING); + } + return true; + } + /* Cover blind spot at end of tunnel bridge. */ + if (check_endtile){ + if (HasVehicleOnPos(GetOtherTunnelBridgeEnd(t->tile), v, &FindSpaceBetweenTrainsEnum)) { + /* Revert train if not going with tunnel direction. */ + if (DirToDiagDir(t->direction) != GetTunnelBridgeDirection(t->tile)) { + v->cur_speed = 0; + ToggleBit(t->flags, VRF_REVERSING); + } + return true; + } + } + + return false; +} + +/** Simulate signals in tunnel - bridge. */ +static bool CheckTrainStayInWormHole(Train *t, TileIndex tile) +{ + if (t->force_proceed != 0) return false; + + /* When not exit reverse train. */ + if (!IsTunnelBridgeExit(tile)) { + t->cur_speed = 0; + ToggleBit(t->flags, VRF_REVERSING); + return true; + } + SigSegState seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, t->owner); + if (seg_state == SIGSEG_FULL || (seg_state == SIGSEG_PBS && !TryPathReserve(t))) { + t->cur_speed = 0; + return true; + } + + return false; +} + +static void HandleSignalBehindTrain(Train *v, uint signal_number) +{ + TileIndex tile; + switch (v->direction) { + default: NOT_REACHED(); + case DIR_NE: tile = TileVirtXY(v->x_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break; + case DIR_SE: tile = TileVirtXY(v->x_pos, v->y_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) ); break; + case DIR_SW: tile = TileVirtXY(v->x_pos - (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals), v->y_pos); break; + case DIR_NW: tile = TileVirtXY(v->x_pos, v->y_pos + (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals)); break; + } + + if(tile == v->tile) { + /* Flip signal on ramp. */ + if (IsTunnelBridgeWithSignRed(tile)) { + ClrBitTunnelBridgeExit(tile); + MarkTileDirtyByTile(tile); + } + } else if (IsBridge(v->tile) && signal_number <= 16) { + ClrBit(GetTile(v->tile)->m2, signal_number); + MarkTileDirtyByTile(tile); + } +} + /** * Move a vehicle chain one movement stop forwards. * @param v First vehicle to move. @@ -3287,6 +3429,23 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) goto invalid_rail; } + if (HasWormholeSignals(gp.new_tile)) { + /* If red signal stop. */ + if (v->IsFrontEngine() && v->force_proceed == 0) { + if (IsTunnelBridgeWithSignRed(gp.new_tile)) { + v->cur_speed = 0; + return false; + } + if (IsTunnelBridgeExit(gp.new_tile)) { + v->cur_speed = 0; + goto invalid_rail; + } + /* Flip signal on tunnel entrance tile red. */ + SetBitTunnelBridgeExit(gp.new_tile); + MarkTileDirtyByTile(gp.new_tile); + } + } + if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { Track track = FindFirstTrack(chosen_track); Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir); @@ -3339,6 +3498,64 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) } } } else { + /* Handle signal simulation on tunnel/bridge. */ + TileIndex old_tile = TileVirtXY(v->x_pos, v->y_pos); + if (old_tile != gp.new_tile && HasWormholeSignals(v->tile) && (v->IsFrontEngine() || v->Next() == NULL)){ + if (old_tile == v->tile) { + if (v->IsFrontEngine() && v->force_proceed == 0 && IsTunnelBridgeExit(v->tile)) goto invalid_rail; + /* Entered wormhole set counters. */ + v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE; + v->load_unload_ticks = 0; + } + + uint distance = v->wait_counter; + bool leaving = false; + if (distance == 0) v->wait_counter = (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals); + + if (v->IsFrontEngine()) { + /* Check if track in front is free and see if we can leave wormhole. */ + int z = GetSlopePixelZ(gp.x, gp.y) - v->z_pos; + if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && !(abs(z) > 2)) { + if (CheckTrainStayInWormHole(v, gp.new_tile)) return false; + leaving = true; + } else { + if (IsToCloseBehindTrain(v, gp.new_tile, distance == 0)) { + if (distance == 0) v->wait_counter = 0; + v->cur_speed = 0; + return false; + } + /* flip signal in front to red on bridges*/ + if (distance == 0 && v->load_unload_ticks <= 15 && IsBridge(v->tile)){ + SetBit(GetTile(v->tile)->m2, v->load_unload_ticks); + MarkTileDirtyByTile(gp.new_tile); + } + } + } + if (v->Next() == NULL) { + if (v->load_unload_ticks > 0 && v->load_unload_ticks <= 16 && distance == (TILE_SIZE * _settings_client.gui.simulated_wormhole_signals) - TILE_SIZE) HandleSignalBehindTrain(v, v->load_unload_ticks - 2); + if (old_tile == v->tile) { + /* We left ramp into wormhole. */ + v->x_pos = gp.x; + v->y_pos = gp.y; + UpdateSignalsOnSegment(old_tile, INVALID_DIAGDIR, v->owner); + } + } + if (distance == 0) v->load_unload_ticks++; + v->wait_counter -= TILE_SIZE; + + if (leaving) { // Reset counters. + v->force_proceed = 0; + v->wait_counter = 0; + v->load_unload_ticks = 0; + v->x_pos = gp.x; + v->y_pos = gp.y; + v->UpdatePosition(); + v->UpdateViewport(false,false); + UpdateSignalsOnSegment(gp.new_tile, INVALID_DIAGDIR, v->owner); + continue; + } + } + if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { /* Perform look-ahead on tunnel exit. */ if (v->IsFrontEngine()) { @@ -3354,7 +3571,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); - if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); + if (v->IsDrawn()) v->Vehicle::UpdateViewport(true); continue; } } @@ -3526,7 +3743,7 @@ static void ChangeTrainDirRandomly(Train *v) do { /* We don't need to twist around vehicles if they're not visible */ - if (!(v->vehstatus & VS_HIDDEN)) { + if (v->IsDrawn()) { v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]); /* Refrain from updating the z position of the vehicle when on * a bridge, because UpdateInclination() will put the vehicle under @@ -3550,7 +3767,7 @@ static bool HandleCrashedTrain(Train *v) { int state = ++v->crash_anim_pos; - if (state == 4 && !(v->vehstatus & VS_HIDDEN)) { + if (state == 4 && v->IsDrawn()) { CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); } @@ -3881,7 +4098,7 @@ static bool TrainLocoHandler(Train *v, bool mode) } for (Train *u = v; u != NULL; u = u->Next()) { - if ((u->vehstatus & VS_HIDDEN) != 0) continue; + if (!u->IsDrawn()) continue; u->UpdateViewport(false, false); } diff --git a/src/transparency.h b/src/transparency.h index ab6f9a6f3..773a633e3 100644 --- a/src/transparency.h +++ b/src/transparency.h @@ -31,6 +31,7 @@ enum TransparencyOption { TO_STRUCTURES, ///< other objects such as transmitters and lighthouses TO_CATENARY, ///< catenary TO_LOADING, ///< loading indicators + TO_TUNNELS, ///< vehicles in tunnels TO_END, TO_INVALID, ///< Invalid transparency option }; diff --git a/src/transparency_gui.cpp b/src/transparency_gui.cpp index 4bad2b056..815622931 100644 --- a/src/transparency_gui.cpp +++ b/src/transparency_gui.cpp @@ -52,14 +52,15 @@ public: case WID_TT_BRIDGES: case WID_TT_STRUCTURES: case WID_TT_CATENARY: - case WID_TT_LOADING: { + case WID_TT_LOADING: + case WID_TT_TUNNELS: { uint i = widget - WID_TT_BEGIN; if (HasBit(_transparency_lock, i)) DrawSprite(SPR_LOCK, PAL_NONE, r.left + 1, r.top + 1); break; } case WID_TT_BUTTONS: for (uint i = WID_TT_BEGIN; i < WID_TT_END; i++) { - if (i == WID_TT_LOADING) continue; // Do not draw button for invisible loading indicators. + if (i >= WID_TT_LOADING) continue; // Do not draw button for invisible loading indicators. const NWidgetBase *wi = this->GetWidget(i); DrawFrameRect(wi->pos_x + 1, r.top + 2, wi->pos_x + wi->current_x - 2, r.bottom - 2, COLOUR_PALE_GREEN, @@ -141,6 +142,7 @@ static const NWidgetPart _nested_transparency_widgets[] = { NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_STRUCTURES), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRANSMITTER, STR_TRANSPARENT_STRUCTURES_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_CATENARY), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_BUILD_X_ELRAIL, STR_TRANSPARENT_CATENARY_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_LOADING), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRAINLIST, STR_TRANSPARENT_LOADING_TOOLTIP), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_TUNNELS), SetMinimalSize(22, 22), SetFill(0, 1), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_TRANSPARENT_TUNNELS_TOOLTIP), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetFill(1, 1), EndContainer(), EndContainer(), /* Panel with 'invisibility' buttons. */ diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index c862f0dbc..759a001aa 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -470,6 +470,8 @@ static void DrawTile_Trees(TileInfo *ti) default: DrawGroundSprite(_clear_land_sprites_snow_desert[GetTreeDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break; } + DrawOverlay(ti, MP_TREES); + /* Do not draw trees when the invisible trees setting is set */ if (IsInvisibilitySet(TO_TREES)) return; @@ -813,4 +815,5 @@ extern const TileTypeProcs _tile_type_trees_procs = { NULL, // vehicle_enter_tile_proc GetFoundation_Trees, // get_foundation_proc TerraformTile_Trees, // terraform_tile_proc + NULL // copypaste_tile_proc }; diff --git a/src/tree_map.h b/src/tree_map.h index e614099fc..8eaa1a75e 100644 --- a/src/tree_map.h +++ b/src/tree_map.h @@ -74,7 +74,7 @@ enum TreeGround { static inline TreeType GetTreeType(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return (TreeType)_m[t].m3; + return (TreeType)GetTile(t)->m3; } /** @@ -89,7 +89,7 @@ static inline TreeType GetTreeType(TileIndex t) static inline TreeGround GetTreeGround(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return (TreeGround)GB(_m[t].m2, 6, 3); + return (TreeGround)GB(GetTile(t)->m2, 6, 3); } /** @@ -114,7 +114,7 @@ static inline TreeGround GetTreeGround(TileIndex t) static inline uint GetTreeDensity(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m2, 4, 2); + return GB(GetTile(t)->m2, 4, 2); } /** @@ -131,8 +131,8 @@ static inline uint GetTreeDensity(TileIndex t) static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m2, 4, 2, d); - SB(_m[t].m2, 6, 3, g); + SB(GetTile(t)->m2, 4, 2, d); + SB(GetTile(t)->m2, 6, 3, g); } /** @@ -149,7 +149,7 @@ static inline void SetTreeGroundDensity(TileIndex t, TreeGround g, uint d) static inline uint GetTreeCount(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m5, 6, 2) + 1; + return GB(GetTile(t)->m5, 6, 2) + 1; } /** @@ -166,7 +166,7 @@ static inline uint GetTreeCount(TileIndex t) static inline void AddTreeCount(TileIndex t, int c) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m5 += c << 6; + GetTile(t)->m5 += c << 6; } /** @@ -181,7 +181,7 @@ static inline void AddTreeCount(TileIndex t, int c) static inline uint GetTreeGrowth(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m5, 0, 3); + return GB(GetTile(t)->m5, 0, 3); } /** @@ -196,7 +196,7 @@ static inline uint GetTreeGrowth(TileIndex t) static inline void AddTreeGrowth(TileIndex t, int a) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m5 += a; + GetTile(t)->m5 += a; } /** @@ -212,7 +212,7 @@ static inline void AddTreeGrowth(TileIndex t, int a) static inline void SetTreeGrowth(TileIndex t, uint g) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m5, 0, 3, g); + SB(GetTile(t)->m5, 0, 3, g); } /** @@ -226,7 +226,7 @@ static inline void SetTreeGrowth(TileIndex t, uint g) static inline uint GetTreeCounter(TileIndex t) { assert(IsTileType(t, MP_TREES)); - return GB(_m[t].m2, 0, 4); + return GB(GetTile(t)->m2, 0, 4); } /** @@ -241,7 +241,7 @@ static inline uint GetTreeCounter(TileIndex t) static inline void AddTreeCounter(TileIndex t, int a) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - _m[t].m2 += a; + GetTile(t)->m2 += a; } /** @@ -256,7 +256,7 @@ static inline void AddTreeCounter(TileIndex t, int a) static inline void SetTreeCounter(TileIndex t, uint c) { assert(IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m2, 0, 4, c); + SB(GetTile(t)->m2, 0, 4, c); } /** @@ -275,12 +275,12 @@ static inline void MakeTree(TileIndex t, TreeType type, uint count, uint growth, { SetTileType(t, MP_TREES); SetTileOwner(t, OWNER_NONE); - _m[t].m2 = ground << 6 | density << 4 | 0; - _m[t].m3 = type; - _m[t].m4 = 0 << 5 | 0 << 2; - _m[t].m5 = count << 6 | growth; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = ground << 6 | density << 4 | 0; + GetTile(t)->m3 = type; + GetTile(t)->m4 = 0 << 5 | 0 << 2; + GetTile(t)->m5 = count << 6 | growth; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } #endif /* TREE_MAP_H */ diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index 4e6d5a7e1..c992b09de 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -21,27 +21,90 @@ * @param tile the tile to search from. * @return the tile of the other end of the tunnel. */ -TileIndex GetOtherTunnelEnd(TileIndex tile) +template +typename TileIndexT::T GetOtherTunnelEnd(typename TileIndexT::T tile) { + assert(IsTunnelTile(tile)); + DiagDirection dir = GetTunnelBridgeDirection(tile); - TileIndexDiff delta = TileOffsByDiagDir(dir); - int z = GetTileZ(tile); + TileIndexDiff delta = TileOffsByDiagDir(dir, MapOf(tile)); + uint h = TileHeight(tile); - dir = ReverseDiagDir(dir); + if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { + h--; +continue_ne_nw: do { tile += delta; - } while ( - !IsTunnelTile(tile) || - GetTunnelBridgeDirection(tile) != dir || - GetTileZ(tile) != z - ); + } while (TileHeight(tile) != h); + } else { +continue_se_sw: + tile += delta; + do { + tile += delta; + } while (TileHeight(tile) != h); + tile -= delta; + } + + if (IsTunnelTile(tile) && GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) { + } else { + /* Handle Chunnels. + * Only look for tunnel when hight level changes. + * And only at sea level. + */ + assert(h <= 1); + (h == 0) ? h = 1 : h = 0; + if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { + goto continue_ne_nw; + } else { + goto continue_se_sw; + } + } return tile; } +/* instantiate */ +template TileIndex GetOtherTunnelEnd(TileIndex tile); +template GenericTileIndex GetOtherTunnelEnd(GenericTileIndex tile); +/** + * Is there a Chunnel in the way in the given direction? + * Only between height level 0 and 1. + * Used to avoid building bridge or tunnel between existing chunnel. + * @param tile the tile to search from. + * @param dir the direction to start searching to. + * @return true if and only if there is a chunnel. + */ +bool IsBetweenChunnelPortals(TileIndex tile, DiagDirection dir) +{ + uint h = 0; + TileIndexDiff delta = TileOffsByDiagDir(dir); + if (GetTileZ(tile) > 0) return false; + + do { + if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) { + do { + tile += delta; + if (!IsValidTile(tile)) return false; + } while (TileHeight(tile) != h); + } else { + tile += delta; + do { + tile += delta; + if (!IsValidTile(tile)) return false; + } while (TileHeight(tile) != h); + tile -= delta; + } + (h == 0) ? h = 1 : h = 0; + } while (!IsTunnelTile(tile)); + + if (GetTunnelBridgeDirection(tile) != ReverseDiagDir(dir)) return false; + + return true; +} /** * Is there a tunnel in the way in the given direction? + * Between level 0 and 1 terraforming is allowed. (No search) * @param tile the tile to search from. * @param z the 'z' to search on. * @param dir the direction to start searching to. @@ -49,6 +112,9 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) */ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir) { + /* Between level 0 and 1 terraforming is allowed. */ + if (GetTileZ(tile) <= 1) return false; + TileIndexDiff delta = TileOffsByDiagDir(dir); int height; diff --git a/src/tunnel_map.h b/src/tunnel_map.h index e200a1275..73a55e2d5 100644 --- a/src/tunnel_map.h +++ b/src/tunnel_map.h @@ -21,25 +21,42 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return true if and only if this tile is a tunnel (entrance) */ -static inline bool IsTunnel(TileIndex t) +template +static inline bool IsTunnel(typename TileIndexT::T t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return !HasBit(_m[t].m5, 7); + return !HasBit(GetTile(t)->m5, 7); } +/** @copydoc IsTunnel(TileIndexT::T) */ +static inline bool IsTunnel(TileIndex t) { return IsTunnel(t); } +/** @copydoc IsTunnel(TileIndexT::T) */ +static inline bool IsTunnel(GenericTileIndex t) { return IsTunnel(t); } /** * Is this a tunnel (entrance)? * @param t the tile that might be a tunnel * @return true if and only if this tile is a tunnel (entrance) */ -static inline bool IsTunnelTile(TileIndex t) +template +static inline bool IsTunnelTile(typename TileIndexT::T t) { return IsTileType(t, MP_TUNNELBRIDGE) && IsTunnel(t); } +/** @copydoc IsTunnelTile(TileIndexT::T) */ +static inline bool IsTunnelTile(TileIndex t) { return IsTunnelTile(t); } +/** @copydoc IsTunnelTile(TileIndexT::T) */ +static inline bool IsTunnelTile(GenericTileIndex t) { return IsTunnelTile(t); } + +template +typename TileIndexT::T GetOtherTunnelEnd(typename TileIndexT::T t); +/** @copydoc GetOtherTunnelEnd(TileIndexT::T) */ +static inline TileIndex GetOtherTunnelEnd(TileIndex t) { return GetOtherTunnelEnd(t); } +/** @copydoc GetOtherTunnelEnd(TileIndexT::T) */ +static inline GenericTileIndex GetOtherTunnelEnd(GenericTileIndex t) { return GetOtherTunnelEnd(t); } -TileIndex GetOtherTunnelEnd(TileIndex); bool IsTunnelInWay(TileIndex, int z); bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); +bool IsBetweenChunnelPortals(TileIndex tile, DiagDirection dir); /** * Makes a road tunnel entrance @@ -48,20 +65,25 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir); * @param d the direction facing out of the tunnel * @param r the road type used in the tunnel */ -static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTypes r) +template +static inline void MakeRoadTunnel(typename TileIndexT::T t, Owner o, DiagDirection d, RoadTypes r) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = TRANSPORT_ROAD << 2 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = TRANSPORT_ROAD << 2 | d; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; SetRoadOwner(t, ROADTYPE_ROAD, o); if (o != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, o); SetRoadTypes(t, r); } +/** @copydoc MakeRoadTunnel(TileIndexT::T,Owner,DiagDirection,RoadTypes) */ +static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTypes r) { MakeRoadTunnel(t, o, d, r); } +/** @copydoc MakeRoadTunnel(TileIndexT::T,Owner,DiagDirection,RoadTypes) */ +static inline void MakeRoadTunnel(GenericTileIndex t, Owner o, DiagDirection d, RoadTypes r) { MakeRoadTunnel(t, o, d, r); } /** * Makes a rail tunnel entrance @@ -70,16 +92,21 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp * @param d the direction facing out of the tunnel * @param r the rail type used in the tunnel */ -static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r) +template +static inline void MakeRailTunnel(typename TileIndexT::T t, Owner o, DiagDirection d, RailType r) { SetTileType(t, MP_TUNNELBRIDGE); SetTileOwner(t, o); - _m[t].m2 = 0; - _m[t].m3 = r; - _m[t].m4 = 0; - _m[t].m5 = TRANSPORT_RAIL << 2 | d; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = r; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = TRANSPORT_RAIL << 2 | d; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeRailTunnel(TileIndexT::T,Owner,DiagDirection,RailType) */ +static inline void MakeRailTunnel(TileIndex t, Owner o, DiagDirection d, RailType r) { MakeRailTunnel(t, o, d, r); } +/** @copydoc MakeRailTunnel(TileIndexT::T,Owner,DiagDirection,RailType) */ +static inline void MakeRailTunnel(GenericTileIndex t, Owner o, DiagDirection d, RailType r) { MakeRailTunnel(t, o, d, r); } #endif /* TUNNEL_MAP_H */ diff --git a/src/tunnelbridge.h b/src/tunnelbridge.h index 0a2c2293d..9e46c3ea8 100644 --- a/src/tunnelbridge.h +++ b/src/tunnelbridge.h @@ -23,7 +23,8 @@ void MarkBridgeDirty(TileIndex tile); * @param end The end of the tunnel or bridge. * @return length of bridge/tunnel middle */ -static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) +template +static inline uint GetTunnelBridgeLength(typename TileIndexT::T begin, typename TileIndexT::T end) { int x1 = TileX(begin); int y1 = TileY(begin); @@ -32,6 +33,10 @@ static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) return abs(x2 + y2 - x1 - y1) - 1; } +/** @copydoc GetTunnelBridgeLength(TileIndexT::T,TileIndexT::T) */ +static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end) { return GetTunnelBridgeLength(begin, end); } +/** @copydoc GetTunnelBridgeLength(TileIndexT::T,TileIndexT::T) */ +static inline uint GetTunnelBridgeLength(GenericTileIndex begin, GenericTileIndex end) { return GetTunnelBridgeLength(begin, end); } extern TileIndex _build_tunnel_endtile; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 9ba9b26e3..9f4b59eb1 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -17,6 +17,7 @@ #include "newgrf_object.h" #include "viewport_func.h" #include "cmd_helper.h" +#include "copypaste_cmd.h" #include "command_func.h" #include "town.h" #include "train.h" @@ -30,6 +31,7 @@ #include "date_func.h" #include "clear_func.h" #include "vehicle_func.h" +#include "vehicle_gui.h" #include "sound_func.h" #include "tunnelbridge.h" #include "cheat_type.h" @@ -40,6 +42,7 @@ #include "object_base.h" #include "water.h" #include "company_gui.h" +#include "clipboard_gui.h" #include "table/strings.h" #include "table/bridge_land.h" @@ -225,6 +228,24 @@ CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoC return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG); } +BridgeType FastestAvailableBridgeType(uint bridge_len) +{ + BridgeType ret = MAX_BRIDGES; + uint max_speed = 0; + + /* loop for all bridgetypes */ + for (BridgeType brd_type = 0; brd_type != MAX_BRIDGES; brd_type++) { + if (CheckBridgeAvailability(brd_type, bridge_len).Failed()) continue; + uint speed = GetBridgeSpec(brd_type)->speed; + if (max_speed < speed) { + max_speed = speed; + ret = brd_type; + } + } + + return ret; +} + /** * Build a Bridge * @param end_tile end tile @@ -255,7 +276,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u switch (transport_type) { case TRANSPORT_ROAD: roadtypes = Extract(p2); - if (!HasExactlyOneBit(roadtypes) || !HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR; + if (!HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR; break; case TRANSPORT_RAIL: @@ -322,6 +343,12 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u if (transport_type == TRANSPORT_WATER && (tileh_start == SLOPE_FLAT || tileh_end == SLOPE_FLAT)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); if (z_start != z_end) return_cmd_error(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT); + /* Don't allow building bridge ramps between chunnel portals. */ + DiagDirection dir = DiagdirBetweenTiles(tile_start, tile_end); + if (IsBetweenChunnelPortals(tile_start, dir)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + if (IsBetweenChunnelPortals(tile_start, ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + if (IsBetweenChunnelPortals(tile_end, ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + CommandCost cost(EXPENSES_CONSTRUCTION); Owner owner; bool is_new_owner; @@ -576,7 +603,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u * @param flags type of operation * @param p1 bit 0-3 railtype or roadtypes * bit 8-9 transport type - * @param p2 unused + * @param p2 the end tile (only if DC_PASTE flags is set) * @param text unused * @return the cost of this operation or an error */ @@ -589,6 +616,9 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, RailType railtype = INVALID_RAILTYPE; RoadTypes rts = ROADTYPES_NONE; _build_tunnel_endtile = 0; + + TileIndex expected_end_tile = (flags & DC_PASTE) ? p2 : (uint32)0; + switch (transport_type) { case TRANSPORT_RAIL: railtype = Extract(p1); @@ -597,7 +627,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, case TRANSPORT_ROAD: rts = Extract(p1); - if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(company, rts)) return CMD_ERROR; + if (!HasRoadTypesAvail(company, rts)) return CMD_ERROR; break; default: return CMD_ERROR; @@ -618,18 +648,43 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, int start_z; int end_z; Slope start_tileh = GetTileSlope(start_tile, &start_z); - DiagDirection direction = GetInclinedSlopeDirection(start_tileh); - if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); + + DiagDirection direction; + if (expected_end_tile == 0) { // the end tile is given implicitly by the terrain + direction = GetInclinedSlopeDirection(start_tileh); + if (direction == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); + } else { // the end tile is given explicitly + direction = DiagdirBetweenTiles(start_tile, expected_end_tile); + if (direction == INVALID_DIAGDIR) return CMD_ERROR; + if (InclinedSlope(direction) != RemoveHalftileSlope(start_tileh)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); + } if (HasTileWaterGround(start_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); - CommandCost ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); - if (ret.Failed()) return ret; + /* Don't allow building tunnel tiles between chunnel portals. */ + if (IsBetweenChunnelPortals(start_tile, direction)) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + if (IsBetweenChunnelPortals(start_tile, ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + + /* Check if there is already the entrance we are willing to build */ + bool may_be_already_built = + (flags & DC_PASTE) && + IsTileOwner(start_tile, _current_company) && + IsTunnelTile(start_tile) && + GetTunnelBridgeTransportType(start_tile) == transport_type && + GetTunnelBridgeDirection(start_tile) == direction && + (transport_type == TRANSPORT_RAIL ? GetTileRailType(start_tile) == railtype : GetRoadTypes(start_tile) == rts); - /* XXX - do NOT change 'ret' in the loop, as it is used as the price - * for the clearing of the entrance of the tunnel. Assigning it to - * cost before the loop will yield different costs depending on start- - * position, because of increased-cost-by-length: 'cost += cost >> 3' */ + /* If the end tile is not given then we have a match already */ + if (may_be_already_built && expected_end_tile == 0) return_cmd_error(STR_ERROR_ALREADY_BUILT); + + CommandCost cost(EXPENSES_CONSTRUCTION); + + /* Clear the entrance */ + if (!may_be_already_built) { + CommandCost ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (ret.Failed()) return ret; + cost.AddCost(ret); + } TileIndexDiff delta = TileOffsByDiagDir(direction); DiagDirection tunnel_in_way_dir; @@ -647,33 +702,79 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, int tiles = 0; /* Number of tiles at which the cost increase coefficient per tile is halved */ int tiles_bump = 25; + end_tile += delta; - CommandCost cost(EXPENSES_CONSTRUCTION); + CommandCost ret(EXPENSES_CONSTRUCTION); + /* XXX: The 'ret' is used recursively inside the loop to calculate the cost-by-length: + * 'ret += ret >> 3' */ Slope end_tileh; for (;;) { - end_tile += delta; if (!IsValidTile(end_tile)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER); end_tileh = GetTileSlope(end_tile, &end_z); - if (start_z == end_z) break; + if (start_z == end_z) { + /* Handle chunnels only on sea level */ + if (end_z != 0) break; + + /* Test for minimal one water tile. */ + if (!IsValidTile(end_tile + delta) || !((IsWaterTile(end_tile + delta)) || (IsCoastTile(end_tile + delta)))) break; + + /* Continue until water is passed and suitable endsstop is found. */ + for (;;) { + if (!IsValidTile(end_tile)) break; + + end_tileh = GetTileSlope(end_tile); + if(direction == DIAGDIR_NE && (end_tileh & SLOPE_NE) == SLOPE_NE) break; + if(direction == DIAGDIR_SE && (end_tileh & SLOPE_SE) == SLOPE_SE) break; + if(direction == DIAGDIR_SW && (end_tileh & SLOPE_SW) == SLOPE_SW) break; + if(direction == DIAGDIR_NW && (end_tileh & SLOPE_NW) == SLOPE_NW) break; + + end_tile += delta; + tiles++; + if (tiles == tiles_bump) { + tiles_coef++; + tiles_bump *= 2; + } + ret.AddCost(_price[PR_BUILD_TUNNEL]); + ret.AddCost(ret.GetCost() >> tiles_coef); // add a multiplier for longer tunnels + } + } + + if (end_tile == expected_end_tile) { // are we getting too far? + _build_tunnel_endtile = end_tile; + return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL); + } if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) { return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); } + end_tile += delta; tiles++; if (tiles == tiles_bump) { tiles_coef++; tiles_bump *= 2; } - cost.AddCost(_price[PR_BUILD_TUNNEL]); - cost.AddCost(cost.GetCost() >> tiles_coef); // add a multiplier for longer tunnels + ret.AddCost(_price[PR_BUILD_TUNNEL]); + ret.AddCost(ret.GetCost() >> tiles_coef); // add a multiplier for longer tunnels } + cost.AddCost(ret.GetCost()); + + /* is the end tile reached? */ + if (expected_end_tile != 0 && expected_end_tile != end_tile) { + if (may_be_already_built) { + return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST); + } else { + return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + } + } + + /* booth ends match? */ + if (may_be_already_built) return_cmd_error(STR_ERROR_ALREADY_BUILT); /* Add the cost of the entrance */ cost.AddCost(_price[PR_BUILD_TUNNEL]); - cost.AddCost(ret); /* if the command fails from here on we want the end tile to be highlighted */ _build_tunnel_endtile = end_tile; @@ -682,7 +783,10 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if (HasTileWaterGround(end_tile)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER); - /* Clear the tile in any case */ + /* Don't allow building tunnels between chunnel portals looking sideways. */ + if (IsBetweenChunnelPortals(end_tile, ChangeDiagDir(direction, DIAGDIRDIFF_90RIGHT))) return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY); + + /* Clear the end tile in any case */ ret = DoCommand(end_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND); cost.AddCost(ret); @@ -710,7 +814,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, /* Pay for the rail/road in the tunnel including entrances */ switch (transport_type) { - case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * _price[PR_BUILD_ROAD] * 2); break; + case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * _price[PR_BUILD_ROAD] * 2 * CountBits(rts)); break; case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break; default: NOT_REACHED(); } @@ -1125,6 +1229,103 @@ static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bo } } +/* Draws a signal on tunnel / bridge entrance tile. */ +static void DrawTunnelBridgeRampSignal(const TileInfo *ti) +{ + bool side = (_settings_game.vehicle.road_side != 0) &&_settings_game.construction.train_signal_side; + + static const Point SignalPositions[2][4] = { + { /* X X Y Y Signals on the left side */ + {13, 3}, { 2, 13}, { 3, 4}, {13, 14} + }, {/* X X Y Y Signals on the right side */ + {14, 13}, { 3, 3}, {13, 2}, { 3, 13} + } + }; + + uint position; + DiagDirection dir = GetTunnelBridgeDirection(ti->tile); + + switch (dir) { + default: NOT_REACHED(); + case DIAGDIR_NE: position = 0; break; + case DIAGDIR_SE: position = 2; break; + case DIAGDIR_SW: position = 1; break; + case DIAGDIR_NW: position = 3; break; + } + + uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x; + uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y; + uint z = ti->z; + + if (ti->tileh != SLOPE_FLAT && IsBridge(ti->tile)) z += 8; // sloped bridge head + SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC); + + SpriteID sprite; + if (variant == SIG_ELECTRIC) { + /* Normal electric signals are picked from original sprites. */ + sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + IsTunnelBridgeWithSignGreen(ti->tile)); + } else { + /* All other signals are picked from add on sprites. */ + sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + IsTunnelBridgeWithSignGreen(ti->tile)); + } + + AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR); +} + +/* Draws a signal on tunnel / bridge entrance tile. */ +static void DrawBrigeSignalOnMiddelPart(const TileInfo *ti, TileIndex bridge_start_tile, uint z) +{ + + uint bridge_signal_position = 0; + int m2_position = 0; + + uint bridge_section = GetTunnelBridgeLength(ti->tile, bridge_start_tile) + 1; + + while (bridge_signal_position <= bridge_section) { + bridge_signal_position += _settings_client.gui.simulated_wormhole_signals; + if (bridge_signal_position == bridge_section) { + bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side; + + static const Point SignalPositions[2][4] = { + { /* X X Y Y Signals on the left side */ + {11, 3}, { 4, 13}, { 3, 4}, {11, 13} + }, {/* X X Y Y Signals on the right side */ + {11, 13}, { 4, 3}, {13, 4}, { 3, 11} + } + }; + + uint position; + + switch (GetTunnelBridgeDirection(bridge_start_tile)) { + default: NOT_REACHED(); + case DIAGDIR_NE: position = 0; break; + case DIAGDIR_SE: position = 2; break; + case DIAGDIR_SW: position = 1; break; + case DIAGDIR_NW: position = 3; break; + } + + uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][position].x; + uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][position].y; + z += 5; + + SignalVariant variant = (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC); + + SpriteID sprite; + + if (variant == SIG_ELECTRIC) { + /* Normal electric signals are picked from original sprites. */ + sprite = SPR_ORIGINAL_SIGNALS_BASE + ((position << 1) + !HasBit(GetTile(bridge_start_tile)->m2, m2_position)); + } else { + /* All other signals are picked from add on sprites. */ + sprite = SPR_SIGNALS_BASE + ((SIGTYPE_NORMAL - 1) * 16 + variant * 64 + (position << 1) + !HasBit(GetTile(bridge_start_tile)->m2, m2_position)); + } + + AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR); + } + m2_position++; + } +} + /** * Draws a tunnel of bridge tile. * For tunnels, this is rather simple, as you only need to draw the entrance. @@ -1206,6 +1407,8 @@ static void DrawTile_TunnelBridge(TileInfo *ti) if (surface != 0) DrawGroundSprite(surface + tunnelbridge_direction, PAL_NONE); } + DrawOverlay(ti, MP_TUNNELBRIDGE); + /* PBS debugging, draw reserved tracks darker */ if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) { if (rti->UsesOverlay()) { @@ -1239,6 +1442,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti) AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x, ti->y, BB_data[6], BB_data[7], TILE_HEIGHT, ti->z); AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, ti->x + BB_data[4], ti->y + BB_data[5], BB_data[6], BB_data[7], TILE_HEIGHT, ti->z); + /* Draw signals for tunnel. */ + if (IsTunnelBridgeEntrance(ti->tile)) DrawTunnelBridgeRampSignal(ti); + DrawBridgeMiddle(ti); } else { // IsBridge(ti->tile) const PalSpriteID *psid; @@ -1346,6 +1552,9 @@ static void DrawTile_TunnelBridge(TileInfo *ti) } } + /* Draw signals for bridge. */ + if (HasWormholeSignals(ti->tile)) DrawTunnelBridgeRampSignal(ti); + DrawBridgeMiddle(ti); } } @@ -1494,6 +1703,9 @@ void DrawBridgeMiddle(const TileInfo *ti) if (HasRailCatenaryDrawn(GetRailType(rampsouth))) { DrawRailCatenaryOnBridge(ti); } + if (HasWormholeSignals(rampsouth)) { + IsTunnelBridgeExit(rampsouth) ? DrawBrigeSignalOnMiddelPart(ti, rampnorth, z): DrawBrigeSignalOnMiddelPart(ti, rampsouth, z); + } } /* draw roof, the component of the bridge which is logically between the vehicle and the camera */ @@ -1582,9 +1794,9 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td) TransportType tt = GetTunnelBridgeTransportType(tile); if (IsTunnel(tile)) { - td->str = (tt == TRANSPORT_RAIL) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD; - } else { // IsBridge(tile) - td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt]; + td->str = (tt == TRANSPORT_RAIL) ? HasWormholeSignals(tile) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD_SIGNAL : STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD; + } else { // IsBridge(tile) + td->str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : HasWormholeSignals(tile) ? STR_LAI_BRIDGE_DESCRIPTION_RAILROAD_SIGNAL : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt]; } td->owner[0] = GetTileOwner(tile); @@ -1654,6 +1866,26 @@ static void TileLoop_TunnelBridge(TileIndex tile) } } +static bool ClickTile_TunnelBridge(TileIndex tile) +{ + /* Show vehicles found in tunnel. */ + if (IsTunnelTile(tile)) { + int count = 0; + const Train *t; + TileIndex tile_end = GetOtherTunnelBridgeEnd(tile); + FOR_ALL_TRAINS(t) { + if (!t->IsFrontEngine()) continue; + if (tile == t->tile || tile_end == t->tile) { + ShowVehicleViewWindow(t); + count++; + } + if (count > 19) break; // no more than 20 windows open + } + if (count > 0) return true; + } + return false; +} + static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) { TransportType transport_type = GetTunnelBridgeTransportType(tile); @@ -1899,6 +2131,178 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); } +static void CopyPastePlaceTunnel(GenericTileIndex tile, DiagDirection dir, uint mid_len, TransportType transport_type, uint rail_road_types) +{ + GenericTileIndex end_tile = TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x * (mid_len + 1), TileIndexDiffCByDiagDir(dir).y * (mid_len + 1)); + if (IsMainMapTile(tile)) { + _current_pasting->DoCommand(AsMainMapTile(tile), rail_road_types | (transport_type << 8), AsMainMapTile(end_tile), CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE)); + if (_current_pasting->last_result.Failed() && _current_pasting->last_result.GetErrorMessage() == _current_pasting->err_message && _build_tunnel_endtile != 0) { + _current_pasting->err_tile = _build_tunnel_endtile; + } + } else { + if (transport_type == TRANSPORT_RAIL) { + MakeRailTunnel(tile, OWNER_NONE, dir, (RailType)rail_road_types); + MakeRailTunnel(end_tile, OWNER_NONE, ReverseDiagDir(dir), (RailType)rail_road_types); + } else { + MakeRoadTunnel(tile, OWNER_NONE, dir, (RoadTypes)rail_road_types); + MakeRoadTunnel(end_tile, OWNER_NONE, ReverseDiagDir(dir), (RoadTypes)rail_road_types); + } + } +} + +static void CopyPastePlaceBridge(GenericTileIndex tile, DiagDirection dir, uint mid_len, BridgeType bridgetype, TransportType transport_type, uint rail_road_types) +{ + GenericTileIndex end_tile = TILE_ADDXY(tile, TileIndexDiffCByDiagDir(dir).x * (mid_len + 1), TileIndexDiffCByDiagDir(dir).y * (mid_len + 1)); + if (IsMainMapTile(tile)) { + _current_pasting->DoCommand(AsMainMapTile(end_tile), AsMainMapTile(tile), bridgetype | (rail_road_types << 8) | (transport_type << 15), CMD_BUILD_BRIDGE | CMD_MSG(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE)); + } else { + switch (transport_type) { + case TRANSPORT_RAIL: + MakeRailBridgeRamp(tile, OWNER_NONE, bridgetype, dir, (RailType)rail_road_types); + MakeRailBridgeRamp(end_tile, OWNER_NONE, bridgetype, ReverseDiagDir(dir), (RailType)rail_road_types); + break; + + case TRANSPORT_ROAD: + MakeRoadBridgeRamp(tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, bridgetype, dir, (RoadTypes)rail_road_types); + MakeRoadBridgeRamp(end_tile, OWNER_NONE, OWNER_NONE, OWNER_NONE, bridgetype, ReverseDiagDir(dir), (RoadTypes)rail_road_types); + break; + + case TRANSPORT_WATER: + MakeAqueductBridgeRamp(tile, OWNER_NONE, dir); + MakeAqueductBridgeRamp(end_tile, OWNER_NONE, ReverseDiagDir(dir)); + break; + + default: + NOT_REACHED(); + } + + Axis axis = DiagDirToAxis(dir); + while (mid_len-- > 0) { + tile = TileAddByDiagDir(tile, dir); + SetBridgeMiddle(tile, axis); + } + } +} + +/** + * Test a given tunnel/bridge tile if there is any contented to be copied from it. + * + * Tunnels and bridges can't be copy/pasted tile by tile, we have to do it in one step for all + * tiles (booth ends). So the function writes the other end to location pointed by \c other_end + * but only once per a tunnel/bridge - when it's northern tile is tested. For the second tile + * (second end) it still returns \c true but writes "invalid" tile. + * + * If the funtion returns \c false, \c object_rect remains unchanged. + * + * @param tile the tile to test + * @param src_area the area we are copying + * @param mode copy-paste mode + * @param other_end (out, may be NULL) other end or "invalid" tile, depending on which tile of the bridge was given + * @param company the #Company to check ownership against to + * @param preview (out, may be NULL) information on how to higlight preview of the tile + * @return whether this tile needs to be copy-pasted + */ +bool TestTunnelBridgeTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileIndex *other_end, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) +{ + if (preview != NULL) MemSetT(preview, 0); + + /* test ownership */ + if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; + /* test if tunnel/bridge transport type is enabled in the current copy/paste mode */ + TransportType tt = GetTunnelBridgeTransportType(tile); + assert_compile(CPM_WITH_RAIL_TRANSPORT == 1 << TRANSPORT_RAIL); + if (!HasBit(mode, tt)) return false; + + if (IsMainMapTile(tile) || other_end != NULL) { + GenericTileIndex end_tile = GetOtherTunnelBridgeEnd(tile); + /* test if tunnel/bridge is within copy area */ + if (IsMainMapTile(tile) && !src_area.Contains(end_tile)) return false; + + if (other_end != NULL) { + *other_end = end_tile; + if (tile > end_tile) *other_end = GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)); // copy this tunnel/bridge only once + } + } + + if (preview != NULL) { + preview->highlight_tile_rect = true; + if (tt == TRANSPORT_RAIL) preview->highlight_track_bits = AxisToTrackBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))); + } + + return true; +} + +void CopyPasteTile_TunnelBridge(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) +{ + GenericTileIndex src_other_end; + if (!TestTunnelBridgeTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &src_other_end)) return; // src_other_end will be acquired + if (!IsValidTileIndex(src_other_end)) return; // copy this tunnel/bridge only once + + DiagDirection src_dir = GetTunnelBridgeDirection(src_tile); + DiagDirection dst_dir = TransformDiagDir(src_dir, copy_paste.transformation); + uint mid_len = GetTunnelBridgeLength(src_tile, src_other_end); + + /* Terrafrom tiles if needed */ + if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { + TileIndex first_end = AsMainMapTile(dst_tile); + TileIndex second_end = first_end + (mid_len + 1) * ToTileIndexDiff(TileIndexDiffCByDiagDir(dst_dir)); + + /* copy-paste heights around entrances/heads */ + CopyPasteHeights(GenericTileArea(src_tile, 1, 1), GenericTileIndex(first_end), copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + CopyPasteHeights(GenericTileArea(src_other_end, 1, 1), GenericTileIndex(second_end), copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + + /* now level land in the middle */ + if (mid_len > 1) { + /* calculate height and leveling variant */ + CopyPasteLevelVariant variant; + uint height; + if (IsTunnel(src_tile)) { + variant = CPLV_LEVEL_BELOW; + height = GetTileMaxZ(src_tile); + } else { + variant = CPLV_LEVEL_ABOVE; + height = GetBridgeHeight(src_tile) - 1; + } + height += copy_paste.height_delta; + + /* strat from the northern corner of the second (counting from north) middle tile + * and finish at the southern corner of the last but one middle tile */ + TileArea leveling_area(first_end, second_end); + if (DiagDirToAxis(dst_dir) == AXIS_X) { + leveling_area.tile += TileDiffXY(2, 0); + leveling_area.w -= 3; // length + leveling_area.h += 1; + } else { + leveling_area.tile += TileDiffXY(0, 2); + leveling_area.w += 1; + leveling_area.h -= 3; // length + } + + /* level land */ + LevelPasteLand(leveling_area, height, variant); + if (IsPastingInterrupted()) return; + } + } + + TransportType transport_type = GetTunnelBridgeTransportType(src_tile); + uint rail_road_types = 0; + switch (transport_type) { + case TRANSPORT_RAIL: rail_road_types = (copy_paste.mode & CPM_CONVERT_RAILTYPE) ? copy_paste.railtype : GetRailType(src_tile); break; + case TRANSPORT_ROAD: rail_road_types = GetRoadTypes(src_tile); break; + default: break; + } + + if (IsTunnel(src_tile)) { + CopyPastePlaceTunnel(dst_tile, dst_dir, mid_len, transport_type, rail_road_types); + } else { + BridgeType bridge_type = (copy_paste.mode & CPM_UPGRADE_BRIDGES) ? FastestAvailableBridgeType(mid_len) : GetBridgeType(src_tile); + CopyPastePlaceBridge(dst_tile, dst_dir, mid_len, bridge_type, transport_type, rail_road_types); + } +} + + extern const TileTypeProcs _tile_type_tunnelbridge_procs = { DrawTile_TunnelBridge, // draw_tile_proc GetSlopePixelZ_TunnelBridge, // get_slope_z_proc @@ -1906,7 +2310,7 @@ extern const TileTypeProcs _tile_type_tunnelbridge_procs = { NULL, // add_accepted_cargo_proc GetTileDesc_TunnelBridge, // get_tile_desc_proc GetTileTrackStatus_TunnelBridge, // get_tile_track_status_proc - NULL, // click_tile_proc + ClickTile_TunnelBridge, // click_tile_proc NULL, // animate_tile_proc TileLoop_TunnelBridge, // tile_loop_proc ChangeTileOwner_TunnelBridge, // change_tile_owner_proc @@ -1914,4 +2318,5 @@ extern const TileTypeProcs _tile_type_tunnelbridge_procs = { VehicleEnter_TunnelBridge, // vehicle_enter_tile_proc GetFoundation_TunnelBridge, // get_foundation_proc TerraformTile_TunnelBridge, // terraform_tile_proc + CopyPasteTile_TunnelBridge, // copypaste_tile_proc }; diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 0f7f17b3a..a077f1108 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -25,11 +25,16 @@ * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return the above mentioned direction */ -static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) +template +static inline DiagDirection GetTunnelBridgeDirection(typename TileIndexT::T t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(GetTile(t)->m5, 0, 2); } +/** @copydoc GetTunnelBridgeDirection(TileIndexT::T) */ +static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) { return GetTunnelBridgeDirection(t); } +/** @copydoc GetTunnelBridgeDirection(TileIndexT::T) */ +static inline DiagDirection GetTunnelBridgeDirection(GenericTileIndex t) { return GetTunnelBridgeDirection(t); } /** * Tunnel: Get the transport type of the tunnel (road or rail) @@ -38,11 +43,16 @@ static inline DiagDirection GetTunnelBridgeDirection(TileIndex t) * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return the transport type in the tunnel/bridge */ -static inline TransportType GetTunnelBridgeTransportType(TileIndex t) +template +static inline TransportType GetTunnelBridgeTransportType(typename TileIndexT::T t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return (TransportType)GB(_m[t].m5, 2, 2); + return (TransportType)GB(GetTile(t)->m5, 2, 2); } +/** @copydoc GetTunnelBridgeTransportType(TileIndexT::T) */ +static inline TransportType GetTunnelBridgeTransportType(TileIndex t) { return GetTunnelBridgeTransportType(t); } +/** @copydoc GetTunnelBridgeTransportType(TileIndexT::T) */ +static inline TransportType GetTunnelBridgeTransportType(GenericTileIndex t) { return GetTunnelBridgeTransportType(t); } /** * Tunnel: Is this tunnel entrance in a snowy or desert area? @@ -54,7 +64,7 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t) static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - return HasBit(_me[t].m7, 5); + return HasBit(GetTileEx(t)->m7, 5); } /** @@ -68,7 +78,7 @@ static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) { assert(IsTileType(t, MP_TUNNELBRIDGE)); - SB(_me[t].m7, 5, 1, snow_or_desert); + SB(GetTileEx(t)->m7, 5, 1, snow_or_desert); } /** @@ -77,11 +87,16 @@ static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) * @pre IsTileType(t, MP_TUNNELBRIDGE) * @return other end */ -static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) +template +static inline typename TileIndexT::T GetOtherTunnelBridgeEnd(typename TileIndexT::T t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t); } +/** @copydoc GetOtherTunnelBridgeEnd(TileIndexT::T) */ +static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t) { return GetOtherTunnelBridgeEnd(t); } +/** @copydoc GetOtherTunnelBridgeEnd(TileIndexT::T) */ +static inline GenericTileIndex GetOtherTunnelBridgeEnd(GenericTileIndex t) { return GetOtherTunnelBridgeEnd(t); } /** @@ -94,7 +109,7 @@ static inline bool HasTunnelBridgeReservation(TileIndex t) { assert(IsTileType(t, MP_TUNNELBRIDGE)); assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); - return HasBit(_m[t].m5, 4); + return HasBit(GetTile(t)->m5, 4); } /** @@ -107,7 +122,7 @@ static inline void SetTunnelBridgeReservation(TileIndex t, bool b) { assert(IsTileType(t, MP_TUNNELBRIDGE)); assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL); - SB(_m[t].m5, 4, 1, b ? 1 : 0); + SB(GetTile(t)->m5, 4, 1, b ? 1 : 0); } /** @@ -121,4 +136,98 @@ static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t) return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; } +/** + * Declare tunnel/bridge with signal simulation. + * @param t the tunnel/bridge tile. + */ +static inline void SetBitTunnelBridgeSignal(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + SetBit(GetTile(t)->m5, 5); +} + +/** + * Remove tunnel/bridge with signal simulation. + * @param t the tunnel/bridge tile. + */ +static inline void ClrBitTunnelBridgeSignal(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + ClrBit(GetTile(t)->m5, 5); +} + +/** + * Declare tunnel/bridge exit. + * @param t the tunnel/bridge tile. + */ +static inline void SetBitTunnelBridgeExit(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + SetBit(GetTile(t)->m5, 6); +} + +/** + * Remove tunnel/bridge exit declaration. + * @param t the tunnel/bridge tile. + */ +static inline void ClrBitTunnelBridgeExit(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + ClrBit(GetTile(t)->m5, 6); +} + +/** + * Is this a tunnel/bridge pair with signal simulation? + * On tunnel/bridge pair minimal one of the two bits is set. + * @param t the tile that might be a tunnel/bridge. + * @return true if and only if this tile is a tunnel/bridge with signal simulation. + */ +static inline bool HasWormholeSignals(TileIndex t) +{ + return IsTileType(t, MP_TUNNELBRIDGE) && (HasBit(GetTile(t)->m5, 5) || HasBit(GetTile(t)->m5, 6)) ; +} + +/** + * Is this a tunnel/bridge with sign on green? + * @param t the tile that might be a tunnel/bridge with sign set green. + * @pre IsTileType(t, MP_TUNNELBRIDGE) + * @return true if and only if this tile is a tunnel/bridge entrance. + */ +static inline bool IsTunnelBridgeWithSignGreen(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + return HasBit(GetTile(t)->m5, 5) && !HasBit(GetTile(t)->m5, 6); +} + +static inline bool IsTunnelBridgeWithSignRed(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + return HasBit(GetTile(t)->m5, 5) && HasBit(GetTile(t)->m5, 6); +} + +/** + * Is this a tunnel/bridge entrance tile with signal? + * Tunnel bridge signal simulation has allways bit 5 on at entrance. + * @param t the tile that might be a tunnel/bridge. + * @return true if and only if this tile is a tunnel/bridge entrance. + */ +static inline bool IsTunnelBridgeEntrance(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + return HasBit(GetTile(t)->m5, 5) ; +} + +/** + * Is this a tunnel/bridge exit? + * @param t the tile that might be a tunnel/bridge. + * @return true if and only if this tile is a tunnel/bridge exit. + */ +static inline bool IsTunnelBridgeExit(TileIndex t) +{ + assert(IsTileType(t, MP_TUNNELBRIDGE)); + return !HasBit(GetTile(t)->m5, 5) && HasBit(GetTile(t)->m5, 6); +} + + + #endif /* TUNNELBRIDGE_MAP_H */ diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 4dbdf24df..fcaadc886 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -262,6 +262,13 @@ uint Vehicle::Crash(bool flooded) return RandomRange(pass + 1); // Randomise deceased passengers. } +bool Vehicle::IsDrawn() const +{ + return !(this->vehstatus & VS_HIDDEN) || + (!IsTransparencySet(TO_TUNNELS) && + ((this->type == VEH_TRAIN && Train::From(this)->track == TRACK_BIT_WORMHOLE) || + (this->type == VEH_ROAD && RoadVehicle::From(this)->state == RVSB_WORMHOLE))); +} /** * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking. @@ -851,7 +858,7 @@ Vehicle::~Vehicle() /* sometimes, eg. for disaster vehicles, when company bankrupts, when removing crashed/flooded vehicles, * it may happen that vehicle chain is deleted when visible */ - if (!(this->vehstatus & VS_HIDDEN)) this->MarkAllViewportsDirty(); + if (this->IsDrawn()) this->MarkAllViewportsDirty(); Vehicle *v = this->Next(); this->SetNext(NULL); @@ -956,7 +963,7 @@ void CallVehicleTicks() if (front->vehstatus & VS_CRASHED) continue; /* Do not play any sound when in depot or tunnel */ - if (v->vehstatus & VS_HIDDEN) continue; + if (!v->IsDrawn()) continue; /* Do not play any sound when stopped */ if ((front->vehstatus & VS_STOPPED) && (front->type != VEH_TRAIN || front->cur_speed == 0)) continue; @@ -1054,7 +1061,7 @@ static void DoDrawVehicle(const Vehicle *v) if (v->vehstatus & VS_DEFPAL) pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); /* Check whether the vehicle shall be transparent due to the game state */ - bool shadowed = (v->vehstatus & VS_SHADOW) != 0; + bool shadowed = (v->vehstatus & (VS_SHADOW | VS_HIDDEN)) != 0; if (v->type == VEH_EFFECT) { /* Check whether the vehicle shall be transparent/invisible due to GUI settings. @@ -1111,7 +1118,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi) const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF while (v != NULL) { - if (!(v->vehstatus & VS_HIDDEN) && + if (v->IsDrawn() && l <= v->coord.right && t <= v->coord.bottom && r >= v->coord.left && @@ -1146,7 +1153,7 @@ Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y) y = ScaleByZoom(y, vp->zoom) + vp->virtual_top; FOR_ALL_VEHICLES(v) { - if ((v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0 && + if (v->IsDrawn() && !(v->vehstatus & VS_UNCLICKABLE) && x >= v->coord.left && x <= v->coord.right && y >= v->coord.top && y <= v->coord.bottom) { diff --git a/src/vehicle_base.h b/src/vehicle_base.h index fc40f22a6..06c60d57a 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -354,6 +354,12 @@ public: uint GetConsistTotalCapacity() const; /** + * Is this vehicle drawn? + * @return true if it is drawn + */ + bool IsDrawn() const; + + /** * Marks the vehicles to be redrawn and updates cached variables * * This method marks the area of the vehicle on the screen as dirty. diff --git a/src/viewport.cpp b/src/viewport.cpp index a1bb2c81d..560fc3bec 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -41,19 +41,19 @@ * looking only at the X-coordinate. * * \verbatim - * ╳ * - * ╱ ╲ * - * ╳ 0 ╳ * - * ╱ ╲ ╱ ╲ * - * ╳-1 ╳ 1 ╳ * - * ╱ ╲ ╱ ╲ ╱ ╲ * - * ╳-2 ╳ 0 ╳ 2 ╳ * - * ╲ ╱ ╲ ╱ ╲ ╱ * - * ╳-1 ╳ 1 ╳ * - * ╲ ╱ ╲ ╱ * - * ╳ 0 ╳ * - * ╲ ╱ * - * ╳ * + * ? * + * ? ? * + * ? 0 ? * + * ? ? ? ? * + * ?-1 ? 1 ? * + * ? ? ? ? ? ? * + * ?-2 ? 0 ? 2 ? * + * ? ? ? ? ? ? * + * ?-1 ? 1 ? * + * ? ? ? ? * + * ? 0 ? * + * ? ? * + * ? * * \endverbatim * * @@ -75,11 +75,14 @@ #include "blitter/factory.hpp" #include "strings_func.h" #include "zoom_func.h" +#include "overlay.h" +#include "overlay_cmd.h" #include "vehicle_func.h" #include "company_func.h" #include "waypoint_func.h" #include "window_func.h" #include "tilehighlight_func.h" +#include "clipboard_gui.h" #include "window_gui.h" #include "linkgraph/linkgraph_gui.h" #include "viewport_sprite_sorter.h" @@ -937,6 +940,32 @@ static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal) DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL); } +/** + * Draws a selection point on a tile. + * + * @param ti TileInfo Tile that is being drawn + * @param pal Palette to apply. + */ +static void DrawPointSelection(const TileInfo *ti, PaletteID pal) +{ + /* Figure out the Z coordinate for the single dot. */ + int z = 0; + FoundationPart foundation_part = FOUNDATION_PART_NORMAL; + if (ti->tileh & SLOPE_N) { + z += TILE_HEIGHT; + if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; + } + if (IsHalftileSlope(ti->tileh)) { + Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); + if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; + if (halftile_corner != CORNER_S) { + foundation_part = FOUNDATION_PART_HALFTILE; + if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; + } + } + DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, pal, ti, z, foundation_part); +} + static bool IsPartOfAutoLine(int px, int py) { px -= _thd.selstart.x; @@ -1004,6 +1033,31 @@ static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type) DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part); } +static void DrawPastePreviewSelection(const TileInfo *ti, bool is_redsq) +{ + TilePastePreview tile_preview; + GetTilePastePreview(ti->tile, &tile_preview); + + /* draw tile rectangle */ + if (!is_redsq && tile_preview.highlight_tile_rect) DrawTileSelectionRect(ti, PAL_NONE); + + /* draw tracks */ + Track t; + FOR_EACH_SET_TRACK(t, tile_preview.highlight_track_bits) DrawAutorailSelection(ti, t); + + /* draw height point */ + PaletteID pal; + int height_diff = tile_preview.tile_height - TileHeight(ti->tile); + if (height_diff > 0) { + pal = PALETTE_SEL_TILE_RED; // target height is grater then current + } else if (height_diff < 0) { + pal = PALETTE_SEL_TILE_BLUE; // target height is lower then current + } else { + pal = PAL_NONE; // target and current height is the same + } + DrawPointSelection(ti, pal); +} + /** * Checks if the specified tile is selected and if so draws selection using correct selectionstyle. * @param *ti TileInfo Tile that is being drawn @@ -1029,22 +1083,11 @@ draw_inner: if (_thd.drawstyle & HT_RECT) { if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE); } else if (_thd.drawstyle & HT_POINT) { - /* Figure out the Z coordinate for the single dot. */ - int z = 0; - FoundationPart foundation_part = FOUNDATION_PART_NORMAL; - if (ti->tileh & SLOPE_N) { - z += TILE_HEIGHT; - if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; - } - if (IsHalftileSlope(ti->tileh)) { - Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); - if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; - if (halftile_corner != CORNER_S) { - foundation_part = FOUNDATION_PART_HALFTILE; - if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; - } + if (_thd.place_mode & HT_PASTE_PREVIEW) { + DrawPastePreviewSelection(ti, is_redsq); + } else { + DrawPointSelection(ti, PAL_NONE); } - DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part); } else if (_thd.drawstyle & HT_RAIL) { /* autorail highlight piece under cursor */ HighLightStyle type = _thd.drawstyle & HT_DIR_MASK; @@ -1255,8 +1298,7 @@ static void ViewportAddTownNames(DrawPixelInfo *dpi) const Town *t; FOR_ALL_TOWNS(t) { ViewportAddString(dpi, ZOOM_LVL_OUT_16X, &t->cache.sign, - _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, - STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK, + t->Label(), t->SmallLabel(), STR_VIEWPORT_TOWN_TINY_BLACK, t->index, t->cache.population); } } @@ -2153,7 +2195,7 @@ static void PlaceObject() pt = GetTileBelowCursor(); if (pt.x == -1) return; - if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT) { + if ((_thd.place_mode & HT_DRAG_MASK) == HT_POINT && !(_thd.place_mode & HT_PASTE_PREVIEW)) { pt.x += TILE_SIZE / 2; pt.y += TILE_SIZE / 2; } @@ -2396,8 +2438,10 @@ void UpdateTileSelection() break; case HT_POINT: new_drawstyle = HT_POINT; - x1 += TILE_SIZE / 2; - y1 += TILE_SIZE / 2; + if (!(_thd.place_mode & HT_PASTE_PREVIEW)) { + x1 += TILE_SIZE / 2; + y1 += TILE_SIZE / 2; + } break; case HT_RAIL: /* Draw one highlighted tile in any direction */ @@ -3141,6 +3185,19 @@ EventState VpHandlePlaceSizingDrag() return ES_HANDLED; } +extern EventState VpHandleMouseWheel(int mousewheel) +{ + EventState ret = ES_NOT_HANDLED; + + Window *w = _thd.GetCallbackWnd(); + if (w != NULL) { + ret = w->OnPlaceMouseWheel(GetTileBelowCursor(), mousewheel); + if (ret == ES_HANDLED) SetSelectionTilesDirty(); + } + + return ret; +} + /** * Change the cursor and mouse click/drag handling to a mode for performing special operations like tile area selection, object placement, etc. * @param icon New shape of the mouse cursor. @@ -3225,6 +3282,14 @@ Point GetViewportStationMiddle(const ViewPort *vp, const Station *st) return p; } +void DrawOverlay(const TileInfo *ti, TileType tt) +{ + if (Overlays::Instance()->IsTileInCatchmentArea(ti, PRODUCTION)) { + DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); + } else if (Overlays::Instance()->IsTileInCatchmentArea(ti, ACCEPTANCE)) { + DrawTileSelectionRect(ti, PAL_NONE); + } +} /** Helper class for getting the best sprite sorter. */ struct ViewportSSCSS { VpSorterChecker fct_checker; ///< The check function. diff --git a/src/viewport_func.h b/src/viewport_func.h index cbdcc5019..fb6008d1a 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -17,6 +17,7 @@ #include "window_type.h" #include "tile_type.h" #include "station_type.h" +#include "tile_cmd.h" static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference is displayed as 50m. @@ -84,4 +85,6 @@ void MarkTileDirtyByTileOutsideMap(int x, int y); Point GetViewportStationMiddle(const ViewPort *vp, const Station *st); -#endif /* VIEWPORT_FUNC_H */ +void DrawOverlay(const TileInfo *ti, TileType tt); + +#endif /* VIEWPORT_FUNC_H */ \ No newline at end of file diff --git a/src/viewport_gui.cpp b/src/viewport_gui.cpp index cbd300dec..1a09e97f9 100644 --- a/src/viewport_gui.cpp +++ b/src/viewport_gui.cpp @@ -16,6 +16,7 @@ #include "strings_func.h" #include "zoom_func.h" #include "window_func.h" +#include "gfx_func.h" #include "widgets/viewport_widget.h" @@ -47,15 +48,21 @@ static const NWidgetPart _nested_extra_view_port_widgets[] = { EndContainer(), EndContainer(), NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_EV_FOLLOW_CURSOR), SetMinimalSize(22, 22), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_EXTRA_VIEW_FOLLOW_CURSOR, STR_EXTRA_VIEW_FOLLOW_CURSOR_TT), + EndContainer(), + NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; class ExtraViewportWindow : public Window { + bool follow_cursor; public: ExtraViewportWindow(WindowDesc *desc, int window_number, TileIndex tile) : Window(desc) { + this->follow_cursor = false; + this->InitNested(window_number); NWidgetViewport *nvp = this->GetWidget(WID_EV_VIEWPORT); @@ -117,6 +124,12 @@ public: this->viewport->dest_scrollpos_y = y + (w->viewport->virtual_height - this->viewport->virtual_height) / 2; break; } + + case WID_EV_FOLLOW_CURSOR: + this->follow_cursor ^= true; + this->SetWidgetsDisabledState(this->follow_cursor, WID_EV_MAIN_TO_VIEW, WID_EV_VIEW_TO_MAIN, WIDGET_LIST_END); + this->SetDirty(); + break; } } @@ -130,6 +143,9 @@ public: virtual void OnScroll(Point delta) { + /* do not scroll if following cursor */ + if (this->follow_cursor) return; + this->viewport->scrollpos_x += ScaleByZoom(delta.x, this->viewport->zoom); this->viewport->scrollpos_y += ScaleByZoom(delta.y, this->viewport->zoom); this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; @@ -154,6 +170,25 @@ public: /* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */ HandleZoomMessage(this, this->viewport, WID_EV_ZOOM_IN, WID_EV_ZOOM_OUT); } + + virtual void OnPaint() + { + this->SetWidgetLoweredState(WID_EV_FOLLOW_CURSOR, this->follow_cursor); + this->DrawWidgets(); + } + + virtual void OnMouseLoop() + { + if (!this->follow_cursor) return; + if (FindWindowFromPt(_cursor.pos.x, _cursor.pos.y) != this) + { + Point pt = GetTileBelowCursor(); + if (pt.x > -1 && pt.y > -1) + { + ScrollWindowTo(pt.x, pt.y, -1, this, true); + } + } + } }; static WindowDesc _extra_view_port_desc( diff --git a/src/viewport_type.h b/src/viewport_type.h index 07485c324..bb171f794 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -121,6 +121,9 @@ enum ViewportDragDropSelectionProcess { DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks) DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses) DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks) + + /* Clipboard */ + DDSP_COPY_TO_CLIPBOARD, ///< Copy area to clipboard }; #endif /* VIEWPORT_TYPE_H */ diff --git a/src/void_cmd.cpp b/src/void_cmd.cpp index ffe54df99..83a6760fc 100644 --- a/src/void_cmd.cpp +++ b/src/void_cmd.cpp @@ -83,4 +83,5 @@ extern const TileTypeProcs _tile_type_void_procs = { NULL, // vehicle_enter_tile_proc GetFoundation_Void, // get_foundation_proc TerraformTile_Void, // terraform_tile_proc + NULL // copypaste_tile_proc }; diff --git a/src/void_map.h b/src/void_map.h index 5ccc4e9d7..97ce33f29 100644 --- a/src/void_map.h +++ b/src/void_map.h @@ -18,17 +18,22 @@ * Make a nice void tile ;) * @param t the tile to make void */ -static inline void MakeVoid(TileIndex t) +template +static inline void MakeVoid(typename TileIndexT::T t) { SetTileType(t, MP_VOID); SetTileHeight(t, 0); - _m[t].m1 = 0; - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = 0; - _me[t].m6 = 0; - _me[t].m7 = 0; + GetTile(t)->m1 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = 0; + GetTileEx(t)->m6 = 0; + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeVoid(TileIndexT::T) */ +static inline void MakeVoid(TileIndex t) { MakeVoid(t); } +/** @copydoc MakeVoid(TileIndexT::T) */ +static inline void MakeVoid(GenericTileIndex t) { MakeVoid(t); } #endif /* VOID_MAP_H */ diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 4392eb210..c05a12918 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "cmd_helper.h" +#include "copypaste_cmd.h" #include "landscape.h" #include "viewport_func.h" #include "command_func.h" @@ -38,6 +39,7 @@ #include "date_func.h" #include "company_base.h" #include "company_gui.h" +#include "clipboard_gui.h" #include "newgrf_generic.h" #include "table/strings.h" @@ -105,8 +107,15 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); - if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { - return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); + WaterClass wc1, wc2; + if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { + /* When pasting a ship depot, there may be no water yet (a canal will be placed when DC_EXE'ing). + * Ignore that there is no water so we can calculate the cost more precisely. */ + wc1 = wc2 = WATER_CLASS_INVALID; + } else { + if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); + wc1 = GetWaterClass(tile); + wc2 = GetWaterClass(tile2); } if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); @@ -118,8 +127,6 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui if (!Depot::CanAllocateItem()) return CMD_ERROR; - WaterClass wc1 = GetWaterClass(tile); - WaterClass wc2 = GetWaterClass(tile2); CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); bool add_cost = !IsWaterTile(tile); @@ -146,6 +153,7 @@ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); + assert(wc1 != WATER_CLASS_INVALID && wc2 != WATER_CLASS_INVALID); MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); @@ -863,6 +871,7 @@ static void DrawTile_Water(TileInfo *ti) DrawWaterDepot(ti); break; } + DrawOverlay(ti, MP_WATER); } void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part) @@ -1315,6 +1324,175 @@ static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); } +void CopyPastePlaceCannal(GenericTileIndex tile) +{ + if (IsMainMapTile(tile)) { + _current_pasting->DoCommand(AsMainMapTile(tile), AsMainMapTile(tile), WATER_CLASS_CANAL, CMD_BUILD_CANAL | CMD_MSG(STR_ERROR_CAN_T_BUILD_CANALS)); + } else { + MakeCanal(tile, OWNER_NONE, 0); + } +} + +static void CopyPastePlaceLock(GenericTileIndex tile, DiagDirection dir) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + if (dir != GetInclinedSlopeDirection(GetTilePixelSlope(tile, NULL))) { + _current_pasting->CollectError(t, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION, STR_ERROR_CAN_T_BUILD_LOCKS); + } else if (IsTileType(t, MP_WATER) && IsTileOwner(t, _current_company) && IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) { + _current_pasting->CollectError(t, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_LOCKS); + } else { + _current_pasting->DoCommand(t, 0, 0, CMD_BUILD_LOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_LOCKS)); + } + } else { + MakeLock(tile, OWNER_NONE, dir, WATER_CLASS_INVALID, WATER_CLASS_INVALID, WATER_CLASS_INVALID); + } +} + +static void CopyPastePlaceShipDepot(GenericTileIndex tile, DiagDirection dir) +{ + GenericTileIndex other_tile = TileAddByDiagDir(tile, ReverseDiagDir(dir)); + + if (IsMainMapTile(tile)) { + TileIndex t1 = AsMainMapTile(tile); + TileIndex t2 = AsMainMapTile(other_tile); + /* build a canal if not on water */ + if (!HasTileWaterGround(t1)) { + CopyPastePlaceCannal(GenericTileIndex(t1)); + if (_current_pasting->last_result.Failed()) return; + } + if (!HasTileWaterGround(t2)) { + CopyPastePlaceCannal(GenericTileIndex(t2)); + if (_current_pasting->last_result.Failed()) return; + } + /* build the depot */ + if (IsShipDepotTile(t1) && IsTileOwner(t1, _current_company) && GetOtherShipDepotTile(t1) == t2) { + _current_pasting->CollectError(t1, STR_ERROR_ALREADY_BUILT, STR_ERROR_CAN_T_BUILD_SHIP_DEPOT); + } else { + _current_pasting->DoCommand(min(t1, t2), DiagDirToAxis(dir), 0, CMD_BUILD_SHIP_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT)); + } + } else { + MakeShipDepot(min(tile, other_tile), OWNER_NONE, 0, DEPOT_PART_NORTH, DiagDirToAxis(dir), WATER_CLASS_INVALID); + MakeShipDepot(max(tile, other_tile), OWNER_NONE, 0, DEPOT_PART_SOUTH, DiagDirToAxis(dir), WATER_CLASS_INVALID); + } +} + +/** + * Test a given water tile if there is any contented to be copied from it. + * + * Some water objects (e.g. water locks) can't be copy/pasted tile by tile, we have to do it with + * bigger rectangular pieces. The function writes this area to location pointed by \c object_rect + * but only once per a piece - when a certain tile is tested: + * - in case of water locks, valid area is written when the center tile of a lock is tested + * - in case of ship depots, valid area is written when the northern tile of a depot is tested + * For the rest of tiles the function still returns \c true but writes "invalid" area. + * + * If the funtion returns \c false, \c object_rect remains unchanged. + * + * @param tile the tile to test + * @param src_area the tile area we are copying + * @param mode copy-paste mode + * @param object_rect (out, may be NULL) area to be copy pasted in this step or "invalid" area, depending on which tile was given + * @param company the #Company to check ownership against to + * @param preview (out, may be NULL) information on how to higlight preview of the tile + * @return whether this tile needs to be copy-pasted + */ +bool TestWaterTileCopyability(GenericTileIndex tile, const GenericTileArea &src_area, CopyPasteMode mode, GenericTileArea *object_rect, CompanyID company = _current_company, TileContentPastePreview *preview = NULL) +{ + if (preview != NULL) MemSetT(preview, 0); + + if (!(mode & CPM_WITH_WATER_TRANSPORT)) return false; + if (IsMainMapTile(tile) && !IsTileOwner(tile, company)) return false; + + switch (GetWaterTileType(tile)) { + case WATER_TILE_CLEAR: + if (GetWaterClass(tile) != WATER_CLASS_CANAL) return false; // copy only cannals + if (object_rect != NULL) *object_rect = GenericTileArea(tile, 1, 1); + break; + + case WATER_TILE_LOCK: + if (IsMainMapTile(tile) || object_rect != NULL) { + DiagDirection dir = GetLockDirection(tile); + switch (GetLockPart(tile)) { + case LOCK_PART_MIDDLE: { + GenericTileArea ta(tile, 1, 1); + ta.Add(TileAddByDiagDir(tile, dir)); + ta.Add(TileAddByDiagDir(tile, ReverseDiagDir(dir))); + if (IsMainMapTile(tile) && !src_area.Contains(ta)) return false; + if (object_rect != NULL) *object_rect = ta; + break; + } + + case LOCK_PART_UPPER: + dir = ReverseDiagDir(dir); + /* FALLTHROUGH */ + case LOCK_PART_LOWER: + if (IsMainMapTile(tile) && !src_area.Contains(TileAddByDiagDir(TileAddByDiagDir(tile, dir), dir))) return false; + if (object_rect != NULL) *object_rect = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); + break; + } + } + break; + + case WATER_TILE_DEPOT: { + /* test if the depot is within copy area */ + GenericTileIndex other_tile = GetOtherShipDepotTile(tile); + if (IsMainMapTile(tile) && !src_area.Contains(other_tile)) return false; + + if (object_rect != NULL) { + if (tile < other_tile) { // copy this depot only once + *object_rect = GenericTileArea(tile, other_tile); + } else { + *object_rect = GenericTileArea(GenericTileIndex(INVALID_TILE_INDEX, MapOf(tile)), 0, 0); + } + } + break; + } + + default: + return false; + } + + if (preview != NULL) preview->highlight_tile_rect = true; + return true; +} + +void CopyPasteTile_Water(GenericTileIndex src_tile, GenericTileIndex dst_tile, const CopyPasteParams ©_paste) +{ + GenericTileArea src_object_rect; + if (!TestWaterTileCopyability(src_tile, copy_paste.src_area, copy_paste.mode, &src_object_rect)) return; + if (!IsValidTileIndex(src_object_rect.tile)) return; // copy water object (e.g. water lock or ship depot) only once + + /* Terraform tiles if needed */ + if (IsMainMapTile(dst_tile) && (copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_MINIMAL) { + GenericTileIndex t = copy_paste.src_area.ReverseTransformTile(src_tile, dst_tile, copy_paste.transformation); // transformed northern tile of the area.src + t = copy_paste.src_area.TransformTile(src_object_rect.tile, t, copy_paste.transformation); // transformed northern tile of the src_object_rect + t = src_object_rect.ReverseTransformedNorth(t, copy_paste.transformation); // northern tile of the transformed src_object_rect + CopyPasteHeights(src_object_rect, t, copy_paste.transformation, copy_paste.height_delta); + if (IsPastingInterrupted()) return; + } + + switch (GetWaterTileType(src_tile)) { + case WATER_TILE_CLEAR: + CopyPastePlaceCannal(dst_tile); + break; + + case WATER_TILE_LOCK: { + DiagDirection dir = TransformDiagDir(GetLockDirection(src_tile), copy_paste.transformation); + CopyPastePlaceLock(dst_tile, dir); + break; + } + + case WATER_TILE_DEPOT: + CopyPastePlaceShipDepot(dst_tile, TransformDiagDir(GetShipDepotDirection(src_tile), copy_paste.transformation)); + break; + + default: + NOT_REACHED(); + break; + } +} + extern const TileTypeProcs _tile_type_water_procs = { DrawTile_Water, // draw_tile_proc @@ -1331,4 +1509,5 @@ extern const TileTypeProcs _tile_type_water_procs = { VehicleEnter_Water, // vehicle_enter_tile_proc GetFoundation_Water, // get_foundation_proc TerraformTile_Water, // terraform_tile_proc + CopyPasteTile_Water, // copypaste_tile_proc }; diff --git a/src/water_map.h b/src/water_map.h index ab249a827..7e251829f 100644 --- a/src/water_map.h +++ b/src/water_map.h @@ -74,17 +74,22 @@ enum LockPart { * @param t Water tile to query. * @return Water tile type at the tile. */ -static inline WaterTileType GetWaterTileType(TileIndex t) +template +static inline WaterTileType GetWaterTileType(typename TileIndexT::T t) { assert(IsTileType(t, MP_WATER)); - switch (GB(_m[t].m5, WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { - case WBL_TYPE_NORMAL: return HasBit(_m[t].m5, WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; + switch (GB(GetTile(t)->m5, WBL_TYPE_BEGIN, WBL_TYPE_COUNT)) { + case WBL_TYPE_NORMAL: return HasBit(GetTile(t)->m5, WBL_COAST_FLAG) ? WATER_TILE_COAST : WATER_TILE_CLEAR; case WBL_TYPE_LOCK: return WATER_TILE_LOCK; case WBL_TYPE_DEPOT: return WATER_TILE_DEPOT; default: NOT_REACHED(); } } +/** @copydoc GetWaterTileType(TileIndexT::T) */ +static inline WaterTileType GetWaterTileType(TileIndex t) { return GetWaterTileType(t); } +/** @copydoc GetWaterTileType(TileIndexT::T) */ +static inline WaterTileType GetWaterTileType(GenericTileIndex t) { return GetWaterTileType(t); } /** * Checks whether the tile has an waterclass associated. @@ -92,10 +97,15 @@ static inline WaterTileType GetWaterTileType(TileIndex t) * @param t Tile to query. * @return True if the tiletype has a waterclass. */ -static inline bool HasTileWaterClass(TileIndex t) +template +static inline bool HasTileWaterClass(typename TileIndexT::T t) { return IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT); } +/** @copydoc HasTileWaterClass(TileIndexT::T) */ +static inline bool HasTileWaterClass(TileIndex t) { return HasTileWaterClass(t); } +/** @copydoc HasTileWaterClass(TileIndexT::T) */ +static inline bool HasTileWaterClass(GenericTileIndex t) { return HasTileWaterClass(t); } /** * Get the water class at a tile. @@ -103,11 +113,16 @@ static inline bool HasTileWaterClass(TileIndex t) * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) * @return Water class at the tile. */ -static inline WaterClass GetWaterClass(TileIndex t) +template +static inline WaterClass GetWaterClass(typename TileIndexT::T t) { assert(HasTileWaterClass(t)); - return (WaterClass)GB(_m[t].m1, 5, 2); + return (WaterClass)GB(GetTile(t)->m1, 5, 2); } +/** @copydoc GetWaterClass(TileIndexT::T) */ +static inline WaterClass GetWaterClass(TileIndex t) { return GetWaterClass(t); } +/** @copydoc GetWaterClass(TileIndexT::T) */ +static inline WaterClass GetWaterClass(GenericTileIndex t) { return GetWaterClass(t); } /** * Set the water class at a tile. @@ -115,11 +130,16 @@ static inline WaterClass GetWaterClass(TileIndex t) * @param wc New water class. * @pre IsTileType(t, MP_WATER) || IsTileType(t, MP_STATION) || IsTileType(t, MP_INDUSTRY) || IsTileType(t, MP_OBJECT) */ -static inline void SetWaterClass(TileIndex t, WaterClass wc) +template +static inline void SetWaterClass(typename TileIndexT::T t, WaterClass wc) { assert(HasTileWaterClass(t)); - SB(_m[t].m1, 5, 2, wc); + SB(GetTile(t)->m1, 5, 2, wc); } +/** @copydoc SetWaterClass(TileIndexT::T,WaterClass) */ +static inline void SetWaterClass(TileIndex t, WaterClass wc) { return SetWaterClass(t, wc); } +/** @copydoc SetWaterClass(TileIndexT::T,WaterClass) */ +static inline void SetWaterClass(GenericTileIndex t, WaterClass wc) { return SetWaterClass(t, wc); } /** * Tests if the tile was built on water. @@ -138,10 +158,15 @@ static inline bool IsTileOnWater(TileIndex t) * @return \c true if any type of clear water like ocean, river, or canal. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsWater(TileIndex t) +template +static inline bool IsWater(typename TileIndexT::T t) { return GetWaterTileType(t) == WATER_TILE_CLEAR; } +/** @copydoc IsWater(TileIndexT::T) */ +static inline bool IsWater(TileIndex t) { return IsWater(t); } +/** @copydoc IsWater(TileIndexT::T) */ +static inline bool IsWater(GenericTileIndex t) { return IsWater(t); } /** * Is it a sea water tile? @@ -160,10 +185,15 @@ static inline bool IsSea(TileIndex t) * @return \c true if it is a canal tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsCanal(TileIndex t) +template +static inline bool IsCanal(typename TileIndexT::T t) { return IsWater(t) && GetWaterClass(t) == WATER_CLASS_CANAL; } +/** @copydoc IsCanal(TileIndexT::T) */ +static inline bool IsCanal(TileIndex t) { return IsCanal(t); } +/** @copydoc IsCanal(TileIndexT::T) */ +static inline bool IsCanal(GenericTileIndex t) { return IsCanal(t); } /** * Is it a river water tile? @@ -181,10 +211,15 @@ static inline bool IsRiver(TileIndex t) * @param t Tile to query. * @return \c true if it is a plain water tile. */ -static inline bool IsWaterTile(TileIndex t) +template +static inline bool IsWaterTile(typename TileIndexT::T t) { return IsTileType(t, MP_WATER) && IsWater(t); } +/** @copydoc IsWaterTile(TileIndexT::T) */ +static inline bool IsWaterTile(TileIndex t) { return IsWaterTile(t); } +/** @copydoc IsWaterTile(TileIndexT::T) */ +static inline bool IsWaterTile(GenericTileIndex t) { return IsWaterTile(t); } /** * Is it a coast tile? @@ -192,10 +227,15 @@ static inline bool IsWaterTile(TileIndex t) * @return \c true if it is a sea water tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsCoast(TileIndex t) +template +static inline bool IsCoast(typename TileIndexT::T t) { return GetWaterTileType(t) == WATER_TILE_COAST; } +/** @copydoc IsCoast(TileIndexT::T) */ +static inline bool IsCoast(TileIndex t) { return IsCoast(t); } +/** @copydoc IsCoast(TileIndexT::T) */ +static inline bool IsCoast(GenericTileIndex t) { return IsCoast(t); } /** * Is it a coast tile @@ -213,20 +253,30 @@ static inline bool IsCoastTile(TileIndex t) * @return \c true if it is a ship depot tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsShipDepot(TileIndex t) +template +static inline bool IsShipDepot(typename TileIndexT::T t) { return GetWaterTileType(t) == WATER_TILE_DEPOT; } +/** @copydoc IsShipDepot(TileIndexT::T) */ +static inline bool IsShipDepot(TileIndex t) { return IsShipDepot(t); } +/** @copydoc IsShipDepot(TileIndexT::T) */ +static inline bool IsShipDepot(GenericTileIndex t) { return IsShipDepot(t); } /** * Is it a ship depot tile? * @param t Tile to query. * @return \c true if it is a ship depot tile. */ -static inline bool IsShipDepotTile(TileIndex t) +template +static inline bool IsShipDepotTile(typename TileIndexT::T t) { return IsTileType(t, MP_WATER) && IsShipDepot(t); } +/** @copydoc IsShipDepotTile(TileIndexT::T) */ +static inline bool IsShipDepotTile(TileIndex t) { return IsShipDepotTile(t); } +/** @copydoc IsShipDepotTile(TileIndexT::T) */ +static inline bool IsShipDepotTile(GenericTileIndex t) { return IsShipDepotTile(t); } /** * Get the axis of the ship depot. @@ -234,11 +284,16 @@ static inline bool IsShipDepotTile(TileIndex t) * @return Axis of the depot. * @pre IsShipDepotTile(t) */ -static inline Axis GetShipDepotAxis(TileIndex t) +template +static inline Axis GetShipDepotAxis(typename TileIndexT::T t) { assert(IsShipDepotTile(t)); - return (Axis)GB(_m[t].m5, WBL_DEPOT_AXIS, 1); + return (Axis)GB(GetTile(t)->m5, WBL_DEPOT_AXIS, 1); } +/** @copydoc GetShipDepotAxis(TileIndexT::T) */ +static inline Axis GetShipDepotAxis(TileIndex t) { return GetShipDepotAxis(t); } +/** @copydoc GetShipDepotAxis(TileIndexT::T) */ +static inline Axis GetShipDepotAxis(GenericTileIndex t) { return GetShipDepotAxis(t); } /** * Get the part of a ship depot. @@ -246,11 +301,16 @@ static inline Axis GetShipDepotAxis(TileIndex t) * @return Part of the depot. * @pre IsShipDepotTile(t) */ -static inline DepotPart GetShipDepotPart(TileIndex t) +template +static inline DepotPart GetShipDepotPart(typename TileIndexT::T t) { assert(IsShipDepotTile(t)); - return (DepotPart)GB(_m[t].m5, WBL_DEPOT_PART, 1); + return (DepotPart)GB(GetTile(t)->m5, WBL_DEPOT_PART, 1); } +/** @copydoc GetShipDepotPart(TileIndexT::T) */ +static inline DepotPart GetShipDepotPart(TileIndex t) { return GetShipDepotPart(t); } +/** @copydoc GetShipDepotPart(TileIndexT::T) */ +static inline DepotPart GetShipDepotPart(GenericTileIndex t) { return GetShipDepotPart(t); } /** * Get the direction of the ship depot. @@ -258,10 +318,15 @@ static inline DepotPart GetShipDepotPart(TileIndex t) * @return Direction of the depot. * @pre IsShipDepotTile(t) */ -static inline DiagDirection GetShipDepotDirection(TileIndex t) +template +static inline DiagDirection GetShipDepotDirection(typename TileIndexT::T t) { return XYNSToDiagDir(GetShipDepotAxis(t), GetShipDepotPart(t)); } +/** @copydoc GetShipDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetShipDepotDirection(TileIndex t) { return GetShipDepotDirection(t); } +/** @copydoc GetShipDepotDirection(TileIndexT::T) */ +static inline DiagDirection GetShipDepotDirection(GenericTileIndex t) { return GetShipDepotDirection(t); } /** * Get the other tile of the ship depot. @@ -269,10 +334,15 @@ static inline DiagDirection GetShipDepotDirection(TileIndex t) * @return Tile containing the other section of the depot. * @pre IsShipDepotTile(t) */ -static inline TileIndex GetOtherShipDepotTile(TileIndex t) +template +static inline typename TileIndexT::T GetOtherShipDepotTile(typename TileIndexT::T t) { - return t + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1) : TileDiffXY(1, 0)); + return t + (GetShipDepotPart(t) != DEPOT_PART_NORTH ? -1 : 1) * (GetShipDepotAxis(t) != AXIS_X ? TileDiffXY(0, 1, MapOf(t)) : TileDiffXY(1, 0, MapOf(t))); } +/** @copydoc GetOtherShipDepotTile(TileIndexT::T) */ +static inline TileIndex GetOtherShipDepotTile(TileIndex t) { return GetOtherShipDepotTile(t); } +/** @copydoc GetOtherShipDepotTile(TileIndexT::T) */ +static inline GenericTileIndex GetOtherShipDepotTile(GenericTileIndex t) { return GetOtherShipDepotTile(t); } /** * Get the most northern tile of a ship depot. @@ -294,10 +364,15 @@ static inline TileIndex GetShipDepotNorthTile(TileIndex t) * @return \c true if it is a water lock tile. * @pre IsTileType(t, MP_WATER) */ -static inline bool IsLock(TileIndex t) +template +static inline bool IsLock(typename TileIndexT::T t) { return GetWaterTileType(t) == WATER_TILE_LOCK; } +/** @copydoc IsLock(TileIndexT::T) */ +static inline bool IsLock(TileIndex t) { return IsLock(t); } +/** @copydoc IsLock(TileIndexT::T) */ +static inline bool IsLock(GenericTileIndex t) { return IsLock(t); } /** * Get the direction of the water lock. @@ -305,11 +380,16 @@ static inline bool IsLock(TileIndex t) * @return Direction of the lock. * @pre IsTileType(t, MP_WATER) && IsLock(t) */ -static inline DiagDirection GetLockDirection(TileIndex t) +template +static inline DiagDirection GetLockDirection(typename TileIndexT::T t) { assert(IsLock(t)); - return (DiagDirection)GB(_m[t].m5, WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); + return (DiagDirection)GB(GetTile(t)->m5, WBL_LOCK_ORIENT_BEGIN, WBL_LOCK_ORIENT_COUNT); } +/** @copydoc GetLockDirection(TileIndexT::T) */ +static inline DiagDirection GetLockDirection(TileIndex t) { return GetLockDirection(t); } +/** @copydoc GetLockDirection(TileIndexT::T) */ +static inline DiagDirection GetLockDirection(GenericTileIndex t) { return GetLockDirection(t); } /** * Get the part of a lock. @@ -317,11 +397,16 @@ static inline DiagDirection GetLockDirection(TileIndex t) * @return The part. * @pre IsTileType(t, MP_WATER) && IsLock(t) */ -static inline byte GetLockPart(TileIndex t) +template +static inline byte GetLockPart(typename TileIndexT::T t) { assert(IsLock(t)); - return GB(_m[t].m5, WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); + return GB(GetTile(t)->m5, WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT); } +/** @copydoc GetLockPart(TileIndexT::T) */ +static inline byte GetLockPart(TileIndex t) { return GetLockPart(t); } +/** @copydoc GetLockPart(TileIndexT::T) */ +static inline byte GetLockPart(GenericTileIndex t) { return GetLockPart(t); } /** * Get the random bits of the water tile. @@ -332,7 +417,7 @@ static inline byte GetLockPart(TileIndex t) static inline byte GetWaterTileRandomBits(TileIndex t) { assert(IsTileType(t, MP_WATER)); - return _m[t].m4; + return GetTile(t)->m4; } /** @@ -356,12 +441,12 @@ static inline void MakeShore(TileIndex t) SetTileType(t, MP_WATER); SetTileOwner(t, OWNER_WATER); SetWaterClass(t, WATER_CLASS_SEA); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN | 1 << WBL_COAST_FLAG; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } /** @@ -371,18 +456,23 @@ static inline void MakeShore(TileIndex t) * @param wc The class of water the tile has to be * @param random_bits Eventual random bits to be set for this tile */ -static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_bits) +template +static inline void MakeWater(typename TileIndexT::T t, Owner o, WaterClass wc, uint8 random_bits) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, wc); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = random_bits; - _m[t].m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = random_bits; + GetTile(t)->m5 = WBL_TYPE_NORMAL << WBL_TYPE_BEGIN; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeWater(TileIndexT::T,Owner,WaterClass,uint8) */ +static inline void MakeWater(TileIndex t, Owner o, WaterClass wc, uint8 random_bits) { MakeWater(t, o, wc, random_bits); } +/** @copydoc MakeWater(TileIndexT::T,Owner,WaterClass,uint8) */ +static inline void MakeWater(GenericTileIndex t, Owner o, WaterClass wc, uint8 random_bits) { MakeWater(t, o, wc, random_bits); } /** * Make a sea tile. @@ -409,11 +499,16 @@ static inline void MakeRiver(TileIndex t, uint8 random_bits) * @param o The owner of the canal * @param random_bits Random bits to be set for this tile */ -static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) +template +static inline void MakeCanal(typename TileIndexT::T t, Owner o, uint8 random_bits) { assert(o != OWNER_WATER); MakeWater(t, o, WATER_CLASS_CANAL, random_bits); } +/** @copydoc MakeCanal(TileIndexT::T,Owner,uint) */ +static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) { MakeCanal(t, o, random_bits); } +/** @copydoc MakeCanal(TileIndexT::T,Owner,uint) */ +static inline void MakeCanal(GenericTileIndex t, Owner o, uint8 random_bits) { MakeCanal(t, o, random_bits); } /** * Make a ship depot section. @@ -424,18 +519,23 @@ static inline void MakeCanal(TileIndex t, Owner o, uint8 random_bits) * @param a Axis of the depot. * @param original_water_class Original water class. */ -static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) +template +static inline void MakeShipDepot(typename TileIndexT::T t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); - _m[t].m2 = did; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = did; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = WBL_TYPE_DEPOT << WBL_TYPE_BEGIN | part << WBL_DEPOT_PART | a << WBL_DEPOT_AXIS; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeShipDepot(TileIndexT::T,Owner,DepotID,DepotPart,Axis,WaterClass) */ +static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { MakeShipDepot(t, o, did, part, a, original_water_class); } +/** @copydoc MakeShipDepot(TileIndexT::T,Owner,DepotID,DepotPart,Axis,WaterClass) */ +static inline void MakeShipDepot(GenericTileIndex t, Owner o, DepotID did, DepotPart part, Axis a, WaterClass original_water_class) { MakeShipDepot(t, o, did, part, a, original_water_class); } /** * Make a lock section. @@ -446,18 +546,23 @@ static inline void MakeShipDepot(TileIndex t, Owner o, DepotID did, DepotPart pa * @param original_water_class Original water class. * @see MakeLock */ -static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) +template +static inline void MakeLockTile(typename TileIndexT::T t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { SetTileType(t, MP_WATER); SetTileOwner(t, o); SetWaterClass(t, original_water_class); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0; - _m[t].m5 = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; - SB(_me[t].m6, 2, 4, 0); - _me[t].m7 = 0; + GetTile(t)->m2 = 0; + GetTile(t)->m3 = 0; + GetTile(t)->m4 = 0; + GetTile(t)->m5 = WBL_TYPE_LOCK << WBL_TYPE_BEGIN | part << WBL_LOCK_PART_BEGIN | dir << WBL_LOCK_ORIENT_BEGIN; + SB(GetTileEx(t)->m6, 2, 4, 0); + GetTileEx(t)->m7 = 0; } +/** @copydoc MakeLockTile(TileIndexT::T,Owner,LockPart,DiagDirection,WaterClass) */ +static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { MakeLockTile(t, o, part, dir, original_water_class); } +/** @copydoc MakeLockTile(TileIndexT::T,Owner,LockPart,DiagDirection,WaterClass) */ +static inline void MakeLockTile(GenericTileIndex t, Owner o, LockPart part, DiagDirection dir, WaterClass original_water_class) { MakeLockTile(t, o, part, dir, original_water_class); } /** * Make a water lock. @@ -468,9 +573,10 @@ static inline void MakeLockTile(TileIndex t, Owner o, LockPart part, DiagDirecti * @param wc_upper Original water class of the upper part. * @param wc_middle Original water class of the middle part. */ -static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) +template +static inline void MakeLock(typename TileIndexT::T t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { - TileIndexDiff delta = TileOffsByDiagDir(d); + TileIndexDiff delta = TileOffsByDiagDir(d, MapOf(t)); /* Keep the current waterclass and owner for the tiles. * It allows to restore them after the lock is deleted */ @@ -478,5 +584,9 @@ static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc MakeLockTile(t - delta, IsWaterTile(t - delta) ? GetTileOwner(t - delta) : o, LOCK_PART_LOWER, d, wc_lower); MakeLockTile(t + delta, IsWaterTile(t + delta) ? GetTileOwner(t + delta) : o, LOCK_PART_UPPER, d, wc_upper); } +/** @copydoc MakeLock(TileIndexT::T,Owner,DiagDirection,WaterClass,WaterClass,WaterClass) */ +static inline void MakeLock(TileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { MakeLock(t, o, d, wc_lower, wc_upper, wc_middle); } +/** @copydoc MakeLock(TileIndexT::T,Owner,DiagDirection,WaterClass,WaterClass,WaterClass) */ +static inline void MakeLock(GenericTileIndex t, Owner o, DiagDirection d, WaterClass wc_lower, WaterClass wc_upper, WaterClass wc_middle) { MakeLock(t, o, d, wc_lower, wc_upper, wc_middle); } #endif /* WATER_MAP_H */ diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 54489fa45..0595db172 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -13,6 +13,8 @@ #include "cmd_helper.h" #include "command_func.h" +#include "copypaste_cmd.h" +#include "clipboard_func.h" #include "landscape.h" #include "bridge_map.h" #include "town.h" @@ -34,6 +36,9 @@ #include "safeguards.h" +extern ClipboardStationsBuilder _clipboard_stations_builder; +extern int _station_cmd_specindex_to_paste; + /** * Update the virtual coords needed to draw the waypoint sign. */ @@ -41,7 +46,7 @@ void Waypoint::UpdateVirtCoord() { Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE); SetDParam(0, this->index); - this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT); + this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT, STR_VIEWPORT_WAYPOINT); /* Recenter viewport */ InvalidateWindowData(WC_WAYPOINT_VIEW, this->index); } @@ -101,8 +106,9 @@ extern CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags); * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. + * @param flags flags for the command */ -static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint) +static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. @@ -120,11 +126,21 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * } } - if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); + /* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing). + * Ignore that so we can calculate the cost more precisely. */ + bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC); + + if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) { + if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); + } Owner owner = GetTileOwner(tile); - CommandCost ret = CheckOwnership(owner); - if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile); + if (!ignore_lack_of_tracks || owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(owner); + if (ret.Failed()) return ret; + } + + CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); @@ -182,9 +198,8 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = INVALID_STATION; - bool distant_join = (station_to_join != INVALID_STATION); - if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR; + if (station_to_join != INVALID_STATION && !Waypoint::IsValidID(station_to_join)) return CMD_ERROR; /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = INVALID_STATION; @@ -193,7 +208,7 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis))); for (int i = 0; i < count; i++) { TileIndex tile = start_tile + i * offset; - CommandCost ret = IsValidTileForWaypoint(tile, axis, &est); + CommandCost ret = IsValidTileForWaypoint(tile, axis, &est, flags); if (ret.Failed()) return ret; } @@ -253,7 +268,14 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint /* But for NewGRF waypoints we like to have their style. */ GetStationLayout(layout_ptr, count, 1, spec); } - byte map_spec_index = AllocateSpecToStation(spec, wp, true); + + byte map_spec_index; + if ((flags & DC_PASTE) && (_station_cmd_specindex_to_paste != -1)) { + assert(_station_cmd_specindex_to_paste == 0 || (_station_cmd_specindex_to_paste < wp->num_specs && wp->speclist[_station_cmd_specindex_to_paste].spec == spec)); + map_spec_index = _station_cmd_specindex_to_paste; + } else { + map_spec_index = AllocateSpecToStation(spec, wp, true); + } Company *c = Company::Get(wp->owner); for (int i = 0; i < count; i++) { @@ -288,7 +310,18 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint */ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + if (tile == 0) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + + WaterClass wc; + if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { + /* When pasting a buoy, there may be no water yet (a canal will be placed when DC_EXE'ing). + * Ignore that there is no water so we can calculate the cost more precisely. */ + wc = WATER_CLASS_INVALID; + } else { + if (!HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); + wc = GetWaterClass(tile); + } + if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); @@ -423,3 +456,54 @@ CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, ui } return CommandCost(); } + +void CopyPastePlaceRailWaypoint(GenericTileIndex tile, StationID sid, Axis axis, RailType rt, StationGfx gfx, StationClassID stat_class, byte stat_type, int specindex, bool adjacent) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + /* check if required track is already there, try to build one if not */ + if (!IsTileOwner(t, _current_company) || + (!IsRailWaypointTile(tile) && !IsPlainRailTile(tile)) || + GetRailType(t) != rt || + (IsTileType(t, MP_STATION) ? GetRailStationAxis(tile) != axis : !HasBit(GetTrackBits(t), AxisToTrack(axis)))) { + CopyPastePlaceTracks(tile, rt, AxisToTrackBits(axis)); + if (_current_pasting->last_result.Failed()) return; + } + /* build the waypoint */ + _station_cmd_specindex_to_paste = specindex; + uint32 p1 = 0; + SB(p1, 0, 4, rt); + SB(p1, 4, 1, axis); + SB(p1, 8, 8, 1); // width + SB(p1, 16, 8, 1); // height + SB(p1, 24, 1, adjacent); + uint32 p2 = 0; + SB(p2, 0, 8, stat_class); + SB(p2, 8, 8, stat_type); + SB(p2, 16, 16, sid); + _current_pasting->DoCommand(t, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT)); + } else { + MakeRailWaypoint(tile, OWNER_NONE, sid, axis, gfx & ~1, rt); + assert(IsInsideMM(specindex, 0, MAX_UVALUE(byte) + 1)); + SetCustomStationSpecIndex(tile, (byte)specindex); + _clipboard_stations_builder.AddRailPart(sid, stat_class, stat_type, (byte)specindex); + } +} + +void CopyPastePlaceBuoy(GenericTileIndex tile, StationID sid, WaterClass wc) +{ + if (IsMainMapTile(tile)) { + TileIndex t = AsMainMapTile(tile); + /* build a piece of canal if not on water */ + if (!HasTileWaterGround(t)) { + CopyPastePlaceCannal(tile); + if (_current_pasting->last_result.Failed()) return; + } + /* build the buoy */ + _current_pasting->DoCommand(t, 0, 0, CMD_BUILD_BUOY | CMD_MSG(STR_ERROR_CAN_T_POSITION_BUOY_HERE)); + } else { + SetTileOwner(tile, OWNER_NONE); + MakeBuoy(tile, sid, wc); + _clipboard_stations_builder.AddPart(sid); + } +} diff --git a/src/widgets/clipboard_widget.h b/src/widgets/clipboard_widget.h new file mode 100644 index 000000000..a4c59d184 --- /dev/null +++ b/src/widgets/clipboard_widget.h @@ -0,0 +1,49 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file clipboard_widget.h Types related to the clipboard widgets. */ + +#ifndef WIDGETS_CLIPBOARD_WIDGET_H +#define WIDGETS_CLIPBOARD_WIDGET_H + +/** Widgets of the #ClipboardToolbarWindow class. */ +enum ClipboardToolbarWidgets { + WID_CT_CLIPBOARD_1, ///< Button to switch to clipboard #1 + WID_CT_CLIPBOARD_2, ///< Button to switch to clipboard #2 + WID_CT_CLIPBOARD_3, ///< Button to switch to clipboard #3 + WID_CT_CLIPBOARD_4, ///< Button to switch to clipboard #4 + + WID_CT_COPY, ///< Copy button (single player) + WID_CT_PASTE, ///< Paste button (single player) + + WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< First button to toggle copy-paste flag + WID_CT_WITH_RAIL = WID_CT_PASTE_FLAG_BUTTON_BEGIN, ///< Toggle rails button + WID_CT_WITH_ROAD, ///< Toggle roads button + WID_CT_WITH_WATER, ///< Toggle water button + WID_CT_WITH_AIR, ///< Toggle air button + WID_CT_MIRROR_SIGNALS, ///< Toggle signal mirrorig button + WID_CT_UPGRADE_BRIDGES, ///< Toggle bridge upgrading button + WID_CT_WITH_STATIONS, ///< Toggle stations button + WID_CT_PASTE_FLAG_BUTTON_END, ///< Past-the-last button to toggle copy-paste flag + + WID_CT_CONVERT_RAILTYPE = WID_CT_PASTE_FLAG_BUTTON_END, ///< Button to select railtype to convert to + + WID_CT_TERRAFORM, ///< Button to select terraforming mode + + WID_CT_TRANSFORMATION, ///< Button to show/reset clipboard transformation + WID_CT_ROTATE_LEFT, ///< Rotate left button + WID_CT_ROTATE_RIGHT, ///< Rotate right button + WID_CT_REFLECT_NE_SW, ///< Reflect against NE-SW axis button + WID_CT_REFLECT_NW_SE, ///< Reflect against NW-SE axis button + + WID_CT_HEIGHT_DIFF_GLYPH, ///< Image in front of buttons to increase/decrease height level + WID_CT_HEIGHT_DIFF, ///< Panel with buttons to increase/decrease height level + WID_CT_HEIGHT_DIFF_INCREASE, ///< Button to increase height level + WID_CT_HEIGHT_DIFF_DECREASE, ///< Button to decrease height level +}; + +#endif /* WIDGETS_CLIPBOARD_WIDGET_H */ diff --git a/src/widgets/group_widget.h b/src/widgets/group_widget.h index 41e0bcd45..7f4a0cdcd 100644 --- a/src/widgets/group_widget.h +++ b/src/widgets/group_widget.h @@ -33,6 +33,7 @@ enum GroupListWidgets { WID_GL_RENAME_GROUP, ///< Rename group button. WID_GL_REPLACE_PROTECTION, ///< Replace protection button. WID_GL_INFO, ///< Group info. + WID_GL_GROUP_INFO, ///< Label for info about group income. }; #endif /* WIDGETS_GROUP_WIDGET_H */ diff --git a/src/widgets/station_widget.h b/src/widgets/station_widget.h index 82fe392e3..2fd2fd408 100644 --- a/src/widgets/station_widget.h +++ b/src/widgets/station_widget.h @@ -23,6 +23,7 @@ enum StationViewWidgets { WID_SV_SCROLLBAR, ///< Scrollbar. WID_SV_ACCEPT_RATING_LIST, ///< List of accepted cargoes / rating of cargoes. WID_SV_LOCATION, ///< 'Location' button. + WID_SV_COVERAGE, ///< Show area coverage button WID_SV_ACCEPTS_RATINGS, ///< 'Accepts' / 'Ratings' button. WID_SV_RENAME, ///< 'Rename' button. WID_SV_CLOSE_AIRPORT, ///< 'Close airport' button. diff --git a/src/widgets/terraform_widget.h b/src/widgets/terraform_widget.h index 7f8a4c4d1..4327d1f25 100644 --- a/src/widgets/terraform_widget.h +++ b/src/widgets/terraform_widget.h @@ -19,6 +19,7 @@ enum TerraformToolbarWidgets { WID_TT_LOWER_LAND = WID_TT_BUTTONS_START, ///< Lower land button. WID_TT_RAISE_LAND, ///< Raise land button. WID_TT_LEVEL_LAND, ///< Level land button. + WID_TT_CLIPBOARD, ///< Button to open the clipboard toolbar WID_TT_DEMOLISH, ///< Demolish aka dynamite button. WID_TT_BUY_LAND, ///< Buy land button. WID_TT_PLANT_TREES, ///< Plant trees button (note: opens separate window, no place-push-button). diff --git a/src/widgets/transparency_widget.h b/src/widgets/transparency_widget.h index 87618fcb6..dd3880ab7 100644 --- a/src/widgets/transparency_widget.h +++ b/src/widgets/transparency_widget.h @@ -25,6 +25,7 @@ enum TransparencyToolbarWidgets { WID_TT_STRUCTURES, ///< Object structure transparency toggle button. WID_TT_CATENARY, ///< Catenary transparency toggle button. WID_TT_LOADING, ///< Loading indicators transparency toggle button. + WID_TT_TUNNELS, ///< Vehicles in tunnels toggle button. WID_TT_END, ///< End of toggle buttons. /* Panel with buttons for invisibility */ diff --git a/src/widgets/viewport_widget.h b/src/widgets/viewport_widget.h index 187659f36..57c964e70 100644 --- a/src/widgets/viewport_widget.h +++ b/src/widgets/viewport_widget.h @@ -14,12 +14,13 @@ /** Widgets of the #ExtraViewportWindow class. */ enum ExtraViewportWidgets { - WID_EV_CAPTION, ///< Caption of window. - WID_EV_VIEWPORT, ///< The viewport. - WID_EV_ZOOM_IN, ///< Zoom in. - WID_EV_ZOOM_OUT, ///< Zoom out. - WID_EV_MAIN_TO_VIEW, ///< Center the view of this viewport on the main view. - WID_EV_VIEW_TO_MAIN, ///< Center the main view on the view of this viewport. + WID_EV_CAPTION, ///< Caption of window. + WID_EV_VIEWPORT, ///< The viewport. + WID_EV_ZOOM_IN, ///< Zoom in. + WID_EV_ZOOM_OUT, ///< Zoom out. + WID_EV_MAIN_TO_VIEW, ///< Center the view of this viewport on the main view. + WID_EV_VIEW_TO_MAIN, ///< Center the main view on the view of this viewport. + WID_EV_FOLLOW_CURSOR, ///< Follow the cursor with this viewport. }; #endif /* WIDGETS_VIEWPORT_WIDGET_H */ diff --git a/src/window.cpp b/src/window.cpp index 8378f60f7..1c6a1c97f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2734,6 +2734,7 @@ enum MouseClick { MAX_OFFSET_HOVER = 5, ///< Maximum mouse movement before stopping a hover event. }; extern EventState VpHandlePlaceSizingDrag(); +extern EventState VpHandleMouseWheel(int mousewheel); static void ScrollMainViewport(int x, int y) { @@ -2795,11 +2796,12 @@ static void MouseLoop(MouseClick click, int mousewheel) HandlePlacePresize(); UpdateTileSelection(); - if (VpHandlePlaceSizingDrag() == ES_HANDLED) return; - if (HandleMouseDragDrop() == ES_HANDLED) return; - if (HandleWindowDragging() == ES_HANDLED) return; - if (HandleScrollbarScrolling() == ES_HANDLED) return; - if (HandleViewportScroll() == ES_HANDLED) return; + if (VpHandlePlaceSizingDrag() == ES_HANDLED) return; + if (VpHandleMouseWheel(mousewheel) == ES_HANDLED) return; + if (HandleMouseDragDrop() == ES_HANDLED) return; + if (HandleWindowDragging() == ES_HANDLED) return; + if (HandleScrollbarScrolling() == ES_HANDLED) return; + if (HandleViewportScroll() == ES_HANDLED) return; HandleMouseOver(); diff --git a/src/window_gui.h b/src/window_gui.h index b81b06e39..a0230028d 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -769,6 +769,15 @@ public: virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) {} /** + * The user scrolling the mouse wheel while the tile highlight mode + * has been set. + * @param pt the exact point on the map where the mouse is. + * @param mousewheel the amount of scrolls. + * @return #ES_HANDLED to prevent viewport from zooming. + */ + virtual EventState OnPlaceMouseWheel(Point pt, int mousewheel) { return ES_NOT_HANDLED; } + + /** * The user moves over the map when a tile highlight mode has been set * when the special mouse mode has been set to 'PRESIZE' mode. An * example of this is the tile highlight for dock building.