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