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 Station *st;
00403 FOR_ALL_STATIONS(st) {
00404 if (st->owner == old_owner) {
00405
00406
00407 st->owner = new_owner == INVALID_OWNER ? OWNER_NONE : new_owner;
00408 }
00409 }
00410
00411
00412 Waypoint *wp;
00413 FOR_ALL_WAYPOINTS(wp) {
00414 if (wp->owner == old_owner) {
00415 wp->owner = new_owner == INVALID_OWNER ? OWNER_NONE : new_owner;
00416 }
00417 }
00418
00419
00420
00421 RemoveAllEngineReplacementForCompany(GetCompany(old_owner));
00422
00423 if (new_owner == INVALID_OWNER) {
00424 RemoveAllGroupsForCompany(old_owner);
00425 } else {
00426 Group *g;
00427 FOR_ALL_GROUPS(g) {
00428 if (g->owner == old_owner) g->owner = new_owner;
00429 }
00430 }
00431
00432 Sign *si;
00433 FOR_ALL_SIGNS(si) {
00434 if (si->owner == old_owner) si->owner = new_owner == INVALID_OWNER ? OWNER_NONE : new_owner;
00435 }
00436
00437
00438 if (new_owner != INVALID_OWNER) ChangeWindowOwner(old_owner, new_owner);
00439
00440 _current_company = old;
00441
00442 MarkWholeScreenDirty();
00443 }
00444
00445 static void ChangeNetworkOwner(Owner current_owner, Owner new_owner)
00446 {
00447 #ifdef ENABLE_NETWORK
00448 if (!_networking) return;
00449
00450 if (current_owner == _local_company) {
00451 _network_playas = new_owner;
00452 SetLocalCompany(new_owner);
00453 }
00454
00455 if (!_network_server) return;
00456
00457 NetworkServerChangeOwner(current_owner, new_owner);
00458 #endif
00459 }
00460
00461 static void CompanyCheckBankrupt(Company *c)
00462 {
00463
00464 if (c->money >= 0) {
00465 c->quarters_of_bankrupcy = 0;
00466 return;
00467 }
00468
00469 c->quarters_of_bankrupcy++;
00470
00471 CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1);
00472 cni->FillData(c);
00473
00474 switch (c->quarters_of_bankrupcy) {
00475 case 0:
00476 case 1:
00477 free(cni);
00478 break;
00479
00480 case 2:
00481 SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE);
00482 SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED);
00483 SetDParamStr(2, cni->company_name);
00484 AddNewsItem(STR_02B6, NS_COMPANY_TROUBLE, 0, 0, cni);
00485 AI::BroadcastNewEvent(new AIEventCompanyInTrouble(c->index));
00486 break;
00487 case 3: {
00488
00489
00490 if (IsHumanCompany(c->index)) {
00491 SetDParam(0, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE);
00492 SetDParam(1, STR_7057_WILL_BE_SOLD_OFF_OR_DECLARED);
00493 SetDParamStr(2, cni->company_name);
00494 AddNewsItem(STR_02B6, NS_COMPANY_TROUBLE, 0, 0, cni);
00495 break;
00496 }
00497
00498
00499
00500 Money val = CalculateCompanyValue(c);
00501 if (val > 0) {
00502 c->bankrupt_value = val;
00503 c->bankrupt_asked = 1 << c->index;
00504 c->bankrupt_timeout = 0;
00505 free(cni);
00506 break;
00507 }
00508
00509 }
00510 default:
00511 case 4:
00512 if (!_networking && _local_company == c->index) {
00513
00514
00515
00516
00517 c->bankrupt_asked = MAX_UVALUE(CompanyMask);
00518 c->bankrupt_timeout = 0x456;
00519 break;
00520 }
00521
00522
00523 DeleteCompanyWindows(c->index);
00524
00525
00526 SetDParam(0, STR_705C_BANKRUPT);
00527 SetDParam(1, STR_705D_HAS_BEEN_CLOSED_DOWN_BY);
00528 SetDParamStr(2, cni->company_name);
00529 AddNewsItem(STR_02B6, NS_COMPANY_BANKRUPT, 0, 0, cni);
00530
00531
00532 ChangeNetworkOwner(c->index, COMPANY_SPECTATOR);
00533 ChangeOwnershipOfCompanyItems(c->index, INVALID_OWNER);
00534
00535 if (!IsHumanCompany(c->index)) AI::Stop(c->index);
00536
00537 CompanyID c_index = c->index;
00538 delete c;
00539 AI::BroadcastNewEvent(new AIEventCompanyBankrupt(c_index));
00540 }
00541 }
00542
00543 static void CompaniesGenStatistics()
00544 {
00545 Station *st;
00546 Company *c;
00547
00548 FOR_ALL_STATIONS(st) {
00549 _current_company = st->owner;
00550 CommandCost cost(EXPENSES_PROPERTY, _price.station_value >> 1);
00551 SubtractMoneyFromCompany(cost);
00552 }
00553
00554 if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, _cur_month))
00555 return;
00556
00557 FOR_ALL_COMPANIES(c) {
00558 memmove(&c->old_economy[1], &c->old_economy[0], sizeof(c->old_economy) - sizeof(c->old_economy[0]));
00559 c->old_economy[0] = c->cur_economy;
00560 memset(&c->cur_economy, 0, sizeof(c->cur_economy));
00561
00562 if (c->num_valid_stat_ent != 24) c->num_valid_stat_ent++;
00563
00564 UpdateCompanyRatingAndValue(c, true);
00565 CompanyCheckBankrupt(c);
00566
00567 if (c->block_preview != 0) c->block_preview--;
00568 }
00569
00570 InvalidateWindow(WC_INCOME_GRAPH, 0);
00571 InvalidateWindow(WC_OPERATING_PROFIT, 0);
00572 InvalidateWindow(WC_DELIVERED_CARGO, 0);
00573 InvalidateWindow(WC_PERFORMANCE_HISTORY, 0);
00574 InvalidateWindow(WC_COMPANY_VALUE, 0);
00575 InvalidateWindow(WC_COMPANY_LEAGUE, 0);
00576 }
00577
00578 static void AddSingleInflation(Money *value, uint16 *frac, int32 amt)
00579 {
00580
00581 if ((INT64_MAX / amt) < (*value + 1)) {
00582 *value = INT64_MAX / amt;
00583 *frac = 0;
00584 } else {
00585 int64 tmp = (int64)*value * amt + *frac;
00586 *frac = GB(tmp, 0, 16);
00587 *value += tmp >> 16;
00588 }
00589 }
00590
00591 static void AddInflation(bool check_year = true)
00592 {
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608 if (check_year && (_cur_year - _settings_game.game_creation.starting_year) >= (ORIGINAL_MAX_YEAR - ORIGINAL_BASE_YEAR)) return;
00609
00610
00611
00612
00613
00614
00615 int32 inf = _economy.infl_amount * 54;
00616
00617 for (uint i = 0; i != NUM_PRICES; i++) {
00618 AddSingleInflation((Money*)&_price + i, _price_frac + i, inf);
00619 }
00620
00621 AddSingleInflation(&_economy.max_loan_unround, &_economy.max_loan_unround_fract, inf);
00622
00623 if (_economy.max_loan + 50000 <= _economy.max_loan_unround) _economy.max_loan += 50000;
00624
00625 inf = _economy.infl_amount_pr * 54;
00626 for (CargoID i = 0; i < NUM_CARGO; i++) {
00627 AddSingleInflation(
00628 (Money*)_cargo_payment_rates + i,
00629 _cargo_payment_rates_frac + i,
00630 inf
00631 );
00632 }
00633
00634 InvalidateWindowClasses(WC_BUILD_VEHICLE);
00635 InvalidateWindowClasses(WC_REPLACE_VEHICLE);
00636 InvalidateWindowClasses(WC_VEHICLE_DETAILS);
00637 InvalidateWindow(WC_PAYMENT_RATES, 0);
00638 }
00639
00640 static void CompaniesPayInterest()
00641 {
00642 const Company *c;
00643
00644 FOR_ALL_COMPANIES(c) {
00645 _current_company = c->index;
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655 Money yearly_fee = c->current_loan * _economy.interest_rate / 100;
00656 Money up_to_previous_month = yearly_fee * _cur_month / 12;
00657 Money up_to_this_month = yearly_fee * (_cur_month + 1) / 12;
00658
00659 SubtractMoneyFromCompany(CommandCost(EXPENSES_LOAN_INT, up_to_this_month - up_to_previous_month));
00660
00661 SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, _price.station_value >> 2));
00662 }
00663 }
00664
00665 static void HandleEconomyFluctuations()
00666 {
00667 if (_settings_game.difficulty.economy == 0) return;
00668
00669 if (--_economy.fluct == 0) {
00670 _economy.fluct = -(int)GB(Random(), 0, 2);
00671 AddNewsItem(STR_7073_WORLD_RECESSION_FINANCIAL, NS_ECONOMY, 0, 0);
00672 } else if (_economy.fluct == -12) {
00673 _economy.fluct = GB(Random(), 0, 8) + 312;
00674 AddNewsItem(STR_7074_RECESSION_OVER_UPTURN_IN, NS_ECONOMY, 0, 0);
00675 }
00676 }
00677
00678 static byte _price_category[NUM_PRICES] = {
00679 0, 2, 2, 2, 2, 2, 2, 2,
00680 2, 2, 2, 2, 2, 2, 2, 2,
00681 2, 2, 2, 2, 2, 2, 2, 2,
00682 2, 2, 2, 2, 2, 2, 2, 2,
00683 2, 2, 2, 2, 2, 2, 2, 2,
00684 2, 2, 1, 1, 1, 1, 1, 1,
00685 2,
00686 };
00687
00688 static const Money _price_base[NUM_PRICES] = {
00689 100,
00690 100,
00691 95,
00692 65,
00693 275,
00694 600,
00695 500,
00696 700,
00697 450,
00698 200,
00699 180,
00700 600,
00701 200,
00702 200,
00703 350,
00704 400000,
00705 2000,
00706 700000,
00707 14000,
00708 65000,
00709 20,
00710 250,
00711 20,
00712 40,
00713 200,
00714 500,
00715 20,
00716 -70,
00717 10,
00718 50,
00719 80,
00720 80,
00721 90,
00722 30,
00723 10000,
00724 50,
00725 30,
00726 50,
00727 50,
00728 55,
00729 1600,
00730 40,
00731 5600,
00732 5200,
00733 4800,
00734 9600,
00735 1600,
00736 5600,
00737 1000000,
00738 };
00739
00740 static byte price_base_multiplier[NUM_PRICES];
00741
00745 void ResetPriceBaseMultipliers()
00746 {
00747 uint i;
00748
00749
00750 for (i = 0; i < NUM_PRICES; i++)
00751 price_base_multiplier[i] = 8;
00752 }
00753
00761 void SetPriceBaseMultiplier(uint price, byte factor)
00762 {
00763 assert(price < NUM_PRICES);
00764 price_base_multiplier[price] = factor;
00765 }
00766
00771 void StartupIndustryDailyChanges(bool init_counter)
00772 {
00773 uint map_size = MapLogX() + MapLogY();
00774
00775
00776
00777
00778
00779
00780 _economy.industry_daily_increment = (1 << map_size) / 31;
00781
00782 if (init_counter) {
00783
00784 _economy.industry_daily_change_counter = 0;
00785 }
00786 }
00787
00788 void StartupEconomy()
00789 {
00790 int i;
00791
00792 assert(sizeof(_price) == NUM_PRICES * sizeof(Money));
00793
00794 for (i = 0; i != NUM_PRICES; i++) {
00795 Money price = _price_base[i];
00796 if (_price_category[i] != 0) {
00797 uint mod = _price_category[i] == 1 ? _settings_game.difficulty.vehicle_costs : _settings_game.difficulty.construction_cost;
00798 if (mod < 1) {
00799 price = price * 3 >> 2;
00800 } else if (mod > 1) {
00801 price = price * 9 >> 3;
00802 }
00803 }
00804 if (price_base_multiplier[i] > 8) {
00805 price <<= price_base_multiplier[i] - 8;
00806 } else {
00807 price >>= 8 - price_base_multiplier[i];
00808 }
00809 ((Money*)&_price)[i] = price;
00810 _price_frac[i] = 0;
00811 }
00812
00813 _economy.interest_rate = _settings_game.difficulty.initial_interest;
00814 _economy.infl_amount = _settings_game.difficulty.initial_interest;
00815 _economy.infl_amount_pr = max(0, _settings_game.difficulty.initial_interest - 1);
00816 _economy.max_loan_unround = _economy.max_loan = _settings_game.difficulty.max_loan;
00817 _economy.fluct = GB(Random(), 0, 8) + 168;
00818
00819 StartupIndustryDailyChanges(true);
00820
00821 }
00822
00823 void ResetEconomy()
00824 {
00825
00826 bool needed = false;
00827
00828 for (CargoID c = 0; c < NUM_CARGO; c++) {
00829 const CargoSpec *cs = GetCargo(c);
00830 if (!cs->IsValid()) continue;
00831 if (_cargo_payment_rates[c] == 0) {
00832 needed = true;
00833 break;
00834 }
00835 }
00836
00837 if (!needed) return;
00838
00839
00840
00841 Money old_value = _economy.max_loan_unround;
00842
00843
00844 StartupEconomy();
00845 InitializeLandscapeVariables(false);
00846
00847
00848 while (old_value > _economy.max_loan_unround) {
00849 AddInflation(false);
00850 }
00851 }
00852
00853 Money GetPriceByIndex(uint8 index)
00854 {
00855 if (index > NUM_PRICES) return 0;
00856
00857 return ((Money*)&_price)[index];
00858 }
00859
00860
00861 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00862 {
00863 TileIndex tile;
00864 TileIndex tile2;
00865 Pair tp;
00866
00867
00868 const CargoSpec *cs = GetCargo(s->cargo_type);
00869 SetDParam(0, mode ? cs->name : cs->name_single);
00870
00871 if (s->age < 12) {
00872 if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL) {
00873 SetDParam(1, STR_INDUSTRY);
00874 SetDParam(2, s->from);
00875 tile = GetIndustry(s->from)->xy;
00876
00877 if (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD) {
00878 SetDParam(4, STR_INDUSTRY);
00879 SetDParam(5, s->to);
00880 tile2 = GetIndustry(s->to)->xy;
00881 } else {
00882 SetDParam(4, STR_TOWN);
00883 SetDParam(5, s->to);
00884 tile2 = GetTown(s->to)->xy;
00885 }
00886 } else {
00887 SetDParam(1, STR_TOWN);
00888 SetDParam(2, s->from);
00889 tile = GetTown(s->from)->xy;
00890
00891 SetDParam(4, STR_TOWN);
00892 SetDParam(5, s->to);
00893 tile2 = GetTown(s->to)->xy;
00894 }
00895 } else {
00896 SetDParam(1, s->from);
00897 tile = GetStation(s->from)->xy;
00898
00899 SetDParam(2, s->to);
00900 tile2 = GetStation(s->to)->xy;
00901 }
00902
00903 tp.a = tile;
00904 tp.b = tile2;
00905
00906 return tp;
00907 }
00908
00909 void DeleteSubsidyWithTown(TownID index)
00910 {
00911 Subsidy *s;
00912
00913 for (s = _subsidies; s != endof(_subsidies); s++) {
00914 if (s->cargo_type != CT_INVALID && s->age < 12) {
00915 const CargoSpec *cs = GetCargo(s->cargo_type);
00916 if (((cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) && (index == s->from || index == s->to)) ||
00917 ((cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) && index == s->to)) {
00918 s->cargo_type = CT_INVALID;
00919 }
00920 }
00921 }
00922 }
00923
00924 void DeleteSubsidyWithIndustry(IndustryID index)
00925 {
00926 Subsidy *s;
00927
00928 for (s = _subsidies; s != endof(_subsidies); s++) {
00929 if (s->cargo_type != CT_INVALID && s->age < 12) {
00930 const CargoSpec *cs = GetCargo(s->cargo_type);
00931 if (cs->town_effect != TE_PASSENGERS && cs->town_effect != TE_MAIL &&
00932 (index == s->from || (cs->town_effect != TE_GOODS && cs->town_effect != TE_FOOD && index == s->to))) {
00933 s->cargo_type = CT_INVALID;
00934 }
00935 }
00936 }
00937 }
00938
00939 void DeleteSubsidyWithStation(StationID index)
00940 {
00941 Subsidy *s;
00942 bool dirty = false;
00943
00944 for (s = _subsidies; s != endof(_subsidies); s++) {
00945 if (s->cargo_type != CT_INVALID && s->age >= 12 &&
00946 (s->from == index || s->to == index)) {
00947 s->cargo_type = CT_INVALID;
00948 dirty = true;
00949 }
00950 }
00951
00952 if (dirty)
00953 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
00954 }
00955
00956 struct FoundRoute {
00957 uint distance;
00958 CargoID cargo;
00959 void *from;
00960 void *to;
00961 };
00962
00963 static void FindSubsidyPassengerRoute(FoundRoute *fr)
00964 {
00965 Town *from, *to;
00966
00967 fr->distance = UINT_MAX;
00968
00969 fr->from = from = GetRandomTown();
00970 if (from == NULL || from->population < 400) return;
00971
00972 fr->to = to = GetRandomTown();
00973 if (from == to || to == NULL || to->population < 400 || to->pct_pass_transported > 42)
00974 return;
00975
00976 fr->distance = DistanceManhattan(from->xy, to->xy);
00977 }
00978
00979 static void FindSubsidyCargoRoute(FoundRoute *fr)
00980 {
00981 Industry *i;
00982 int trans, total;
00983 CargoID cargo;
00984
00985 fr->distance = UINT_MAX;
00986
00987 fr->from = i = GetRandomIndustry();
00988 if (i == NULL) return;
00989
00990
00991 if (HasBit(Random(), 0) && i->produced_cargo[1] != CT_INVALID) {
00992 cargo = i->produced_cargo[1];
00993 trans = i->last_month_pct_transported[1];
00994 total = i->last_month_production[1];
00995 } else {
00996 cargo = i->produced_cargo[0];
00997 trans = i->last_month_pct_transported[0];
00998 total = i->last_month_production[0];
00999 }
01000
01001
01002
01003
01004 if (total == 0 || trans > 42 || cargo == CT_INVALID) return;
01005
01006 const CargoSpec *cs = GetCargo(cargo);
01007 if (cs->town_effect == TE_PASSENGERS) return;
01008
01009 fr->cargo = cargo;
01010
01011 if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
01012
01013 Town *t = GetRandomTown();
01014
01015
01016 if (t == NULL || t->population < 900) return;
01017
01018 fr->distance = DistanceManhattan(i->xy, t->xy);
01019 fr->to = t;
01020 } else {
01021
01022 Industry *i2 = GetRandomIndustry();
01023
01024
01025 if (i2 == NULL || i == i2 ||
01026 (cargo != i2->accepts_cargo[0] &&
01027 cargo != i2->accepts_cargo[1] &&
01028 cargo != i2->accepts_cargo[2])) {
01029 return;
01030 }
01031 fr->distance = DistanceManhattan(i->xy, i2->xy);
01032 fr->to = i2;
01033 }
01034 }
01035
01036 static bool CheckSubsidyDuplicate(Subsidy *s)
01037 {
01038 const Subsidy *ss;
01039
01040 for (ss = _subsidies; ss != endof(_subsidies); ss++) {
01041 if (s != ss &&
01042 ss->from == s->from &&
01043 ss->to == s->to &&
01044 ss->cargo_type == s->cargo_type) {
01045 s->cargo_type = CT_INVALID;
01046 return true;
01047 }
01048 }
01049 return false;
01050 }
01051
01052
01053 static void SubsidyMonthlyHandler()
01054 {
01055 Subsidy *s;
01056 Pair pair;
01057 Station *st;
01058 uint n;
01059 FoundRoute fr;
01060 bool modified = false;
01061
01062 for (s = _subsidies; s != endof(_subsidies); s++) {
01063 if (s->cargo_type == CT_INVALID) continue;
01064
01065 if (s->age == 12 - 1) {
01066 pair = SetupSubsidyDecodeParam(s, 1);
01067 AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, pair.a, pair.b);
01068 s->cargo_type = CT_INVALID;
01069 modified = true;
01070 AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s - _subsidies));
01071 } else if (s->age == 2 * 12 - 1) {
01072 st = GetStation(s->to);
01073 if (st->owner == _local_company) {
01074 pair = SetupSubsidyDecodeParam(s, 1);
01075 AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, pair.a, pair.b);
01076 }
01077 s->cargo_type = CT_INVALID;
01078 modified = true;
01079 AI::BroadcastNewEvent(new AIEventSubsidyExpired(s - _subsidies));
01080 } else {
01081 s->age++;
01082 }
01083 }
01084
01085
01086 if (Chance16(1, 4)) {
01087
01088 s = _subsidies;
01089 while (s->cargo_type != CT_INVALID) {
01090 if (++s == endof(_subsidies))
01091 goto no_add;
01092 }
01093
01094 n = 1000;
01095 do {
01096 FindSubsidyPassengerRoute(&fr);
01097 if (fr.distance <= 70) {
01098 s->cargo_type = CT_PASSENGERS;
01099 s->from = ((Town*)fr.from)->index;
01100 s->to = ((Town*)fr.to)->index;
01101 goto add_subsidy;
01102 }
01103 FindSubsidyCargoRoute(&fr);
01104 if (fr.distance <= 70) {
01105 s->cargo_type = fr.cargo;
01106 s->from = ((Industry*)fr.from)->index;
01107 {
01108 const CargoSpec *cs = GetCargo(fr.cargo);
01109 s->to = (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) ? ((Town*)fr.to)->index : ((Industry*)fr.to)->index;
01110 }
01111 add_subsidy:
01112 if (!CheckSubsidyDuplicate(s)) {
01113 s->age = 0;
01114 pair = SetupSubsidyDecodeParam(s, 0);
01115 AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, pair.a, pair.b);
01116 AI::BroadcastNewEvent(new AIEventSubsidyOffer(s - _subsidies));
01117 modified = true;
01118 break;
01119 }
01120 }
01121 } while (n--);
01122 }
01123 no_add:;
01124 if (modified)
01125 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
01126 }
01127
01128 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type)
01129 {
01130 const CargoSpec *cs = GetCargo(cargo_type);
01131
01132
01133 if (HasBit(cs->callback_mask, CBM_CARGO_PROFIT_CALC)) {
01134 uint32 var18 = min(dist, 0xFFFF) | (min(num_pieces, 0xFF) << 16) | (transit_days << 24);
01135 uint16 callback = GetCargoCallback(CBID_CARGO_PROFIT_CALC, 0, var18, cs);
01136 if (callback != CALLBACK_FAILED) {
01137 int result = GB(callback, 0, 14);
01138
01139
01140 if (HasBit(callback, 14)) result = 0x4000 - result;
01141
01142
01143
01144
01145 return result * num_pieces * _cargo_payment_rates[cargo_type] / 8192;
01146 }
01147 }
01148
01149
01150 if (_settings_game.game_creation.landscape == LT_TEMPERATE && cs->label == 'VALU' && dist < 10) return 0;
01151
01152
01153 static const int MIN_TIME_FACTOR = 31;
01154 static const int MAX_TIME_FACTOR = 255;
01155
01156 const int days1 = cs->transit_days[0];
01157 const int days2 = cs->transit_days[1];
01158 const int days_over_days1 = max( transit_days - days1, 0);
01159 const int days_over_days2 = max(days_over_days1 - days2, 0);
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171 const int time_factor = max(MAX_TIME_FACTOR - days_over_days1 - days_over_days2, MIN_TIME_FACTOR);
01172
01173 return BigMulS(dist * time_factor * num_pieces, _cargo_payment_rates[cargo_type], 21);
01174 }
01175
01176
01177 struct FindIndustryToDeliverData {
01178 const Rect *rect;
01179 CargoID cargo_type;
01180
01181 Industry *ind;
01182 const IndustrySpec *indspec;
01183 uint cargo_index;
01184 };
01185
01186 static bool FindIndustryToDeliver(TileIndex ind_tile, void *user_data)
01187 {
01188 FindIndustryToDeliverData *callback_data = (FindIndustryToDeliverData *)user_data;
01189 const Rect *rect = callback_data->rect;
01190 CargoID cargo_type = callback_data->cargo_type;
01191
01192
01193 if (!IsTileType(ind_tile, MP_INDUSTRY)) return false;
01194
01195
01196 int x = TileX(ind_tile);
01197 int y = TileY(ind_tile);
01198 if (x < rect->left || x > rect->right || y < rect->top || y > rect->bottom) return false;
01199
01200 Industry *ind = GetIndustryByTile(ind_tile);
01201 const IndustrySpec *indspec = GetIndustrySpec(ind->type);
01202
01203 uint cargo_index;
01204 for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
01205 if (cargo_type == ind->accepts_cargo[cargo_index]) break;
01206 }
01207
01208 if (cargo_index >= lengthof(ind->accepts_cargo)) return false;
01209
01210
01211 if (HasBit(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) {
01212 uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO, 0, GetReverseCargoTranslation(cargo_type, indspec->grf_prop.grffile), ind, ind->type, ind->xy);
01213 if (res == 0) return false;
01214 }
01215
01216
01217 callback_data->ind = ind;
01218 callback_data->indspec = indspec;
01219 callback_data->cargo_index = cargo_index;
01220 return true;
01221 }
01222
01231 static void DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, int num_pieces, SmallIndustryList *industry_set)
01232 {
01233 if (st->rect.IsEmpty()) return;
01234
01235
01236 int catchment_radius = st->GetCatchmentRadius();
01237 Rect rect = {
01238 max<int>(st->rect.left - catchment_radius, 0),
01239 max<int>(st->rect.top - catchment_radius, 0),
01240 min<int>(st->rect.right + catchment_radius, MapMaxX()),
01241 min<int>(st->rect.bottom + catchment_radius, MapMaxY())
01242 };
01243
01244
01245 TileIndex start_tile = st->xy;
01246 uint max_radius = max(
01247 max(DistanceManhattan(start_tile, TileXY(rect.left , rect.top)), DistanceManhattan(start_tile, TileXY(rect.left , rect.bottom))),
01248 max(DistanceManhattan(start_tile, TileXY(rect.right, rect.top)), DistanceManhattan(start_tile, TileXY(rect.right, rect.bottom)))
01249 );
01250
01251 FindIndustryToDeliverData callback_data;
01252 callback_data.rect = ▭
01253 callback_data.cargo_type = cargo_type;
01254 callback_data.ind = NULL;
01255 callback_data.indspec = NULL;
01256 callback_data.cargo_index = 0;
01257
01258
01259
01260
01261
01262
01263
01264 if (CircularTileSearch(&start_tile, 2 * max_radius + 1, FindIndustryToDeliver, &callback_data)) {
01265 Industry *best = callback_data.ind;
01266 uint accepted_cargo_index = callback_data.cargo_index;
01267 assert(best != NULL);
01268
01269
01270 if (industry_set != NULL) industry_set->Include(best);
01271
01272 best->incoming_cargo_waiting[accepted_cargo_index] = min(num_pieces + best->incoming_cargo_waiting[accepted_cargo_index], 0xFFFF);
01273 }
01274 }
01275
01276 static bool CheckSubsidised(Station *from, Station *to, CargoID cargo_type)
01277 {
01278 Subsidy *s;
01279 TileIndex xy;
01280 Pair pair;
01281
01282
01283 for (s = _subsidies; s != endof(_subsidies); s++) {
01284 if (s->cargo_type == cargo_type &&
01285 s->age >= 12 &&
01286 s->from == from->index &&
01287 s->to == to->index) {
01288 return true;
01289 }
01290 }
01291
01292
01293 for (s = _subsidies; s != endof(_subsidies); s++) {
01294 if (s->cargo_type == cargo_type && s->age < 12) {
01295
01296 const CargoSpec *cs = GetCargo(cargo_type);
01297 if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) {
01298 xy = GetTown(s->from)->xy;
01299 } else {
01300 xy = GetIndustry(s->from)->xy;
01301 }
01302 if (DistanceMax(xy, from->xy) > 9) continue;
01303
01304
01305 switch (cs->town_effect) {
01306 case TE_PASSENGERS:
01307 case TE_MAIL:
01308 case TE_GOODS:
01309 case TE_FOOD:
01310 xy = GetTown(s->to)->xy;
01311 break;
01312
01313 default:
01314 xy = GetIndustry(s->to)->xy;
01315 break;
01316 }
01317 if (DistanceMax(xy, to->xy) > 9) continue;
01318
01319
01320 s->age = 12;
01321 s->from = from->index;
01322 s->to = to->index;
01323
01324
01325 pair = SetupSubsidyDecodeParam(s, 0);
01326 InjectDParam(1);
01327
01328 SetDParam(0, _current_company);
01329 AddNewsItem(
01330 STR_2031_SERVICE_SUBSIDY_AWARDED + _settings_game.difficulty.subsidy_multiplier,
01331 NS_SUBSIDIES,
01332 pair.a, pair.b
01333 );
01334 AI::BroadcastNewEvent(new AIEventSubsidyAwarded(s - _subsidies));
01335
01336 InvalidateWindow(WC_SUBSIDIES_LIST, 0);
01337 return true;
01338 }
01339 }
01340 return false;
01341 }
01342
01353 static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit, SmallIndustryList *industry_set)
01354 {
01355 bool subsidised;
01356 Station *s_from, *s_to;
01357 Money profit;
01358
01359 assert(num_pieces > 0);
01360
01361
01362 {
01363 Company *c = GetCompany(_current_company);
01364 c->cur_economy.delivered_cargo += num_pieces;
01365 SetBit(c->cargo_types, cargo_type);
01366 }
01367
01368
01369 s_from = IsValidStationID(source) ? GetStation(source) : NULL;
01370 s_to = GetStation(dest);
01371
01372
01373 subsidised = s_from != NULL && CheckSubsidised(s_from, s_to, cargo_type);
01374
01375
01376 const CargoSpec *cs = GetCargo(cargo_type);
01377 if (cs->town_effect == TE_FOOD) s_to->town->new_act_food += num_pieces;
01378 if (cs->town_effect == TE_WATER) s_to->town->new_act_water += num_pieces;
01379
01380
01381 DeliverGoodsToIndustry(s_to, cargo_type, num_pieces, industry_set);
01382
01383
01384 profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(source_tile, s_to->xy), days_in_transit, cargo_type);
01385
01386
01387 if (subsidised) {
01388 switch (_settings_game.difficulty.subsidy_multiplier) {
01389 case 0: profit += profit >> 1; break;
01390 case 1: profit *= 2; break;
01391 case 2: profit *= 3; break;
01392 default: profit *= 4; break;
01393 }
01394 }
01395
01396 return profit;
01397 }
01398
01404 static void TriggerIndustryProduction(Industry *i)
01405 {
01406 const IndustrySpec *indspec = GetIndustrySpec(i->type);
01407 uint16 callback = indspec->callback_flags;
01408
01409 i->was_cargo_delivered = true;
01410 i->last_cargo_accepted_at = _date;
01411
01412 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
01413 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) {
01414 IndustryProductionCallback(i, 0);
01415 } else {
01416 InvalidateWindow(WC_INDUSTRY_VIEW, i->index);
01417 }
01418 } else {
01419 for (uint cargo_index = 0; cargo_index < lengthof(i->incoming_cargo_waiting); cargo_index++) {
01420 uint cargo_waiting = i->incoming_cargo_waiting[cargo_index];
01421 if (cargo_waiting == 0) continue;
01422
01423 i->produced_cargo_waiting[0] = min(i->produced_cargo_waiting[0] + (cargo_waiting * indspec->input_cargo_multiplier[cargo_index][0] / 256), 0xFFFF);
01424 i->produced_cargo_waiting[1] = min(i->produced_cargo_waiting[1] + (cargo_waiting * indspec->input_cargo_multiplier[cargo_index][1] / 256), 0xFFFF);
01425
01426 i->incoming_cargo_waiting[cargo_index] = 0;
01427 }
01428 }
01429
01430 TriggerIndustry(i, INDUSTRY_TRIGGER_RECEIVED_CARGO);
01431 StartStopIndustryTileAnimation(i, IAT_INDUSTRY_RECEIVED_CARGO);
01432 }
01433
01438 void VehiclePayment(Vehicle *front_v)
01439 {
01440 int result = 0;
01441
01442 Money vehicle_profit = 0;
01443 Money route_profit = 0;
01444 Money virtual_profit = 0;
01445
01446 StationID last_visited = front_v->last_station_visited;
01447 Station *st = GetStation(last_visited);
01448
01449
01450 CompanyID old_company = _current_company;
01451 _current_company = front_v->owner;
01452
01453
01454 ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED);
01455
01456
01457 front_v->load_unload_time_rem = 1;
01458
01459
01460 static SmallIndustryList industry_set;
01461 industry_set.Clear();
01462
01463 for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
01464
01465 if (v->cargo_cap == 0 || v->cargo.Empty() || front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) continue;
01466
01467
01468 if (!v->cargo.UnpaidCargo()) {
01469 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01470 continue;
01471 }
01472
01473 GoodsEntry *ge = &st->goods[v->cargo_type];
01474 const CargoList::List *cargos = v->cargo.Packets();
01475
01476 for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) {
01477 CargoPacket *cp = *it;
01478 if (!cp->paid_for &&
01479 cp->source != last_visited &&
01480 HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) &&
01481 (front_v->current_order.GetUnloadType() & OUFB_TRANSFER) == 0) {
01482
01483 st->time_since_unload = 0;
01484
01485
01486 Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit, &industry_set);
01487 cp->paid_for = true;
01488 route_profit += profit;
01489 vehicle_profit += profit - cp->feeder_share;
01490
01491 result |= 1;
01492
01493 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01494 } else if (front_v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) {
01495 if (!cp->paid_for && (front_v->current_order.GetUnloadType() & OUFB_TRANSFER) != 0) {
01496 Money profit = GetTransportedGoodsIncome(
01497 cp->count,
01498
01499 DistanceManhattan(cp->loaded_at_xy, GetStation(last_visited)->xy),
01500 cp->days_in_transit,
01501 v->cargo_type);
01502
01503 front_v->profit_this_year += profit << 8;
01504 virtual_profit += profit;
01505 cp->feeder_share += profit;
01506 cp->paid_for = true;
01507 }
01508 result |= 2;
01509
01510 SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01511 }
01512 }
01513 v->cargo.InvalidateCache();
01514 }
01515
01516
01517 const Industry * const *isend = industry_set.End();
01518 for (Industry **iid = industry_set.Begin(); iid != isend; iid++) {
01519 TriggerIndustryProduction(*iid);
01520 }
01521
01522 if (virtual_profit > 0) {
01523 ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit);
01524 }
01525
01526 if (route_profit != 0) {
01527 front_v->profit_this_year += vehicle_profit << 8;
01528 SubtractMoneyFromCompany(CommandCost(front_v->GetExpenseType(true), -route_profit));
01529
01530 if (IsLocalCompany() && !PlayVehicleSound(front_v, VSE_LOAD_UNLOAD)) {
01531 SndPlayVehicleFx(SND_14_CASHTILL, front_v);
01532 }
01533
01534 ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit);
01535 }
01536
01537 _current_company = old_company;
01538 }
01539
01548 static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
01549 {
01550 assert(v->current_order.IsType(OT_LOADING));
01551
01552
01553 if (--v->load_unload_time_rem != 0) {
01554 if (_settings_game.order.improved_load && (v->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
01555
01556 for (; v != NULL; v = v->Next()) {
01557 int cap_left = v->cargo_cap - v->cargo.Count();
01558 if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
01559 }
01560 }
01561 return;
01562 }
01563
01564 StationID last_visited = v->last_station_visited;
01565 Station *st = GetStation(last_visited);
01566
01567 if (v->type == VEH_TRAIN && (!IsTileType(v->tile, MP_STATION) || GetStationIndex(v->tile) != st->index)) {
01568
01569
01570 SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
01571 return;
01572 }
01573
01574 int unloading_time = 0;
01575 Vehicle *u = v;
01576 int result = 0;
01577
01578 bool completely_emptied = true;
01579 bool anything_unloaded = false;
01580 bool anything_loaded = false;
01581 uint32 cargo_not_full = 0;
01582 uint32 cargo_full = 0;
01583
01584 v->cur_speed = 0;
01585
01586 for (; v != NULL; v = v->Next()) {
01587 if (v->cargo_cap == 0) continue;
01588
01589 byte load_amount = EngInfo(v->engine_type)->load_amount;
01590
01591
01592 if (v->type == VEH_AIRCRAFT && !IsNormalAircraft(v)) load_amount = (load_amount + 3) / 4;
01593
01594 if (_settings_game.order.gradual_loading && HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_LOAD_AMOUNT)) {
01595 uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
01596 if (cb_load_amount != CALLBACK_FAILED && GB(cb_load_amount, 0, 8) != 0) load_amount = GB(cb_load_amount, 0, 8);
01597 }
01598
01599 GoodsEntry *ge = &st->goods[v->cargo_type];
01600
01601 if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (u->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
01602 uint cargo_count = v->cargo.Count();
01603 uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
01604 bool remaining = false;
01605 bool accepted = false;
01606
01607 if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) {
01608
01609 remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, last_visited);
01610
01611 result |= 1;
01612 accepted = true;
01613 }
01614
01615
01616
01617
01618
01619
01620 if (u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER) && (!accepted || v->cargo.Count() == cargo_count)) {
01621 remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded);
01622 SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
01623
01624 result |= 2;
01625 } else if (!accepted) {
01626
01627
01628 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01629 continue;
01630 }
01631
01632
01633 st->time_since_unload = 0;
01634
01635 unloading_time += amount_unloaded;
01636
01637 anything_unloaded = true;
01638 if (_settings_game.order.gradual_loading && remaining) {
01639 completely_emptied = false;
01640 } else {
01641
01642 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01643 }
01644
01645 continue;
01646 }
01647
01648
01649 if (u->current_order.GetLoadType() & OLFB_NO_LOAD) continue;
01650
01651
01652 int t;
01653 switch (u->type) {
01654 case VEH_TRAIN: t = u->u.rail.cached_max_speed; break;
01655 case VEH_ROAD: t = u->max_speed / 2; break;
01656 default: t = u->max_speed; break;
01657 }
01658
01659
01660 ge->last_speed = min(t, 255);
01661 ge->last_age = _cur_year - u->build_year;
01662 ge->days_since_pickup = 0;
01663
01664
01665
01666 int cap_left = v->cargo_cap - v->cargo.Count();
01667 if (!ge->cargo.Empty() && cap_left > 0) {
01668 uint cap = cap_left;
01669 uint count = ge->cargo.Count();
01670
01671
01672
01673 if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) {
01674 SetBit(cargo_not_full, v->cargo_type);
01675 continue;
01676 }
01677
01678 if (cap > count) cap = count;
01679 if (_settings_game.order.gradual_loading) cap = min(cap, load_amount);
01680 if (_settings_game.order.improved_load) {
01681
01682 cap = min((uint)cargo_left[v->cargo_type], cap);
01683 cargo_left[v->cargo_type] -= cap;
01684 }
01685
01686 if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
01687
01688
01689
01690
01691
01692
01693
01694
01695 completely_emptied = false;
01696 anything_loaded = true;
01697
01698 ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy);
01699
01700 st->time_since_load = 0;
01701 st->last_vehicle_type = v->type;
01702
01703 StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
01704
01705 unloading_time += cap;
01706
01707 result |= 2;
01708 }
01709
01710 if (v->cargo.Count() >= v->cargo_cap) {
01711 SetBit(cargo_full, v->cargo_type);
01712 } else {
01713 SetBit(cargo_not_full, v->cargo_type);
01714 }
01715 }
01716
01717
01718 completely_emptied &= anything_unloaded;
01719
01720
01721
01722
01723
01724 if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
01725
01726 for (v = u; v != NULL; v = v->Next()) {
01727 int cap_left = v->cargo_cap - v->cargo.Count();
01728 if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
01729 }
01730 }
01731
01732 v = u;
01733
01734 if (anything_loaded || anything_unloaded) {
01735 if (_settings_game.order.gradual_loading) {
01736
01737
01738 const uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
01739
01740 unloading_time = gradual_loading_wait_time[v->type];
01741 }
01742 } else {
01743 bool finished_loading = true;
01744 if (v->current_order.GetLoadType() & OLFB_FULL_LOAD) {
01745 if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
01746
01747
01748 if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.Count()) ||
01749 (cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) {
01750 finished_loading = false;
01751 }
01752 } else if (cargo_not_full != 0) {
01753 finished_loading = false;
01754 }
01755 }
01756 unloading_time = 20;
01757
01758 SB(v->vehicle_flags, VF_LOADING_FINISHED, 1, finished_loading);
01759 }
01760
01761 if (v->type == VEH_TRAIN) {
01762
01763 int overhang = v->u.rail.cached_total_length - st->GetPlatformLength(v->tile) * TILE_SIZE;
01764 if (overhang > 0) {
01765 unloading_time <<= 1;
01766 unloading_time += (overhang * unloading_time) / 8;
01767 }
01768 }
01769
01770
01771
01772
01773
01774
01775
01776 if (_game_mode != GM_MENU && (_settings_client.gui.loading_indicators > (uint)(v->owner != _local_company && _local_company != COMPANY_SPECTATOR))) {
01777 StringID percent_up_down = STR_NULL;
01778 int percent = CalcPercentVehicleFilled(v, &percent_up_down);
01779 if (v->fill_percent_te_id == INVALID_TE_ID) {
01780 v->fill_percent_te_id = ShowFillingPercent(v->x_pos, v->y_pos, v->z_pos + 20, percent, percent_up_down);
01781 } else {
01782 UpdateFillingPercent(v->fill_percent_te_id, percent, percent_up_down);
01783 }
01784 }
01785
01786 v->load_unload_time_rem = unloading_time;
01787
01788 if (completely_emptied) {
01789 TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
01790 }
01791
01792 if (result != 0) {
01793 InvalidateWindow(GetWindowClassForVehicleType(v->type), v->owner);
01794 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01795
01796 st->MarkTilesDirty(true);
01797 v->MarkDirty();
01798
01799 if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited);
01800 }
01801 }
01802
01808 void LoadUnloadStation(Station *st)
01809 {
01810 int cargo_left[NUM_CARGO];
01811
01812 for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
01813
01814 std::list<Vehicle *>::iterator iter;
01815 for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
01816 Vehicle *v = *iter;
01817 if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
01818 }
01819 }
01820
01821 void CompaniesMonthlyLoop()
01822 {
01823 CompaniesGenStatistics();
01824 if (_settings_game.economy.inflation) AddInflation();
01825 CompaniesPayInterest();
01826
01827 _current_company = OWNER_NONE;
01828 HandleEconomyFluctuations();
01829 SubsidyMonthlyHandler();
01830 }
01831
01832 static void DoAcquireCompany(Company *c)
01833 {
01834 Company *owner;
01835 int i;
01836 Money value;
01837 CompanyID ci = c->index;
01838
01839 CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1);
01840 cni->FillData(c, GetCompany(_current_company));
01841
01842 SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER);
01843 SetDParam(1, c->bankrupt_value == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR);
01844 SetDParamStr(2, cni->company_name);
01845 SetDParamStr(3, cni->other_company_name);
01846 SetDParam(4, c->bankrupt_value);
01847 AddNewsItem(STR_02B6, NS_COMPANY_MERGER, 0, 0, cni);
01848 AI::BroadcastNewEvent(new AIEventCompanyMerger(ci, _current_company));
01849
01850
01851 ChangeNetworkOwner(ci, _current_company);
01852 ChangeOwnershipOfCompanyItems(ci, _current_company);
01853
01854 if (c->bankrupt_value == 0) {
01855 owner = GetCompany(_current_company);
01856 owner->current_loan += c->current_loan;
01857 }
01858
01859 value = CalculateCompanyValue(c) >> 2;
01860 CompanyID old_company = _current_company;
01861 for (i = 0; i != 4; i++) {
01862 if (c->share_owners[i] != COMPANY_SPECTATOR) {
01863 _current_company = c->share_owners[i];
01864 SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -value));
01865 }
01866 }
01867 _current_company = old_company;
01868
01869 if (!IsHumanCompany(c->index)) AI::Stop(c->index);
01870
01871 DeleteCompanyWindows(ci);
01872 InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
01873 InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
01874 InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
01875 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
01876
01877 delete c;
01878 }
01879
01880 extern int GetAmountOwnedBy(const Company *c, Owner owner);
01881
01888 CommandCost CmdBuyShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01889 {
01890 CommandCost cost(EXPENSES_OTHER);
01891
01892
01893
01894 if (!IsValidCompanyID((CompanyID)p1) || !_settings_game.economy.allow_shares || _current_company == (CompanyID)p1) return CMD_ERROR;
01895
01896 Company *c = GetCompany((CompanyID)p1);
01897
01898
01899 if (_cur_year - c->inaugurated_year < 6) return_cmd_error(STR_PROTECTED);
01900
01901
01902 if (GetAmountOwnedBy(c, COMPANY_SPECTATOR) == 0) return cost;
01903
01904
01905 if (GetAmountOwnedBy(c, COMPANY_SPECTATOR) == 1 && !c->is_ai) return cost;
01906
01907 cost.AddCost(CalculateCompanyValue(c) >> 2);
01908 if (flags & DC_EXEC) {
01909 OwnerByte *b = c->share_owners;
01910 int i;
01911
01912 while (*b != COMPANY_SPECTATOR) b++;
01913 *b = _current_company;
01914
01915 for (i = 0; c->share_owners[i] == _current_company;) {
01916 if (++i == 4) {
01917 c->bankrupt_value = 0;
01918 DoAcquireCompany(c);
01919 break;
01920 }
01921 }
01922 InvalidateWindow(WC_COMPANY, p1);
01923 }
01924 return cost;
01925 }
01926
01933 CommandCost CmdSellShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01934 {
01935
01936
01937 if (!IsValidCompanyID((CompanyID)p1) || !_settings_game.economy.allow_shares || _current_company == (CompanyID)p1) return CMD_ERROR;
01938
01939 Company *c = GetCompany((CompanyID)p1);
01940
01941
01942 if (GetAmountOwnedBy(c, _current_company) == 0) return CommandCost();
01943
01944
01945 Money cost = CalculateCompanyValue(c) >> 2;
01946 cost = -(cost - (cost >> 7));
01947
01948 if (flags & DC_EXEC) {
01949 OwnerByte *b = c->share_owners;
01950 while (*b != _current_company) b++;
01951 *b = COMPANY_SPECTATOR;
01952 InvalidateWindow(WC_COMPANY, p1);
01953 }
01954 return CommandCost(EXPENSES_OTHER, cost);
01955 }
01956
01966 CommandCost CmdBuyCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01967 {
01968 CompanyID cid = (CompanyID)p1;
01969
01970
01971 if (!IsValidCompanyID(cid) || _networking) return CMD_ERROR;
01972
01973
01974 if (cid == _current_company) return CMD_ERROR;
01975
01976 Company *c = GetCompany(cid);
01977
01978 if (!c->is_ai) return CMD_ERROR;
01979
01980 if (flags & DC_EXEC) {
01981 DoAcquireCompany(c);
01982 }
01983 return CommandCost(EXPENSES_OTHER, c->bankrupt_value);
01984 }