diff --git a/bin/data/innerhighlight.grf b/bin/data/innerhighlight.grf new file mode 100644 index 0000000000..4118f515d1 Binary files /dev/null and b/bin/data/innerhighlight.grf differ diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index c958101718..770a868c00 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -663,6 +663,7 @@ + @@ -748,6 +749,7 @@ + @@ -831,6 +833,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index d83ca2a376..3400671e56 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1218,6 +1218,9 @@ Header Files + + Header Files + Core Source Code @@ -1473,6 +1476,9 @@ GUI Source Code + + GUI Source Code + Widgets @@ -1722,6 +1728,9 @@ Command handlers + + Command handlers + Save/Load handlers diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index a84a122b32..60a4518eaa 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -1926,6 +1926,10 @@ RelativePath=".\..\src\zoom_type.h" > + + + + + + + + + + + + Append() = new DropDownListStringItem(STR_SETTINGS_MENU_SCRIPT_SETTINGS, OME_SCRIPT_SETTINGS, false); *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_NEWGRF_SETTINGS, OME_NEWGRFSETTINGS, false); + *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_ZONING, OME_ZONING, false); *list->Append() = new DropDownListStringItem(STR_SETTINGS_MENU_TRANSPARENCY_OPTIONS, OME_TRANSPARENCIES, false); *list->Append() = new DropDownListItem(-1, false); *list->Append() = new DropDownListCheckedItem(STR_SETTINGS_MENU_TOWN_NAMES_DISPLAYED, OME_SHOW_TOWNNAMES, false, HasBit(_display_opt, DO_SHOW_TOWN_NAMES)); @@ -347,6 +350,7 @@ static CallBackFunction MenuClickSettings(int index) case OME_SETTINGS: ShowGameSettings(); return CBF_NONE; case OME_SCRIPT_SETTINGS: ShowAIConfigWindow(); return CBF_NONE; case OME_NEWGRFSETTINGS: ShowNewGRFSettings(!_networking && _settings_client.gui.UserIsAllowedToChangeNewGRFs(), true, true, &_grfconfig); return CBF_NONE; + case OME_ZONING: ShowZoningToolbar(); break; case OME_TRANSPARENCIES: ShowTransparencyToolbar(); break; case OME_SHOW_TOWNNAMES: ToggleBit(_display_opt, DO_SHOW_TOWN_NAMES); break; diff --git a/src/viewport.cpp b/src/viewport.cpp index a1bb2c81d1..142738be41 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -80,6 +80,7 @@ #include "waypoint_func.h" #include "window_func.h" #include "tilehighlight_func.h" +#include "zoning.h" #include "window_gui.h" #include "linkgraph/linkgraph_gui.h" #include "viewport_sprite_sorter.h" @@ -1200,7 +1201,10 @@ static void ViewportAddLandscape() _vd.last_foundation_child[1] = NULL; _tile_type_procs[tile_type]->draw_tile_proc(&tile_info); - if (tile_info.tile != INVALID_TILE) DrawTileSelection(&tile_info); + if (tile_info.tile != INVALID_TILE) { + DrawTileSelection(&tile_info); + DrawTileZoning(&tile_info); + } } } } diff --git a/src/window_type.h b/src/window_type.h index 2b6b13595d..ba46330a27 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -668,6 +668,7 @@ enum WindowClass { * - 0 = #SpriteAlignerWidgets */ WC_SPRITE_ALIGNER, + WC_ZONING_TOOLBAR, /** * Linkgraph legend; %Window numbers: diff --git a/src/zoning.h b/src/zoning.h new file mode 100644 index 0000000000..bfea293342 --- /dev/null +++ b/src/zoning.h @@ -0,0 +1,48 @@ +/* $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 zoning.h */ + +#ifndef ZONING_H +#define ZONING_H + +#include "tile_cmd.h" +#include "company_type.h" + +/** + * Zoning evaluation modes + */ +enum ZoningEvaluationMode { + ZEM_NOTHING = 0, ///< No zoning action selected + ZEM_AUTHORITY, ///< Check the local authority's opinion. + ZEM_CAN_BUILD, ///< Check wither or not the player can build. + ZEM_STA_CATCH, ///< Check catchment area for stations + ZEM_BUL_UNSER, ///< Check for unserved buildings + ZEM_IND_UNSER, ///< Check for unserved industries +}; + +/** + * Global Zoning state structure + */ +struct Zoning { + ZoningEvaluationMode inner; + ZoningEvaluationMode outer; +}; + +extern Zoning _zoning; + +SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode); + +int TileZoningEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode); + +void DrawTileZoning(const TileInfo *ti); + +void ShowZoningToolbar(); + +#endif /* ZONING_H */ diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp new file mode 100644 index 0000000000..5f9240d76c --- /dev/null +++ b/src/zoning_cmd.cpp @@ -0,0 +1,332 @@ +/* $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 zoning_cmd.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "station_type.h" +#include "station_base.h" +#include "industry.h" +#include "gfx_func.h" +#include "viewport_func.h" +#include "map_func.h" +#include "company_func.h" +#include "town_map.h" +#include "table/sprites.h" +#include "station_func.h" +#include "station_map.h" +#include "town.h" +#include "zoning.h" + +Zoning _zoning; +static const SpriteID ZONING_INVALID_SPRITE_ID = UINT_MAX; + +/** + * Draw the zoning sprites. + * + * @param SpriteID image + * the image + * @param SpriteID colour + * the colour of the zoning + * @param TileInfo ti + * the tile + */ +void DrawZoningSprites(SpriteID image, SpriteID colour, const TileInfo *ti) +{ + if (colour != ZONING_INVALID_SPRITE_ID) { + AddSortableSpriteToDraw(image + ti->tileh, colour, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + } +} + +/** + * Detect whether this area is within the acceptance of any station. + * + * @param TileArea area + * the area to search by + * @param Owner owner + * the owner of the stations which we need to match again + * @param StationFacility facility_mask + * one or more facilities in the mask must be present for a station to be used + * @return true if a station is found + */ +bool IsAreaWithinAcceptanceZoneOfStation(TileArea area, Owner owner, StationFacility facility_mask) +{ + int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED); + + StationFinder morestations(TileArea(TileXY(TileX(area.tile) - (catchment / 2), TileY(area.tile) - (catchment / 2)), + TileX(area.tile) + area.w + catchment, TileY(area.tile) + area.h + catchment)); + + for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) { + const Station *st = *st_iter; + if (st->owner != owner || !(st->facilities & facility_mask)) continue; + Rect rect = st->GetCatchmentRect(); + return TileArea(TileXY(rect.left, rect.top), TileXY(rect.right, rect.bottom)).Intersects(area); + } + + return false; +} + +/** + * Detect whether this tile is within the acceptance of any station. + * + * @param TileIndex tile + * the tile to search by + * @param Owner owner + * the owner of the stations + * @param StationFacility facility_mask + * one or more facilities in the mask must be present for a station to be used + * @return true if a station is found + */ +bool IsTileWithinAcceptanceZoneOfStation(TileIndex tile, Owner owner, StationFacility facility_mask) +{ + int catchment = _settings_game.station.station_spread + (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED); + + StationFinder morestations(TileArea(TileXY(TileX(tile) - (catchment / 2), TileY(tile) - (catchment / 2)), + catchment, catchment)); + + for (Station * const *st_iter = morestations.GetStations()->Begin(); st_iter != morestations.GetStations()->End(); ++st_iter) { + const Station *st = *st_iter; + if (st->owner != owner || !(st->facilities & facility_mask)) continue; + Rect rect = st->GetCatchmentRect(); + if ((uint)rect.left <= TileX(tile) && TileX(tile) <= (uint)rect.right + && (uint)rect.top <= TileY(tile) && TileY(tile) <= (uint)rect.bottom) { + return true; + } + } + + return false; +} + +/** + * Check whether the player can build in tile. + * + * @param TileIndex tile + * @param Owner owner + * @return red if they cannot + */ +SpriteID TileZoneCheckBuildEvaluation(TileIndex tile, Owner owner) +{ + /* Let's first check for the obvious things you cannot build on */ + switch (GetTileType(tile)) { + case MP_INDUSTRY: + case MP_OBJECT: + case MP_STATION: + case MP_HOUSE: + case MP_TUNNELBRIDGE: + return SPR_ZONING_INNER_HIGHLIGHT_RED; + + /* There are only two things you can own (or some else + * can own) that you can still build on. i.e. roads and + * railways. + * @todo + * Add something more intelligent, check what tool the + * user is currently using (and if none, assume some + * standards), then check it against if owned by some- + * one else (e.g. railway on someone else's road). + * While that being said, it should also check if it + * is not possible to build railway/road on someone + * else's/your own road/railway (e.g. the railway track + * is curved or a cross). + */ + case MP_ROAD: + case MP_RAILWAY: + if (GetTileOwner(tile) != owner) { + return SPR_ZONING_INNER_HIGHLIGHT_RED; + } else { + return ZONING_INVALID_SPRITE_ID; + } + + default: + return ZONING_INVALID_SPRITE_ID; + } +} + +/** + * Check the opinion of the local authority in the tile. + * + * @param TileIndex tile + * @param Owner owner + * @return black if no opinion, orange if bad, + * light blue if good or invalid if no town + */ +SpriteID TileZoneCheckOpinionEvaluation(TileIndex tile, Owner owner) +{ + int opinion = 0; // 0: no town, 1: no opinion, 2: bad, 3: good + Town *town = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); + + if (town != NULL) { + if (HasBit(town->have_ratings, owner)) { + opinion = (town->ratings[owner] > 0) ? 3 : 2; + } else { + opinion = 1; + } + } + + switch (opinion) { + case 1: return SPR_ZONING_INNER_HIGHLIGHT_BLACK; // no opinion + case 2: return SPR_ZONING_INNER_HIGHLIGHT_ORANGE; // bad + case 3: return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE; // good + default: return ZONING_INVALID_SPRITE_ID; // no town + } +} + +/** + * Detect whether the tile is within the catchment zone of a station. + * + * @param TileIndex tile + * @param Owner owner + * @return black if within, light blue if only in acceptance zone + * and nothing if no nearby station. + */ +SpriteID TileZoneCheckStationCatchmentEvaluation(TileIndex tile, Owner owner) +{ + // Never on a station. + if (IsTileType(tile, MP_STATION)) { + return ZONING_INVALID_SPRITE_ID; + } + + // For provided goods + StationFinder stations(TileArea(tile, 1, 1)); + + for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) { + const Station *st = *st_iter; + if (st->owner == owner) { + return SPR_ZONING_INNER_HIGHLIGHT_BLACK; + } + } + + // For accepted goods + if (IsTileWithinAcceptanceZoneOfStation(tile, owner, ~FACIL_NONE)) { + return SPR_ZONING_INNER_HIGHLIGHT_LIGHT_BLUE; + } + + return ZONING_INVALID_SPRITE_ID; +} + +/** + * Detect whether a building is unserved by a station of owner. + * + * @param TileIndex tile + * @param Owner owner + * @return red if unserved, orange if only accepting, nothing if served or not + * a building + */ +SpriteID TileZoneCheckUnservedBuildingsEvaluation(TileIndex tile, Owner owner) +{ + if (!IsTileType(tile, MP_HOUSE)) { + return ZONING_INVALID_SPRITE_ID; + } + + CargoArray dat; + + memset(&dat, 0, sizeof(dat)); + AddAcceptedCargo(tile, dat, NULL); + if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) { + // nothing is accepted, so now test if cargo is produced + AddProducedCargo(tile, dat); + if (dat[CT_MAIL] + dat[CT_PASSENGERS] == 0) { + // total is still 0, so give up + return ZONING_INVALID_SPRITE_ID; + } + } + + StationFinder stations(TileArea(tile, 1, 1)); + + for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) { + const Station *st = *st_iter; + if (st->owner == owner) { + return ZONING_INVALID_SPRITE_ID; + } + } + + // For accepted goods + if (IsTileWithinAcceptanceZoneOfStation(tile, owner, ~FACIL_NONE)) { + return SPR_ZONING_INNER_HIGHLIGHT_ORANGE; + } + + return SPR_ZONING_INNER_HIGHLIGHT_RED; +} + +/** + * Detect whether an industry is unserved by a station of owner. + * + * @param TileIndex tile + * @param Owner owner + * @return red if unserved, orange if only accepting, nothing if served or not + * a building + */ +SpriteID TileZoneCheckUnservedIndustriesEvaluation(TileIndex tile, Owner owner) +{ + if (IsTileType(tile, MP_INDUSTRY)) { + Industry *ind = Industry::GetByTile(tile); + StationFinder stations(ind->location); + + for (Station * const *st_iter = stations.GetStations()->Begin(); st_iter != stations.GetStations()->End(); ++st_iter) { + const Station *st = *st_iter; + if (st->owner == owner && st->facilities & (~FACIL_BUS_STOP)) { + return ZONING_INVALID_SPRITE_ID; + } + } + + // For accepted goods + if (IsAreaWithinAcceptanceZoneOfStation(ind->location, owner, ~FACIL_BUS_STOP)) { + return SPR_ZONING_INNER_HIGHLIGHT_ORANGE; + } + + return SPR_ZONING_INNER_HIGHLIGHT_RED; + } + + return ZONING_INVALID_SPRITE_ID; +} + +/** + * General evaluation function; calls all the other functions depending on + * evaluation mode. + * + * @param TileIndex tile + * Tile to be evaluated. + * @param Owner owner + * The current player + * @param ZoningEvaluationMode ev_mode + * The current evaluation mode. + * @return The colour returned by the evaluation functions (none if no ev_mode). + */ +SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, ZoningEvaluationMode ev_mode) +{ + switch (ev_mode) { + case ZEM_CAN_BUILD: return TileZoneCheckBuildEvaluation(tile, owner); + case ZEM_AUTHORITY: return TileZoneCheckOpinionEvaluation(tile, owner); + case ZEM_STA_CATCH: return TileZoneCheckStationCatchmentEvaluation(tile, owner); + case ZEM_BUL_UNSER: return TileZoneCheckUnservedBuildingsEvaluation(tile, owner); + case ZEM_IND_UNSER: return TileZoneCheckUnservedIndustriesEvaluation(tile, owner); + default: return ZONING_INVALID_SPRITE_ID; + } +} + +/** + * Draw the the zoning on the tile. + * + * @param TileInfo ti + * the tile to draw on. + */ +void DrawTileZoning(const TileInfo *ti) +{ + if (IsTileType(ti->tile, MP_VOID) || _game_mode != GM_NORMAL) { + return; + } + + if (_zoning.outer != ZEM_NOTHING) { + DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti); + } + + if (_zoning.inner != ZEM_NOTHING) { + DrawZoningSprites(SPR_ZONING_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.inner), ti); + } +} diff --git a/src/zoning_gui.cpp b/src/zoning_gui.cpp new file mode 100644 index 0000000000..74be21b8f6 --- /dev/null +++ b/src/zoning_gui.cpp @@ -0,0 +1,181 @@ +/* $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 zoning_gui.cpp */ + +#include "stdafx.h" +#include "openttd.h" +#include "widgets/dropdown_func.h" +#include "widget_type.h" +#include "window_func.h" +#include "gui.h" +#include "viewport_func.h" +#include "sound_func.h" +#include "table/sprites.h" +#include "table/strings.h" +#include "strings_func.h" +#include "gfx_func.h" +#include "core/geometry_func.hpp" +#include "core/random_func.hpp" +#include "zoning.h" + +enum ZoningToolbarWidgets { + ZTW_OUTER = 4, + ZTW_OUTER_DROPDOWN, + ZTW_INNER, + ZTW_INNER_DROPDOWN, + ZTW_CAPTION +}; + +static const StringID _zone_type_strings[] = { + STR_ZONING_NO_ZONING, + STR_ZONING_AUTHORITY, + STR_ZONING_CAN_BUILD, + STR_ZONING_STA_CATCH, + STR_ZONING_BUL_UNSER, + STR_ZONING_IND_UNSER, + INVALID_STRING_ID +}; + +static const ZoningEvaluationMode _zone_type_modes[] = { + ZEM_NOTHING, + ZEM_AUTHORITY, + ZEM_CAN_BUILD, + ZEM_STA_CATCH, + ZEM_BUL_UNSER, + ZEM_IND_UNSER, +}; + +static ZoningEvaluationMode DropDownIndexToZoningEvaluationMode(int index) +{ + if (index < 0 || index >= (int) lengthof(_zone_type_modes)) { + return ZEM_NOTHING; + } + return _zone_type_modes[index]; +} + +static int ZoningEvaluationModeToDropDownIndex(ZoningEvaluationMode ev_mode) +{ + for (int i = 0; i < (int) lengthof(_zone_type_modes); i++) { + if (_zone_type_modes[i] == ev_mode) return i; + } + NOT_REACHED(); +} + +struct ZoningWindow : public Window { + + ZoningWindow(WindowDesc *desc, int window_number) + : Window(desc) + { + this->InitNested(window_number); + this->InvalidateData(); + } + + virtual void OnPaint() + { + this->DrawWidgets(); + } + + virtual void OnClick(Point pt, int widget, int click_count) + { + switch (widget) { + case ZTW_OUTER_DROPDOWN: + ShowDropDownMenu(this, _zone_type_strings, ZoningEvaluationModeToDropDownIndex(_zoning.outer), ZTW_OUTER_DROPDOWN, 0, 0); + break; + + case ZTW_INNER_DROPDOWN: + ShowDropDownMenu(this, _zone_type_strings, ZoningEvaluationModeToDropDownIndex(_zoning.inner), ZTW_INNER_DROPDOWN, 0, 0); + break; + } + } + + virtual void OnDropdownSelect(int widget, int index) + { + switch(widget) { + case ZTW_OUTER_DROPDOWN: + _zoning.outer = DropDownIndexToZoningEvaluationMode(index); + break; + + case ZTW_INNER_DROPDOWN: + _zoning.inner = DropDownIndexToZoningEvaluationMode(index); + break; + } + this->InvalidateData(); + MarkWholeScreenDirty(); + } + + virtual void SetStringParameters(int widget) const + { + switch (widget) { + case ZTW_OUTER_DROPDOWN: + SetDParam(0, _zone_type_strings[ZoningEvaluationModeToDropDownIndex(_zoning.outer)]); + break; + + case ZTW_INNER_DROPDOWN: + SetDParam(0, _zone_type_strings[ZoningEvaluationModeToDropDownIndex(_zoning.inner)]); + break; + } + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) + { + const StringID *strs = NULL; + switch (widget) { + case ZTW_OUTER_DROPDOWN: + case ZTW_INNER_DROPDOWN: + strs = _zone_type_strings; + break; + } + if (strs != NULL) { + while (*strs != INVALID_STRING_ID) { + *size = maxdim(*size, GetStringBoundingBox(*strs++)); + } + } + size->width += padding.width; + size->height = FONT_HEIGHT_NORMAL + WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM; + } +}; + +static const NWidgetPart _nested_zoning_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_CAPTION, COLOUR_GREY, ZTW_CAPTION), SetDataTip(STR_ZONING_TOOLBAR, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_GREY), + NWidget(WWT_STICKYBOX, COLOUR_GREY), + EndContainer(), + + NWidget(WWT_PANEL, COLOUR_GREY), + NWidget(NWID_HORIZONTAL, COLOUR_GREY), SetPIP(10, 3, 10), + NWidget(NWID_VERTICAL, COLOUR_GREY), SetPadding(5, 0, 5, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_ZONING_OUTER, STR_NULL), SetResize(1, 0), SetPadding(1, 6, 1, 6), + NWidget(WWT_TEXT, COLOUR_GREY, ZTW_OUTER), + NWidget(WWT_TEXT, COLOUR_GREY), SetDataTip(STR_ZONING_INNER, STR_NULL), SetResize(1, 0), SetPadding(1, 6, 1, 6), + NWidget(WWT_TEXT, COLOUR_GREY, ZTW_INNER), + EndContainer(), + NWidget(NWID_VERTICAL, COLOUR_GREY), SetPadding(5, 0, 5, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, ZTW_OUTER_DROPDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY), + NWidget(WWT_DROPDOWN, COLOUR_GREY, ZTW_INNER_DROPDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY), + EndContainer(), + EndContainer(), + EndContainer() +}; + +static WindowDesc _zoning_desc ( + WDP_CENTER, "zoning_gui", 0, 0, + WC_ZONING_TOOLBAR, WC_NONE, + 0, + _nested_zoning_widgets, lengthof(_nested_zoning_widgets) +); + +void ShowZoningToolbar() +{ + AllocateWindowDescFront(&_zoning_desc, 0); +}