4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file town_gui.cpp GUI for towns. */
18 #include "map/zoneheight.h"
19 #include "map/slope.h"
20 #include "map/bridge.h"
21 #include "viewport_func.h"
24 #include "command_func.h"
25 #include "company_func.h"
26 #include "company_base.h"
27 #include "company_gui.h"
28 #include "network/network.h"
30 #include "strings_func.h"
31 #include "sound_func.h"
32 #include "tilehighlight_func.h"
33 #include "sortlist_type.h"
35 #include "landscape.h"
36 #include "querystring_gui.h"
37 #include "window_func.h"
38 #include "townname_func.h"
39 #include "core/geometry_func.hpp"
40 #include "core/random_func.hpp"
41 #include "core/smallvec_type.hpp"
43 #include "widgets/dropdown_func.h"
44 #include "economy_func.h"
45 #include "newgrf_config.h"
46 #include "newgrf_house.h"
47 #include "newgrf_cargo.h"
48 #include "date_func.h"
49 #include "zoom_func.h"
50 #include "slope_func.h"
52 #include "widgets/town_widget.h"
54 #include "table/strings.h"
56 typedef GUIList
<const Town
*> GUITownList
;
58 static const NWidgetPart _nested_town_authority_widgets
[] = {
59 NWidget(NWID_HORIZONTAL
),
60 NWidget(WWT_CLOSEBOX
, COLOUR_BROWN
),
61 NWidget(WWT_CAPTION
, COLOUR_BROWN
, WID_TA_CAPTION
), SetDataTip(STR_LOCAL_AUTHORITY_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
62 NWidget(WWT_SHADEBOX
, COLOUR_BROWN
),
63 NWidget(WWT_DEFSIZEBOX
, COLOUR_BROWN
),
64 NWidget(WWT_STICKYBOX
, COLOUR_BROWN
),
66 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TA_RATING_INFO
), SetMinimalSize(317, 92), SetResize(1, 1), EndContainer(),
67 NWidget(NWID_HORIZONTAL
),
68 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TA_COMMAND_LIST
), SetMinimalSize(305, 52), SetResize(1, 0), SetDataTip(0x0, STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP
), SetScrollbar(WID_TA_SCROLLBAR
), EndContainer(),
69 NWidget(NWID_VSCROLLBAR
, COLOUR_BROWN
, WID_TA_SCROLLBAR
),
71 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TA_ACTION_INFO
), SetMinimalSize(317, 52), SetResize(1, 0), EndContainer(),
72 NWidget(NWID_HORIZONTAL
),
73 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TA_EXECUTE
), SetMinimalSize(317, 12), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_LOCAL_AUTHORITY_DO_IT_BUTTON
, STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP
),
74 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
78 /** Town authority window. */
79 struct TownAuthorityWindow
: Window
{
81 Town
*town
; ///< Town being displayed.
82 int sel_index
; ///< Currently selected town action, \c 0 to \c TACT_COUNT-1, \c -1 means no action selected.
84 uint displayed_actions_on_previous_painting
; ///< Actions that were available on the previous call to OnPaint()
87 * Get the position of the Nth set bit.
89 * If there is no Nth bit set return -1
91 * @param bits The value to search in
92 * @param n The Nth set bit from which we want to know the position
93 * @return The position of the Nth set bit
95 static int GetNthSetBit(uint32 bits
, int n
)
99 FOR_EACH_SET_BIT(i
, bits
) {
108 TownAuthorityWindow (const WindowDesc
*desc
, WindowNumber window_number
) :
109 Window (desc
), town (NULL
), sel_index(-1), vscroll (NULL
),
110 displayed_actions_on_previous_painting (0)
112 this->town
= Town::Get(window_number
);
113 this->InitNested(window_number
);
114 this->vscroll
= this->GetScrollbar(WID_TA_SCROLLBAR
);
115 this->vscroll
->SetCapacity((this->GetWidget
<NWidgetBase
>(WID_TA_COMMAND_LIST
)->current_y
- WD_FRAMERECT_TOP
- WD_FRAMERECT_BOTTOM
) / FONT_HEIGHT_NORMAL
);
118 void OnPaint (BlitArea
*dpi
) OVERRIDE
121 uint buttons
= GetMaskOfTownActions(&numact
, _local_company
, this->town
);
122 if (buttons
!= displayed_actions_on_previous_painting
) this->SetDirty();
123 displayed_actions_on_previous_painting
= buttons
;
125 this->vscroll
->SetCount(numact
+ 1);
127 if (this->sel_index
!= -1 && !HasBit(buttons
, this->sel_index
)) {
128 this->sel_index
= -1;
131 this->SetWidgetDisabledState(WID_TA_EXECUTE
, this->sel_index
== -1);
133 this->DrawWidgets (dpi
);
134 if (!this->IsShaded()) this->DrawRatings (dpi
);
137 /** Draw the contents of the ratings panel. May request a resize of the window if the contents does not fit. */
138 void DrawRatings (BlitArea
*dpi
)
140 NWidgetBase
*nwid
= this->GetWidget
<NWidgetBase
>(WID_TA_RATING_INFO
);
141 uint left
= nwid
->pos_x
+ WD_FRAMERECT_LEFT
;
142 uint right
= nwid
->pos_x
+ nwid
->current_x
- 1 - WD_FRAMERECT_RIGHT
;
144 uint y
= nwid
->pos_y
+ WD_FRAMERECT_TOP
;
146 DrawString (dpi
, left
, right
, y
, STR_LOCAL_AUTHORITY_COMPANY_RATINGS
);
147 y
+= FONT_HEIGHT_NORMAL
;
149 Dimension icon_size
= GetSpriteSize(SPR_COMPANY_ICON
);
150 int icon_width
= icon_size
.width
;
151 int icon_y_offset
= (FONT_HEIGHT_NORMAL
- icon_size
.height
) / 2;
153 Dimension exclusive_size
= GetSpriteSize(SPR_EXCLUSIVE_TRANSPORT
);
154 int exclusive_width
= exclusive_size
.width
;
155 int exclusive_y_offset
= (FONT_HEIGHT_NORMAL
- exclusive_size
.height
) / 2;
157 bool rtl
= _current_text_dir
== TD_RTL
;
158 uint text_left
= left
+ (rtl
? 0 : icon_width
+ exclusive_width
+ 4);
159 uint text_right
= right
- (rtl
? icon_width
+ exclusive_width
+ 4 : 0);
160 uint icon_left
= rtl
? right
- icon_width
: left
;
161 uint exclusive_left
= rtl
? right
- icon_width
- exclusive_width
- 2 : left
+ icon_width
+ 2;
163 /* Draw list of companies */
165 FOR_ALL_COMPANIES(c
) {
166 if ((HasBit(this->town
->have_ratings
, c
->index
) || this->town
->exclusivity
== c
->index
)) {
167 DrawCompanyIcon (dpi
, c
->index
, icon_left
, y
+ icon_y_offset
);
169 SetDParam(0, c
->index
);
170 SetDParam(1, c
->index
);
172 int r
= this->town
->ratings
[c
->index
];
174 (str
= STR_CARGO_RATING_APPALLING
, r
<= RATING_APPALLING
) || // Apalling
175 (str
++, r
<= RATING_VERYPOOR
) || // Very Poor
176 (str
++, r
<= RATING_POOR
) || // Poor
177 (str
++, r
<= RATING_MEDIOCRE
) || // Mediocore
178 (str
++, r
<= RATING_GOOD
) || // Good
179 (str
++, r
<= RATING_VERYGOOD
) || // Very Good
180 (str
++, r
<= RATING_EXCELLENT
) || // Excellent
181 (str
++, true); // Outstanding
184 if (this->town
->exclusivity
== c
->index
) {
185 DrawSprite (dpi
, SPR_EXCLUSIVE_TRANSPORT
, COMPANY_SPRITE_COLOUR(c
->index
), exclusive_left
, y
+ exclusive_y_offset
);
188 DrawString (dpi
, text_left
, text_right
, y
, STR_LOCAL_AUTHORITY_COMPANY_RATING
);
189 y
+= FONT_HEIGHT_NORMAL
;
193 y
= y
+ WD_FRAMERECT_BOTTOM
- nwid
->pos_y
; // Compute needed size of the widget.
194 if (y
> nwid
->current_y
) {
195 /* If the company list is too big to fit, mark ourself dirty and draw again. */
196 ResizeWindow(this, 0, y
- nwid
->current_y
, false);
200 virtual void SetStringParameters(int widget
) const
202 if (widget
== WID_TA_CAPTION
) SetDParam(0, this->window_number
);
205 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
208 case WID_TA_ACTION_INFO
:
209 if (this->sel_index
!= -1) {
210 SetDParam(0, _price
[PR_TOWN_ACTION
] * _town_action_costs
[this->sel_index
] >> 8);
211 DrawStringMultiLine (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, r
.top
+ WD_FRAMERECT_TOP
, r
.bottom
- WD_FRAMERECT_BOTTOM
,
212 STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING
+ this->sel_index
);
215 case WID_TA_COMMAND_LIST
: {
217 uint buttons
= GetMaskOfTownActions(&numact
, _local_company
, this->town
);
218 int y
= r
.top
+ WD_FRAMERECT_TOP
;
219 int pos
= this->vscroll
->GetPosition();
222 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, y
, STR_LOCAL_AUTHORITY_ACTIONS_TITLE
);
223 y
+= FONT_HEIGHT_NORMAL
;
226 for (int i
= 0; buttons
; i
++, buttons
>>= 1) {
227 if (pos
<= -5) break; ///< Draw only the 5 fitting lines
229 if ((buttons
& 1) && --pos
< 0) {
230 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, y
,
231 STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN
+ i
, this->sel_index
== i
? TC_WHITE
: TC_ORANGE
);
232 y
+= FONT_HEIGHT_NORMAL
;
240 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
243 case WID_TA_ACTION_INFO
: {
244 assert(size
->width
> padding
.width
&& size
->height
> padding
.height
);
245 const uint width
= size
->width
- (WD_FRAMERECT_LEFT
+ WD_FRAMERECT_RIGHT
);
247 for (int i
= 0; i
< TACT_COUNT
; i
++) {
248 SetDParam(0, _price
[PR_TOWN_ACTION
] * _town_action_costs
[i
] >> 8);
249 uint h
= GetStringHeight (STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING
+ i
, width
);
250 hmax
= max (hmax
, h
);
252 size
->height
= max (size
->height
, hmax
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
);
256 case WID_TA_COMMAND_LIST
:
257 size
->height
= WD_FRAMERECT_TOP
+ 5 * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_BOTTOM
;
258 size
->width
= GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTIONS_TITLE
).width
;
259 for (uint i
= 0; i
< TACT_COUNT
; i
++ ) {
260 size
->width
= max(size
->width
, GetStringBoundingBox(STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN
+ i
).width
);
262 size
->width
+= WD_FRAMERECT_LEFT
+ WD_FRAMERECT_RIGHT
;
265 case WID_TA_RATING_INFO
:
266 resize
->height
= FONT_HEIGHT_NORMAL
;
267 size
->height
= WD_FRAMERECT_TOP
+ 9 * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_BOTTOM
;
272 virtual void OnClick(Point pt
, int widget
, int click_count
)
275 case WID_TA_COMMAND_LIST
: {
276 int y
= this->GetRowFromWidget(pt
.y
, WID_TA_COMMAND_LIST
, 1, FONT_HEIGHT_NORMAL
);
277 if (!IsInsideMM(y
, 0, 5)) return;
279 y
= GetNthSetBit(GetMaskOfTownActions(NULL
, _local_company
, this->town
), y
+ this->vscroll
->GetPosition() - 1);
284 /* FALL THROUGH, when double-clicking. */
285 if (click_count
== 1 || y
< 0) break;
289 DoCommandP(this->town
->xy
, this->window_number
, this->sel_index
, CMD_DO_TOWN_ACTION
);
294 virtual void OnHundredthTick()
300 static WindowDesc::Prefs
_town_authority_prefs ("view_town_authority");
302 static const WindowDesc
_town_authority_desc(
304 WC_TOWN_AUTHORITY
, WC_NONE
,
306 _nested_town_authority_widgets
, lengthof(_nested_town_authority_widgets
),
307 &_town_authority_prefs
310 static void ShowTownAuthorityWindow(uint town
)
312 AllocateWindowDescFront
<TownAuthorityWindow
>(&_town_authority_desc
, town
);
316 /* Town view window. */
317 struct TownViewWindow
: Window
{
319 Town
*town
; ///< Town displayed by the window.
322 static const int WID_TV_HEIGHT_NORMAL
= 162;
324 TownViewWindow (const WindowDesc
*desc
, WindowNumber window_number
) :
325 Window (desc
), town (NULL
)
327 this->CreateNestedTree();
329 this->town
= Town::Get(window_number
);
330 if (this->town
->larger_town
) this->GetWidget
<NWidgetCore
>(WID_TV_CAPTION
)->widget_data
= STR_TOWN_VIEW_CITY_CAPTION
;
332 this->InitNested(window_number
);
334 this->flags
|= WF_DISABLE_VP_SCROLL
;
335 NWidgetViewport
*nvp
= this->GetWidget
<NWidgetViewport
>(WID_TV_VIEWPORT
);
336 nvp
->InitializeViewport(this, this->town
->xy
, ZOOM_LVL_NEWS
);
338 /* disable renaming town in network games if you are not the server */
339 this->SetWidgetDisabledState(WID_TV_CHANGE_NAME
, _networking
&& !_network_server
);
342 void OnPaint (BlitArea
*dpi
) OVERRIDE
344 if (_game_mode
!= GM_EDITOR
) {
345 this->SetWidgetLoweredState (WID_TV_SHOW_AREA
, _thd
.town
== this->town
->index
);
348 this->DrawWidgets (dpi
);
351 virtual void SetStringParameters(int widget
) const
353 if (widget
== WID_TV_CAPTION
) SetDParam(0, this->town
->index
);
356 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
358 if (widget
!= WID_TV_INFO
) return;
360 uint y
= r
.top
+ WD_FRAMERECT_TOP
;
362 SetDParam(0, this->town
->cache
.population
);
363 SetDParam(1, this->town
->cache
.num_houses
);
364 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
, STR_TOWN_VIEW_POPULATION_HOUSES
);
366 SetDParam(0, this->town
->supplied
[CT_PASSENGERS
].old_act
);
367 SetDParam(1, this->town
->supplied
[CT_PASSENGERS
].old_max
);
368 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX
);
370 SetDParam(0, this->town
->supplied
[CT_MAIL
].old_act
);
371 SetDParam(1, this->town
->supplied
[CT_MAIL
].old_max
);
372 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX
);
375 for (int i
= TE_BEGIN
; i
< TE_END
; i
++) {
376 if (this->town
->goal
[i
] == 0) continue;
377 if (this->town
->goal
[i
] == TOWN_GROWTH_WINTER
&& (TileHeight(this->town
->xy
) < LowestSnowLine() || this->town
->cache
.population
<= 90)) continue;
378 if (this->town
->goal
[i
] == TOWN_GROWTH_DESERT
&& (GetTropicZone(this->town
->xy
) != TROPICZONE_DESERT
|| this->town
->cache
.population
<= 60)) continue;
381 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH
);
385 bool rtl
= _current_text_dir
== TD_RTL
;
386 uint cargo_text_left
= r
.left
+ WD_FRAMERECT_LEFT
+ (rtl
? 0 : 20);
387 uint cargo_text_right
= r
.right
- WD_FRAMERECT_RIGHT
- (rtl
? 20 : 0);
389 const CargoSpec
*cargo
= FindFirstCargoWithTownEffect((TownEffect
)i
);
390 assert(cargo
!= NULL
);
394 if (this->town
->goal
[i
] == TOWN_GROWTH_DESERT
|| this->town
->goal
[i
] == TOWN_GROWTH_WINTER
) {
395 /* For 'original' gameplay, don't show the amount required (you need 1 or more ..) */
396 string
= STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL
;
397 if (this->town
->received
[i
].old_act
== 0) {
398 string
= STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL
;
400 if (this->town
->goal
[i
] == TOWN_GROWTH_WINTER
&& TileHeight(this->town
->xy
) < GetSnowLine()) {
401 string
= STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER
;
405 SetDParam(0, cargo
->name
);
407 string
= STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED
;
408 if (this->town
->received
[i
].old_act
< this->town
->goal
[i
]) {
409 string
= STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED
;
412 SetDParam(0, cargo
->Index());
413 SetDParam(1, this->town
->received
[i
].old_act
);
414 SetDParam(2, cargo
->Index());
415 SetDParam(3, this->town
->goal
[i
]);
417 DrawString (dpi
, cargo_text_left
, cargo_text_right
, y
+= FONT_HEIGHT_NORMAL
, string
);
420 if (HasBit(this->town
->flags
, TOWN_IS_GROWING
)) {
421 SetDParam(0, ((this->town
->growth_rate
& (~TOWN_GROW_RATE_CUSTOM
)) * TOWN_GROWTH_TICKS
+ DAY_TICKS
) / DAY_TICKS
);
422 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, this->town
->fund_buildings_months
== 0 ? STR_TOWN_VIEW_TOWN_GROWS_EVERY
: STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED
);
424 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, STR_TOWN_VIEW_TOWN_GROW_STOPPED
);
427 /* only show the town noise, if the noise option is activated. */
428 if (_settings_game
.economy
.station_noise_level
) {
429 SetDParam(0, this->town
->noise_reached
);
430 SetDParam(1, this->town
->MaxTownNoise());
431 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_LEFT
, y
+= FONT_HEIGHT_NORMAL
, STR_TOWN_VIEW_NOISE_IN_TOWN
);
434 if (this->town
->text
!= NULL
) {
435 SetDParamStr(0, this->town
->text
);
436 DrawStringMultiLine (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, y
+= FONT_HEIGHT_NORMAL
, UINT16_MAX
, STR_JUST_RAW_STRING
, TC_BLACK
);
440 virtual void OnClick(Point pt
, int widget
, int click_count
)
443 case WID_TV_CENTER_VIEW
: // scroll to location
445 ShowExtraViewPortWindow(this->town
->xy
);
447 ScrollMainWindowToTile(this->town
->xy
);
451 case WID_TV_SHOW_AUTHORITY
: // town authority
452 ShowTownAuthorityWindow(this->window_number
);
455 case WID_TV_CHANGE_NAME
: // rename
456 SetDParam(0, this->window_number
);
457 ShowQueryString(STR_TOWN_NAME
, STR_TOWN_VIEW_RENAME_TOWN_BUTTON
, MAX_LENGTH_TOWN_NAME_CHARS
, this, CS_ALPHANUMERAL
, QSF_ENABLE_DEFAULT
| QSF_LEN_IN_CHARS
);
460 case WID_TV_EXPAND
: { // expand town - only available on Scenario editor
461 /* Warn the user if towns are not allowed to build roads, but do this only once per OpenTTD run. */
462 static bool _warn_town_no_roads
= false;
464 if (!_settings_game
.economy
.allow_town_roads
&& !_warn_town_no_roads
) {
465 ShowErrorMessage(STR_ERROR_TOWN_EXPAND_WARN_NO_ROADS
, INVALID_STRING_ID
, WL_WARNING
);
466 _warn_town_no_roads
= true;
469 DoCommandP(0, this->window_number
, 0, CMD_EXPAND_TOWN
);
473 case WID_TV_DELETE
: // delete town - only available on Scenario editor
474 DoCommandP(0, this->window_number
, 0, CMD_DELETE_TOWN
);
477 case WID_TV_SHOW_AREA
: // show town area
478 if (_thd
.town
== this->town
->index
) {
479 _thd
.town
= INVALID_TOWN
;
481 if (_thd
.town
!= INVALID_TOWN
) {
482 SetWindowWidgetDirty (WC_TOWN_VIEW
, _thd
.town
, WID_TV_SHOW_AREA
);
483 MarkTownAreaDirty (_thd
.town
);
485 _thd
.town
= this->town
->index
;
487 SetWidgetDirty (WID_TV_SHOW_AREA
);
488 MarkTownAreaDirty (this->town
->index
);
493 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
497 size
->height
= GetDesiredInfoHeight(size
->width
);
503 * Gets the desired height for the information panel.
504 * @return the desired height in pixels.
506 uint
GetDesiredInfoHeight(int width
) const
508 uint aimed_height
= 3 * FONT_HEIGHT_NORMAL
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
511 for (int i
= TE_BEGIN
; i
< TE_END
; i
++) {
512 if (this->town
->goal
[i
] == 0) continue;
513 if (this->town
->goal
[i
] == TOWN_GROWTH_WINTER
&& (TileHeight(this->town
->xy
) < LowestSnowLine() || this->town
->cache
.population
<= 90)) continue;
514 if (this->town
->goal
[i
] == TOWN_GROWTH_DESERT
&& (GetTropicZone(this->town
->xy
) != TROPICZONE_DESERT
|| this->town
->cache
.population
<= 60)) continue;
517 aimed_height
+= FONT_HEIGHT_NORMAL
;
520 aimed_height
+= FONT_HEIGHT_NORMAL
;
522 aimed_height
+= FONT_HEIGHT_NORMAL
;
524 if (_settings_game
.economy
.station_noise_level
) aimed_height
+= FONT_HEIGHT_NORMAL
;
526 if (this->town
->text
!= NULL
) {
527 SetDParamStr(0, this->town
->text
);
528 aimed_height
+= GetStringHeight(STR_JUST_RAW_STRING
, width
- WD_FRAMERECT_LEFT
- WD_FRAMERECT_RIGHT
);
534 void ResizeWindowAsNeeded()
536 const NWidgetBase
*nwid_info
= this->GetWidget
<NWidgetBase
>(WID_TV_INFO
);
537 uint aimed_height
= GetDesiredInfoHeight(nwid_info
->current_x
);
538 if (aimed_height
> nwid_info
->current_y
|| (aimed_height
< nwid_info
->current_y
&& nwid_info
->current_y
> nwid_info
->smallest_y
)) {
543 virtual void OnResize()
545 if (this->viewport
!= NULL
) {
546 NWidgetViewport
*nvp
= this->GetWidget
<NWidgetViewport
>(WID_TV_VIEWPORT
);
547 nvp
->UpdateViewportCoordinates(this);
549 ScrollWindowToTile(this->town
->xy
, this, true); // Re-center viewport.
554 * Some data on this window has become invalid.
555 * @param data Information about the changed data.
556 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
558 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
560 if (!gui_scope
) return;
561 /* Called when setting station noise or required cargoes have changed, in order to resize the window */
562 this->SetDirty(); // refresh display for current size. This will allow to avoid glitches when downgrading
563 this->ResizeWindowAsNeeded();
566 virtual void OnQueryTextFinished(char *str
)
568 if (str
== NULL
) return;
570 DoCommandP(0, this->window_number
, 0, CMD_RENAME_TOWN
, str
);
574 static const NWidgetPart _nested_town_game_view_widgets
[] = {
575 NWidget(NWID_HORIZONTAL
),
576 NWidget(WWT_CLOSEBOX
, COLOUR_BROWN
),
577 NWidget(WWT_CAPTION
, COLOUR_BROWN
, WID_TV_CAPTION
), SetDataTip(STR_TOWN_VIEW_TOWN_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
578 NWidget(WWT_SHADEBOX
, COLOUR_BROWN
),
579 NWidget(WWT_DEFSIZEBOX
, COLOUR_BROWN
),
580 NWidget(WWT_STICKYBOX
, COLOUR_BROWN
),
582 NWidget(WWT_PANEL
, COLOUR_BROWN
),
583 NWidget(WWT_INSET
, COLOUR_BROWN
), SetPadding(2, 2, 2, 2),
584 NWidget(NWID_VIEWPORT
, INVALID_COLOUR
, WID_TV_VIEWPORT
), SetMinimalSize(254, 86), SetFill(1, 0), SetResize(1, 1), SetPadding(1, 1, 1, 1),
587 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TV_INFO
), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(),
588 NWidget(NWID_HORIZONTAL
),
589 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
590 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
591 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_SHOW_AUTHORITY
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON
, STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP
),
592 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_SHOW_AREA
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_SHOW_TOWN_AREA
, STR_SHOW_TOWN_AREA_TOOLTIP
),
594 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
595 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_CENTER_VIEW
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION
, STR_TOWN_VIEW_CENTER_TOOLTIP
),
596 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_CHANGE_NAME
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_RENAME
, STR_TOWN_VIEW_RENAME_TOOLTIP
),
599 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
603 static WindowDesc::Prefs
_town_game_view_prefs ("view_town");
605 static const WindowDesc
_town_game_view_desc(
606 WDP_AUTO
, 260, TownViewWindow::WID_TV_HEIGHT_NORMAL
,
607 WC_TOWN_VIEW
, WC_NONE
,
609 _nested_town_game_view_widgets
, lengthof(_nested_town_game_view_widgets
),
610 &_town_game_view_prefs
613 static const NWidgetPart _nested_town_editor_view_widgets
[] = {
614 NWidget(NWID_HORIZONTAL
),
615 NWidget(WWT_CLOSEBOX
, COLOUR_BROWN
),
616 NWidget(WWT_CAPTION
, COLOUR_BROWN
, WID_TV_CAPTION
), SetDataTip(STR_TOWN_VIEW_TOWN_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
617 NWidget(WWT_SHADEBOX
, COLOUR_BROWN
),
618 NWidget(WWT_DEFSIZEBOX
, COLOUR_BROWN
),
619 NWidget(WWT_STICKYBOX
, COLOUR_BROWN
),
621 NWidget(WWT_PANEL
, COLOUR_BROWN
),
622 NWidget(WWT_INSET
, COLOUR_BROWN
), SetPadding(2, 2, 2, 2),
623 NWidget(NWID_VIEWPORT
, INVALID_COLOUR
, WID_TV_VIEWPORT
), SetMinimalSize(254, 86), SetFill(1, 1), SetResize(1, 1), SetPadding(1, 1, 1, 1),
626 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TV_INFO
), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(),
627 NWidget(NWID_HORIZONTAL
),
628 NWidget(NWID_VERTICAL
, NC_EQUALSIZE
),
629 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
630 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_EXPAND
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_EXPAND_BUTTON
, STR_TOWN_VIEW_EXPAND_TOOLTIP
),
631 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_DELETE
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_TOWN_VIEW_DELETE_BUTTON
, STR_TOWN_VIEW_DELETE_TOOLTIP
),
633 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
),
634 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_CENTER_VIEW
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_LOCATION
, STR_TOWN_VIEW_CENTER_TOOLTIP
),
635 NWidget(WWT_PUSHTXTBTN
, COLOUR_BROWN
, WID_TV_CHANGE_NAME
), SetMinimalSize(130, 12), SetFill(1, 1), SetResize(1, 0), SetDataTip(STR_BUTTON_RENAME
, STR_TOWN_VIEW_RENAME_TOOLTIP
),
638 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
642 static WindowDesc::Prefs
_town_editor_view_prefs ("view_town_scen");
644 static const WindowDesc
_town_editor_view_desc(
645 WDP_AUTO
, 260, TownViewWindow::WID_TV_HEIGHT_NORMAL
,
646 WC_TOWN_VIEW
, WC_NONE
,
648 _nested_town_editor_view_widgets
, lengthof(_nested_town_editor_view_widgets
),
649 &_town_editor_view_prefs
652 void ShowTownViewWindow(TownID town
)
654 if (_game_mode
== GM_EDITOR
) {
655 AllocateWindowDescFront
<TownViewWindow
>(&_town_editor_view_desc
, town
);
657 AllocateWindowDescFront
<TownViewWindow
>(&_town_game_view_desc
, town
);
661 static const NWidgetPart _nested_town_directory_widgets
[] = {
662 NWidget(NWID_HORIZONTAL
),
663 NWidget(WWT_CLOSEBOX
, COLOUR_BROWN
),
664 NWidget(WWT_CAPTION
, COLOUR_BROWN
), SetDataTip(STR_TOWN_DIRECTORY_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
665 NWidget(WWT_SHADEBOX
, COLOUR_BROWN
),
666 NWidget(WWT_DEFSIZEBOX
, COLOUR_BROWN
),
667 NWidget(WWT_STICKYBOX
, COLOUR_BROWN
),
669 NWidget(NWID_HORIZONTAL
),
670 NWidget(NWID_VERTICAL
),
671 NWidget(NWID_HORIZONTAL
),
672 NWidget(WWT_TEXTBTN
, COLOUR_BROWN
, WID_TD_SORT_ORDER
), SetDataTip(STR_BUTTON_SORT_BY
, STR_TOOLTIP_SORT_ORDER
),
673 NWidget(WWT_DROPDOWN
, COLOUR_BROWN
, WID_TD_SORT_CRITERIA
), SetDataTip(STR_JUST_STRING
, STR_TOOLTIP_SORT_CRITERIA
),
674 NWidget(WWT_PANEL
, COLOUR_BROWN
), SetResize(1, 0), EndContainer(),
676 NWidget(WWT_PANEL
, COLOUR_BROWN
, WID_TD_LIST
), SetMinimalSize(196, 0), SetDataTip(0x0, STR_TOWN_DIRECTORY_LIST_TOOLTIP
),
677 SetFill(1, 0), SetResize(0, 10), SetScrollbar(WID_TD_SCROLLBAR
), EndContainer(),
678 NWidget(WWT_PANEL
, COLOUR_BROWN
),
679 NWidget(WWT_TEXT
, COLOUR_BROWN
, WID_TD_WORLD_POPULATION
), SetPadding(2, 0, 0, 2), SetMinimalSize(196, 12), SetFill(1, 0), SetDataTip(STR_TOWN_POPULATION
, STR_NULL
),
682 NWidget(NWID_VERTICAL
),
683 NWidget(NWID_VSCROLLBAR
, COLOUR_BROWN
, WID_TD_SCROLLBAR
),
684 NWidget(WWT_RESIZEBOX
, COLOUR_BROWN
),
689 /** Town directory window class. */
690 struct TownDirectoryWindow
: public Window
{
692 /* Runtime saved values */
693 static Listing last_sorting
;
694 static const Town
*last_town
;
696 /* Constants for sorting towns */
697 static const StringID sorter_names
[];
698 static GUITownList::SortFunction
* const sorter_funcs
[];
704 void BuildSortTownList()
706 if (this->towns
.NeedRebuild()) {
711 *this->towns
.Append() = t
;
714 this->towns
.Compact();
715 this->towns
.RebuildDone();
716 this->vscroll
->SetCount(this->towns
.Length()); // Update scrollbar as well.
718 /* Always sort the towns. */
719 this->last_town
= NULL
;
721 this->SetWidgetDirty(WID_TD_LIST
); // Force repaint of the displayed towns.
724 /** Sort by town name */
725 static int CDECL
TownNameSorter(const Town
* const *a
, const Town
* const *b
)
727 static char buf_cache
[64];
732 SetDParam(0, ta
->index
);
733 GetString (buf
, STR_TOWN_NAME
);
735 /* If 'b' is the same town as in the last round, use the cached value
736 * We do this to speed stuff up ('b' is called with the same value a lot of
737 * times after each other) */
738 if (tb
!= last_town
) {
740 SetDParam(0, tb
->index
);
741 GetString (buf_cache
, STR_TOWN_NAME
);
744 return strnatcmp(buf
, buf_cache
); // Sort by name (natural sorting).
747 /** Sort by population (default descending, as big towns are of the most interest). */
748 static int CDECL
TownPopulationSorter(const Town
* const *a
, const Town
* const *b
)
750 uint32 a_population
= (*a
)->cache
.population
;
751 uint32 b_population
= (*b
)->cache
.population
;
752 if (a_population
== b_population
) return TownDirectoryWindow::TownNameSorter(a
, b
);
753 return (a_population
< b_population
) ? -1 : 1;
756 /** Sort by town rating */
757 static int CDECL
TownRatingSorter(const Town
* const *a
, const Town
* const *b
)
759 int before
= TownDirectoryWindow::last_sorting
.order
? 1 : -1; // Value to get 'a' before 'b'.
761 /* Towns without rating are always after towns with rating. */
762 if (HasBit((*a
)->have_ratings
, _local_company
)) {
763 if (HasBit((*b
)->have_ratings
, _local_company
)) {
764 int16 a_rating
= (*a
)->ratings
[_local_company
];
765 int16 b_rating
= (*b
)->ratings
[_local_company
];
766 if (a_rating
== b_rating
) return TownDirectoryWindow::TownNameSorter(a
, b
);
767 return (a_rating
< b_rating
) ? -1 : 1;
771 if (HasBit((*b
)->have_ratings
, _local_company
)) return -before
;
772 return -before
* TownDirectoryWindow::TownNameSorter(a
, b
); // Sort unrated towns always on ascending town name.
776 TownDirectoryWindow (const WindowDesc
*desc
) :
777 Window (desc
), towns(), vscroll (NULL
)
779 this->CreateNestedTree();
781 this->vscroll
= this->GetScrollbar(WID_TD_SCROLLBAR
);
783 this->towns
.SetListing(this->last_sorting
);
784 this->towns
.SetSortFuncs(TownDirectoryWindow::sorter_funcs
);
785 this->towns
.ForceRebuild();
786 this->BuildSortTownList();
791 virtual void SetStringParameters(int widget
) const
794 case WID_TD_WORLD_POPULATION
:
795 SetDParam(0, GetWorldPopulation());
798 case WID_TD_SORT_CRITERIA
:
799 SetDParam(0, TownDirectoryWindow::sorter_names
[this->towns
.SortType()]);
804 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
807 case WID_TD_SORT_ORDER
:
808 this->DrawSortButtonState (dpi
, widget
, this->towns
.IsDescSortOrder() ? SBS_DOWN
: SBS_UP
);
813 int y
= r
.top
+ WD_FRAMERECT_TOP
;
814 if (this->towns
.Length() == 0) { // No towns available.
815 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
, y
, STR_TOWN_DIRECTORY_NONE
);
819 /* At least one town available. */
820 bool rtl
= _current_text_dir
== TD_RTL
;
821 Dimension icon_size
= GetSpriteSize(SPR_TOWN_RATING_GOOD
);
822 int text_left
= r
.left
+ WD_FRAMERECT_LEFT
+ (rtl
? 0 : icon_size
.width
+ 2);
823 int text_right
= r
.right
- WD_FRAMERECT_RIGHT
- (rtl
? icon_size
.width
+ 2 : 0);
824 int icon_x
= rtl
? r
.right
- WD_FRAMERECT_RIGHT
- icon_size
.width
: r
.left
+ WD_FRAMERECT_LEFT
;
826 for (uint i
= this->vscroll
->GetPosition(); i
< this->towns
.Length(); i
++) {
827 const Town
*t
= this->towns
[i
];
828 assert(t
->xy
!= INVALID_TILE
);
830 /* Draw rating icon. */
831 if (_game_mode
== GM_EDITOR
|| !HasBit(t
->have_ratings
, _local_company
)) {
832 DrawSprite (dpi
, SPR_TOWN_RATING_NA
, PAL_NONE
, icon_x
, y
+ (this->resize
.step_height
- icon_size
.height
) / 2);
834 SpriteID icon
= SPR_TOWN_RATING_APALLING
;
835 if (t
->ratings
[_local_company
] > RATING_VERYPOOR
) icon
= SPR_TOWN_RATING_MEDIOCRE
;
836 if (t
->ratings
[_local_company
] > RATING_GOOD
) icon
= SPR_TOWN_RATING_GOOD
;
837 DrawSprite (dpi
, icon
, PAL_NONE
, icon_x
, y
+ (this->resize
.step_height
- icon_size
.height
) / 2);
840 SetDParam(0, t
->index
);
841 SetDParam(1, t
->cache
.population
);
842 DrawString (dpi
, text_left
, text_right
, y
+ (this->resize
.step_height
- FONT_HEIGHT_NORMAL
) / 2, STR_TOWN_DIRECTORY_TOWN
);
844 y
+= this->resize
.step_height
;
845 if (++n
== this->vscroll
->GetCapacity()) break; // max number of towns in 1 window
852 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
855 case WID_TD_SORT_ORDER
: {
856 Dimension d
= GetStringBoundingBox(this->GetWidget
<NWidgetCore
>(widget
)->widget_data
);
857 d
.width
+= padding
.width
+ Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
858 d
.height
+= padding
.height
;
859 *size
= maxdim(*size
, d
);
862 case WID_TD_SORT_CRITERIA
: {
863 Dimension d
= {0, 0};
864 for (uint i
= 0; TownDirectoryWindow::sorter_names
[i
] != INVALID_STRING_ID
; i
++) {
865 d
= maxdim(d
, GetStringBoundingBox(TownDirectoryWindow::sorter_names
[i
]));
867 d
.width
+= padding
.width
;
868 d
.height
+= padding
.height
;
869 *size
= maxdim(*size
, d
);
873 Dimension d
= GetStringBoundingBox(STR_TOWN_DIRECTORY_NONE
);
874 for (uint i
= 0; i
< this->towns
.Length(); i
++) {
875 const Town
*t
= this->towns
[i
];
879 SetDParam(0, t
->index
);
880 SetDParamMaxDigits(1, 8);
881 d
= maxdim(d
, GetStringBoundingBox(STR_TOWN_DIRECTORY_TOWN
));
883 Dimension icon_size
= GetSpriteSize(SPR_TOWN_RATING_GOOD
);
884 d
.width
+= icon_size
.width
+ 2;
885 d
.height
= max(d
.height
, icon_size
.height
);
886 resize
->height
= d
.height
;
888 d
.width
+= padding
.width
+ WD_FRAMERECT_LEFT
+ WD_FRAMERECT_RIGHT
;
889 d
.height
+= padding
.height
+ WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
890 *size
= maxdim(*size
, d
);
893 case WID_TD_WORLD_POPULATION
: {
894 SetDParamMaxDigits(0, 10);
895 Dimension d
= GetStringBoundingBox(STR_TOWN_POPULATION
);
896 d
.width
+= padding
.width
;
897 d
.height
+= padding
.height
;
898 *size
= maxdim(*size
, d
);
904 virtual void OnClick(Point pt
, int widget
, int click_count
)
907 case WID_TD_SORT_ORDER
: // Click on sort order button
908 if (this->towns
.SortType() != 2) { // A different sort than by rating.
909 this->towns
.ToggleSortOrder();
910 this->last_sorting
= this->towns
.GetListing(); // Store new sorting order.
912 /* Some parts are always sorted ascending on name. */
913 this->last_sorting
.order
= !this->last_sorting
.order
;
914 this->towns
.SetListing(this->last_sorting
);
915 this->towns
.ForceResort();
921 case WID_TD_SORT_CRITERIA
: // Click on sort criteria dropdown
922 ShowDropDownMenu(this, TownDirectoryWindow::sorter_names
, this->towns
.SortType(), WID_TD_SORT_CRITERIA
, 0, 0);
925 case WID_TD_LIST
: { // Click on Town Matrix
926 uint id_v
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_TD_LIST
, WD_FRAMERECT_TOP
);
927 if (id_v
>= this->towns
.Length()) return; // click out of town bounds
929 const Town
*t
= this->towns
[id_v
];
932 ShowExtraViewPortWindow(t
->xy
);
934 ScrollMainWindowToTile(t
->xy
);
941 virtual void OnDropdownSelect(int widget
, int index
)
943 if (widget
!= WID_TD_SORT_CRITERIA
) return;
945 if (this->towns
.SortType() != index
) {
946 this->towns
.SetSortType(index
);
947 this->last_sorting
= this->towns
.GetListing(); // Store new sorting order.
948 this->BuildSortTownList();
952 void OnPaint (BlitArea
*dpi
) OVERRIDE
954 if (this->towns
.NeedRebuild()) this->BuildSortTownList();
955 this->DrawWidgets (dpi
);
958 virtual void OnHundredthTick()
960 this->BuildSortTownList();
964 virtual void OnResize()
966 this->vscroll
->SetCapacityFromWidget(this, WID_TD_LIST
);
970 * Some data on this window has become invalid.
971 * @param data Information about the changed data.
972 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
974 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
977 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
978 this->towns
.ForceRebuild();
980 this->towns
.ForceResort();
985 Listing
TownDirectoryWindow::last_sorting
= {false, 0};
986 const Town
*TownDirectoryWindow::last_town
= NULL
;
988 /** Names of the sorting functions. */
989 const StringID
TownDirectoryWindow::sorter_names
[] = {
991 STR_SORT_BY_POPULATION
,
996 /** Available town directory sorting functions. */
997 GUITownList::SortFunction
* const TownDirectoryWindow::sorter_funcs
[] = {
999 &TownPopulationSorter
,
1003 static WindowDesc::Prefs
_town_directory_prefs ("list_towns");
1005 static const WindowDesc
_town_directory_desc(
1007 WC_TOWN_DIRECTORY
, WC_NONE
,
1009 _nested_town_directory_widgets
, lengthof(_nested_town_directory_widgets
),
1010 &_town_directory_prefs
1013 void ShowTownDirectory()
1015 if (BringWindowToFrontById(WC_TOWN_DIRECTORY
, 0)) return;
1016 new TownDirectoryWindow(&_town_directory_desc
);
1019 void CcFoundTown(const CommandCost
&result
, TileIndex tile
, uint32 p1
, uint32 p2
)
1021 if (result
.Failed()) return;
1023 if (HasBit(p1
, 6)) {
1024 ScrollMainWindowToTile(Town::Get(_new_town_id
)->xy
);
1026 if (_settings_client
.sound
.confirm
) SndPlayTileFx(SND_1F_SPLAT_OTHER
, tile
);
1027 if (!_settings_client
.gui
.persistent_buildingtools
) ResetPointerMode();
1031 StringID
GetErrFoundTown (TileIndex tile
, uint32 p1
, uint32 p2
, const char *text
)
1033 return HasBit(p1
, 6) ? STR_ERROR_CAN_T_GENERATE_TOWN
: STR_ERROR_CAN_T_FOUND_TOWN_HERE
;
1036 static const NWidgetPart _nested_found_town_widgets
[] = {
1037 NWidget(NWID_HORIZONTAL
),
1038 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
1039 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
), SetDataTip(STR_FOUND_TOWN_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
1040 NWidget(WWT_SHADEBOX
, COLOUR_DARK_GREEN
),
1041 NWidget(WWT_STICKYBOX
, COLOUR_DARK_GREEN
),
1043 /* Construct new town(s) buttons. */
1044 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
),
1045 NWidget(NWID_SPACER
), SetMinimalSize(0, 2),
1046 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_NEW_TOWN
), SetMinimalSize(156, 12), SetFill(1, 0),
1047 SetDataTip(STR_FOUND_TOWN_NEW_TOWN_BUTTON
, STR_FOUND_TOWN_NEW_TOWN_TOOLTIP
), SetPadding(0, 2, 1, 2),
1048 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_TF_RANDOM_TOWN
), SetMinimalSize(156, 12), SetFill(1, 0),
1049 SetDataTip(STR_FOUND_TOWN_RANDOM_TOWN_BUTTON
, STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP
), SetPadding(0, 2, 1, 2),
1050 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_TF_MANY_RANDOM_TOWNS
), SetMinimalSize(156, 12), SetFill(1, 0),
1051 SetDataTip(STR_FOUND_TOWN_MANY_RANDOM_TOWNS
, STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP
), SetPadding(0, 2, 0, 2),
1052 /* Town name selection. */
1053 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetMinimalSize(156, 14), SetPadding(0, 2, 0, 2), SetDataTip(STR_FOUND_TOWN_NAME_TITLE
, STR_NULL
),
1054 NWidget(WWT_EDITBOX
, COLOUR_GREY
, WID_TF_TOWN_NAME_EDITBOX
), SetMinimalSize(156, 12), SetPadding(0, 2, 3, 2),
1055 SetDataTip(STR_FOUND_TOWN_NAME_EDITOR_TITLE
, STR_FOUND_TOWN_NAME_EDITOR_HELP
),
1056 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_TF_TOWN_NAME_RANDOM
), SetMinimalSize(78, 12), SetPadding(0, 2, 0, 2), SetFill(1, 0),
1057 SetDataTip(STR_FOUND_TOWN_NAME_RANDOM_BUTTON
, STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP
),
1058 /* Town size selection. */
1059 NWidget(NWID_HORIZONTAL
), SetPIP(2, 0, 2),
1060 NWidget(NWID_SPACER
), SetFill(1, 0),
1061 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetMinimalSize(148, 14), SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_TITLE
, STR_NULL
),
1062 NWidget(NWID_SPACER
), SetFill(1, 0),
1064 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(2, 0, 2),
1065 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_SIZE_SMALL
), SetMinimalSize(78, 12), SetFill(1, 0),
1066 SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON
, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP
),
1067 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_SIZE_MEDIUM
), SetMinimalSize(78, 12), SetFill(1, 0),
1068 SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON
, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP
),
1070 NWidget(NWID_SPACER
), SetMinimalSize(0, 1),
1071 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(2, 0, 2),
1072 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_SIZE_LARGE
), SetMinimalSize(78, 12), SetFill(1, 0),
1073 SetDataTip(STR_FOUND_TOWN_INITIAL_SIZE_LARGE_BUTTON
, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP
),
1074 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_SIZE_RANDOM
), SetMinimalSize(78, 12), SetFill(1, 0),
1075 SetDataTip(STR_FOUND_TOWN_SIZE_RANDOM
, STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP
),
1077 NWidget(NWID_SPACER
), SetMinimalSize(0, 3),
1078 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_CITY
), SetPadding(0, 2, 0, 2), SetMinimalSize(156, 12), SetFill(1, 0),
1079 SetDataTip(STR_FOUND_TOWN_CITY
, STR_FOUND_TOWN_CITY_TOOLTIP
), SetFill(1, 0),
1080 /* Town roads selection. */
1081 NWidget(NWID_HORIZONTAL
), SetPIP(2, 0, 2),
1082 NWidget(NWID_SPACER
), SetFill(1, 0),
1083 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
), SetMinimalSize(148, 14), SetDataTip(STR_FOUND_TOWN_ROAD_LAYOUT
, STR_NULL
),
1084 NWidget(NWID_SPACER
), SetFill(1, 0),
1086 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(2, 0, 2),
1087 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_LAYOUT_ORIGINAL
), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_ORIGINAL
, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT
),
1088 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_LAYOUT_BETTER
), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_BETTER_ROADS
, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT
),
1090 NWidget(NWID_SPACER
), SetMinimalSize(0, 1),
1091 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(2, 0, 2),
1092 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_LAYOUT_GRID2
), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_2X2_GRID
, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT
),
1093 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_LAYOUT_GRID3
), SetMinimalSize(78, 12), SetFill(1, 0), SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_3X3_GRID
, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT
),
1095 NWidget(NWID_SPACER
), SetMinimalSize(0, 1),
1096 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_TF_LAYOUT_RANDOM
), SetPadding(0, 2, 0, 2), SetMinimalSize(0, 12), SetFill(1, 0),
1097 SetDataTip(STR_FOUND_TOWN_SELECT_LAYOUT_RANDOM
, STR_FOUND_TOWN_SELECT_TOWN_ROAD_LAYOUT
), SetFill(1, 0),
1098 NWidget(NWID_SPACER
), SetMinimalSize(0, 2),
1102 /** Found a town window class. */
1103 struct FoundTownWindow
: Window
{
1105 TownSize town_size
; ///< Selected town size
1106 TownLayout town_layout
; ///< Selected town layout
1107 bool city
; ///< Are we building a city?
1108 QueryStringC
<MAX_LENGTH_TOWN_NAME_CHARS
> townname_editbox
; ///< Townname editbox
1109 bool townnamevalid
; ///< Is generated town name valid?
1110 uint32 townnameparts
; ///< Generated town name
1111 TownNameParams params
; ///< Town name parameters
1114 FoundTownWindow (const WindowDesc
*desc
, WindowNumber window_number
) :
1116 town_size(TSZ_MEDIUM
),
1117 town_layout(_settings_game
.economy
.town_layout
),
1120 townnamevalid(false),
1122 params(_settings_game
.game_creation
.town_name
)
1124 this->InitNested(window_number
);
1125 this->querystrings
[WID_TF_TOWN_NAME_EDITBOX
] = &this->townname_editbox
;
1126 this->RandomTownName();
1127 this->UpdateButtons(true);
1130 void RandomTownName()
1132 this->townnamevalid
= GenerateTownName(&this->townnameparts
);
1134 if (!this->townnamevalid
) {
1135 this->townname_editbox
.DeleteAll();
1137 this->townname_editbox
.clear();
1138 AppendTownName (&this->townname_editbox
, &this->params
, this->townnameparts
);
1139 this->townname_editbox
.UpdateSize();
1141 UpdateOSKOriginalText(this, WID_TF_TOWN_NAME_EDITBOX
);
1143 this->SetWidgetDirty(WID_TF_TOWN_NAME_EDITBOX
);
1146 void UpdateButtons(bool check_availability
)
1148 if (check_availability
&& _game_mode
!= GM_EDITOR
) {
1149 this->SetWidgetsDisabledState(true, WID_TF_RANDOM_TOWN
, WID_TF_MANY_RANDOM_TOWNS
, WID_TF_SIZE_LARGE
, WIDGET_LIST_END
);
1150 this->SetWidgetsDisabledState(_settings_game
.economy
.found_town
!= TF_CUSTOM_LAYOUT
,
1151 WID_TF_LAYOUT_ORIGINAL
, WID_TF_LAYOUT_BETTER
, WID_TF_LAYOUT_GRID2
, WID_TF_LAYOUT_GRID3
, WID_TF_LAYOUT_RANDOM
, WIDGET_LIST_END
);
1152 if (_settings_game
.economy
.found_town
!= TF_CUSTOM_LAYOUT
) town_layout
= _settings_game
.economy
.town_layout
;
1155 for (int i
= WID_TF_SIZE_SMALL
; i
<= WID_TF_SIZE_RANDOM
; i
++) {
1156 this->SetWidgetLoweredState(i
, i
== WID_TF_SIZE_SMALL
+ this->town_size
);
1159 this->SetWidgetLoweredState(WID_TF_CITY
, this->city
);
1161 for (int i
= WID_TF_LAYOUT_ORIGINAL
; i
<= WID_TF_LAYOUT_RANDOM
; i
++) {
1162 this->SetWidgetLoweredState(i
, i
== WID_TF_LAYOUT_ORIGINAL
+ this->town_layout
);
1168 void ExecuteFoundTownCommand(TileIndex tile
, bool random
)
1170 const char *name
= NULL
;
1172 if (!this->townnamevalid
) {
1173 name
= this->townname_editbox
.GetText();
1175 /* If user changed the name, send it */
1176 sstring
<MAX_LENGTH_TOWN_NAME_CHARS
* MAX_CHAR_LENGTH
> buf
;
1177 AppendTownName (&buf
, &this->params
, this->townnameparts
);
1178 if (strcmp (buf
.c_str(), this->townname_editbox
.GetText()) != 0) name
= this->townname_editbox
.GetText();
1181 bool success
= DoCommandP(tile
, this->town_size
| this->city
<< 2 | this->town_layout
<< 3 | random
<< 6,
1182 townnameparts
, CMD_FOUND_TOWN
, name
);
1184 /* Rerandomise name, if success and no cost-estimation. */
1185 if (success
&& !_shift_pressed
) this->RandomTownName();
1188 virtual void OnClick(Point pt
, int widget
, int click_count
)
1191 case WID_TF_NEW_TOWN
:
1192 HandlePlacePushButton (this, WID_TF_NEW_TOWN
, SPR_CURSOR_TOWN
, POINTER_TILE
);
1195 case WID_TF_RANDOM_TOWN
:
1196 this->ExecuteFoundTownCommand(0, true);
1199 case WID_TF_TOWN_NAME_RANDOM
:
1200 this->RandomTownName();
1201 this->SetFocusedWidget(WID_TF_TOWN_NAME_EDITBOX
);
1204 case WID_TF_MANY_RANDOM_TOWNS
:
1205 _generating_world
= true;
1206 UpdateNearestTownForRoadTiles(true);
1207 if (!GenerateTowns(this->town_layout
)) {
1208 ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_TOWN
, STR_ERROR_NO_SPACE_FOR_TOWN
, WL_INFO
);
1210 UpdateNearestTownForRoadTiles(false);
1211 _generating_world
= false;
1214 case WID_TF_SIZE_SMALL
: case WID_TF_SIZE_MEDIUM
: case WID_TF_SIZE_LARGE
: case WID_TF_SIZE_RANDOM
:
1215 this->town_size
= (TownSize
)(widget
- WID_TF_SIZE_SMALL
);
1216 this->UpdateButtons(false);
1221 this->SetWidgetLoweredState(WID_TF_CITY
, this->city
);
1225 case WID_TF_LAYOUT_ORIGINAL
: case WID_TF_LAYOUT_BETTER
: case WID_TF_LAYOUT_GRID2
:
1226 case WID_TF_LAYOUT_GRID3
: case WID_TF_LAYOUT_RANDOM
:
1227 this->town_layout
= (TownLayout
)(widget
- WID_TF_LAYOUT_ORIGINAL
);
1228 this->UpdateButtons(false);
1233 virtual void OnPlaceObject(Point pt
, TileIndex tile
)
1235 this->ExecuteFoundTownCommand(tile
, false);
1238 virtual void OnPlaceObjectAbort()
1240 this->RaiseButtons();
1241 this->UpdateButtons(false);
1245 * Some data on this window has become invalid.
1246 * @param data Information about the changed data.
1247 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
1249 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
1251 if (!gui_scope
) return;
1252 this->UpdateButtons(true);
1256 static WindowDesc::Prefs
_found_town_prefs ("build_town");
1258 static const WindowDesc
_found_town_desc(
1260 WC_FOUND_TOWN
, WC_NONE
,
1262 _nested_found_town_widgets
, lengthof(_nested_found_town_widgets
),
1266 void ShowFoundTownWindow()
1268 if (_game_mode
!= GM_EDITOR
&& !Company::IsValidID(_local_company
)) return;
1269 AllocateWindowDescFront
<FoundTownWindow
>(&_found_town_desc
, 0);
1274 * Window for selecting towns to build a house in.
1276 struct SelectTownWindow
: Window
{
1277 const TileIndex tile
; ///< tile where to build the house
1278 const HouseID house
; ///< house to build
1279 std::vector
<Town
*> towns
; ///< sorted vector of towns
1280 Scrollbar
*vscroll
; ///< scrollbar for the town list
1281 bool rebuild
; ///< town vector must be rebuilt
1283 void RebuildTownList (void);
1285 SelectTownWindow (const WindowDesc
*desc
, TileIndex tile
, HouseID house
)
1286 : Window(desc
), tile(tile
), house(house
), towns(),
1287 vscroll(NULL
), rebuild(false)
1289 this->CreateNestedTree();
1290 this->vscroll
= this->GetScrollbar(WID_ST_SCROLLBAR
);
1291 this->RebuildTownList();
1295 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
1297 if (widget
!= WID_ST_PANEL
) return;
1299 /* Determine the widest string */
1300 Dimension d
= { 0, 0 };
1301 for (uint i
= 0; i
< Town::pool
.items
; i
++) {
1303 d
= maxdim(d
, GetStringBoundingBox(STR_SELECT_TOWN_LIST_ITEM
));
1306 resize
->height
= d
.height
;
1308 d
.width
+= WD_FRAMERECT_RIGHT
+ WD_FRAMERECT_LEFT
;
1309 d
.height
+= WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
1313 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
1315 if (widget
!= WID_ST_PANEL
) return;
1317 uint y
= r
.top
+ WD_FRAMERECT_TOP
;
1318 uint end
= min(this->vscroll
->GetCount(), this->vscroll
->GetPosition() + this->vscroll
->GetCapacity());
1319 for (uint i
= this->vscroll
->GetPosition(); i
< end
; i
++) {
1320 SetDParam(0, this->towns
[i
]->index
);
1321 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, y
, STR_SELECT_TOWN_LIST_ITEM
);
1322 y
+= this->resize
.step_height
;
1326 virtual void OnClick(Point pt
, int widget
, int click_count
)
1328 if (widget
!= WID_ST_PANEL
) return;
1330 uint pos
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_ST_PANEL
, WD_FRAMERECT_TOP
);
1331 if (pos
>= this->towns
.size()) return;
1333 Town
*town
= this->towns
[pos
];
1335 StringID err
= IsNewTownHouseAllowed (town
, this->house
);
1336 if (err
!= STR_NULL
) {
1337 ShowErrorMessage (STR_ERROR_CAN_T_BUILD_HOUSE_HERE
,
1338 err
, WL_INFO
, pt
.x
, pt
.y
);
1343 DoBuildHouse (town
, this->tile
, this->house
, InteractiveRandom());
1345 /* Close the window */
1349 void OnPaint (BlitArea
*dpi
) OVERRIDE
1351 if (this->rebuild
) this->RebuildTownList();
1352 this->DrawWidgets (dpi
);
1355 virtual void OnResize()
1357 this->vscroll
->SetCapacityFromWidget(this, WID_ST_PANEL
, WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
);
1360 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
1362 this->rebuild
= true;
1366 struct TownDistanceSorter
{
1367 const TileIndex tile
;
1369 TownDistanceSorter (TileIndex tile
) : tile (tile
)
1373 bool operator() (const Town
*t1
, const Town
*t2
)
1375 return DistanceSquare (tile
, t1
->xy
) < DistanceSquare (tile
, t2
->xy
);
1379 void SelectTownWindow::RebuildTownList (void)
1381 this->towns
.clear();
1385 this->towns
.push_back (t
);
1388 TownDistanceSorter
sorter (this->tile
);
1389 std::stable_sort (this->towns
.begin(), this->towns
.end(), sorter
);
1391 this->rebuild
= false;
1392 this->vscroll
->SetCount (this->towns
.size());
1395 static const NWidgetPart _nested_select_town_widgets
[] = {
1396 NWidget(NWID_HORIZONTAL
),
1397 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
1398 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_ST_CAPTION
), SetDataTip(STR_SELECT_TOWN_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
1399 NWidget(WWT_DEFSIZEBOX
, COLOUR_DARK_GREEN
),
1401 NWidget(NWID_HORIZONTAL
),
1402 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, WID_ST_PANEL
), SetResize(1, 0), SetScrollbar(WID_ST_SCROLLBAR
), EndContainer(),
1403 NWidget(NWID_VERTICAL
),
1404 NWidget(NWID_VSCROLLBAR
, COLOUR_DARK_GREEN
, WID_ST_SCROLLBAR
),
1405 NWidget(WWT_RESIZEBOX
, COLOUR_DARK_GREEN
),
1410 static WindowDesc::Prefs
_select_town_prefs ("select_town");
1412 static const WindowDesc
_select_town_desc(
1414 WC_SELECT_TOWN
, WC_NONE
,
1416 _nested_select_town_widgets
, lengthof(_nested_select_town_widgets
),
1421 /** The window used for building houses. */
1422 class HousePickerWindow
: public Window
{
1423 std::vector
<HouseID
> houses
; ///< List of available houses.
1424 std::vector
<uint16
> sets
; ///< List of house sets, each item points the first house of the set in the houses array.
1425 uint sel_set
; ///< Index of the selected house set.
1426 uint sel_offset
; ///< Index of the selected house.
1427 StringID name
; ///< Name of the selected house.
1428 uint32 supply
; ///< Cargo mask of produced cargo.
1429 char acceptance
[DRAW_STRING_BUFFER
]; ///< String representation of accepted cargo.
1430 uint line_height
; ///< Height of a single line in the list of house sets.
1432 static HouseID cur_house
; ///< House selected in the house picker window.
1434 uint
GetSetSize (uint set
) const
1436 assert (set
< this->sets
.size());
1437 uint next
= set
+ 1;
1438 return ((next
== this->sets
.size()) ? this->houses
.size() : this->sets
[next
])
1439 - this->sets
[this->sel_set
];
1442 static void GetAcceptedCargo (CargoArray
*acceptance
, HouseID house
)
1444 const HouseSpec
*hs
= HouseSpec::Get (house
);
1447 /* Set the initial accepted cargo types. */
1448 for (uint i
= 0; i
< lengthof(accepts
); i
++) {
1449 accepts
[i
] = hs
->accepts_cargo
[i
];
1452 /* Check for custom accepted cargo types. */
1453 if (HasBit(hs
->callback_mask
, CBM_HOUSE_ACCEPT_CARGO
)) {
1454 uint16 callback
= GetHouseCallback (CBID_HOUSE_ACCEPT_CARGO
, 0, 0, house
);
1455 if (callback
!= CALLBACK_FAILED
) {
1456 /* Replace accepted cargo types with translated values from callback. */
1457 accepts
[0] = GetCargoTranslation (GB(callback
, 0, 5), hs
->grf_prop
.grffile
);
1458 accepts
[1] = GetCargoTranslation (GB(callback
, 5, 5), hs
->grf_prop
.grffile
);
1459 accepts
[2] = GetCargoTranslation (GB(callback
, 10, 5), hs
->grf_prop
.grffile
);
1463 /* Check for custom cargo acceptance */
1464 if (HasBit(hs
->callback_mask
, CBM_HOUSE_CARGO_ACCEPTANCE
)) {
1465 uint16 callback
= GetHouseCallback (CBID_HOUSE_CARGO_ACCEPTANCE
, 0, 0, house
);
1466 if (callback
!= CALLBACK_FAILED
) {
1467 if (accepts
[0] != CT_INVALID
) (*acceptance
)[accepts
[0]] += GB(callback
, 0, 4);
1468 if (accepts
[1] != CT_INVALID
) (*acceptance
)[accepts
[1]] += GB(callback
, 4, 4);
1469 if (_settings_game
.game_creation
.landscape
!= LT_TEMPERATE
&& HasBit(callback
, 12)) {
1470 /* The 'S' bit indicates food instead of goods. */
1471 (*acceptance
)[CT_FOOD
] += GB(callback
, 8, 4);
1472 } else if (accepts
[2] != CT_INVALID
) {
1473 (*acceptance
)[accepts
[2]] += GB(callback
, 8, 4);
1479 /* No custom acceptance, so fill in with the default values. */
1480 for (uint i
= 0; i
< lengthof(accepts
); i
++) {
1481 if (accepts
[i
] != CT_INVALID
) (*acceptance
)[accepts
[i
]] += hs
->cargo_acceptance
[i
];
1485 void BuildSetList (void)
1487 /* Try to reselect the previous selection. */
1489 this->sel_offset
= 0;
1491 const GRFFile
*cur_grffile
= (cur_house
!= INVALID_HOUSE_ID
) ?
1492 HouseSpec::Get (cur_house
)->grf_prop
.grffile
: NULL
;
1494 assert (!this->houses
.empty());
1498 HouseID house
= this->houses
[i
];
1499 const HouseSpec
*hs
= HouseSpec::Get (house
);
1500 const GRFFile
*grffile
= hs
->grf_prop
.grffile
;
1501 if (grffile
== cur_grffile
) {
1502 this->sel_set
= this->sets
.size();
1504 this->sets
.push_back (i
);
1506 if (house
== cur_house
) {
1507 this->sel_offset
= i
;
1510 if (i
== this->houses
.size()) return;
1511 house
= this->houses
[i
];
1512 if (HouseSpec::Get(house
)->grf_prop
.grffile
!= grffile
) break;
1517 void SetObjectToPlace (void)
1519 SetPointerMode (POINTER_TILE
, this, SPR_CURSOR_TOWN
);
1522 void UpdateCache (void)
1524 if (cur_house
== INVALID_HOUSE_ID
) {
1525 SetTileSelectSize (1, 1);
1526 this->name
= STR_EMPTY
;
1528 this->acceptance
[0] = '\0';
1532 const HouseSpec
*hs
= HouseSpec::Get (cur_house
);
1534 SetTileSelectSize ((hs
->building_flags
& BUILDING_2_TILES_X
) ? 2 : 1,
1535 (hs
->building_flags
& BUILDING_2_TILES_Y
) ? 2 : 1);
1537 /* Cache house name. */
1538 this->name
= hs
->building_name
;
1539 uint16 callback_res
= GetHouseCallback (CBID_HOUSE_CUSTOM_NAME
, 1, 0, cur_house
);
1540 if (callback_res
!= CALLBACK_FAILED
&& callback_res
!= 0x400) {
1541 uint32 grfid
= hs
->grf_prop
.grffile
->grfid
;
1542 if (callback_res
> 0x400) {
1543 ErrorUnknownCallbackResult (grfid
, CBID_HOUSE_CUSTOM_NAME
, callback_res
);
1545 StringID ret
= GetGRFStringID (grfid
, 0xD000 + callback_res
);
1546 if (ret
!= STR_NULL
&& ret
!= STR_UNDEFINED
) this->name
= ret
;
1550 /* Cache house production. */
1551 if (HasBit(hs
->callback_mask
, CBM_HOUSE_PRODUCE_CARGO
)) {
1552 CargoArray production
;
1553 for (uint i
= 0; i
< 256; i
++) {
1554 uint16 callback
= GetHouseCallback (CBID_HOUSE_PRODUCE_CARGO
, i
, 0, cur_house
);
1555 if (callback
== CALLBACK_FAILED
|| callback
== CALLBACK_HOUSEPRODCARGO_END
) break;
1556 CargoID c
= GetCargoTranslation (GB(callback
, 8, 7), hs
->grf_prop
.grffile
);
1557 if (c
!= CT_INVALID
) production
[c
]++;
1560 for (CargoID i
= 0; i
< NUM_CARGO
; i
++) if (production
[i
] != 0) SetBit (mask
, i
);
1561 this->supply
= mask
;
1564 if (hs
->population
> 0) mask
|= (1 << CT_PASSENGERS
);
1565 if (hs
->mail_generation
> 0) mask
|= (1 << CT_MAIL
);
1566 this->supply
= mask
;
1569 /* Cache house acceptance. */
1571 GetAcceptedCargo (&cargo
, cur_house
);
1573 stringb
buf (this->acceptance
);
1575 for (CargoID i
= 0; i
< NUM_CARGO
; ++i
) {
1576 if (cargo
[i
] == 0) continue;
1578 /* Add a comma between each item. */
1579 if (!buf
.empty()) buf
.append (", ");
1581 /* If the accepted value is less than 8, show it in 1/8s. */
1583 SetDParam (0, cargo
[i
]);
1584 SetDParam (1, CargoSpec::Get(i
)->name
);
1585 AppendString (&buf
, STR_LAND_AREA_INFORMATION_CARGO_EIGHTS
);
1587 AppendString (&buf
, CargoSpec::Get(i
)->name
);
1591 if (buf
.empty()) AppendString (&buf
, STR_JUST_NOTHING
);
1595 HousePickerWindow (const WindowDesc
*desc
, WindowNumber number
) :
1596 Window (desc
), houses(), sets(), sel_set (0), sel_offset (0),
1597 name (STR_NULL
), supply (0), line_height (0)
1599 memset (this->acceptance
, 0, sizeof(this->acceptance
));
1601 this->CreateNestedTree();
1602 /* there is no shade box but we will shade the window if there is no house to show */
1603 this->shade_select
= this->GetWidget
<NWidgetStacked
> (WID_HP_MAIN_PANEL_SEL
);
1604 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
> (WID_HP_HOUSE_SELECT_MATRIX
);
1605 matrix
->SetScrollbar (this->GetScrollbar (WID_HP_HOUSE_SELECT_SCROLL
));
1606 this->InitNested (number
);
1608 if (cur_house
!= INVALID_HOUSE_ID
) {
1609 matrix
->SetClicked (this->sel_offset
); // set clicked item again to make it visible
1610 this->SetObjectToPlace();
1616 void OnInit() OVERRIDE
1618 /* Rebuild house list. */
1620 this->houses
.clear();
1622 /* Collect houses. */
1623 for (HouseID house
= 0; house
< NUM_HOUSES
; house
++) {
1624 const HouseSpec
*hs
= HouseSpec::Get(house
);
1626 if (!hs
->enabled
) continue;
1627 if (hs
->grf_prop
.override
!= INVALID_HOUSE_ID
) continue;
1629 uint landscape
= _settings_game
.game_creation
.landscape
;
1630 uint mask
= (landscape
!= LT_ARCTIC
) ? (HZ_TEMP
<< landscape
) :
1631 (HZ_SUBARTC_ABOVE
| HZ_SUBARTC_BELOW
);
1632 HouseZones availability
= hs
->building_availability
;
1633 if ((availability
& mask
) == 0) continue;
1634 if ((availability
& HZ_ZONALL
) == 0) continue;
1636 this->houses
.push_back (house
);
1639 if (this->houses
.empty()) {
1640 cur_house
= INVALID_HOUSE_ID
;
1641 /* Hide widgets if we have no houses to show. */
1642 this->SetShaded (true);
1644 std::sort (this->houses
.begin(), this->houses
.end());
1646 this->BuildSetList();
1648 this->SetShaded (false);
1650 /* Show the list of house sets if we have at least 2 items to show. */
1651 this->GetWidget
<NWidgetStacked
> (WID_HP_HOUSE_SETS_SEL
)->SetDisplayedPlane (this->sets
.size() > 1 ? 0 : SZSP_NONE
);
1652 /* Set the number of items in the list of house sets. */
1653 this->GetWidget
<NWidgetCore
> (WID_HP_HOUSE_SETS
)->widget_data
= (this->sets
.size() << MAT_ROW_START
) | (1 << MAT_COL_START
);
1654 /* Show the landscape info only in arctic climate (above/below snowline). */
1655 this->GetWidget
<NWidgetStacked
> (WID_HP_HOUSE_LANDSCAPE_SEL
)->SetDisplayedPlane (_settings_game
.game_creation
.landscape
== LT_ARCTIC
? 0 : SZSP_NONE
);
1656 /* Update the matrix of houses. */
1657 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
> (WID_HP_HOUSE_SELECT_MATRIX
);
1658 matrix
->SetCount (GetSetSize (this->sel_set
));
1659 matrix
->SetClicked (this->sel_offset
);
1660 cur_house
= this->houses
[this->sets
[this->sel_set
] + this->sel_offset
];
1663 this->UpdateCache();
1665 /* If we have exactly one set of houses and it is not the default one then display its name in the title bar. */
1666 this->GetWidget
<NWidgetCore
> (WID_HP_CAPTION
)->widget_data
=
1667 (this->sets
.size() == 1 && HouseSpec::Get (this->houses
[0])->grf_prop
.grffile
!= NULL
) ?
1668 STR_BUILD_HOUSE_CUSTOM_CAPTION
: STR_BUILD_HOUSE_CAPTION
;
1671 const GRFFile
*GetGRFFileOfSet (uint set
) const
1673 assert (set
< this->sets
.size());
1674 return HouseSpec::Get (this->houses
[this->sets
[set
]])->grf_prop
.grffile
;
1677 static const char *GetNameOfSet (const GRFFile
*grffile
)
1679 return GetGRFConfig (grffile
->grfid
)->GetName();
1682 void SetStringParameters (int widget
) const OVERRIDE
1685 case WID_HP_CAPTION
:
1686 if (this->sets
.size() == 1) {
1687 const GRFFile
*grffile
= GetGRFFileOfSet (0);
1688 if (grffile
!= NULL
) {
1689 SetDParamStr (0, GetNameOfSet (grffile
));
1694 case WID_HP_HOUSE_NAME
: {
1695 SetDParam (0, this->name
);
1699 case WID_HP_HISTORICAL_BUILDING
:
1700 SetDParam (0, ((cur_house
!= INVALID_HOUSE_ID
) && (HouseSpec::Get (cur_house
)->extra_flags
& BUILDING_IS_HISTORICAL
) != 0) ? STR_BUILD_HOUSE_HISTORICAL_BUILDING
: STR_EMPTY
);
1703 case WID_HP_HOUSE_POPULATION
:
1704 SetDParam (0, (cur_house
!= INVALID_HOUSE_ID
) ? HouseSpec::Get (cur_house
)->population
: 0);
1707 case WID_HP_HOUSE_ZONES
: {
1708 HouseZones zones
= (cur_house
!= INVALID_HOUSE_ID
) ? HouseSpec::Get(cur_house
)->building_availability
: HZ_NOZNS
;
1709 for (int i
= 0; i
< HZB_END
; i
++) {
1710 SetDParam (i
, HasBit (zones
, HZB_END
- 1 - i
) ? STR_BUILD_HOUSE_ZONE_ENABLED
: STR_BUILD_HOUSE_ZONE_DISABLED
);
1715 case WID_HP_HOUSE_LANDSCAPE
: {
1717 if (cur_house
!= INVALID_HOUSE_ID
) {
1718 switch (HouseSpec::Get(cur_house
)->building_availability
& (HZ_SUBARTC_ABOVE
| HZ_SUBARTC_BELOW
)) {
1719 case HZ_SUBARTC_ABOVE
: info
= STR_BUILD_HOUSE_ABOVE_SNOWLINE
; break;
1720 case HZ_SUBARTC_BELOW
: info
= STR_BUILD_HOUSE_BELOW_SNOWLINE
; break;
1721 default: info
= STR_BUILD_HOUSE_ABOVE_OR_BELOW_SNOWLINE
; break;
1726 SetDParam (0, info
);
1730 case WID_HP_HOUSE_YEARS
: {
1731 if (cur_house
!= INVALID_HOUSE_ID
) {
1732 const HouseSpec
*hs
= HouseSpec::Get (cur_house
);
1733 SetDParam (0, hs
->min_year
<= _cur_year
? STR_BUILD_HOUSE_GOOD_YEAR
: STR_BUILD_HOUSE_BAD_YEAR
);
1734 SetDParam (1, hs
->min_year
);
1735 SetDParam (2, hs
->max_year
>= _cur_year
? STR_BUILD_HOUSE_GOOD_YEAR
: STR_BUILD_HOUSE_BAD_YEAR
);
1736 SetDParam (3, hs
->max_year
);
1738 SetDParam (0, STR_EMPTY
);
1740 SetDParam (2, STR_EMPTY
);
1746 case WID_HP_HOUSE_ACCEPTANCE
:
1747 SetDParamStr (0, this->acceptance
);
1750 case WID_HP_HOUSE_SUPPLY
:
1751 SetDParam (0, this->supply
);
1758 void UpdateWidgetSize (int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
) OVERRIDE
1761 case WID_HP_HOUSE_SETS
: {
1763 for (uint i
= 0; i
< this->sets
.size(); i
++) {
1764 const GRFFile
*grffile
= this->GetGRFFileOfSet (i
);
1765 uint w
= (grffile
!= NULL
) ?
1766 GetStringBoundingBox (GetNameOfSet (grffile
)).width
:
1767 GetStringBoundingBox (STR_BUILD_HOUSE_ORIGINAL_SET_NAME
).width
;
1768 max_w
= max (max_w
, w
);
1770 size
->width
= max (size
->width
, max_w
+ padding
.width
);
1771 this->line_height
= FONT_HEIGHT_NORMAL
+ WD_MATRIX_TOP
+ WD_MATRIX_BOTTOM
;
1772 size
->height
= this->sets
.size() * this->line_height
;
1776 case WID_HP_HOUSE_PREVIEW
:
1777 size
->width
= ScaleGUITrad (2 * TILE_PIXELS
);
1778 size
->height
= ScaleGUITrad (142);
1781 case WID_HP_HOUSE_NAME
:
1782 size
->width
= 120; // we do not want this window to get too wide, better clip
1785 case WID_HP_HISTORICAL_BUILDING
:
1786 size
->width
= max (size
->width
, GetStringBoundingBox (STR_BUILD_HOUSE_HISTORICAL_BUILDING
).width
+ padding
.width
);
1789 case WID_HP_HOUSE_POPULATION
:
1790 /* Max popultion is 255 - 3 digits */
1791 SetDParamMaxDigits (0, 3);
1792 size
->width
= max (size
->width
, GetStringBoundingBox (STR_BUILD_HOUSE_POPULATION
).width
+ padding
.width
);
1795 case WID_HP_HOUSE_ZONES
: {
1796 for (int i
= 0; i
< HZB_END
; i
++) {
1797 SetDParam (2 * i
, STR_BUILD_HOUSE_ZONE_ENABLED
); // colour
1798 SetDParam (2 * i
+ 1, i
+ 1); // digit: 1(center)/2/3/4/5(edge)
1800 size
->width
= max (size
->width
, GetStringBoundingBox (STR_BUILD_HOUSE_ZONES
).width
+ padding
.width
);
1804 case WID_HP_HOUSE_LANDSCAPE
: {
1805 SetDParam (0, STR_BUILD_HOUSE_ABOVE_OR_BELOW_SNOWLINE
);
1806 Dimension dim
= GetStringBoundingBox (STR_BUILD_HOUSE_LANDSCAPE
);
1807 SetDParam (0, STR_BUILD_HOUSE_ABOVE_SNOWLINE
);
1808 dim
= maxdim (dim
, GetStringBoundingBox (STR_BUILD_HOUSE_LANDSCAPE
));
1809 SetDParam (0, STR_BUILD_HOUSE_BELOW_SNOWLINE
);
1810 dim
= maxdim (dim
, GetStringBoundingBox (STR_BUILD_HOUSE_LANDSCAPE
));
1811 dim
.width
+= padding
.width
;
1812 dim
.height
+= padding
.height
;
1813 *size
= maxdim (*size
, dim
);
1817 case WID_HP_HOUSE_YEARS
: {
1818 SetDParam (0, STR_BUILD_HOUSE_GOOD_YEAR
);
1819 SetDParamMaxDigits (1, 8);
1820 SetDParam (2, STR_BUILD_HOUSE_GOOD_YEAR
);
1821 SetDParamMaxDigits (3, 8);
1822 Dimension dim
= GetStringBoundingBox (STR_BUILD_HOUSE_YEARS
);
1823 dim
.width
+= padding
.width
;
1824 dim
.height
+= padding
.height
;
1825 *size
= maxdim (*size
, dim
);
1829 case WID_HP_HOUSE_SELECT_MATRIX
:
1830 resize
->height
= 1; // don't snap to rows of this matrix
1833 case WID_HP_HOUSE_SELECT
:
1834 size
->width
= ScaleGUITrad(64) + 2;
1835 size
->height
= ScaleGUITrad(58) + 2;
1838 /* these texts can be long, better clip */
1839 case WID_HP_HOUSE_ACCEPTANCE
:
1840 case WID_HP_HOUSE_SUPPLY
:
1848 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
1850 switch (GB(widget
, 0, 16)) {
1851 case WID_HP_HOUSE_SETS
: {
1852 int y
= r
.top
+ WD_MATRIX_TOP
;
1853 for (uint i
= 0; i
< this->sets
.size(); i
++) {
1854 const GRFFile
*grffile
= this->GetGRFFileOfSet (i
);
1856 if (grffile
!= NULL
) {
1857 SetDParamStr (0, GetNameOfSet (grffile
));
1858 str
= STR_JUST_RAW_STRING
;
1860 str
= STR_BUILD_HOUSE_ORIGINAL_SET_NAME
;
1862 DrawString (dpi
, r
.left
+ WD_MATRIX_LEFT
, r
.right
- WD_MATRIX_RIGHT
, y
, str
, i
== this->sel_set
? TC_WHITE
: TC_BLACK
);
1863 y
+= this->line_height
;
1868 case WID_HP_HOUSE_PREVIEW
:
1869 if (cur_house
!= INVALID_HOUSE_ID
) {
1870 DrawHouseImage (cur_house
, dpi
, r
.left
, r
.top
, r
.right
, r
.bottom
);
1874 case WID_HP_HOUSE_SELECT
: {
1875 HouseID house
= this->houses
[this->sets
[this->sel_set
] + GB(widget
, 16, 16)];
1876 DrawHouseImage (house
, dpi
,
1877 r
.left
+ WD_MATRIX_LEFT
, r
.top
+ WD_MATRIX_TOP
,
1878 r
.right
- WD_MATRIX_RIGHT
, r
.bottom
- WD_MATRIX_BOTTOM
);
1884 void OnClick (Point pt
, int widget
, int click_count
) OVERRIDE
1886 switch (GB(widget
, 0, 16)) {
1887 case WID_HP_HOUSE_SETS
: {
1888 uint index
= (uint
)(pt
.y
- this->GetWidget
<NWidgetBase
>(widget
)->pos_y
) / this->line_height
;
1889 if (index
< this->sets
.size() && index
!= this->sel_set
) {
1890 this->SetObjectToPlace();
1891 cur_house
= this->houses
[this->sets
[index
]];
1892 this->sel_set
= index
;
1893 this->sel_offset
= 0;
1895 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
>(WID_HP_HOUSE_SELECT_MATRIX
);
1896 matrix
->SetCount (GetSetSize (index
));
1897 matrix
->SetClicked (0);
1898 this->UpdateCache();
1904 case WID_HP_HOUSE_SELECT
: {
1905 uint index
= GB(widget
, 16, 16);
1906 if (index
!= this->sel_offset
) {
1907 this->SetObjectToPlace();
1908 cur_house
= this->houses
[this->sets
[this->sel_set
] + index
];
1909 this->sel_offset
= index
;
1911 NWidgetMatrix
*matrix
= this->GetWidget
<NWidgetMatrix
>(WID_HP_HOUSE_SELECT_MATRIX
);
1912 matrix
->SetClicked (index
);
1913 this->UpdateCache();
1921 void OnPlaceObject (Point pt
, TileIndex tile
) OVERRIDE
;
1923 void OnPlaceObjectAbort (void) OVERRIDE
1925 this->sel_offset
= -1;
1926 this->GetWidget
<NWidgetMatrix
>(WID_HP_HOUSE_SELECT_MATRIX
)->SetClicked (-1);
1931 static StringID
CheckPlaceHouse (TileIndex tile
, HouseID house
, Town
*town
)
1933 const HouseSpec
*hs
= HouseSpec::Get (house
);
1935 int z
= GetTileMaxZ (tile
);
1937 if (_settings_game
.game_creation
.landscape
== LT_ARCTIC
) {
1938 bool above_snowline
= z
> HighestSnowLine();
1939 HouseZones mask
= above_snowline
? HZ_SUBARTC_ABOVE
: HZ_SUBARTC_BELOW
;
1940 if ((hs
->building_availability
& mask
) == 0) {
1941 return above_snowline
?
1942 STR_ERROR_BUILDING_NOT_ALLOWED_ABOVE_SNOW_LINE
:
1943 STR_ERROR_BUILDING_NOT_ALLOWED_BELOW_SNOW_LINE
;
1948 StringID err
= IsNewTownHouseAllowed (town
, house
);
1949 if (err
!= STR_NULL
) return err
;
1953 if (hs
->building_flags
& BUILDING_2_TILES_X
) ta
.w
++;
1954 if (hs
->building_flags
& BUILDING_2_TILES_Y
) ta
.h
++;
1956 bool noslope
= (hs
->building_flags
& TILE_NOT_SLOPED
) != 0;
1958 TILE_AREA_LOOP(test
, ta
) {
1960 if (!IsTileFlat (test
)) {
1961 return STR_ERROR_FLAT_LAND_REQUIRED
;
1964 if (IsSteepSlope (GetTileSlope (test
)) || (GetTileMaxZ (test
) != z
)) {
1965 return STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
;
1969 if (HasBridgeAbove (test
)) {
1970 return STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
;
1973 CommandCost clear
= DoCommand (test
, 0, 0, DC_AUTO
| DC_NO_WATER
, CMD_LANDSCAPE_CLEAR
);
1974 if (clear
.Failed()) return clear
.GetErrorMessage();
1980 void HousePickerWindow::OnPlaceObject (Point pt
, TileIndex tile
)
1982 HouseID house
= cur_house
;
1983 if (house
== INVALID_HOUSE_ID
) return;
1986 if (!_ctrl_pressed
) {
1987 /* Add the house to the closest town. */
1988 Town
*town
= CalcClosestTownFromTile (tile
);
1990 err
= STR_ERROR_MUST_FOUND_TOWN_FIRST
;
1992 err
= CheckPlaceHouse (tile
, house
, town
);
1993 if (err
== STR_NULL
) {
1994 DoBuildHouse (town
, tile
, house
, InteractiveRandom());
1999 /* Show a list of towns to join. */
2000 if (Town::pool
.items
== 0) {
2001 err
= STR_ERROR_MUST_FOUND_TOWN_FIRST
;
2003 err
= CheckPlaceHouse (tile
, house
, NULL
);
2004 if (err
== STR_NULL
) {
2005 DeleteWindowByClass (WC_SELECT_TOWN
);
2006 new SelectTownWindow (&_select_town_desc
, tile
, house
);
2012 ShowErrorMessage (STR_ERROR_CAN_T_BUILD_HOUSE_HERE
, err
,
2013 WL_INFO
, pt
.x
, pt
.y
);
2016 static const NWidgetPart _nested_house_picker_widgets
[] = {
2017 NWidget(NWID_HORIZONTAL
),
2018 NWidget(WWT_CLOSEBOX
, COLOUR_DARK_GREEN
),
2019 NWidget(WWT_CAPTION
, COLOUR_DARK_GREEN
, WID_HP_CAPTION
), SetDataTip(STR_BUILD_HOUSE_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
2020 NWidget(WWT_DEFSIZEBOX
, COLOUR_DARK_GREEN
),
2022 NWidget(NWID_SELECTION
, COLOUR_DARK_GREEN
, WID_HP_MAIN_PANEL_SEL
),
2023 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL
),
2024 NWidget(NWID_HORIZONTAL
), SetPIP(5, 0, 0),
2025 NWidget(NWID_VERTICAL
), SetPIP(5, 2, 2),
2026 /* List of house sets */
2027 NWidget(NWID_SELECTION
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_SETS_SEL
),
2028 NWidget(NWID_HORIZONTAL
),
2029 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_HP_HOUSE_SETS
), SetMinimalSize(0, 60), SetFill(1, 0), SetResize(0, 0),
2030 SetMatrixDataTip(1, 1, STR_BUILD_HOUSE_HOUSESET_LIST_TOOLTIP
),
2033 /* House picture and label */
2034 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_PREVIEW
), SetFill(1, 1), SetResize(0, 1), SetMinimalSize(2 * TILE_PIXELS
, 142), SetPadding(5, 0, 5, 0),
2035 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_NAME
), SetDataTip(STR_BUILD_HOUSE_NAME
, STR_NULL
), SetMinimalSize(120, 0),
2036 NWidget(WWT_LABEL
, COLOUR_DARK_GREEN
, WID_HP_HISTORICAL_BUILDING
), SetDataTip(STR_JUST_STRING
, STR_NULL
),
2037 /* House info (short) */
2038 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_POPULATION
), SetDataTip(STR_BUILD_HOUSE_POPULATION
, STR_NULL
), SetPadding(5, 0, 0, 0),
2039 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_ZONES
), SetDataTip(STR_BUILD_HOUSE_ZONES
, STR_NULL
),
2040 NWidget(NWID_SELECTION
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_LANDSCAPE_SEL
),
2041 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_LANDSCAPE
), SetDataTip(STR_BUILD_HOUSE_LANDSCAPE
, STR_NULL
),
2043 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_YEARS
), SetDataTip(STR_BUILD_HOUSE_YEARS
, STR_NULL
),
2046 NWidget(NWID_MATRIX
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_SELECT_MATRIX
), SetPIP(0, 2, 0), SetPadding(2, 2, 2, 2), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL
),
2047 NWidget(WWT_PANEL
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_SELECT
), SetMinimalSize(64, 64), SetFill(0, 0), SetResize(0, 0),
2048 SetDataTip(0x0, STR_BUILD_HOUSE_SELECT_HOUSE_TOOLTIP
), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL
),
2051 NWidget(NWID_VSCROLLBAR
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_SELECT_SCROLL
),
2053 NWidget(NWID_HORIZONTAL
), SetPIP(5, 2, 0),
2054 /* House info (long) */
2055 NWidget(NWID_VERTICAL
), SetPIP(0, 2, 5),
2056 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_ACCEPTANCE
), SetDataTip(STR_BUILD_HOUSE_ACCEPTED_CARGO
, STR_NULL
), SetFill(1, 0), SetResize(1, 0),
2057 NWidget(WWT_TEXT
, COLOUR_DARK_GREEN
, WID_HP_HOUSE_SUPPLY
), SetDataTip(STR_BUILD_HOUSE_SUPPLIED_CARGO
, STR_NULL
), SetFill(1, 0), SetResize(1, 0),
2060 NWidget(NWID_VERTICAL
),
2061 NWidget(NWID_SPACER
), SetFill(0, 1),
2062 NWidget(WWT_RESIZEBOX
, COLOUR_DARK_GREEN
),
2069 static WindowDesc::Prefs
_house_picker_prefs ("build_house");
2071 static const WindowDesc
_house_picker_desc(
2073 WC_BUILD_HOUSE
, WC_BUILD_TOOLBAR
,
2075 _nested_house_picker_widgets
, lengthof(_nested_house_picker_widgets
),
2076 &_house_picker_prefs
2079 HouseID
HousePickerWindow::cur_house
= INVALID_HOUSE_ID
;
2082 * Show our house picker.
2083 * @param parent The toolbar window we're associated with.
2085 void ShowBuildHousePicker (void)
2087 AllocateWindowDescFront
<HousePickerWindow
> (&_house_picker_desc
, 0);