From 536a95dfd0d1db0db94963e5eebb4b89daab7661 Mon Sep 17 00:00:00 2001 From: patch-import Date: Sun, 2 Aug 2015 19:37:42 +0100 Subject: [PATCH 1/3] Import combined Enhanced viewport: zoom out, overlays & tooltips (r53_27127) patch https://www.tt-forums.net/viewtopic.php?f=33&t=53394 --- bin/data/route_step.grf | Bin 0 -> 255 bytes projects/openttd_vs100.vcxproj | 10 + projects/openttd_vs100.vcxproj.filters | 30 + projects/openttd_vs80.vcproj | 40 ++ projects/openttd_vs90.vcproj | 40 ++ source.list | 10 + src/airport_gui.cpp | 2 +- src/animated_tile.cpp | 4 +- src/blitter/32bpp_anim.cpp | 44 ++ src/blitter/32bpp_anim.hpp | 2 + src/blitter/32bpp_base.cpp | 20 + src/blitter/32bpp_base.hpp | 2 + src/blitter/32bpp_optimized.cpp | 4 +- src/blitter/8bpp_base.cpp | 5 + src/blitter/8bpp_base.hpp | 1 + src/blitter/8bpp_optimized.cpp | 4 +- src/blitter/base.hpp | 20 + src/blitter/null.hpp | 1 + src/clear_cmd.cpp | 46 +- src/clear_func.h | 6 + src/command.cpp | 21 +- src/command_func.h | 9 +- src/command_type.h | 11 +- src/console_gui.cpp | 2 +- src/dock_gui.cpp | 2 +- src/gfx.cpp | 3 + src/gfxinit.cpp | 108 +++ src/hotkeys.cpp | 2 + src/industry_cmd.cpp | 26 +- src/industry_gui.cpp | 8 + src/lang/english.txt | 67 ++ src/main_gui.cpp | 40 +- src/misc_gui.cpp | 12 +- src/network/core/packet.cpp | 41 +- src/network/core/packet.h | 4 +- src/network/network_client.cpp | 2 +- src/network/network_command.cpp | 26 +- src/network/network_server.cpp | 2 +- src/newgrf_animation_base.h | 2 +- src/newgrf_house.cpp | 4 +- src/newgrf_industrytiles.cpp | 2 +- src/newgrf_station.cpp | 2 +- src/object_cmd.cpp | 4 +- src/openttd.cpp | 3 + src/order_backup.cpp | 2 +- src/order_gui.cpp | 50 +- src/osk_gui.cpp | 2 +- src/pathfinder/npf/npf.cpp | 4 +- src/pathfinder/yapf/yapf_rail.cpp | 2 +- src/pbs.cpp | 16 +- src/plans.cpp | 21 + src/plans_base.h | 237 ++++++ src/plans_cmd.cpp | 140 ++++ src/plans_func.h | 22 + src/plans_gui.cpp | 366 ++++++++++ src/plans_type.h | 25 + src/rail_cmd.cpp | 12 +- src/rail_gui.cpp | 6 +- src/road_cmd.cpp | 6 +- src/road_gui.cpp | 2 +- src/saveload/plans_sl.cpp | 87 +++ src/saveload/saveload.cpp | 4 +- src/script/api/script_object.cpp | 2 +- src/settings.cpp | 2 +- src/settings_gui.cpp | 16 +- src/settings_type.h | 11 + src/signal.cpp | 2 +- src/smallmap_colours.h | 106 +++ src/smallmap_gui.cpp | 138 +--- src/smallmap_gui.h | 28 +- src/sound.cpp | 2 +- src/spritecache.cpp | 96 +++ src/spritecache.h | 2 + src/station.cpp | 2 +- src/stdafx.h | 15 + src/table/darklight_colours.h | 52 ++ src/table/settings.ini | 105 +++ src/table/sprites.h | 10 +- src/table/tree_land.h | 17 + src/tilearea_type.h | 62 ++ src/timetable_gui.cpp | 29 + src/toolbar_gui.cpp | 9 +- src/town_cmd.cpp | 4 +- src/train_cmd.cpp | 20 +- src/tree_cmd.cpp | 12 +- src/tree_gui.cpp | 21 +- src/vehicle.cpp | 62 +- src/vehicle_func.h | 1 + src/vehicle_gui.cpp | 74 +- src/vehicle_gui.h | 30 + src/viewport.cpp | 955 ++++++++++++++++++++++++- src/viewport_func.h | 14 +- src/viewport_gui.cpp | 55 +- src/viewport_type.h | 16 +- src/water_cmd.cpp | 2 +- src/waypoint_cmd.cpp | 2 +- src/widgets/dropdown.cpp | 43 +- src/widgets/dropdown_func.h | 5 +- src/widgets/dropdown_type.h | 11 +- src/widgets/plans_widget.h | 27 + src/window.cpp | 48 +- src/window_func.h | 2 + src/window_gui.h | 7 +- src/window_type.h | 5 + src/zoom_type.h | 10 +- 105 files changed, 3507 insertions(+), 323 deletions(-) create mode 100644 bin/data/route_step.grf create mode 100644 src/plans.cpp create mode 100644 src/plans_base.h create mode 100644 src/plans_cmd.cpp create mode 100644 src/plans_func.h create mode 100644 src/plans_gui.cpp create mode 100644 src/plans_type.h create mode 100644 src/saveload/plans_sl.cpp create mode 100644 src/smallmap_colours.h create mode 100644 src/table/darklight_colours.h create mode 100644 src/widgets/plans_widget.h diff --git a/bin/data/route_step.grf b/bin/data/route_step.grf new file mode 100644 index 0000000000000000000000000000000000000000..4da31fa6fe9ff5d03eea34d35a19f52af5f55a98 GIT binary patch literal 255 zcmXqGU}O?!009;TMn=XL%oo^p@N|gnP`Sd$C<2jSWn|>%=LgB0;9+89><|Y+Js@=L zNa%>^DDLR(=;~P5v8!W8$N7%u9nU)cc8UO@cBc~%Ms^keVLK4c>)Z*{EW^mi12L5u zYTF7PMurZF6JksZ3@?~pu(5&c;^*HWut1_i?1Vvw&H;}Or;b3NRrwutK&$3-tnSz~ z;UtndK&OM;wt%NYVuw1jiHXSz<{7*l5*^A13}#sF@aPabkkBCpG;L#t&V&^mcTXJX N_ + @@ -557,6 +558,9 @@ + + + @@ -587,6 +591,7 @@ + @@ -723,6 +728,7 @@ + @@ -784,6 +790,7 @@ + @@ -812,6 +819,7 @@ + @@ -856,6 +864,7 @@ + @@ -880,6 +889,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index b5dcdfc5a5..8785a528d8 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -300,6 +300,9 @@ Source Files + + Source Files + Source Files @@ -900,6 +903,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + Header Files @@ -990,6 +1002,9 @@ Header Files + + Header Files + Header Files @@ -1398,6 +1413,9 @@ GUI Source Code + + GUI Source Code + GUI Source Code @@ -1581,6 +1599,9 @@ Widgets + + Widgets + Widgets @@ -1665,6 +1686,9 @@ Command handlers + + Command handlers + Command handlers @@ -1797,6 +1821,9 @@ Save/Load handlers + + Save/Load handlers + Save/Load handlers @@ -1869,6 +1896,9 @@ Tables + + Tables + Tables diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 17c2d8d4f1..0823b99128 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -698,6 +698,10 @@ RelativePath=".\..\src\pbs.cpp" > + + @@ -1502,6 +1506,18 @@ RelativePath=".\..\src\pbs.h" > + + + + + + @@ -1622,6 +1638,10 @@ RelativePath=".\..\src\slope_type.h" > + + @@ -2174,6 +2194,10 @@ RelativePath=".\..\src\osk_gui.cpp" > + + @@ -2422,6 +2446,10 @@ RelativePath=".\..\src\widgets\osk_widget.h" > + + @@ -2538,6 +2566,10 @@ RelativePath=".\..\src\order_cmd.cpp" > + + @@ -2718,6 +2750,10 @@ RelativePath=".\..\src\saveload\order_sl.cpp" > + + @@ -2818,6 +2854,10 @@ RelativePath=".\..\src\table\control_codes.h" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 666760ce78..a7f7caa523 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -695,6 +695,10 @@ RelativePath=".\..\src\pbs.cpp" > + + @@ -1499,6 +1503,18 @@ RelativePath=".\..\src\pbs.h" > + + + + + + @@ -1619,6 +1635,10 @@ RelativePath=".\..\src\slope_type.h" > + + @@ -2171,6 +2191,10 @@ RelativePath=".\..\src\osk_gui.cpp" > + + @@ -2419,6 +2443,10 @@ RelativePath=".\..\src\widgets\osk_widget.h" > + + @@ -2535,6 +2563,10 @@ RelativePath=".\..\src\order_cmd.cpp" > + + @@ -2715,6 +2747,10 @@ RelativePath=".\..\src\saveload\order_sl.cpp" > + + @@ -2815,6 +2851,10 @@ RelativePath=".\..\src\table\control_codes.h" > + + diff --git a/source.list b/source.list index 3b66ab9fca..2a2b7ff996 100644 --- a/source.list +++ b/source.list @@ -65,6 +65,7 @@ network/network_udp.cpp openttd.cpp order_backup.cpp pbs.cpp +plans.cpp progress.cpp rail.cpp rev.cpp @@ -296,6 +297,9 @@ order_base.h order_func.h order_type.h pbs.h +plans_base.h +plans_func.h +plans_type.h progress.h querystring_gui.h rail.h @@ -326,6 +330,7 @@ signs_func.h signs_type.h slope_func.h slope_type.h +smallmap_colours.h smallmap_gui.h sortlist_type.h sound_func.h @@ -481,6 +486,7 @@ news_gui.cpp object_gui.cpp order_gui.cpp osk_gui.cpp +plans_gui.cpp rail_gui.cpp road_gui.cpp roadveh_gui.cpp @@ -544,6 +550,7 @@ widgets/news_widget.h widgets/object_widget.h widgets/order_widget.h widgets/osk_widget.h +widgets/plans_widget.h widgets/rail_widget.h widgets/road_widget.h widgets/settings_widget.h @@ -574,6 +581,7 @@ industry_cmd.cpp misc_cmd.cpp object_cmd.cpp order_cmd.cpp +plans_cmd.cpp rail_cmd.cpp road_cmd.cpp roadveh_cmd.cpp @@ -620,6 +628,7 @@ saveload/oldloader.cpp saveload/oldloader.h saveload/oldloader_sl.cpp saveload/order_sl.cpp +saveload/plans_sl.cpp saveload/saveload.cpp saveload/saveload.h saveload/saveload_filter.h @@ -646,6 +655,7 @@ table/build_industry.h table/cargo_const.h table/clear_land.h table/control_codes.h +table/darklight_colours.h table/elrail_data.h table/engines.h table/genland.h diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 29a0d83ada..b526f2987b 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -62,7 +62,7 @@ static void PlaceAirport(TileIndex tile) uint32 p1 = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); p1 |= _selected_airport_layout << 8; - CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" }; + CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_ERROR_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, 0, "" }; ShowSelectStationIfNeeded(cmdcont, TileArea(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE)); } diff --git a/src/animated_tile.cpp b/src/animated_tile.cpp index 78dda8b3b6..5874207316 100644 --- a/src/animated_tile.cpp +++ b/src/animated_tile.cpp @@ -37,7 +37,7 @@ void DeleteAnimatedTile(TileIndex tile) */ memmove(ti, ti + 1, (_animated_tile_list + _animated_tile_count - (ti + 1)) * sizeof(*ti)); _animated_tile_count--; - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); return; } } @@ -50,7 +50,7 @@ void DeleteAnimatedTile(TileIndex tile) */ void AddAnimatedTile(TileIndex tile) { - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); for (const TileIndex *ti = _animated_tile_list; ti < _animated_tile_list + _animated_tile_count; ti++) { if (tile == *ti) return; diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index a3083e40b3..ad401dbffa 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -317,6 +317,50 @@ void Blitter_32bppAnim::SetPixel(void *video, int x, int y, uint8 colour) this->anim_buf[((uint32 *)video - (uint32 *)_screen.dst_ptr) + x + y * this->anim_buf_width] = colour | (DEFAULT_BRIGHTNESS << 8); } +void Blitter_32bppAnim::SetLine(void *video, int x, int y, uint8 *colours, uint width) +{ + Colour *dst = (Colour *)video + x + y * _screen.pitch; + + if (_screen_disable_anim) { + do { + *dst = LookupColourInPalette(*colours); + dst++; + colours++; + } while (--width); + } else { + uint16 *dstanim = (uint16 *)(&this->anim_buf[(uint32 *)video - (uint32 *)_screen.dst_ptr + x + y * _screen.pitch]); + do { + *dstanim = *colours | (DEFAULT_BRIGHTNESS << 8); + *dst = LookupColourInPalette(*colours); + dst++; + dstanim++; + colours++; + } while (--width); + } +} + +void Blitter_32bppAnim::SetLine32(void *video, int x, int y, uint32 *colours, uint width) +{ + Colour *dst = (Colour *)video + x + y * _screen.pitch; + + if (_screen_disable_anim) { + do { + *dst = *colours; + dst++; + colours++; + } while (--width); + } else { + uint16 *dstanim = (uint16 *)(&this->anim_buf[(uint32 *)video - (uint32 *)_screen.dst_ptr + x + y * _screen.pitch]); + do { + *dstanim = 0; + *dst = *colours; + dst++; + dstanim++; + colours++; + } while (--width); + } +} + void Blitter_32bppAnim::DrawRect(void *video, int width, int height, uint8 colour) { if (_screen_disable_anim) { diff --git a/src/blitter/32bpp_anim.hpp b/src/blitter/32bpp_anim.hpp index e707c4437d..1e4a4ea4ae 100644 --- a/src/blitter/32bpp_anim.hpp +++ b/src/blitter/32bpp_anim.hpp @@ -32,6 +32,8 @@ public: /* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); /* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void SetLine(void *video, int x, int y, uint8 *colours, uint width); + /* virtual */ void SetLine32(void *video, int x, int y, uint32 *colours, uint width); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/32bpp_base.cpp b/src/blitter/32bpp_base.cpp index 26dd2f037e..6b7d8ad4e3 100644 --- a/src/blitter/32bpp_base.cpp +++ b/src/blitter/32bpp_base.cpp @@ -24,6 +24,26 @@ void Blitter_32bppBase::SetPixel(void *video, int x, int y, uint8 colour) *((Colour *)video + x + y * _screen.pitch) = LookupColourInPalette(colour); } +void Blitter_32bppBase::SetLine(void *video, int x, int y, uint8 *colours, uint width) +{ + Colour *dst = (Colour *)video + x + y * _screen.pitch; + do { + *dst = LookupColourInPalette(*colours); + dst++; + colours++; + } while (--width); +} + +void Blitter_32bppBase::SetLine32(void *video, int x, int y, uint32 *colours, uint width) +{ + Colour *dst = (Colour *)video + x + y * _screen.pitch; + do { + *dst = *colours; + dst++; + colours++; + } while (--width); +} + void Blitter_32bppBase::DrawRect(void *video, int width, int height, uint8 colour) { Colour colour32 = LookupColourInPalette(colour); diff --git a/src/blitter/32bpp_base.hpp b/src/blitter/32bpp_base.hpp index 26c3dee3fd..22bca227ad 100644 --- a/src/blitter/32bpp_base.hpp +++ b/src/blitter/32bpp_base.hpp @@ -23,6 +23,8 @@ public: /* virtual */ uint8 GetScreenDepth() { return 32; } /* virtual */ void *MoveTo(void *video, int x, int y); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void SetLine(void *video, int x, int y, uint8 *colours, uint width); + /* virtual */ void SetLine32(void *video, int x, int y, uint32 *colours, uint width); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp index cc056f5b59..649b27312c 100644 --- a/src/blitter/32bpp_optimized.cpp +++ b/src/blitter/32bpp_optimized.cpp @@ -280,8 +280,8 @@ Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::Sprite *sprite, Alloc zoom_max = ZOOM_LVL_NORMAL; } else { zoom_min = _settings_client.gui.zoom_min; - zoom_max = _settings_client.gui.zoom_max; - if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX; + zoom_max = (ZoomLevel) min(_settings_client.gui.zoom_max, ZOOM_LVL_DRAW_SPR); + if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_DRAW_SPR; } for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { diff --git a/src/blitter/8bpp_base.cpp b/src/blitter/8bpp_base.cpp index eab6eaa0db..12150eb6af 100644 --- a/src/blitter/8bpp_base.cpp +++ b/src/blitter/8bpp_base.cpp @@ -35,6 +35,11 @@ void Blitter_8bppBase::SetPixel(void *video, int x, int y, uint8 colour) *((uint8 *)video + x + y * _screen.pitch) = colour; } +void Blitter_8bppBase::SetLine(void *video, int x, int y, uint8 *colours, uint width) +{ + memcpy((uint8 *)video + x + y * _screen.pitch, colours, width * sizeof(uint8)); +} + void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8 colour) { do { diff --git a/src/blitter/8bpp_base.hpp b/src/blitter/8bpp_base.hpp index 2dff784992..a99f1180aa 100644 --- a/src/blitter/8bpp_base.hpp +++ b/src/blitter/8bpp_base.hpp @@ -21,6 +21,7 @@ public: /* virtual */ void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal); /* virtual */ void *MoveTo(void *video, int x, int y); /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour); + /* virtual */ void SetLine(void *video, int x, int y, uint8 *colours, uint width); /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour); /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height); /* virtual */ void CopyToBuffer(const void *video, void *dst, int width, int height); diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp index 0f07e7c7bb..90cec8805a 100644 --- a/src/blitter/8bpp_optimized.cpp +++ b/src/blitter/8bpp_optimized.cpp @@ -134,8 +134,8 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::Sprite *sprite, Alloca zoom_max = ZOOM_LVL_NORMAL; } else { zoom_min = _settings_client.gui.zoom_min; - zoom_max = _settings_client.gui.zoom_max; - if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX; + zoom_max = (ZoomLevel) min(_settings_client.gui.zoom_max, ZOOM_LVL_DRAW_SPR); + if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_DRAW_SPR; } for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { diff --git a/src/blitter/base.hpp b/src/blitter/base.hpp index a9403b339d..a699c5a2d0 100644 --- a/src/blitter/base.hpp +++ b/src/blitter/base.hpp @@ -100,6 +100,26 @@ public: */ virtual void SetPixel(void *video, int x, int y, uint8 colour) = 0; + /** + * Draw a sequence of pixels on the video-buffer. + * @param video The destination pointer (video-buffer). + * @param x The x position within video-buffer. + * @param y The y position within video-buffer. + * @param colours A 8bpp colour mapping buffer. + * @param width The length of the line. + */ + virtual void SetLine(void *video, int x, int y, uint8 *colours, uint width) = 0; + + /** + * Draw a sequence of pixels on the video-buffer (no LookupColourInPalette). + * @param video The destination pointer (video-buffer). + * @param x The x position within video-buffer. + * @param y The y position within video-buffer. + * @param colours A 32bpp colour buffer. + * @param width The length of the line. + */ + virtual void SetLine32(void *video, int x, int y, uint32 *colours, uint width) { NOT_REACHED(); }; + /** * Make a single horizontal line in a single colour on the video-buffer. * @param video The destination pointer (video-buffer). diff --git a/src/blitter/null.hpp b/src/blitter/null.hpp index a6fed2ebca..96863c0c3e 100644 --- a/src/blitter/null.hpp +++ b/src/blitter/null.hpp @@ -23,6 +23,7 @@ public: /* virtual */ Sprite *Encode(const SpriteLoader::Sprite *sprite, AllocatorProc *allocator); /* virtual */ void *MoveTo(void *video, int x, int y) { return NULL; }; /* virtual */ void SetPixel(void *video, int x, int y, uint8 colour) {}; + /* virtual */ void SetLine(void *video, int x, int y, uint8 *colours, uint width) {}; /* virtual */ void DrawRect(void *video, int width, int height, uint8 colour) {}; /* virtual */ void DrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash) {}; /* virtual */ void CopyFromBuffer(void *video, const void *src, int width, int height) {}; diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index bfa5949bce..bda6ade14d 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -46,20 +46,45 @@ static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlag flags) return price; } +SpriteID GetSpriteIDForClearLand(const Slope slope, byte set) +{ + return SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(slope) + set * 19; +} + void DrawClearLandTile(const TileInfo *ti, byte set) { - DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh) + set * 19, PAL_NONE); + DrawGroundSprite(GetSpriteIDForClearLand(ti->tileh, set), PAL_NONE); } -void DrawHillyLandTile(const TileInfo *ti) +SpriteID GetSpriteIDForHillyLand(const Slope slope, const uint rough_index) { - if (ti->tileh != SLOPE_FLAT) { - DrawGroundSprite(SPR_FLAT_ROUGH_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + if (slope != SLOPE_FLAT) { + return SPR_FLAT_ROUGH_LAND + SlopeToSpriteOffset(slope); } else { - DrawGroundSprite(_landscape_clear_sprites_rough[GB(ti->x ^ ti->y, 4, 3)], PAL_NONE); + return _landscape_clear_sprites_rough[rough_index]; } } +void DrawHillyLandTile(const TileInfo *ti) +{ + DrawGroundSprite(GetSpriteIDForHillyLand(ti->tileh, GB(ti->x ^ ti->y, 4, 3)), PAL_NONE); +} + +SpriteID GetSpriteIDForRocks(const Slope slope, const uint tile_hash) +{ + return (tile_hash & 1 ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + SlopeToSpriteOffset(slope); +} + +SpriteID GetSpriteIDForFields(const Slope slope, const uint field_type) +{ + return _clear_land_sprites_farmland[field_type] + SlopeToSpriteOffset(slope); +} + +SpriteID GetSpriteIDForSnowDesert(const Slope slope, const uint density) +{ + return _clear_land_sprites_snow_desert[density] + SlopeToSpriteOffset(slope); +} + static void DrawClearLandFence(const TileInfo *ti) { /* combine fences into one sprite object */ @@ -112,17 +137,17 @@ static void DrawTile_Clear(TileInfo *ti) break; case CLEAR_ROCKS: - DrawGroundSprite((TileHash(ti->x, ti->y) & 1 ? SPR_FLAT_ROCKY_LAND_2 : SPR_FLAT_ROCKY_LAND_1) + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + DrawGroundSprite(GetSpriteIDForRocks(ti->tileh, TileHash(ti->x, ti->y)), PAL_NONE); break; case CLEAR_FIELDS: - DrawGroundSprite(_clear_land_sprites_farmland[GetFieldType(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + DrawGroundSprite(GetSpriteIDForFields(ti->tileh, GetFieldType(ti->tile)), PAL_NONE); DrawClearLandFence(ti); break; case CLEAR_SNOW: case CLEAR_DESERT: - DrawGroundSprite(_clear_land_sprites_snow_desert[GetClearDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); + DrawGroundSprite(GetSpriteIDForSnowDesert(ti->tileh, GetClearDensity(ti->tile)), PAL_NONE); break; } @@ -171,7 +196,7 @@ static void UpdateFences(TileIndex tile) dirty = true; } - if (dirty) MarkTileDirtyByTile(tile); + if (dirty) MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } @@ -253,7 +278,6 @@ static void TileLoop_Clear(TileIndex tile) int z; if (IsTileFlat(tile, &z) && z == 0) { DoFloodTile(tile); - MarkTileDirtyByTile(tile); return; } } @@ -307,7 +331,7 @@ static void TileLoop_Clear(TileIndex tile) return; } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } void GenerateClearTile() diff --git a/src/clear_func.h b/src/clear_func.h index b128288d1b..c5bc85d2bd 100644 --- a/src/clear_func.h +++ b/src/clear_func.h @@ -17,4 +17,10 @@ void DrawHillyLandTile(const TileInfo *ti); void DrawClearLandTile(const TileInfo *ti, byte set); +SpriteID GetSpriteIDForClearLand(const Slope slope, byte set); +SpriteID GetSpriteIDForHillyLand(const Slope slope, const uint rough_index); +SpriteID GetSpriteIDForRocks(const Slope slope, const uint tile_hash); +SpriteID GetSpriteIDForFields(const Slope slope, const uint field_type); +SpriteID GetSpriteIDForSnowDesert(const Slope slope, const uint density); + #endif /* CLEAR_FUNC_H */ diff --git a/src/command.cpp b/src/command.cpp index 959610cd28..406a38ae90 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -198,6 +198,12 @@ CommandProc CmdSetTimetableStart; CommandProc CmdOpenCloseAirport; +CommandProc CmdAddPlan; +CommandProc CmdAddPlanLine; +CommandProc CmdRemovePlan; +CommandProc CmdRemovePlanLine; +CommandProc CmdChangePlanVisibility; + #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} /** @@ -354,6 +360,12 @@ 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(CmdAddPlan, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN + DEF_CMD(CmdAddPlanLine, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN_LINE + DEF_CMD(CmdRemovePlan, 0, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN + DEF_CMD(CmdRemovePlanLine, 0, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN_LINE + DEF_CMD(CmdChangePlanVisibility, 0, CMDT_OTHER_MANAGEMENT ), // CMD_CHANGE_PLAN_VISIBILITY }; /*! @@ -542,9 +554,10 @@ bool DoCommandP(const CommandContainer *container, bool my_cmd) * @param callback A callback function to call after the command is finished * @param text The text to pass * @param my_cmd indicator if the command is from a company or server (to display error messages for a user) + * @param binary_length The quantity of binary data in text * @return \c true if the command succeeded, else \c false. */ -bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd) +bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, uint32 binary_length) { /* Cost estimation is generally only done when the * local user presses shift while doing somthing. @@ -574,7 +587,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac if (!(cmd & CMD_NETWORK_COMMAND) && GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = CLIENT_ID_SERVER; #endif - CommandCost res = DoCommandPInternal(tile, p1, p2, cmd, callback, text, my_cmd, estimate_only); + CommandCost res = DoCommandPInternal(tile, p1, p2, cmd, callback, text, my_cmd, estimate_only, binary_length); if (res.Failed()) { /* Only show the error when it's for us. */ StringID error_part1 = GB(cmd, 16, 16); @@ -620,7 +633,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac * @param estimate_only whether to give only the estimate or also execute the command * @return the command cost of this function. */ -CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, bool estimate_only) +CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, bool estimate_only, uint32 binary_length) { /* Prevent recursion; it gives a mess over the network */ assert(_docommand_recursive == 0); @@ -699,7 +712,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, * send it to the command-queue and abort execution */ if (_networking && !_generating_world && !(cmd & CMD_NETWORK_COMMAND)) { - NetworkSendCommand(tile, p1, p2, cmd & ~CMD_FLAGS_MASK, callback, text, _current_company); + NetworkSendCommand(tile, p1, p2, cmd & ~CMD_FLAGS_MASK, callback, text, _current_company, binary_length); cur_company.Restore(); /* Don't return anything special here; no error, no costs. diff --git a/src/command_func.h b/src/command_func.h index c4cc51e3da..42a4ff8804 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -37,13 +37,13 @@ static const CommandCost CMD_ERROR = CommandCost(INVALID_STRING_ID); CommandCost DoCommand(TileIndex tile, uint32 p1, uint32 p2, DoCommandFlag flags, uint32 cmd, const char *text = NULL); CommandCost DoCommand(const CommandContainer *container, DoCommandFlag flags); -bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback = NULL, const char *text = NULL, bool my_cmd = true); +bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback = NULL, const char *text = NULL, bool my_cmd = true, uint32 binary_length = 0); bool DoCommandP(const CommandContainer *container, bool my_cmd = true); -CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, bool estimate_only); +CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, bool my_cmd, bool estimate_only, uint32 binary_length); #ifdef ENABLE_NETWORK -void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, CompanyID company); +void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, CompanyID company, uint32 binary_length); #endif /* ENABLE_NETWORK */ extern Money _additional_cash_required; @@ -102,6 +102,9 @@ CommandCallback CcPlaceSign; CommandCallback CcTerraform; CommandCallback CcGiveMoney; +/* plans_gui.cpp */ +CommandCallback CcAddPlan; + /* rail_gui.cpp */ CommandCallback CcPlaySound1E; CommandCallback CcRailDepot; diff --git a/src/command_type.h b/src/command_type.h index f318216acc..211efa61fd 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -329,6 +329,12 @@ enum Commands { CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft + CMD_ADD_PLAN, + CMD_ADD_PLAN_LINE, + CMD_REMOVE_PLAN, + CMD_REMOVE_PLAN_LINE, + CMD_CHANGE_PLAN_VISIBILITY, + CMD_END, ///< Must ALWAYS be on the end of this list!! (period) }; @@ -466,6 +472,8 @@ struct Command { */ typedef void CommandCallback(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2); +#define MAX_CMD_TEXT_LENGTH 32000 + /** * Structure for buffering the build command when selecting a station to join. */ @@ -475,7 +483,8 @@ struct CommandContainer { uint32 p2; ///< parameter p2. uint32 cmd; ///< command being executed. CommandCallback *callback; ///< any callback function executed upon successful completion of the command. - char text[32 * MAX_CHAR_LENGTH]; ///< possible text sent for name changes etc, in bytes including '\0'. + uint32 binary_length; ///< in case text contains binary data, this describes its length. + char text[MAX_CMD_TEXT_LENGTH]; ///< possible text sent for name changes etc, in bytes including '\0'. }; #endif /* COMMAND_TYPE_H */ diff --git a/src/console_gui.cpp b/src/console_gui.cpp index ed46938cd5..6e807b3e25 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -374,7 +374,7 @@ struct IConsoleWindow : Window this->Scroll(-wheel); } - virtual void OnFocusLost() + virtual void OnFocusLost(Window *newly_focused_window) { VideoDriver::GetInstance()->EditBoxLostFocus(); } diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index cd994bc0be..b68361c7fa 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -190,7 +190,7 @@ struct BuildDocksToolbarWindow : Window { 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, _ctrl_pressed, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, 0, "" }; /* Determine the watery part of the dock. */ DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); diff --git a/src/gfx.cpp b/src/gfx.cpp index 6db886dda2..016d7addec 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1195,6 +1195,9 @@ void ScreenSizeChanged() _dirty_bytes_per_line = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH); _dirty_blocks = ReallocT(_dirty_blocks, _dirty_bytes_per_line * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT)); + extern uint32 *_vp_map_line; + _vp_map_line = ReallocT(_vp_map_line, _screen.width); + /* check the dirty rect */ if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width; if (_invalid_rect.bottom >= _screen.height) _invalid_rect.bottom = _screen.height; diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 10bc0afa17..cf79eb1e3b 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -19,6 +19,12 @@ #include "blitter/factory.hpp" #include "video/video_driver.hpp" #include "window_func.h" +#include "zoom_func.h" +#include "clear_map.h" +#include "clear_func.h" +#include "tree_map.h" +#include "table/tree_land.h" +#include "blitter/32bpp_base.hpp" /* The type of set we're replacing */ #define SET_TYPE "graphics" @@ -193,6 +199,9 @@ static void LoadSpriteTables() ); } + /* Load route step graphics */ + LoadGrfFile("route_step.grf", SPR_ROUTE_STEP_BASE, i++); + /* Initialize the unicode to sprite mapping table */ InitializeUnicodeGlyphMap(); @@ -308,6 +317,103 @@ void CheckBlitter() ReInitAllWindows(); } +static void UpdateRouteStepSpriteSize() +{ + extern uint _vp_route_step_width; + extern uint _vp_route_step_height_top; + extern uint _vp_route_step_height_middle; + extern uint _vp_route_step_height_bottom; + extern SubSprite _vp_route_step_subsprite; + + Dimension d = GetSpriteSize(SPR_ROUTE_STEP_TOP); + _vp_route_step_width = d.width; + _vp_route_step_height_top = d.height; + + d = GetSpriteSize(SPR_ROUTE_STEP_MIDDLE); + _vp_route_step_height_middle = d.height; + assert(_vp_route_step_width == d.width); + + d = GetSpriteSize(SPR_ROUTE_STEP_BOTTOM); + _vp_route_step_height_bottom = d.height; + assert(_vp_route_step_width == d.width); + + const int char_height = GetCharacterHeight(FS_SMALL) + 1; + _vp_route_step_subsprite.right = ScaleByZoom(_vp_route_step_width, ZOOM_LVL_GUI); + _vp_route_step_subsprite.bottom = ScaleByZoom(char_height, ZOOM_LVL_GUI); + _vp_route_step_subsprite.left = 0; + _vp_route_step_subsprite.top = 0; +} + +/* multi can be density, field type, ... */ +static SpriteID GetSpriteIDForClearGround(const ClearGround cg, const Slope slope, const uint multi) +{ + switch (cg) { + case CLEAR_GRASS: + return GetSpriteIDForClearLand(slope, (byte) multi); + case CLEAR_ROUGH: + return GetSpriteIDForHillyLand(slope, multi); + case CLEAR_ROCKS: + return GetSpriteIDForRocks(slope, multi); + case CLEAR_FIELDS: + return GetSpriteIDForFields(slope, multi); + case CLEAR_SNOW: + case CLEAR_DESERT: + return GetSpriteIDForSnowDesert(slope, multi); + default: NOT_REACHED(); + } +} + +/** Once the sprites are loaded, we can determine main colours of ground/water/... */ +void GfxDetermineMainColours() +{ + /* Water. */ + extern uint32 _vp_map_water_colour[5]; + _vp_map_water_colour[0] = GetSpriteMainColour(SPR_FLAT_WATER_TILE, PAL_NONE); + if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) { + _vp_map_water_colour[1] = Blitter_32bppBase::MakeTransparent(_vp_map_water_colour[0], 256, 192).data; // lighter + _vp_map_water_colour[2] = Blitter_32bppBase::MakeTransparent(_vp_map_water_colour[0], 192, 256).data; // darker + _vp_map_water_colour[3] = _vp_map_water_colour[2]; + _vp_map_water_colour[4] = _vp_map_water_colour[1]; + } + + /* Clear ground. */ + extern uint32 _vp_map_vegetation_clear_colours[16][6][8]; + memset(_vp_map_vegetation_clear_colours, 0, sizeof(_vp_map_vegetation_clear_colours)); + const struct { + byte min; + byte max; + } multi[6] = { + { 0, 3 }, // CLEAR_GRASS, density + { 0, 7 }, // CLEAR_ROUGH, "random" based on position + { 0, 1 }, // CLEAR_ROCKS, tile hash parity + { 0, 7 }, // CLEAR_FIELDS, some field types + { 0, 3 }, // CLEAR_SNOW, density + { 1, 3 }, // CLEAR_DESERT, density + }; + for (uint s = 0; s <= SLOPE_ELEVATED; s++) { + for (uint cg = 0; cg < 6; cg++) { + for (uint m = multi[cg].min; m <= multi[cg].max; m++) { + _vp_map_vegetation_clear_colours[s][cg][m] = GetSpriteMainColour(GetSpriteIDForClearGround((ClearGround) cg, (Slope) s, m), PAL_NONE); + } + } + } + + /* Trees. */ + extern uint32 _vp_map_vegetation_tree_colours[5][MAX_TREE_COUNT_BY_LANDSCAPE]; + const uint base = _tree_base_by_landscape[_settings_game.game_creation.landscape]; + const uint count = _tree_count_by_landscape[_settings_game.game_creation.landscape]; + for (uint tg = 0; tg < 5; tg++) { + for (uint i = base; i < base + count; i++) { + _vp_map_vegetation_tree_colours[tg][i - base] = GetSpriteMainColour(_tree_sprites[i].sprite, _tree_sprites[i].pal); + } + const int diff = MAX_TREE_COUNT_BY_LANDSCAPE - count; + if (diff > 0) { + for (uint i = count; i < MAX_TREE_COUNT_BY_LANDSCAPE; i++) + _vp_map_vegetation_tree_colours[tg][i] = _vp_map_vegetation_tree_colours[tg][i - count]; + } + } +} + /** Initialise and load all the sprites. */ void GfxLoadSprites() { @@ -318,7 +424,9 @@ void GfxLoadSprites() GfxInitSpriteMem(); LoadSpriteTables(); GfxInitPalettes(); + GfxDetermineMainColours(); + UpdateRouteStepSpriteSize(); UpdateCursorSize(); } diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 84288a0e81..a66a6f1946 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -61,6 +61,8 @@ static const KeycodeNames _keycode_to_name[] = { {"NUM_MINUS", WKC_NUM_MINUS}, {"=", WKC_EQUALS}, {"-", WKC_MINUS}, + {"PAGE_UP", WKC_PAGEUP}, + {"PAGE_DOWN", WKC_PAGEDOWN}, }; /** diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 5971964fd7..77c45410a7 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -523,7 +523,7 @@ static void TransportIndustryGoods(TileIndex tile) ResetIndustryConstructionStage(tile); SetIndustryCompleted(tile); SetIndustryGfx(tile, newgfx); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } } @@ -556,7 +556,7 @@ static void AnimateTile_Industry(TileIndex tile) } SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -574,7 +574,7 @@ static void AnimateTile_Industry(TileIndex tile) } SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -588,7 +588,7 @@ static void AnimateTile_Industry(TileIndex tile) } SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -601,7 +601,7 @@ static void AnimateTile_Industry(TileIndex tile) DeleteAnimatedTile(tile); } else { SetAnimationFrame(tile, m + 1); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } break; @@ -627,7 +627,7 @@ static void AnimateTile_Industry(TileIndex tile) } SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -640,7 +640,7 @@ static void AnimateTile_Industry(TileIndex tile) gfx = (gfx < 155) ? gfx + 1 : 148; SetIndustryGfx(tile, gfx); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -659,7 +659,7 @@ static void AnimateTile_Industry(TileIndex tile) } else { SetAnimationFrame(tile, m); SetIndustryGfx(tile, gfx); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } break; @@ -685,7 +685,7 @@ static void AnimateTile_Industry(TileIndex tile) byte m = (GetAnimationFrame(tile) + 1) | 0x40; if (m > 0xC2) m = 0xC0; SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } else if (state >= 0x200 && state < 0x3A0) { int i = (state < 0x220 || state >= 0x380) ? 7 : 3; if (state & i) return; @@ -693,7 +693,7 @@ static void AnimateTile_Industry(TileIndex tile) byte m = (GetAnimationFrame(tile) & 0xBF) - 1; if (m < 0x80) m = 0x82; SetAnimationFrame(tile, m); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; } @@ -723,7 +723,7 @@ static void MakeIndustryTileBigger(TileIndex tile) StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE); if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); if (!IsIndustryCompleted(tile)) return; @@ -818,7 +818,7 @@ static void TileLoop_Industry(TileIndex tile) if (newgfx != INDUSTRYTILE_NOANIM) { ResetIndustryConstructionStage(tile); SetIndustryGfx(tile, newgfx); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); return; } @@ -1021,7 +1021,7 @@ static void PlantFarmField(TileIndex tile, IndustryID industry) if (IsSuitableForFarmField(cur_tile, true)) { MakeField(cur_tile, field_type, industry); SetClearCounter(cur_tile, counter); - MarkTileDirtyByTile(cur_tile); + MarkTileDirtyByTile(cur_tile, ZOOM_LVL_DRAW_MAP); } } diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 47ca4fd21f..aaa0b07a78 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -2285,6 +2285,14 @@ struct IndustryCargoesWindow : public Window { /* Only notify the smallmap window if it exists. In particular, do not * bring it to the front to prevent messing up any nice layout of the user. */ InvalidateWindowClassesData(WC_SMALLMAP, 0); + + /* Notify viewports too. */ + Window *w; + FOR_ALL_WINDOWS_FROM_BACK(w) { + if (w->viewport != NULL) + if (w->viewport->zoom >= ZOOM_LVL_DRAW_MAP && w->viewport->map_type == VPMT_INDUSTRY) + w->InvalidateData(); + } } /** diff --git a/src/lang/english.txt b/src/lang/english.txt index 059c791d7d..cd104dd3bb 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -400,6 +400,7 @@ STR_MAP_MENU_MAP_OF_WORLD :Map of world STR_MAP_MENU_EXTRA_VIEW_PORT :Extra viewport STR_MAP_MENU_LINGRAPH_LEGEND :Cargo Flow Legend STR_MAP_MENU_SIGN_LIST :Sign list +STR_MAP_MENU_PLAN_LIST :Plan list ############ range for town menu starts STR_TOWN_MENU_TOWN_DIRECTORY :Town directory @@ -1367,6 +1368,32 @@ STR_CONFIG_SETTING_OSK_ACTIVATION_DISABLED :Disabled STR_CONFIG_SETTING_OSK_ACTIVATION_DOUBLE_CLICK :Double click STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK_FOCUS :Single click (when focussed) STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK :Single click (immediately) +STR_CONFIG_SETTING_SHOW_VEHICLE_ROUTE_STEPS :Show the vehicle's route steps: {STRING2} + +STR_CONFIG_SETTING_VIEWPORT_MAP_SCAN_SURROUNDINGS :Scan surroundings (better for high zoom out levels): {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SLOPES :Show slopes: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_BRIDGES :Show bridges: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS :Show tunnels: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP :Show scrolling viewport: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP_NOTHING :No +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP_CONTOUR :Contour +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP_BLEND :Blend with some white (32bpp only) +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP_ALL :Blend + contour +STR_CONFIG_SETTING_VIEWPORT_MAP_USE_OWNER_COLOUR_BRIDGE_TUNNEL :Use owner's colour for bridges and tunnels: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE :Default mode: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE_VEGETATION :Vegetation +STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE_OWNER :Owner +STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE_INDUSTRY :Industry +STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK :Function of double-click: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK_DO_NOTHING :Do nothing +STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK_ZOOM_MAIN :Zoom in directly to 1X +STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK_NEW_EXTRA :Open an extra viewport +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_VEHICLE_ROUTE :Show the vehicle's route: {STRING2} +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_VEHICLE_ROUTE_NO :No +STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_VEHICLE_ROUTE_SIMPLE :Simple +STR_CONFIG_SETTING_VIEWPORT_MAP_DRAW_ROUTE_DASH :Drawing style of vehicle's route: {STRING} +STR_CONFIG_SETTING_VIEWPORT_MAP_DRAW_ROUTE_DASH_VALUE :dashed lines of {COMMA} pixel{P "" s} +STR_CONFIG_SETTING_VIEWPORT_MAP_DRAW_ROUTE_DASH_DISABLED :plain lines STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU :Right-click emulation: {STRING2} STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_HELPTEXT :Select the method to emulate right mouse-button clicks @@ -1594,6 +1621,10 @@ STR_CONFIG_SETTING_ZOOM_LVL_NORMAL :Normal STR_CONFIG_SETTING_ZOOM_LVL_OUT_2X :2x STR_CONFIG_SETTING_ZOOM_LVL_OUT_4X :4x STR_CONFIG_SETTING_ZOOM_LVL_OUT_8X :8x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_16X :16x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_32X :32x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_64X :64x +STR_CONFIG_SETTING_ZOOM_LVL_OUT_128X :128x STR_CONFIG_SETTING_TOWN_GROWTH :Town growth speed: {STRING2} STR_CONFIG_SETTING_TOWN_GROWTH_HELPTEXT :Speed of town growth STR_CONFIG_SETTING_TOWN_GROWTH_NONE :None @@ -1691,6 +1722,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Industr STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Cargo distribution STR_CONFIG_SETTING_AI :{ORANGE}Competitors STR_CONFIG_SETTING_AI_NPC :{ORANGE}Computer players +STR_CONFIG_SETTING_VIEWPORT_MAP_OPTIONS :{ORANGE}Map mode STR_CONFIG_SETTING_PATHFINDER_OPF :Original STR_CONFIG_SETTING_PATHFINDER_NPF :NPF @@ -3288,6 +3320,14 @@ STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Centre t STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Production level: {YELLOW}{COMMA}% STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}The industry has announced imminent closure! +# Industry tooltip +STR_INDUSTRY_VIEW_TRANSPORTED_TOOLTIP :{BLACK}{STRING} +STR_INDUSTRY_VIEW_TRANSPORTED_TOOLTIP_TOOLTIP :{BLACK}{STRING}{}{CARGO_LONG} ({COMMA}%) +STR_INDUSTRY_VIEW_TRANSPORTED_TOOLTIP_TOOLTIP_TOOLTIP :{BLACK}{STRING}{}{CARGO_LONG} ({COMMA}%){}{BLACK}{CARGO_LONG} ({COMMA}%) + +# Town tooltip +STR_TOWN_NAME_TOOLTIP :{BLACK}{TOWN} + ############ range for requires starts STR_INDUSTRY_VIEW_REQUIRES_CARGO :{BLACK}Requires: {YELLOW}{STRING}{RAW_STRING} STR_INDUSTRY_VIEW_REQUIRES_CARGO_CARGO :{BLACK}Requires: {YELLOW}{STRING}{RAW_STRING}, {STRING}{RAW_STRING} @@ -4037,6 +4077,22 @@ STR_TEXTFILE_VIEW_README :{BLACK}View rea STR_TEXTFILE_VIEW_CHANGELOG :{BLACK}Changelog STR_TEXTFILE_VIEW_LICENCE :{BLACK}Licence +# Plans window +STR_PLANS_CAPTION :{WHITE}Plans +STR_PLANS_NEW_PLAN :{BLACK}New +STR_PLANS_NEW_PLAN_TOOLTIP :{BLACK}Create a new plan +STR_PLANS_ADD_LINES :{BLACK}Add lines +STR_PLANS_ADDING_LINES :{BLACK}Adding... +STR_PLANS_HIDE_ALL :{BLACK}Hide all +STR_PLANS_HIDE_ALL_TOOLTIP :{BLACK}Set the visibility of all the plans and all their lines to false +STR_PLANS_VISIBILITY_PRIVATE :{BLACK}Make private +STR_PLANS_VISIBILITY_PUBLIC :{BLACK}Make public +STR_PLANS_VISIBILITY_TOOLTIP :{BLACK}Toggle the visibility of a plan (private is yellow, public is blue). A public plan will be displayed in the plan window of the other companies but only its owner can add lines to it. +STR_PLANS_DELETE :{BLACK}Delete +STR_PLANS_DELETE_TOOLTIP :{BLACK}Delete the selected item in the list +STR_PLANS_LIST_ITEM_PLAN :Plan #{NUM}: {NUM} line{P "" s} ({DATE_SHORT}) +STR_PLANS_LIST_ITEM_LINE : -- Line #{NUM}: {NUM} segment{P "" s} +STR_PLANS_LIST_TOOLTIP :{BLACK}Double click any item in the list to (un)fold the related plan # Vehicle loading indicators STR_PERCENT_UP_SMALL :{TINY_FONT}{WHITE}{NUM}%{UP_ARROW} @@ -4460,6 +4516,11 @@ STR_ERROR_CAN_T_PLACE_SIGN_HERE :{WHITE}Can't pl STR_ERROR_CAN_T_CHANGE_SIGN_NAME :{WHITE}Can't change sign name... STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Can't delete sign... +# Plan related errors +STR_ERROR_TOO_MANY_PLANS :{WHITE}... too many plans +STR_ERROR_TOO_MANY_NODES :{WHITE}... too many nodes in plan line +STR_ERROR_NO_MORE_SPACE_FOR_LINES :{WHITE}No more space for lines + # Translatable comment for OpenTTD's desktop shortcut STR_DESKTOP_SHORTCUT_COMMENT :A simulation game based on Transport Tycoon Deluxe @@ -4879,6 +4940,12 @@ STR_SAVEGAME_NAME_DEFAULT :{COMPANY}, {STR STR_SAVEGAME_NAME_SPECTATOR :Spectator, {1:STRING1} # Viewport strings +STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP :{BROWN}{NUM} {STRING} +STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_STATION :{BLACK}ST +STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_WAYPOINT :{GRAY}WP +STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_IMPLICIT :{GRAY}IM +STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_DEPOT :{RED}DE + STR_VIEWPORT_TOWN_POP :{WHITE}{TOWN} ({COMMA}) STR_VIEWPORT_TOWN :{WHITE}{TOWN} STR_VIEWPORT_TOWN_TINY_BLACK :{TINY_FONT}{BLACK}{TOWN} diff --git a/src/main_gui.cpp b/src/main_gui.cpp index cdef1e6a6e..dad3b7d2c4 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -231,6 +231,8 @@ enum { GHK_CHAT_ALL, GHK_CHAT_COMPANY, GHK_CHAT_SERVER, + GHK_CHANGE_MAP_MODE_PREV, + GHK_CHANGE_MAP_MODE_NEXT, }; struct MainWindow : Window @@ -249,6 +251,7 @@ struct MainWindow : Window NWidgetViewport *nvp = this->GetWidget(WID_M_VIEWPORT); nvp->InitializeViewport(this, TileXY(32, 32), ZOOM_LVL_VIEWPORT); + this->viewport->map_type = (ViewportMapType) _settings_client.gui.default_viewport_map_mode; this->viewport->overlay = new LinkGraphOverlay(this, WID_M_VIEWPORT, 0, 0, 3); this->refresh = LINKGRAPH_DELAY; } @@ -423,6 +426,25 @@ struct MainWindow : Window break; #endif + case GHK_CHANGE_MAP_MODE_PREV: + if (_focused_window && _focused_window->viewport && _focused_window->viewport->zoom >= ZOOM_LVL_DRAW_MAP) { + _focused_window->viewport->map_type = ChangeRenderMode(_focused_window->viewport, true); + _focused_window->SetDirty(); + } else if (this->viewport->zoom >= ZOOM_LVL_DRAW_MAP) { + this->viewport->map_type = ChangeRenderMode(this->viewport, true); + this->SetDirty(); + } + break; + case GHK_CHANGE_MAP_MODE_NEXT: + if (_focused_window && _focused_window->viewport && _focused_window->viewport->zoom >= ZOOM_LVL_DRAW_MAP) { + _focused_window->viewport->map_type = ChangeRenderMode(_focused_window->viewport, false); + _focused_window->SetDirty(); + } else if (this->viewport->zoom >= ZOOM_LVL_DRAW_MAP) { + this->viewport->map_type = ChangeRenderMode(this->viewport, false); + this->SetDirty(); + } + break; + default: return ES_NOT_HANDLED; } return ES_HANDLED; @@ -439,7 +461,11 @@ struct MainWindow : Window virtual void OnMouseWheel(int wheel) { - if (_settings_client.gui.scrollwheel_scrolling == 0) { + if (_ctrl_pressed) { + /* Cycle through the drawing modes */ + this->viewport->map_type = ChangeRenderMode(this->viewport, wheel < 0); + this->SetDirty(); + } else if (_settings_client.gui.scrollwheel_scrolling == 0) { ZoomInOrOutToCursorWindow(wheel < 0, this); } } @@ -465,6 +491,16 @@ struct MainWindow : Window InvalidateWindowData(WC_MAIN_TOOLBAR, 0, data, true); } + virtual void OnMouseOver(Point pt, int widget) + { + if (pt.x != -1 && _game_mode != GM_MENU) { + /* Show tooltip with last month production or town name */ + const Point p = GetTileBelowCursor(); + const TileIndex tile = TileVirtXY(p.x, p.y); + if (tile < MapSize()) ShowTooltipForTile(this, tile); + } + } + static HotkeyList hotkeys; }; @@ -517,6 +553,8 @@ static Hotkey global_hotkeys[] = { Hotkey(_ghk_chat_company_keys, "chat_company", GHK_CHAT_COMPANY), Hotkey(_ghk_chat_server_keys, "chat_server", GHK_CHAT_SERVER), #endif + Hotkey(WKC_PAGEUP, "previous_map_mode", GHK_CHANGE_MAP_MODE_PREV), + Hotkey(WKC_PAGEDOWN, "next_map_mode", GHK_CHANGE_MAP_MODE_NEXT), HOTKEY_LIST_END }; HotkeyList MainWindow::hotkeys("global", global_hotkeys); diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 1ed7444bc5..24ed5055d9 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -643,6 +643,7 @@ struct TooltipsWindow : public Window byte paramcount; ///< Number of string parameters in #string_id. uint64 params[5]; ///< The string parameters. TooltipCloseCondition close_cond; ///< Condition for closing the window. + char buffer[DRAW_STRING_BUFFER]; ///< Text to draw TooltipsWindow(Window *parent, StringID str, uint paramcount, const uint64 params[], TooltipCloseCondition close_tooltip) : Window(&_tool_tips_desc) { @@ -653,6 +654,7 @@ struct TooltipsWindow : public Window memcpy(this->params, params, sizeof(this->params[0]) * paramcount); this->paramcount = paramcount; this->close_cond = close_tooltip; + if (this->paramcount == 0) GetString(this->buffer, str, lastof(this->buffer)); // Get the text while params are available this->InitNested(); @@ -698,10 +700,14 @@ struct TooltipsWindow : public Window GfxFillRect(r.left, r.top, r.right, r.bottom, PC_BLACK); GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_LIGHT_YELLOW); - for (uint arg = 0; arg < this->paramcount; arg++) { - SetDParam(arg, this->params[arg]); + if (this->paramcount == 0) { + DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, this->buffer, TC_FROMSTRING, SA_CENTER); + } else { + for (uint arg = 0; arg < this->paramcount; arg++) { + SetDParam(arg, this->params[arg]); + } + DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, this->string_id, TC_FROMSTRING, SA_CENTER); } - DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, this->string_id, TC_FROMSTRING, SA_CENTER); } virtual void OnMouseLoop() diff --git a/src/network/core/packet.cpp b/src/network/core/packet.cpp index 7548132e0b..b3ebd422e2 100644 --- a/src/network/core/packet.cpp +++ b/src/network/core/packet.cpp @@ -15,6 +15,7 @@ #include "../../stdafx.h" #include "../../string_func.h" +#include "../../command_type.h" #include "packet.h" @@ -32,7 +33,7 @@ Packet::Packet(NetworkSocketHandler *cs) this->next = NULL; this->pos = 0; // We start reading from here this->size = 0; - this->buffer = MallocT(SEND_MTU); + this->buffer = MallocT(SHRT_MAX); } /** @@ -47,7 +48,7 @@ Packet::Packet(PacketType type) /* Skip the size so we can write that in before sending the packet */ this->pos = 0; this->size = sizeof(PacketSize); - this->buffer = MallocT(SEND_MTU); + this->buffer = MallocT(SHRT_MAX); this->buffer[this->size++] = type; } @@ -99,7 +100,7 @@ void Packet::Send_bool(bool data) */ void Packet::Send_uint8(uint8 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->size < SHRT_MAX - sizeof(data)); this->buffer[this->size++] = data; } @@ -109,7 +110,7 @@ void Packet::Send_uint8(uint8 data) */ void Packet::Send_uint16(uint16 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->size < SHRT_MAX - sizeof(data)); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); } @@ -120,7 +121,7 @@ void Packet::Send_uint16(uint16 data) */ void Packet::Send_uint32(uint32 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->size < SHRT_MAX - sizeof(data)); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); this->buffer[this->size++] = GB(data, 16, 8); @@ -133,7 +134,7 @@ void Packet::Send_uint32(uint32 data) */ void Packet::Send_uint64(uint64 data) { - assert(this->size < SEND_MTU - sizeof(data)); + assert(this->size < SHRT_MAX - sizeof(data)); this->buffer[this->size++] = GB(data, 0, 8); this->buffer[this->size++] = GB(data, 8, 8); this->buffer[this->size++] = GB(data, 16, 8); @@ -153,10 +154,22 @@ void Packet::Send_string(const char *data) { assert(data != NULL); /* The <= *is* valid due to the fact that we are comparing sizes and not the index. */ - assert(this->size + strlen(data) + 1 <= SEND_MTU); + assert(this->size + strlen(data) + 1 <= SHRT_MAX); while ((this->buffer[this->size++] = *data++) != '\0') {} } +/** + * Sends a binary data over the network. + * @param data The data to send + */ +void Packet::Send_binary(const char *data, const size_t size) +{ + assert(data != NULL); + assert(size < MAX_CMD_TEXT_LENGTH); + memcpy(&this->buffer[this->size], data, size); + this->size += (PacketSize) size; +} + /* * Receiving commands @@ -311,4 +324,18 @@ void Packet::Recv_string(char *buffer, size_t size, StringValidationSettings set str_validate(bufp, last, settings); } +/** + * Reads binary data. + * @param buffer The buffer to put the data into. + * @param size The size of the buffer. + */ +void Packet::Recv_binary(char *buffer, size_t size) +{ + /* Don't allow reading from a closed socket */ + if (cs->HasClientQuit()) return; + + memcpy(buffer, &this->buffer[this->pos], size); + this->pos += (PacketSize) size; +} + #endif /* ENABLE_NETWORK */ diff --git a/src/network/core/packet.h b/src/network/core/packet.h index 7f344d0179..b27e590d55 100644 --- a/src/network/core/packet.h +++ b/src/network/core/packet.h @@ -52,7 +52,7 @@ struct Packet { PacketSize size; /** The current read/write position in the packet */ PacketSize pos; - /** The buffer of this packet, of basically variable length up to SEND_MTU. */ + /** The buffer of this packet, of basically variable length up to SHRT_MAX. */ byte *buffer; private: @@ -73,6 +73,7 @@ public: void Send_uint32(uint32 data); void Send_uint64(uint64 data); void Send_string(const char *data); + void Send_binary(const char *data, const size_t size); /* Reading/receiving of packets */ void ReadRawPacketSize(); @@ -85,6 +86,7 @@ public: uint32 Recv_uint32(); uint64 Recv_uint64(); void Recv_string(char *buffer, size_t size, StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK); + void Recv_binary(char *buffer, size_t size); }; #endif /* ENABLE_NETWORK */ diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index d4d294146e..a78d2bf621 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -862,7 +862,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet * the server will give us a client-id and let us in */ _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; ShowJoinStatusWindow(); - NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company); + NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company, 0); } } else { /* take control over an existing company */ diff --git a/src/network/network_command.cpp b/src/network/network_command.cpp index 6e5458fd86..72babbc772 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 */ CcAddPlan, }; /** @@ -136,8 +137,9 @@ static CommandQueue _local_execution_queue; * @param callback A callback function to call after the command is finished * @param text The text to pass * @param company The company that wants to send the command + * @param binary_length The quantity of binary data in text */ -void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, CompanyID company) +void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback, const char *text, CompanyID company, uint32 binary_length) { assert((cmd & CMD_FLAGS_MASK) == 0); @@ -149,7 +151,12 @@ void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, Comman c.cmd = cmd; c.callback = callback; - strecpy(c.text, (text != NULL) ? text : "", lastof(c.text)); + c.binary_length = binary_length; + if (binary_length == 0) { + strecpy(c.text, (text != NULL) ? text : "", lastof(c.text)); + } else { + memcpy(c.text, text, binary_length); + } if (_network_server) { /* If we are the server, we queue the command in our 'special' queue. @@ -310,7 +317,13 @@ const char *NetworkGameSocketHandler::ReceiveCommand(Packet *p, CommandPacket *c cp->p1 = p->Recv_uint32(); cp->p2 = p->Recv_uint32(); cp->tile = p->Recv_uint32(); - p->Recv_string(cp->text, lengthof(cp->text), (!_network_server && GetCommandFlags(cp->cmd) & CMD_STR_CTRL) != 0 ? SVS_ALLOW_CONTROL_CODE | SVS_REPLACE_WITH_QUESTION_MARK : SVS_REPLACE_WITH_QUESTION_MARK); + cp->binary_length = p->Recv_uint32(); + if (cp->binary_length == 0) { + p->Recv_string(cp->text, lengthof(cp->text), (!_network_server && GetCommandFlags(cp->cmd) & CMD_STR_CTRL) != 0 ? SVS_ALLOW_CONTROL_CODE | SVS_REPLACE_WITH_QUESTION_MARK : SVS_REPLACE_WITH_QUESTION_MARK); + } else { + if ((p->pos + (PacketSize) cp->binary_length + /* callback index */ 1) > p->size) return "invalid binary data length"; + p->Recv_binary(cp->text, cp->binary_length); + } byte callback = p->Recv_uint8(); if (callback >= lengthof(_callback_table)) return "invalid callback"; @@ -331,7 +344,12 @@ void NetworkGameSocketHandler::SendCommand(Packet *p, const CommandPacket *cp) p->Send_uint32(cp->p1); p->Send_uint32(cp->p2); p->Send_uint32(cp->tile); - p->Send_string(cp->text); + p->Send_uint32(cp->binary_length); + if (cp->binary_length == 0) { + p->Send_string(cp->text); + } else { + p->Send_binary(cp->text, cp->binary_length); + } byte callback = 0; while (callback < lengthof(_callback_table) && _callback_table[callback] != cp->callback) { diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 6a33c73d89..7e59261762 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -2192,7 +2192,7 @@ void NetworkServerNewCompany(const Company *c, NetworkClientInfo *ci) /* ci is NULL when replaying, or for AIs. In neither case there is a client. */ ci->client_playas = c->index; NetworkUpdateClientInfo(ci->client_id); - NetworkSendCommand(0, 0, 0, CMD_RENAME_PRESIDENT, NULL, ci->client_name, c->index); + NetworkSendCommand(0, 0, 0, CMD_RENAME_PRESIDENT, NULL, ci->client_name, c->index, 0); } /* Announce new company on network. */ diff --git a/src/newgrf_animation_base.h b/src/newgrf_animation_base.h index 7b94031fbd..3ccbe93700 100644 --- a/src/newgrf_animation_base.h +++ b/src/newgrf_animation_base.h @@ -102,7 +102,7 @@ struct AnimationBase { } SetAnimationFrame(tile, frame); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } /** diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 6c9c614af9..61d9538c81 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -597,7 +597,7 @@ bool NewHouseTileLoop(TileIndex tile) } SetHouseProcessingTime(tile, hs->processing_time); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); return true; } @@ -632,7 +632,7 @@ static void DoTriggerHouse(TileIndex tile, HouseTrigger trigger, byte base_rando case HOUSE_TRIGGER_TILE_LOOP_TOP: if (!first) { /* The top tile is marked dirty by the usual TileLoop */ - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); break; } /* Random value of first tile already set. */ diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 90a17550d0..ba92b1c05a 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -336,7 +336,7 @@ static void DoTriggerIndustryTile(TileIndex tile, IndustryTileTrigger trigger, I random_bits &= ~object.reseed[VSG_SCOPE_SELF]; random_bits |= new_random_bits & object.reseed[VSG_SCOPE_SELF]; SetIndustryRandomBits(tile, random_bits); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); reseed_industry |= object.reseed[VSG_SCOPE_PARENT]; } diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index cd5dad7b47..37db2f8416 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -1030,7 +1030,7 @@ void TriggerStationRandomisation(Station *st, TileIndex tile, StationRandomTrigg random_bits |= Random() & reseed; SetStationTileRandomBits(tile, random_bits); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } } diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp index b5b9921525..130caf505a 100644 --- a/src/object_cmd.cpp +++ b/src/object_cmd.cpp @@ -124,7 +124,7 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u DirtyCompanyInfrastructureWindows(owner); } MakeObject(t, owner, o->index, wc, Random()); - MarkTileDirtyByTile(t); + MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP); } Object::IncTypeCount(type); @@ -140,7 +140,7 @@ static void IncreaseAnimationStage(TileIndex tile) TileArea ta = Object::GetByTile(tile)->location; TILE_AREA_LOOP(t, ta) { SetAnimationFrame(t, GetAnimationFrame(t) + 1); - MarkTileDirtyByTile(t); + MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP); } } diff --git a/src/openttd.cpp b/src/openttd.cpp index c149ebbd4d..29846a2102 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -63,6 +63,7 @@ #include "subsidy_func.h" #include "gfx_layout.h" #include "viewport_sprite_sorter.h" +#include "smallmap_gui.h" #include "linkgraph/linkgraphschedule.h" @@ -1178,6 +1179,8 @@ void SwitchToMode(SwitchMode new_mode) default: NOT_REACHED(); } + + SmallMapWindow::RebuildColourIndexIfNecessary(); } diff --git a/src/order_backup.cpp b/src/order_backup.cpp index 597ad13bba..de0517cf71 100644 --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -210,7 +210,7 @@ CommandCost CmdClearOrderBackup(TileIndex tile, DoCommandFlag flags, uint32 p1, /* We need to circumvent the "prevention" from this command being executed * while the game is paused, so use the internal method. Nor do we want * this command to get its cost estimated when shift is pressed. */ - DoCommandPInternal(ob->tile, 0, user, CMD_CLEAR_ORDER_BACKUP, NULL, NULL, true, false); + DoCommandPInternal(ob->tile, 0, user, CMD_CLEAR_ORDER_BACKUP, NULL, NULL, true, false, 0); } else { /* The command came from the game logic, i.e. the clearing of a tile. * In that case we have no need to actually sync this, just do it. */ diff --git a/src/order_gui.cpp b/src/order_gui.cpp index d4feae35ca..d831c0cf5f 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -617,6 +617,8 @@ private: order.SetDepotActionType(ODATFB_NEAREST_DEPOT); DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER)); + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); } /** @@ -702,6 +704,8 @@ private: /* When networking, move one order lower */ int selected = this->selected_order + (int)_networking; + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); if (DoCommandP(this->vehicle->tile, this->vehicle->index, this->OrderGetSel(), CMD_DELETE_ORDER | CMD_MSG(STR_ERROR_CAN_T_DELETE_THIS_ORDER))) { this->selected_order = selected >= this->vehicle->GetNumOrders() ? -1 : selected; this->UpdateButtonState(); @@ -795,6 +799,13 @@ public: this->OnInvalidateData(VIWD_MODIFY_ORDERS); } + ~OrdersWindow() + { + if (!FocusWindowById(WC_VEHICLE_VIEW, this->window_number)) { + MarkAllRouteStepsDirty(this); + } + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { @@ -1169,6 +1180,8 @@ public: order.MakeConditional(order_id); DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), order.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER)); + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); } ResetObjectToPlace(); break; @@ -1227,7 +1240,7 @@ public: } else { const Order *o = this->vehicle->GetOrder(this->OrderGetSel()); ShowDropDownMenu(this, _order_non_stop_drowdown, o->GetNonStopType(), WID_O_NON_STOP, 0, - o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12)); + o->IsType(OT_GOTO_STATION) ? 0 : (o->IsType(OT_GOTO_WAYPOINT) ? 3 : 12), 0, DDSF_LOST_FOCUS); } break; @@ -1247,7 +1260,7 @@ public: case OPOS_SHARE: sel = 3; break; default: NOT_REACHED(); } - ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, sel, WID_O_GOTO, 0, 0); + ShowDropDownMenu(this, this->vehicle->type == VEH_AIRCRAFT ? _order_goto_dropdown_aircraft : _order_goto_dropdown, sel, WID_O_GOTO, 0, 0, 0, DDSF_LOST_FOCUS); } break; @@ -1255,7 +1268,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_FullLoad(-1); } else { - ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 2); + ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), WID_O_FULL_LOAD, 0, 2, 0, DDSF_LOST_FOCUS); } break; @@ -1263,7 +1276,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_Unload(-1); } else { - ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 8); + ShowDropDownMenu(this, _order_unload_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetUnloadType(), WID_O_UNLOAD, 0, 8, 0, DDSF_LOST_FOCUS); } break; @@ -1275,7 +1288,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_Service(-1); } else { - ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_SERVICE, 0, 0); + ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_SERVICE, 0, 0, 0, DDSF_LOST_FOCUS); } break; @@ -1283,7 +1296,7 @@ public: if (this->GetWidget(widget)->ButtonHit(pt)) { this->OrderClick_Refit(0, true); } else { - ShowDropDownMenu(this, _order_refit_action_dropdown, 0, WID_O_REFIT_DROPDOWN, 0, 0); + ShowDropDownMenu(this, _order_refit_action_dropdown, 0, WID_O_REFIT_DROPDOWN, 0, 0, 0, DDSF_LOST_FOCUS); } break; @@ -1302,7 +1315,7 @@ public: case WID_O_COND_COMPARATOR: { const Order *o = this->vehicle->GetOrder(this->OrderGetSel()); - ShowDropDownMenu(this, _order_conditional_condition, o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0); + ShowDropDownMenu(this, _order_conditional_condition, o->GetConditionComparator(), WID_O_COND_COMPARATOR, 0, (o->GetConditionVariable() == OCV_REQUIRES_SERVICE) ? 0x3F : 0xC0, 0, DDSF_LOST_FOCUS); break; } @@ -1448,6 +1461,8 @@ public: if (cmd.IsType(OT_NOTHING)) return; if (DoCommandP(this->vehicle->tile, this->vehicle->index + (this->OrderGetSel() << 20), cmd.Pack(), CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER))) { + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); /* With quick goto the Go To button stays active */ if (!_settings_client.gui.quick_goto) ResetObjectToPlace(); } @@ -1510,6 +1525,27 @@ public: this->vscroll->SetCapacityFromWidget(this, WID_O_ORDER_LIST); } + virtual void OnFocus(Window *previously_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) { + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) { + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); + } + } + + const Vehicle *GetVehicle() + { + return this->vehicle; + } + static HotkeyList hotkeys; }; diff --git a/src/osk_gui.cpp b/src/osk_gui.cpp index 974e465f43..0ebc393def 100644 --- a/src/osk_gui.cpp +++ b/src/osk_gui.cpp @@ -206,7 +206,7 @@ struct OskWindow : public Window { this->parent->SetWidgetDirty(this->text_btn); } - virtual void OnFocusLost() + virtual void OnFocusLost(Window *newly_focused_window) { VideoDriver::GetInstance()->EditBoxLostFocus(); delete this; diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index bb705e5f4a..6a5a717584 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -281,14 +281,14 @@ static void NPFMarkTile(TileIndex tile) /* DEBUG: mark visited tiles by mowing the grass under them ;-) */ if (!IsRailDepot(tile)) { SetRailGroundType(tile, RAIL_GROUND_BARREN); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; case MP_ROAD: if (!IsRoadDepot(tile)) { SetRoadside(tile, ROADSIDE_BARREN); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; diff --git a/src/pathfinder/yapf/yapf_rail.cpp b/src/pathfinder/yapf/yapf_rail.cpp index dea4e28c1b..fcc07ded6a 100644 --- a/src/pathfinder/yapf/yapf_rail.cpp +++ b/src/pathfinder/yapf/yapf_rail.cpp @@ -82,7 +82,7 @@ private: do { if (HasStationReservation(tile)) return false; SetRailStationReservation(tile, true); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); tile = TILE_ADD(tile, diff); } while (IsCompatibleTrainStationTile(tile, start) && tile != m_origin_tile); diff --git a/src/pbs.cpp b/src/pbs.cpp index 76cb3b9d63..02ce9ed605 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -66,7 +66,7 @@ void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool do { SetRailStationReservation(tile, b); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); tile = TILE_ADD(tile, diff); } while (IsCompatibleTrainStationTile(tile, start)); } @@ -85,7 +85,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) if (_settings_client.gui.show_track_reservation) { /* show the reserved rail if needed */ - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } switch (GetTileType(tile)) { @@ -94,7 +94,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) if (IsRailDepot(tile)) { if (!HasDepotReservation(tile)) { SetDepotReservation(tile, true); - MarkTileDirtyByTile(tile); // some GRFs change their appearance when tile is reserved + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); // some GRFs change their appearance when tile is reserved return true; } } @@ -104,7 +104,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) if (IsLevelCrossing(tile) && !HasCrossingReservation(tile)) { SetCrossingReservation(tile, true); BarCrossing(tile); - MarkTileDirtyByTile(tile); // crossing barred, make tile dirty + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); // crossing barred, make tile dirty return true; } break; @@ -113,7 +113,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) if (HasStationRail(tile) && !HasStationReservation(tile)) { SetRailStationReservation(tile, true); if (trigger_stations && IsRailStation(tile)) TriggerStationRandomisation(NULL, tile, SRT_PATH_RESERVATION); - MarkTileDirtyByTile(tile); // some GRFs need redraw after reserving track + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); // some GRFs need redraw after reserving track return true; } break; @@ -141,14 +141,14 @@ void UnreserveRailTrack(TileIndex tile, Track t) assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0); if (_settings_client.gui.show_track_reservation) { - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } switch (GetTileType(tile)) { case MP_RAILWAY: if (IsRailDepot(tile)) { SetDepotReservation(tile, false); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); break; } if (IsPlainRail(tile)) UnreserveTrack(tile, t); @@ -164,7 +164,7 @@ void UnreserveRailTrack(TileIndex tile, Track t) case MP_STATION: if (HasStationRail(tile)) { SetRailStationReservation(tile, false); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; diff --git a/src/plans.cpp b/src/plans.cpp new file mode 100644 index 0000000000..a98c09a437 --- /dev/null +++ b/src/plans.cpp @@ -0,0 +1,21 @@ +/* $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 plans.cpp Handling of plans. */ + +#include "stdafx.h" +#include "plans_base.h" +#include "core/pool_func.hpp" + +/** Initialize the plan-pool */ +PlanPool _plan_pool("Plan"); +INSTANTIATE_POOL_METHODS(Plan) + +Plan *_current_plan = NULL; +Plan *_new_plan = NULL; diff --git a/src/plans_base.h b/src/plans_base.h new file mode 100644 index 0000000000..ca31a5ca13 --- /dev/null +++ b/src/plans_base.h @@ -0,0 +1,237 @@ +/* $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 plans_base.h Base class for plans. */ + +#ifndef PLANS_BASE_H +#define PLANS_BASE_H + +#include "plans_func.h" +#include "core/pool_type.hpp" +#include "company_type.h" +#include "company_func.h" +#include "command_func.h" +#include "map_func.h" +#include "date_func.h" +#include "viewport_func.h" +#include "core/endian_func.hpp" +#include + +typedef Pool PlanPool; +typedef std::vector TileVector; +typedef std::vector PlanLineVector; +extern PlanPool _plan_pool; + +struct PlanLine { + bool visible; + bool focused; + TileVector tiles; + + PlanLine() + { + this->visible = true; + this->focused = false; + } + + ~PlanLine() + { + this->Clear(); + } + + void Clear() + { + this->tiles.clear(); + } + + bool AppendTile(TileIndex tile) + { + const uint cnt = (uint) this->tiles.size(); + + if (cnt > 0) { + const TileIndex last_tile = this->tiles[cnt - 1]; + if (last_tile == tile) return false; + MarkTileLineDirty(last_tile, tile); + + if (cnt > 1) { + const TileIndex t0 = this->tiles[cnt - 2]; + const int x0 = (int) TileX(t0); + const int y0 = (int) TileY(t0); + const TileIndex t1 = this->tiles[cnt - 1]; + const int x1 = (int) TileX(t1); + const int y1 = (int) TileY(t1); + const int x2 = (int) TileX(tile); + const int y2 = (int) TileY(tile); + + if ((y1 - y0) * (x2 - x1) == (y2 - y1) * (x1 - x0)) { // Same direction. + if (abs(x2 - x1) <= abs(x2 - x0) && abs(y2 - y1) <= abs(y2 - y0)) { // Tile i+1 is between i and i+2. + /* The new tile is in the continuity, just update the last tile. */ + this->tiles[cnt - 1] = tile; + MarkTileLineDirty(t1, tile); + return true; + } + } + } + } + + if (this->tiles.size() * sizeof(TileIndex) >= MAX_CMD_TEXT_LENGTH) return false; + + this->tiles.push_back(tile); + return true; + } + + void SetFocus(bool focused) + { + if (this->focused != focused) this->MarkDirty(); + this->focused = focused; + } + + bool ToggleVisibility() + { + this->SetVisibility(!this->visible); + return this->visible; + } + + void SetVisibility(bool visible) + { + if (this->visible != visible) this->MarkDirty(); + this->visible = visible; + } + + void MarkDirty() + { + const uint sz = (uint) this->tiles.size(); + for (uint i = 1; i < sz; i++) { + MarkTileLineDirty(this->tiles[i-1], this->tiles[i]); + } + } + + TileIndex *Export(uint *buffer_length) + { + const uint cnt = (uint) this->tiles.size(); + const uint datalen = sizeof(TileIndex) * cnt; + TileIndex *buffer = (TileIndex *) malloc(datalen); + if (buffer) { + for (uint i = 0; i < cnt; i++) { + buffer[i] = TO_LE32(this->tiles[i]); + } + if (buffer_length) *buffer_length = datalen; + } + return buffer; + } + + void Import(const TileIndex* data, const uint data_length) + { + for (uint i = data_length; i != 0; i--, data++) { + this->tiles.push_back(FROM_LE32(*data)); + } + } +}; + +struct Plan : PlanPool::PoolItem<&_plan_pool> { + OwnerByte owner; + PlanLineVector lines; + PlanLine *temp_line; + bool visible; + bool visible_by_all; + bool show_lines; + Date creation_date; + + Plan(Owner owner = INVALID_OWNER) + { + this->owner = owner; + this->creation_date = _date; + this->visible = false; + this->visible_by_all = false; + this->show_lines = false; + this->temp_line = new PlanLine(); + } + + ~Plan() + { + for (PlanLineVector::iterator it = lines.begin(); it != lines.end(); it++) { + delete (*it); + } + this->lines.clear(); + delete temp_line; + } + + void SetFocus(bool focused) + { + for (PlanLineVector::iterator it = lines.begin(); it != lines.end(); it++) { + (*it)->SetFocus(focused); + } + } + + void SetVisibility(bool visible, bool do_lines = true) + { + this->visible = visible; + + if (!do_lines) return; + for (PlanLineVector::iterator it = lines.begin(); it != lines.end(); it++) { + (*it)->SetVisibility(visible); + } + } + + bool ToggleVisibility() + { + this->SetVisibility(!this->visible); + return this->visible; + } + + PlanLine *NewLine() + { + PlanLine *pl = new PlanLine(); + if (pl) this->lines.push_back(pl); + return pl; + } + + bool StoreTempTile(TileIndex tile) + { + return this->temp_line->AppendTile(tile); + } + + bool ValidateNewLine() + { + bool ret = false; + if (this->temp_line->tiles.size() > 1) { + uint buffer_length = 0; + const TileIndex *buffer = this->temp_line->Export(&buffer_length); + if (buffer) { + _current_plan->SetVisibility(true, false); + ret = DoCommandP(0, _current_plan->index, (uint32) this->temp_line->tiles.size(), CMD_ADD_PLAN_LINE, NULL, (const char *) buffer, true, buffer_length); + free(buffer); + } + _current_plan->temp_line->MarkDirty(); + this->temp_line->Clear(); + } + return ret; + } + + bool IsListable() + { + return (this->owner == _local_company || this->visible_by_all); + } + + bool IsVisible() + { + if (!this->IsListable()) return false; + return this->visible; + } + + bool ToggleVisibilityByAll() + { + if (_current_plan->owner == _local_company) DoCommandP(0, _current_plan->index, !this->visible_by_all, CMD_CHANGE_PLAN_VISIBILITY); + return this->visible_by_all; + } +}; + +#define FOR_ALL_PLANS_FROM(var, start) FOR_ALL_ITEMS_FROM(Plan, plan_index, var, start) +#define FOR_ALL_PLANS(var) FOR_ALL_PLANS_FROM(var, 0) + +#endif /* PLANS_BASE_H */ diff --git a/src/plans_cmd.cpp b/src/plans_cmd.cpp new file mode 100644 index 0000000000..1cc585e410 --- /dev/null +++ b/src/plans_cmd.cpp @@ -0,0 +1,140 @@ +/* $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 plans_cmd.cpp Handling of plan related commands. */ + +#include "stdafx.h" +#include "command_func.h" +#include "plans_base.h" +#include "plans_func.h" +#include "window_func.h" +#include "company_func.h" +#include "window_gui.h" +#include "table/strings.h" + +/** + * Create a new plan. + * @param tile unused + * @param flags type of operation + * @param p1 owner of the plan + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdAddPlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (!Plan::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_PLANS); + if (flags & DC_EXEC) { + Owner o = (Owner) p1; + _new_plan = new Plan(o); + if (o == _local_company) { + _new_plan->SetVisibility(true); + Window *w = FindWindowById(WC_PLANS, 0); + if (w) w->InvalidateData(INVALID_PLAN, false); + } + } + return CommandCost(); +} + +/** + * Create a new line in a plan. + * @param tile unused + * @param flags type of operation + * @param p1 plan id + * @param p2 number of nodes + * @param text list of tile indexes that compose the line, encoded in base64 + * @return the cost of this operation or an error + */ +CommandCost CmdAddPlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (flags & DC_EXEC) { + Plan *p = Plan::Get(p1); + PlanLine *pl = p->NewLine(); + if (!pl) return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_LINES); + if (p2 > (MAX_CMD_TEXT_LENGTH / sizeof(TileIndex))) return_cmd_error(STR_ERROR_TOO_MANY_NODES); + pl->Import((const TileIndex *) text, p2); + if (p->IsListable()) { + pl->SetVisibility(p->visible); + if (p->visible) pl->MarkDirty(); + Window *w = FindWindowById(WC_PLANS, 0); + if (w) w->InvalidateData(INVALID_PLAN, false); + } + } + return CommandCost(); +} + +/** + * Edit the visibility of a plan. + * @param tile unused + * @param flags type of operation + * @param p1 plan id + * @param p2 visibility + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdChangePlanVisibility(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (flags & DC_EXEC) { + Plan *p = Plan::Get(p1); + p->visible_by_all = p2 != 0; + Window *w = FindWindowById(WC_PLANS, 0); + if (w) w->InvalidateData(INVALID_PLAN, false); + } + return CommandCost(); +} + +/** + * Delete a plan. + * @param tile unused + * @param flags type of operation + * @param p1 plan id + * @param p2 unused + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdRemovePlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (flags & DC_EXEC) { + Plan *p = Plan::Get(p1); + if (p->IsListable()) { + p->SetVisibility(false); + Window *w = FindWindowById(WC_PLANS, 0); + if (w) w->InvalidateData(p->index, false); + } + if (p == _current_plan) _current_plan = NULL; + delete p; + } + return CommandCost(); +} + +/** + * Remove a line from a plan. + * @param tile unused + * @param flags type of operation + * @param p1 plan id + * @param p2 line id + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdRemovePlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (flags & DC_EXEC) { + Plan *p = Plan::Get(p1); + PlanLineVector::iterator it = p->lines.begin(); + std::advance(it, p2); + (*it)->SetVisibility(false); + delete *it; + p->lines.erase(it); + if (p->IsListable()) { + Window *w = FindWindowById(WC_PLANS, 0); + if (w) w->InvalidateData(p->index, false); + } + } + return CommandCost(); +} diff --git a/src/plans_func.h b/src/plans_func.h new file mode 100644 index 0000000000..d158c2bf15 --- /dev/null +++ b/src/plans_func.h @@ -0,0 +1,22 @@ +/* $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 plans_func.h Functions related to plans. */ + +#ifndef PLANS_FUNC_H +#define PLANS_FUNC_H + +#include "plans_type.h" + +void ShowPlansWindow(); + +extern Plan *_new_plan; +extern Plan *_current_plan; + +#endif /* PLANS_FUNC_H */ diff --git a/src/plans_gui.cpp b/src/plans_gui.cpp new file mode 100644 index 0000000000..8139e49d24 --- /dev/null +++ b/src/plans_gui.cpp @@ -0,0 +1,366 @@ +/* $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 plans_gui.cpp The GUI for planning. */ + +#include "stdafx.h" +#include "plans_func.h" +#include "plans_base.h" +#include "command_func.h" +#include "company_func.h" +#include "company_gui.h" +#include "settings_gui.h" +#include "window_gui.h" +#include "window_func.h" +#include "viewport_func.h" +#include "gfx_func.h" +#include "tilehighlight_func.h" +#include "strings_func.h" +#include "core/pool_func.hpp" +#include "widgets/plans_widget.h" +#include "table/strings.h" +#include "table/sprites.h" + +static const NWidgetPart _nested_plans_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, WID_PLN_CAPTION), SetDataTip(STR_PLANS_CAPTION, STR_NULL), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_INSET, COLOUR_GREY, WID_PLN_LIST), SetFill(1, 1), SetPadding(2, 1, 2, 2), SetResize(1, 0), SetScrollbar(WID_PLN_SCROLLBAR), SetDataTip(STR_NULL, STR_PLANS_LIST_TOOLTIP), + EndContainer(), + EndContainer(), + EndContainer(), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_PLN_SCROLLBAR), + EndContainer(), + + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_NEW), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_NEW_PLAN, STR_NULL), + NWidget(WWT_TEXTBTN_2, COLOUR_GREY, WID_PLN_ADD_LINES), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_ADD_LINES, STR_PLANS_ADDING_LINES), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_PLN_VISIBILITY), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_VISIBILITY_PUBLIC, STR_PLANS_VISIBILITY_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_HIDE_ALL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_HIDE_ALL, STR_NULL), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_DELETE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_DELETE, STR_PLANS_DELETE_TOOLTIP), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _plans_desc( + WDP_AUTO, "plans", 300, 100, + WC_PLANS, WC_NONE, + WDF_CONSTRUCTION, + _nested_plans_widgets, lengthof(_nested_plans_widgets) +); + +struct PlansWindow : Window { + typedef struct { + bool is_plan; + int plan_id; + int line_id; + } ListItem; + + Scrollbar *vscroll; + int text_offset; + std::vector list; ///< The translation table linking panel indices to their related PlanID. + int selected; ///< What item is currently selected in the panel. + + PlansWindow(WindowDesc *desc) : Window(desc) + { + this->CreateNestedTree(); + this->vscroll = this->GetScrollbar(WID_PLN_SCROLLBAR); + this->FinishInitNested(); + + Dimension spr_dim = GetSpriteSize(SPR_COMPANY_ICON); + this->text_offset = WD_FRAMETEXT_LEFT + spr_dim.width + 2 + SETTING_BUTTON_WIDTH; + + this->selected = INT_MAX; + RebuildList(); + } + + ~PlansWindow() + { + this->list.clear(); + _current_plan = NULL; + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case WID_PLN_NEW: + DoCommandP(0, _local_company, 0, CMD_ADD_PLAN, CcAddPlan); + break; + case WID_PLN_ADD_LINES: + if (_current_plan) HandlePlacePushButton(this, widget, SPR_CURSOR_MOUSE, HT_POINT); + break; + case WID_PLN_DELETE: + if (this->selected != INT_MAX) { + if (this->list[this->selected].is_plan) { + DoCommandP(0, this->list[this->selected].plan_id, 0, CMD_REMOVE_PLAN); + } else { + DoCommandP(0, this->list[this->selected].plan_id, this->list[this->selected].line_id, CMD_REMOVE_PLAN_LINE); + } + } + break; + case WID_PLN_HIDE_ALL: { + Plan *p; + FOR_ALL_PLANS(p) { + if (p->IsListable()) p->SetVisibility(false); + } + this->SetWidgetDirty(WID_PLN_LIST); + break; + } + case WID_PLN_VISIBILITY: + if (_current_plan) _current_plan->ToggleVisibilityByAll(); + break; + case WID_PLN_LIST: { + int new_selected = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_PLN_LIST, WD_FRAMERECT_TOP); + if (this->selected != INT_MAX) { + _current_plan->SetFocus(false); + } + if (new_selected != INT_MAX) { + if (this->list[new_selected].is_plan) { + _current_plan = Plan::Get(this->list[new_selected].plan_id); + _current_plan->SetFocus(true); + if (pt.x >= 22 && pt.x < 41) _current_plan->ToggleVisibility(); + } else { + _current_plan = Plan::Get(this->list[new_selected].plan_id); + PlanLine *pl = _current_plan->lines[this->list[new_selected].line_id]; + pl->SetFocus(true); + if (pt.x >= 22 && pt.x < 41) { + if (pl->ToggleVisibility()) _current_plan->SetVisibility(true, false); + } + } + if (click_count > 1 && (pt.x < 22 || pt.x >= 41)) { + _current_plan->show_lines = !_current_plan->show_lines; + this->InvalidateData(INVALID_PLAN); + } + } else { + if (_current_plan) { + _current_plan->SetFocus(false); + _current_plan = NULL; + } + } + this->selected = new_selected; + this->SetDirty(); + break; + } + default: break; + } + } + + virtual void OnPaint() + { + this->SetWidgetDisabledState(WID_PLN_HIDE_ALL, this->vscroll->GetCount() == 0); + if (_current_plan) { + this->SetWidgetsDisabledState(_current_plan->owner != _local_company, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END); + this->GetWidget(WID_PLN_VISIBILITY)->widget_data = _current_plan->visible_by_all ? STR_PLANS_VISIBILITY_PRIVATE : STR_PLANS_VISIBILITY_PUBLIC; + } else { + this->SetWidgetsDisabledState(true, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END); + } + this->DrawWidgets(); + } + + virtual void DrawWidget(const Rect &r, int widget) const + { + switch (widget) { + case WID_PLN_LIST: { + uint y = r.top + WD_FRAMERECT_TOP; // Offset from top of widget. + if (this->vscroll->GetCount() == 0) { + DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_STATION_LIST_NONE); + return; + } + + bool rtl = _current_text_dir == TD_RTL; + uint icon_left = 4 + (rtl ? r.right - this->text_offset : r.left); + uint btn_left = (rtl ? icon_left - SETTING_BUTTON_WIDTH + 4 : icon_left + SETTING_BUTTON_WIDTH - 4); + uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : this->text_offset); + uint text_right = r.right - (rtl ? this->text_offset : WD_FRAMERECT_RIGHT); + + for (uint16 i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) { + Plan *p = Plan::Get(list[i].plan_id); + + if (i == this->selected) GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_GREY); + + if (list[i].is_plan) { + DrawCompanyIcon(p->owner, icon_left, y + (FONT_HEIGHT_NORMAL - 10) / 2 + 1); + DrawBoolButton(btn_left, y + (FONT_HEIGHT_NORMAL - 10) / 2, p->visible, true); + SetDParam(0, list[i].plan_id + 1); + SetDParam(1, p->lines.size()); + SetDParam(2, p->creation_date); + DrawString(text_left, text_right, y, STR_PLANS_LIST_ITEM_PLAN, p->visible_by_all ? TC_LIGHT_BLUE : TC_YELLOW); + } else { + PlanLine *pl = p->lines[list[i].line_id]; + DrawBoolButton(btn_left, y + (FONT_HEIGHT_NORMAL - 10) / 2, pl->visible, true); + SetDParam(0, list[i].line_id + 1); + SetDParam(1, pl->tiles.size() - 1); + DrawString(text_left, text_right, y, STR_PLANS_LIST_ITEM_LINE, TC_WHITE); + } + y += this->resize.step_height; + } + break; + } + } + } + + virtual void OnResize() + { + this->vscroll->SetCapacityFromWidget(this, WID_PLN_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM); + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + switch (widget) { + case WID_PLN_LIST: + resize->height = FONT_HEIGHT_NORMAL; + size->height = resize->height * 5 + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; + break; + } + } + + /** The drawing of a line starts. */ + virtual void OnPlaceObject(Point pt, TileIndex tile) + { + /* A player can't add lines to a public plan of another company. */ + if (_current_plan && _current_plan->owner == _local_company) VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_DRAW_PLANLINE); + } + + /** The drawing of a line is in progress. */ + virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) + { + const Point p = GetTileBelowCursor(); + const TileIndex tile = TileVirtXY(p.x, p.y); + if (_current_plan && tile < MapSize()) { + _current_plan->StoreTempTile(tile); + _thd.selstart = _thd.selend; + } + } + + /** The drawing of a line ends up normally. */ + virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) + { + if (_current_plan) _current_plan->ValidateNewLine(); + } + + /** The drawing of a line is aborted. */ + virtual void OnPlaceObjectAbort() + { + if (_current_plan) { + _current_plan->temp_line->MarkDirty(); + _current_plan->temp_line->Clear(); + } + + this->RaiseWidget(WID_PLN_ADD_LINES); + this->SetWidgetDirty(WID_PLN_ADD_LINES); + } + + void RebuildList() + { + int old_focused_plan_id = this->selected == INT_MAX ? INT_MAX : this->list[this->selected].plan_id; + + int sbcnt = 0; + this->list.clear(); + Plan *p; + FOR_ALL_PLANS(p) { + if (!p->IsListable()) continue; + + ListItem li; + li.is_plan = true; + li.plan_id = p->index; + this->list.push_back(li); + if (old_focused_plan_id == p->index) this->selected = sbcnt; + sbcnt++; + + if (p->show_lines) { + const int sz = (int) p->lines.size(); + sbcnt += sz; + li.is_plan = false; + for (int i = 0; i < sz; i++) { + li.line_id = i; + this->list.push_back(li); + } + } + } + + if (this->selected == INT_MAX) ResetObjectToPlace(); + + this->vscroll->SetCount(sbcnt); + } + + virtual void OnInvalidateData(int data = 0, bool gui_scope = true) + { + if (data != INVALID_PLAN && this->selected != INT_MAX) { + if (this->list[this->selected].plan_id == data) { + /* Invalidate the selection if the selected plan has been modified or deleted. */ + this->selected = INT_MAX; + + /* Cancel drawing associated to the deleted plan. */ + ResetObjectToPlace(); + } + } + + RebuildList(); + } + + void SelectPlan(PlanID plan_index) + { + if (this->selected != INT_MAX) { + if (plan_index == this->list[this->selected].plan_id) return; + Plan::Get(this->list[this->selected].plan_id)->SetFocus(false); + } + + if (plan_index == INVALID_PLAN) { + this->selected = INT_MAX; + return; + } + Plan::Get(plan_index)->SetFocus(true); + + for (size_t i = 0; i < this->list.size(); i++) { + if (this->list[i].is_plan && this->list[i].plan_id == plan_index) { + this->selected = (int) i; + return; + } + } + } +}; + +/** Show the window to manage plans. */ +void ShowPlansWindow() +{ + if (BringWindowToFrontById(WC_PLANS, 0) != NULL) return; + new PlansWindow(&_plans_desc); +} + +/** + * Only the creator of a plan executes this function. + * The other players should not be bothered with these changes. + */ +void CcAddPlan(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2) +{ + if (result.Failed()) return; + + _current_plan = _new_plan; + _current_plan->SetVisibility(true); + + Window *w = FindWindowById(WC_PLANS, 0); + if (w) { + w->InvalidateData(INVALID_PLAN, false); + ((PlansWindow *) w)->SelectPlan(_current_plan->index); + if (!w->IsWidgetLowered(WID_PLN_ADD_LINES)) { + w->SetWidgetDisabledState(WID_PLN_ADD_LINES, false); + HandlePlacePushButton(w, WID_PLN_ADD_LINES, SPR_CURSOR_MOUSE, HT_POINT); + } + } +} diff --git a/src/plans_type.h b/src/plans_type.h new file mode 100644 index 0000000000..5947fc39e4 --- /dev/null +++ b/src/plans_type.h @@ -0,0 +1,25 @@ +/* $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 plans_type.h Types related to planning. */ + +#ifndef PLANS_TYPE_H +#define PLANS_TYPE_H + +#include "stdafx.h" +#include "core/smallvec_type.hpp" +#include "tile_type.h" + +typedef uint16 PlanID; +struct PlanLine; +struct Plan; + +static const PlanID INVALID_PLAN = 0xFFFF; ///< Sentinel for an invalid plan. + +#endif /* PLANS_TYPE_H */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index d3093aa7b9..38ad5f0182 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1137,7 +1137,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint mask = GetPresentSignals(tile) & SignalOnTrack(track); SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask)); } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); AddTrackToSignalBuffer(tile, track, _current_company); YapfNotifyTrackLayoutChange(tile, track); if (v != NULL) { @@ -1462,7 +1462,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1 YapfNotifyTrackLayoutChange(tile, track); if (v != NULL) TryPathReserve(v, false); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]); @@ -1605,7 +1605,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 } SetRailType(tile, totype); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); /* update power of train on this tile */ FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc); } @@ -1688,13 +1688,13 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); - MarkTileDirtyByTile(tile); - MarkTileDirtyByTile(endtile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + MarkTileDirtyByTile(endtile, ZOOM_LVL_DRAW_MAP); if (IsBridge(tile)) { TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); TileIndex t = tile + delta; - for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function + for (; t != endtile; t += delta) MarkTileDirtyByTile(t, ZOOM_LVL_DRAW_MAP); // TODO encapsulate this into a function } } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index ff330b0352..03062edfe7 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -198,7 +198,7 @@ static void PlaceRail_Station(TileIndex tile) int h = _settings_client.gui.station_platlength; if (!_railstation.orientation) Swap(w, h); - CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" }; + CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, 0, "" }; ShowSelectStationIfNeeded(cmdcont, TileArea(tile, w, h)); } } @@ -719,7 +719,7 @@ struct BuildRailToolbarWindow : Window { uint32 p1 = _cur_railtype | (select_method == VPM_FIX_X ? AXIS_X : AXIS_Y) << 4 | ta.w << 8 | ta.h << 16 | _ctrl_pressed << 24; uint32 p2 = STAT_CLASS_WAYP | _cur_waypoint_type << 8 | INVALID_STATION << 16; - CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT), CcPlaySound1E, "" }; + CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_WAYPOINT | CMD_MSG(STR_ERROR_CAN_T_BUILD_TRAIN_WAYPOINT), CcPlaySound1E, 0, "" }; ShowSelectWaypointIfNeeded(cmdcont, ta); } } @@ -876,7 +876,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) uint32 p1 = _cur_railtype | _railstation.orientation << 4 | numtracks << 8 | platlength << 16 | _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, "" }; + CommandContainer cmdcont = { ta.tile, p1, p2, CMD_BUILD_RAIL_STATION | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION), CcStation, 0, "" }; ShowSelectStationIfNeeded(cmdcont, ta); } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index e56633696a..886bab0a4c 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -1522,7 +1522,7 @@ static void TileLoop_Road(TileIndex tile) TileY(tile) * TILE_SIZE + 7, 0, EV_BULLDOZER); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); return; } } @@ -1547,7 +1547,7 @@ static void TileLoop_Road(TileIndex tile) cur_rs = ROADSIDE_BARREN; } SetRoadside(tile, cur_rs); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } else if (IncreaseRoadWorksCounter(tile)) { TerminateRoadWorks(tile); @@ -1562,7 +1562,7 @@ static void TileLoop_Road(TileIndex tile) } } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 6b23ca631e..2cb788eee4 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -233,7 +233,7 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, uint32 p2, u p2 |= ddir << 6; // Set the DiagDirecion into p2 bits 6 and 7. TileArea ta(start_tile, end_tile); - CommandContainer cmdcont = { ta.tile, ta.w | ta.h << 8, p2, cmd, CcRoadStop, "" }; + CommandContainer cmdcont = { ta.tile, ta.w | ta.h << 8, p2, cmd, CcRoadStop, 0, "" }; ShowSelectStationIfNeeded(cmdcont, ta); } diff --git a/src/saveload/plans_sl.cpp b/src/saveload/plans_sl.cpp new file mode 100644 index 0000000000..7e39d9239c --- /dev/null +++ b/src/saveload/plans_sl.cpp @@ -0,0 +1,87 @@ +/* $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 plans_sl.cpp Code handling saving and loading of plans data. */ + +#include "../stdafx.h" +#include "../plans_base.h" +#include "../fios.h" + +#include "saveload.h" + +/** Description of a plan within the savegame. */ +static const SaveLoad _plan_desc[] = { + SLE_VAR(Plan, owner, SLE_UINT8), + SLE_VAR(Plan, visible, SLE_BOOL), + SLE_VAR(Plan, visible_by_all, SLE_BOOL), + SLE_VAR(Plan, creation_date, SLE_INT32), + SLE_END() +}; + +/** Save all plans. */ +static void Save_PLAN() +{ + Plan *p; + FOR_ALL_PLANS(p) { + SlSetArrayIndex(p->index); + SlObject(p, _plan_desc); + } +} + +/** Load all plans. */ +static void Load_PLAN() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Plan *p = new (index) Plan(); + SlObject(p, _plan_desc); + } +} + +/** Save all plan lines. */ +static void Save_PLANLINE() +{ + Plan *p; + FOR_ALL_PLANS(p) { + for (size_t i = 0; i < p->lines.size(); i++) { + SlSetArrayIndex((uint) p->index << 16 | (uint) i); + PlanLine *pl = p->lines[i]; + size_t plsz = pl->tiles.size(); + SlSetLength(plsz * sizeof(TileIndex)); + SlArray(&pl->tiles[0], plsz, SLE_UINT32); + } + } +} + +/** Load all plan lines. */ +static void Load_PLANLINE() +{ + int index; + while ((index = SlIterateArray()) != -1) { + Plan *p = Plan::Get((uint) index >> 16); + uint line_index = index & 0xFFFF; + if (p->lines.size() <= line_index) p->lines.resize(line_index + 1); + PlanLine *pl = new PlanLine(); + p->lines[line_index] = pl; + size_t plsz = SlGetFieldLength() / sizeof(TileIndex); + pl->tiles.resize(plsz); + SlArray(&pl->tiles[0], plsz, SLE_UINT32); + } + + Plan *p; + FOR_ALL_PLANS(p) { + p->SetVisibility(false); + } +} + +/** Chunk handlers related to plans. */ +extern const ChunkHandler _plan_chunk_handlers[] = { + { 'PLAN', Save_PLAN, Load_PLAN, NULL, NULL, CH_ARRAY}, + { 'PLLN', Save_PLANLINE, Load_PLANLINE, NULL, NULL, CH_ARRAY | CH_LAST}, +}; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 717777804a..be945c6064 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -263,7 +263,7 @@ * 193 26802 * 194 26881 */ -extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 195; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading @@ -447,6 +447,7 @@ extern const ChunkHandler _linkgraph_chunk_handlers[]; extern const ChunkHandler _airport_chunk_handlers[]; extern const ChunkHandler _object_chunk_handlers[]; extern const ChunkHandler _persistent_storage_chunk_handlers[]; +extern const ChunkHandler _plan_chunk_handlers[]; /** Array of all chunks in a savegame, \c NULL terminated. */ static const ChunkHandler * const _chunk_handlers[] = { @@ -483,6 +484,7 @@ static const ChunkHandler * const _chunk_handlers[] = { _airport_chunk_handlers, _object_chunk_handlers, _persistent_storage_chunk_handlers, + _plan_chunk_handlers, NULL, }; diff --git a/src/script/api/script_object.cpp b/src/script/api/script_object.cpp index 49dba6bb73..14ef59d8a0 100644 --- a/src/script/api/script_object.cpp +++ b/src/script/api/script_object.cpp @@ -307,7 +307,7 @@ ScriptObject::ActiveInstance::~ActiveInstance() #endif /* Try to perform the command. */ - CommandCost res = ::DoCommandPInternal(tile, p1, p2, cmd, (_networking && !_generating_world) ? ScriptObject::GetActiveInstance()->GetDoCommandCallback() : NULL, text, false, estimate_only); + CommandCost res = ::DoCommandPInternal(tile, p1, p2, cmd, (_networking && !_generating_world) ? ScriptObject::GetActiveInstance()->GetDoCommandCallback() : NULL, text, false, estimate_only, 0); /* We failed; set the error and bail out */ if (res.Failed()) { diff --git a/src/settings.cpp b/src/settings.cpp index e2baed5ba6..f97f64d0cb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1966,7 +1966,7 @@ void SyncCompanySettings() const void *new_var = GetVariableAddress(&_settings_client.company, &sd->save); uint32 old_value = (uint32)ReadValue(old_var, sd->save.conv); uint32 new_value = (uint32)ReadValue(new_var, sd->save.conv); - if (old_value != new_value) NetworkSendCommand(0, i, new_value, CMD_CHANGE_COMPANY_SETTING, NULL, NULL, _local_company); + if (old_value != new_value) NetworkSendCommand(0, i, new_value, CMD_CHANGE_COMPANY_SETTING, NULL, NULL, _local_company, 0); } } #endif /* ENABLE_NETWORK */ diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f1af4804e4..08b817b78b 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1256,7 +1256,6 @@ uint SettingsContainer::GetMaxHelpHeight(int maxw) return biggest; } - /** * Draw a row in the settings panel. * @@ -1478,6 +1477,9 @@ 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.show_vehicle_route_steps")); + graphics->Add(new SettingEntry("gui.show_vehicle_route")); + graphics->Add(new SettingEntry("gui.dash_level_of_route_lines")); } SettingsPage *sound = main->Add(new SettingsPage(STR_CONFIG_SETTING_SOUND)); @@ -1505,6 +1507,18 @@ static SettingsContainer &GetSettingsTree() SettingsPage *viewports = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_VIEWPORTS)); { + SettingsPage *viewport_map = interface->Add(new SettingsPage(STR_CONFIG_SETTING_VIEWPORT_MAP_OPTIONS)); + { + viewport_map->Add(new SettingEntry("gui.default_viewport_map_mode")); + viewport_map->Add(new SettingEntry("gui.action_when_viewport_map_is_dblclicked")); + viewport_map->Add(new SettingEntry("gui.viewport_map_scan_surroundings")); + viewport_map->Add(new SettingEntry("gui.show_scrolling_viewport_on_map")); + viewport_map->Add(new SettingEntry("gui.show_slopes_on_viewport_map")); + viewport_map->Add(new SettingEntry("gui.show_bridges_on_map")); + viewport_map->Add(new SettingEntry("gui.show_tunnels_on_map")); + viewport_map->Add(new SettingEntry("gui.use_owner_colour_for_tunnelbridge")); + } + viewports->Add(new SettingEntry("gui.auto_scrolling")); viewports->Add(new SettingEntry("gui.reverse_scroll")); viewports->Add(new SettingEntry("gui.smooth_scroll")); diff --git a/src/settings_type.h b/src/settings_type.h index 9ea98caea8..c49477f123 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -111,6 +111,16 @@ struct GUISettings { 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? + bool viewport_map_scan_surroundings; ///< look for the most important tile in surroundings + bool show_slopes_on_viewport_map; ///< use slope orientation to render the ground + uint32 default_viewport_map_mode; ///< the mode to use by default when a viewport is in map mode, 0=owner, 1=industry, 2=vegetation + uint32 action_when_viewport_map_is_dblclicked; ///< what to do when a doubleclick occurs on the viewport map + uint32 show_scrolling_viewport_on_map; ///< when a no map viewport is scrolled, its location is marked on the other map viewports + bool show_bridges_on_map; ///< bridges are rendered on a viewport in map mode + bool show_tunnels_on_map; ///< tunnels are rendered on a viewport in map mode + uint32 show_vehicle_route; ///< show a vehicle's route when its orders/timetable window is focused + uint32 dash_level_of_route_lines; ///< the dash level passed to GfxDrawLine() (plain if 0) + bool use_owner_colour_for_tunnelbridge;///< bridges and tunnels are rendered with their owner's colour bool timetable_arrival_departure; ///< show arrivals and departures in vehicle timetables bool left_mouse_btn_scrolling; ///< left mouse button scroll bool pause_on_newgame; ///< whether to start new games paused or not @@ -136,6 +146,7 @@ struct GUISettings { byte missing_strings_threshold; ///< the number of missing strings before showing the warning uint8 graph_line_thickness; ///< the thickness of the lines in the various graph guis uint8 osk_activation; ///< Mouse gesture to trigger the OSK. + bool show_vehicle_route_steps; ///< when a window related to a specific vehicle is focused, show route steps uint16 console_backlog_timeout; ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity. uint16 console_backlog_length; ///< the minimum amount of items in the console backlog before items will be removed. diff --git a/src/signal.cpp b/src/signal.cpp index 8e870b53db..04060a5aba 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -446,7 +446,7 @@ static void UpdateSignalsAroundSegment(SigFlags flags) _globset.Add(tile, exitdir); // do not check for full global set, first update all signals } SetSignalStateByTrackdir(tile, trackdir, newstate); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } diff --git a/src/smallmap_colours.h b/src/smallmap_colours.h new file mode 100644 index 0000000000..f4eea04b06 --- /dev/null +++ b/src/smallmap_colours.h @@ -0,0 +1,106 @@ +/* $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 smallmap_colours.h Colours used by smallmap. */ + +#ifndef SMALLMAP_COLOURS_H +#define SMALLMAP_COLOURS_H + +#include "core/endian_func.hpp" + +static const uint8 PC_ROUGH_LAND = 0x52; ///< Dark green palette colour for rough land. +static const uint8 PC_GRASS_LAND = 0x54; ///< Dark green palette colour for grass land. +static const uint8 PC_BARE_LAND = 0x37; ///< Brown palette colour for bare land. +static const uint8 PC_FIELDS = 0x25; ///< Light brown palette colour for fields. +static const uint8 PC_TREES = 0x57; ///< Green palette colour for trees. +static const uint8 PC_WATER = 0xCA; ///< Dark blue palette colour for water. + +#define MKCOLOUR(x) TO_LE32X(x) + +#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x)) +#define MKCOLOUR_X0X0(x) (MKCOLOUR(0x01000100) * (uint)(x)) +#define MKCOLOUR_0X0X(x) (MKCOLOUR(0x00010001) * (uint)(x)) +#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x)) +#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x)) + +#define MKCOLOUR_XYXY(x, y) (MKCOLOUR_X0X0(x) | MKCOLOUR_0X0X(y)) +#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y)) + +#define MKCOLOUR_0000 MKCOLOUR_XXXX(0x00) +#define MKCOLOUR_0FF0 MKCOLOUR_0XX0(0xFF) +#define MKCOLOUR_F00F MKCOLOUR_X00X(0xFF) +#define MKCOLOUR_FFFF MKCOLOUR_XXXX(0xFF) + +#include "table/heightmap_colours.h" +#include "table/darklight_colours.h" + +/** Colour scheme of the smallmap. */ +struct SmallMapColourScheme { + uint32 *height_colours; ///< Cached colours for each level in a map. + const uint32 *height_colours_base; ///< Base table for determining the colours + size_t colour_count; ///< The number of colours. + uint32 default_colour; ///< Default colour of the land. +}; + +extern SmallMapColourScheme _heightmap_schemes[]; + +struct AndOr { + uint32 mor; + uint32 mand; +}; + +static inline uint32 ApplyMask(uint32 colour, const AndOr *mask) +{ + return (colour & mask->mand) | mask->mor; +} + +/** Colour masks for "Contour" and "Routes" modes. */ +static const AndOr _smallmap_contours_andor[] = { + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_CLEAR + {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F}, // MP_RAILWAY + {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_ROAD + {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_HOUSE + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TREES + {MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000}, // MP_STATION + {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000}, // MP_WATER + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_VOID + {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000}, // MP_INDUSTRY + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE + {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_OBJECT + {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F}, +}; + +/** Colour masks for "Vehicles", "Industry", and "Vegetation" modes. */ +static const AndOr _smallmap_vehicles_andor[] = { + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_CLEAR + {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_RAILWAY + {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_ROAD + {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_HOUSE + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TREES + {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_STATION + {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000}, // MP_WATER + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_VOID + {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000}, // MP_INDUSTRY + {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE + {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_OBJECT + {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, +}; + +static const uint32 _vegetation_clear_bits[] = { + MKCOLOUR_XXXX(PC_GRASS_LAND), ///< full grass + MKCOLOUR_XXXX(PC_ROUGH_LAND), ///< rough land + MKCOLOUR_XXXX(PC_GREY), ///< rocks + MKCOLOUR_XXXX(PC_FIELDS), ///< fields + MKCOLOUR_XXXX(PC_LIGHT_BLUE), ///< snow + MKCOLOUR_XXXX(PC_ORANGE), ///< desert + MKCOLOUR_XXXX(PC_GRASS_LAND), ///< unused + MKCOLOUR_XXXX(PC_GRASS_LAND), ///< unused +}; + +#endif /* SMALLMAP_COLOURS_H */ diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp index dea0caa391..b77cffd21c 100644 --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -24,6 +24,7 @@ #include "window_func.h" #include "company_base.h" +#include "smallmap_colours.h" #include "smallmap_gui.h" #include "table/strings.h" @@ -37,15 +38,6 @@ static int _smallmap_cargo_count; ///< Number of cargos in the link stats leg /** Link stat colours shown in legenda. */ static uint8 _linkstat_colours_in_legenda[] = {0, 1, 3, 5, 7, 9, 11}; -static const int NUM_NO_COMPANY_ENTRIES = 4; ///< Number of entries in the owner legend that are not companies. - -static const uint8 PC_ROUGH_LAND = 0x52; ///< Dark green palette colour for rough land. -static const uint8 PC_GRASS_LAND = 0x54; ///< Dark green palette colour for grass land. -static const uint8 PC_BARE_LAND = 0x37; ///< Brown palette colour for bare land. -static const uint8 PC_FIELDS = 0x25; ///< Light brown palette colour for fields. -static const uint8 PC_TREES = 0x57; ///< Green palette colour for trees. -static const uint8 PC_WATER = 0xCA; ///< Dark blue palette colour for water. - /** Macro for ordinary entry of LegendAndColour */ #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, 0, INVALID_COMPANY, true, false, false} @@ -133,7 +125,7 @@ static const LegendAndColour _legend_vegetation[] = { MKEND() }; -static LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = { +LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1] = { MO(PC_WATER, STR_SMALLMAP_LEGENDA_WATER), MO(0x00, STR_SMALLMAP_LEGENDA_NO_OWNER), // This colour will vary depending on settings. MO(PC_DARK_RED, STR_SMALLMAP_LEGENDA_TOWNS), @@ -155,17 +147,17 @@ static LegendAndColour _legend_linkstats[NUM_CARGO + lengthof(_linkstat_colours_ * Allow room for all industries, plus a terminator entry * This is required in order to have the industry slots all filled up */ -static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1]; +LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1]; /** For connecting industry type to position in industries list(small map legend) */ -static uint _industry_to_list_pos[NUM_INDUSTRYTYPES]; +uint _industry_to_list_pos[NUM_INDUSTRYTYPES]; /** Show heightmap in industry and owner mode of smallmap window. */ -static bool _smallmap_show_heightmap = false; +bool _smallmap_show_heightmap = false; /** Highlight a specific industry type */ static IndustryType _smallmap_industry_highlight = INVALID_INDUSTRYTYPE; /** State of highlight blinking */ static bool _smallmap_industry_highlight_state; /** For connecting company ID to position in owner list (small map legend) */ -static uint _company_to_list_pos[MAX_COMPANIES]; +uint _company_to_list_pos[MAX_COMPANIES]; /** * Fills an array for the industries legends. @@ -241,35 +233,9 @@ static const LegendAndColour * const _legend_table[] = { _legend_land_owners, }; -#define MKCOLOUR(x) TO_LE32X(x) - -#define MKCOLOUR_XXXX(x) (MKCOLOUR(0x01010101) * (uint)(x)) -#define MKCOLOUR_X0X0(x) (MKCOLOUR(0x01000100) * (uint)(x)) -#define MKCOLOUR_0X0X(x) (MKCOLOUR(0x00010001) * (uint)(x)) -#define MKCOLOUR_0XX0(x) (MKCOLOUR(0x00010100) * (uint)(x)) -#define MKCOLOUR_X00X(x) (MKCOLOUR(0x01000001) * (uint)(x)) - -#define MKCOLOUR_XYXY(x, y) (MKCOLOUR_X0X0(x) | MKCOLOUR_0X0X(y)) -#define MKCOLOUR_XYYX(x, y) (MKCOLOUR_X00X(x) | MKCOLOUR_0XX0(y)) - -#define MKCOLOUR_0000 MKCOLOUR_XXXX(0x00) -#define MKCOLOUR_0FF0 MKCOLOUR_0XX0(0xFF) -#define MKCOLOUR_F00F MKCOLOUR_X00X(0xFF) -#define MKCOLOUR_FFFF MKCOLOUR_XXXX(0xFF) - -#include "table/heightmap_colours.h" - -/** Colour scheme of the smallmap. */ -struct SmallMapColourScheme { - uint32 *height_colours; ///< Cached colours for each level in a map. - const uint32 *height_colours_base; ///< Base table for determining the colours - size_t colour_count; ///< The number of colours. - uint32 default_colour; ///< Default colour of the land. -}; - /** Available colour schemes for height maps. */ -static SmallMapColourScheme _heightmap_schemes[] = { - {NULL, _green_map_heights, lengthof(_green_map_heights), MKCOLOUR_XXXX(0x54)}, ///< Green colour scheme. +SmallMapColourScheme _heightmap_schemes[] = { + {NULL, _green_map_heights, lengthof(_green_map_heights), MKCOLOUR_XXXX(0x5B)}, ///< Green colour scheme. {NULL, _dark_green_map_heights, lengthof(_dark_green_map_heights), MKCOLOUR_XXXX(0x62)}, ///< Dark green colour scheme. {NULL, _violet_map_heights, lengthof(_violet_map_heights), MKCOLOUR_XXXX(0x82)}, ///< Violet colour scheme. }; @@ -351,66 +317,6 @@ void BuildOwnerLegend() _smallmap_company_count = i; } -struct AndOr { - uint32 mor; - uint32 mand; -}; - -static inline uint32 ApplyMask(uint32 colour, const AndOr *mask) -{ - return (colour & mask->mand) | mask->mor; -} - - -/** Colour masks for "Contour" and "Routes" modes. */ -static const AndOr _smallmap_contours_andor[] = { - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_CLEAR - {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F}, // MP_RAILWAY - {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_ROAD - {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_HOUSE - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TREES - {MKCOLOUR_XXXX(PC_LIGHT_BLUE), MKCOLOUR_0000}, // MP_STATION - {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000}, // MP_WATER - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_VOID - {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000}, // MP_INDUSTRY - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE - {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_OBJECT - {MKCOLOUR_0XX0(PC_GREY ), MKCOLOUR_F00F}, -}; - -/** Colour masks for "Vehicles", "Industry", and "Vegetation" modes. */ -static const AndOr _smallmap_vehicles_andor[] = { - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_CLEAR - {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_RAILWAY - {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_ROAD - {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_HOUSE - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TREES - {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, // MP_STATION - {MKCOLOUR_XXXX(PC_WATER ), MKCOLOUR_0000}, // MP_WATER - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_VOID - {MKCOLOUR_XXXX(PC_DARK_RED ), MKCOLOUR_0000}, // MP_INDUSTRY - {MKCOLOUR_0000 , MKCOLOUR_FFFF}, // MP_TUNNELBRIDGE - {MKCOLOUR_0XX0(PC_DARK_RED ), MKCOLOUR_F00F}, // MP_OBJECT - {MKCOLOUR_0XX0(PC_BLACK ), MKCOLOUR_F00F}, -}; - -/** Mapping of tile type to importance of the tile (higher number means more interesting to show). */ -static const byte _tiletype_importance[] = { - 2, // MP_CLEAR - 8, // MP_RAILWAY - 7, // MP_ROAD - 5, // MP_HOUSE - 2, // MP_TREES - 9, // MP_STATION - 2, // MP_WATER - 1, // MP_VOID - 6, // MP_INDUSTRY - 8, // MP_TUNNELBRIDGE - 2, // MP_OBJECT - 0, -}; - - static inline TileType GetEffectiveTileType(TileIndex tile) { TileType t = GetTileType(tile); @@ -522,17 +428,6 @@ static inline uint32 GetSmallMapLinkStatsPixels(TileIndex tile, TileType t) return _smallmap_show_heightmap ? GetSmallMapContoursPixels(tile, t) : GetSmallMapRoutesPixels(tile, t); } -static const uint32 _vegetation_clear_bits[] = { - MKCOLOUR_XXXX(PC_GRASS_LAND), ///< full grass - MKCOLOUR_XXXX(PC_ROUGH_LAND), ///< rough land - MKCOLOUR_XXXX(PC_GREY), ///< rocks - MKCOLOUR_XXXX(PC_FIELDS), ///< fields - MKCOLOUR_XXXX(PC_LIGHT_BLUE), ///< snow - MKCOLOUR_XXXX(PC_ORANGE), ///< desert - MKCOLOUR_XXXX(PC_GRASS_LAND), ///< unused - MKCOLOUR_XXXX(PC_GRASS_LAND), ///< unused -}; - /** * Return the colour a tile would be displayed with in the smallmap in mode "Vegetation". * @@ -592,6 +487,16 @@ static inline uint32 GetSmallMapOwnerPixels(TileIndex tile, TileType t) return MKCOLOUR_XXXX(_legend_land_owners[_company_to_list_pos[o]].colour); } +static void NotifyAllViewports(ViewportMapType map_type) +{ + Window *w; + FOR_ALL_WINDOWS_FROM_BACK(w) { + if (w->viewport != NULL) + if (w->viewport->zoom >= ZOOM_LVL_DRAW_MAP && w->viewport->map_type == map_type) + w->InvalidateData(); + } +} + /** Vehicle colours in #SMT_VEHICLES mode. Indexed by #VehicleTypeByte. */ static const byte _vehicle_type_colours[6] = { PC_RED, PC_YELLOW, PC_LIGHT_BLUE, PC_WHITE, PC_BLACK, PC_RED @@ -1483,6 +1388,7 @@ int SmallMapWindow::GetPositionOnLegend(Point pt) /* If click on industries label, find right industry type and enable/disable it. */ if (click_pos < _smallmap_industry_count) { this->SelectLegendItem(click_pos, _legend_from_industries, _smallmap_industry_count); + NotifyAllViewports(VPMT_INDUSTRY); } } else if (this->map_type == SMT_LINKSTATS) { if (click_pos < _smallmap_cargo_count) { @@ -1492,6 +1398,7 @@ int SmallMapWindow::GetPositionOnLegend(Point pt) } else if (this->map_type == SMT_OWNER) { if (click_pos < _smallmap_company_count) { this->SelectLegendItem(click_pos, _legend_land_owners, _smallmap_company_count, NUM_NO_COMPANY_ENTRIES); + NotifyAllViewports(VPMT_OWNER); } } this->SetDirty(); @@ -1505,9 +1412,11 @@ int SmallMapWindow::GetPositionOnLegend(Point pt) switch (this->map_type) { case SMT_INDUSTRY: tbl = _legend_from_industries; + NotifyAllViewports(VPMT_INDUSTRY); break; case SMT_OWNER: tbl = &(_legend_land_owners[NUM_NO_COMPANY_ENTRIES]); + NotifyAllViewports(VPMT_OWNER); break; case SMT_LINKSTATS: tbl = _legend_linkstats; @@ -1526,6 +1435,7 @@ int SmallMapWindow::GetPositionOnLegend(Point pt) case WID_SM_SHOW_HEIGHT: // Enable/disable showing of heightmap. _smallmap_show_heightmap = !_smallmap_show_heightmap; this->SetWidgetLoweredState(WID_SM_SHOW_HEIGHT, _smallmap_show_heightmap); + NotifyAllViewports(VPMT_INDUSTRY); this->SetDirty(); break; } @@ -1572,7 +1482,7 @@ int SmallMapWindow::GetPositionOnLegend(Point pt) { if (widget != WID_SM_MAP || _scrolling_viewport) return false; - _scrolling_viewport = true; + _scrolling_viewport = this; return true; } diff --git a/src/smallmap_gui.h b/src/smallmap_gui.h index 7a48058417..aee73894c6 100644 --- a/src/smallmap_gui.h +++ b/src/smallmap_gui.h @@ -20,9 +20,34 @@ #include "linkgraph/linkgraph_gui.h" #include "widgets/smallmap_widget.h" +static const int NUM_NO_COMPANY_ENTRIES = 4; ///< Number of entries in the owner legend that are not companies. + +/** Mapping of tile type to importance of the tile (higher number means more interesting to show). */ +static const byte _tiletype_importance[] = { + 2, // MP_CLEAR + 8, // MP_RAILWAY + 7, // MP_ROAD + 5, // MP_HOUSE + 2, // MP_TREES + 9, // MP_STATION + 2, // MP_WATER + 1, // MP_VOID + 6, // MP_INDUSTRY + 8, // MP_TUNNELBRIDGE + 2, // MP_OBJECT + 0, +}; + /* set up the cargos to be displayed in the smallmap's route legend */ void BuildLinkStatsLegend(); +struct TunnelBridgeToMap { + TileIndex from_tile; + TileIndex to_tile; + uint8 colour; +}; +typedef SmallVector TunnelBridgeToMapVector; + void BuildIndustriesLegend(); void ShowSmallMap(); void BuildLandLegend(); @@ -147,7 +172,6 @@ protected: return Company::IsValidID(_local_company) ? 1U << _local_company : 0xffffffff; } - void RebuildColourIndexIfNecessary(); uint GetNumberRowsLegend(uint columns) const; void SelectLegendItem(int click_pos, LegendAndColour *legend, int end_legend_item, int begin_legend_item = 0); void SwitchMapType(SmallMapType map_type); @@ -176,6 +200,8 @@ public: SmallMapWindow(WindowDesc *desc, int window_number); virtual ~SmallMapWindow() { delete this->overlay; } + static void RebuildColourIndexIfNecessary(); + void SmallMapCenterOnCurrentPos(); Point GetStationMiddle(const Station *st) const; diff --git a/src/sound.cpp b/src/sound.cpp index 79dd988bfa..8c23dd2e87 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -189,7 +189,7 @@ static void StartSound(SoundID sound_id, float pan, uint volume) } -static const byte _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87}; +static const byte _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87, 10, 1, 1, 1}; assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_COUNT); static const byte _sound_base_vol[] = { diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 908e7599ab..e9ab6b7538 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -853,6 +853,102 @@ void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator) } } +/** + * Reads a sprite and finds its most representative colour. + * @param sprite Sprite to read. + * @param palette_id Palette for remapping colours. + * @return if blitter supports 32bpp, average Colour.data else a palette index. + */ +uint32 GetSpriteMainColour(SpriteID sprite_id, PaletteID palette_id) +{ + if (!SpriteExists(sprite_id)) return 0; + + SpriteCache *sc = GetSpriteCache(sprite_id); + if (sc->type != ST_NORMAL) return 0; + + const byte * const remap = (palette_id == PAL_NONE ? NULL : GetNonSprite(GB(palette_id, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1); + + uint8 file_slot = sc->file_slot; + size_t file_pos = sc->file_pos; + + SpriteLoader::Sprite sprites[ZOOM_LVL_COUNT]; + SpriteLoader::Sprite *sprite = &sprites[ZOOM_LVL_SHIFT]; + sprites[ZOOM_LVL_NORMAL].type = ST_NORMAL; + SpriteLoaderGrf sprite_loader(sc->container_ver); + uint8 sprite_avail; + const uint8 screen_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth(); + + /* Try to read the 32bpp sprite first. */ + if (screen_depth == 32) { + sprite_avail = sprite_loader.LoadSprite(sprites, file_slot, file_pos, ST_NORMAL, true); + if (sprite_avail & ZOOM_LVL_BASE) { + /* Return the average colour. */ + uint32 r = 0, g = 0, b = 0, cnt = 0; + SpriteLoader::CommonPixel *pixel = sprite->data; + for (uint x = sprite->width * sprite->height; x != 0; x--) { + if (pixel->a) { + if (remap && pixel->m) { + const Colour c = _cur_palette.palette[remap[pixel->m]]; + if (c.a) { + r += c.r; + g += c.g; + b += c.b; + cnt++; + } + } else { + r += pixel->r; + g += pixel->g; + b += pixel->b; + cnt++; + } + } + pixel++; + } + return cnt ? Colour(r / cnt, g / cnt, b / cnt).data : 0; + } + } + + /* No 32bpp, try 8bpp. */ + sprite_avail = sprite_loader.LoadSprite(sprites, file_slot, file_pos, ST_NORMAL, false); + if (sprite_avail & ZOOM_LVL_BASE) { + SpriteLoader::CommonPixel *pixel = sprite->data; + if (screen_depth == 32) { + /* Return the average colour. */ + uint32 r = 0, g = 0, b = 0, cnt = 0; + for (uint x = sprite->width * sprite->height; x != 0; x--) { + if (pixel->a) { + const uint col_index = remap ? remap[pixel->m] : pixel->m; + const Colour c = _cur_palette.palette[col_index]; + r += c.r; + g += c.g; + b += c.b; + cnt++; + } + pixel++; + } + return cnt ? Colour(r / cnt, g / cnt, b / cnt).data : 0; + } else { + /* Return the most used indexed colour. */ + int cnt[256]; + memset(cnt, 0, sizeof(cnt)); + for (uint x = sprite->width * sprite->height; x != 0; x--) { + cnt[remap ? remap[pixel->m] : pixel->m]++; + pixel++; + } + int cnt_max = -1; + uint32 rk = 0; + for (uint x = 1; x < lengthof(cnt); x++) { + if (cnt[x] > cnt_max) { + rk = x; + cnt_max = cnt[x]; + } + } + return rk; + } + } + + return 0; +} static void GfxInitSpriteCache() { diff --git a/src/spritecache.h b/src/spritecache.h index 803bdb32c2..c631e9d5fa 100644 --- a/src/spritecache.h +++ b/src/spritecache.h @@ -57,4 +57,6 @@ bool LoadNextSprite(int load_index, byte file_index, uint file_sprite_id, byte c bool SkipSpriteData(byte type, uint16 num); void DupSprite(SpriteID old_spr, SpriteID new_spr); +uint32 GetSpriteMainColour(SpriteID sprite_id, PaletteID palette_id); + #endif /* SPRITECACHE_H */ diff --git a/src/station.cpp b/src/station.cpp index e0a48ef229..31fa7234a3 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -219,7 +219,7 @@ void Station::MarkTilesDirty(bool cargo_change) const for (h = 0; h < train_station.h; h++) { for (w = 0; w < train_station.w; w++) { if (this->TileBelongsToRailStation(tile)) { - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } tile += TileDiffXY(1, 0); } diff --git a/src/stdafx.h b/src/stdafx.h index d68605c196..de87374f29 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -514,4 +514,19 @@ static inline void free(const void *ptr) #define IGNORE_UNINITIALIZED_WARNING_STOP #endif +/** + * Using _mm_prefetch() with gcc implies the compile flag -msse. + * This is not the case with __builtin_prefetch() so the latter can be used in normal .cpp files. + */ +#if defined(_MSC_VER) + #define INCLUDE_FOR_PREFETCH_NTA + #define PREFETCH_NTA(address) _mm_prefetch((const char *) (address), _MM_HINT_NTA); +#elif defined(__GNUC__) + #define INCLUDE_FOR_PREFETCH_NTA "stdafx.h" + #define PREFETCH_NTA(address) __builtin_prefetch((const void *) (address), 0, 0); +#else + #define INCLUDE_FOR_PREFETCH_NTA "stdafx.h" + #define PREFETCH_NTA(address) +#endif + #endif /* STDAFX_H */ diff --git a/src/table/darklight_colours.h b/src/table/darklight_colours.h new file mode 100644 index 0000000000..09d315e3b1 --- /dev/null +++ b/src/table/darklight_colours.h @@ -0,0 +1,52 @@ +/* $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 darklight_colours.h The colour tables to lighten and darken. + */ + +static const byte _lighten_colour[] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, // 0 + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x0F, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x0F, // 1 + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x0F, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 2 + 0x31, 0x0F, 0x33, 0x34, 0x45, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x0F, 0x3D, 0x3E, 0x3F, 0x40, // 3 + 0x41, 0x42, 0x43, 0x44, 0x45, 0x0F, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x0F, // 4 + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x0F, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x0F, // 5 + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x0F, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x0F, // 6 + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x0F, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x0F, // 7 + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x0F, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x0F, // 8 + 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x0F, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, // 9 + 0xA1, 0x0F, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x0F, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, // A + 0xB1, 0x0F, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0x0F, // B + 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0x0F, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0x0F, 0xCF, 0xD0, // C + 0xD1, 0x0F, 0x0F, 0xD2, 0xD3, 0xD4, 0xD5, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0x0F, // D + 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0x0F, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0x0F, 0xE3, // E + 0xE8, 0xF2, 0xF3, 0xCE, 0x44, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x0F, 0xFC, 0xFD, 0xFF, // F +}; + +static const byte _darken_colour[] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 0 + 0x02, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x02, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, // 1 + 0x02, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x02, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, // 2 + 0x2F, 0x30, 0x42, 0x32, 0x33, 0x02, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x02, 0x3C, 0x3D, 0x3E, // 3 + 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x01, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, // 4 + 0x02, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x02, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, // 5 + 0x02, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x02, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, // 6 + 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0xEF, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, // 7 + 0x02, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x02, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, // 8 + 0x02, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x02, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, // 9 + 0x9F, 0xA0, 0xB4, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0x02, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, // A + 0xAF, 0xB0, 0x02, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, // B + 0x3F, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x02, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xF3, 0xCE, // C + 0xCF, 0xD0, 0xD3, 0xD4, 0xD5, 0xD6, 0xDF, 0x02, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, // D + 0xE1, 0xE2, 0x02, 0xEF, 0xE3, 0xE4, 0xE5, 0xE6, 0xB8, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0x02, // E + 0xB5, 0x02, 0xF1, 0xF2, 0x43, 0x02, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFE, 0xF5, 0x0F // F +}; diff --git a/src/table/settings.ini b/src/table/settings.ini index 3cd29e067c..e01c197355 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2782,6 +2782,104 @@ strhelp = STR_CONFIG_SETTING_SCROLLWHEEL_MULTIPLIER_HELPTEXT strval = STR_JUST_COMMA cat = SC_BASIC +[SDTC_BOOL] +var = gui.viewport_map_scan_surroundings +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SCAN_SURROUNDINGS +proc = RedrawScreen + +[SDTC_BOOL] +var = gui.show_slopes_on_viewport_map +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SLOPES +proc = RedrawScreen + +[SDTC_BOOL] +var = gui.show_bridges_on_map +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_BRIDGES +proc = RedrawScreen + +[SDTC_BOOL] +var = gui.show_tunnels_on_map +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS +proc = RedrawScreen + +[SDTC_VAR] +var = gui.show_vehicle_route +type = SLE_UINT32 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +guiflags = SGF_MULTISTRING +def = 0 +min = 0 +max = 1 +interval = 1 +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_VEHICLE_ROUTE +strval = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_VEHICLE_ROUTE_NO +proc = RedrawScreen + +[SDTC_VAR] +var = gui.dash_level_of_route_lines +type = SLE_UINT32 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +guiflags = SGF_0ISDISABLED +def = 0 +min = 0 +max = 10 +interval = 1 +str = STR_CONFIG_SETTING_VIEWPORT_MAP_DRAW_ROUTE_DASH +strval = STR_CONFIG_SETTING_VIEWPORT_MAP_DRAW_ROUTE_DASH_VALUE +proc = RedrawScreen +cat = SC_EXPERT + +[SDTC_BOOL] +var = gui.use_owner_colour_for_tunnelbridge +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = false +str = STR_CONFIG_SETTING_VIEWPORT_MAP_USE_OWNER_COLOUR_BRIDGE_TUNNEL +proc = RedrawScreen + +[SDTC_VAR] +var = gui.show_scrolling_viewport_on_map +type = SLE_UINT32 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +guiflags = SGF_MULTISTRING +def = 3 +min = 0 +max = 3 +interval = 1 +str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP +strval = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_SCROLLING_VP_NOTHING + +[SDTC_VAR] +var = gui.default_viewport_map_mode +type = SLE_UINT32 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +guiflags = SGF_MULTISTRING +def = 0 +min = 0 +max = 2 +interval = 1 +str = STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE +strval = STR_CONFIG_SETTING_VIEWPORT_MAP_DEFAULT_MODE_VEGETATION + +[SDTC_VAR] +var = gui.action_when_viewport_map_is_dblclicked +type = SLE_UINT32 +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +guiflags = SGF_MULTISTRING +def = 1 +min = 0 +max = 2 +interval = 1 +str = STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK +strval = STR_CONFIG_SETTING_VIEWPORT_MAP_ACTION_DBLCLICK_DO_NOTHING + [SDTC_BOOL] var = gui.pause_on_newgame flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC @@ -3130,6 +3228,13 @@ strhelp = STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT strval = STR_JUST_COMMA proc = RedrawScreen +[SDTC_BOOL] +var = gui.show_vehicle_route_steps +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +str = STR_CONFIG_SETTING_SHOW_VEHICLE_ROUTE_STEPS +proc = RedrawScreen + ; For the dedicated build we'll enable dates in logs by default. [SDTC_BOOL] ifdef = DEDICATED diff --git a/src/table/sprites.h b/src/table/sprites.h index 81d5388f99..665ed5161b 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -163,7 +163,15 @@ 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; +/** Sprites for the route step marker. */ +static const SpriteID SPR_ROUTE_STEP_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; +static const SpriteID SPR_ROUTE_STEP_TOP = SPR_ROUTE_STEP_BASE + 0; +static const SpriteID SPR_ROUTE_STEP_MIDDLE = SPR_ROUTE_STEP_BASE + 1; +static const SpriteID SPR_ROUTE_STEP_BOTTOM = SPR_ROUTE_STEP_BASE + 2; +static const SpriteID SPR_ROUTE_STEP_BOTTOM_SHADOW = SPR_ROUTE_STEP_BASE + 3; +static const SpriteID ROUTE_STEP_SPRITE_COUNT = 4; + +static const SpriteID SPR_SIGNALS_BASE = SPR_ROUTE_STEP_BASE + ROUTE_STEP_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; diff --git a/src/table/tree_land.h b/src/table/tree_land.h index 64757267ec..28cb75ecd6 100644 --- a/src/table/tree_land.h +++ b/src/table/tree_land.h @@ -12,8 +12,11 @@ #ifndef TREE_LAND_H #define TREE_LAND_H +#include "../sprite.h" + static const byte _tree_base_by_landscape[4] = {0, 12, 20, 32}; static const byte _tree_count_by_landscape[4] = {12, 8, 12, 9}; +#define MAX_TREE_COUNT_BY_LANDSCAPE 12 struct TreePos { uint8 x; @@ -227,4 +230,18 @@ static const PalSpriteID _tree_layout_sprite[164 + (79 - 48 + 1)][4] = { { { 0x716, PAL_NONE }, { 0x701, PAL_NONE }, { 0x6fa, PAL_NONE }, { 0x716, PAL_NONE } }, // 31 }; +/** Tree Sprites with their palettes */ +static const PalSpriteID _tree_sprites[] = { + { 1621, PAL_NONE }, { 1587, PAL_NONE }, { 1656, PAL_NONE }, { 1579, PAL_NONE }, + { 1607, PAL_NONE }, { 1593, PAL_NONE }, { 1614, PAL_NONE }, { 1586, PAL_NONE }, + { 1663, PAL_NONE }, { 1677, PAL_NONE }, { 1691, PAL_NONE }, { 1705, PAL_NONE }, + { 1711, PAL_NONE }, { 1746, PAL_NONE }, { 1753, PAL_NONE }, { 1732, PAL_NONE }, + { 1739, PAL_NONE }, { 1718, PAL_NONE }, { 1725, PAL_NONE }, { 1760, PAL_NONE }, + { 1838, PAL_NONE }, { 1844, PAL_NONE }, { 1866, PAL_NONE }, { 1871, PAL_NONE }, + { 1899, PAL_NONE }, { 1935, PAL_NONE }, { 1928, PAL_NONE }, { 1915, PAL_NONE }, + { 1887, PAL_NONE }, { 1908, PAL_NONE }, { 1824, PAL_NONE }, { 1943, PAL_NONE }, + { 1950, PAL_NONE }, { 1957, PALETTE_TO_GREEN }, { 1964, PALETTE_TO_RED }, { 1971, PAL_NONE }, + { 1978, PAL_NONE }, { 1985, PALETTE_TO_RED, }, { 1992, PALETTE_TO_PALE_GREEN }, { 1999, PALETTE_TO_YELLOW }, { 2006, PALETTE_TO_RED } +}; + #endif /* TREE_LAND_H */ diff --git a/src/tilearea_type.h b/src/tilearea_type.h index 45bfb3d4c0..ba3a08f1ad 100644 --- a/src/tilearea_type.h +++ b/src/tilearea_type.h @@ -12,6 +12,8 @@ #ifndef TILEAREA_TYPE_H #define TILEAREA_TYPE_H +#include "stdafx.h" +#include INCLUDE_FOR_PREFETCH_NTA #include "map_func.h" /** Represents the covered area of e.g. a rail station */ @@ -184,6 +186,65 @@ public: } }; +/** Iterator to iterate over a tile area (rectangle) of the map. + * It prefetches tiles once per row. + */ +class OrthogonalPrefetchTileIterator { +private: + TileIndex tile; ///< The current tile we are at. + 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. + +public: + /** + * Construct the iterator. + * @param ta Area, i.e. begin point and width/height of to-be-iterated area. + */ + OrthogonalPrefetchTileIterator(const TileArea &ta) : tile(ta.w == 0 || ta.h == 0 ? INVALID_TILE : ta.tile), w(ta.w), x(ta.w), y(ta.h) + { + PREFETCH_NTA(&_m[ta.tile]); + } + + /** Some compilers really like this. */ + virtual ~OrthogonalPrefetchTileIterator() + { + } + + /** + * Get the tile we are currently at. + * @return The tile we are at, or INVALID_TILE when we're done. + */ + inline operator TileIndex () const + { + return this->tile; + } + + /** + * Move ourselves to the next tile in the rectangle on the map. + */ + inline OrthogonalPrefetchTileIterator& 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; + PREFETCH_NTA(&_m[tile]); + } else { + this->tile = INVALID_TILE; + } + return *this; + } + + virtual OrthogonalPrefetchTileIterator *Clone() const + { + return new OrthogonalPrefetchTileIterator(*this); + } +}; + /** Iterator to iterate over a diagonal area of the map. */ class DiagonalTileIterator : public TileIterator { private: @@ -230,5 +291,6 @@ public: * @param ta The tile area to search over. */ #define TILE_AREA_LOOP(var, ta) for (OrthogonalTileIterator var(ta); var != INVALID_TILE; ++var) +#define TILE_AREA_LOOP_WITH_PREFETCH(var, ta) for (OrthogonalPrefetchTileIterator var(ta); var != INVALID_TILE; ++var) #endif /* TILEAREA_TYPE_H */ diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index ffea9a8642..aec95530e5 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -24,6 +24,7 @@ #include "date_gui.h" #include "vehicle_gui.h" #include "settings_type.h" +#include "viewport_func.h" #include "widgets/timetable_widget.h" @@ -171,6 +172,13 @@ struct TimetableWindow : Window { this->owner = this->vehicle->owner; } + ~TimetableWindow() + { + if (!FocusWindowById(WC_VEHICLE_VIEW, this->window_number)) { + MarkAllRouteStepsDirty(this); + } + } + /** * Build the arrival-departure list for a given vehicle * @param v the vehicle to make the list for @@ -647,6 +655,27 @@ struct TimetableWindow : Window { this->GetWidget(WID_VT_ARRIVAL_DEPARTURE_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : SZSP_NONE); this->GetWidget(WID_VT_EXPECTED_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : 1); } + + virtual void OnFocus(Window *previously_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) { + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) { + MarkAllRoutePathsDirty(this->vehicle); + MarkAllRouteStepsDirty(this); + } + } + + const Vehicle *GetVehicle() + { + return this->vehicle; + } }; static const NWidgetPart _nested_timetable_widgets[] = { diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index de73252177..d0bbc9c46d 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -46,6 +46,7 @@ #include "game/game.hpp" #include "goal_base.h" #include "story_base.h" +#include "plans_func.h" #include "widgets/toolbar_widget.h" @@ -447,6 +448,7 @@ enum MapMenuEntries { MME_SHOW_SIGNLISTS, MME_SHOW_TOWNDIRECTORY, MME_SHOW_INDUSTRYDIRECTORY, + MME_SHOW_PLANS, }; static CallBackFunction ToolbarMapClick(Window *w) @@ -456,6 +458,7 @@ static CallBackFunction ToolbarMapClick(Window *w) *list->Append() = new DropDownListStringItem(STR_MAP_MENU_EXTRA_VIEW_PORT, MME_SHOW_EXTRAVIEWPORTS, false); *list->Append() = new DropDownListStringItem(STR_MAP_MENU_LINGRAPH_LEGEND, MME_SHOW_LINKGRAPH, false); *list->Append() = new DropDownListStringItem(STR_MAP_MENU_SIGN_LIST, MME_SHOW_SIGNLISTS, false); + *list->Append() = new DropDownListStringItem(STR_MAP_MENU_PLAN_LIST, MME_SHOW_PLANS, false); PopupMainToolbMenu(w, WID_TN_SMALL_MAP, list, 0); return CBF_NONE; } @@ -487,6 +490,7 @@ static CallBackFunction MenuClickMap(int index) case MME_SHOW_SIGNLISTS: ShowSignList(); break; case MME_SHOW_TOWNDIRECTORY: ShowTownDirectory(); break; case MME_SHOW_INDUSTRYDIRECTORY: ShowIndustryDirectory(); break; + case MME_SHOW_PLANS: ShowPlansWindow(); break; } return CBF_NONE; } @@ -605,7 +609,7 @@ static CallBackFunction MenuClickCompany(int index) if (_network_server) { DoCommandP(0, 0, _network_own_client_id, CMD_COMPANY_CTRL); } else { - NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company); + NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company, 0); } return CBF_NONE; @@ -1648,6 +1652,7 @@ enum MainToolbarHotkeys { MTHK_EXTRA_VIEWPORT, MTHK_CLIENT_LIST, MTHK_SIGN_LIST, + MTHK_PLAN_LIST, }; /** Main toolbar. */ @@ -1738,6 +1743,7 @@ struct MainToolbarWindow : Window { case MTHK_CLIENT_LIST: if (_networking) ShowClientList(); break; #endif case MTHK_SIGN_LIST: ShowSignList(); break; + case MTHK_PLAN_LIST: ShowPlansWindow(); break; default: return ES_NOT_HANDLED; } return ES_HANDLED; @@ -1843,6 +1849,7 @@ static Hotkey maintoolbar_hotkeys[] = { Hotkey((uint16)0, "client_list", MTHK_CLIENT_LIST), #endif Hotkey((uint16)0, "sign_list", MTHK_SIGN_LIST), + Hotkey('P', "plan_list", MTHK_PLAN_LIST), HOTKEY_LIST_END }; HotkeyList MainToolbarWindow::hotkeys("maintoolbar", maintoolbar_hotkeys); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 6674c905bd..6b53eadfd1 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -330,7 +330,7 @@ static void AnimateTile_Town(TileIndex tile) DeleteAnimatedTile(tile); } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } /** @@ -440,7 +440,7 @@ static void MakeSingleHouseBigger(TileIndex tile) ChangePopulation(Town::GetByTile(tile), HouseSpec::Get(GetHouseType(tile))->population); ResetHouseAge(tile); } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } /** diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index cb9238eb2e..b480c8a10a 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1691,7 +1691,7 @@ void UpdateLevelCrossing(TileIndex tile, bool sound) if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile); } SetCrossingBarred(tile, new_state); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } @@ -1706,7 +1706,7 @@ static inline void MaybeBarCrossingWithSound(TileIndex tile) if (!IsCrossingBarred(tile)) { BarCrossing(tile); if (_settings_client.sound.ambient) SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } @@ -2170,7 +2170,7 @@ static bool CheckTrainStayInDepot(Train *v) } SetDepotReservation(v->tile, true); - if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile, ZOOM_LVL_DRAW_MAP); VehicleServiceInDepot(v); SetWindowClassesDirty(WC_TRAINS_LIST); @@ -2212,8 +2212,8 @@ static void ClearPathReservation(const Train *v, TileIndex tile, Trackdir track_ SetTunnelBridgeReservation(end, false); if (_settings_client.gui.show_track_reservation) { - MarkTileDirtyByTile(tile); - MarkTileDirtyByTile(end); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + MarkTileDirtyByTile(end, ZOOM_LVL_DRAW_MAP); } } } @@ -2278,7 +2278,7 @@ void FreeTrainTrackReservation(const Train *v, TileIndex origin, Trackdir orig_t } else { /* Turn the signal back to red. */ SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_RED); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) { break; @@ -2553,7 +2553,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, if (res_dest.okay) { /* Got a valid reservation that ends at a safe target, quick exit. */ if (got_reservation != NULL) *got_reservation = true; - if (changed_signal) MarkTileDirtyByTile(tile); + if (changed_signal) MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); TryReserveRailTrack(v->tile, TrackdirToTrack(v->GetVehicleTrackdir())); return best_track; } @@ -2612,7 +2612,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, best_track = FindFirstTrack(res); TryReserveRailTrack(v->tile, TrackdirToTrack(v->GetVehicleTrackdir())); if (got_reservation != NULL) *got_reservation = true; - if (changed_signal) MarkTileDirtyByTile(tile); + if (changed_signal) MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } else { FreeTrainTrackReservation(v); if (mark_stuck) MarkTrainAsStuck(v); @@ -2660,7 +2660,7 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TryReserveRailTrack(v->tile, TrackdirToTrack(v->GetVehicleTrackdir())); - if (changed_signal) MarkTileDirtyByTile(tile); + if (changed_signal) MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); return best_track; } @@ -2713,7 +2713,7 @@ bool TryPathReserve(Train *v, bool mark_as_stuck, bool first_tile_okay) /* If we are in a depot, tentatively reserve the depot. */ if (v->track == TRACK_BIT_DEPOT) { SetDepotReservation(v->tile, true); - if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile, ZOOM_LVL_DRAW_MAP); } DiagDirection exitdir = TrackdirToExitdir(origin.trackdir); diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index 4fcda82d81..e52528a243 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -363,7 +363,7 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (flags & DC_EXEC) { AddTreeCount(tile, 1); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); if (c != NULL) c->tree_limit -= 1 << 16; } /* 2x as expensive to add more trees to an existing tile */ @@ -429,7 +429,7 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Plant full grown trees in scenario editor */ PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); if (c != NULL) c->tree_limit -= 1 << 16; /* When planting rainforest-trees, set tropiczone to rainforest in editor. */ @@ -581,7 +581,7 @@ static void TileLoopTreesDesert(TileIndex tile) case TROPICZONE_DESERT: if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) { SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } break; @@ -630,7 +630,7 @@ static void TileLoopTreesAlps(TileIndex tile) return; } } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } static void TileLoop_Trees(TileIndex tile) @@ -653,7 +653,7 @@ static void TileLoop_Trees(TileIndex tile) uint density = GetTreeDensity(tile); if (density < 3) { SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } } if (GetTreeCounter(tile) < 15) { @@ -746,7 +746,7 @@ static void TileLoop_Trees(TileIndex tile) break; } - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); } void OnTick_Trees() diff --git a/src/tree_gui.cpp b/src/tree_gui.cpp index f21eeaef2e..0fc12cf781 100644 --- a/src/tree_gui.cpp +++ b/src/tree_gui.cpp @@ -29,21 +29,6 @@ void PlaceTreesRandomly(); -/** Tree Sprites with their palettes */ -const PalSpriteID tree_sprites[] = { - { 1621, PAL_NONE }, { 1587, PAL_NONE }, { 1656, PAL_NONE }, { 1579, PAL_NONE }, - { 1607, PAL_NONE }, { 1593, PAL_NONE }, { 1614, PAL_NONE }, { 1586, PAL_NONE }, - { 1663, PAL_NONE }, { 1677, PAL_NONE }, { 1691, PAL_NONE }, { 1705, PAL_NONE }, - { 1711, PAL_NONE }, { 1746, PAL_NONE }, { 1753, PAL_NONE }, { 1732, PAL_NONE }, - { 1739, PAL_NONE }, { 1718, PAL_NONE }, { 1725, PAL_NONE }, { 1760, PAL_NONE }, - { 1838, PAL_NONE }, { 1844, PAL_NONE }, { 1866, PAL_NONE }, { 1871, PAL_NONE }, - { 1899, PAL_NONE }, { 1935, PAL_NONE }, { 1928, PAL_NONE }, { 1915, PAL_NONE }, - { 1887, PAL_NONE }, { 1908, PAL_NONE }, { 1824, PAL_NONE }, { 1943, PAL_NONE }, - { 1950, PAL_NONE }, { 1957, PALETTE_TO_GREEN }, { 1964, PALETTE_TO_RED }, { 1971, PAL_NONE }, - { 1978, PAL_NONE }, { 1985, PALETTE_TO_RED, }, { 1992, PALETTE_TO_PALE_GREEN }, { 1999, PALETTE_TO_YELLOW }, { 2006, PALETTE_TO_RED } -}; - - /** * The build trees window. */ @@ -75,8 +60,8 @@ public: offset.y = 0; for (int i = this->base; i < this->base + this->count; i++) { - if (i >= (int)lengthof(tree_sprites)) return size; - this_size = GetSpriteSize(tree_sprites[i].sprite, &offset); + if (i >= (int)lengthof(_tree_sprites)) return size; + this_size = GetSpriteSize(_tree_sprites[i].sprite, &offset); size.width = max(size.width, 2 * max(this_size.width, -offset.x)); size.height = max(size.height, max(this_size.height, -offset.y)); } @@ -108,7 +93,7 @@ public: int i = this->base + widget - WID_BT_TYPE_11; /* Trees "grow" in the centre on the bottom line of the buttons */ - DrawSprite(tree_sprites[i].sprite, tree_sprites[i].pal, (r.left + r.right) / 2 + WD_FRAMERECT_LEFT, r.bottom - 7); + DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, (r.left + r.right) / 2 + WD_FRAMERECT_LEFT, r.bottom - 7); } virtual void OnClick(Point pt, int widget, int click_count) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 3b570ab273..00f82d1443 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -52,6 +52,7 @@ #include "gamelog.h" #include "linkgraph/linkgraph.h" #include "linkgraph/refresh.h" +#include "blitter/factory.hpp" #include "table/strings.h" @@ -1081,6 +1082,62 @@ void ViewportAddVehicles(DrawPixelInfo *dpi) } } +void ViewportMapDrawVehicles(DrawPixelInfo *dpi) +{ + /* The bounding rectangle */ + const int l = dpi->left; + const int r = dpi->left + dpi->width; + const int t = dpi->top; + const int b = dpi->top + dpi->height; + + /* The hash area to scan */ + int xl, xu, yl, yu; + + if (dpi->width + (70 * ZOOM_LVL_BASE) < (1 << (7 + 6 + ZOOM_LVL_SHIFT))) { + xl = GB(l - (70 * ZOOM_LVL_BASE), 7 + ZOOM_LVL_SHIFT, 6); + xu = GB(r, 7 + ZOOM_LVL_SHIFT, 6); + } else { + /* scan whole hash row */ + xl = 0; + xu = 0x3F; + } + + if (dpi->height + (70 * ZOOM_LVL_BASE) < (1 << (6 + 6 + ZOOM_LVL_SHIFT))) { + yl = GB(t - (70 * ZOOM_LVL_BASE), 6 + ZOOM_LVL_SHIFT, 6) << 6; + yu = GB(b, 6 + ZOOM_LVL_SHIFT, 6) << 6; + } else { + /* scan whole column */ + yl = 0; + yu = 0x3F << 6; + } + + const int w = UnScaleByZoom(dpi->width, dpi->zoom); + const int h = UnScaleByZoom(dpi->height, dpi->zoom); + Blitter *blitter = BlitterFactory::GetCurrentBlitter(); + for (int y = yl;; y = (y + (1 << 6)) & (0x3F << 6)) { + for (int x = xl;; x = (x + 1) & 0x3F) { + const Vehicle *v = _vehicle_viewport_hash[x + y]; // already masked & 0xFFF + + while (v != NULL) { + if (!(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) && (v->type != VEH_EFFECT)) { + Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos); + const int pixel_x = UnScaleByZoomLower(pt.x - dpi->left, dpi->zoom); + if (IsInsideMM(pixel_x, 0, w)) { + const int pixel_y = UnScaleByZoomLower(pt.y - dpi->top, dpi->zoom); + if (IsInsideMM(pixel_y, 0, h)) + blitter->SetPixel(dpi->dst_ptr, pixel_x, pixel_y, PC_WHITE); + } + } + v = v->hash_viewport_next; + } + + if (x == xu) break; + } + + if (y == yu) break; + } +} + /** * Find the vehicle close to the clicked coordinates. * @param vp Viewport clicked in. @@ -1355,7 +1412,7 @@ void VehicleEnterDepot(Vehicle *v) SetWindowClassesDirty(WC_TRAINS_LIST); /* Clear path reservation */ SetDepotReservation(t->tile, false); - if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile); + if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(t->tile, ZOOM_LVL_DRAW_MAP); UpdateSignalsOnSegment(t->tile, INVALID_DIAGDIR, t->owner); t->wait_counter = 0; @@ -1500,7 +1557,8 @@ void Vehicle::UpdateViewport(bool dirty) min(old_coord.left, this->coord.left), min(old_coord.top, this->coord.top), max(old_coord.right, this->coord.right) + 1 * ZOOM_LVL_BASE, - max(old_coord.bottom, this->coord.bottom) + 1 * ZOOM_LVL_BASE + max(old_coord.bottom, this->coord.bottom) + 1 * ZOOM_LVL_BASE, + this->type != VEH_EFFECT ? ZOOM_LVL_END : ZOOM_LVL_DRAW_MAP ); } } diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 9eb6b91219..a06449d62b 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -58,6 +58,7 @@ void ResetVehicleColourMap(); byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoID dest_cargo_type); void ViewportAddVehicles(DrawPixelInfo *dpi); +void ViewportMapDrawVehicles(DrawPixelInfo *dpi); void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical); CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore = NULL); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index d9cb116a1a..23d1f64593 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -605,6 +605,32 @@ struct RefitWindow : public Window { this->SetWidgetDisabledState(WID_VR_REFIT, this->sel[0] < 0); } + ~RefitWindow() + { + if (this->window_number != INVALID_VEHICLE) { + if (!FocusWindowById(WC_VEHICLE_VIEW, this->window_number)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + } + + virtual void OnFocus(Window *previously_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + virtual void OnInit() { if (this->cargo != NULL) { @@ -1614,7 +1640,7 @@ public: case WID_VL_SORT_BY_PULLDOWN:// Select sorting criteria dropdown menu ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), WID_VL_SORT_BY_PULLDOWN, 0, - (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10)); + (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10), 0, DDSF_LOST_FOCUS); return; case WID_VL_LIST: { // Matrix to show vehicles @@ -1875,6 +1901,16 @@ struct VehicleDetailsWindow : Window { this->tab = TDW_TAB_CARGO; } + ~VehicleDetailsWindow() + { + if (this->window_number != INVALID_VEHICLE) { + if (!FocusWindowById(WC_VEHICLE_VIEW, this->window_number)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + } + /** * Some data on this window has become invalid. * @param data Information about the changed data. @@ -2173,7 +2209,7 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICE_INTERVAL_DROPDOWN: { const Vehicle *v = Vehicle::Get(this->window_number); - ShowDropDownMenu(this, _service_interval_dropdown, v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0); + ShowDropDownMenu(this, _service_interval_dropdown, v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0, 0, DDSF_LOST_FOCUS); break; } @@ -2223,6 +2259,22 @@ struct VehicleDetailsWindow : Window { this->vscroll->SetCapacityFromWidget(this, WID_VD_MATRIX); } } + + virtual void OnFocus(Window *previously_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } }; /** Vehicle details window descriptor. */ @@ -2495,12 +2547,30 @@ public: ~VehicleViewWindow() { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number, false); DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false); DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false); DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false); } + virtual void OnFocus(Window *previously_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, previously_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (HasFocusedVehicleChanged(this->window_number, newly_focused_window)) { + if (this->window_number != INVALID_VEHICLE) MarkAllRoutePathsDirty(Vehicle::Get(this->window_number)); + MarkAllRouteStepsDirty(this); + } + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { const Vehicle *v = Vehicle::Get(this->window_number); diff --git a/src/vehicle_gui.h b/src/vehicle_gui.h index 83e098dcd9..3b17a05bc1 100644 --- a/src/vehicle_gui.h +++ b/src/vehicle_gui.h @@ -18,6 +18,7 @@ #include "station_type.h" #include "engine_type.h" #include "company_type.h" +#include "widgets/dropdown_func.h" void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit = false); @@ -101,4 +102,33 @@ Vehicle *CheckClickOnVehicle(const struct ViewPort *vp, int x, int y); void DrawVehicleImage(const Vehicle *v, int left, int right, int y, VehicleID selection, EngineImageType image_type, int skip); +/** + * Tell if the focused window concerns the specified vehicle. + * @param vid Vehicle id to check. + * @param ref_window The window to check against. + * @return True if the focused window is about specified vehicle. + */ +static inline bool HasFocusedVehicleChanged(const VehicleID vid, Window *ref_window) +{ + if (ref_window) { + WindowClass wc = ref_window->window_class; + WindowNumber wn = ref_window->window_number; + + if (wc == WC_DROPDOWN_MENU) GetParentWindowInfo(ref_window, wc, wn); + + switch (wc) { + default: + break; + case WC_VEHICLE_DETAILS: + case WC_VEHICLE_REFIT: + case WC_VEHICLE_ORDERS: + case WC_VEHICLE_TIMETABLE: + case WC_VEHICLE_VIEW: + return ((uint32) wn != vid); + } + } + + return true; +} + #endif /* VEHICLE_GUI_H */ diff --git a/src/viewport.cpp b/src/viewport.cpp index 06c0dbe8ef..55ebf5d793 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -69,6 +69,13 @@ */ #include "stdafx.h" +#include "clear_map.h" +#include "tree_map.h" +#include "industry.h" +#include "smallmap_gui.h" +#include "smallmap_colours.h" +#include "table/tree_land.h" +#include "blitter/32bpp_base.hpp" #include "landscape.h" #include "viewport_func.h" #include "station_base.h" @@ -76,6 +83,8 @@ #include "town.h" #include "signs_base.h" #include "signs_func.h" +#include "plans_base.h" +#include "plans_func.h" #include "vehicle_base.h" #include "vehicle_gui.h" #include "blitter/factory.hpp" @@ -90,6 +99,9 @@ #include "linkgraph/linkgraph_gui.h" #include "viewport_sprite_sorter.h" #include "bridge_map.h" +#include "depot_base.h" +#include "tunnelbridge_map.h" +#include "gui.h" #include @@ -149,6 +161,9 @@ typedef SmallVector StringSpriteToDrawVector; typedef SmallVector ParentSpriteToDrawVector; typedef SmallVector ChildScreenSpriteToDrawVector; +typedef std::list> RankOrderTypeList; +typedef std::map RouteStepsMap; + /** Data structure storing rendering information */ struct ViewportDrawer { DrawPixelInfo dpi; @@ -158,6 +173,7 @@ struct ViewportDrawer { ParentSpriteToDrawVector parent_sprites_to_draw; ParentSpriteToSortVector parent_sprites_to_sort; ///< Parent sprite pointer array used for sorting ChildScreenSpriteToDrawVector child_screen_sprites_to_draw; + TunnelBridgeToMapVector tunnel_bridge_to_map; int *last_child; @@ -169,10 +185,18 @@ struct ViewportDrawer { Point foundation_offset[FOUNDATION_PART_END]; ///< Pixel offset for ground sprites on the foundations. }; -static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom); +static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom); +static DrawPixelInfo _dpi_for_text; static ViewportDrawer _vd; +RouteStepsMap _vp_route_steps; +uint _vp_route_step_width = 0; +uint _vp_route_step_height_top = 0; +uint _vp_route_step_height_middle = 0; +uint _vp_route_step_height_bottom = 0; +SubSprite _vp_route_step_subsprite; + TileHighlightData _thd; static TileInfo *_cur_ti; bool _draw_bounding_boxes = false; @@ -180,6 +204,8 @@ bool _draw_dirty_blocks = false; uint _dirty_block_colour = 0; static VpSpriteSorter _vp_sprite_sorter = NULL; +const byte *_pal2trsp_remap_ptr = NULL; + static Point MapXYZToViewport(const ViewPort *vp, int x, int y, int z) { Point p = RemapCoords(x, y, z); @@ -234,6 +260,8 @@ void InitializeWindowViewport(Window *w, int x, int y, vp->follow_vehicle = (VehicleID)(follow_flags & 0xFFFFF); veh = Vehicle::Get(vp->follow_vehicle); pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos); + MarkAllRoutePathsDirty(veh); + MarkAllRouteStepsDirty(w); } else { uint x = TileX(follow_flags) * TILE_SIZE; uint y = TileY(follow_flags) * TILE_SIZE; @@ -1798,6 +1826,41 @@ static void ViewportDrawBoundingBoxes(const ParentSpriteToSortVector *psd) } } +static void ViewportMapStoreBridgeTunnel(const ViewPort * const vp, const TileIndex tile) +{ + extern LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1]; + extern uint _company_to_list_pos[MAX_COMPANIES]; + + /* No need to bother for hidden things */ + const bool tile_is_tunnel = IsTunnel(tile); + if (tile_is_tunnel) { + if (!_settings_client.gui.show_tunnels_on_map) return; + } else { + if (!_settings_client.gui.show_bridges_on_map) return; + } + const Owner o = GetTileOwner(tile); + if (!_legend_land_owners[_company_to_list_pos[o]].show_on_map) return; + + /* Check if already stored */ + TunnelBridgeToMapVector * const tbtmv = &_vd.tunnel_bridge_to_map; + TunnelBridgeToMap *tbtm = tbtmv->Begin(); + const TunnelBridgeToMap * const tbtm_end = tbtmv->End(); + while (tbtm != tbtm_end) { + if (tile == tbtm->from_tile || tile == tbtm->to_tile) return; + tbtm++; + } + + /* It's a new one, add it to the list */ + tbtm = tbtmv->Append(); + tbtm->from_tile = tile; + tbtm->to_tile = GetOtherTunnelBridgeEnd(tile); + if (vp->map_type == VPMT_OWNER && _settings_client.gui.use_owner_colour_for_tunnelbridge && o < MAX_COMPANIES) { + const uint8 colour = _legend_land_owners[_company_to_list_pos[o]].colour; + tbtm->colour = tile_is_tunnel ? _darken_colour[colour] : _lighten_colour[colour]; + } else + tbtm->colour = tile_is_tunnel ? PC_BLACK : PC_VERY_LIGHT_YELLOW; +} + /** * Draw/colour the blocks that have been redrawn. */ @@ -1857,6 +1920,672 @@ static void ViewportDrawStrings(ZoomLevel zoom, const StringSpriteToDrawVector * } } +static inline Vehicle *GetVehicleFromWindow(Window *w) +{ + if (w) { + WindowClass wc = w->window_class; + WindowNumber wn = w->window_number; + + if (wc == WC_DROPDOWN_MENU) GetParentWindowInfo(w, wc, wn); + + switch (wc) { + case WC_VEHICLE_VIEW: + case WC_VEHICLE_ORDERS: + case WC_VEHICLE_TIMETABLE: + case WC_VEHICLE_DETAILS: + case WC_VEHICLE_REFIT: + if (wn != INVALID_VEHICLE) return Vehicle::Get(wn); + break; + default: + break; + } + } + return NULL; +} + +static inline TileIndex GetLastValidOrderLocation(const Vehicle *veh) +{ + Order *order; + TileIndex tmp, result = INVALID_TILE; + FOR_VEHICLE_ORDERS(veh, order) { + switch (order->GetType()) { + case OT_GOTO_STATION: + case OT_GOTO_WAYPOINT: + case OT_IMPLICIT: + case OT_GOTO_DEPOT: + tmp = order->GetLocation(veh, veh->type == VEH_AIRCRAFT); + if (tmp != INVALID_TILE) result = tmp; + break; + default: + break; + } + } + return result; +} + +static inline Order *GetFinalOrder(const Vehicle *veh, Order *order) +{ + while (order->IsType(OT_CONDITIONAL)) + order = veh->GetOrder(order->GetConditionSkipToOrder()); + return order; +} + +/** Draw the route of a vehicle. */ +static void ViewportMapDrawVehicleRoute(const ViewPort *vp) +{ + Order *order; + const Vehicle *veh = GetVehicleFromWindow(_focused_window); + if (!veh) return; + + switch (_settings_client.gui.show_vehicle_route) { + /* case 0: return; // No */ + case 1: { // Simple + TileIndex from_tile = GetLastValidOrderLocation(veh); + if (from_tile == INVALID_TILE) return; + + DrawPixelInfo *old_dpi = _cur_dpi; + _cur_dpi = &_dpi_for_text; + + FOR_VEHICLE_ORDERS(veh, order) { + const Point from_pt = RemapCoords2(TileX(from_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(from_tile) * TILE_SIZE + TILE_SIZE / 2); + const int from_x = UnScaleByZoom(from_pt.x, vp->zoom); + const int from_y = UnScaleByZoom(from_pt.y, vp->zoom); + + Order *final_order = GetFinalOrder(veh, order); + const TileIndex to_tile = final_order->GetLocation(veh, veh->type == VEH_AIRCRAFT); + if (to_tile == INVALID_TILE) continue; + const Point to_pt = RemapCoords2(TileX(to_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(to_tile) * TILE_SIZE + TILE_SIZE / 2); + const int to_x = UnScaleByZoom(to_pt.x, vp->zoom); + const int to_y = UnScaleByZoom(to_pt.y, vp->zoom); + + int line_width = 3; + if (_settings_client.gui.dash_level_of_route_lines == 0) { + GfxDrawLine(from_x, from_y, to_x, to_y, PC_BLACK, 3, _settings_client.gui.dash_level_of_route_lines); + line_width = 1; + } + GfxDrawLine(from_x, from_y, to_x, to_y, (final_order == order) ? PC_WHITE : PC_YELLOW, line_width, _settings_client.gui.dash_level_of_route_lines); + + const OrderType ot = order->GetType(); + if (ot == OT_GOTO_STATION || ot == OT_GOTO_DEPOT || ot == OT_GOTO_WAYPOINT || ot == OT_IMPLICIT) from_tile = to_tile; + } + + _cur_dpi = old_dpi; + break; + } + } +} + +static inline void DrawRouteStep(const ViewPort * const vp, const TileIndex tile, const RankOrderTypeList list) +{ + if (tile == INVALID_TILE) return; + const Point pt = RemapCoords2(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2); + const int x = UnScaleByZoomLower(pt.x - _vd.dpi.left, _vd.dpi.zoom) - (_vp_route_step_width / 2); + const int char_height = GetCharacterHeight(FS_SMALL) + 1; + const int rsth = _vp_route_step_height_top + (int) list.size() * char_height + _vp_route_step_height_bottom; + const int y = UnScaleByZoomLower(pt.y - _vd.dpi.top, _vd.dpi.zoom) - rsth; + + /* Draw the background. */ + DrawSprite(SPR_ROUTE_STEP_TOP, PAL_NONE, _cur_dpi->left + x, _cur_dpi->top + y); + uint y2 = y + _vp_route_step_height_top; + + for (size_t r = list.size(); r != 0; r--, y2 += char_height) { + DrawSprite(SPR_ROUTE_STEP_MIDDLE, PAL_NONE, _cur_dpi->left + x, _cur_dpi->top + y2, &_vp_route_step_subsprite); + } + + DrawSprite(SPR_ROUTE_STEP_BOTTOM, PAL_NONE, _cur_dpi->left + x, _cur_dpi->top + y2); + SpriteID s = SPR_ROUTE_STEP_BOTTOM_SHADOW; + DrawSprite(SetBit(s, PALETTE_MODIFIER_TRANSPARENT), PALETTE_TO_TRANSPARENT, _cur_dpi->left + x, _cur_dpi->top + y2); + + /* Fill with the data. */ + DrawPixelInfo *old_dpi = _cur_dpi; + y2 = y + _vp_route_step_height_top; + for (RankOrderTypeList::const_iterator cit = list.begin(); cit != list.end(); cit++, y2 += char_height) { + SetDParam(0, cit->first); + switch (cit->second) { + case OT_GOTO_STATION: + SetDParam(1, STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_STATION); + goto draw; + case OT_GOTO_DEPOT: + SetDParam(1, STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_DEPOT); + goto draw; + case OT_GOTO_WAYPOINT: + SetDParam(1, STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_WAYPOINT); + goto draw; + case OT_IMPLICIT: { + SetDParam(1, STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP_IMPLICIT); +draw: + /* Write order's info */ + _cur_dpi = &_dpi_for_text; + DrawString(_dpi_for_text.left + x, _dpi_for_text.left + x + _vp_route_step_width - 1, _dpi_for_text.top + y2, STR_VIEWPORT_SHOW_VEHICLE_ROUTE_STEP, TC_FROMSTRING, SA_CENTER, false, FS_SMALL); + break; + } + default: break; // OT_NOTHING OT_LOADING OT_LEAVESTATION OT_DUMMY OT_CONDITIONAL + } + } + _cur_dpi = old_dpi; +} + +static bool ViewportPrepareVehicleRouteSteps(const Vehicle * const veh) +{ + if (!veh) return false; + + if (_vp_route_steps.size() == 0) { + /* Prepare data. */ + Order *order; + int order_rank = 0; + FOR_VEHICLE_ORDERS(veh, order) { + const TileIndex tile = order->GetLocation(veh, veh->type == VEH_AIRCRAFT); + order_rank++; + if (tile == INVALID_TILE) continue; + _vp_route_steps[tile].push_back(std::pair(order_rank, order->GetType())); + } + } + + return true; +} + +/** Draw the route steps of a vehicle. */ +static void ViewportDrawVehicleRouteSteps(const ViewPort * const vp) +{ + const Vehicle * const veh = GetVehicleFromWindow(_focused_window); + if (veh && ViewportPrepareVehicleRouteSteps(veh)) { + for (RouteStepsMap::const_iterator cit = _vp_route_steps.begin(); cit != _vp_route_steps.end(); cit++) { + DrawRouteStep(vp, cit->first, cit->second); + } + } +} + +void ViewportDrawPlans(const ViewPort *vp) +{ + DrawPixelInfo *old_dpi = _cur_dpi; + _cur_dpi = &_dpi_for_text; + + Plan *p; + FOR_ALL_PLANS(p) { + if (!p->IsVisible()) continue; + for (PlanLineVector::iterator it = p->lines.begin(); it != p->lines.end(); it++) { + PlanLine *pl = *it; + if (!pl->visible) continue; + for (uint i = 1; i < pl->tiles.size(); i++) { + const TileIndex from_tile = pl->tiles[i-1]; + const Point from_pt = RemapCoords2(TileX(from_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(from_tile) * TILE_SIZE + TILE_SIZE / 2); + const int from_x = UnScaleByZoom(from_pt.x, vp->zoom); + const int from_y = UnScaleByZoom(from_pt.y, vp->zoom); + + const TileIndex to_tile = pl->tiles[i]; + const Point to_pt = RemapCoords2(TileX(to_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(to_tile) * TILE_SIZE + TILE_SIZE / 2); + const int to_x = UnScaleByZoom(to_pt.x, vp->zoom); + const int to_y = UnScaleByZoom(to_pt.y, vp->zoom); + + GfxDrawLine(from_x, from_y, to_x, to_y, PC_BLACK, 3); + if (pl->focused) { + GfxDrawLine(from_x, from_y, to_x, to_y, PC_RED, 1); + } else { + GfxDrawLine(from_x, from_y, to_x, to_y, PC_WHITE, 1); + } + } + } + } + + if (_current_plan && _current_plan->temp_line->tiles.size() > 1) { + for (uint i = 1; i < _current_plan->temp_line->tiles.size(); i++) { + const TileIndex from_tile = _current_plan->temp_line->tiles[i-1]; + const Point from_pt = RemapCoords2(TileX(from_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(from_tile) * TILE_SIZE + TILE_SIZE / 2); + const int from_x = UnScaleByZoom(from_pt.x, vp->zoom); + const int from_y = UnScaleByZoom(from_pt.y, vp->zoom); + + const TileIndex to_tile = _current_plan->temp_line->tiles[i]; + const Point to_pt = RemapCoords2(TileX(to_tile) * TILE_SIZE + TILE_SIZE / 2, TileY(to_tile) * TILE_SIZE + TILE_SIZE / 2); + const int to_x = UnScaleByZoom(to_pt.x, vp->zoom); + const int to_y = UnScaleByZoom(to_pt.y, vp->zoom); + + GfxDrawLine(from_x, from_y, to_x, to_y, PC_WHITE, 3, 1); + } + } + + _cur_dpi = old_dpi; +} + +#define SLOPIFY_COLOUR(tile, height, vF, vW, vS, vE, vN, action) { \ + if (show_slope) { \ + const Slope slope = GetTileSlope((tile), (height)); \ + switch (slope) { \ + case SLOPE_FLAT: \ + case SLOPE_ELEVATED: \ + action (vF); break; \ + default: { \ + switch (slope & SLOPE_EW) { \ + case SLOPE_W: action (vW); break; \ + case SLOPE_E: action (vE); break; \ + default: action (slope & SLOPE_S) ? (vS) : (vN); break; \ + } \ + break; \ + } \ + } \ + } else { \ + action (vF); \ + } \ +} +#define RETURN_SLOPIFIED_COLOUR(tile, height, colour, colour_light, colour_dark) SLOPIFY_COLOUR(tile, height, colour, colour_light, colour_dark, colour_dark, colour_light, return) +#define ASSIGN_SLOPIFIED_COLOUR(tile, height, colour, colour_light, colour_dark, to_var) SLOPIFY_COLOUR(tile, height, colour, colour_light, colour_dark, colour_dark, colour_light, to_var =) +#define GET_SLOPE_INDEX(slope_index) SLOPIFY_COLOUR(tile, NULL, 0, 1, 2, 3, 4, slope_index =) + +#define COL8TO32(x) _cur_palette.palette[x].data +#define COLOUR_FROM_INDEX(x) ((const uint8 *)&(x))[colour_index] +#define IS32(x) (is_32bpp ? COL8TO32(x) : (x)) + +/* Variables containing Colour if 32bpp or palette index if 8bpp. */ +uint32 _vp_map_vegetation_clear_colours[16][6][8]; ///< [Slope][ClearGround][Multi (see LoadClearGroundMainColours())] +uint32 _vp_map_vegetation_tree_colours[5][MAX_TREE_COUNT_BY_LANDSCAPE]; ///< [TreeGround][max of _tree_count_by_landscape] +uint32 _vp_map_water_colour[5]; ///< [Slope] + +static inline uint ViewportMapGetColourIndexMulti(const TileIndex tile, const ClearGround cg) +{ + switch (cg) { + case CLEAR_GRASS: + case CLEAR_SNOW: + case CLEAR_DESERT: + return GetClearDensity(tile); + case CLEAR_ROUGH: + return GB(TileX(tile) ^ TileY(tile), 4, 3); + case CLEAR_ROCKS: + return TileHash(TileX(tile), TileY(tile)) & 1; + case CLEAR_FIELDS: + return GetFieldType(tile) & 7; + default: NOT_REACHED(); + } +} + +static const ClearGround _treeground_to_clearground[5] = { + CLEAR_GRASS, // TREE_GROUND_GRASS + CLEAR_ROUGH, // TREE_GROUND_ROUGH + CLEAR_SNOW, // TREE_GROUND_SNOW_DESERT, make it +1 if _settings_game.game_creation.landscape == LT_TROPIC + CLEAR_GRASS, // TREE_GROUND_SHORE + CLEAR_SNOW, // TREE_GROUND_ROUGH_SNOW, make it +1 if _settings_game.game_creation.landscape == LT_TROPIC +}; + +template +static inline uint32 ViewportMapGetColourVegetation(const TileIndex tile, TileType t, const uint colour_index) +{ + uint32 colour; + switch (t) { + case MP_CLEAR: { + Slope slope = show_slope ? (Slope) (GetTileSlope(tile, NULL) & 15) : SLOPE_FLAT; + uint multi; + ClearGround cg = GetClearGround(tile); + if (cg == CLEAR_FIELDS && colour_index & 1) { + cg = CLEAR_GRASS; + multi = 1; + } else multi = ViewportMapGetColourIndexMulti(tile, cg); + return _vp_map_vegetation_clear_colours[slope][cg][multi]; + } + + case MP_INDUSTRY: + colour = IsTileForestIndustry(tile) ? (colour_index & 1 ? PC_GREEN : 0x7B) : GREY_SCALE(3); + break; + + case MP_TREES: { + const TreeGround tg = GetTreeGround(tile); + const uint td = GetTreeDensity(tile); + if (IsTransparencySet(TO_TREES)) { + ClearGround cg = _treeground_to_clearground[tg]; + if (cg == CLEAR_SNOW && _settings_game.game_creation.landscape == LT_TROPIC) cg = CLEAR_DESERT; + Slope slope = show_slope ? (Slope) (GetTileSlope(tile, NULL) & 15) : SLOPE_FLAT; + uint32 ground_colour = _vp_map_vegetation_clear_colours[slope][cg][td]; + + if (IsInvisibilitySet(TO_TREES)) { + /* Like ground. */ + return ground_colour; + } + + /* Take ground and make it darker. */ + if (is_32bpp) { + return Blitter_32bppBase::MakeTransparent(ground_colour, 192, 256).data; + } else { + /* 8bpp transparent snow trees give blue. Definitely don't want that. Prefer grey. */ + if (cg == CLEAR_SNOW && td > 1) return GREY_SCALE(13 - GetTreeCount(tile)); + return _pal2trsp_remap_ptr[ground_colour]; + } + } else { + if (tg == TREE_GROUND_SNOW_DESERT || tg == TREE_GROUND_ROUGH_SNOW) { + return _vp_map_vegetation_clear_colours[colour_index][_settings_game.game_creation.landscape == LT_TROPIC ? CLEAR_DESERT : CLEAR_SNOW][td]; + } else { + const uint rnd = min(GetTreeCount(tile) ^ (((tile & 3) ^ (TileY(tile) & 3)) * td), MAX_TREE_COUNT_BY_LANDSCAPE - 1); + return _vp_map_vegetation_tree_colours[tg][rnd]; + } + } + } + + case MP_WATER: + if (is_32bpp) { + uint slope_index = 0; + if (GetWaterTileType(tile) != WATER_TILE_COAST) GET_SLOPE_INDEX(slope_index); + return _vp_map_water_colour[slope_index]; + } + /* FALL THROUGH */ + + default: + colour = ApplyMask(MKCOLOUR_XXXX(GREY_SCALE(3)), &_smallmap_vehicles_andor[t]); + colour = COLOUR_FROM_INDEX(colour); + break; + } + + if (is_32bpp) { + return COL8TO32(colour); + } else { + if (show_slope) ASSIGN_SLOPIFIED_COLOUR(tile, NULL, colour, _lighten_colour[colour], _darken_colour[colour], colour); + return colour; + } +} + +template +static inline uint32 ViewportMapGetColourIndustries(const TileIndex tile, const TileType t, const uint colour_index) +{ + extern LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1]; + extern uint _industry_to_list_pos[NUM_INDUSTRYTYPES]; + extern bool _smallmap_show_heightmap; + + TileType t2 = t; + if (t == MP_INDUSTRY) { + /* If industry is allowed to be seen, use its colour on the map. */ + const IndustryType it = Industry::GetByTile(tile)->type; + if (_legend_from_industries[_industry_to_list_pos[it]].show_on_map) + return IS32(GetIndustrySpec(it)->map_colour); + /* Otherwise, return the colour which will make it disappear. */ + t2 = IsTileOnWater(tile) ? MP_WATER : MP_CLEAR; + } + + if (is_32bpp && t2 == MP_WATER) { + uint slope_index = 0; + if (t != MP_INDUSTRY && GetWaterTileType(tile) != WATER_TILE_COAST) GET_SLOPE_INDEX(slope_index); ///< Ignore industry on water not shown on map. + return _vp_map_water_colour[slope_index]; + } + + const int h = TileHeight(tile); + const SmallMapColourScheme * const cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour]; + const uint32 colours = ApplyMask(_smallmap_show_heightmap ? cs->height_colours[h] : cs->default_colour, &_smallmap_vehicles_andor[t2]); + uint32 colour = COLOUR_FROM_INDEX(colours); + + if (show_slope) ASSIGN_SLOPIFIED_COLOUR(tile, NULL, colour, _lighten_colour[colour], _darken_colour[colour], colour); + + return IS32(colour); +} + +template +static inline uint32 ViewportMapGetColourOwner(const TileIndex tile, TileType t, const uint colour_index) +{ + extern LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1]; + extern uint _company_to_list_pos[MAX_COMPANIES]; + + switch (t) { + case MP_INDUSTRY: return IS32(PC_DARK_GREY); + case MP_HOUSE: return IS32(colour_index & 1 ? PC_DARK_RED : GREY_SCALE(3)); + default: break; + } + + const Owner o = GetTileOwner(tile); + if ((o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) || o == OWNER_NONE || o == OWNER_WATER) { + if (t == MP_WATER) { + if (is_32bpp) { + uint slope_index = 0; + if (GetWaterTileType(tile) != WATER_TILE_COAST) GET_SLOPE_INDEX(slope_index); + return _vp_map_water_colour[slope_index]; + } else { + return PC_WATER; + } + } + + const int h = TileHeight(tile); + uint32 colour = COLOUR_FROM_INDEX(_heightmap_schemes[_settings_client.gui.smallmap_land_colour].height_colours[h]); + if (show_slope) ASSIGN_SLOPIFIED_COLOUR(tile, NULL, colour, _lighten_colour[colour], _darken_colour[colour], colour); + return IS32(colour); + + } else if (o == OWNER_TOWN) { + return IS32(t == MP_ROAD ? (colour_index & 1 ? PC_BLACK : GREY_SCALE(3)) : PC_DARK_RED); + } + + /* Train stations are sometimes hard to spot. + * So we give the player a hint by mixing his colour with black. */ + uint32 colour = _legend_land_owners[_company_to_list_pos[o]].colour; + if (t != MP_STATION) { + if (show_slope) ASSIGN_SLOPIFIED_COLOUR(tile, NULL, colour, _lighten_colour[colour], _darken_colour[colour], colour); + } else { + if (GetStationType(tile) == STATION_RAIL) colour = colour_index & 1 ? colour : PC_BLACK; + } + if (is_32bpp) return COL8TO32(colour); + return colour; +} + +static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * const vp, const TileIndex from_tile, TileType * const tile_type) +{ + if (vp->zoom <= ZOOM_LVL_OUT_128X || !_settings_client.gui.viewport_map_scan_surroundings) { + const TileType ttype = GetTileType(from_tile); + /* Store bridges and tunnels. */ + if (ttype != MP_TUNNELBRIDGE) { + *tile_type = ttype; + } else { + ViewportMapStoreBridgeTunnel(vp, from_tile); + switch (GetTunnelBridgeTransportType(from_tile)) { + case TRANSPORT_RAIL: *tile_type = MP_RAILWAY; break; + case TRANSPORT_ROAD: *tile_type = MP_ROAD; break; + default: *tile_type = MP_WATER; break; + } + } + return from_tile; + } + + const uint8 length = (vp->zoom - ZOOM_LVL_OUT_128X) * 2; + TileArea tile_area = TileArea(from_tile, length, length); + tile_area.ClampToMap(); + + /* Find the most important tile of the area. */ + TileIndex result = from_tile; + uint importance = 0; + TILE_AREA_LOOP_WITH_PREFETCH(tile, tile_area) { + const TileType ttype = GetTileType(tile); + const uint tile_importance = _tiletype_importance[ttype]; + if (tile_importance > importance) { + importance = tile_importance; + result = tile; + } + } + + /* Store bridges and tunnels. */ + *tile_type = GetTileType(result); + if (*tile_type == MP_TUNNELBRIDGE) { + ViewportMapStoreBridgeTunnel(vp, result); + switch (GetTunnelBridgeTransportType(result)) { + case TRANSPORT_RAIL: *tile_type = MP_RAILWAY; break; + case TRANSPORT_ROAD: *tile_type = MP_ROAD; break; + default: *tile_type = MP_WATER; break; + } + } + + return result; +} + +/** Get the colour of a tile, can be 32bpp RGB or 8bpp palette index. */ +template +uint32 ViewportMapGetColour(const ViewPort * const vp, uint x, uint y, const uint colour_index) +{ + if (!(IsInsideMM(x, TILE_SIZE, MapMaxX() * TILE_SIZE - 1) && + IsInsideMM(y, TILE_SIZE, MapMaxY() * TILE_SIZE - 1))) + return 0; + + /* Very approximative but fast way to get the tile when taking Z into account. */ + const TileIndex tile_tmp = TileVirtXY(x, y); + const uint z = TileHeight(tile_tmp) * 4; + TileIndex tile = TileVirtXY(x + z, y + z); + if (tile >= MapSize()) return 0; + if (_settings_game.construction.freeform_edges) { + /* tile_tmp and tile must be from the same side, + * otherwise it's an approximation erroneous case + * that leads to a graphic glitch below south west border. + */ + if (TileX(tile_tmp) > (MapSizeX() - (MapSizeX() / 8))) + if ((TileX(tile_tmp) < (MapSizeX() / 2)) != (TileX(tile) < (MapSizeX() / 2))) + return 0; + } + TileType tile_type = MP_VOID; + tile = ViewportMapGetMostSignificantTileType(vp, tile, &tile_type); + if (tile_type == MP_VOID) return 0; + + /* Return the colours. */ + switch (vp->map_type) { + default: return ViewportMapGetColourOwner(tile, tile_type, colour_index); + case VPMT_INDUSTRY: return ViewportMapGetColourIndustries(tile, tile_type, colour_index); + case VPMT_VEGETATION: return ViewportMapGetColourVegetation(tile, tile_type, colour_index); + } +} + +/* Taken from http://stereopsis.com/doubleblend.html, PixelBlend() is faster than ComposeColourRGBANoCheck() */ +static inline void PixelBlend(uint32 * const d, const uint32 s) +{ + const uint32 a = (s >> 24) + 1; + const uint32 dstrb = *d & 0xFF00FF; + const uint32 dstg = *d & 0xFF00; + const uint32 srcrb = s & 0xFF00FF; + const uint32 srcg = s & 0xFF00; + uint32 drb = srcrb - dstrb; + uint32 dg = srcg - dstg; + drb *= a; + dg *= a; + drb >>= 8; + dg >>= 8; + uint32 rb = (drb + dstrb) & 0xFF00FF; + uint32 g = (dg + dstg) & 0xFF00; + *d = rb | g; +} + +/** Draw the bounding boxes of the scrolling viewport (right-clicked and dragged) */ +static void ViewportMapDrawScrollingViewportBox(const ViewPort * const vp) +{ + if (_scrolling_viewport && _scrolling_viewport->viewport) { + const ViewPort * const vp_scrolling = _scrolling_viewport->viewport; + if (vp_scrolling->zoom < ZOOM_LVL_DRAW_MAP) { + /* Check intersection of dpi and vp_scrolling */ + const int mask = ScaleByZoom(-1, vp->zoom); + const int vp_scrolling_virtual_top_mask = vp_scrolling->virtual_top & mask; + const int vp_scrolling_virtual_bottom_mask = (vp_scrolling->virtual_top + vp_scrolling->virtual_height) & mask; + const int t_inter = max(vp_scrolling_virtual_top_mask, _vd.dpi.top); + const int b_inter = min(vp_scrolling_virtual_bottom_mask, _vd.dpi.top + _vd.dpi.height); + if (t_inter < b_inter) { + const int vp_scrolling_virtual_left_mask = vp_scrolling->virtual_left & mask; + const int vp_scrolling_virtual_right_mask = (vp_scrolling->virtual_left + vp_scrolling->virtual_width) & mask; + const int l_inter = max(vp_scrolling_virtual_left_mask, _vd.dpi.left); + const int r_inter = min(vp_scrolling_virtual_right_mask, _vd.dpi.left + _vd.dpi.width); + if (l_inter < r_inter) { + /* OK, so we can draw something that tells where the scrolling viewport is */ + Blitter * const blitter = BlitterFactory::GetCurrentBlitter(); + const int w_inter = UnScaleByZoom(r_inter - l_inter, vp->zoom); + const int h_inter = UnScaleByZoom(b_inter - t_inter, vp->zoom); + const int x = UnScaleByZoom(l_inter - _vd.dpi.left, vp->zoom); + const int y = UnScaleByZoom(t_inter - _vd.dpi.top, vp->zoom); + + /* If asked, with 32bpp we can do some blending */ + if (_settings_client.gui.show_scrolling_viewport_on_map >= 2 && blitter->GetScreenDepth() == 32) + for (int j = y; j < y + h_inter; j++) + for (int i = x; i < x + w_inter; i++) + PixelBlend((uint32*) blitter->MoveTo(_vd.dpi.dst_ptr, i, j), 0x40FCFCFC); + + /* Draw area contour */ + if (_settings_client.gui.show_scrolling_viewport_on_map != 2) { + if (t_inter == vp_scrolling_virtual_top_mask) + for (int i = x; i < x + w_inter; i += 2) + blitter->SetPixel(_vd.dpi.dst_ptr, i, y, PC_WHITE); + if (b_inter == vp_scrolling_virtual_bottom_mask) + for (int i = x; i < x + w_inter; i += 2) + blitter->SetPixel(_vd.dpi.dst_ptr, i, y + h_inter, PC_WHITE); + if (l_inter == vp_scrolling_virtual_left_mask) + for (int j = y; j < y + h_inter; j += 2) + blitter->SetPixel(_vd.dpi.dst_ptr, x, j, PC_WHITE); + if (r_inter == vp_scrolling_virtual_right_mask) + for (int j = y; j < y + h_inter; j += 2) + blitter->SetPixel(_vd.dpi.dst_ptr, x + w_inter, j, PC_WHITE); + } + } + } + } + } +} + +uint32 *_vp_map_line; ///< Buffer for drawing the map of a viewport. + +/** Draw the map on a viewport. */ +template +void ViewportMapDraw(const ViewPort * const vp) +{ + assert(vp != NULL); + Blitter * const blitter = BlitterFactory::GetCurrentBlitter(); + + /* Index of colour: _green_map_heights[] contains blocks of 4 colours, say ABCD + * For a XXXY colour block to render nicely, follow the model: + * line 1: ABCDABCDABCD + * line 2: CDABCDABCDAB + * line 3: ABCDABCDABCD + * => colour_index_base's second bit is changed every new line. + */ + const int sx = UnScaleByZoomLower(_vd.dpi.left, _vd.dpi.zoom); + const int sy = UnScaleByZoomLower(_vd.dpi.top, _vd.dpi.zoom); + const uint line_padding = 2 * (sy & 1); + uint colour_index_base = (sx + line_padding) & 3; + + const int incr_a = (1 << (vp->zoom - 2)) / ZOOM_LVL_BASE; + const int incr_b = (1 << (vp->zoom - 1)) / ZOOM_LVL_BASE; + const int a = (_vd.dpi.left >> 2) / ZOOM_LVL_BASE; + int b = (_vd.dpi.top >> 1) / ZOOM_LVL_BASE; + const int w = UnScaleByZoom(_vd.dpi.width, vp->zoom); + const int h = UnScaleByZoom(_vd.dpi.height, vp->zoom); + int j = 0; + + /* Render base map. */ + do { // For each line + int i = w; + uint colour_index = colour_index_base; + colour_index_base ^= 2; + uint32 *vp_map_line_ptr32 = _vp_map_line; + uint8 *vp_map_line_ptr8 = (uint8*) _vp_map_line; + int c = b - a; + int d = b + a; + do { // For each pixel of a line + if (is_32bpp) { + *vp_map_line_ptr32 = ViewportMapGetColour(vp, c, d, colour_index); + vp_map_line_ptr32++; + } else { + *vp_map_line_ptr8 = (uint8) ViewportMapGetColour(vp, c, d, colour_index); + vp_map_line_ptr8++; + } + colour_index = ++colour_index & 3; + c -= incr_a; + d += incr_a; + } while (--i); + if (is_32bpp) { + blitter->SetLine32(_vd.dpi.dst_ptr, 0, j, _vp_map_line, w); + } else { + blitter->SetLine(_vd.dpi.dst_ptr, 0, j, (uint8*) _vp_map_line, w); + } + b += incr_b; + } while (++j < h); + + /* Render bridges and tunnels */ + if (_vd.tunnel_bridge_to_map.Length() != 0) { + const TunnelBridgeToMap * const tbtm_end = _vd.tunnel_bridge_to_map.End(); + for (const TunnelBridgeToMap *tbtm = _vd.tunnel_bridge_to_map.Begin(); tbtm != tbtm_end; tbtm++) { // For each bridge or tunnel + TileIndex tile = tbtm->from_tile; + const int z = TileHeight(tile) * 4; + TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); + for (; tile != tbtm->to_tile; tile += delta) { // For each tile + const Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, z); + const int x = UnScaleByZoomLower(pt.x - _vd.dpi.left, _vd.dpi.zoom); + if (IsInsideMM(x, 0, w)) { + const int y = UnScaleByZoomLower(pt.y - _vd.dpi.top, _vd.dpi.zoom); + if (IsInsideMM(y, 0, h)) blitter->SetPixel(_vd.dpi.dst_ptr, x, y, tbtm->colour); + } + } + } + } +} + void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom) { DrawPixelInfo *old_dpi = _cur_dpi; @@ -1879,26 +2608,49 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom _vd.dpi.dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(old_dpi->dst_ptr, x - old_dpi->left, y - old_dpi->top); - ViewportAddLandscape(); - ViewportAddVehicles(&_vd.dpi); + _dpi_for_text = _vd.dpi; + _dpi_for_text.left = UnScaleByZoom(_dpi_for_text.left, _dpi_for_text.zoom); + _dpi_for_text.top = UnScaleByZoom(_dpi_for_text.top, _dpi_for_text.zoom); + _dpi_for_text.width = UnScaleByZoom(_dpi_for_text.width, _dpi_for_text.zoom); + _dpi_for_text.height = UnScaleByZoom(_dpi_for_text.height, _dpi_for_text.zoom); + _dpi_for_text.zoom = ZOOM_LVL_NORMAL; + + if (vp->zoom >= ZOOM_LVL_DRAW_MAP) { + /* Here the rendering is like smallmap. */ + if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) { + if (_settings_client.gui.show_slopes_on_viewport_map) ViewportMapDraw(vp); + else ViewportMapDraw(vp); + } else { + _pal2trsp_remap_ptr = IsTransparencySet(TO_TREES) ? GetNonSprite(GB(PALETTE_TO_TRANSPARENT, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1 : NULL; + if (_settings_client.gui.show_slopes_on_viewport_map) ViewportMapDraw(vp); + else ViewportMapDraw(vp); + } + ViewportMapDrawVehicles(&_vd.dpi); + if (_scrolling_viewport && _settings_client.gui.show_scrolling_viewport_on_map) ViewportMapDrawScrollingViewportBox(vp); + if (vp->zoom < ZOOM_LVL_OUT_256X) ViewportAddTownNames(&_vd.dpi); + } else { + /* Classic rendering. */ + ViewportAddLandscape(); + ViewportAddVehicles(&_vd.dpi); - ViewportAddTownNames(&_vd.dpi); - ViewportAddStationNames(&_vd.dpi); - ViewportAddSigns(&_vd.dpi); + ViewportAddTownNames(&_vd.dpi); + ViewportAddStationNames(&_vd.dpi); + ViewportAddSigns(&_vd.dpi); - DrawTextEffects(&_vd.dpi); + DrawTextEffects(&_vd.dpi); - if (_vd.tile_sprites_to_draw.Length() != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw); + if (_vd.tile_sprites_to_draw.Length() != 0) ViewportDrawTileSprites(&_vd.tile_sprites_to_draw); - ParentSpriteToDraw *psd_end = _vd.parent_sprites_to_draw.End(); - for (ParentSpriteToDraw *it = _vd.parent_sprites_to_draw.Begin(); it != psd_end; it++) { - *_vd.parent_sprites_to_sort.Append() = it; - } + ParentSpriteToDraw *psd_end = _vd.parent_sprites_to_draw.End(); + for (ParentSpriteToDraw *it = _vd.parent_sprites_to_draw.Begin(); it != psd_end; it++) { + *_vd.parent_sprites_to_sort.Append() = it; + } - _vp_sprite_sorter(&_vd.parent_sprites_to_sort); - ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw); + _vp_sprite_sorter(&_vd.parent_sprites_to_sort); + ViewportDrawParentSprites(&_vd.parent_sprites_to_sort, &_vd.child_screen_sprites_to_draw); - if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort); + if (_draw_bounding_boxes) ViewportDrawBoundingBoxes(&_vd.parent_sprites_to_sort); + } if (_draw_dirty_blocks) ViewportDrawDirtyBlocks(); DrawPixelInfo dp = _vd.dpi; @@ -1915,15 +2667,19 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom vp->overlay->Draw(&dp); } + if (_settings_client.gui.show_vehicle_route) ViewportMapDrawVehicleRoute(vp); if (_vd.string_sprites_to_draw.Length() != 0) { /* translate to world coordinates */ dp.left = UnScaleByZoom(_vd.dpi.left, zoom); dp.top = UnScaleByZoom(_vd.dpi.top, zoom); ViewportDrawStrings(zoom, &_vd.string_sprites_to_draw); } + if (_settings_client.gui.show_vehicle_route_steps) ViewportDrawVehicleRouteSteps(vp); + ViewportDrawPlans(vp); _cur_dpi = old_dpi; + _vd.tunnel_bridge_to_map.Clear(); _vd.string_sprites_to_draw.Clear(); _vd.tile_sprites_to_draw.Clear(); _vd.parent_sprites_to_draw.Clear(); @@ -1937,7 +2693,7 @@ void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom */ static void ViewportDrawChk(const ViewPort *vp, int left, int top, int right, int bottom) { - if (ScaleByZoom(bottom - top, vp->zoom) * ScaleByZoom(right - left, vp->zoom) > 180000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE) { + if ((vp->zoom < ZOOM_LVL_DRAW_MAP) && (ScaleByZoom(bottom - top, vp->zoom) * ScaleByZoom(right - left, vp->zoom) > 180000 * ZOOM_LVL_BASE * ZOOM_LVL_BASE)) { if ((bottom - top) > (right - left)) { int t = (top + bottom) >> 1; ViewportDrawChk(vp, left, top, right, t); @@ -2147,6 +2903,22 @@ void UpdateViewportPosition(Window *w) ClampViewportToMap(vp, w->viewport->scrollpos_x, w->viewport->scrollpos_y); + if (_scrolling_viewport == w && _settings_client.gui.show_scrolling_viewport_on_map) { + const int gap = ScaleByZoom(1, ZOOM_LVL_MAX); + + int lr_low = vp->virtual_left; + int lr_hi = w->viewport->scrollpos_x; + if (lr_low > lr_hi) Swap(lr_low, lr_hi); + int right = lr_hi + vp->virtual_width + gap; + + int tb_low = vp->virtual_top; + int tb_hi = w->viewport->scrollpos_y; + if (tb_low > tb_hi) Swap(tb_low, tb_hi); + int bottom = tb_hi + vp->virtual_height + gap; + + MarkAllViewportMapsDirty(lr_low, tb_low, right, bottom); + } + SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y); if (update_overlay) RebuildViewportOverlay(w); } @@ -2161,7 +2933,7 @@ void UpdateViewportPosition(Window *w) * @param bottom Bottom edge of area to repaint * @ingroup dirty */ -static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, int bottom) +static void MarkViewportDirty(const ViewPort * const vp, int left, int top, int right, int bottom) { right -= vp->virtual_left; if (right <= 0) return; @@ -2191,14 +2963,63 @@ static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, * @param top Top edge of area to repaint * @param right Right edge of area to repaint * @param bottom Bottom edge of area to repaint + * @param mark_dirty_if_zoomlevel_is_below To tell if an update is relevant or not (for example, animations in map mode are not) * @ingroup dirty */ -void MarkAllViewportsDirty(int left, int top, int right, int bottom) +void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below) { Window *w; FOR_ALL_WINDOWS_FROM_BACK(w) { - ViewPort *vp = w->viewport; + const ViewPort * const vp = w->viewport; if (vp != NULL) { + assert(vp->width != 0); + if (vp->zoom >= mark_dirty_if_zoomlevel_is_below) continue; + MarkViewportDirty(vp, left, top, right, bottom); + } + } +} + +static void MarkRouteStepDirty(const TileIndex tile, uint order_nr) +{ + assert(tile != INVALID_TILE); + Window *w; + const Point pt = RemapCoords2(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2); + const int char_height = GetCharacterHeight(FS_SMALL) + 1; + FOR_ALL_WINDOWS_FROM_BACK(w) { + const ViewPort * const vp = w->viewport; + if (vp != NULL) { + assert(vp->width != 0); + const int half_width = ScaleByZoom((_vp_route_step_width / 2) + 1, vp->zoom); + const int height = ScaleByZoom(_vp_route_step_height_top + char_height * order_nr + _vp_route_step_height_bottom, vp->zoom); + MarkViewportDirty(vp, pt.x - half_width, pt.y - height, pt.x + half_width, pt.y); + } + } +} + +void MarkAllRouteStepsDirty(Window *vehicle_window) +{ + const Vehicle * const veh = GetVehicleFromWindow(vehicle_window); + ViewportPrepareVehicleRouteSteps(veh); + for (RouteStepsMap::const_iterator cit = _vp_route_steps.begin(); cit != _vp_route_steps.end(); cit++) { + MarkRouteStepDirty(cit->first, (uint) cit->second.size()); + } + _vp_route_steps.clear(); +} + +/** + * Mark all viewports in map mode that display an area as dirty (in need of repaint). + * @param left Left edge of area to repaint + * @param top Top edge of area to repaint + * @param right Right edge of area to repaint + * @param bottom Bottom edge of area to repaint + * @ingroup dirty + */ +void MarkAllViewportMapsDirty(int left, int top, int right, int bottom) +{ + Window *w; + FOR_ALL_WINDOWS_FROM_BACK(w) { + const ViewPort *vp = w->viewport; + if (vp != NULL && vp->zoom >= ZOOM_LVL_DRAW_MAP) { assert(vp->width != 0); MarkViewportDirty(vp, left, top, right, bottom); } @@ -2222,16 +3043,18 @@ void ConstrainAllViewportsZoom() /** * Mark a tile given by its index dirty for repaint. * @param tile The tile to mark dirty. + * @param mark_dirty_if_zoomlevel_is_below To tell if an update is relevant or not (for example, animations in map mode are not). * @ingroup dirty */ -void MarkTileDirtyByTile(TileIndex tile) +void MarkTileDirtyByTile(const TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below) { Point pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, GetTilePixelZ(tile)); MarkAllViewportsDirty( pt.x - 31 * ZOOM_LVL_BASE, pt.y - 122 * ZOOM_LVL_BASE, pt.x - 31 * ZOOM_LVL_BASE + 67 * ZOOM_LVL_BASE, - pt.y - 122 * ZOOM_LVL_BASE + 154 * ZOOM_LVL_BASE + pt.y - 122 * ZOOM_LVL_BASE + 154 * ZOOM_LVL_BASE, + mark_dirty_if_zoomlevel_is_below ); } @@ -2249,6 +3072,60 @@ void MarkTileDirtyByTileOutsideMap(int x, int y) ); } +void MarkTileLineDirty(const TileIndex from_tile, const TileIndex to_tile) +{ + assert(from_tile != INVALID_TILE); + assert(to_tile != INVALID_TILE); + + int x1 = TileX(from_tile); + int y1 = TileY(from_tile); + const int x2 = TileX(to_tile); + const int y2 = TileY(to_tile); + + /* http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Simplification */ + const int dx = abs(x2 - x1); + const int dy = abs(y2 - y1); + const int sx = (x1 < x2) ? 1 : -1; + const int sy = (y1 < y2) ? 1 : -1; + int err = dx - dy; + for (;;) { + MarkTileDirtyByTile(TileXY(x1, y1)); + if (x1 == x2 && y1 == y2) break; + const int e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + x1 += sx; + } + if (e2 < dx) { + err += dx; + y1 += sy; + } + } +} + +void MarkAllRoutePathsDirty(const Vehicle *veh) +{ + Order *order; + TileIndex from_tile; + switch (_settings_client.gui.show_vehicle_route) { + case 0: // No + return; + + case 1: // Simple + from_tile = GetLastValidOrderLocation(veh); + if (from_tile == INVALID_TILE) return; + FOR_VEHICLE_ORDERS(veh, order) { + Order *final_order = GetFinalOrder(veh, order); + const TileIndex to_tile = final_order->GetLocation(veh, veh->type == VEH_AIRCRAFT); + if (to_tile == INVALID_TILE) continue; + MarkTileLineDirty(from_tile, to_tile); + const OrderType ot = order->GetType(); + if (ot == OT_GOTO_STATION || ot == OT_GOTO_DEPOT || ot == OT_GOTO_WAYPOINT || ot == OT_IMPLICIT) from_tile = to_tile; + } + break; + } +} + /** * Marks the selected tiles as dirty. * @@ -2487,9 +3364,31 @@ static void PlaceObject() if (w != NULL) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y)); } +bool HandleViewportDoubleClicked(Window *w, int x, int y) +{ + ViewPort *vp = w->viewport; + if (vp->zoom < ZOOM_LVL_DRAW_MAP) return false; + + switch (_settings_client.gui.action_when_viewport_map_is_dblclicked) { + case 0: // Do nothing + return false; + case 1: // Zoom in main viewport + while (vp->zoom != ZOOM_LVL_VIEWPORT) + ZoomInOrOutToCursorWindow(true, w); + return true; + case 2: // Open an extra viewport + ShowExtraViewPortWindowForTileUnderCursor(); + return true; + default: + return false; + } +} bool HandleViewportClicked(const ViewPort *vp, int x, int y) { + /* No click in smallmap mode except for plan making. */ + if (vp->zoom >= ZOOM_LVL_DRAW_MAP && !(_thd.place_mode == HT_POINT && _thd.select_proc == DDSP_DRAW_PLANLINE)) return true; + const Vehicle *v = CheckClickOnVehicle(vp, x, y); if (_thd.place_mode & HT_VEHICLE) { @@ -2604,8 +3503,8 @@ void SetRedErrorSquare(TileIndex tile) _thd.redsq = tile; if (tile != old) { - if (tile != INVALID_TILE) MarkTileDirtyByTile(tile); - if (old != INVALID_TILE) MarkTileDirtyByTile(old); + if (tile != INVALID_TILE) MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); + if (old != INVALID_TILE) MarkTileDirtyByTile(old, ZOOM_LVL_DRAW_MAP); } } @@ -3519,6 +4418,16 @@ void ResetObjectToPlace() SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); } +ViewportMapType ChangeRenderMode(const ViewPort *vp, bool down) { + ViewportMapType map_type = vp->map_type; + if (vp->zoom < ZOOM_LVL_DRAW_MAP) return map_type; + if (down) { + return (map_type == VPMT_MIN) ? VPMT_MAX : (ViewportMapType) (map_type - 1); + } else { + return (map_type == VPMT_MAX) ? VPMT_MIN : (ViewportMapType) (map_type + 1); + } +} + Point GetViewportStationMiddle(const ViewPort *vp, const Station *st) { int x = TileX(st->xy) * TILE_SIZE; diff --git a/src/viewport_func.h b/src/viewport_func.h index 3e05162d8a..50c7860a78 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 "vehicle_base.h" static const int TILE_HEIGHT_STEP = 50; ///< One Z unit tile height difference is displayed as 50m. @@ -28,7 +29,11 @@ ViewPort *IsPtInWindowViewport(const Window *w, int x, int y); Point GetTileBelowCursor(); void UpdateViewportPosition(Window *w); -void MarkAllViewportsDirty(int left, int top, int right, int bottom); +void MarkAllViewportsDirty(int left, int top, int right, int bottom, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); +void MarkAllViewportMapsDirty(int left, int top, int right, int bottom); +void MarkAllRouteStepsDirty(Window *vehicle_window); +void MarkTileLineDirty(const TileIndex from_tile, const TileIndex to_tile); +void MarkAllRoutePathsDirty(const Vehicle *veh); bool DoZoomInOutWindow(ZoomStateChange how, Window *w); void ZoomInOrOutToCursorWindow(bool in, Window * w); @@ -58,6 +63,7 @@ void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const Vie void StartSpriteCombine(); void EndSpriteCombine(); +bool HandleViewportDoubleClicked(Window *w, int x, int y); bool HandleViewportClicked(const ViewPort *vp, int x, int y); void SetRedErrorSquare(TileIndex tile); void SetTileSelectSize(int w, int h); @@ -77,11 +83,15 @@ void UpdateAllVirtCoords(); extern Point _tile_fract_coords; -void MarkTileDirtyByTile(TileIndex tile); +void MarkTileDirtyByTile(const TileIndex tile, const ZoomLevel mark_dirty_if_zoomlevel_is_below = ZOOM_LVL_END); int GetRowAtTile(int viewport_y, Point tile, bool bridge_correct); void MarkTileDirtyByTileOutsideMap(int x, int y); +ViewportMapType ChangeRenderMode(const ViewPort *vp, bool down); + Point GetViewportStationMiddle(const ViewPort *vp, const Station *st); +void ShowTooltipForTile(Window *w, const TileIndex tile); + #endif /* VIEWPORT_FUNC_H */ diff --git a/src/viewport_gui.cpp b/src/viewport_gui.cpp index 90b0e1468f..b3a3f8b0d5 100644 --- a/src/viewport_gui.cpp +++ b/src/viewport_gui.cpp @@ -16,6 +16,9 @@ #include "strings_func.h" #include "zoom_func.h" #include "window_func.h" +#include "gfx_func.h" +#include "industry.h" +#include "town_map.h" #include "widgets/viewport_widget.h" @@ -78,6 +81,7 @@ public: this->viewport->scrollpos_y = pt.y - this->viewport->virtual_height / 2; this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x; this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y; + this->viewport->map_type = (ViewportMapType) _settings_client.gui.default_viewport_map_mode; } virtual void SetStringParameters(int widget) const @@ -138,10 +142,24 @@ public: virtual void OnMouseWheel(int wheel) { - if (_settings_client.gui.scrollwheel_scrolling == 0) { + if (_ctrl_pressed) { + /* Cycle through the drawing modes */ + this->viewport->map_type = ChangeRenderMode(this->viewport, wheel < 0); + this->SetDirty(); + } else if (_settings_client.gui.scrollwheel_scrolling == 0) { ZoomInOrOutToCursorWindow(wheel < 0, this); } } + + virtual void OnMouseOver(Point pt, int widget) + { + if (pt.x != -1) { + /* Show tooltip with last month production or town name */ + const Point p = GetTileBelowCursor(); + const TileIndex tile = TileVirtXY(p.x, p.y); + if (tile < MapSize()) ShowTooltipForTile(this, tile); + } + } /** * Some data on this window has become invalid. @@ -189,3 +207,38 @@ void ShowExtraViewPortWindowForTileUnderCursor() Point pt = GetTileBelowCursor(); ShowExtraViewPortWindow(pt.x != -1 ? TileVirtXY(pt.x, pt.y) : INVALID_TILE); } + +void ShowTooltipForTile(Window *w, const TileIndex tile) +{ + switch (GetTileType(tile)) { + case MP_ROAD: + if (IsRoadDepot(tile)) return; + /* FALL THROUGH */ + case MP_HOUSE: { + if (HasBit(_display_opt, DO_SHOW_TOWN_NAMES)) return; // No need for a town name tooltip when it is already displayed + SetDParam(0, GetTownIndex(tile)); + GuiShowTooltips(w, STR_TOWN_NAME_TOOLTIP, 0, NULL, TCC_HOVER); + break; + } + case MP_INDUSTRY: { + const Industry *ind = Industry::GetByTile(tile); + const IndustrySpec *indsp = GetIndustrySpec(ind->type); + + StringID str = STR_INDUSTRY_VIEW_TRANSPORTED_TOOLTIP; + uint prm_count = 0; + SetDParam(prm_count++, indsp->name); + for (byte i = 0; i < lengthof(ind->produced_cargo); i++) { + if (ind->produced_cargo[i] != CT_INVALID) { + SetDParam(prm_count++, ind->produced_cargo[i]); + SetDParam(prm_count++, ind->last_month_production[i]); + SetDParam(prm_count++, ToPercent8(ind->last_month_pct_transported[i])); + str++; + } + } + GuiShowTooltips(w, str, 0, NULL, TCC_HOVER); + break; + } + default: + return; + } +} \ No newline at end of file diff --git a/src/viewport_type.h b/src/viewport_type.h index 800c8b4952..7ab7cea55d 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -17,6 +17,17 @@ class LinkGraphOverlay; +enum ViewportMapType { + VPMT_BEGIN = 0, + VPMT_VEGETATION = 0, + VPMT_OWNER, + VPMT_INDUSTRY, + VPMT_END, + + VPMT_MIN = VPMT_VEGETATION, + VPMT_MAX = VPMT_INDUSTRY, +}; + /** * Data structure for viewport, display of a part of the world */ @@ -31,7 +42,9 @@ struct ViewPort { int virtual_width; ///< width << zoom int virtual_height; ///< height << zoom - ZoomLevel zoom; ///< The zoom level of the viewport. + ZoomLevel zoom; ///< The zoom level of the viewport. + ViewportMapType map_type; ///< Rendering type + LinkGraphOverlay *overlay; }; @@ -104,6 +117,7 @@ enum ViewportDragDropSelectionProcess { DDSP_CREATE_RIVER, ///< Create rivers DDSP_PLANT_TREES, ///< Plant trees DDSP_BUILD_BRIDGE, ///< Bridge placement + DDSP_DRAW_PLANLINE, ///< Draw a line for a plan /* Rail specific actions */ DDSP_PLACE_RAIL, ///< Rail placement diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 7b74363be4..5f38566b70 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -1128,7 +1128,7 @@ static void DoDryUp(TileIndex tile) case MP_TREES: SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); break; case MP_WATER: diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index efa48003f2..47049374ef 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -266,7 +266,7 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint MakeRailWaypoint(tile, wp->owner, wp->index, axis, layout_ptr[i], GetRailType(tile)); SetCustomStationSpecIndex(tile, map_spec_index); SetRailStationReservation(tile, reserved); - MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP); DeallocateSpecFromStation(wp, old_specindex); YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis)); diff --git a/src/widgets/dropdown.cpp b/src/widgets/dropdown.cpp index e83772799b..635c405126 100644 --- a/src/widgets/dropdown.cpp +++ b/src/widgets/dropdown.cpp @@ -99,6 +99,7 @@ struct DropdownWindow : Window { int scrolling; ///< If non-zero, auto-scroll the item list (one time). Point position; ///< Position of the topleft corner of the window. Scrollbar *vscroll; + DropDownSyncFocus sync_parent_focus; ///< Call parent window's OnFocus[Lost](). /** * Create a dropdown menu. @@ -113,12 +114,15 @@ struct DropdownWindow : Window { * @param scroll Dropdown menu has a scrollbar. * @param widget Widgets of the dropdown menu window. */ - DropdownWindow(Window *parent, const DropDownList *list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll) + DropdownWindow(Window *parent, const DropDownList *list, int selected, int button, bool instant_close, const Point &position, const Dimension &size, Colours wi_colour, bool scroll, DropDownSyncFocus sync_parent_focus) : Window(&_dropdown_desc) { assert(list->Length() > 0); this->position = position; + this->parent_wnd_class = parent->window_class; + this->parent_wnd_num = parent->window_number; + this->sync_parent_focus = sync_parent_focus; this->CreateNestedTree(); @@ -148,8 +152,6 @@ struct DropdownWindow : Window { this->vscroll->SetCapacity(size.height * (uint16)list->Length() / list_height); this->vscroll->SetCount((uint16)list->Length()); - this->parent_wnd_class = parent->window_class; - this->parent_wnd_num = parent->window_number; this->parent_button = button; this->list = list; this->selected_index = selected; @@ -318,6 +320,22 @@ struct DropdownWindow : Window { } } } + + virtual void OnFocus(Window *previously_focused_window) + { + if (this->sync_parent_focus & DDSF_RECV_FOCUS) { + Window *parent = FindWindowById(this->parent_wnd_class, this->parent_wnd_num); + if (parent) parent->OnFocus(previously_focused_window); + } + } + + virtual void OnFocusLost(Window *newly_focused_window) + { + if (this->sync_parent_focus & DDSF_LOST_FOCUS) { + Window *parent = FindWindowById(this->parent_wnd_class, this->parent_wnd_num); + if (parent) parent->OnFocusLost(newly_focused_window); + } + } }; /** @@ -334,7 +352,7 @@ struct DropdownWindow : Window { * @param instant_close Set to true if releasing mouse button should close the * list regardless of where the cursor is. */ -void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width, bool instant_close) +void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width, bool instant_close, DropDownSyncFocus sync_parent_focus) { DeleteWindowById(WC_DROPDOWN_MENU, 0); @@ -382,7 +400,7 @@ void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int b Point dw_pos = { w->left + (_current_text_dir == TD_RTL ? wi_rect.right + 1 - width : wi_rect.left), top}; Dimension dw_size = {width, height}; - new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll); + new DropdownWindow(w, list, selected, button, instant_close, dw_pos, dw_size, wi_colour, scroll, sync_parent_focus); } /** @@ -398,7 +416,7 @@ void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int b * @param instant_close Set to true if releasing mouse button should close the * list regardless of where the cursor is. */ -void ShowDropDownList(Window *w, const DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close) +void ShowDropDownList(Window *w, const DropDownList *list, int selected, int button, uint width, bool auto_width, bool instant_close, DropDownSyncFocus sync_parent_focus) { /* Our parent's button widget is used to determine where to place the drop * down list window. */ @@ -425,7 +443,7 @@ void ShowDropDownList(Window *w, const DropDownList *list, int selected, int but } } - ShowDropDownListAt(w, list, selected, button, wi_rect, wi_colour, auto_width, instant_close); + ShowDropDownListAt(w, list, selected, button, wi_rect, wi_colour, auto_width, instant_close, sync_parent_focus); } /** @@ -439,7 +457,7 @@ void ShowDropDownList(Window *w, const DropDownList *list, int selected, int but * @param hidden_mask Bitmask for hidden items (items with their bit set are not copied to the dropdown list). * @param width Width of the dropdown menu. If \c 0, use the width of parent widget \a button. */ -void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) +void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width, DropDownSyncFocus sync_parent_focus) { DropDownList *list = new DropDownList(); @@ -455,7 +473,7 @@ void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int butt return; } - ShowDropDownList(w, list, selected, button, width); + ShowDropDownList(w, list, selected, button, width, false, false, sync_parent_focus); } /** @@ -482,3 +500,10 @@ int HideDropDownMenu(Window *pw) return -1; } +void GetParentWindowInfo(Window *w, WindowClass &parent_wc, WindowNumber &parent_wn) +{ + DropdownWindow *dw = dynamic_cast(w); + assert(dw != NULL); + parent_wc = dw->parent_wnd_class; + parent_wn = dw->parent_wnd_num; +} diff --git a/src/widgets/dropdown_func.h b/src/widgets/dropdown_func.h index 4c7e134562..fef28393b0 100644 --- a/src/widgets/dropdown_func.h +++ b/src/widgets/dropdown_func.h @@ -13,11 +13,14 @@ #define WIDGETS_DROPDOWN_FUNC_H #include "../window_gui.h" +#include "dropdown_type.h" /* Show drop down menu containing a fixed list of strings */ -void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width = 0); +void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width = 0, DropDownSyncFocus sync_parent_focus = DDSF_NONE); /* Hide drop down menu of a parent window */ int HideDropDownMenu(Window *pw); +void GetParentWindowInfo(Window *w, WindowClass &parent_wc, WindowNumber &parent_wn); + #endif /* WIDGETS_DROPDOWN_FUNC_H */ diff --git a/src/widgets/dropdown_type.h b/src/widgets/dropdown_type.h index b65d4557bd..0a19f79ffe 100644 --- a/src/widgets/dropdown_type.h +++ b/src/widgets/dropdown_type.h @@ -17,6 +17,13 @@ #include "../core/smallvec_type.hpp" #include "table/strings.h" +enum DropDownSyncFocus { + DDSF_NONE = 0, + DDSF_RECV_FOCUS = 1, + DDSF_LOST_FOCUS = 2, + DDSF_ALL = DDSF_RECV_FOCUS | DDSF_LOST_FOCUS, +}; + /** * Base list item class from which others are derived. If placed in a list it * will appear as a horizontal line in the menu. @@ -85,8 +92,8 @@ public: */ typedef AutoDeleteSmallVector DropDownList; -void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width = false, bool instant_close = false); +void ShowDropDownListAt(Window *w, const DropDownList *list, int selected, int button, Rect wi_rect, Colours wi_colour, bool auto_width = false, bool instant_close = false, DropDownSyncFocus sync_parent_focus = DDSF_NONE); -void ShowDropDownList(Window *w, const DropDownList *list, int selected, int button, uint width = 0, bool auto_width = false, bool instant_close = false); +void ShowDropDownList(Window *w, const DropDownList *list, int selected, int button, uint width = 0, bool auto_width = false, bool instant_close = false, DropDownSyncFocus sync_parent_focus = DDSF_NONE); #endif /* WIDGETS_DROPDOWN_TYPE_H */ diff --git a/src/widgets/plans_widget.h b/src/widgets/plans_widget.h new file mode 100644 index 0000000000..938ac34649 --- /dev/null +++ b/src/widgets/plans_widget.h @@ -0,0 +1,27 @@ +/* $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 plans_widget.h Types related to the plans widgets. */ + +#ifndef WIDGETS_PLANS_WIDGET_H +#define WIDGETS_PLANS_WIDGET_H + +/** Widgets of the #PlansWindow class. */ +enum PlansWidgets { + WID_PLN_CAPTION, ///< Caption of the window. + WID_PLN_LIST, + WID_PLN_SCROLLBAR, + WID_PLN_NEW, + WID_PLN_ADD_LINES, + WID_PLN_VISIBILITY, + WID_PLN_HIDE_ALL, + WID_PLN_DELETE, +}; + +#endif /* WIDGETS_PLANS_WIDGET_H */ diff --git a/src/window.cpp b/src/window.cpp index 398ddf6703..b36849fc91 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -72,7 +72,7 @@ int _scrollbar_start_pos; int _scrollbar_size; byte _scroller_click_timeout = 0; -bool _scrolling_viewport; ///< A viewport is being scrolled with the mouse. +Window *_scrolling_viewport; ///< A viewport is being scrolled with the mouse. bool _mouse_hovering; ///< The mouse is hovering over the same point. SpecialMouseMode _special_mouse_mode; ///< Mode of the mouse. @@ -423,8 +423,8 @@ void SetFocusedWindow(Window *w) _focused_window = w; /* So we can inform it that it lost focus */ - if (old_focused != NULL) old_focused->OnFocusLost(); - if (_focused_window != NULL) _focused_window->OnFocus(); + if (old_focused != NULL) old_focused->OnFocusLost(w); + if (_focused_window != NULL) _focused_window->OnFocus(old_focused); } /** @@ -481,7 +481,7 @@ bool Window::SetFocusedWidget(int widget_index) /** * Called when window looses focus */ -void Window::OnFocusLost() +void Window::OnFocusLost(Window *newly_focused_window) { if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus(); } @@ -1046,7 +1046,7 @@ Window::~Window() /* Make sure we don't try to access this window as the focused window when it doesn't exist anymore. */ if (_focused_window == this) { - this->OnFocusLost(); + this->OnFocusLost(NULL); _focused_window = NULL; } @@ -1376,6 +1376,7 @@ static void BringWindowToFront(Window *w) { RemoveWindowFromZOrdering(w); AddWindowToZOrdering(w); + SetFocusedWindow(w); w->SetDirty(); } @@ -1416,8 +1417,9 @@ void Window::InitializeData(WindowNumber window_number) /* Give focus to the opened window unless a text box * of focused window has focus (so we don't interrupt typing). But if the new - * window has a text box, then take focus anyway. */ - if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL) SetFocusedWindow(this); + * window has a text box, then take focus anyway. + * Do not give the focus while scrolling a viewport (like when the News pops up) */ + if (_scrolling_viewport == NULL && this->window_class != WC_TOOLTIPS && this->window_class != WC_NEWS_WINDOW && this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this); /* Insert the window into the correct location in the z-ordering. */ AddWindowToZOrdering(this); @@ -1800,7 +1802,7 @@ void InitWindowSystem() _focused_window = NULL; _mouseover_last_w = NULL; _last_scroll_window = NULL; - _scrolling_viewport = false; + _scrolling_viewport = NULL; _mouse_hovering = false; NWidgetLeaf::InvalidateDimensionCache(); // Reset cached sizes of several widgets. @@ -2280,8 +2282,8 @@ static void StartWindowDrag(Window *w) _drag_delta.x = w->left - _cursor.pos.x; _drag_delta.y = w->top - _cursor.pos.y; - BringWindowToFront(w); DeleteWindowById(WC_DROPDOWN_MENU, 0); + BringWindowToFront(w); } /** @@ -2298,8 +2300,8 @@ static void StartWindowSizing(Window *w, bool to_left) _drag_delta.x = _cursor.pos.x; _drag_delta.y = _cursor.pos.y; - BringWindowToFront(w); DeleteWindowById(WC_DROPDOWN_MENU, 0); + BringWindowToFront(w); } /** @@ -2360,7 +2362,7 @@ static EventState HandleViewportScroll() { bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); - if (!_scrolling_viewport) return ES_NOT_HANDLED; + if (_scrolling_viewport == NULL) return ES_NOT_HANDLED; /* When we don't have a last scroll window we are starting to scroll. * When the last scroll window and this are not the same we went @@ -2369,7 +2371,7 @@ static EventState HandleViewportScroll() if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) { _cursor.fix_at = false; - _scrolling_viewport = false; + _scrolling_viewport = NULL; _last_scroll_window = NULL; return ES_NOT_HANDLED; } @@ -2542,6 +2544,22 @@ EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode) return ES_HANDLED; } +/** + * Focus a window by its class and window number (if it is open). + * @param cls Window class. + * @param number Number of the window within the window class. + * @return True if a window answered to the criteria. + */ +bool FocusWindowById(WindowClass cls, WindowNumber number) +{ + Window *w = FindWindowById(cls, number); + if (w) { + MaybeBringWindowToFront(w); + return true; + } + return false; +} + /** * Handle keyboard input. * @param keycode Virtual keycode of the key. @@ -2794,19 +2812,21 @@ static void MouseLoop(MouseClick click, int mousewheel) if (scrollwheel_scrolling) click = MC_RIGHT; // we are using the scrollwheel in a viewport, so we emulate right mouse button switch (click) { case MC_DOUBLE_LEFT: + if (HandleViewportDoubleClicked(w, x, y)) break; + /* FALL THROUGH */ case MC_LEFT: DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite); if (!HandleViewportClicked(vp, x, y) && !(w->flags & WF_DISABLE_VP_SCROLL) && _settings_client.gui.left_mouse_btn_scrolling) { - _scrolling_viewport = true; + _scrolling_viewport = w; _cursor.fix_at = false; } break; case MC_RIGHT: if (!(w->flags & WF_DISABLE_VP_SCROLL)) { - _scrolling_viewport = true; + _scrolling_viewport = w; _cursor.fix_at = true; /* clear 2D scrolling caches before we start a 2D scroll */ diff --git a/src/window_func.h b/src/window_func.h index 453b889789..c783d774b7 100644 --- a/src/window_func.h +++ b/src/window_func.h @@ -53,6 +53,8 @@ void SetWindowClassesDirty(WindowClass cls); void DeleteWindowById(WindowClass cls, WindowNumber number, bool force = true); void DeleteWindowByClass(WindowClass cls); +bool FocusWindowById(WindowClass cls, WindowNumber number); + bool EditBoxInGlobalFocus(); Point GetCaretPosition(); diff --git a/src/window_gui.h b/src/window_gui.h index 70b80c6824..682e31c023 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -586,10 +586,11 @@ public: /** * Called when window gains focus + * @param previously_focused_window The window that lost the focus. */ - virtual void OnFocus() {} + virtual void OnFocus(Window *previously_focused_window) {} - virtual void OnFocusLost(); + virtual void OnFocusLost(Window *newly_focused_window); /** * A key has been pressed. @@ -888,7 +889,7 @@ extern int _scrollbar_start_pos; extern int _scrollbar_size; extern byte _scroller_click_timeout; -extern bool _scrolling_viewport; +extern Window *_scrolling_viewport; extern bool _mouse_hovering; /** Mouse modes. */ diff --git a/src/window_type.h b/src/window_type.h index a8a8128f58..e34e55f42b 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -681,6 +681,11 @@ enum WindowClass { */ WC_SAVE_PRESET, + /** + * Plans window. + */ + WC_PLANS, + WC_INVALID = 0xFFFF, ///< Invalid window. }; diff --git a/src/zoom_type.h b/src/zoom_type.h index ec80eaf3b7..6e17a8449a 100644 --- a/src/zoom_type.h +++ b/src/zoom_type.h @@ -27,6 +27,10 @@ enum ZoomLevel { ZOOM_LVL_OUT_8X, ///< Zoomed 8 times out. ZOOM_LVL_OUT_16X, ///< Zoomed 16 times out. ZOOM_LVL_OUT_32X, ///< Zoomed 32 times out. + ZOOM_LVL_OUT_64X, ///< Zoomed 64 times out. + ZOOM_LVL_OUT_128X, ///< Zoomed 128 times out. + ZOOM_LVL_OUT_256X, ///< Zoomed 256 times out. + ZOOM_LVL_OUT_512X, ///< Zoomed 512 times out. ZOOM_LVL_END, ///< End for iteration. ZOOM_LVL_COUNT = ZOOM_LVL_END - ZOOM_LVL_BEGIN, ///< Number of zoom levels. @@ -44,8 +48,10 @@ enum ZoomLevel { ZOOM_LVL_DETAIL = ZOOM_LVL_OUT_8X, ///< All zoomlevels below or equal to this, will result in details on the screen, like road-work, ... - ZOOM_LVL_MIN = ZOOM_LVL_NORMAL, ///< Minimum zoom level. - ZOOM_LVL_MAX = ZOOM_LVL_OUT_32X, ///< Maximum zoom level. + ZOOM_LVL_MIN = ZOOM_LVL_NORMAL, ///< Minimum zoom level. + ZOOM_LVL_MAX = ZOOM_LVL_OUT_512X, ///< Maximum zoom level. + ZOOM_LVL_DRAW_MAP = ZOOM_LVL_OUT_64X, ///< All zoomlevels above or equal to this are rendered with map style + ZOOM_LVL_DRAW_SPR = ZOOM_LVL_DRAW_MAP - 1, ///< All zoomlevels below or equal to this are rendered with sprites }; DECLARE_POSTFIX_INCREMENT(ZoomLevel) From 03b6c41c674fbf14efad10d79a97a1719f26e7b1 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 5 Aug 2015 21:31:17 +0100 Subject: [PATCH 2/3] Add shorthand flag in SlxiSubChunkFlags for XSCF_IGNORABLE_UNKNOWN | XSCF_IGNORABLE_VERSION. --- src/saveload/extended_ver_sl.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 3dffee5a7c..39a03478c2 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -75,6 +75,8 @@ enum SlxiSubChunkFlags { XSCF_IGNORABLE_VERSION = 1 << 1, ///< the loader is free to ignore this without aborting the load if the version is greater than the maximum that can be loaded XSCF_EXTRA_DATA_PRESENT = 1 << 2, ///< extra data field is present, extra data in some sub-chunk/feature specific format, not used for anything yet XSCF_CHUNK_ID_LIST_PRESENT = 1 << 3, ///< chunk ID list field is present, list of chunks which this sub-chunk/feature adds to the save game, this can be used to discard the chunks if the feature is unknown + + XSCF_IGNORABLE_ALL = XSCF_IGNORABLE_UNKNOWN | XSCF_IGNORABLE_VERSION, ///< all "ignorable" flags }; DECLARE_ENUM_AS_BIT_SET(SlxiSubChunkFlags) From 44d3c84a118a72e1a5e6273884e4c4bdcc31ac1e Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 5 Aug 2015 21:33:47 +0100 Subject: [PATCH 3/3] Save/load changes for enhanced viewport patch: plans. --- src/saveload/extended_ver_sl.cpp | 1 + src/saveload/extended_ver_sl.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index be40cad17b..bdb490917e 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,6 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { + { XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 1, 1, "enh_viewport_plans", NULL, NULL, "PLAN,PLLN" }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 39a03478c2..befbf70724 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -21,6 +21,7 @@ */ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use + XSLFI_ENH_VIEWPORT_PLANS, ///< Enhanced viewport patch: plans XSLFI_SIZE, ///< Total count of features, including null feature };