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 assert(v->load_unload_time_rem != 0);
01556
01557
01558 if (--v->load_unload_time_rem != 0) {
01559 if (_settings_game.order.improved_load && (v->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
01560
01561 for (; v != NULL; v = v->Next()) {
01562 int cap_left = v->cargo_cap - v->cargo.Count();
01563 if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
01564 }
01565 }
01566 return;
01567 }
01568
01569 StationID last_visited = v->last_station_visited;
01570 Station *st = GetStation(last_visited);
01571
01572 if (v->type == VEH_TRAIN && (!IsTileType(v->tile, MP_STATION) || GetStationIndex(v->tile) != st->index)) {
01573
01574
01575 SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
01576 return;
01577 }
01578
01579 int unloading_time = 0;
01580 Vehicle *u = v;
01581 int result = 0;
01582
01583 bool completely_emptied = true;
01584 bool anything_unloaded = false;
01585 bool anything_loaded = false;
01586 uint32 cargo_not_full = 0;
01587 uint32 cargo_full = 0;
01588
01589 v->cur_speed = 0;
01590
01591 CargoPayment *payment = v->cargo_payment;
01592
01593 for (; v != NULL; v = v->Next()) {
01594 if (v->cargo_cap == 0) continue;
01595
01596 byte load_amount = EngInfo(v->engine_type)->load_amount;
01597
01598
01599 if (v->type == VEH_AIRCRAFT && !IsNormalAircraft(v)) load_amount = (load_amount + 3) / 4;
01600
01601 if (_settings_game.order.gradual_loading && HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_LOAD_AMOUNT)) {
01602 uint16 cb_load_amount = GetVehicleCallback(CBID_VEHICLE_LOAD_AMOUNT, 0, 0, v->engine_type, v);
01603 if (cb_load_amount != CALLBACK_FAILED && GB(cb_load_amount, 0, 8) != 0) load_amount = GB(cb_load_amount, 0, 8);
01604 }
01605
01606 GoodsEntry *ge = &st->goods[v->cargo_type];
01607
01608 if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (u->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
01609 uint cargo_count = v->cargo.Count();
01610 uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
01611 bool remaining = false;
01612 bool accepted = false;
01613
01614 payment->SetCargo(v->cargo_type);
01615
01616 if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) {
01617
01618 remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, payment, last_visited);
01619
01620 result |= 1;
01621 accepted = true;
01622 }
01623
01624
01625
01626
01627
01628
01629 if (u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER) && (!accepted || v->cargo.Count() == cargo_count)) {
01630 remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, u->current_order.GetUnloadType() & OUFB_TRANSFER ? CargoList::MTA_TRANSFER : CargoList::MTA_UNLOAD, payment);
01631 SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
01632
01633 result |= 2;
01634 } else if (!accepted) {
01635
01636
01637 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01638
01639
01640
01641
01642
01643 anything_unloaded = true;
01644 continue;
01645 }
01646
01647
01648 st->time_since_unload = 0;
01649
01650 unloading_time += amount_unloaded;
01651
01652 anything_unloaded = true;
01653 if (_settings_game.order.gradual_loading && remaining) {
01654 completely_emptied = false;
01655 } else {
01656
01657 ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
01658 }
01659
01660 continue;
01661 }
01662
01663
01664 if (u->current_order.GetLoadType() & OLFB_NO_LOAD) continue;
01665
01666
01667 int t;
01668 switch (u->type) {
01669 case VEH_TRAIN: t = u->u.rail.cached_max_speed; break;
01670 case VEH_ROAD: t = u->max_speed / 2; break;
01671 default: t = u->max_speed; break;
01672 }
01673
01674
01675 ge->last_speed = min(t, 255);
01676 ge->last_age = _cur_year - u->build_year;
01677 ge->days_since_pickup = 0;
01678
01679
01680
01681 int cap_left = v->cargo_cap - v->cargo.Count();
01682 if (!ge->cargo.Empty() && cap_left > 0) {
01683 uint cap = cap_left;
01684 uint count = ge->cargo.Count();
01685
01686
01687
01688 if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) {
01689 SetBit(cargo_not_full, v->cargo_type);
01690 continue;
01691 }
01692
01693 if (cap > count) cap = count;
01694 if (_settings_game.order.gradual_loading) cap = min(cap, load_amount);
01695 if (_settings_game.order.improved_load) {
01696
01697 cap = min((uint)cargo_left[v->cargo_type], cap);
01698 cargo_left[v->cargo_type] -= cap;
01699 }
01700
01701 if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
01702
01703
01704
01705
01706
01707
01708
01709
01710 completely_emptied = false;
01711 anything_loaded = true;
01712
01713 ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, NULL, st->xy);
01714
01715 st->time_since_load = 0;
01716 st->last_vehicle_type = v->type;
01717
01718 StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
01719
01720 unloading_time += cap;
01721
01722 result |= 2;
01723 }
01724
01725 if (v->cargo.Count() >= v->cargo_cap) {
01726 SetBit(cargo_full, v->cargo_type);
01727 } else {
01728 SetBit(cargo_not_full, v->cargo_type);
01729 }
01730 }
01731
01732
01733 completely_emptied &= anything_unloaded;
01734
01735
01736
01737
01738
01739 if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
01740
01741 for (v = u; v != NULL; v = v->Next()) {
01742 int cap_left = v->cargo_cap - v->cargo.Count();
01743 if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
01744 }
01745 }
01746
01747 v = u;
01748
01749 if (!anything_unloaded) delete payment;
01750
01751 if (anything_loaded || anything_unloaded) {
01752 if (_settings_game.order.gradual_loading) {
01753
01754
01755 const uint gradual_loading_wait_time[] = { 40, 20, 10, 20 };
01756
01757 unloading_time = gradual_loading_wait_time[v->type];
01758 }
01759 } else {
01760 bool finished_loading = true;
01761 if (v->current_order.GetLoadType() & OLFB_FULL_LOAD) {
01762 if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
01763
01764
01765 if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.Count()) ||
01766 (cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) {
01767 finished_loading = false;
01768 }
01769 } else if (cargo_not_full != 0) {
01770 finished_loading = false;
01771 }
01772 }
01773 unloading_time = 20;
01774
01775 SB(v->vehicle_flags, VF_LOADING_FINISHED, 1, finished_loading);
01776 }
01777
01778 if (v->type == VEH_TRAIN) {
01779
01780 int overhang = v->u.rail.cached_total_length - st->GetPlatformLength(v->tile) * TILE_SIZE;
01781 if (overhang > 0) {
01782 unloading_time <<= 1;
01783 unloading_time += (overhang * unloading_time) / 8;
01784 }
01785 }
01786
01787
01788
01789
01790
01791
01792
01793 if (_game_mode != GM_MENU && (_settings_client.gui.loading_indicators > (uint)(v->owner != _local_company && _local_company != COMPANY_SPECTATOR))) {
01794 StringID percent_up_down = STR_NULL;
01795 int percent = CalcPercentVehicleFilled(v, &percent_up_down);
01796 if (v->fill_percent_te_id == INVALID_TE_ID) {
01797 v->fill_percent_te_id = ShowFillingPercent(v->x_pos, v->y_pos, v->z_pos + 20, percent, percent_up_down);
01798 } else {
01799 UpdateFillingPercent(v->fill_percent_te_id, percent, percent_up_down);
01800 }
01801 }
01802
01803
01804 v->load_unload_time_rem = max(1, unloading_time);
01805
01806 if (completely_emptied) {
01807 TriggerVehicle(v, VEHICLE_TRIGGER_EMPTY);
01808 }
01809
01810 if (result != 0) {
01811 InvalidateWindow(GetWindowClassForVehicleType(v->type), v->owner);
01812 InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
01813
01814 st->MarkTilesDirty(true);
01815 v->MarkDirty();
01816
01817 if (result & 2) InvalidateWindow(WC_STATION_VIEW, last_visited);
01818 }
01819 }
01820
01826 void LoadUnloadStation(Station *st)
01827 {
01828
01829 if (st->loading_vehicles.empty()) return;
01830
01831 int cargo_left[NUM_CARGO];
01832
01833 for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
01834
01835 std::list<Vehicle *>::iterator iter;
01836 for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
01837 Vehicle *v = *iter;
01838 if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
01839 }
01840
01841
01842 const Industry * const *isend = _cargo_delivery_destinations.End();
01843 for (Industry **iid = _cargo_delivery_destinations.Begin(); iid != isend; iid++) {
01844 TriggerIndustryProduction(*iid);
01845 }
01846 _cargo_delivery_destinations.Clear();
01847 }
01848
01849 void CompaniesMonthlyLoop()
01850 {
01851 CompaniesGenStatistics();
01852 if (_settings_game.economy.inflation) AddInflation();
01853 CompaniesPayInterest();
01854
01855 _current_company = OWNER_NONE;
01856 HandleEconomyFluctuations();
01857 SubsidyMonthlyHandler();
01858 }
01859
01860 static void DoAcquireCompany(Company *c)
01861 {
01862 Company *owner;
01863 int i;
01864 Money value;
01865 CompanyID ci = c->index;
01866
01867 CompanyNewsInformation *cni = MallocT<CompanyNewsInformation>(1);
01868 cni->FillData(c, GetCompany(_current_company));
01869
01870 SetDParam(0, STR_7059_TRANSPORT_COMPANY_MERGER);
01871 SetDParam(1, c->bankrupt_value == 0 ? STR_707F_HAS_BEEN_TAKEN_OVER_BY : STR_705A_HAS_BEEN_SOLD_TO_FOR);
01872 SetDParamStr(2, cni->company_name);
01873 SetDParamStr(3, cni->other_company_name);
01874 SetDParam(4, c->bankrupt_value);
01875 AddNewsItem(STR_02B6, NS_COMPANY_MERGER, 0, 0, cni);
01876 AI::BroadcastNewEvent(new AIEventCompanyMerger(ci, _current_company));
01877
01878
01879 ChangeNetworkOwner(ci, _current_company);
01880 ChangeOwnershipOfCompanyItems(ci, _current_company);
01881
01882 if (c->bankrupt_value == 0) {
01883 owner = GetCompany(_current_company);
01884 owner->current_loan += c->current_loan;
01885 }
01886
01887 value = CalculateCompanyValue(c) >> 2;
01888 CompanyID old_company = _current_company;
01889 for (i = 0; i != 4; i++) {
01890 if (c->share_owners[i] != COMPANY_SPECTATOR) {
01891 _current_company = c->share_owners[i];
01892 SubtractMoneyFromCompany(CommandCost(EXPENSES_OTHER, -value));
01893 }
01894 }
01895 _current_company = old_company;
01896
01897 if (!IsHumanCompany(c->index)) AI::Stop(c->index);
01898
01899 DeleteCompanyWindows(ci);
01900 InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
01901 InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
01902 InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
01903 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
01904
01905 delete c;
01906 }
01907
01908 extern int GetAmountOwnedBy(const Company *c, Owner owner);
01909
01916 CommandCost CmdBuyShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01917 {
01918 CommandCost cost(EXPENSES_OTHER);
01919
01920
01921
01922 if (!IsValidCompanyID((CompanyID)p1) || !_settings_game.economy.allow_shares || _current_company == (CompanyID)p1) return CMD_ERROR;
01923
01924 Company *c = GetCompany((CompanyID)p1);
01925
01926
01927 if (_cur_year - c->inaugurated_year < 6) return_cmd_error(STR_PROTECTED);
01928
01929
01930 if (GetAmountOwnedBy(c, COMPANY_SPECTATOR) == 0) return cost;
01931
01932
01933 if (GetAmountOwnedBy(c, COMPANY_SPECTATOR) == 1 && !c->is_ai) return cost;
01934
01935 cost.AddCost(CalculateCompanyValue(c) >> 2);
01936 if (flags & DC_EXEC) {
01937 OwnerByte *b = c->share_owners;
01938 int i;
01939
01940 while (*b != COMPANY_SPECTATOR) b++;
01941 *b = _current_company;
01942
01943 for (i = 0; c->share_owners[i] == _current_company;) {
01944 if (++i == 4) {
01945 c->bankrupt_value = 0;
01946 DoAcquireCompany(c);
01947 break;
01948 }
01949 }
01950 InvalidateWindow(WC_COMPANY, p1);
01951 }
01952 return cost;
01953 }
01954
01961 CommandCost CmdSellShareInCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01962 {
01963
01964
01965 if (!IsValidCompanyID((CompanyID)p1) || !_settings_game.economy.allow_shares || _current_company == (CompanyID)p1) return CMD_ERROR;
01966
01967 Company *c = GetCompany((CompanyID)p1);
01968
01969
01970 if (GetAmountOwnedBy(c, _current_company) == 0) return CommandCost();
01971
01972
01973 Money cost = CalculateCompanyValue(c) >> 2;
01974 cost = -(cost - (cost >> 7));
01975
01976 if (flags & DC_EXEC) {
01977 OwnerByte *b = c->share_owners;
01978 while (*b != _current_company) b++;
01979 *b = COMPANY_SPECTATOR;
01980 InvalidateWindow(WC_COMPANY, p1);
01981 }
01982 return CommandCost(EXPENSES_OTHER, cost);
01983 }
01984
01994 CommandCost CmdBuyCompany(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01995 {
01996 CompanyID cid = (CompanyID)p1;
01997
01998
01999 if (!IsValidCompanyID(cid) || _networking) return CMD_ERROR;
02000
02001
02002 if (cid == _current_company) return CMD_ERROR;
02003
02004 Company *c = GetCompany(cid);
02005
02006 if (!c->is_ai) return CMD_ERROR;
02007
02008 if (flags & DC_EXEC) {
02009 DoAcquireCompany(c);
02010 }
02011 return CommandCost(EXPENSES_OTHER, c->bankrupt_value);
02012 }