00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "currency.h"
00008 #include "landscape.h"
00009 #include "news.h"
00010 #include "player_base.h"
00011 #include "player_func.h"
00012 #include "station.h"
00013 #include "command_func.h"
00014 #include "saveload.h"
00015 #include "industry.h"
00016 #include "town.h"
00017 #include "network/network.h"
00018 #include "engine.h"
00019 #include "network/network_data.h"
00020 #include "variables.h"
00021 #include "vehicle_gui.h"
00022 #include "ai/ai.h"
00023 #include "train.h"
00024 #include "roadveh.h"
00025 #include "aircraft.h"
00026 #include "newgrf_engine.h"
00027 #include "newgrf_sound.h"
00028 #include "newgrf_callbacks.h"
00029 #include "newgrf_industries.h"
00030 #include "newgrf_industrytiles.h"
00031 #include "unmovable.h"
00032 #include "cargotype.h"
00033 #include "player_face.h"
00034 #include "group.h"
00035 #include "strings_func.h"
00036 #include "tile_cmd.h"
00037 #include "functions.h"
00038 #include "window_func.h"
00039 #include "date_func.h"
00040 #include "vehicle_func.h"
00041 #include "sound_func.h"
00042 #include "track_type.h"
00043 #include "track_func.h"
00044 #include "road_func.h"
00045 #include "rail_map.h"
00046 #include "signal_func.h"
00047 #include "gfx_func.h"
00048 #include "autoreplace_func.h"
00049 #include "signs.h"
00050
00051 #include "table/strings.h"
00052 #include "table/sprites.h"
00053
00065 static inline int32 BigMulS(const int32 a, const int32 b, const uint8 shift)
00066 {
00067 return (int32)((int64)a * (int64)b >> shift);
00068 }
00069
00081 static inline uint32 BigMulSU(const uint32 a, const uint32 b, const uint8 shift)
00082 {
00083 return (uint32)((uint64)a * (uint64)b >> shift);
00084 }
00085
00086
00087 const ScoreInfo _score_info[] = {
00088 { SCORE_VEHICLES, 120, 100 },
00089 { SCORE_STATIONS, 80, 100 },
00090 { SCORE_MIN_PROFIT, 10000, 100 },
00091 { SCORE_MIN_INCOME, 50000, 50 },
00092 { SCORE_MAX_INCOME, 100000, 100 },
00093 { SCORE_DELIVERED, 40000, 400 },
00094 { SCORE_CARGO, 8, 50 },
00095 { SCORE_MONEY, 10000000, 50 },
00096 { SCORE_LOAN, 250000, 50 },
00097 { SCORE_TOTAL, 0, 0 }
00098 };
00099
00100 int _score_part[MAX_PLAYERS][SCORE_END];
00101 Economy _economy;
00102 Subsidy _subsidies[MAX_PLAYERS];
00103 Prices _price;
00104 uint16 _price_frac[NUM_PRICES];
00105 Money _cargo_payment_rates[NUM_CARGO];
00106 uint16 _cargo_payment_rates_frac[NUM_CARGO];
00107 Money _additional_cash_required;
00108
00109 Money CalculateCompanyValue(const Player* p)
00110 {
00111 PlayerID owner = p->index;
00112 Money value = 0;
00113
00114 Station *st;
00115 uint num = 0;
00116
00117 FOR_ALL_STATIONS(st) {
00118 if (st->owner == owner) num += CountBits(st->facilities);
00119 }
00120
00121 value += num * _price.station_value * 25;
00122
00123 Vehicle *v;
00124 FOR_ALL_VEHICLES(v) {
00125 if (v->owner != owner) continue;
00126
00127 if (v->type == VEH_TRAIN ||
00128 v->type == VEH_ROAD ||
00129 (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) ||
00130 v->type == VEH_SHIP) {
00131 value += v->value * 3 >> 1;
00132 }
00133 }
00134
00135
00136 value -= p->current_loan;
00137 value += p->player_money;
00138
00139 return max(value, (Money)1);
00140 }
00141
00148 int UpdateCompanyRatingAndValue(Player *p, bool update)
00149 {
00150 byte owner = p->index;
00151 int score = 0;
00152
00153 memset(_score_part[owner], 0, sizeof(_score_part[owner]));
00154
00155
00156 {
00157 Vehicle *v;
00158 Money min_profit = 0;
00159 bool min_profit_first = true;
00160 uint num = 0;
00161
00162 FOR_ALL_VEHICLES(v) {
00163 if (v->owner != owner) continue;
00164 if (IsPlayerBuildableVehicleType(v->type) && v->IsPrimaryVehicle()) {
00165 num++;
00166 if (v->age > 730) {
00167
00168 if (min_profit_first || min_profit > v->profit_last_year) {
00169 min_profit = v->profit_last_year;
00170 min_profit_first = false;
00171 }
00172 }
00173 }
00174 }
00175
00176 min_profit >>= 8;
00177
00178 _score_part[owner][SCORE_VEHICLES] = num;
00179
00180 if (min_profit > 0)
00181 _score_part[owner][SCORE_MIN_PROFIT] = ClampToI32(min_profit);
00182 }
00183
00184
00185 {
00186 uint num = 0;
00187 const Station* st;
00188
00189 FOR_ALL_STATIONS(st) {
00190 if (st->owner == owner) num += CountBits(st->facilities);
00191 }
00192 _score_part[owner][SCORE_STATIONS] = num;
00193 }
00194
00195
00196 {
00197 int numec = min(p->num_valid_stat_ent, 12);
00198 if (numec != 0) {
00199 const PlayerEconomyEntry *pee = p->old_economy;
00200 Money min_income = pee->income + pee->expenses;
00201 Money max_income = pee->income + pee->expenses;
00202
00203 do {
00204 min_income = min(min_income, pee->income + pee->expenses);
00205 max_income = max(max_income, pee->income + pee->expenses);
00206 } while (++pee,--numec);
00207
00208 if (min_income > 0)
00209 _score_part[owner][SCORE_MIN_INCOME] = ClampToI32(min_income);
00210
00211 _score_part[owner][SCORE_MAX_INCOME] = ClampToI32(max_income);
00212 }
00213 }
00214
00215
00216 {
00217 const PlayerEconomyEntry* pee;
00218 int numec;
00219 uint32 total_delivered;
00220
00221 numec = min(p->num_valid_stat_ent, 4);
00222 if (numec != 0) {
00223 pee = p->old_economy;
00224 total_delivered = 0;
00225 do {
00226 total_delivered += pee->delivered_cargo;
00227 } while (++pee,--numec);
00228
00229 _score_part[owner][SCORE_DELIVERED] = total_delivered;
00230 }
00231 }
00232
00233
00234 {
00235 uint num = CountBits(p->cargo_types);
00236 _score_part[owner][SCORE_CARGO] = num;
00237 if (update) p->cargo_types = 0;
00238 }
00239
00240
00241 {
00242 if (p->player_money > 0) {
00243 _score_part[owner][SCORE_MONEY] = ClampToI32(p->player_money);
00244 }
00245 }
00246
00247
00248 {
00249 _score_part[owner][SCORE_LOAN] = ClampToI32(_score_info[SCORE_LOAN].needed - p->current_loan);
00250 }
00251
00252
00253 {
00254 int total_score = 0;
00255 int s;
00256 score = 0;
00257 for (ScoreID i = SCORE_BEGIN; i < SCORE_END; i++) {
00258
00259 if (i == SCORE_TOTAL) continue;
00260
00261 s = Clamp(_score_part[owner][i], 0, _score_info[i].needed) * _score_info[i].score / _score_info[i].needed;
00262 score += s;
00263 total_score += _score_info[i].score;
00264 }
00265
00266 _score_part[owner][SCORE_TOTAL] = score;
00267
00268
00269 if (total_score != SCORE_MAX) score = score * SCORE_MAX / total_score;
00270 }
00271
00272 if (update) {
00273 p->old_economy[0].performance_history = score;
00274 UpdateCompanyHQ(p, score);
00275 p->old_economy[0].company_value = CalculateCompanyValue(p);
00276 }
00277
00278 InvalidateWindow(WC_PERFORMANCE_DETAIL, 0);
00279 return score;
00280 }
00281
00282
00283 void ChangeOwnershipOfPlayerItems(PlayerID old_player, PlayerID new_player)
00284 {
00285 Town *t;
00286 PlayerID old = _current_player;
00287
00288 assert(old_player != new_player);
00289
00290 {
00291 Player *p;
00292 uint i;
00293
00294
00295 _current_player = old_player;
00296 FOR_ALL_PLAYERS(p) {
00297 if (!p->is_active) continue;
00298 for (i = 0; i < 4; i++) {
00299 if (p->share_owners[i] == old_player) {
00300
00301 CommandCost res = DoCommand(0, p->index, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
00302
00303
00304 SubtractMoneyFromPlayer(res);
00305 }
00306 }
00307 }
00308
00309
00310 p = GetPlayer(old_player);
00311 for (i = 0; i < 4; i++) {
00312 _current_player = p->share_owners[i];
00313 if (_current_player != PLAYER_SPECTATOR) {
00314
00315 CommandCost res = DoCommand(0, old_player, 0, DC_EXEC, CMD_SELL_SHARE_IN_COMPANY);
00316
00317
00318 SubtractMoneyFromPlayer(res);
00319 }
00320 }
00321 }
00322
00323 _current_player = old_player;
00324
00325
00326
00327
00328 if (new_player == PLAYER_SPECTATOR) {
00329 GetPlayer(old_player)->player_money = MAX_UVALUE(uint64) >> 2;
00330 }
00331
00332 if (new_player == PLAYER_SPECTATOR) {
00333 Subsidy *s;
00334
00335 for (s = _subsidies; s != endof(_subsidies); s++) {
00336 if (s->cargo_type != CT_INVALID && s->age >= 12) {
00337 if (GetStation(s->to)->owner == old_player) s->cargo_type = CT_INVALID;
00338 }
00339 }
00340 }
00341
00342
00343 FOR_ALL_TOWNS(t) {
00344
00345 if (new_player != PLAYER_SPECTATOR) {
00346 if (HasBit(t->have_ratings, old_player)) {
00347 if (HasBit(t->have_ratings, new_player)) {
00348
00349 t->ratings[new_player] = max(t->ratings[new_player], t->ratings[old_player]);
00350 } else {
00351 SetBit(t->have_ratings, new_player);
00352 t->ratings[new_player] = t->ratings[old_player];
00353 }
00354 }
00355 }
00356
00357
00358 t->ratings[old_player] = 500;
00359 ClrBit(t->have_ratings, old_player);
00360 }
00361
00362 {
00363 int num_train = 0;
00364 int num_road = 0;
00365 int num_ship = 0;
00366 int num_aircraft = 0;
00367 Vehicle *v;
00368
00369
00370 FOR_ALL_VEHICLES(v) {
00371 if (v->owner == new_player) {
00372 switch (v->type) {
00373 case VEH_TRAIN: if (IsFrontEngine(v)) num_train++; break;
00374 case VEH_ROAD: if (IsRoadVehFront(v)) num_road++; break;
00375 case VEH_SHIP: num_ship++; break;
00376 case VEH_AIRCRAFT: if (IsNormalAircraft(v)) num_aircraft++; break;
00377 default: break;
00378 }
00379 }
00380 }
00381
00382 FOR_ALL_VEHICLES(v) {
00383 if (v->owner == old_player && IsInsideMM(v->type, VEH_TRAIN, VEH_AIRCRAFT + 1)) {
00384 if (new_player == PLAYER_SPECTATOR) {
00385 DeleteWindowById(WC_VEHICLE_VIEW, v->index);
00386 DeleteWindowById(WC_VEHICLE_DETAILS, v->index);
00387 DeleteWindowById(WC_VEHICLE_ORDERS, v->index);
00388
00389 if (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && IsFreeWagon(v))) {
00390 switch (v->type) {
00391 default: NOT_REACHED();
00392
00393 case VEH_TRAIN: {
00394 Vehicle *u = v;
00395 do {
00396 Vehicle *next = GetNextVehicle(u);
00397 delete u;
00398 u = next;
00399 } while (u != NULL);
00400 } break;
00401
00402 case VEH_ROAD:
00403 case VEH_SHIP:
00404 delete v;
00405 break;
00406
00407 case VEH_AIRCRAFT:
00408 DeleteVehicleChain(v);
00409 break;
00410 }
00411 }
00412 } else {
00413 v->owner = new_player;
00414 v->colormap = PAL_NONE;
00415 v->group_id = DEFAULT_GROUP;
00416 if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
00417 switch (v->type) {
00418 case VEH_TRAIN: if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;
00419 case VEH_ROAD: if (IsRoadVehFront(v)) v->unitnumber = ++num_road; break;
00420 case VEH_SHIP: v->unitnumber = ++num_ship; break;
00421 case VEH_AIRCRAFT: if (IsNormalAircraft(v)) v->unitnumber = ++num_aircraft; break;
00422 default: NOT_REACHED();
00423 }
00424 }
00425 }
00426 }
00427 }
00428
00429
00430 {
00431 TileIndex tile = 0;
00432 do {
00433 ChangeTileOwner(tile, old_player, new_player);
00434 } while (++tile != MapSize());
00435
00436 if (new_player != PLAYER_SPECTATOR) {
00437
00438
00439
00440
00441 tile = 0;
00442
00443 do {
00444 if (IsTileType(tile, MP_RAILWAY) && IsTileOwner(tile, new_player) && HasSignals(tile)) {
00445 TrackBits tracks = GetTrackBits(tile);
00446 do {
00447 Track track = RemoveFirstTrack(&tracks);
00448 if (HasSignalOnTrack(tile, track)) AddTrackToSignalBuffer(tile, track, new_player);
00449 } while (tracks != TRACK_BIT_NONE);
00450 } else if (IsLevelCrossingTile(tile) && IsTileOwner(tile, new_player)) {
00451 UpdateLevelCrossing(tile);
00452 }
00453 } while (++tile != MapSize());
00454 }
00455
00456
00457 UpdateSignalsInBuffer();
00458 }
00459
00460
00461
00462 RemoveAllEngineReplacementForPlayer(GetPlayer(old_player));
00463
00464 if (new_player == PLAYER_SPECTATOR) {
00465 RemoveAllGroupsForPlayer(old_player);
00466 } else {
00467 Group *g;
00468 FOR_ALL_GROUPS(g) {
00469 if (g->owner == old_player) g->owner = new_player;
00470 }
00471 }
00472
00473 Sign *si;
00474 FOR_ALL_SIGNS(si) {
00475 if (si->owner == old_player) si->owner = new_player == PLAYER_SPECTATOR ? OWNER_NONE : new_player;
00476 }
00477
00478
00479 if (new_player != PLAYER_SPECTATOR) ChangeWindowOwner(old_player, new_player);
00480
00481 _current_player = old;
00482
00483 MarkWholeScreenDirty();
00484 }
00485
00486 static void ChangeNetworkOwner(PlayerID current_player, PlayerID new_player)
00487 {
00488 #ifdef ENABLE_NETWORK
00489 if (!_networking) return;
00490
00491 if (current_player == _local_player) {
00492 _network_playas = new_player;
00493 SetLocalPlayer(new_player);
00494 }
00495
00496 if (!_network_server) return;
00497
00498
00499
00500 NetworkTCPSocketHandler *cs;
00501 NetworkClientInfo *ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
00502
00503
00504 if (current_player == ci->client_playas) {
00505 ci->client_playas = new_player;
00506 NetworkUpdateClientInfo(NETWORK_SERVER_INDEX);
00507 }
00508
00509
00510 FOR_ALL_CLIENTS(cs) {
00511 ci = DEREF_CLIENT_INFO(cs);
00512 if (current_player == ci->client_playas) {
00513 ci->client_playas = new_player;
00514 NetworkUpdateClientInfo(ci->client_index);
00515 }
00516 }
00517 #endif
00518 }
00519
00520 static void PlayersCheckBankrupt(Player *p)
00521 {
00522 PlayerID owner;
00523
00524
00525 if (p->player_money >= 0) {
00526 p->quarters_of_bankrupcy = 0;
00527 return;
00528 }
00529
00530 p->quarters_of_bankrupcy++;
00531
00532 owner = p->index;
00533
00534 switch (p->quarters_of_bankrupcy) {
00535 case 2:
00536 AddNewsItem( (StringID)(owner | NB_BTROUBLE),
00537 NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
00538 break;
00539 case 3: {
00540
00541
00542 if (IsHumanPlayer(owner)) {
00543 AddNewsItem( (StringID)(owner | NB_BTROUBLE),
00544 NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
00545 break;
00546 }
00547
00548
00549
00550 Money val = CalculateCompanyValue(p);
00551 if (val > 0) {
00552 p->bankrupt_value = val;
00553 p->bankrupt_asked = 1 << owner;
00554 p->bankrupt_timeout = 0;
00555 break;
00556 }
00557
00558 }
00559 case 4: {
00560
00561 DeletePlayerWindows(owner);
00562
00563
00564 SetDParam(0, p->index);
00565 AddNewsItem( (StringID)(owner | NB_BBANKRUPT), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
00566
00567 if (IsHumanPlayer(owner)) {
00568
00569
00570
00571 if (!_networking) {
00572 p->bankrupt_asked = 0xFF;
00573 p->bankrupt_timeout = 0x456;
00574 break;
00575 }
00576
00577 ChangeNetworkOwner(owner, PLAYER_SPECTATOR);
00578 }
00579
00580
00581 ChangeOwnershipOfPlayerItems(owner, PLAYER_SPECTATOR);
00582
00583 p->is_active = false;
00584
00585 if (!IsHumanPlayer(owner) && (!_networking || _network_server) && _ai.enabled)
00586 AI_PlayerDied(owner);
00587 }
00588 }
00589 }
00590
00591 void DrawNewsBankrupcy(Window *w)
00592 {
00593 DrawNewsBorder(w);
00594
00595 const NewsItem *ni = WP(w, news_d).ni;
00596 Player *p = GetPlayer((PlayerID)GB(ni->string_id, 0, 4));
00597 DrawPlayerFace(p->face, p->player_color, 2, 23);
00598 GfxFillRect(3, 23, 3 + 91, 23 + 118, PALETTE_TO_STRUCT_GREY | (1 << USE_COLORTABLE));
00599
00600 SetDParam(0, p->index);
00601
00602 DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94);
00603
00604 switch (ni->string_id & 0xF0) {
00605 case NB_BTROUBLE:
00606 DrawStringCentered(w->width >> 1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, TC_FROMSTRING);
00607
00608 SetDParam(0, p->index);
00609
00610 DrawStringMultiCenter(
00611 ((w->width - 101) >> 1) + 98,
00612 90,
00613 STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED,
00614 w->width - 101);
00615 break;
00616
00617 case NB_BMERGER:
00618 DrawStringCentered(w->width >> 1, 1, STR_7059_TRANSPORT_COMPANY_MERGER, TC_FROMSTRING);
00619 SetDParam(0, ni->params[0]);
00620 SetDParam(1, p->index);
00621 SetDParam(2, ni->params[1]);
00622 DrawStringMultiCenter(
00623 ((w->width - 101) >> 1) + 98,
00624 90,
00625 ni->params[1] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR,
00626 w->width - 101);
00627 break;
00628
00629 case NB_BBANKRUPT:
00630 DrawStringCentered(w->width >> 1, 1, STR_705C_BANKRUPT, TC_FROMSTRING);
00631 SetDParam(0, ni->params[0]);
00632 DrawStringMultiCenter(
00633 ((w->width - 101) >> 1) + 98,
00634 90,
00635 STR_705D_HAS_BEEN_CLOSED_DOWN_BY,
00636 w->width - 101);
00637 break;
00638
00639 case NB_BNEWCOMPANY:
00640 DrawStringCentered(w->width >> 1, 1, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED, TC_FROMSTRING);
00641 SetDParam(0, p->index);
00642 SetDParam(1, ni->params[0]);
00643 DrawStringMultiCenter(
00644 ((w->width - 101) >> 1) + 98,
00645 90,
00646 STR_705F_STARTS_CONSTRUCTION_NEAR,
00647 w->width - 101);
00648 break;
00649
00650 default:
00651 NOT_REACHED();
00652 }
00653 }
00654
00655 StringID GetNewsStringBankrupcy(const NewsItem *ni)
00656 {
00657 const Player *p = GetPlayer((PlayerID)GB(ni->string_id, 0, 4));
00658
00659 switch (ni->string_id & 0xF0) {
00660 case NB_BTROUBLE:
00661 SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE);
00662 SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED);
00663 SetDParam(2, p->index);
00664 return STR_02B6;
00665 case NB_BMERGER:
00666 SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER);
00667 SetDParam(1, ni->params[1] == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR);
00668 SetDParam(2, ni->params[0]);
00669 SetDParam(3, p->index);
00670 SetDParam(4, ni->params[1]);
00671 return STR_02B6;
00672 case NB_BBANKRUPT:
00673 SetDParam(0, STR_705C_BANKRUPT);
00674 SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY);
00675 SetDParam(2, ni->params[0]);
00676 return STR_02B6;
00677 case NB_BNEWCOMPANY:
00678 SetDParam(0, STR_705E_NEW_TRANSPORT_COMPANY_LAUNCHED);
00679 SetDParam(1, STR_705F_STARTS_CONSTRUCTION_NEAR);
00680 SetDParam(2, p->index);
00681 SetDParam(3, ni->params[0]);
00682 return STR_02B6;
00683 default:
00684 NOT_REACHED();
00685 }
00686 }
00687
00688 static void PlayersGenStatistics()
00689 {
00690 Station *st;
00691 Player *p;
00692
00693 FOR_ALL_STATIONS(st) {
00694 _current_player = st->owner;
00695 CommandCost cost(EXPENSES_PROPERTY, _price.station_value >> 1);
00696 SubtractMoneyFromPlayer(cost);
00697 }
00698
00699 if (!HasBit(1<<0|1<<3|1<<6|1<<9, _cur_month))
00700 return;
00701
00702 FOR_ALL_PLAYERS(p) {
00703 if (p->is_active) {
00704 memmove(&p->old_economy[1], &p->old_economy[0], sizeof(p->old_economy) - sizeof(p->old_economy[0]));
00705 p->old_economy[0] = p->cur_economy;
00706 memset(&p->cur_economy, 0, sizeof(p->cur_economy));
00707
00708 if (p->num_valid_stat_ent != 24) p->num_valid_stat_ent++;
00709
00710 UpdateCompanyRatingAndValue(p, true);
00711 PlayersCheckBankrupt(p);
00712
00713 if (p->block_preview != 0) p->block_preview--;
00714 }
00715 }
00716
00717 InvalidateWindow(WC_INCOME_GRAPH, 0);
00718 InvalidateWindow(WC_OPERATING_PROFIT, 0);
00719 InvalidateWindow(WC_DELIVERED_CARGO, 0);
00720 InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
00721 InvalidateWindow(WC_COMPANY_VALUE, 0);
00722 InvalidateWindow(WC_COMPANY_LEAGUE, 0);
00723 }
00724
00725 static void AddSingleInflation(Money *value, uint16 *frac, int32 amt)
00726 {
00727
00728 if ((INT64_MAX / amt) < (*value + 1)) {
00729 *value = INT64_MAX / amt;
00730 *frac = 0;
00731 } else {
00732 int64 tmp = (int64)*value * amt + *frac;
00733 *frac = GB(tmp, 0, 16);
00734 *value += tmp >> 16;
00735 }
00736 }
00737
00738 static void AddInflation()
00739 {
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755 if ((_cur_year - _patches.starting_year) >= (ORIGINAL_MAX_YEAR - ORIGINAL_BASE_YEAR)) return;
00756
00757
00758
00759
00760
00761
00762 Money inf = _economy.infl_amount * 54;
00763
00764 for (uint i = 0; i != NUM_PRICES; i++) {
00765 AddSingleInflation((Money*)&_price + i, _price_frac + i, inf);
00766 }
00767
00768 AddSingleInflation(&_economy.max_loan_unround, &_economy.max_loan_unround_fract, inf);
00769
00770 if (_economy.max_loan + 50000 <= _economy.max_loan_unround) _economy.max_loan += 50000;
00771
00772 inf = _economy.infl_amount_pr * 54;
00773 for (CargoID i = 0; i < NUM_CARGO; i++) {
00774 AddSingleInflation(
00775 (Money*)_cargo_payment_rates + i,
00776 _cargo_payment_rates_frac + i,
00777 inf
00778 );
00779 }
00780
00781 InvalidateWindowClasses(WC_BUILD_VEHICLE);
00782 InvalidateWindowClasses(WC_REPLACE_VEHICLE);
00783 InvalidateWindowClasses(WC_VEHICLE_DETAILS);
00784 InvalidateWindow(WC_PAYMENT_RATES, 0);
00785 }
00786
00787 static void PlayersPayInterest()
00788 {
00789 const Player* p;
00790 int interest = _economy.interest_rate * 54;
00791
00792 FOR_ALL_PLAYERS(p) {
00793 if (!p->is_active) continue;
00794
00795 _current_player = p->index;
00796
00797 SubtractMoneyFromPlayer(CommandCost(EXPENSES_LOAN_INT, (Money)BigMulSU(p->current_loan, interest, 16)));
00798
00799 SubtractMoneyFromPlayer(CommandCost(EXPENSES_OTHER, _price.station_value >> 2));
00800 }
00801 }
00802
00803 static void HandleEconomyFluctuations()
00804 {
00805 if (_opt.diff.economy == 0) return;
00806
00807 if (--_economy.fluct == 0) {
00808 _economy.fluct = -(int)GB(Random(), 0, 2);
00809 AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
00810 } else if (_economy.fluct == -12) {
00811 _economy.fluct = GB(Random(), 0, 8) + 312;
00812 AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NEWS_FLAGS(NM_NORMAL,0,NT_ECONOMY,0), 0, 0);
00813 }
00814 }
00815
00816 static byte _price_category[NUM_PRICES] = {
00817 0, 2, 2, 2, 2, 2, 2, 2,
00818 2, 2, 2, 2, 2, 2, 2, 2,
00819 2, 2, 2, 2, 2, 2, 2, 2,
00820 2, 2, 2, 2, 2, 2, 2, 2,
00821 2, 2, 2, 2, 2, 2, 2, 2,
00822 2, 2, 1, 1, 1, 1, 1, 1,
00823 2,
00824 };
00825
00826 static const Money _price_base[NUM_PRICES] = {
00827 100,
00828 100,
00829 95,
00830 65,
00831 275,
00832 600,
00833 500,
00834 700,
00835 450,
00836 200,
00837 180,
00838 600,
00839 200,
00840 200,
00841 350,
00842 400000,
00843 2000,
00844 700000,
00845 14000,
00846 65000,
00847 20,
00848 250,
00849 20,
00850 40,
00851 200,
00852 500,
00853 20,
00854 -70,
00855 10,
00856 50,
00857 80,
00858 80,
00859 90,
00860 30,
00861 10000,
00862 50,
00863 30,
00864 50,
00865 50,
00866 55,
00867 1600,
00868 40,
00869 5600,
00870 5200,
00871 4800,
00872 9600,
00873 1600,
00874 5600,
00875 1000000,
00876 };
00877
00878 static byte price_base_multiplier[NUM_PRICES];
00879
00883 void ResetPriceBaseMultipliers()
00884 {
00885 uint i;
00886
00887
00888 for (i = 0; i < NUM_PRICES; i++)
00889 price_base_multiplier[i] = 8;
00890 }
00891
00899 void SetPriceBaseMultiplier(uint price, byte factor)
00900 {
00901 assert(price < NUM_PRICES);
00902 price_base_multiplier[price] = factor;
00903 }
00904
00905 void StartupEconomy()
00906 {
00907 int i;
00908
00909 assert(sizeof(_price) == NUM_PRICES * sizeof(Money));
00910
00911 for (i = 0; i != NUM_PRICES; i++) {
00912 Money price = _price_base[i];
00913 if (_price_category[i] != 0) {
00914 uint mod = _price_category[i] == 1 ? _opt.diff.vehicle_costs : _opt.diff.construction_cost;
00915 if (mod < 1) {
00916 price = price * 3 >> 2;
00917 } else if (mod > 1) {
00918 price = price * 9 >> 3;
00919 }
00920 }
00921 if (price_base_multiplier[i] > 8) {
00922 price <<= price_base_multiplier[i] - 8;
00923 } else {
00924 price >>= 8 - price_base_multiplier[i];
00925 }
00926 ((Money*)&_price)[i] = price;
00927 _price_frac[i] = 0;
00928 }
00929
00930 _economy.interest_rate = _opt.diff.initial_interest;
00931 _economy.infl_amount = _opt.diff.initial_interest;
00932 _economy.infl_amount_pr = max(0, _opt.diff.initial_interest - 1);
00933 _economy.max_loan_unround = _economy.max_loan = _opt.diff.max_loan * 1000;
00934 _economy.fluct = GB(Random(), 0, 8) + 168;
00935 }
00936
00937
00938 Money GetPriceByIndex(uint8 index)
00939 {
00940 if (index > NUM_PRICES) return 0;
00941
00942 return ((Money*)&_price)[index];
00943 }
00944
00945
00946 Pair SetupSubsidyDecodeParam(const Subsidy* s, bool mode)
00947 {
00948 TileIndex tile;
00949 TileIndex tile2;
00950 Pair tp;
00951
00952
00953 const CargoSpec *cs = GetCargo(s->cargo_type);
00954 SetDParam(0, mode ? cs->name : cs->name_single);
00955
00956 if (s->age < 12) {
00957 if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL) {
00958 SetDParam(1, STR_INDUSTRY);
00959 SetDParam(2, s->from);
00960 tile = GetIndustry(s->from)->xy;
00961
00962 if (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD) {
00963 SetDParam(4, STR_INDUSTRY);
00964 SetDParam(5, s->to);
00965 tile2 = GetIndustry(s->to)->xy;
00966 } else {
00967 SetDParam(4, STR_TOWN);
00968 SetDParam(5, s->to);
00969 tile2 = GetTown(s->to)->xy;
00970 }
00971 } else {
00972 SetDParam(1, STR_TOWN);
00973 SetDParam(2, s->from);
00974 tile = GetTown(s->from)->xy;
00975
00976 SetDParam(4, STR_TOWN);
00977 SetDParam(5, s->to);
00978 tile2 = GetTown(s->to)->xy;
00979 }
00980 } else {
00981 SetDParam(1, s->from);
00982 tile = GetStation(s->from)->xy;
00983
00984 SetDParam(2, s->to);
00985 tile2 = GetStation(s->to)->xy;
00986 }
00987
00988 tp.a = tile;
00989 tp.b = tile2;
00990
00991 return tp;
00992 }
00993
00994 void DeleteSubsidyWithTown(TownID index)
00995 {
00996 Subsidy *s;
00997
00998 for (s = _subsidies; s != endof(_subsidies); s++) {
00999 if (s->cargo_type != CT_INVALID && s->age < 12) {
01000 const CargoSpec *cs = GetCargo(s->cargo_type);
01001 if (((cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) && (index == s->from || index == s->to)) ||
01002 ((cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) && index == s->to)) {
01003 s->cargo_type = CT_INVALID;
01004 }
01005 }
01006 }
01007 }
01008
01009 void DeleteSubsidyWithIndustry(IndustryID index)
01010 {
01011 Subsidy *s;
01012
01013 for (s = _subsidies; s != endof(_subsidies); s++) {
01014 if (s->cargo_type != CT_INVALID && s->age < 12) {
01015 const CargoSpec *cs = GetCargo(s->cargo_type);
01016 if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL &&
01017 (index == s->from || (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD && index == s->to))) {
01018 s->cargo_type = CT_INVALID;
01019 }
01020 }
01021 }
01022 }
01023
01024 void DeleteSubsidyWithStation(StationID index)
01025 {
01026 Subsidy *s;
01027 bool dirty = false;
01028
01029 for (s = _subsidies; s != endof(_subsidies); s++) {
01030 if (s->cargo_type != CT_INVALID && s->age >= 12 &&
01031 (s->from == index || s->to == index)) {
01032 s->cargo_type = CT_INVALID;
01033 dirty = true;
01034 }
01035 }
01036
01037 if (dirty)
01038 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
01039 }
01040
01041 struct FoundRoute {
01042 uint distance;
01043 CargoID cargo;
01044 void *from;
01045 void *to;
01046 };
01047
01048 static void FindSubsidyPassengerRoute(FoundRoute *fr)
01049 {
01050 Town *from,*to;
01051
01052 fr->distance = (uint)-1;
01053
01054 fr->from = from = GetRandomTown();
01055 if (from == NULL || from->population < 400) return;
01056
01057 fr->to = to = GetRandomTown();
01058 if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42)
01059 return;
01060
01061 fr->distance = DistanceManhattan(from->xy, to->xy);
01062 }
01063
01064 static void FindSubsidyCargoRoute(FoundRoute *fr)
01065 {
01066 Industry *i;
01067 int trans, total;
01068 CargoID cargo;
01069
01070 fr->distance = (uint)-1;
01071
01072 fr->from = i = GetRandomIndustry();
01073 if (i == NULL) return;
01074
01075
01076 if (HasBit(Random(), 0) && i->produced_cargo[1] != CT_INVALID) {
01077 cargo = i->produced_cargo[1];
01078 trans = i->last_month_pct_transported[1];
01079 total = i->last_month_production[1];
01080 } else {
01081 cargo = i->produced_cargo[0];
01082 trans = i->last_month_pct_transported[0];
01083 total = i->last_month_production[0];
01084 }
01085
01086
01087
01088
01089 if (total == 0 || trans > 42 || cargo == CT_INVALID) return;
01090
01091 const CargoSpec *cs = GetCargo(cargo);
01092 if (cs->town_effect == TE_PASSENGERS) return;
01093
01094 fr->cargo = cargo;
01095
01096 if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
01097
01098 Town *t = GetRandomTown();
01099
01100
01101 if (t == NULL || t->population < 900) return;
01102
01103 fr->distance = DistanceManhattan(i->xy, t->xy);
01104 fr->to = t;
01105 } else {
01106
01107 Industry *i2 = GetRandomIndustry();
01108
01109
01110 if (i2 == NULL || i == i2 ||
01111 (cargo != i2->accepts_cargo[0] &&
01112 cargo != i2->accepts_cargo[1] &&
01113 cargo != i2->accepts_cargo[2])) {
01114 return;
01115 }
01116 fr->distance = DistanceManhattan(i->xy, i2->xy);
01117 fr->to = i2;
01118 }
01119 }
01120
01121 static bool CheckSubsidyDuplicate(Subsidy *s)
01122 {
01123 const Subsidy* ss;
01124
01125 for (ss = _subsidies; ss != endof(_subsidies); ss++) {
01126 if (s != ss &&
01127 ss->from == s->from &&
01128 ss->to == s->to &&
01129 ss->cargo_type == s->cargo_type) {
01130 s->cargo_type = CT_INVALID;
01131 return true;
01132 }
01133 }
01134 return false;
01135 }
01136
01137
01138 static void SubsidyMonthlyHandler()
01139 {
01140 Subsidy *s;
01141 Pair pair;
01142 Station *st;
01143 uint n;
01144 FoundRoute fr;
01145 bool modified = false;
01146
01147 for (s = _subsidies; s != endof(_subsidies); s++) {
01148 if (s->cargo_type == CT_INVALID) continue;
01149
01150 if (s->age == 12-1) {
01151 pair = SetupSubsidyDecodeParam(s, 1);
01152 AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
01153 s->cargo_type = CT_INVALID;
01154 modified = true;
01155 } else if (s->age == 2*12-1) {
01156 st = GetStation(s->to);
01157 if (st->owner == _local_player) {
01158 pair = SetupSubsidyDecodeParam(s, 1);
01159 AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
01160 }
01161 s->cargo_type = CT_INVALID;
01162 modified = true;
01163 } else {
01164 s->age++;
01165 }
01166 }
01167
01168
01169 if (Chance16(1,4)) {
01170
01171 s = _subsidies;
01172 while (s->cargo_type != CT_INVALID) {
01173 if (++s == endof(_subsidies))
01174 goto no_add;
01175 }
01176
01177 n = 1000;
01178 do {
01179 FindSubsidyPassengerRoute(&fr);
01180 if (fr.distance <= 70) {
01181 s->cargo_type = CT_PASSENGERS;
01182 s->from = ((Town*)fr.from)->index;
01183 s->to = ((Town*)fr.to)->index;
01184 goto add_subsidy;
01185 }
01186 FindSubsidyCargoRoute(&fr);
01187 if (fr.distance <= 70) {
01188 s->cargo_type = fr.cargo;
01189 s->from = ((Industry*)fr.from)->index;
01190 {
01191 const CargoSpec *cs = GetCargo(fr.cargo);
01192 s->to = (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
01193 }
01194 add_subsidy:
01195 if (!CheckSubsidyDuplicate(s)) {
01196 s->age = 0;
01197 pair = SetupSubsidyDecodeParam(s, 0);
01198 AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
01199 modified = true;
01200 break;
01201 }
01202 }
01203 } while (n--);
01204 }
01205 no_add:;
01206 if (modified)
01207 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
01208 }
01209
01210 static const SaveLoad _subsidies_desc[] = {
01211 SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
01212 SLE_VAR(Subsidy, age, SLE_UINT8),
01213 SLE_CONDVAR(Subsidy, from, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
01214 SLE_CONDVAR(Subsidy, from, SLE_UINT16, 5, SL_MAX_VERSION),
01215 SLE_CONDVAR(Subsidy, to, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
01216 SLE_CONDVAR(Subsidy, to, SLE_UINT16, 5, SL_MAX_VERSION),
01217 SLE_END()
01218 };
01219
01220 static void Save_SUBS()
01221 {
01222 int i;
01223 Subsidy *s;
01224
01225 for (i = 0; i != lengthof(_subsidies); i++) {
01226 s = &_subsidies[i];
01227 if (s->cargo_type != CT_INVALID) {
01228 SlSetArrayIndex(i);
01229 SlObject(s, _subsidies_desc);
01230 }
01231 }
01232 }
01233
01234 static void Load_SUBS()
01235 {
01236 int index;
01237 while ((index = SlIterateArray()) != -1)
01238 SlObject(&_subsidies[index], _subsidies_desc);
01239 }
01240
01241 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
01242 {
01243 const CargoSpec *cs = GetCargo(cargo_type);
01244
01245
01246 if (HasBit(cs->callback_mask, CBM_CARGO_PROFIT_CALC)) {
01247 uint32 var18 = min(dist, 0xFFFF) | (min(num_pieces, 0xFF) << 16) | (transit_days << 24);
01248 uint16 callback = GetCargoCallback(CBID_CARGO_PROFIT_CALC, 0, var18, cs);
01249 if (callback != CALLBACK_FAILED) {
01250 int result = GB(callback, 0, 14);
01251
01252
01253 if (HasBit(callback, 14)) result = 0x4000 - result;
01254
01255
01256
01257
01258 return result * num_pieces * _cargo_payment_rates[cargo_type] / 8192;
01259 }
01260 }
01261
01262
01263 if (_opt.landscape == LT_TEMPERATE && cs->label == 'VALU' && dist < 10) return 0;
01264
01265
01266 static const int MIN_TIME_FACTOR = 31;
01267 static const int MAX_TIME_FACTOR = 255;
01268
01269 const int days1 = cs->transit_days[0];
01270 const int days2 = cs->transit_days[1];
01271 const int days_over_days1 = transit_days - days1;
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283 int time_factor;
01284 if (days_over_days1 <= 0) {
01285 time_factor = MAX_TIME_FACTOR;
01286 } else if (days_over_days1 <= days2) {
01287 time_factor = MAX_TIME_FACTOR - days_over_days1;
01288 } else {
01289 time_factor = MAX_TIME_FACTOR - 2 * days_over_days1 + days2;
01290 }
01291
01292 if (time_factor < MIN_TIME_FACTOR) time_factor = MIN_TIME_FACTOR;
01293
01294 return BigMulS(dist * time_factor * num_pieces, _cargo_payment_rates[cargo_type], 21);
01295 }
01296
01297 static void DeliverGoodsToIndustry(TileIndex xy, CargoID cargo_type, int num_pieces)
01298 {
01299 Industry *best = NULL;
01300 Industry *ind;
01301 const IndustrySpec *indspec;
01302 uint best_dist;
01303 uint accepted_cargo_index = 0;
01304
01305
01306
01307
01308
01309 best_dist = (_patches.station_spread + 8) * 2;
01310 FOR_ALL_INDUSTRIES(ind) {
01311 indspec = GetIndustrySpec(ind->type);
01312 uint i;
01313
01314 for (i = 0; i < lengthof(ind->accepts_cargo); i++) {
01315 if (cargo_type == ind->accepts_cargo[i]) break;
01316 }
01317
01318
01319 if (i == lengthof(ind->accepts_cargo)) continue;
01320
01321 if (HasBit(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) {
01322 uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO, 0, GetReverseCargoTranslation(cargo_type, indspec->grf_prop.grffile), ind, ind->type, ind->xy);
01323 if (res == 0) continue;
01324 }
01325
01326 uint dist = DistanceManhattan(ind->xy, xy);
01327
01328 if (dist < best_dist) {
01329 best = ind;
01330 best_dist = dist;
01331 accepted_cargo_index = i;
01332 }
01333 }
01334
01335
01336 if (best != NULL) {
01337 indspec = GetIndustrySpec(best->type);
01338 uint16 callback = indspec->callback_flags;
01339
01340 best->was_cargo_delivered = true;
01341 best->last_cargo_accepted_at = _date;
01342
01343 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
01344 best->incoming_cargo_waiting[accepted_cargo_index] = min(num_pieces + best->incoming_cargo_waiting[accepted_cargo_index], 0xFFFF);
01345 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) {
01346 IndustryProductionCallback(best, 0);
01347 } else {
01348 InvalidateWindow(WC_INDUSTRY_VIEW, best->index);
01349 }
01350 } else {
01351 best->produced_cargo_waiting[0] = min(best->produced_cargo_waiting[0] + (num_pieces * indspec->input_cargo_multiplier[accepted_cargo_index][0] / 256), 0xFFFF);
01352 best->produced_cargo_waiting[1] = min(best->produced_cargo_waiting[1] + (num_pieces * indspec->input_cargo_multiplier[accepted_cargo_index][1] / 256), 0xFFFF);
01353 }
01354
01355 TriggerIndustry(best, INDUSTRY_TRIGGER_RECEIVED_CARGO);
01356 StartStopIndustryTileAnimation(best, IAT_INDUSTRY_RECEIVED_CARGO);
01357 }
01358 }
01359
01360 static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type)
01361 {
01362 Subsidy *s;
01363 TileIndex xy;
01364 Pair pair;
01365
01366
01367 for (s = _subsidies; s != endof(_subsidies); s++) {
01368 if (s->cargo_type == cargo_type &&
01369 s->age >= 12 &&
01370 s->from == from->index &&
01371 s->to == to->index) {
01372 return true;
01373 }
01374 }
01375
01376
01377 for (s = _subsidies; s != endof(_subsidies); s++) {
01378 if (s->cargo_type == cargo_type && s->age < 12) {
01379
01380 const CargoSpec *cs = GetCargo(cargo_type);
01381 if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) {
01382 xy = GetTown(s->from)->xy;
01383 } else {
01384 xy = (GetIndustry(s->from))->xy;
01385 }
01386 if (DistanceMax(xy, from->xy) > 9) continue;
01387
01388
01389 switch (cs->town_effect) {
01390 case TE_PASSENGERS:
01391 case TE_MAIL:
01392 case TE_GOODS:
01393 case TE_FOOD:
01394 xy = GetTown(s->to)->xy;
01395 break;
01396
01397 default:
01398 xy = GetIndustry(s->to)->xy;
01399 break;
01400 }
01401 if (DistanceMax(xy, to->xy) > 9) continue;
01402
01403
01404 s->age = 12;
01405 s->from = from->index;
01406 s->to = to->index;
01407
01408
01409 pair = SetupSubsidyDecodeParam(s, 0);
01410 InjectDParam(1);
01411
01412 SetDParam(0, _current_player);
01413 AddNewsItem(
01414 STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier,
01415 NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0),
01416 pair.a, pair.b
01417 );
01418
01419 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
01420 return true;
01421 }
01422 }
01423 return false;
01424 }
01425
01426 static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit)
01427 {
01428 bool subsidised;
01429 Station *s_from, *s_to;
01430 Money profit;
01431
01432 assert(num_pieces > 0);
01433
01434
01435 {
01436 Player *p = GetPlayer(_current_player);
01437 p->cur_economy.delivered_cargo += num_pieces;
01438 SetBit(p->cargo_types, cargo_type);
01439 }
01440
01441
01442 s_from = GetStation(source);
01443 s_to = GetStation(dest);
01444
01445
01446 subsidised = CheckSubsidised(s_from, s_to, cargo_type);
01447
01448
01449 const CargoSpec *cs = GetCargo(cargo_type);
01450 if (cs->town_effect == TE_FOOD) s_to->town->new_act_food += num_pieces;
01451 if (cs->town_effect == TE_WATER) s_to->town->new_act_water += num_pieces;
01452
01453
01454 DeliverGoodsToIndustry(s_to->xy, cargo_type, num_pieces);
01455
01456
01457 profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(source_tile, s_to->xy), days_in_transit, cargo_type);
01458
01459
01460 if (subsidised) {
01461 switch (_opt.diff.subsidy_multiplier) {
01462 case 0: profit += profit >> 1; break;
01463 case 1: profit *= 2; break;
01464 case 2: profit *= 3; break;
01465 default: profit *= 4; break;
01466 }
01467 }
01468
01469 return profit;
01470 }
01471
01476 void VehiclePayment(Vehicle *front_v)
01477 {
01478 int result = 0;
01479
01480 Money vehicle_profit = 0;
01481 Money route_profit = 0;
01482 Money virtual_profit = 0;
01483
01484 StationID last_visited = front_v->last_station_visited;
01485 Station *st = GetStation(last_visited);
01486
01487
01488 PlayerID old_player = _current_player;
01489 _current_player = front_v->owner;
01490
01491
01492 ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED);
01493
01494
01495 front_v->load_unload_time_rem = 1;
01496
01497 for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
01498
01499 if (v->cargo_cap == 0 || v->cargo.Empty()) continue;
01500
01501
01502 if (!v->cargo.UnpaidCargo()) {
01503 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01504 continue;
01505 }
01506
01507 GoodsEntry *ge = &st->goods[v->cargo_type];
01508 const CargoList::List *cargos = v->cargo.Packets();
01509
01510 for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) {
01511 CargoPacket *cp = *it;
01512 if (!cp->paid_for &&
01513 cp->source != last_visited &&
01514 HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) &&
01515 (front_v->current_order.flags & OFB_TRANSFER) == 0) {
01516
01517 st->time_since_unload = 0;
01518
01519
01520 Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit);
01521 cp->paid_for = true;
01522 route_profit += profit;
01523 vehicle_profit += profit - cp->feeder_share;
01524
01525 result |= 1;
01526
01527 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01528 } else if (front_v->current_order.flags & (OFB_UNLOAD | OFB_TRANSFER)) {
01529 if (!cp->paid_for && (front_v->current_order.flags & OFB_TRANSFER) != 0) {
01530 Money profit = GetTransportedGoodsIncome(
01531 cp->count,
01532
01533 DistanceManhattan(cp->loaded_at_xy, GetStation(last_visited)->xy),
01534 cp->days_in_transit,
01535 v->cargo_type);
01536
01537 front_v->profit_this_year += profit << 8;
01538 virtual_profit += profit;
01539 cp->feeder_share += profit;
01540 cp->paid_for = true;
01541 }
01542 result |= 2;
01543
01544 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01545 }
01546 }
01547 v->cargo.InvalidateCache();
01548 }
01549
01550 if (virtual_profit > 0) {
01551 ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit);
01552 }
01553
01554 if (route_profit != 0) {
01555 front_v->profit_this_year += vehicle_profit << 8;
01556 SubtractMoneyFromPlayer(CommandCost(front_v->GetExpenseType(true), -route_profit));
01557
01558 if (IsLocalPlayer() && !PlayVehicleSound(front_v, VSE_LOAD_UNLOAD)) {
01559 SndPlayVehicleFx(SND_14_CASHTILL, front_v);
01560 }
01561
01562 ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit);
01563 }
01564
01565 _current_player = old_player;
01566 }
01567
01576 static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
01577 {
01578 assert(v->current_order.type == OT_LOADING);
01579
01580
01581 if (--v->load_unload_time_rem != 0) {
01582 if (_patches.improved_load && HasBit(v->current_order.flags, OF_FULL_LOAD)) {
01583
01584 for (; v != NULL; v = v->Next()) {
01585 if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
01586 }
01587 }
01588 return;
01589 }
01590
01591 StationID last_visited = v->last_station_visited;
01592 Station *st = GetStation(last_visited);
01593
01594 if (v->type == VEH_TRAIN && (!IsTileType(v->tile, MP_STATION) || GetStationIndex(v->tile) != st->index)) {
01595
01596
01597 SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
01598 return;
01599 }
01600
01601 int unloading_time = 0;
01602 Vehicle *u = v;
01603 int result = 0;
01604 uint cap;
01605
01606 bool completely_emptied = true;
01607 bool anything_unloaded = false;
01608 bool anything_loaded = false;
01609 uint32 cargo_not_full = 0;
01610 uint32 cargo_full = 0;
01611
01612 v->cur_speed = 0;
01613
01614 for (; v != NULL; v = v->Next()) {
01615 if (v->cargo_cap == 0) continue;
01616
01617 byte load_amount = EngInfo(v->engine_type)->load_amount;
01618 if (_patches.gradual_loading && HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_LOAD_AMOUNT)) {
01619 uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
01620 if (cb_load_amount != CALLBACK_FAILED && GB(cb_load_amount, 0, 8) != 0) load_amount = GB(cb_load_amount, 0, 8);
01621 }
01622
01623 GoodsEntry *ge = &st->goods[v->cargo_type];
01624
01625 if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
01626 uint cargo_count = v->cargo.Count();
01627 uint amount_unloaded = _patches.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
01628 bool remaining;
01629
01630 if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.flags & OFB_TRANSFER)) {
01631
01632 remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, last_visited);
01633
01634 result |= 1;
01635 } else if (u->current_order.flags & (OFB_UNLOAD | OFB_TRANSFER)) {
01636 remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded);
01637 SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
01638
01639 result |= 2;
01640 } else {
01641
01642
01643 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01644 continue;
01645 }
01646
01647
01648 st->time_since_unload = 0;
01649
01650 unloading_time += amount_unloaded;
01651
01652 anything_unloaded = true;
01653 if (_patches.gradual_loading && remaining) {
01654 completely_emptied = false;
01655 } else {
01656
01657 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01658 }
01659
01660 continue;
01661 }
01662
01663
01664 if (u->current_order.flags & OFB_UNLOAD) continue;
01665
01666
01667 int t;
01668 switch (u->type) {
01669 case VEH_TRAIN: t = u->u.rail.cached_max_speed; break;
01670 case VEH_ROAD: t = u->max_speed / 2; break;
01671 default: t = u->max_speed; break;
01672 }
01673
01674
01675 ge->last_speed = min(t, 255);
01676 ge->last_age = _cur_year - u->build_year;
01677 ge->days_since_pickup = 0;
01678
01679
01680
01681 if (!ge->cargo.Empty() &&
01682 (cap = v->cargo_cap - v->cargo.Count()) != 0) {
01683 uint count = ge->cargo.Count();
01684
01685
01686
01687 if (_patches.improved_load && cargo_left[v->cargo_type] <= 0) {
01688 SetBit(cargo_not_full, v->cargo_type);
01689 continue;
01690 }
01691
01692 if (cap > count) cap = count;
01693 if (_patches.gradual_loading) cap = min(cap, load_amount);
01694 if (_patches.improved_load) {
01695
01696 cap = min((uint)cargo_left[v->cargo_type], cap);
01697 cargo_left[v->cargo_type] -= cap;
01698 }
01699
01700 if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
01701
01702
01703
01704
01705
01706
01707
01708
01709 completely_emptied = false;
01710 anything_loaded = true;
01711
01712 ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy);
01713
01714 st->time_since_load = 0;
01715 st->last_vehicle_type = v->type;
01716
01717 unloading_time += cap;
01718
01719 result |= 2;
01720 }
01721
01722 if (v->cargo.Count() == v->cargo_cap) {
01723 SetBit(cargo_full, v->cargo_type);
01724 } else {
01725 SetBit(cargo_not_full, v->cargo_type);
01726 }
01727 }
01728
01729
01730 completely_emptied &= anything_unloaded;
01731
01732
01733
01734
01735
01736 if (_patches.improved_load && HasBit(u->current_order.flags, OF_FULL_LOAD)) {
01737
01738 for (v = u; v != NULL; v = v->Next()) {
01739 if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
01740 }
01741 }
01742
01743 v = u;
01744
01745 if (anything_loaded || anything_unloaded) {
01746 if (_patches.gradual_loading) {
01747
01748
01749 const uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
01750
01751 unloading_time = gradual_loading_wait_time[v->type];
01752 }
01753 } else {
01754 bool finished_loading = true;
01755 if (HasBit(v->current_order.flags, OF_FULL_LOAD)) {
01756 if (_patches.full_load_any) {
01757
01758
01759 if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap != v->cargo.Count()) ||
01760 (cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) {
01761 finished_loading = false;
01762 }
01763 } else if (cargo_not_full != 0) {
01764 finished_loading = false;
01765 }
01766 }
01767 unloading_time = 20;
01768
01769 SB(v->vehicle_flags, VF_LOADING_FINISHED, 1, finished_loading);
01770 }
01771
01772 if (v->type == VEH_TRAIN) {
01773
01774 int overhang = v->u.rail.cached_total_length - st->GetPlatformLength(v->tile) * TILE_SIZE;
01775 if (overhang > 0) {
01776 unloading_time <<= 1;
01777 unloading_time += (overhang * unloading_time) / 8;
01778 }
01779 }
01780
01781
01782
01783
01784
01785
01786
01787 if (_game_mode != GM_MENU && (_patches.loading_indicators > (uint)(v->owner != _local_player && _local_player != PLAYER_SPECTATOR))) {
01788 StringID percent_up_down = STR_NULL;
01789 int percent = CalcPercentVehicleFilled(v, &percent_up_down);
01790 if (v->fill_percent_te_id == INVALID_TE_ID) {
01791 v->fill_percent_te_id = ShowFillingPercent(v->x_pos, v->y_pos, v->z_pos + 20, percent, percent_up_down);
01792 } else {
01793 UpdateFillingPercent(v->fill_percent_te_id, percent, percent_up_down);
01794 }
01795 }
01796
01797 v->load_unload_time_rem = unloading_time;
01798
01799 if (completely_emptied) {
01800 TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
01801 }
01802
01803 if (result != 0) {
01804 InvalidateWindow(v->GetVehicleListWindowClass(), v->owner);
01805 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01806
01807 st->MarkTilesDirty(true);
01808 v->MarkDirty();
01809
01810 if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited);
01811 }
01812 }
01813
01819 void LoadUnloadStation(Station *st)
01820 {
01821 int cargo_left[NUM_CARGO];
01822
01823 for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
01824
01825 std::list<Vehicle *>::iterator iter;
01826 for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
01827 Vehicle *v = *iter;
01828 if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
01829 }
01830 }
01831
01832 void PlayersMonthlyLoop()
01833 {
01834 PlayersGenStatistics();
01835 if (_patches.inflation && _cur_year < MAX_YEAR)
01836 AddInflation();
01837 PlayersPayInterest();
01838
01839 _current_player = OWNER_NONE;
01840 HandleEconomyFluctuations();
01841 SubsidyMonthlyHandler();
01842 }
01843
01844 static void DoAcquireCompany(Player *p)
01845 {
01846 Player *owner;
01847 int i;
01848 Money value;
01849
01850 SetDParam(0, p->index);
01851 SetDParam(1, p->bankrupt_value);
01852 AddNewsItem( (StringID)(_current_player | NB_BMERGER), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
01853
01854
01855 PlayerID pi = p->index;
01856 ChangeNetworkOwner(pi, _current_player);
01857 ChangeOwnershipOfPlayerItems(pi, _current_player);
01858
01859 if (p->bankrupt_value == 0) {
01860 owner = GetPlayer(_current_player);
01861 owner->current_loan += p->current_loan;
01862 }
01863
01864 value = CalculateCompanyValue(p) >> 2;
01865 PlayerID old_player = _current_player;
01866 for (i = 0; i != 4; i++) {
01867 if (p->share_owners[i] != PLAYER_SPECTATOR) {
01868 _current_player = p->share_owners[i];
01869 SubtractMoneyFromPlayer(CommandCost(EXPENSES_OTHER, -value));
01870 }
01871 }
01872 _current_player = old_player;
01873
01874 p->is_active = false;
01875
01876 DeletePlayerWindows(pi);
01877 RebuildVehicleLists();
01878 }
01879
01880 extern int GetAmountOwnedBy(const Player *p, PlayerID owner);
01881
01888 CommandCost CmdBuyShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01889 {
01890 Player *p;
01891 CommandCost cost(EXPENSES_OTHER);
01892
01893
01894
01895 if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares || _current_player == (PlayerID)p1) return CMD_ERROR;
01896
01897 p = GetPlayer((PlayerID)p1);
01898
01899
01900 if (!p->is_active) return CMD_ERROR;
01901
01902
01903 if (_cur_year - p->inaugurated_year < 6) return_cmd_error(STR_PROTECTED);
01904
01905
01906 if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 0) return cost;
01907
01908
01909 if (GetAmountOwnedBy(p, PLAYER_SPECTATOR) == 1 && !p->is_ai) return cost;
01910
01911 cost.AddCost(CalculateCompanyValue(p) >> 2);
01912 if (flags & DC_EXEC) {
01913 PlayerByte* b = p->share_owners;
01914 int i;
01915
01916 while (*b != PLAYER_SPECTATOR) b++;
01917 *b = _current_player;
01918
01919 for (i = 0; p->share_owners[i] == _current_player;) {
01920 if (++i == 4) {
01921 p->bankrupt_value = 0;
01922 DoAcquireCompany(p);
01923 break;
01924 }
01925 }
01926 InvalidateWindow(WC_COMPANY, p1);
01927 }
01928 return cost;
01929 }
01930
01937 CommandCost CmdSellShareInCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01938 {
01939 Player *p;
01940 Money cost;
01941
01942
01943
01944 if (!IsValidPlayer((PlayerID)p1) || !_patches.allow_shares || _current_player == (PlayerID)p1) return CMD_ERROR;
01945
01946 p = GetPlayer((PlayerID)p1);
01947
01948
01949 if (!p->is_active) return CMD_ERROR;
01950
01951
01952 if (GetAmountOwnedBy(p, _current_player) == 0) return CommandCost();
01953
01954
01955 cost = CalculateCompanyValue(p) >> 2;
01956 cost = -(cost - (cost >> 7));
01957
01958 if (flags & DC_EXEC) {
01959 PlayerByte* b = p->share_owners;
01960 while (*b != _current_player) b++;
01961 *b = PLAYER_SPECTATOR;
01962 InvalidateWindow(WC_COMPANY, p1);
01963 }
01964 return CommandCost(EXPENSES_OTHER, cost);
01965 }
01966
01976 CommandCost CmdBuyCompany(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
01977 {
01978 Player *p;
01979 PlayerID pid = (PlayerID)p1;
01980
01981
01982 if (!IsValidPlayer(pid) || _networking) return CMD_ERROR;
01983
01984
01985 if (pid == _current_player) return CMD_ERROR;
01986
01987 p = GetPlayer(pid);
01988
01989 if (!p->is_ai) return CMD_ERROR;
01990
01991 if (flags & DC_EXEC) {
01992 DoAcquireCompany(p);
01993 }
01994 return CommandCost(EXPENSES_OTHER, p->bankrupt_value);
01995 }
01996
01998 static void SaveLoad_PRIC()
01999 {
02000 int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
02001 SlArray(&_price, NUM_PRICES, vt);
02002 SlArray(&_price_frac, NUM_PRICES, SLE_UINT16);
02003 }
02004
02006 static void SaveLoad_CAPR()
02007 {
02008 uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO;
02009 int vt = CheckSavegameVersion(65) ? (SLE_FILE_I32 | SLE_VAR_I64) : SLE_INT64;
02010 SlArray(&_cargo_payment_rates, num_cargo, vt);
02011 SlArray(&_cargo_payment_rates_frac, num_cargo, SLE_UINT16);
02012 }
02013
02014 static const SaveLoad _economy_desc[] = {
02015 SLE_CONDVAR(Economy, max_loan, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
02016 SLE_CONDVAR(Economy, max_loan, SLE_INT64, 65, SL_MAX_VERSION),
02017 SLE_CONDVAR(Economy, max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
02018 SLE_CONDVAR(Economy, max_loan_unround, SLE_INT64, 65, SL_MAX_VERSION),
02019 SLE_CONDVAR(Economy, max_loan_unround_fract, SLE_UINT16, 70, SL_MAX_VERSION),
02020 SLE_VAR(Economy, fluct, SLE_INT16),
02021 SLE_VAR(Economy, interest_rate, SLE_UINT8),
02022 SLE_VAR(Economy, infl_amount, SLE_UINT8),
02023 SLE_VAR(Economy, infl_amount_pr, SLE_UINT8),
02024 SLE_END()
02025 };
02026
02028 static void SaveLoad_ECMY()
02029 {
02030 SlObject(&_economy, _economy_desc);
02031 }
02032
02033 extern const ChunkHandler _economy_chunk_handlers[] = {
02034 { 'PRIC', SaveLoad_PRIC, SaveLoad_PRIC, CH_RIFF | CH_AUTO_LENGTH},
02035 { 'CAPR', SaveLoad_CAPR, SaveLoad_CAPR, CH_RIFF | CH_AUTO_LENGTH},
02036 { 'SUBS', Save_SUBS, Load_SUBS, CH_ARRAY},
02037 { 'ECMY', SaveLoad_ECMY, SaveLoad_ECMY, CH_RIFF | CH_LAST},
02038 };