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