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