00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "town.h"
00009 #include "viewport_func.h"
00010 #include "gfx_func.h"
00011 #include "gui.h"
00012 #include "window_gui.h"
00013 #include "textbuf_gui.h"
00014 #include "command_func.h"
00015 #include "player_func.h"
00016 #include "player_base.h"
00017 #include "player_gui.h"
00018 #include "network/network.h"
00019 #include "variables.h"
00020 #include "strings_func.h"
00021 #include "economy_func.h"
00022 #include "core/alloc_func.hpp"
00023 #include "settings_type.h"
00024
00025 #include "table/sprites.h"
00026 #include "table/strings.h"
00027
00028 enum TownAuthorityWidget {
00029 TWA_CLOSEBOX = 0,
00030 TWA_CAPTION,
00031 TWA_RATING_INFO,
00032 TWA_COMMAND_LIST,
00033 TWA_SCROLLBAR,
00034 TWA_ACTION_INFO,
00035 TWA_EXECUTE,
00036 };
00037
00038 static const Widget _town_authority_widgets[] = {
00039 { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00040 { WWT_CAPTION, RESIZE_NONE, 13, 11, 316, 0, 13, STR_2022_LOCAL_AUTHORITY, STR_018C_WINDOW_TITLE_DRAG_THIS},
00041 { WWT_PANEL, RESIZE_NONE, 13, 0, 316, 14, 105, 0x0, STR_NULL},
00042 { WWT_PANEL, RESIZE_NONE, 13, 0, 304, 106, 157, 0x0, STR_2043_LIST_OF_THINGS_TO_DO_AT},
00043 { WWT_SCROLLBAR, RESIZE_NONE, 13, 305, 316, 106, 157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00044 { WWT_PANEL, RESIZE_NONE, 13, 0, 316, 158, 209, 0x0, STR_NULL},
00045 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 316, 210, 221, STR_2042_DO_IT, STR_2044_CARRY_OUT_THE_HIGHLIGHTED},
00046 { WIDGETS_END},
00047 };
00048
00049 extern const byte _town_action_costs[8];
00050
00051 enum TownActions {
00052 TACT_NONE = 0x00,
00053
00054 TACT_ADVERTISE_SMALL = 0x01,
00055 TACT_ADVERTISE_MEDIUM = 0x02,
00056 TACT_ADVERTISE_LARGE = 0x04,
00057 TACT_ROAD_REBUILD = 0x08,
00058 TACT_BUILD_STATUE = 0x10,
00059 TACT_FOUND_BUILDINGS = 0x20,
00060 TACT_BUY_RIGHTS = 0x40,
00061 TACT_BRIBE = 0x80,
00062
00063 TACT_ADVERTISE = TACT_ADVERTISE_SMALL | TACT_ADVERTISE_MEDIUM | TACT_ADVERTISE_LARGE,
00064 TACT_CONSTRUCTION = TACT_ROAD_REBUILD | TACT_BUILD_STATUE | TACT_FOUND_BUILDINGS,
00065 TACT_FUNDS = TACT_BUY_RIGHTS | TACT_BRIBE,
00066 TACT_ALL = TACT_ADVERTISE | TACT_CONSTRUCTION | TACT_FUNDS,
00067 };
00068
00069 DECLARE_ENUM_AS_BIT_SET(TownActions);
00070
00077 uint GetMaskOfTownActions(int *nump, PlayerID pid, const Town *t)
00078 {
00079 int num = 0;
00080 TownActions buttons = TACT_NONE;
00081
00082
00083 if (pid != PLAYER_SPECTATOR && !(_patches.bribe && t->unwanted[pid])) {
00084
00085
00086 Money avail = GetPlayer(pid)->player_money + _price.station_value * 200;
00087 Money ref = _price.build_industry >> 8;
00088
00089
00090
00091 for (uint i = 0; i != lengthof(_town_action_costs); i++) {
00092 const TownActions cur = (TownActions)(1 << i);
00093
00094
00095 if (cur == TACT_BRIBE && (!_patches.bribe || t->ratings[pid] >= RATING_BRIBE_MAXIMUM))
00096 continue;
00097
00098
00099 if (cur == TACT_BUY_RIGHTS && !_patches.exclusive_rights)
00100 continue;
00101
00102
00103 if (cur == TACT_BUILD_STATUE && HasBit(t->statues, pid))
00104 continue;
00105
00106 if (avail >= _town_action_costs[i] * ref) {
00107 buttons |= cur;
00108 num++;
00109 }
00110 }
00111 }
00112
00113 if (nump != NULL) *nump = num;
00114 return buttons;
00115 }
00116
00126 static int GetNthSetBit(uint32 bits, int n)
00127 {
00128 if (n >= 0) {
00129 uint i;
00130 FOR_EACH_SET_BIT(i, bits) {
00131 n--;
00132 if (n < 0) return i;
00133 }
00134 }
00135 return -1;
00136 }
00137
00138 static void TownAuthorityWndProc(Window *w, WindowEvent *e)
00139 {
00140 switch (e->event) {
00141 case WE_PAINT: {
00142 const Town *t = GetTown(w->window_number);
00143 int numact;
00144 uint buttons = GetMaskOfTownActions(&numact, _local_player, t);
00145
00146 SetVScrollCount(w, numact + 1);
00147
00148 if (WP(w, def_d).data_1 != -1 && !HasBit(buttons, WP(w,def_d).data_1))
00149 WP(w, def_d).data_1 = -1;
00150
00151 w->SetWidgetDisabledState(6, WP(w, def_d).data_1 == -1);
00152
00153 {
00154 int y;
00155 const Player *p;
00156 int r;
00157 StringID str;
00158
00159 SetDParam(0, w->window_number);
00160 DrawWindowWidgets(w);
00161
00162 DrawString(2, 15, STR_2023_TRANSPORT_COMPANY_RATINGS, TC_FROMSTRING);
00163
00164
00165 y = 25;
00166 FOR_ALL_PLAYERS(p) {
00167 if (p->is_active && (HasBit(t->have_ratings, p->index) || t->exclusivity == p->index)) {
00168 DrawPlayerIcon(p->index, 2, y);
00169
00170 SetDParam(0, p->index);
00171 SetDParam(1, p->index);
00172
00173 r = t->ratings[p->index];
00174 (str = STR_3035_APPALLING, r <= RATING_APPALLING) ||
00175 (str++, r <= RATING_VERYPOOR) ||
00176 (str++, r <= RATING_POOR) ||
00177 (str++, r <= RATING_MEDIOCRE) ||
00178 (str++, r <= RATING_GOOD) ||
00179 (str++, r <= RATING_VERYGOOD) ||
00180 (str++, r <= RATING_EXCELLENT) ||
00181 (str++, true);
00182
00183 SetDParam(2, str);
00184 if (t->exclusivity == p->index) {
00185 DrawSprite(SPR_BLOT, PALETTE_TO_RED, 18, y);
00186 }
00187
00188 DrawString(28, y, STR_2024, TC_FROMSTRING);
00189 y += 10;
00190 }
00191 }
00192 }
00193
00194
00195 {
00196 int y = 107, i;
00197 int pos = w->vscroll.pos;
00198
00199 if (--pos < 0) {
00200 DrawString(2, y, STR_2045_ACTIONS_AVAILABLE, TC_FROMSTRING);
00201 y += 10;
00202 }
00203 for (i = 0; buttons; i++, buttons >>= 1) {
00204 if (pos <= -5) break;
00205
00206 if ((buttons & 1) && --pos < 0) {
00207 DrawString(3, y, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i, TC_ORANGE);
00208 y += 10;
00209 }
00210 }
00211 }
00212
00213 {
00214 int i = WP(w, def_d).data_1;
00215
00216 if (i != -1) {
00217 SetDParam(1, (_price.build_industry >> 8) * _town_action_costs[i]);
00218 SetDParam(0, STR_2046_SMALL_ADVERTISING_CAMPAIGN + i);
00219 DrawStringMultiLine(2, 159, STR_204D_INITIATE_A_SMALL_LOCAL + i, 313);
00220 }
00221 }
00222
00223 } break;
00224
00225 case WE_DOUBLE_CLICK:
00226 case WE_CLICK:
00227 switch (e->we.click.widget) {
00228 case TWA_COMMAND_LIST: {
00229 const Town *t = GetTown(w->window_number);
00230 int y = (e->we.click.pt.y - 0x6B) / 10;
00231
00232 if (!IsInsideMM(y, 0, 5)) return;
00233
00234 y = GetNthSetBit(GetMaskOfTownActions(NULL, _local_player, t), y + w->vscroll.pos - 1);
00235 if (y >= 0) {
00236 WP(w, def_d).data_1 = y;
00237 SetWindowDirty(w);
00238 }
00239
00240 if (e->event != WE_DOUBLE_CLICK || y < 0) break;
00241 }
00242
00243 case TWA_EXECUTE:
00244 DoCommandP(GetTown(w->window_number)->xy, w->window_number, WP(w, def_d).data_1, NULL, CMD_DO_TOWN_ACTION | CMD_MSG(STR_00B4_CAN_T_DO_THIS));
00245 break;
00246 }
00247 break;
00248
00249 case WE_4:
00250 SetWindowDirty(w);
00251 break;
00252 }
00253 }
00254
00255 static const WindowDesc _town_authority_desc = {
00256 WDP_AUTO, WDP_AUTO, 317, 222, 317, 222,
00257 WC_TOWN_AUTHORITY, WC_NONE,
00258 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
00259 _town_authority_widgets,
00260 TownAuthorityWndProc
00261 };
00262
00263 static void ShowTownAuthorityWindow(uint town)
00264 {
00265 Window *w = AllocateWindowDescFront(&_town_authority_desc, town);
00266
00267 if (w != NULL) {
00268 w->vscroll.cap = 5;
00269 WP(w, def_d).data_1 = -1;
00270 }
00271 }
00272
00273 static void TownViewWndProc(Window *w, WindowEvent *e)
00274 {
00275 Town *t = GetTown(w->window_number);
00276
00277 switch (e->event) {
00278 case WE_CREATE:
00279 if (t->larger_town) w->widget[1].data = STR_CITY;
00280 break;
00281
00282 case WE_PAINT:
00283
00284 w->SetWidgetDisabledState(8, _networking && !_network_server);
00285
00286 SetDParam(0, t->index);
00287 DrawWindowWidgets(w);
00288
00289 SetDParam(0, t->population);
00290 SetDParam(1, t->num_houses);
00291 DrawString(2, 107, STR_2006_POPULATION, TC_FROMSTRING);
00292
00293 SetDParam(0, t->act_pass);
00294 SetDParam(1, t->max_pass);
00295 DrawString(2, 117, STR_200D_PASSENGERS_LAST_MONTH_MAX, TC_FROMSTRING);
00296
00297 SetDParam(0, t->act_mail);
00298 SetDParam(1, t->max_mail);
00299 DrawString(2, 127, STR_200E_MAIL_LAST_MONTH_MAX, TC_FROMSTRING);
00300
00301 DrawWindowViewport(w);
00302 break;
00303
00304 case WE_CLICK:
00305 switch (e->we.click.widget) {
00306 case 6:
00307 ScrollMainWindowToTile(t->xy);
00308 break;
00309
00310 case 7:
00311 ShowTownAuthorityWindow(w->window_number);
00312 break;
00313
00314 case 8:
00315 SetDParam(0, w->window_number);
00316 ShowQueryString(STR_TOWN, STR_2007_RENAME_TOWN, 31, 130, w, CS_ALPHANUMERAL);
00317 break;
00318
00319 case 9:
00320 ExpandTown(t);
00321 break;
00322
00323 case 10:
00324 delete t;
00325 break;
00326 } break;
00327
00328 case WE_ON_EDIT_TEXT:
00329 if (e->we.edittext.str[0] != '\0') {
00330 _cmd_text = e->we.edittext.str;
00331 DoCommandP(0, w->window_number, 0, NULL,
00332 CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN));
00333 }
00334 break;
00335 }
00336 }
00337
00338
00339 static const Widget _town_view_widgets[] = {
00340 { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00341 { WWT_CAPTION, RESIZE_NONE, 13, 11, 247, 0, 13, STR_2005, STR_018C_WINDOW_TITLE_DRAG_THIS},
00342 { WWT_STICKYBOX, RESIZE_NONE, 13, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON},
00343 { WWT_PANEL, RESIZE_NONE, 13, 0, 259, 14, 105, 0x0, STR_NULL},
00344 { WWT_INSET, RESIZE_NONE, 13, 2, 257, 16, 103, 0x0, STR_NULL},
00345 { WWT_PANEL, RESIZE_NONE, 13, 0, 259, 106, 137, 0x0, STR_NULL},
00346 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 85, 138, 149, STR_00E4_LOCATION, STR_200B_CENTER_THE_MAIN_VIEW_ON},
00347 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 86, 171, 138, 149, STR_2020_LOCAL_AUTHORITY, STR_2021_SHOW_INFORMATION_ON_LOCAL},
00348 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 172, 259, 138, 149, STR_0130_RENAME, STR_200C_CHANGE_TOWN_NAME},
00349 { WIDGETS_END},
00350 };
00351
00352 static const WindowDesc _town_view_desc = {
00353 WDP_AUTO, WDP_AUTO, 260, 150, 260, 150,
00354 WC_TOWN_VIEW, WC_NONE,
00355 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
00356 _town_view_widgets,
00357 TownViewWndProc
00358 };
00359
00360 static const Widget _town_view_scen_widgets[] = {
00361 { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00362 { WWT_CAPTION, RESIZE_NONE, 13, 11, 172, 0, 13, STR_2005, STR_018C_WINDOW_TITLE_DRAG_THIS},
00363 { WWT_STICKYBOX, RESIZE_NONE, 13, 248, 259, 0, 13, 0x0, STR_STICKY_BUTTON},
00364 { WWT_PANEL, RESIZE_NONE, 13, 0, 259, 14, 105, 0x0, STR_NULL},
00365 { WWT_INSET, RESIZE_NONE, 13, 2, 257, 16, 103, 0x0, STR_NULL},
00366 { WWT_PANEL, RESIZE_NONE, 13, 0, 259, 106, 137, 0x0, STR_NULL},
00367 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 85, 138, 149, STR_00E4_LOCATION, STR_200B_CENTER_THE_MAIN_VIEW_ON},
00368 { WWT_EMPTY, RESIZE_NONE, 0, 0, 0, 0, 0, 0x0, STR_NULL},
00369 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 173, 247, 0, 13, STR_0130_RENAME, STR_200C_CHANGE_TOWN_NAME},
00370 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 86, 171, 138, 149, STR_023C_EXPAND, STR_023B_INCREASE_SIZE_OF_TOWN},
00371 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 172, 259, 138, 149, STR_0290_DELETE, STR_0291_DELETE_THIS_TOWN_COMPLETELY},
00372 { WIDGETS_END},
00373 };
00374
00375 static const WindowDesc _town_view_scen_desc = {
00376 WDP_AUTO, WDP_AUTO, 260, 150, 260, 150,
00377 WC_TOWN_VIEW, WC_NONE,
00378 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
00379 _town_view_scen_widgets,
00380 TownViewWndProc
00381 };
00382
00383 void ShowTownViewWindow(TownID town)
00384 {
00385 Window *w;
00386
00387 if (_game_mode != GM_EDITOR) {
00388 w = AllocateWindowDescFront(&_town_view_desc, town);
00389 } else {
00390 w = AllocateWindowDescFront(&_town_view_scen_desc, town);
00391 }
00392
00393 if (w != NULL) {
00394 w->flags4 |= WF_DISABLE_VP_SCROLL;
00395 AssignWindowViewport(w, 3, 17, 0xFE, 0x56, GetTown(town)->xy, ZOOM_LVL_TOWN);
00396 }
00397 }
00398
00399 enum TownDirectoryWidget {
00400 TDW_SORTNAME = 3,
00401 TDW_SORTPOPULATION,
00402 TDW_CENTERTOWN,
00403 };
00404 static const Widget _town_directory_widgets[] = {
00405 { WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00406 { WWT_CAPTION, RESIZE_NONE, 13, 11, 195, 0, 13, STR_2000_TOWNS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00407 { WWT_STICKYBOX, RESIZE_NONE, 13, 196, 207, 0, 13, 0x0, STR_STICKY_BUTTON},
00408 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 0, 98, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
00409 { WWT_PUSHTXTBTN, RESIZE_NONE, 13, 99, 195, 14, 25, STR_SORT_BY_POPULATION, STR_SORT_ORDER_TIP},
00410 { WWT_PANEL, RESIZE_BOTTOM, 13, 0, 195, 26, 189, 0x0, STR_200A_TOWN_NAMES_CLICK_ON_NAME},
00411 { WWT_SCROLLBAR, RESIZE_BOTTOM, 13, 196, 207, 14, 189, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00412 { WWT_PANEL, RESIZE_TB, 13, 0, 195, 190, 201, 0x0, STR_NULL},
00413 { WWT_RESIZEBOX, RESIZE_TB, 13, 196, 207, 190, 201, 0x0, STR_RESIZE_BUTTON},
00414 { WIDGETS_END},
00415 };
00416
00417
00418
00419 static uint _num_town_sort;
00420
00421 static char _bufcache[64];
00422 static const Town* _last_town;
00423
00424 static int CDECL TownNameSorter(const void *a, const void *b)
00425 {
00426 const Town* ta = *(const Town**)a;
00427 const Town* tb = *(const Town**)b;
00428 char buf1[64];
00429 int r;
00430
00431 SetDParam(0, ta->index);
00432 GetString(buf1, STR_TOWN, lastof(buf1));
00433
00434
00435
00436
00437 if (tb != _last_town) {
00438 _last_town = tb;
00439 SetDParam(0, tb->index);
00440 GetString(_bufcache, STR_TOWN, lastof(_bufcache));
00441 }
00442
00443 r = strcmp(buf1, _bufcache);
00444 if (_town_sort_order & 1) r = -r;
00445 return r;
00446 }
00447
00448 static int CDECL TownPopSorter(const void *a, const void *b)
00449 {
00450 const Town* ta = *(const Town**)a;
00451 const Town* tb = *(const Town**)b;
00452 int r = ta->population - tb->population;
00453 if (_town_sort_order & 1) r = -r;
00454 return r;
00455 }
00456
00457 static void MakeSortedTownList()
00458 {
00459 const Town* t;
00460 uint n = 0;
00461
00462
00463 _town_sort = ReallocT(_town_sort, GetMaxTownIndex() + 1);
00464
00465 FOR_ALL_TOWNS(t) _town_sort[n++] = t;
00466
00467 _num_town_sort = n;
00468
00469 _last_town = NULL;
00470 qsort((void*)_town_sort, n, sizeof(_town_sort[0]), _town_sort_order & 2 ? TownPopSorter : TownNameSorter);
00471
00472 DEBUG(misc, 3, "Resorting towns list");
00473 }
00474
00475
00476 static void TownDirectoryWndProc(Window *w, WindowEvent *e)
00477 {
00478 switch (e->event) {
00479 case WE_PAINT: {
00480 if (_town_sort_dirty) {
00481 _town_sort_dirty = false;
00482 MakeSortedTownList();
00483 }
00484
00485 SetVScrollCount(w, _num_town_sort);
00486
00487 DrawWindowWidgets(w);
00488 DrawSortButtonState(w, (_town_sort_order <= 1) ? TDW_SORTNAME : TDW_SORTPOPULATION, _town_sort_order & 1 ? SBS_DOWN : SBS_UP);
00489
00490 {
00491 int n = 0;
00492 uint16 i = w->vscroll.pos;
00493 int y = 28;
00494
00495 while (i < _num_town_sort) {
00496 const Town* t = _town_sort[i];
00497
00498 assert(t->xy);
00499
00500 SetDParam(0, t->index);
00501 SetDParam(1, t->population);
00502 DrawString(2, y, STR_2057, TC_FROMSTRING);
00503
00504 y += 10;
00505 i++;
00506 if (++n == w->vscroll.cap) break;
00507 }
00508 SetDParam(0, GetWorldPopulation());
00509 DrawString(3, w->height - 12 + 2, STR_TOWN_POPULATION, TC_FROMSTRING);
00510 }
00511 } break;
00512
00513 case WE_CLICK:
00514 switch (e->we.click.widget) {
00515 case TDW_SORTNAME: {
00516 _town_sort_order = (_town_sort_order == 0) ? 1 : 0;
00517 _town_sort_dirty = true;
00518 SetWindowDirty(w);
00519 } break;
00520
00521 case TDW_SORTPOPULATION: {
00522 _town_sort_order = (_town_sort_order == 2) ? 3 : 2;
00523 _town_sort_dirty = true;
00524 SetWindowDirty(w);
00525 } break;
00526
00527 case TDW_CENTERTOWN: {
00528 const Town* t;
00529
00530 uint16 id_v = (e->we.click.pt.y - 28) / 10;
00531
00532 if (id_v >= w->vscroll.cap) return;
00533
00534 id_v += w->vscroll.pos;
00535
00536 if (id_v >= _num_town_sort) return;
00537
00538 t = _town_sort[id_v];
00539 assert(t->xy);
00540 ScrollMainWindowToTile(t->xy);
00541 } break;
00542 }
00543 break;
00544
00545 case WE_4:
00546 SetWindowDirty(w);
00547 break;
00548
00549 case WE_RESIZE:
00550 w->vscroll.cap += e->we.sizing.diff.y / 10;
00551 break;
00552 }
00553 }
00554
00555 static const WindowDesc _town_directory_desc = {
00556 WDP_AUTO, WDP_AUTO, 208, 202, 208, 202,
00557 WC_TOWN_DIRECTORY, WC_NONE,
00558 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00559 _town_directory_widgets,
00560 TownDirectoryWndProc
00561 };
00562
00563
00564 void ShowTownDirectory()
00565 {
00566 Window *w = AllocateWindowDescFront(&_town_directory_desc, 0);
00567
00568 if (w != NULL) {
00569 w->vscroll.cap = 16;
00570 w->resize.step_height = 10;
00571 w->resize.height = w->height - 10 * 6;
00572 }
00573 }