40
72
DPIW_RESIZE_WIDGET,
43
/** Attached struct to the window extended data */
45
int index; ///< index of the element in the matrix
46
IndustryType select; ///< industry corresponding to the above index
47
uint16 callback_timer; ///< timer counter for callback eventual verification
48
bool timer_enabled; ///< timer can be used
75
/** Widget definition of the dynamic place industries gui */
76
static const Widget _build_industry_widgets[] = {
77
{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_DARK_GREEN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // DPIW_CLOSEBOX
78
{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_DARK_GREEN, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS}, // DPIW_CAPTION
79
{ WWT_MATRIX, RESIZE_RB, COLOUR_DARK_GREEN, 0, 157, 14, 118, 0x801, STR_INDUSTRY_SELECTION_HINT}, // DPIW_MATRIX_WIDGET
80
{ WWT_SCROLLBAR, RESIZE_LRB, COLOUR_DARK_GREEN, 158, 169, 14, 118, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DPIW_SCROLLBAR
81
{ WWT_PANEL, RESIZE_RTB, COLOUR_DARK_GREEN, 0, 169, 119, 199, 0x0, STR_NULL}, // DPIW_INFOPANEL
82
{ WWT_TEXTBTN, RESIZE_RTB, COLOUR_DARK_GREEN, 0, 157, 200, 211, STR_FUND_NEW_INDUSTRY, STR_NULL}, // DPIW_FUND_WIDGET
83
{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_DARK_GREEN, 158, 169, 200, 211, 0x0, STR_RESIZE_BUTTON}, // DPIW_RESIZE_WIDGET
50
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(fnd_d));
52
/** Helper struct holding the available industries for current situation */
53
static struct IndustryData {
87
/** Window definition of the dynamic place industries gui */
88
static const WindowDesc _build_industry_desc(
89
WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
90
WC_BUILD_INDUSTRY, WC_NONE,
91
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE | WDF_CONSTRUCTION,
92
_build_industry_widgets
95
class BuildIndustryWindow : public Window {
96
int selected_index; ///< index of the element in the matrix
97
IndustryType selected_type; ///< industry corresponding to the above index
98
uint16 callback_timer; ///< timer counter for callback eventual verification
99
bool timer_enabled; ///< timer can be used
54
100
uint16 count; ///< How many industries are loaded
55
101
IndustryType index[NUM_INDUSTRYTYPES + 1]; ///< Type of industry, in the order it was loaded
56
102
StringID text[NUM_INDUSTRYTYPES + 1]; ///< Text coming from CBM_IND_FUND_MORE_TEXT (if ever)
57
103
bool enabled[NUM_INDUSTRYTYPES + 1]; ///< availability state, coming from CBID_INDUSTRY_AVAILABLE (if ever)
60
assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.text));
61
assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.enabled));
63
static void SetupFundArrays(Window *w)
66
const IndustrySpec *indsp;
70
for (uint i = 0; i < lengthof(_fund_gui.index); i++) {
71
_fund_gui.index[i] = INVALID_INDUSTRYTYPE;
72
_fund_gui.text[i] = STR_NULL;
73
_fund_gui.enabled[i] = false;
76
if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
77
_fund_gui.index[_fund_gui.count] = INVALID_INDUSTRYTYPE;
79
WP(w, fnd_d).timer_enabled = false;
81
/* Fill the arrays with industries.
82
* The tests performed after the enabled allow to load the industries
83
* In the same way they are inserted by grf (if any)
85
for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
86
indsp = GetIndustrySpec(ind);
88
/* Rule is that editor mode loads all industries.
89
* In game mode, all non raw industries are loaded too
90
* and raw ones are loaded only when setting allows it */
91
if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) {
92
/* Unselect if the industry is no longer in the list */
93
if (WP(w, fnd_d).select == ind) WP(w, fnd_d).index = -1;
108
const IndustrySpec *indsp;
112
for (uint i = 0; i < lengthof(this->index); i++) {
113
this->index[i] = INVALID_INDUSTRYTYPE;
114
this->text[i] = STR_NULL;
115
this->enabled[i] = false;
118
if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
119
this->index[this->count] = INVALID_INDUSTRYTYPE;
121
this->timer_enabled = false;
123
/* Fill the arrays with industries.
124
* The tests performed after the enabled allow to load the industries
125
* In the same way they are inserted by grf (if any)
127
for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
128
indsp = GetIndustrySpec(ind);
130
/* Rule is that editor mode loads all industries.
131
* In game mode, all non raw industries are loaded too
132
* and raw ones are loaded only when setting allows it */
133
if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) {
134
/* Unselect if the industry is no longer in the list */
135
if (this->selected_type == ind) this->selected_index = -1;
138
this->index[this->count] = ind;
139
this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
140
/* Keep the selection to the correct line */
141
if (this->selected_type == ind) this->selected_index = this->count;
146
/* first indutry type is selected if the current selection is invalid.
147
* I'll be damned if there are none available ;) */
148
if (this->selected_index == -1) {
149
this->selected_index = 0;
150
this->selected_type = this->index[0];
155
BuildIndustryWindow() : Window(&_build_industry_desc)
157
/* Shorten the window to the equivalant of the additionnal purchase
158
* info coming from the callback. SO it will only be available to its full
159
* height when newindistries are loaded */
160
if (!_loaded_newgrf_features.has_newindustries) {
161
this->widget[DPIW_INFOPANEL].bottom -= 44;
162
this->widget[DPIW_FUND_WIDGET].bottom -= 44;
163
this->widget[DPIW_FUND_WIDGET].top -= 44;
164
this->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
165
this->widget[DPIW_RESIZE_WIDGET].top -= 44;
166
this->resize.height = this->height -= 44;
169
this->timer_enabled = _loaded_newgrf_features.has_newindustries;
171
this->vscroll.cap = 8; // rows in grid, same in scroller
172
this->resize.step_height = 13;
174
this->selected_index = -1;
175
this->selected_type = INVALID_INDUSTRYTYPE;
177
/* Initialize arrays */
180
this->callback_timer = DAY_TICKS;
182
this->FindWindowPlacementAndResize(&_build_industry_desc);
185
virtual void OnPaint()
187
const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
188
int x_str = this->widget[DPIW_INFOPANEL].left + 3;
189
int y_str = this->widget[DPIW_INFOPANEL].top + 3;
190
const Widget *wi = &this->widget[DPIW_INFOPANEL];
191
int max_width = wi->right - wi->left - 4;
193
/* Raw industries might be prospected. Show this fact by changing the string
194
* In Editor, you just build, while ingame, or you fund or you prospect */
195
if (_game_mode == GM_EDITOR) {
196
/* We've chosen many random industries but no industries have been specified */
197
if (indsp == NULL) this->enabled[this->selected_index] = _settings_game.difficulty.number_industries != 0;
198
this->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
200
this->widget[DPIW_FUND_WIDGET].data = (_settings_game.construction.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
202
this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]);
204
SetVScrollCount(this, this->count);
208
/* and now with the matrix painting */
209
for (byte i = 0; i < this->vscroll.cap && ((i + this->vscroll.pos) < this->count); i++) {
213
bool selected = this->selected_index == i + this->vscroll.pos;
215
if (this->index[i + this->vscroll.pos] == INVALID_INDUSTRYTYPE) {
216
DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
96
_fund_gui.index[_fund_gui.count] = ind;
97
_fund_gui.enabled[_fund_gui.count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
98
/* Keep the selection to the correct line */
99
if (WP(w, fnd_d).select == ind) WP(w, fnd_d).index = _fund_gui.count;
104
/* first indutry type is selected if the current selection is invalid.
105
* I'll be damned if there are none available ;) */
106
if (WP(w, fnd_d).index == -1) {
107
WP(w, fnd_d).index = 0;
108
WP(w, fnd_d).select = _fund_gui.index[0];
112
static void BuildDynamicIndustryWndProc(Window *w, WindowEvent *e)
116
/* Shorten the window to the equivalant of the additionnal purchase
117
* info coming from the callback. SO it will only be available to tis full
118
* height when newindistries are loaded */
119
if (!_loaded_newgrf_features.has_newindustries) {
120
w->widget[DPIW_INFOPANEL].bottom -= 44;
121
w->widget[DPIW_FUND_WIDGET].bottom -= 44;
122
w->widget[DPIW_FUND_WIDGET].top -= 44;
123
w->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
124
w->widget[DPIW_RESIZE_WIDGET].top -= 44;
125
w->resize.height = w->height -= 44;
128
WP(w, fnd_d).timer_enabled = _loaded_newgrf_features.has_newindustries;
130
w->vscroll.cap = 8; // rows in grid, same in scroller
131
w->resize.step_height = 13;
133
WP(w, fnd_d).index = -1;
134
WP(w, fnd_d).select = INVALID_INDUSTRYTYPE;
136
/* Initialize arrays */
139
WP(w, fnd_d).callback_timer = DAY_TICKS;
143
const IndustrySpec *indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
144
int x_str = w->widget[DPIW_INFOPANEL].left + 3;
145
int y_str = w->widget[DPIW_INFOPANEL].top + 3;
146
const Widget *wi = &w->widget[DPIW_INFOPANEL];
147
int max_width = wi->right - wi->left - 4;
149
/* Raw industries might be prospected. Show this fact by changing the string
150
* In Editor, you just build, while ingame, or you fund or you prospect */
151
if (_game_mode == GM_EDITOR) {
152
/* We've chosen many random industries but no industries have been specified */
153
if (indsp == NULL) _fund_gui.enabled[WP(w, fnd_d).index] = _opt.diff.number_industries != 0;
154
w->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
156
w->widget[DPIW_FUND_WIDGET].data = (_patches.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
158
w->SetWidgetDisabledState(DPIW_FUND_WIDGET, !_fund_gui.enabled[WP(w, fnd_d).index]);
160
SetVScrollCount(w, _fund_gui.count);
162
DrawWindowWidgets(w);
164
/* and now with the matrix painting */
165
for (byte i = 0; i < w->vscroll.cap && ((i + w->vscroll.pos) < _fund_gui.count); i++) {
169
bool selected = WP(w, fnd_d).index == i + w->vscroll.pos;
171
if (_fund_gui.index[i + w->vscroll.pos] == INVALID_INDUSTRYTYPE) {
172
DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
175
const IndustrySpec *indsp = GetIndustrySpec(_fund_gui.index[i + w->vscroll.pos]);
177
/* Draw the name of the industry in white is selected, otherwise, in orange */
178
DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
179
GfxFillRect(x, y + 1 + offset, x + 10, y + 7 + offset, selected ? 15 : 0);
180
GfxFillRect(x + 1, y + 2 + offset, x + 9, y + 6 + offset, indsp->map_colour);
183
if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
184
DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
188
if (_game_mode != GM_EDITOR) {
189
SetDParam(0, indsp->GetConstructionCost());
190
DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
194
/* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
195
StringID str = STR_4827_REQUIRES;
197
SetDParam(0, STR_00D0_NOTHING);
198
for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
199
if (indsp->accepts_cargo[j] == CT_INVALID) continue;
201
SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
203
DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
206
/* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
207
str = STR_4827_PRODUCES;
209
SetDParam(0, STR_00D0_NOTHING);
210
for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
211
if (indsp->produced_cargo[j] == CT_INVALID) continue;
213
SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
215
DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
218
/* Get the additional purchase info text, if it has not already been */
219
if (_fund_gui.text[WP(w, fnd_d).index] == STR_NULL) { // Have i been called already?
220
if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) { // No. Can it be called?
221
uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, WP(w, fnd_d).select, INVALID_TILE);
222
if (callback_res != CALLBACK_FAILED) { // Did it failed?
223
StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
224
_fund_gui.text[WP(w, fnd_d).index] = newtxt; // Store it for further usage
229
/* Draw the Additional purchase text, provided by newgrf callback, if any.
230
* Otherwhise, will print Nothing */
231
str = _fund_gui.text[WP(w, fnd_d).index];
232
if (str != STR_NULL && str != STR_UNDEFINED) {
234
DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
238
case WE_DOUBLE_CLICK:
239
if (e->we.click.widget != DPIW_MATRIX_WIDGET) break;
240
e->we.click.widget = DPIW_FUND_WIDGET;
244
switch (e->we.click.widget) {
245
case DPIW_MATRIX_WIDGET: {
246
const IndustrySpec *indsp;
247
int y = (e->we.click.pt.y - w->widget[DPIW_MATRIX_WIDGET].top) / 13 + w->vscroll.pos ;
249
if (y >= 0 && y < _fund_gui.count) { // Is it within the boundaries of available data?
250
WP(w, fnd_d).index = y;
251
WP(w, fnd_d).select = _fund_gui.index[WP(w, fnd_d).index];
252
indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
256
if ((_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
257
WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
258
/* Reset the button state if going to prospecting or "build many industries" */
260
ResetObjectToPlace();
265
case DPIW_FUND_WIDGET: {
266
if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
267
w->HandleButtonClick(DPIW_FUND_WIDGET);
269
if (GetNumTowns() == 0) {
270
ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
272
extern void GenerateIndustries();
273
_generating_world = true;
274
GenerateIndustries();
275
_generating_world = false;
277
} else if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && GetIndustrySpec(WP(w, fnd_d).select)->IsRawIndustry()) {
278
DoCommandP(0, WP(w, fnd_d).select, InteractiveRandom(), NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
279
w->HandleButtonClick(DPIW_FUND_WIDGET);
219
const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll.pos]);
221
/* Draw the name of the industry in white is selected, otherwise, in orange */
222
DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
223
GfxFillRect(x, y + 1 + offset, x + 10, y + 7 + offset, selected ? 15 : 0);
224
GfxFillRect(x + 1, y + 2 + offset, x + 9, y + 6 + offset, indsp->map_colour);
227
if (this->selected_type == INVALID_INDUSTRYTYPE) {
228
DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
232
if (_game_mode != GM_EDITOR) {
233
SetDParam(0, indsp->GetConstructionCost());
234
DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
238
/* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
239
StringID str = STR_4827_REQUIRES;
241
SetDParam(0, STR_00D0_NOTHING);
242
SetDParam(1, STR_EMPTY);
243
for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
244
if (indsp->accepts_cargo[j] == CT_INVALID) continue;
246
SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
247
SetDParam(p++, GetCargoSuffix(j, CST_FUND, NULL, this->selected_type, indsp));
249
DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
252
/* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
253
str = STR_4827_PRODUCES;
255
SetDParam(0, STR_00D0_NOTHING);
256
SetDParam(1, STR_EMPTY);
257
for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
258
if (indsp->produced_cargo[j] == CT_INVALID) continue;
260
SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
261
SetDParam(p++, GetCargoSuffix(j + 3, CST_FUND, NULL, this->selected_type, indsp));
263
DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
266
/* Get the additional purchase info text, if it has not already been */
267
if (this->text[this->selected_index] == STR_NULL) { // Have i been called already?
268
if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) { // No. Can it be called?
269
uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
270
if (callback_res != CALLBACK_FAILED) { // Did it failed?
271
StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res); // No. here's the new string
272
this->text[this->selected_index] = newtxt; // Store it for further usage
277
/* Draw the Additional purchase text, provided by newgrf callback, if any.
278
* Otherwhise, will print Nothing */
279
str = this->text[this->selected_index];
280
if (str != STR_NULL && str != STR_UNDEFINED) {
282
DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
286
virtual void OnDoubleClick(Point pt, int widget)
288
if (widget != DPIW_MATRIX_WIDGET) return;
289
this->OnClick(pt, DPIW_FUND_WIDGET);
292
virtual void OnClick(Point pt, int widget)
295
case DPIW_MATRIX_WIDGET: {
296
const IndustrySpec *indsp;
297
int y = (pt.y - this->widget[DPIW_MATRIX_WIDGET].top) / 13 + this->vscroll.pos ;
299
if (y >= 0 && y < count) { // Is it within the boundaries of available data?
300
this->selected_index = y;
301
this->selected_type = this->index[y];
302
indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
306
if ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
307
this->selected_type == INVALID_INDUSTRYTYPE) {
308
/* Reset the button state if going to prospecting or "build many industries" */
309
this->RaiseButtons();
310
ResetObjectToPlace();
315
case DPIW_FUND_WIDGET: {
316
if (this->selected_type == INVALID_INDUSTRYTYPE) {
317
this->HandleButtonClick(DPIW_FUND_WIDGET);
319
if (GetNumTowns() == 0) {
320
ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
281
HandlePlacePushButton(w, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
288
/* Adjust the number of items in the matrix depending of the rezise */
289
w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
290
w->widget[DPIW_MATRIX_WIDGET].data = (w->vscroll.cap << 8) + 1;
295
/* We do not need to protect ourselves against "Random Many Industries" in this mode */
296
const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
297
uint32 seed = InteractiveRandom();
299
if (_game_mode == GM_EDITOR) {
300
/* Show error if no town exists at all */
301
if (GetNumTowns() == 0) {
302
SetDParam(0, indsp->name);
303
ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
307
_current_player = OWNER_NONE;
308
_generating_world = true;
309
_ignore_restrictions = true;
310
success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
312
SetDParam(0, indsp->name);
313
ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
316
_ignore_restrictions = false;
317
_generating_world = false;
319
success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
322
/* If an industry has been built, just reset the cursor and the system */
323
if (success) ResetObjectToPlace();
327
if (_pause_game != 0) break;
328
if (!WP(w, fnd_d).timer_enabled) break;
329
if (--WP(w, fnd_d).callback_timer == 0) {
330
/* We have just passed another day.
331
* See if we need to update availability of currently selected industry */
332
WP(w, fnd_d).callback_timer = DAY_TICKS; //restart counter
334
const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
336
if (indsp->enabled) {
337
bool call_back_result = CheckIfCallBackAllowsAvailability(WP(w, fnd_d).select, IACT_USERCREATION);
339
/* Only if result does match the previous state would it require a redraw. */
340
if (call_back_result != _fund_gui.enabled[WP(w, fnd_d).index]) {
341
_fund_gui.enabled[WP(w, fnd_d).index] = call_back_result;
349
case WE_ABORT_PLACE_OBJ:
353
case WE_INVALIDATE_DATA:
359
/** Widget definition of the dynamic place industries gui */
360
static const Widget _build_dynamic_industry_widgets[] = {
361
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // DPIW_CLOSEBOX
362
{ WWT_CAPTION, RESIZE_RIGHT, 7, 11, 169, 0, 13, STR_0314_FUND_NEW_INDUSTRY, STR_018C_WINDOW_TITLE_DRAG_THIS}, // DPIW_CAPTION
363
{ WWT_MATRIX, RESIZE_RB, 7, 0, 157, 14, 118, 0x801, STR_INDUSTRY_SELECTION_HINT}, // DPIW_MATRIX_WIDGET
364
{ WWT_SCROLLBAR, RESIZE_LRB, 7, 158, 169, 14, 118, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // DPIW_SCROLLBAR
365
{ WWT_PANEL, RESIZE_RTB, 7, 0, 169, 119, 199, 0x0, STR_NULL}, // DPIW_INFOPANEL
366
{ WWT_TEXTBTN, RESIZE_RTB, 7, 0, 157, 200, 211, STR_FUND_NEW_INDUSTRY, STR_NULL}, // DPIW_FUND_WIDGET
367
{ WWT_RESIZEBOX, RESIZE_LRTB, 7, 158, 169, 200, 211, 0x0, STR_RESIZE_BUTTON}, // DPIW_RESIZE_WIDGET
371
/** Window definition of the dynamic place industries gui */
372
static const WindowDesc _build_industry_dynamic_desc = {
373
WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
374
WC_BUILD_INDUSTRY, WC_NONE,
375
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
376
_build_dynamic_industry_widgets,
377
BuildDynamicIndustryWndProc,
322
extern void GenerateIndustries();
323
_generating_world = true;
324
GenerateIndustries();
325
_generating_world = false;
327
} else if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
328
DoCommandP(0, this->selected_type, InteractiveRandom(), CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
329
this->HandleButtonClick(DPIW_FUND_WIDGET);
331
HandlePlacePushButton(this, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
337
virtual void OnResize(Point new_size, Point delta)
339
/* Adjust the number of items in the matrix depending of the rezise */
340
this->vscroll.cap += delta.y / (int)this->resize.step_height;
341
this->widget[DPIW_MATRIX_WIDGET].data = (this->vscroll.cap << 8) + 1;
344
virtual void OnPlaceObject(Point pt, TileIndex tile)
347
/* We do not need to protect ourselves against "Random Many Industries" in this mode */
348
const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
349
uint32 seed = InteractiveRandom();
351
if (_game_mode == GM_EDITOR) {
352
/* Show error if no town exists at all */
353
if (GetNumTowns() == 0) {
354
SetDParam(0, indsp->name);
355
ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
359
_current_company = OWNER_NONE;
360
_generating_world = true;
361
_ignore_restrictions = true;
362
success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
364
SetDParam(0, indsp->name);
365
ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
368
_ignore_restrictions = false;
369
_generating_world = false;
371
success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
374
/* If an industry has been built, just reset the cursor and the system */
375
if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
378
virtual void OnTick()
380
if (_pause_game != 0) return;
381
if (!this->timer_enabled) return;
382
if (--this->callback_timer == 0) {
383
/* We have just passed another day.
384
* See if we need to update availability of currently selected industry */
385
this->callback_timer = DAY_TICKS; // restart counter
387
const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
389
if (indsp->enabled) {
390
bool call_back_result = CheckIfCallBackAllowsAvailability(this->selected_type, IACT_USERCREATION);
392
/* Only if result does match the previous state would it require a redraw. */
393
if (call_back_result != this->enabled[this->selected_index]) {
394
this->enabled[this->selected_index] = call_back_result;
401
virtual void OnTimeout()
403
this->RaiseButtons();
406
virtual void OnPlaceObjectAbort()
408
this->RaiseButtons();
411
virtual void OnInvalidateData(int data = 0)
380
418
void ShowBuildIndustryWindow()
382
if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return;
383
AllocateWindowDescFront(&_build_industry_dynamic_desc, 0);
420
if (_game_mode != GM_EDITOR && !IsValidCompanyID(_local_company)) return;
421
if (BringWindowToFrontById(WC_BUILD_INDUSTRY, 0)) return;
422
new BuildIndustryWindow();
386
425
static void UpdateIndustryProduction(Industry *i);
388
static inline bool isProductionMinimum(const Industry *i, int pt)
427
static inline bool IsProductionMinimum(const Industry *i, int pt)
390
429
return i->production_rate[pt] == 0;
393
static inline bool isProductionMaximum(const Industry *i, int pt)
432
static inline bool IsProductionMaximum(const Industry *i, int pt)
395
434
return i->production_rate[pt] >= 255;
557
545
if (callback_res != CALLBACK_FAILED) {
558
546
StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
559
547
if (message != STR_NULL && message != STR_UNDEFINED) {
560
const Widget *wi = &w->widget[IVW_INFO];
548
const Widget *wi = &this->widget[IVW_INFO];
563
551
PrepareTextRefStackUsage(6);
564
552
/* Use all the available space left from where we stand up to the end of the window */
565
DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, wi->bottom - y);
553
y += DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, -1);
566
554
StopTextRefStackUsage();
571
DrawWindowViewport(w);
559
if (y > this->widget[IVW_INFO].bottom) {
561
ResizeWindowForWidget(this, IVW_INFO, 0, y - this->widget[IVW_INFO].top);
566
this->DrawViewport();
569
virtual void OnClick(Point pt, int widget)
577
switch (e->we.click.widget) {
581
i = GetIndustry(w->window_number);
583
/* We should work if needed.. */
584
if (!IsProductionAlterable(i)) return;
585
x = e->we.click.pt.x;
586
line = (e->we.click.pt.y - WP(w, indview_d).production_offset_y) / 10;
587
if (e->we.click.pt.y >= WP(w, indview_d).production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
588
if (IsInsideMM(x, 5, 25) ) {
589
/* Clicked buttons, decrease or increase production */
591
if (isProductionMinimum(i, line)) return;
592
i->production_rate[line] = max(i->production_rate[line] / 2, 0);
594
/* a zero production industry is unlikely to give anything but zero, so push it a little bit */
595
int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
596
if (isProductionMaximum(i, line)) return;
597
i->production_rate[line] = minu(new_prod, 255);
577
i = GetIndustry(this->window_number);
579
/* We should work if needed.. */
580
if (!IsProductionAlterable(i)) return;
582
line = (pt.y - this->production_offset_y) / 10;
583
if (pt.y >= this->production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
584
if (IsInsideMM(x, 5, 25) ) {
585
/* Clicked buttons, decrease or increase production */
587
if (IsProductionMinimum(i, line)) return;
588
i->production_rate[line] = max(i->production_rate[line] / 2, 0);
590
/* a zero production industry is unlikely to give anything but zero, so push it a little bit */
591
int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
592
if (IsProductionMaximum(i, line)) return;
593
i->production_rate[line] = minu(new_prod, 255);
596
UpdateIndustryProduction(i);
598
this->flags4 |= WF_TIMEOUT_BEGIN;
599
this->clicked_line = line + 1;
600
this->clicked_button = (x < 15 ? 1 : 2);
601
} else if (IsInsideMM(x, 34, 160)) {
602
/* clicked the text */
603
this->editbox_line = line;
604
SetDParam(0, i->production_rate[line] * 8);
605
ShowQueryString(STR_CONFIG_SETTING_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, this, CS_ALPHANUMERAL, QSF_NONE);
600
UpdateIndustryProduction(i);
602
w->flags4 |= 5 << WF_TIMEOUT_SHL;
603
WP(w, indview_d).clicked_line = line + 1;
604
WP(w, indview_d).clicked_button = (x < 15 ? 1 : 2);
605
} else if (IsInsideMM(x, 34, 160)) {
606
/* clicked the text */
607
WP(w, indview_d).editbox_line = line;
608
SetDParam(0, i->production_rate[line] * 8);
609
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, w, CS_ALPHANUMERAL);
614
i = GetIndustry(w->window_number);
615
ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
621
WP(w, indview_d).clicked_line = 0;
622
WP(w, indview_d).clicked_button = 0;
626
case WE_ON_EDIT_TEXT:
627
if (e->we.edittext.str[0] != '\0') {
628
Industry* i = GetIndustry(w->window_number);
629
int line = WP(w, indview_d).editbox_line;
631
i->production_rate[line] = ClampU(atoi(e->we.edittext.str), 0, 255);
632
UpdateIndustryProduction(i);
611
i = GetIndustry(this->window_number);
613
ShowExtraViewPortWindow(i->xy + TileDiffXY(1, 1));
615
ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
621
virtual void OnTimeout()
623
this->clicked_line = 0;
624
this->clicked_button = 0;
628
virtual void OnResize(Point new_size, Point delta)
630
this->viewport->width += delta.x;
631
this->viewport->height += delta.y;
632
this->viewport->virtual_width += delta.x;
633
this->viewport->virtual_height += delta.y;
634
this->viewport->dest_scrollpos_x -= delta.x;
635
this->viewport->dest_scrollpos_y -= delta.y;
636
UpdateViewportPosition(this);
639
virtual void OnQueryTextFinished(char *str)
641
if (StrEmpty(str)) return;
643
Industry *i = GetIndustry(this->window_number);
644
int line = this->editbox_line;
646
i->production_rate[line] = ClampU(atoi(str), 0, 255);
647
UpdateIndustryProduction(i);
638
652
static void UpdateIndustryProduction(Industry *i)
647
661
/** Widget definition of the view industy gui */
648
662
static const Widget _industry_view_widgets[] = {
649
{ WWT_CLOSEBOX, RESIZE_NONE, 9, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // IVW_CLOSEBOX
650
{ WWT_CAPTION, RESIZE_NONE, 9, 11, 247, 0, 13, STR_4801, STR_018C_WINDOW_TITLE_DRAG_THIS}, // IVW_CAPTION
651
{ WWT_STICKYBOX, RESIZE_NONE, 9, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON}, // IVW_STICKY
652
{ WWT_PANEL, RESIZE_NONE, 9, 0, 259, 14, 105, 0x0, STR_NULL}, // IVW_BACKGROUND
653
{ WWT_INSET, RESIZE_NONE, 9, 2, 257, 16, 103, 0x0, STR_NULL}, // IVW_VIEWPORT
654
{ WWT_PANEL, RESIZE_BOTTOM, 9, 0, 259, 106, 147, 0x0, STR_NULL}, // IVW_INFO
655
{ WWT_PUSHTXTBTN, RESIZE_TB, 9, 0, 129, 148, 159, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON}, // IVW_GOTO
656
{ WWT_PANEL, RESIZE_TB, 9, 130, 247, 148, 159, 0x0, STR_NULL}, // IVW_SPACER
657
{ WWT_RESIZEBOX, RESIZE_TB, 9, 248, 259, 148, 159, 0x0, STR_RESIZE_BUTTON}, // IVW_RESIZE_WIDGET
663
{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_CREAM, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // IVW_CLOSEBOX
664
{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_CREAM, 11, 247, 0, 13, STR_4801, STR_018C_WINDOW_TITLE_DRAG_THIS}, // IVW_CAPTION
665
{ WWT_STICKYBOX, RESIZE_LR, COLOUR_CREAM, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON}, // IVW_STICKY
666
{ WWT_PANEL, RESIZE_RB, COLOUR_CREAM, 0, 259, 14, 105, 0x0, STR_NULL}, // IVW_BACKGROUND
667
{ WWT_INSET, RESIZE_RB, COLOUR_CREAM, 2, 257, 16, 103, 0x0, STR_NULL}, // IVW_VIEWPORT
668
{ WWT_PANEL, RESIZE_RTB, COLOUR_CREAM, 0, 259, 106, 107, 0x0, STR_NULL}, // IVW_INFO
669
{ WWT_PUSHTXTBTN, RESIZE_TB, COLOUR_CREAM, 0, 129, 108, 119, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON}, // IVW_GOTO
670
{ WWT_PANEL, RESIZE_RTB, COLOUR_CREAM, 130, 247, 108, 119, 0x0, STR_NULL}, // IVW_SPACER
671
{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_CREAM, 248, 259, 108, 119, 0x0, STR_RESIZE_BUTTON}, // IVW_RESIZE
661
675
/** Window definition of the view industy gui */
662
static const WindowDesc _industry_view_desc = {
663
WDP_AUTO, WDP_AUTO, 260, 160, 260, 160,
676
static const WindowDesc _industry_view_desc(
677
WDP_AUTO, WDP_AUTO, 260, 120, 260, 120,
664
678
WC_INDUSTRY_VIEW, WC_NONE,
665
679
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
666
_industry_view_widgets,
680
_industry_view_widgets
670
683
void ShowIndustryViewWindow(int industry)
672
Window *w = AllocateWindowDescFront(&_industry_view_desc, industry);
675
w->flags4 |= WF_DISABLE_VP_SCROLL;
676
WP(w, indview_d).editbox_line = 0;
677
WP(w, indview_d).clicked_line = 0;
678
WP(w, indview_d).clicked_button = 0;
679
AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetIndustry(w->window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
685
AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
683
688
/** Names of the widgets of the industry directory gui */
685
690
IDW_CLOSEBOX = 0,
694
IDW_DROPDOWN_CRITERIA,
698
701
/** Widget definition of the industy directory gui */
699
702
static const Widget _industry_directory_widgets[] = {
700
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // IDW_CLOSEBOX
701
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 495, 0, 13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // IDW_CAPTION
702
{ WWT_STICKYBOX, RESIZE_NONE, 13, 496, 507, 0, 13, 0x0, STR_STICKY_BUTTON}, // IDW_STICKY
703
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 100, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP}, // IDW_SORTBYNAME
704
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 101, 200, 14, 25, STR_SORT_BY_TYPE, STR_SORT_ORDER_TIP}, // IDW_SORTBYTYPE
705
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 201, 300, 14, 25, STR_SORT_BY_PRODUCTION, STR_SORT_ORDER_TIP}, // IDW_SORTBYPROD
706
{ WWT_PUSHTXTBTN, RESIZE_NONE, 13, 301, 400, 14, 25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP}, // IDW_SORTBYTRANSPORT
707
{ WWT_PANEL, RESIZE_NONE, 13, 401, 495, 14, 25, 0x0, STR_NULL}, // IDW_SPACER
708
{ WWT_PANEL, RESIZE_BOTTOM, 13, 0, 495, 26, 189, 0x0, STR_INDUSTRYDIR_LIST_CAPTION}, // IDW_INDUSRTY_LIST
709
{ WWT_SCROLLBAR, RESIZE_BOTTOM, 13, 496, 507, 14, 177, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // IDW_SCROLLBAR
710
{ WWT_RESIZEBOX, RESIZE_TB, 13, 496, 507, 178, 189, 0x0, STR_RESIZE_BUTTON}, // IDW_RESIZE
703
{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_BROWN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, // IDW_CLOSEBOX
704
{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_BROWN, 11, 415, 0, 13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, // IDW_CAPTION
705
{ WWT_STICKYBOX, RESIZE_LR, COLOUR_BROWN, 416, 427, 0, 13, 0x0, STR_STICKY_BUTTON}, // IDW_STICKY
707
{ WWT_TEXTBTN, RESIZE_NONE, COLOUR_BROWN, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP}, // IDW_DROPDOWN_ORDER
708
{ WWT_DROPDOWN, RESIZE_NONE, COLOUR_BROWN, 81, 243, 14, 25, 0x0, STR_SORT_CRITERIA_TIP}, // IDW_DROPDOWN_CRITERIA
709
{ WWT_PANEL, RESIZE_RIGHT, COLOUR_BROWN, 244, 415, 14, 25, 0x0, STR_NULL}, // IDW_SPACER
711
{ WWT_PANEL, RESIZE_RB, COLOUR_BROWN, 0, 415, 26, 189, 0x0, STR_INDUSTRYDIR_LIST_CAPTION}, // IDW_INDUSRTY_LIST
712
{ WWT_SCROLLBAR, RESIZE_LRB, COLOUR_BROWN, 416, 427, 14, 177, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, // IDW_SCROLLBAR
713
{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_BROWN, 416, 427, 178, 189, 0x0, STR_RESIZE_BUTTON}, // IDW_RESIZE
714
static uint _num_industry_sort;
716
static char _bufcache[96];
717
static const Industry* _last_industry;
719
static byte _industry_sort_order;
721
static int CDECL GeneralIndustrySorter(const void *a, const void *b)
723
const Industry* i = *(const Industry**)a;
724
const Industry* j = *(const Industry**)b;
727
switch (_industry_sort_order >> 1) {
728
default: NOT_REACHED();
729
case 0: /* Sort by Name (handled later) */
733
case 1: /* Sort by Type */
734
r = i->type - j->type;
737
case 2: /* Sort by Production */
738
if (i->produced_cargo[0] == CT_INVALID) {
739
r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
741
if (j->produced_cargo[0] == CT_INVALID) {
745
(i->last_month_production[0] + i->last_month_production[1]) -
746
(j->last_month_production[0] + j->last_month_production[1]);
751
case 3: /* Sort by transported fraction */
752
if (i->produced_cargo[0] == CT_INVALID) {
753
r = (j->produced_cargo[0] == CT_INVALID ? 0 : -1);
755
if (j->produced_cargo[0] == CT_INVALID) {
761
pi = i->last_month_pct_transported[0] * 100 >> 8;
762
if (i->produced_cargo[1] != CT_INVALID) {
763
int p = i->last_month_pct_transported[1] * 100 >> 8;
767
pj = j->last_month_pct_transported[0] * 100 >> 8;
768
if (j->produced_cargo[1] != CT_INVALID) {
769
int p = j->last_month_pct_transported[1] * 100 >> 8;
779
/* default to string sorting if they are otherwise equal */
783
SetDParam(0, i->town->index);
784
GetString(buf1, STR_TOWN, lastof(buf1));
786
if (j != _last_industry) {
788
SetDParam(0, j->town->index);
789
GetString(_bufcache, STR_TOWN, lastof(_bufcache));
791
r = strcmp(buf1, _bufcache);
794
if (_industry_sort_order & 1) r = -r;
717
typedef GUIList<const Industry*> GUIIndustryList;
799
* Makes a sorted industry list.
800
* When there are no industries, the list has to be made. This so when one
801
* starts a new game without industries after playing a game with industries
802
* the list is not populated with invalid industries from the previous game.
721
* The list of industries.
804
static void MakeSortedIndustryList()
809
/* Create array for sorting */
810
_industry_sort = ReallocT(_industry_sort, GetMaxIndustryIndex() + 1);
812
/* Don't attempt a sort if there are no industries */
813
if (GetNumIndustries() != 0) {
814
FOR_ALL_INDUSTRIES(i) _industry_sort[n++] = i;
815
qsort((void*)_industry_sort, n, sizeof(_industry_sort[0]), GeneralIndustrySorter);
818
_num_industry_sort = n;
819
_last_industry = NULL; // used for "cache"
821
DEBUG(misc, 3, "Resorting industries list");
825
static void IndustryDirectoryWndProc(Window *w, WindowEvent *e)
829
if (_industry_sort_dirty) {
830
_industry_sort_dirty = false;
831
MakeSortedIndustryList();
834
SetVScrollCount(w, _num_industry_sort);
836
DrawWindowWidgets(w);
837
DrawSortButtonState(w, IDW_SORTBYNAME + (_industry_sort_order >> 1), _industry_sort_order & 1 ? SBS_DOWN : SBS_UP);
839
uint p = w->vscroll.pos;
842
while (p < _num_industry_sort) {
843
const Industry* i = _industry_sort[p];
845
SetDParam(0, i->index);
846
if (i->produced_cargo[0] != CT_INVALID) {
847
SetDParam(1, i->produced_cargo[0]);
848
SetDParam(2, i->last_month_production[0]);
850
if (i->produced_cargo[1] != CT_INVALID) {
851
SetDParam(3, i->produced_cargo[1]);
852
SetDParam(4, i->last_month_production[1]);
853
SetDParam(5, i->last_month_pct_transported[0] * 100 >> 8);
854
SetDParam(6, i->last_month_pct_transported[1] * 100 >> 8);
855
DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM_TWO, TC_FROMSTRING);
857
SetDParam(3, i->last_month_pct_transported[0] * 100 >> 8);
858
DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM, TC_FROMSTRING);
861
DrawString(4, 28 + n * 10, STR_INDUSTRYDIR_ITEM_NOPROD, TC_FROMSTRING);
864
if (++n == w->vscroll.cap) break;
869
switch (e->we.click.widget) {
870
case IDW_SORTBYNAME: {
871
_industry_sort_order = _industry_sort_order == 0 ? 1 : 0;
872
_industry_sort_dirty = true;
876
case IDW_SORTBYTYPE: {
877
_industry_sort_order = _industry_sort_order == 2 ? 3 : 2;
878
_industry_sort_dirty = true;
882
case IDW_SORTBYPROD: {
883
_industry_sort_order = _industry_sort_order == 4 ? 5 : 4;
884
_industry_sort_dirty = true;
888
case IDW_SORTBYTRANSPORT: {
889
_industry_sort_order = _industry_sort_order == 6 ? 7 : 6;
890
_industry_sort_dirty = true;
894
case IDW_INDUSRTY_LIST: {
895
int y = (e->we.click.pt.y - 28) / 10;
723
class IndustryDirectoryWindow : public Window {
725
/* Runtime saved values */
726
static Listing last_sorting;
727
static const Industry *last_industry;
729
/* Constants for sorting stations */
730
static const StringID sorter_names[];
731
static GUIIndustryList::SortFunction * const sorter_funcs[];
733
GUIIndustryList industries;
735
/** (Re)Build industries list */
736
void BuildIndustriesList()
738
if (!this->industries.NeedRebuild()) return;
740
this->industries.Clear();
742
DEBUG(misc, 3, "Building industry list");
745
FOR_ALL_INDUSTRIES(i) {
746
*this->industries.Append() = i;
749
this->industries.Compact();
750
this->industries.RebuildDone();
754
* Returns percents of cargo transported if industry produces this cargo, else -1
756
* @param i industry to check
757
* @param id cargo slot
758
* @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
760
static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
762
assert(id < lengthof(i->produced_cargo));
764
if (i->produced_cargo[id] == CT_INVALID) return 101;
765
return i->last_month_pct_transported[id] * 100 >> 8;
769
* Returns value representing industry's transported cargo
770
* percentage for industry sorting
772
* @param i industry to check
773
* @return value used for sorting
775
static int GetCargoTransportedSortValue(const Industry *i)
777
int p1 = GetCargoTransportedPercentsIfValid(i, 0);
778
int p2 = GetCargoTransportedPercentsIfValid(i, 1);
780
if (p1 > p2) Swap(p1, p2); // lower value has higher priority
782
return (p1 << 8) + p2;
785
/** Sort industries by name */
786
static int CDECL IndustryNameSorter(const Industry * const *a, const Industry * const *b)
788
static char buf_cache[96];
791
SetDParam(0, (*a)->town->index);
792
GetString(buf, STR_TOWN, lastof(buf));
794
if (*b != last_industry) {
796
SetDParam(0, (*b)->town->index);
797
GetString(buf_cache, STR_TOWN, lastof(buf_cache));
800
return strcmp(buf, buf_cache);
803
/** Sort industries by type and name */
804
static int CDECL IndustryTypeSorter(const Industry * const *a, const Industry * const *b)
806
int r = (*a)->type - (*b)->type;
807
return (r == 0) ? IndustryNameSorter(a, b) : r;
810
/** Sort industries by production and name */
811
static int CDECL IndustryProductionSorter(const Industry * const *a, const Industry * const *b)
815
if ((*a)->produced_cargo[0] == CT_INVALID) {
816
if ((*b)->produced_cargo[0] != CT_INVALID) return -1;
818
if ((*b)->produced_cargo[0] == CT_INVALID) return 1;
820
r = ((*a)->last_month_production[0] + (*a)->last_month_production[1]) -
821
((*b)->last_month_production[0] + (*b)->last_month_production[1]);
824
return (r == 0) ? IndustryNameSorter(a, b) : r;
827
/** Sort industries by transported cargo and name */
828
static int CDECL IndustryTransportedCargoSorter(const Industry * const *a, const Industry * const *b)
830
int r = GetCargoTransportedSortValue(*a) - GetCargoTransportedSortValue(*b);
831
return (r == 0) ? IndustryNameSorter(a, b) : r;
834
/** Sort the industries list */
835
void SortIndustriesList()
837
if (!this->industries.Sort()) return;
839
/* Reset name sorter sort cache */
840
this->last_industry = NULL;
842
/* Set the modified widget dirty */
843
this->InvalidateWidget(IDW_INDUSTRY_LIST);
847
IndustryDirectoryWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
849
this->vscroll.cap = 16;
850
this->resize.height = this->height - 6 * 10; // minimum 10 items
851
this->resize.step_height = 10;
852
this->FindWindowPlacementAndResize(desc);
854
this->industries.SetListing(this->last_sorting);
855
this->industries.SetSortFuncs(this->sorter_funcs);
856
this->industries.ForceRebuild();
857
this->industries.NeedResort();
858
this->SortIndustriesList();
860
this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
863
~IndustryDirectoryWindow()
865
this->last_sorting = this->industries.GetListing();
868
virtual void OnPaint()
870
BuildIndustriesList();
871
SortIndustriesList();
873
SetVScrollCount(this, this->industries.Length());
876
this->DrawSortButtonState(IDW_DROPDOWN_ORDER, this->industries.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
878
int max = min(this->vscroll.pos + this->vscroll.cap, this->industries.Length());
879
int y = 28; // start of the list-widget
881
for (int n = this->vscroll.pos; n < max; ++n) {
882
const Industry *i = this->industries[n];
883
const IndustrySpec *indsp = GetIndustrySpec(i->type);
887
SetDParam(p++, i->index);
889
/* Industry productions */
890
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
891
if (i->produced_cargo[j] == CT_INVALID) continue;
892
SetDParam(p++, i->produced_cargo[j]);
893
SetDParam(p++, i->last_month_production[j]);
894
SetDParam(p++, GetCargoSuffix(j + 3, CST_DIR, (Industry*)i, i->type, indsp));
897
/* Transported productions */
898
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
899
if (i->produced_cargo[j] == CT_INVALID) continue;
900
SetDParam(p++, i->last_month_pct_transported[j] * 100 >> 8);
903
/* Drawing the right string */
904
StringID str = STR_INDUSTRYDIR_ITEM_NOPROD;
905
if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO;
906
DrawStringTruncated(4, y, str, TC_FROMSTRING, this->widget[IDW_INDUSTRY_LIST].right - 4);
912
virtual void OnClick(Point pt, int widget)
915
case IDW_DROPDOWN_ORDER:
916
this->industries.ToggleSortOrder();
920
case IDW_DROPDOWN_CRITERIA:
921
ShowDropDownMenu(this, this->sorter_names, this->industries.SortType(), IDW_DROPDOWN_CRITERIA, 0, 0);
924
case IDW_INDUSTRY_LIST: {
925
int y = (pt.y - 28) / 10;
898
if (!IsInsideMM(y, 0, w->vscroll.cap)) return;
899
p = y + w->vscroll.pos;
900
if (p < _num_industry_sort) {
901
ScrollMainWindowToTile(_industry_sort[p]->xy);
928
if (!IsInsideMM(y, 0, this->vscroll.cap)) return;
929
p = y + this->vscroll.pos;
930
if (p < this->industries.Length()) {
932
ShowExtraViewPortWindow(this->industries[p]->xy);
934
ScrollMainWindowToTile(this->industries[p]->xy);
912
w->vscroll.cap += e->we.sizing.diff.y / 10;
941
virtual void OnDropdownSelect(int widget, int index)
943
if (this->industries.SortType() != index) {
944
this->industries.SetSortType(index);
945
this->widget[IDW_DROPDOWN_CRITERIA].data = this->sorter_names[this->industries.SortType()];
950
virtual void OnResize(Point new_size, Point delta)
952
this->vscroll.cap += delta.y / 10;
955
virtual void OnInvalidateData(int data)
958
this->industries.ForceRebuild();
960
this->industries.ForceResort();
962
this->InvalidateWidget(IDW_INDUSTRY_LIST);
966
Listing IndustryDirectoryWindow::last_sorting = {false, 0};
967
const Industry *IndustryDirectoryWindow::last_industry = NULL;
969
/* Availible station sorting functions */
970
GUIIndustryList::SortFunction * const IndustryDirectoryWindow::sorter_funcs[] = {
973
&IndustryProductionSorter,
974
&IndustryTransportedCargoSorter
977
/* Names of the sorting functions */
978
const StringID IndustryDirectoryWindow::sorter_names[] = {
979
STR_SORT_BY_DROPDOWN_NAME,
981
STR_SORT_BY_PRODUCTION,
982
STR_SORT_BY_TRANSPORTED,
917
987
/** Window definition of the industy directory gui */
918
static const WindowDesc _industry_directory_desc = {
919
WDP_AUTO, WDP_AUTO, 508, 190, 508, 190,
988
static const WindowDesc _industry_directory_desc(
989
WDP_AUTO, WDP_AUTO, 428, 190, 428, 190,
920
990
WC_INDUSTRY_DIRECTORY, WC_NONE,
921
991
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
922
_industry_directory_widgets,
923
IndustryDirectoryWndProc
992
_industry_directory_widgets
926
995
void ShowIndustryDirectory()
928
Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0);
932
w->resize.height = w->height - 6 * 10; // minimum 10 items
933
w->resize.step_height = 10;
997
AllocateWindowDescFront<IndustryDirectoryWindow>(&_industry_directory_desc, 0);